Goal: generating periodic interrupts using the 16-bit TIM3 in the output compare mode (SysTick alternative).
TIM2 - TIM7 are driven by the APB1 timer clock. Refer to this post for more clock-related info.
Note that TIM3_IRQHandler() updates the Output Compare 1 Register value every interrupt.
TIM2 - TIM7 are driven by the APB1 timer clock. Refer to this post for more clock-related info.
#include <stm32f4xx.h> #include "other_stuff.h" #define APB1_FREQ 42000000 // Clock driving TIM3 #define CNT_FREQ 21000000 // TIM3 counter clock (prescaled APB1) #define IT_PER_SEC 2000 // Interrupts per second #define TIM3_PULSE ((CNT_FREQ) / (IT_PER_SEC)) // Output compare reg value #define TIM_PRESCALER (((APB1_FREQ) / (CNT_FREQ))-1) // APB1 prescaler uint16_t current_count = 0; // To sample the counter static void TIM3_Config(void); int main() { TIM_TimeBaseInitTypeDef TIM3_TimeBase; // Time base structure TIM_OCInitTypeDef TIM3_OC; // Output Compare structure TIM3_Config(); TIM3_TimeBase.TIM_ClockDivision = 0; // Not dividing TIM3_TimeBase.TIM_CounterMode = TIM_CounterMode_Up; // Upcounting configuration TIM3_TimeBase.TIM_Period = 65535; // Autoreload value (ARR) TIM3_TimeBase.TIM_Prescaler = TIM_PRESCALER; // Dividing APB1 by 2 TIM_TimeBaseInit(TIM3, &TIM3_TimeBase); // Initializing Time Base structure TIM3_OC.TIM_OCMode = TIM_OCMode_Toggle; // Output compare toggling mode TIM3_OC.TIM_OutputState = TIM_OutputState_Enable; // Enabling the Output Compare state TIM3_OC.TIM_OCPolarity = TIM_OCPolarity_Low; // Reverse polarity TIM3_OC.TIM_Pulse = TIM3_PULSE; // Output Compare 1 reg value TIM_OC1Init(TIM3, &TIM3_OC); // Initializing Output Compare 1 structure TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable); // Disabling Ch.1 Output Compare preload TIM_Cmd(TIM3, ENABLE); // Ready, Set, Go! TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); // Enabling TIM3 Ch.1 interrupts while(1) { //os_sys_init(init_task); // Starting the RTX kernel } } static void TIM3_Config(void) { GPIO_InitTypeDef gpio_C; // GPIOC structure NVIC_InitTypeDef NVIC_TIM3; // NVIC structure RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); // Clocking GPIOC (AHB1 = 84MHz) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // Clocking TIM3 (APB1 = 42MHz) gpio_C.GPIO_Pin = GPIO_Pin_6; // Ch.1 (PC6) gpio_C.GPIO_Mode = GPIO_Mode_AF; // Alternative function gpio_C.GPIO_Speed = GPIO_Fast_Speed; // 50MHz gpio_C.GPIO_OType = GPIO_OType_PP; // Push-pull gpio_C.GPIO_PuPd = GPIO_PuPd_UP ; // Pulling-up GPIO_Init(GPIOC, &gpio_C); // Initializing GPIOC structure GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3); // Routing TIM3 output to PC6 NVIC_TIM3.NVIC_IRQChannel = TIM3_IRQn; // Specifying the channel (stm32f4xx.h) NVIC_TIM3.NVIC_IRQChannelPreemptionPriority = 0; // Only matters for multiple interrupts NVIC_TIM3.NVIC_IRQChannelSubPriority = 0; // Only matters for multiple interrupts NVIC_TIM3.NVIC_IRQChannelCmd = ENABLE; // Enabling global interrupt NVIC_Init(&NVIC_TIM3); // Initializing NVIC structure } void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) // Just a precaution (RESET = 0) { TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // Clear TIM3 Ch.1 flag current_count = TIM_GetCapture1(TIM3); // Get current counter value TIM_SetCompare1(TIM3, current_count + TIM3_PULSE); // Set Output Compare 1 to the new value } }
Note that TIM3_IRQHandler() updates the Output Compare 1 Register value every interrupt.
Very clear
ReplyDeletethank you
where is this other_stuff.h file? what should be in it?
ReplyDeleteFor this application it should be transparent since there are no special defines that require a header anywhere in this application.
Deletecan you modify the program to toggle led13 and led12 i tried with tim4 and it just won't work
ReplyDeleteYou can try this code, it worked for me. Sorry, it is not properly ordered, but it works
Delete
Delete#define green_led GPIO_Pin_12
#define orange_led GPIO_Pin_13
#define red_led GPIO_Pin_14
#define blue_led GPIO_Pin_15
uint16_t PrescalerValue = 0;
uint16_t current_count = 0; // To sample the counter
uint16_t CCR1_Val = 500;
static void TIM4_Config(void);
int main()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
PrescalerValue = (uint16_t) ((SystemCoreClock /2) / 2000) - 1;
TIM4_Config();
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 1000;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_Cmd(TIM4, ENABLE);
TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
while(1)
{
//os_sys_init(init_task);
}
}
static void TIM4_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
GPIO_InitStructure.GPIO_Pin = red_led;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOD, &GPIO_InitStructure);
NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
//current_count = TIM_GetCapture1(TIM4);
//TIM_SetCompare1(TIM4, current_count + CCR1_Val);
GPIO_ToggleBits(GPIOD, red_led);
}
}
Hi,
ReplyDeleteCan you please explain me when will the GPIO out will be at high state and when will it change to low state.
Is it because every time the compare register becomes equal to timer counter, GPIO state will change based on TIM_OCMode value?
Thanks
Abin
Hi,
DeleteCheck out section "18.3.8 Output compare mode" in the STM32F4 reference manual. They explain in detail how it works :)
To make a long story short (at least somewhat short), the pin (PC6) toggles (changes its state) when CNT register (counter driven by the 21MHz clock (follow the blue line in the diagram)) matches the value in the "compare 1 register" which is set to 10500 initially. The value of "compare 1 register" is updated (incremented by 10500) each interrupt :)
The reason it toggles it because of this line of code:
TIM3_OC.TIM_OCMode = TIM_OCMode_Toggle;
Here's an example:
1. Initially CNT is 0 and GPIO is low.
2. CNT starts counting up, 0,1,2,3,4... at 21MHz
3. CNT reaches the value of "compare 1 register" which is 10500 and triggers an interrupt
4. GPIO is high
5. Interrupt code updates the value of "compare 1 register" to 10500 + 10500 = 21000
6. CNT keeps countint up 10500, 10501, 10502, ... at 21MHz
7. CNT reaches the new value of "compare 1 register" which is 21000 and triggers an interrupt
8. GPIO is low
9. Interrupt code updates the value of "compare 1 register" to 21000 + 10500 = 31500
10. CNT keeps counting ...
and so on and so forth :)
Best regards,
Sergey
Nice Tutorial..... Thanks.
ReplyDeleteok I will post it there roo thanks, I am new to this embedded system world and I'm really trying to make sense of the code.
ReplyDelete1 -question since TIM 6 is defined as a basic timer will I still be able to get a square wave by changing the TIM from 3 to 6?
2-could you point out the maths behind the square wave frequency/ I want to modify it to get out 151 HZ, how do I change te prameters?
3-and I am using other pins rather than 6 like pin 9, is there any consideration to take when choosing pins?
1 - Yes, you will still be able to get a square wave.
Delete2 - The math behind the square wave frequency requires understanding of the clock tree a little bit.
You start with a crystal oscillator from which all other clocks are derived as follows:
Crystal oscillator --> PLL --> System clock --> APB Prescaler --> APB1 clock --> Prescaler --> Timer clock
Clock tree can be found here http://00xnor.blogspot.com/2014/01/1-stm32-f4-clock-configuration.html.
Now that you have a timer clock (in my code it is 21MHz), the math is 21MHz / 10500 = 2000 interrupts per second. 10500 is the value of the compare register (it is shown in the diagram above). The PC6 pin is toggled each interrupt (2000 times per second) which results in the 1000Hz square waveform. In your case, you want 151*2 = 302 interrupts per second.
Please note that sometimes it's not possible to get exact frequencies because prescaling is discrete.
3 - For low frequencies (e.g. 151Hz) - no. For high frequencies (e.g. 8MHz) - yes.