2021-07-08 09:06 PM
Hello, I have use Timer 2 in PWM one pulse mode to change the duty cycle of Timer 1 PWM when the pulse have ended using
HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
After restart the board, I press the button to trigger Timer2 One Pulse Mode, and the Timer 1 PWM is produced correctly (The first pulse width is 250us). But on the next button press and the rest, the PWM is produced slightly incorrect (The first pulse width is not 250us anymore).
I hope the images below could help explain a bit of my situation.
Anyone know what could the cause be?
(Image Below) Correct first pulse width that only occur after program/hardware reset
(Image Below) Using different timer 2 channel to adjust PWM 1's duty cycle
(Image Below) Pulse Width of each Channel in Timer 2
(Image Below) Cursor measurement of the correct 250us that only occur after program/hardware reset
(Image Below) Wrong pulse width 1 (Timer 2 channel 1 pulse width no longer 250us)on the next button press
(Image Below) Wrong pulse width 2(Timer 2 channel 1 pulse width no longer 250us)on the next button press
(Image Below) Wrong pulse width 3(Timer 2 channel 1 pulse width no longer 250us)on the next button press
Last thing to mention is when triggering the One Pulse Mode, all the channel will be fired at the same time, I could not selectively fire only 1 of the channel. Therefore I had to use a simple state machine. Hopefully the code below can help me explain a bit.
volatile uint8_t stateMachine_finishedState = 0;
volatile uint8_t stateMachine_nextState = 0;
volatile uint8_t timeTurnOnIsGreat = 1;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(stateMachine_nextState == 0) {
stateMachine_finishedState = 0;
stateMachine_nextState = 1;
__HAL_TIM_SET_COUNTER(&htim2, 0);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
__HAL_TIM_ENABLE(&htim2);
//100% duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COUNTER(&htim1, 0);
}
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
if(stateMachine_nextState == 1){
stateMachine_finishedState = 1;
if(timeTurnOnIsGreat){
stateMachine_nextState = 2;
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_ENABLE(&htim2);
//50% duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim1)/2U);
__HAL_TIM_SET_COUNTER(&htim1, 0);
}
else{
stateMachine_nextState = 3;
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_ENABLE(&htim2);
//0% duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim1)+1);
__HAL_TIM_SET_COUNTER(&htim1, 0);
}
}
}
if(htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2){
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
if(stateMachine_nextState == 2){
stateMachine_finishedState = 2;
stateMachine_nextState = 3;
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_ENABLE(&htim2);
//100% duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COUNTER(&htim1, 0);
}
}
if(htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3){
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_DISABLE);
if(stateMachine_nextState == 3){
stateMachine_finishedState = 3;
stateMachine_nextState = 0;
__HAL_TIM_SET_COUNTER(&htim2, 0);
//0% duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim1)+1);
__HAL_TIM_SET_COUNTER(&htim1, 0);
GPIOE->BSRR |= (1<<21); // Reset the Pin PE5
}
}
}
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 4200-1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xffffffff-0x1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 5;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_1);
sConfigOC.Pulse = 15;
sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_2);
sConfigOC.Pulse = 10;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_3);
/* USER CODE BEGIN TIM2_Init 2 */
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF); //clear Update Interrupt Flag that was Set during initialization
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_2);
HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_3);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE); // disable the timer capture & compare at the beginning when system start
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE); // disable the timer capture & compare at the beginning when system start
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_DISABLE); // disable the timer capture & compare at the beginning when system start
htim2.Instance->CR1 &= ~(TIM_CR1_CEN); //clear Counter Enable flag that was Set during initialization
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
Update:
I placed a breakpoint in the button interrupt and read the registers of timer 2, I compare the register "when the board was just reset and the button is first pressed", and "the second time button is pressed". I then manually set the registers to identical as the first time, and the pulse width 250us is produced correctly again.
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(stateMachine_nextState == 0) {
stateMachine_finishedState = 0;
stateMachine_nextState = 1;
__HAL_TIM_SET_COUNTER(&htim2, 0); //breakpoint
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
(Image below) Left is first button press, Right is second button press
Update2 : manually setting the registers to produce the correct pulse width only worked some times, so it seems the cause is from something else
Update3 : I added the code MX_TIM2_Init(); when the button is pressed and the problem is solved. But this is definitely not the right way, I hope someone could have a look at this project :weary_face:
Update 4: I replaced the MX_TIM2_Init() with htim2.Instance->EGR = TIM_EGR_UG; and the problem is fixed. I'll have to further investigate the reason for this
2021-07-09 02:16 PM
The various HAL TIM routines are difficult to understand, particularly when you have an exact behavior you want to happen.
I would get comfortable with the timer registers and modify those directly. Otherwise, it's twice as much work trying to find the right combination of HAL calls that do what you want.
You've given a ton of data here, but it's still hard to process what is happening vs what you want to happen.
2021-07-12 03:45 AM
Hello, thanks for the reply. I hope you have seen my Update 2 in the post that mentioned writing to the registers worked some of the time only (Or i should say doesn't have any effect). The pulse width varies (not my intention) on its own even when having the same register values. The only time i could produce the correct pulse width is after restarting the board, and subsequent triggers rarely produce the correct pulse width again
2021-07-12 07:13 AM
The ARR and PSC registers are not immediately effective when you update them. By generating an update event, they get loaded from the shadow registers.