2010-03-22 07:15 PM
TIM4 questions - basic
2011-05-17 04:44 AM
By ''callback routine'' do you mean an interrupt handler?
2011-05-17 04:44 AM
Hi Neil,
Yes, interrupt handler. I'm trying to get a real grasp of the timers, so I would like to create ''hello worlds'' showing #1. using the System Ticker - Done #2. Setting Timer4 for a one second interrupt (calling a routine just like I did with the System Ticker) #3. Setting up Timer4 to show PWM2011-05-17 04:44 AM
Hi, did you enable the interrupt in the NVIC?
You have to do 4 tasks, if TIM4 shows activity (which I guess does): 1. select the source of interrupt (I guess overflow is OK) and enable the IT generation for it in TIM4 2. configure priority in NVIC for this source and enable it 3. write the interrupt handler body for TIM4_IRQ_Handler (or whatever routine name you will find in interrupt vector table on the TIM4 location) 4. don't forget to clear the TIM4 OVF flag in the IRQ routine Good luck (or use FW library and some examples ;)2011-05-17 04:44 AM
Hi Putty.
I'll try to answer some of your questions... To understand timer operation you need first to understand basic concepts from CortexM3 buses and clocks. This is not explained by ST datasheets because ARM Cortex M3 manual covers it. Search for ''ARM Cortex M3 Technical Reference Manual'' on Google and you'll find. Take a look at STM32 Reference manual page 108 ''Clock Tree'' figure. First you need to setup your system clock (SYSCLK, in the figure). After that you must choose AHB and APB1 prescaler. Timer4 is sourced by APB1 bus... For example, SYSCLK is 40MHz, AHB prescaler = 1 that gives a 40MHz to AHB bus. APB1 prescaler is 4, which gives a clock of 10MHz to the peripherals atached to it. As noted inside the block sourcing timers clock, this 10MHz is multiplied by 2 for timers. Thus, timer clock base will be 20MHz. Now you can configure your timer. Below is the code I'm using for TIM3 (same parameters for TIM4): RCC_APB1ENR->TIM3EN = 1; //enable clock to timer TIM3_CR1->CEN = 0; //disable timer to begin configuration TIM3_CR1->ARPE = 1; //auto reload counter value TIM3_PSC = 1000; //prescaler. Timer clock == 20MHz / 1000 (50 us) TIM3_ARR = 70; //auto reload value TIM3_CNT = 0; //clear counter value TIM3_DIER->UIE = 1; //enable interrupt on overflow //NVIC configuration (explanation below) setBit(IRQ_CLRPEND0, BIT29); //clear pending interrupt setBit(IRQ_SETENA0, BIT29); //enable interrupt TIM3_CR1->CEN = 1; //enable timer In this example, timer will count up to 70, which gives an interruption every 3.5 ms (70 x 50us). NVIC is the Cortex M3 controller. Almost all interrupts are controlled by NVIC (see Cortex M3 reference manual). Your systick interrupt has worked because it's not controlled by NVIC. Finaly the interruption routine: void TIM3_IRQHandler(void) { TIM3_SR->field.UIF = 0; //clear interrupt flag setBit(IRQ_CLRPEND0, BIT29); //clear NVIC pending interrupt } This is declared in ''stm32f10x_vector.c'' file from ST Library. Also take look at ''stm32f10x_it.c'' and ''stm32f10x_it.h'' files. Take a look there and see if its clear to you. A lot information but I think now you have a direction to folow. Hope this helps.2011-05-17 04:44 AM
Hi Edison and Hagar (and everyone else!)
Well, I can compile (!) but still alittle ''lost'' Here is my program so far... #include ''stdint.h'' #include ''targets/STM32F10x.h'' // LED1 is on PC12 // The button is on PA0 #define SET_BIT(REG, BIT) ((REG) |= (BIT)) #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) #define READ_BIT(REG, BIT) ((REG) & (BIT)) #define CLEAR_REG(REG) ((REG) = (0x0)) #define WRITE_REG(REG, VAL) ((REG) = (VAL)) #define READ_REG(REG) ((REG)) #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) volatile uint32_t flag=1; // init's the LED on RA12 void initLED(void) { // configure LED ports RCC_APB2ENR |= RCC_APB2ENR_IOPCEN; uint32_t pin = 4; uint32_t mode = (uint32_t)0x05 << (pin * 4); uint32_t mask = (uint32_t)0x0f << (pin * 4); uint32_t temp = GPIOC_CRH & ~mask; GPIOC_CRH = temp | mode; } // crossworks handler name void TIM3_IRQHandler(void) { if(flag==1) { GPIOC_BSRR = (1<<12); // turn on LED flag=0; } else { GPIOC_BRR = (1<<12); // turn off LED flag=1; } CLEAR_BIT(TIM3_SR, TIM3_SR_UIF_MASK); SET_BIT(Irq_0_to_31_Clear_Pending, 1 << 29); } // I'm running at 72MHz // I want a one second ''beat'' void initTim3() { SET_BIT(RCC_APB1ENR, RCC_APB1ENR_TIM3EN); CLEAR_BIT(TIM3_CR1, TIM3_CR1_CEN_MASK); SET_BIT(TIM3_CR1, TIM3_CR1_ARPE); WRITE_REG(TIM3_PSC, 1000); WRITE_REG(TIM3_ARR, 70); WRITE_REG(TIM3_CNT, 0); SET_BIT(TIM3_DIER, TIM3_DIER_UIE); SET_BIT(Irq_0_to_31_Clear_Pending, 1 << 29); SET_BIT(Irq_0_to_31_Set_Enable, 1 << 29); } void main(void) { initLED(); initTim3(); while(1) { } } When I run it, the LED stay on and that's it! Booooooo.... I know I need to adjust some numbers because I'm running at 72MHz so my PCS and ARR values need adjusting... Any [more] pointers would be gratly appreciated!2011-05-17 04:44 AM
oops, forgot to enable the timer! DOH! Still does not work...boo...
#include ''stdint.h'' #include ''targets/STM32F10x.h'' // LED1 is on PC12 // The button is on PA0 #define SET_BIT(REG, BIT) ((REG) |= (BIT)) #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) #define READ_BIT(REG, BIT) ((REG) & (BIT)) #define CLEAR_REG(REG) ((REG) = (0x0)) #define WRITE_REG(REG, VAL) ((REG) = (VAL)) #define READ_REG(REG) ((REG)) #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) volatile uint32_t flag=1; // init's the LED on RA12 void initLED(void) { // configure LED ports RCC_APB2ENR |= RCC_APB2ENR_IOPCEN; uint32_t pin = 4; uint32_t mode = (uint32_t)0x05 << (pin * 4); uint32_t mask = (uint32_t)0x0f << (pin * 4); uint32_t temp = GPIOC_CRH & ~mask; GPIOC_CRH = temp | mode; } // crossworks handler name void TIM3_IRQHandler(void) { if(flag==1) { GPIOC_BSRR = (1<<12); // turn on LED flag=0; } else { GPIOC_BRR = (1<<12); // turn off LED flag=1; } CLEAR_BIT(TIM3_SR, TIM3_SR_UIF_MASK); SET_BIT(Irq_0_to_31_Clear_Pending, 1 << 29); } // I'm running at 72MHz // I want a one second ''beat'' void initTim3() { SET_BIT(RCC_APB1ENR, RCC_APB1ENR_TIM3EN); // enable clock to timer CLEAR_BIT(TIM3_CR1, TIM3_CR1_CEN_MASK); // disable tim3 SET_BIT(TIM3_CR1, TIM3_CR1_ARPE); // auto reload counter value WRITE_REG(TIM3_PSC, 1000); // set PSC WRITE_REG(TIM3_ARR, 70); // set ARR WRITE_REG(TIM3_CNT, 0); // clear counter value SET_BIT(TIM3_DIER, TIM3_DIER_UIE); // enable int on overflow SET_BIT(Irq_0_to_31_Clear_Pending, 1 << 29); // clear pending int SET_BIT(Irq_0_to_31_Set_Enable, 1 << 29); // enable int SET_BIT(TIM3_CR1, TIM3_CR1_CEN); // enable tim3 } void main(void) { initLED(); initTim3(); while(1) { } }2011-05-17 04:44 AM
Well, I don't like your code style, but it's my problem, not yours :)
Basically, if your systems runs at 72 MHz, you get overflow every ~1 ms (not precisely calculated division factors). Can you see the update flag turned on? If it sets repeatebly, you'll need to check settings of NVIC then. Do you have in your IDE some option to check content of NVIC and TIM3 registers in a more structured way?2011-05-17 04:44 AM
This code works (tested):
void main(void) { RCC->APB1ENR |= RCC_APB1Periph_TIM3;TIM3->PSC = 1;
TIM3->ARR = 0xFFFF; // TIM3->EGR = 1; TIM3->DIER = 1; TIM3->CR1 = 1;NVIC->ISER[0] |= (1 << TIM3_IRQChannel);
... } void TIM3_IRQHandler(void) { TIM3->SR = 0; }