2022-09-02 06:45 AM
We are having problems getting a PWM output from Timer 1(nothing on the PC10 pin.)
STM32G070
Low Level drivers
Alternate function 2 of PC10 is Timer1 channel 3
Code is based on STM32CubeG0-master\Projects\NUCLEO-G070RB\Examples_LL\TIM\TIM_PWMOutput_Init
The example in the LL drivers uses timer 6 where we need to use timer 1. Timer 1 has more features so we are guessing that there is another initialization that needs to be done. Here is the code but some of the comments still refer to timer 3. Clock setup first, then Timer 1 init and finally code before the main loop begins. The LL_TIM_EnableAllOutputs() call before the main loop was added from a HAL project that used timer 1.
Any suggestions would be appreciated.
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* - Set the pre-scaler value to have TIM1 counter clock equal to 10 kHz */
/* - Set the auto-reload value to have a counter frequency of 100 Hz */
/* TIM1CLK = SystemCoreClock / (APB prescaler & multiplier) */
TimOutClock = SystemCoreClock/1;
timxPrescaler = __LL_TIM_CALC_PSC(SystemCoreClock, 10000);
timxPeriod = __LL_TIM_CALC_ARR(TimOutClock, timxPrescaler, 100);
/* USER CODE END TIM1_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
//LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);
/* TIM3 interrupt Init */
NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0);
NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
TIM_InitStruct.Prescaler = timxPrescaler;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = timxPeriod;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = ((timxPeriod + 1 ) / 2);
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
// Clocking for Group C pins is already enabled
//LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
/**TIM1 GPIO Configuration
PC10 ------> TIM1_CH3
*/
GPIO_InitStruct.Pin = PWM_S_PC10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
/* Clear the update flag */
LL_TIM_ClearFlag_UPDATE(TIM1);
/* Enable the update interrupt */
LL_TIM_EnableIT_UPDATE(TIM1);
/* Enable TIM1 outputs */
LL_TIM_EnableAllOutputs(TIM1);
/* Enable counter */
LL_TIM_EnableCounter(TIM1);
Solved! Go to Solution.
2022-09-06 01:47 PM
Thanks to all the help here this Timer1 PWM code set is running but needs some polishing.
Clock enables and pin setup:
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
/* PC Alternate function Outputs */
GPIO_InitStruct.Pin = PWM_S_PC10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer1 init:
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* - Set the pre-scaler value to have TIM1 counter clock equal to 10 kHz */
/* - Set the auto-reload value to have a counter frequency of 80 Hz */
/* TIM1CLK = SystemCoreClock / (APB prescaler & multiplier) */
TimOutClock = SystemCoreClock/1;
timxPrescaler = __LL_TIM_CALC_PSC(SystemCoreClock, 10000);
timxPeriod = __LL_TIM_CALC_ARR(TimOutClock, timxPrescaler, 80);
/* USER CODE END TIM1_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
//LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM1);
/* TIM1 interrupt Init */
NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0);
NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM1_Init 1 */
TIM_InitStruct.Prescaler = timxPrescaler;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = timxPeriod;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1);
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = ((timxPeriod + 1 ) / 2);
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_LOW;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_LOW;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
// Clocking for Group C pins is already enabled
//LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
/**TIM1 GPIO Configuration
PC10 ------> TIM1_CH3
*/
// GPIO_InitStruct.Pin = PWM_S_PC10;
// GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
// GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
// GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
// GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN;
// GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
// LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
Final cleanup and activation before the main loop:
// Clear Interrupt flags.
LL_TIM_ClearFlag_CC1(TIM1);
LL_TIM_ClearFlag_CC2(TIM1);
LL_TIM_ClearFlag_CC3(TIM1);
LL_TIM_ClearFlag_CC4(TIM1);
/* Clear the update flag */
LL_TIM_ClearFlag_UPDATE(TIM1);
/* Enable the update interrupt */
LL_TIM_EnableIT_UPDATE(TIM1);
/* Enable TIM1 outputs */
LL_TIM_EnableAllOutputs(TIM1);
/* Enable counter */
LL_TIM_EnableCounter(TIM1);
// Enable Timer1 channel 3 CC3E bit.
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3);
2022-09-06 01:49 PM
If I'm not overlooking something again, this looks good.
Are you sure hardware is OK, there are no bad solder joints, and if this is a Nucleo/Disco or similar, are all solder bridges set as required for this particular pin? Try to set PC10 to GPIO Out and toggle, while observing.
JW
2022-09-07 05:30 AM
The hardware is our first prototype board which needed some design tweaks but the processor is in good shape. Everything is running now and I'm testing the duty cycle code. If gbm had not mentioned that bit it would have taken a lot longer to find as we had called LL_TIM_EnableAllOutputs(). It seems like there are two levels of enable. Probably need to read more on PWM in general but as usual we are short on time.
The description of that register is Capture Compare and because we are new to PWM it's name does not make sense. What is the relation between generating a PWM signal capturing or comparing?
2022-09-07 06:55 AM
This nomenclature (and the basic idea) dates back at least to the Programmable Counter Array (PCA) in Intel's 8051Fx (an evolutionary version of the venerable 8051 - maybe there was some standalone timer chip with this feature, too).
The basic idea is, that you have a single counter (i.e. a register incrementing based on some clock) and several "static" registers accompanied by some setup registers and some logic connecting them to IO pins. One set of "static" register, logic and IO pin is called "channel".
Each channel can be set to one of two states:
The STM32 incarnation is the same, except on steroids.
This is why channels - and also the "static" register - are called "Capture Compare".
JW
2022-09-07 08:16 AM
Thanks for the lesson. I thought it had some reference to using PWM setup registers where the GPIO's are in input mode(I think it is some type of motor feedback). I've worked on a lot of micro's but can't remember a register set like these. It might be because the STM32's are so much like larger processors with clocking domains and specialized hardware sections for specific tasks(DMA, CAN, PWM, DAC...). Still have to believe a sub 0.70 cent micro can have a DMA engine.