2018-04-21 02:02 PM
I am generating stimuli for a CPLD circuit using my Discovery board - STM32F303VCT.
I use CubeMX for peripheral/clock initialisation, and AC6 (Eclipse) for code.
I am using TIM15 - PWM CH1/CH1N - One Pulse Mode - with RCR of some value, say N-1.
The PWM signals are to run a half-bridge for an AC motor, and hence need to be complementary with a small dead-time. That is, when CH1 is high, then CH1N is low and vica verca, but with a small period where both are low to prevent shorting the bridge. Both may be low, but both may never be high s
imultaneously
.The code in question is pure polling, where I start the timer-pwm using HAL functions.
The first pulse-train is working fine... Both pwm-signals start from idle and generate N pulses with a small dead-time.
After those pulses, I have a 500 ms delay, and then generate N new pulses, using HAL again. However, this time I always get a ''glitch'' in the ''pwm1'' signal, prior to the pulse-train, as shown
The majority of the code is generated by CubeMX, with generally default settings for the TIM15
and (relevant) user-code in the while(1)-loop
if (HAL_GPIO_ReadPin(GPIOA, bluePB_Pin) == GPIO_PIN_SET)
{
HAL_Delay(200);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim15, TIM_CHANNEL_1);
HAL_Delay(500);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
}
I guess the code is quite naïve, but it works fine for a single PWM signal, and BTW don't worry - I am not the one coding the microcontroller that will generate our PWM signals for our real motor !
I know of a couple of application notes, e.g. ''AN4013 STM32 cross-series timer overview'' and ''AN4776 - General purpose timer cookbook'', but they take some time to digest, and my goal here is just to generate stimuli to test my CPLD.
So, my question is how should I generate complementary pwm pulse-trains with no glitch ?
Also, anyone is very much welcome to refer to (free) tutorials / lectures / best-practice regarding STM32 timers.
https://community.st.com/tags/?tags=tim%20pwm
,https://community.st.com/tags/?tags=one%20pulse%20mode
,https://community.st.com/tags/?tags=learning%20stm32
one-pulse-mode tim-pwm learning-stm32Solved! Go to Solution.
2018-04-23 04:49 PM
Here is what I think has happened (I don't have the 'F3 Cube, this is based on some random version of 'F4 Cube, so I may be wrong): you haven't told us which channel is which, but given PWM1 on normal output is high when CNT is below CCR, pwm2 is the normal output (TIM15_CH1) and pwm1 is the inverted one (TIM15_CH1N).
After the number of pulses programmed in RCR (plus one) is output, in one-pulse mode, TIM15_CR1.CEN is cleared by hardware, but that's it, nothing else happens, i.e. the outputs remain enabled (including TIM15_BDTR.MOE). As TIM15_CNT is then 0, the normal output stays high and the inverted low, actively driven (i.e. not high-Z).
Then you call
HAL_TIM_PWM_Start
(). That basically does three things:/* Enable the Capture compare channel */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE); if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET) { /* Enable the main output */ __HAL_TIM_MOE_ENABLE(htim); } /* Enable the Peripheral */ __HAL_TIM_ENABLE(htim);The last one is setting TIM15_CR1.CEN, which is the only thing really needed here, so that's OK. The thing before that, setting BDCR.MOE, is superfluous at this moment, as MOE remained set; but harmless. The first one is the culprit. Let's have a look at it:
void TIM_CCxChannelCmd(TIM_TypeDef* TIMx, uint32_t Channel, uint32_t ChannelState)
{ uint32_t tmp = 0U; /* Check the parameters */ assert_param(IS_TIM_CC1_INSTANCE(TIMx)); assert_param(IS_TIM_CHANNELS(Channel)); tmp = TIM_CCER_CC1E << Channel; /* Reset the CCxE Bit */ TIMx->CCER &= ~tmp; /* Set or reset the CCxE Bit */ TIMx->CCER |= (uint32_t)(ChannelState << Channel);}So it unconditionally clears the respective enable bit for the channel, and then - as called with the appropriate constant for enable - reenables it.
That causes the normal channel to go High-Z briefly - would there be any significant pulldown on that pin, you'd see a 'negative glitch' on it. But, what's more important here, the complementary output CH1N in the STM32 timers has a tricky property: when the normal output is disabled, it stops to be complementary (i.e. inverted to the normal output) and takes the polarity of the normal output. That causes that actively driven glitch.
JW
2018-04-23 02:43 PM
I guess the code is quite naïve
If you call 'relying on some other unknown code' naive, then yes...
know of a couple of application notes, e.g. 'AN4013 STM32 cross-series timer overview' and 'AN4776 - General purpose timer cookbook', but they take some time to digest,
Then take that time. And start with the timer chapter in RM (don't start with the advanced timers, even if that's what you are using here, as digesting them may be a bit harder than you want at the start). They are badly written, so take your time, re-read them, and experiment.
There ain't any silver bullet in embedded programming, just lots of snake oil.
JW
2018-04-23 04:49 PM
Here is what I think has happened (I don't have the 'F3 Cube, this is based on some random version of 'F4 Cube, so I may be wrong): you haven't told us which channel is which, but given PWM1 on normal output is high when CNT is below CCR, pwm2 is the normal output (TIM15_CH1) and pwm1 is the inverted one (TIM15_CH1N).
After the number of pulses programmed in RCR (plus one) is output, in one-pulse mode, TIM15_CR1.CEN is cleared by hardware, but that's it, nothing else happens, i.e. the outputs remain enabled (including TIM15_BDTR.MOE). As TIM15_CNT is then 0, the normal output stays high and the inverted low, actively driven (i.e. not high-Z).
Then you call
HAL_TIM_PWM_Start
(). That basically does three things:/* Enable the Capture compare channel */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE); if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET) { /* Enable the main output */ __HAL_TIM_MOE_ENABLE(htim); } /* Enable the Peripheral */ __HAL_TIM_ENABLE(htim);The last one is setting TIM15_CR1.CEN, which is the only thing really needed here, so that's OK. The thing before that, setting BDCR.MOE, is superfluous at this moment, as MOE remained set; but harmless. The first one is the culprit. Let's have a look at it:
void TIM_CCxChannelCmd(TIM_TypeDef* TIMx, uint32_t Channel, uint32_t ChannelState)
{ uint32_t tmp = 0U; /* Check the parameters */ assert_param(IS_TIM_CC1_INSTANCE(TIMx)); assert_param(IS_TIM_CHANNELS(Channel)); tmp = TIM_CCER_CC1E << Channel; /* Reset the CCxE Bit */ TIMx->CCER &= ~tmp; /* Set or reset the CCxE Bit */ TIMx->CCER |= (uint32_t)(ChannelState << Channel);}So it unconditionally clears the respective enable bit for the channel, and then - as called with the appropriate constant for enable - reenables it.
That causes the normal channel to go High-Z briefly - would there be any significant pulldown on that pin, you'd see a 'negative glitch' on it. But, what's more important here, the complementary output CH1N in the STM32 timers has a tricky property: when the normal output is disabled, it stops to be complementary (i.e. inverted to the normal output) and takes the polarity of the normal output. That causes that actively driven glitch.
JW
2019-01-23 12:21 AM
Use below code,
if (HAL_GPIO_ReadPin(GPIOA, bluePB_Pin) == GPIO_PIN_SET)
{
HAL_Delay(200);
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim15, TIM_CHANNEL_1);
HAL_Delay(500);
TIM15->CCER = 0; //This is added to avoid glitch.
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
}