Reputation: 31
I'm just starting to learn how to program ARM microcontrollers using ST's HAL. So I have this code which generates a 1 kHz and 20 kHz signal using a timer in output compare mode. That all works well and I tested it with a scope and it's as expected. The issue is with HAL_TIM_PeriodElapsedCallback()
which should get called when whenever the timer has overflown and toggle an LED. The LED's configuration is correct. HAL_TIM_PeriodElapsedCallback()
gets called by HAL_TIM_IRQHandler(&htim3);
which is called whenever an interrupt for timer3 is fired such as when the timer overflows. HAL_TIM_IRQHandler(&htim3);
also gets called often when the output compare register matches that of the timers 'count' register and it calls HAL_TIM_OC_DelayElapsedCallback()
. This all works, so I know the timer3 interrupt is configured properly but I just cant figure out why when the timer overflows the correct callback doesn't get called. What I'm guessing is that the interrputs fired by the Output Compare somehow mess with the overflow interrupt. I've also tried using a debugger and following the code, but HAL_TIM_OC_DelayElapsedCallback()
is just skipped. I've included my main()
as well as the relevant code from the HAL_Timer library which handles all interrupts.
Please help me debug this!
PS: Nucleo_BSP_Init();
simply configures the clock and the GPIO so I won't put this code, since I know the LED works and the clock is configured using CubeMX so that should also be fine.
----------------MAIN CODE --------------------
#include "stm32f4xx_hal.h"
#include <nucleo_hal_bsp.h>
#include <string.h>
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim3;
void MX_TIM3_Init(void);
uint16_t computePulse(TIM_HandleTypeDef *htim, uint32_t chFrequency) {
uint32_t timFrequency= HAL_RCC_GetPCLK1Freq() / (htim->Instance->PSC + 1);
return (uint16_t)(timFrequency / chFrequency);
}
volatile uint16_t CH1_FREQ = 0;
volatile uint16_t CH2_FREQ = 0;
int main(void) {
HAL_Init();
Nucleo_BSP_Init();
MX_TIM3_Init();
HAL_TIM_OC_Start_IT(&htim3, TIM_CHANNEL_1);
HAL_TIM_OC_Start_IT(&htim3, TIM_CHANNEL_2);
while (1);
}
/* TIM3 init function */
void MX_TIM3_Init(void) {
TIM_OC_InitTypeDef sConfigOC;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 2;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 65535;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_OC_Init(&htim3);
CH1_FREQ = computePulse(&htim3, 1000);
CH2_FREQ = computePulse(&htim3, 20000);
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = CH1_FREQ;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = CH2_FREQ;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_OC_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
}
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) {
uint32_t pulse;
/* TIMx_CH1 toggling with frequency = 1KHz */
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
pulse = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
/* Set the Capture Compare Register value */
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, (pulse + CH1_FREQ));
}
/* TIM2_CH2 toggling with frequency = 20KHz */
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
pulse = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
/* Set the Capture Compare Register value */
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, (pulse + CH2_FREQ));
}
}
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef* htim_base) {
GPIO_InitTypeDef GPIO_InitStruct;
if(htim_base->Instance==TIM3) {
__TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PA7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
}
void TIM3_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim3);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if(htim->Instance == TIM3){
// HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
}
IRQ Handler code which gets callled whenever an interupt is fired.
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
/* Capture compare 1 event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !=RESET)
{
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
/* Input capture event */
if((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U)
{
HAL_TIM_IC_CaptureCallback(htim);
}
/* Output compare event */
else
{
HAL_TIM_OC_DelayElapsedCallback(htim);
HAL_TIM_PWM_PulseFinishedCallback(htim);
}
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
}
}
}
/* Capture compare 2 event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
/* Input capture event */
if((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00U)
{
HAL_TIM_IC_CaptureCallback(htim);
}
/* Output compare event */
else
{
HAL_TIM_OC_DelayElapsedCallback(htim);
HAL_TIM_PWM_PulseFinishedCallback(htim);
}
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
}
}
/* Capture compare 3 event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3);
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
/* Input capture event */
if((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00U)
{
HAL_TIM_IC_CaptureCallback(htim);
}
/* Output compare event */
else
{
HAL_TIM_OC_DelayElapsedCallback(htim);
HAL_TIM_PWM_PulseFinishedCallback(htim);
}
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
}
}
/* Capture compare 4 event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4);
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
/* Input capture event */
if((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00U)
{
HAL_TIM_IC_CaptureCallback(htim);
}
/* Output compare event */
else
{
HAL_TIM_OC_DelayElapsedCallback(htim);
HAL_TIM_PWM_PulseFinishedCallback(htim);
}
htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
}
}
/* TIM Update event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
HAL_TIM_PeriodElapsedCallback(htim);
}
}
/* TIM Break input event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK);
HAL_TIMEx_BreakCallback(htim);
}
}
/* TIM Trigger detection event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
HAL_TIM_TriggerCallback(htim);
}
}
/* TIM commutation event */
if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) !=RESET)
{
__HAL_TIM_CLEAR_IT(htim, TIM_FLAG_COM);
HAL_TIMEx_CommutationCallback(htim);
}
}
}
Upvotes: 2
Views: 9764
Reputation: 579
I had a similar situations and after a bit of digging traced it down to this statement in the ST drivers IRQ Handler, the same that you have included in the question:
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) !=RESET)
Confusingly named, this line is actually checking whether the Update callback is enabled.
I therefore was able to trigger the callback once I enabled the TIM_IT_UPDATE
feature...
This can be done in two ways (links are referencing the G4 HAL drivers):
HAL_TIM_Base_Start_IT()
(Not Recommended)Which enables this feature as a consequence
HAL_TIM_Base_Start_IT(htim)
Note: This is only "not recommended" as you are using the OC mode
__HAL_TIM_ENABLE_IT()
Running this in an initializing section (e.g. HAL_TIM_OC_MspInit()
) :
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
I would probably suggest the second option if it works for you, as the ST docstring suggests the former is for setting the timer as a Timer base, which may have unintended consequences for this use case.
Upvotes: 1