Timer Interrupts with STM32 - Page 1
Forum section: electronics / general

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 15, 2015, 01:10:45 pm
content: I am new to ARM MCUs and I have been trying to find my way around the peripherals using STM's HAL library and STM32Cube. I am having trouble in getting interrupts to work. Here, I'm trying to toggle an LED (onboard LD4 LED) on the STM32L100 Discovery Board by using a timer interrupt. The LED should be toggled every time the interrupt routine is run.But all I get is that the LED just stays on forever (which means the interrupt routine runs just once). I don't think it's a problem with the timer settings since I managed to run the timer fine without interrupts. I am assuming I have not initialized the interrupts properly and I can't figure where I went wrong. Any help would be appreciated. My main.c source code is attached below.Line-95: main()Line-148: Clock settingsLine-396: Timer Initialization routineLine-456: GPIO Initialization routineLine-485: Timer interrupt routineThere are initialization functions for various other peripherals in the source file but I'm not using any of them right now. Jut timer and GPIO.

author: Kalvin
author_rank: Super Contributor
timestamp: November 15, 2015, 02:59:51 pm
content: Take a look at this:http://en.radzio.dxp.pl/stm32vldiscovery/lesson4,blinking,with,timer,interrupts.htmlMaybe you will spot the two basic errors in your interrupt handler code.

author: Yansi
author_rank: Super Contributor
timestamp: November 15, 2015, 03:04:39 pm
content: Lol a Delay routine inside an interrupt. What a nonsense. Don't do that!! That's a way to hell.The Delay function uses internally the SysTick interrupt, so you HAVE TO set the two interrupts correct priorities to work. Otherwise you will get stuck inside the Delay function waiting for SysTick interrupt which will never come, because of wrong priorities set.Also, you shoueld usually clear the interrupt pending flag in the peripheral, otherwise the interrupt will keep going again and again.

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 15, 2015, 03:13:31 pm
content: Quote from: Yansi on November 15, 2015, 03:04:39 pmI know that it's nonsense. I was just testing if I'm doing my code correct. It's not a code for something real. I didn't know that HAL_Delay() use SysTick howeverMaybe I'll just add a loop-based delay then. Thanks

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 15, 2015, 03:17:38 pm
content: Quote from: Kalvin on November 15, 2015, 02:59:51 pmSo I have to clear the flag manually, that's the first error. What's the other? I'm not using any other timers so I don't need to check if it's for timer-3 or not right?

author: Yansi
author_rank: Super Contributor
timestamp: November 15, 2015, 04:07:00 pm
content: Udayan Sinha, just my personal suggestion for you: You seem quite smart and it should be better for you, to learn these MCUs from scratch. Why? Because i think it's better to spend effort to actualy learn it, instead of reverse engineering the Cube crap lib package you got with it.You can play with the MCU registers directly, it's no problem. Setting a timer needs only tweaking a few registers. Nothing complicated. Setting up a clock and PLL for the MCU is also simple using the registers. (Only people who do know nothing about says it is complicated. it's not!).Also hard to make any suggestions here. Tha HAL rubbish is so bloated, one must sped a real effort to make it do something.To make timer produce interrupt, simply set CEN bit (start it running) and set the corresponding interrupt bit. Also don't forget to enable the IRQ channel in NVIC!!!You can use the CMSIS functions for that. Easy.

author: Brutte
author_rank: Frequent Contributor
timestamp: November 15, 2015, 04:31:55 pm
content: QuoteI know something and I says it is complicated.Keep away from a clock tree for now. Leave a default 2MHz clock, won't change much for blinking a LED.As for bare bit-banging, I'd prefer STM32L1xx_StdPeripheral library. At least it comes with tested examples.

author: Yansi
author_rank: Super Contributor
timestamp: November 15, 2015, 04:56:06 pm
content: Oh, I haven't noticed this is an L1 device line. Cube/HAL shit is absolutely useless for lowpower stuff. You rather learn how to use bitbanding if you want to make some real low power stuff. (Not bitbanging, but really bitbanding). Some libs use it, most not. Also most programmers are not aware of this nice feature. No Cube/HAL shall be involved in low power applications! Any clock cycle counts into the power consuption.

author: Kalvin
author_rank: Super Contributor
timestamp: November 15, 2015, 05:09:41 pm
content: Quote from: Udayan Sinha on November 15, 2015, 03:17:38 pmThe delay

author: dannyf
author_rank: Super Contributor
timestamp: November 15, 2015, 05:24:06 pm
content: QuoteGood news and bad news: the good news is that there are quite a few ST software engineers here. So your chance of getting some help from them is high.Uncalled for inflamatory remarks removed by moderator. User is on his final warning before his account is stuck where the sun does not shine on a permanent basis. You have been given more than your fair chance.QuoteYour code relies on the HAL libraries, which are bad even by the low ST quality. You will need to make a decision as to if you wish to continue to down that path, risking getting very little help outside of ST (and very little quality help from ST). Or going directly to the registers (as Kalvin's link).My approach is somewhat in between: modulized code that can use either the standard peripheral libraries or your own register version, should you decide to go down that path.Two routines are involved, for a STM32F0:Code:/initialize the timer//timer set tovoid tmr3_init(uint16_t ps, uint16_t period) {TIM_TimeBaseInitTypeDef tmr;//set clock to TIM3RCC_APB1PeriphClockCmd(RCC_TIMx, ENABLE);//timer basestructure 24mhz/(0+1)/(0+1) ~ 2,72mS (655353/24000000)s for full periodtmr.TIM_Prescaler = ps-1 ;tmr.TIM_CounterMode = TIM_CounterMode_Up;//can be TIM_CounterMode_Up or TIM_CounterMode_Downtmr.TIM_Period = period-1;//set the periodtmr.TIM_ClockDivision = TIM_CKD_DIV1;tmr.TIM_RepetitionCounter = /*period-1*/0;//counter runs in repetitionTIM_TimeBaseInit(TIMx, &tmr);//initialize the timer/* TIM IT enable *///TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);//reset isr ptr/*_tmr*/_isr_ptr = /*_tmr3_*/empty_handler;//enable the timerTIM_Cmd(TIMx, ENABLE);}//install user handlervoid tmr3_act(void (*isr_ptr)(void)) {NVIC_InitTypeDef NVIC_InitStructure;/* TIM IT enable */TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);//enable tim2 irqNVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//tim3 irq//NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//0-15NVIC_InitStructure.NVIC_IRQChannelPriority = 1;//0-15NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//enable timer irq/*_tmr*/_isr_ptr = isr_ptr;}//timer 3 interruptvoid TIM3_IRQHandler(void){//if interrupt happens the do this//if(TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET){if (TIMx->SR & TIM_IT_Update) {//clear interrupt and start counting again to get precise freq//TIM_ClearITPendingBit(TIM3, TIM_IT_Update);/* Clear the IT pending Bit */TIMx->SR = (uint16_t)~TIM_IT_Update;//for speed reasons. bit is rc_w0/*_tmr*/_isr_ptr();//IO_FLP(LED_PORT, LED2);}}in your application, you callCode:tmr3_init(TMR_PS_100x, TMR_1ms); //set the tmr3 to trigger every 100msyou then install a user handle for the isr:Code:tmr3_act(my_tmr3_isr); //install my tmr3 isrmy_tmr3_isr() is the function that you want to call whenever the isr is triggered.Porting it to a STM32L requires minimum changes.You will see that the isr portion of the code utilizes direct register access, much like the page Kalvin linked too - the code that utilizes the standard peripheral library is commented out. The initialization portion of the code utilizes only the standard peripheral library, but it can be easily replaced by a functional equivalent version that goes to the registers directly.Hope it helps.

author: neslekkim
author_rank: Super Contributor
timestamp: November 15, 2015, 09:48:11 pm
content: QuoteI wish the moderators on these forums also could look at the contributions dannyf gives, I can handle some "inflamatory remarks" from time to time, the amount of insight he have provided have been very valuable many times as how I see it.

author: richardman
author_rank: Frequent Contributor
timestamp: November 15, 2015, 09:51:42 pm
content: This is how you write a COMPLETE program to blink an LED with JumpStart APIhttps://c4everyone.com/index.php/technologies/jumpstart-api:Code:int main(void){Setup();porta.MakeOutput(LED_PIN, OSPEED_LOW);while (1){porta.Set(LED_PIN);DelayTenth(5);porta.Clear(LED_PIN);DelaySecs(1);}return 0;}static void Setup(void){jsapi_clock.SetSystemClock(16, 0, false, 84, 5);jsapi_cortex_core.SysTick_Timer(SYSTICK_MILLISECOND);}This call:Code:jsapi_clock.SetSystemClock(16, 0, false, 84, 5);set the PLL to 84 MHz from the 16MHz HSI (this is an STM32F401).

author: Simon
author_rank: Unknown
timestamp: November 15, 2015, 10:09:45 pm
content: Quote from: neslekkim on November 15, 2015, 09:48:11 pmI don't care how much he helps and contributes, it does not help me as a moderator or the feeling on the forum and it sets a bad example, remember "bored at work". He has done enough shit stirring in the past to warrant permanent banning so unless Dave has views to the contrary one more sarky comment and he's out! We have had enough collateral from Dannyf for a lifetime thank you!

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 16, 2015, 10:11:03 pm
content: Quote from: Kalvin on November 15, 2015, 02:59:51 pmThanks Kalvin and everyone else for your suggestions and help. As Kalvin (and a few others) pointed out, I had screwed up the interrupt routine by not clearing the interrupt flag (I had misread somewhere that this happens automatically in the HAL documentation).So this was my final interrupt routine:void TIM3_IRQHandler(void){//if more than 1 timer is used with interrupts enabled, we have to manually check which interrupt flag has been set//use __HAL_TIM_GET_FLAG in this caseHAL_GPIO_TogglePin(GPIOC, LD4_Pin); //toggle LD4delay_ms(250); //wait 250 ms__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE); //clear interrupt flag}I also replaced the HAL_Delay() with a loop-based version of my own (thanks to Yansi for pointing out that HAL_Delay() uses SysTick; I figured I won't use it anymore since it could create more problems in the future). As a safeguard, I did fix the priorities for both Timer-3 & SysTick as well.Thanks a lot guys

author: newbrain
author_rank: Super Contributor
timestamp: November 17, 2015, 11:48:15 am
content: Quote from: Udayan Sinha on November 16, 2015, 10:11:03 pmYou probably did not misread, the HAL can clear the interrupt flag for you, but it must have a chance to do it.Since you are writing your own TIM3 IRQ handler, the HAL IRQ handling for the timer is never invoked.The routine in HAL (I did not check the specific one for L1xx, but they are all similar) will call a callback function you can define according to the specific interrupt (e.g. period, output compare etc.), but it must be invoked inside the IRQ handler:Code:void TIM3_IRQHandler(void){HAL_TIM_IRQHandler(htim3)}in your case.Your code seems generated by CubeMX, if you had specified that TIM3 would use interrupt this would have happened automatically (and you'd have learned less!), and you'd be left with filling in:Code:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){// Your code here}Is it worth doing it your way or the HAL way?In this case, writing your own handler seems simpler, in others (when coping with several different kinds of interrupts) one would end up writing something quite similar to what is already in the HAL (check flags, dispatch and clear) so, provided the HAL is already in use...why not?Another note: why keep the busy loop delay inside the handler, now that everything is working?With a reasonable prescaler and period, one can get almost any rate from the timer, and any duty cycle using the PWM/OC capabilities.

author: Udayan Sinha
author_rank: Regular Contributor
timestamp: November 17, 2015, 07:13:33 pm
content: Quote from: newbrain on November 17, 2015, 11:48:15 amI tried with HAL_TIM_PeriodElapsedCallback() initially, but it didn't work. Do you mean to say that we can configure in STM32Cube if we want to use interrupts for a peripheral or not? I don't remember seeing any such settingAs for the loop delay, like I mentioned in a previous post, it was just for testing to see if everything works fine. If I did not use the delay, I would be merely dimming my LED with a PWM signal; my LED was already very bright so I couldn't really tell the difference much. Or i may just have bad eyesight