cancel
Showing results for 
Search instead for 
Did you mean: 

[STM32F429i]PWM problem on few timers/channels

marjano89
Associate II
Posted on June 17, 2014 at 13:45

Hello everyone. I am creating a hexapod with 18 independent servos, using 18 channels on General - Purpose timers. I have problem with few timers, simply servo is not responding to new compare value. I am using timers:

TIM2 - 4 channels working TIM3 - 4 channels working TIM4 - 4 channels working TIM5 - chanel 2 and 3 not working TIM12 - not working Do I have some conflicts with alternative configuration ? My init for timers/pwm:


void initTimer()

{

RCC_TIMCLKPresConfig(RCC_TIMPrescActivated);


/* TIM2 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);


/* TIM3 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);


/* TIM4 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);


/* TIM5 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);


/* TIM12 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM12, ENABLE);


TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;


/* Compute the prescaler value */

uint16_t u16PrescalerValue = (uint16_t) ((SystemCoreClock) / 1000000)- 1;

TIM_TimeBaseStructure.TIM_Prescaler = u16PrescalerValue; // 1MHz

TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // 1 MHz / 20000 = 50 Hz (20ms)

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;


TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

TIM_TimeBaseInit(TIM12, &TIM_TimeBaseStructure);

}


void initPWM()

{

/*

* Servo min(1ms) = 1000

* Servo neutral (1,5ms) = 1500

* Servo max(2ms) = 2000

*

*/


TIM_OCInitTypeDef TIM_OCInitStructure;


TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 0;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;


/*********************TIM2**************************/

/* PWM1 Mode configuration: Channel1 (GPIOA Pin 15)*/

TIM_OC1Init(TIM2, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOB Pin 3)*/

TIM_OC2Init(TIM2, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel3 (GPIOB Pin 10)*/

TIM_OC3Init(TIM2, &TIM_OCInitStructure);

TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel4 (GPIOB Pin 11)*/

TIM_OC4Init(TIM2, &TIM_OCInitStructure);

TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable );


/*********************TIM3**************************/

/* PWM1 Mode configuration: Channel1 (GPIOB Pin 4)*/

TIM_OC1Init(TIM3, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOB Pin 5)*/

TIM_OC2Init(TIM3, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel3 (GPIOB Pin 0)*/

TIM_OC3Init(TIM3, &TIM_OCInitStructure);

TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel4 (GPIOB Pin 1)*/

TIM_OC4Init(TIM3, &TIM_OCInitStructure);

TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable );


/*********************TIM4**************************/

/* PWM1 Mode configuration: Channel1 (GPIOD Pin 12)*/

TIM_OC1Init(TIM4, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOD Pin 13)*/

TIM_OC2Init(TIM4, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel3 (GPIOD Pin 14)*/

TIM_OC3Init(TIM4, &TIM_OCInitStructure);

TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel4 (GPIOD Pin 15)*/

TIM_OC4Init(TIM4, &TIM_OCInitStructure);

TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable );


/*********************TIM5**************************/

/* PWM1 Mode configuration: Channel1 (GPIOA Pin 0)*/

TIM_OC1Init(TIM5, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOA Pin 1)*/

TIM_OC2Init(TIM5, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM5, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel3 (GPIOA Pin 2)*/

TIM_OC3Init(TIM5, &TIM_OCInitStructure);

TIM_OC3PreloadConfig(TIM5, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel4 (GPIOA Pin 3)*/

TIM_OC4Init(TIM5, &TIM_OCInitStructure);

TIM_OC4PreloadConfig(TIM5, TIM_OCPreload_Enable );


/*********************TIM12*************************/

/* PWM1 Mode configuration: Channel1 (GPIOB Pin 14)*/

TIM_OC1Init(TIM12, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM12, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOB Pin 15)*/

TIM_OC2Init(TIM12, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM12, TIM_OCPreload_Enable );


/*********************TIM9*************************/

/* PWM1 Mode configuration: Channel1 (GPIOE Pin 5)*/

TIM_OC1Init(TIM9, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM9, TIM_OCPreload_Enable );


/* PWM1 Mode configuration: Channel2 (GPIOE Pin 6)*/

TIM_OC2Init(TIM9, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM9, TIM_OCPreload_Enable );


//enable timers

TIM_Cmd(TIM2, ENABLE);

TIM_Cmd(TIM3, ENABLE);

TIM_Cmd(TIM4, ENABLE);

TIM_Cmd(TIM5, ENABLE);

TIM_Cmd(TIM9, ENABLE);

TIM_Cmd(TIM12, ENABLE);

}


void initServos()

{

GPIO_InitTypeDef GPIO_PWM;


RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);


GPIO_PWM.GPIO_Mode = GPIO_Mode_AF;

GPIO_PWM.GPIO_Speed = GPIO_Speed_100MHz;

GPIO_PWM.GPIO_OType = GPIO_OType_PP;

GPIO_PWM.GPIO_PuPd = GPIO_PuPd_UP;


//PORT A

GPIO_PWM.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_15;

GPIO_Init(GPIOA, &GPIO_PWM);


GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5 );

GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM5 );

GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM5 );

GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM5 );

GPIO_PinAFConfig(GPIOA, GPIO_PinSource15,GPIO_AF_TIM2 );


//PORT B

GPIO_PWM.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_Init(GPIOB, &GPIO_PWM);


GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_TIM2 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource10,GPIO_AF_TIM2 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource11,GPIO_AF_TIM2 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource14,GPIO_AF_TIM12 );

GPIO_PinAFConfig(GPIOB, GPIO_PinSource15,GPIO_AF_TIM12 );


//PORT D

GPIO_PWM.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_Init(GPIOD, &GPIO_PWM);


GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4 );

GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4 );

GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4 );

GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4 );



}

#pwm #stm32f4 #discovery #output
15 REPLIES 15
Posted on June 17, 2014 at 14:20

Unless you've addressed them, there are a number of physical pin conflicts on the STM32F429I-DISCO

Line numbers make it undesirable to test the code.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
marjano89
Associate II
Posted on June 17, 2014 at 18:49

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6Zh&d=%2Fa%2F0X0000000br0%2Fl9jO0ADPeRX3eRo8Z2gLLcUB1PvzbSo4E_bq6Rq3WQU&asPdf=false
Posted on June 17, 2014 at 20:10

Consider configuring all the fields of the

TIM_OCInitStructure

/**
* @brief Fills each TIM_OCInitStruct member with its default value.
* @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure which will
* be initialized.
* @retval None
*/
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)
{
/* Set the default configuration */
TIM_OCInitStruct->TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStruct->TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStruct->TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStruct->TIM_Pulse = 0x00000000;
TIM_OCInitStruct->TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct->TIM_OCNPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct->TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStruct->TIM_OCNIdleState = TIM_OCNIdleState_Reset;
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
marjano89
Associate II
Posted on June 17, 2014 at 20:53

/**
* @brief Fills each TIM_OCInitStruct member with its default value.
* @param TIM_OCInitStruct: pointer to a TIM_OCInitTypeDef structure which will
* be initialized.
* @retval None
*/
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct)
{
/* Set the default configuration */
TIM_OCInitStruct->TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStruct->TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStruct->TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStruct->TIM_Pulse = 0x00000000;
TIM_OCInitStruct->TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct->TIM_OCNPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct->TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStruct->TIM_OCNIdleState = TIM_OCNIdleState_Reset;
}

TIM_OCInitStruct->TIM_OCMode = TIM_OCMode_Timing; for PWM?

marjano89
Associate II
Posted on June 17, 2014 at 21:04

I have used this default initialization and then changed only mode to PWM1 but didn't help :\

Posted on June 17, 2014 at 21:05

I'm suggesting that you initialize all the fields, either explicitly in your code, with your values, or by using the library function and then setting your subset.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
marjano89
Associate II
Posted on June 17, 2014 at 22:13

Do you have any examples with PWM for TIM5 and TIM12 ?

marjano89
Associate II
Posted on June 18, 2014 at 01:46

I have created little test app for TIM5. Channel 1 and 4 are working, Channelse 2 and 3 not. Source code:

void initUSART1(uint32_t baudrate);
void initPWM();
uint8_t g_u8DataReceived = 0;
char g_u8Character = '0';
int main(void)
{
initUSART1(9600);
initPWM();
/* Infinite loop */
while (1)
{
if(g_u8DataReceived)
{
//reset flag
g_u8DataReceived = 0;
switch(g_u8Character)
{
case '1':
{
TIM_SetCompare1(TIM5, NEUTRAL);
TIM_SetCompare2(TIM5, NEUTRAL);
TIM_SetCompare3(TIM5, NEUTRAL);
TIM_SetCompare4(TIM5, NEUTRAL);
}
break;
case '2':
{
TIM_SetCompare1(TIM5, DEGREE0);
TIM_SetCompare2(TIM5, DEGREE0);
TIM_SetCompare3(TIM5, DEGREE0);
TIM_SetCompare4(TIM5, DEGREE0);
}
break;
case '3':
{
TIM_SetCompare1(TIM5, DEGREE180);
TIM_SetCompare2(TIM5, DEGREE180);
TIM_SetCompare3(TIM5, DEGREE180);
TIM_SetCompare4(TIM5, DEGREE180);
}
break;
}
}
}
}
void initPWM()
{
/* TIM5 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* Compute the prescaler value */
uint16_t u16PrescalerValue = (uint16_t) ((SystemCoreClock) / 1000000)- 1;
TIM_TimeBaseStructure.TIM_Prescaler = u16PrescalerValue; // 1MHz
TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // 1 MHz / 20000 = 50 Hz (20ms)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
/*
* Servo min(1ms) = 1000
* Servo neutral (1,5ms) = 1500
* Servo max(2ms) = 2000
*
*/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/*********************TIM5**************************/
/* PWM1 Mode configuration: Channel1 (GPIOA Pin 0)*/
TIM_OC1Init(TIM5, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable );
/* PWM1 Mode configuration: Channel2 (GPIOA Pin 1)*/
TIM_OC2Init(TIM5, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM5, TIM_OCPreload_Enable );
/* PWM1 Mode configuration: Channel3 (GPIOA Pin 2)*/
TIM_OC3Init(TIM5, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM5, TIM_OCPreload_Enable );
/* PWM1 Mode configuration: Channel4 (GPIOA Pin 3)*/
TIM_OC4Init(TIM5, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM5, TIM_OCPreload_Enable );
TIM_Cmd(TIM5, ENABLE);
///////////////GPIO CONFIGURATION///////////////
GPIO_InitTypeDef GPIO_PWM;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_PWM.GPIO_Mode = GPIO_Mode_AF;
GPIO_PWM.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_PWM.GPIO_OType = GPIO_OType_PP;
GPIO_PWM.GPIO_PuPd = GPIO_PuPd_UP;
//PORT A
GPIO_PWM.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_PWM);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5 );
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM5 );
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_TIM5 );
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_TIM5 );
}
// this is the interrupt request handler (IRQ) for ALL USART1 interrupts
void USART1_IRQHandler(void)
{
if( USART_GetITStatus(USART1, USART_IT_RXNE) != RESET )
{
g_u8Character = USART_ReceiveData(USART1);
g_u8DataReceived = 1;
}
}
void initUSART1(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStruct; // this is for the GPIO pins used as TX and RX
USART_InitTypeDef USART_InitStruct; // this is for the USART1 initilization
NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)
/* enable APB2 peripheral clock for USART1
* note that only USART1 and USART6 are connected to APB2
* the other USARTs are connected to APB1
*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* enable the peripheral clock for the pins used by
* USART1, PB6 for TX and PB7 for RX
*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* This sequence sets up the TX and RX pins
* so they work correctly with the USART1 peripheral
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this defines the IO speed and has nothing to do with the baudrate!
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // this defines the output type as push pull mode (as opposed to open drain)
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // this activates the pullup resistors on the IO pins
GPIO_Init(GPIOB, &GPIO_InitStruct); // now all the values are passed to the GPIO_Init() function which sets the GPIO registers
/* The RX and TX pins are now connected to their AF
* so that the USART1 can take over control of the
* pins
*/
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1 );
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1 );
/* Now the USART_InitStruct is used to define the
* properties of USART1
*/
USART_InitStruct.USART_BaudRate = baudrate; // the baudrate is set to the value we passed into this init function
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // we want the data frame size to be 8 bits (standard)
USART_InitStruct.USART_StopBits = USART_StopBits_1; // we want 1 stop bit (standard)
USART_InitStruct.USART_Parity = USART_Parity_No; // we don't want a parity bit (standard)
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
USART_Init(USART1, &USART_InitStruct); // again all the properties are passed to the USART_Init function which takes care of all the bit setting
/* Here the USART1 receive interrupt is enabled
* and the interrupt controller is configured
* to jump to the USART1_IRQHandler() function
* if the USART1 receive interrupt occurs
*/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // we want to configure the USART1 interrupts
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // this sets the priority group of the USART1 interrupts
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // this sets the subpriority inside the group
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // the USART1 interrupts are globally enabled
NVIC_Init(&NVIC_InitStructure); // the properties are passed to the NVIC_Init function which takes care of the low level stuff
// finally this enables the complete USART1 peripheral
USART_Cmd(USART1, ENABLE);
}

Posted on June 18, 2014 at 02:04

Isn't there just a pin conflict you aren't dealing with?

0690X0000060MmSQAU.gif

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..