2012-06-24 09:08 PM
I'm trying to learn how to use the basic timers, without the benefit of a library, so I have been reading the RM0090 reference manual. As a simple test I want to toggle the blue LED on the dev board (pin D15) once per second.
I wrote the code below, but the LED does not come on. The weird thing is when I run a debugger and setup a brakepoint to print out some information, it actually works. If I remove the brake point it stops working. Any ideas? Code: ============================================ #include ''stm32f4xx.h'' #include ''core_cm4.h'' unsigned long i = 0; int main() { RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Enable GPIOD clock GPIOD->MODER |= GPIO_MODER_MODER15_0; // Enable output mode for D15 RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // Enable TIM6 clock NVIC_EnableIRQ(TIM6_DAC_IRQn); // Enable TIM6 IRQ TIM6->DIER |= TIM_DIER_UIE; // Enable interrupt on update event TIM6->PSC = 41999; // Set prescaler to 41999 TIM6->ARR = 999; // Set auto-reload to 999 TIM6->CR1 |= TIM_CR1_CEN; // Enable TIM6 counter while(1) { // do nothing } } void TIM6_DAC_IRQHandler() { i++; GPIOD->ODR ^= (1 << 15); // Toggle D15 TIM6->SR = 0; // Interrupt has been handled } ============================================ Debugger setup: ============================================ $ arm-none-eabi-gdb -silent -ex 'target extended-remote localhost:4242' firmware.elf Reading symbols from /stm32f4/projects/LED Blinking with TIM6/firmware.elf...done. Remote debugging using localhost:4242 Reset_Handler () at startup_stm32f4xx.s:69 69 movs r1, #0 (gdb) break TIM6_DAC_IRQHandler Breakpoint 1 at 0x8000188: file main.c, line 23. (gdb) commands Type commands for breakpoint(s) 1, one per line. End with a line saying just ''end''. >print i >print *(unsigned long*) 0x40020c14 >continue >end (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /stm32f4/projects/LED Blinking with TIM6/firmware.elf Note: automatically using hardware breakpoints for read-only addresses. TIM6_DAC_IRQHandler () at main.c:23 23 i++; (gdb) c Continuing. Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $1 = 1 $2 = 32768 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $3 = 2 $4 = 0 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $5 = 3 $6 = 32768 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $7 = 4 $8 = 0 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $9 = 5 $10 = 32768 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $11 = 6 $12 = 0 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $13 = 7 $14 = 32768 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $15 = 8 $16 = 0 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $17 = 9 $18 = 32768 Breakpoint 1, TIM6_DAC_IRQHandler () at main.c:23 23 i++; $19 = 10 $20 = 0 ^CBreakpoint 1, Quit (gdb) ============================================ -Farrell2012-06-24 10:21 PM
One potential problem here is that you presuppose the register content, and set arbitrary bits without masking. Another perhaps is the state of the alternate function mux.
Debuggers are invasive, they create an environment that suits them, and it won't look exactly like a normally configured/running chip.2012-06-26 09:12 PM
I fixed part of the problem. If I check that TIM6->SR != 0 in my ISR the timer works.
Working ISR: =========================================== void TIM6_DAC_IRQHandler() { if(TIM6->SR) GPIOD->ODR ^= (1 << 15); // Toggle D15 TIM6->SR &= ~1; // Interrupt has been handled } =========================================== But it seems odd that I would need to. The ISR should only be called when the count overflows or reaches the auto-reload value, right? I wonder if my ISR is getting called when the counter is incremented, not just on an ''update event.'' -Farrell