stm32定时器(TIM)学习笔记
定时器本质上就是一个计数器:
现在小明与小红在一起玩游戏,小明每隔一秒从1数到100,小明在每数两个数之后小红会数一次,但是小红数到5之后又会从零开始数。
例如,小明数从1数到12后,小红会从1数到5之后归零从1开始数。
image-20240315125519932
在这个游戏中,小明就是输入的时钟,小红就是计数器。
Part.1 TIM定时中断
1.时基单元
image-20240315130139425
- 计数器CNT :当有时钟源输入时,边开始计数。
- 预分频器PSC
:对于输入的时钟源,选择是每隔几个数计一次数。
- 自动重装寄存器ARR:
当计数器计到最大值时,清零计数器。
2.定时器类型
image-20240315125853606
stm32F103C8T6仅仅有TIM 1~4。
3.计数模式
image-20240315131747682
对于基本计时器,仅仅只有向上计数这一种计数模式。
而通用定时器与高级定时器有向上计数、向下计数、中央对齐的计数模式。
4.
image-20240316115753789
5.代码实现
5.1 TimerInit
Step 1. 因为Tim2在APB1上,所以开启APB1的外设时钟:
1
| RCC_APB1PeriphClockCmd(RCC_APB1Periph_Tim2,ENABLE);
|
Step 2.选择输入的时钟源,这里有这么几个函数可供选择:
1 2
| void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
|
image-20240316121607585
1 2
| void TIM ITRxExternalclockCoffig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
|
image-20240316121835611
1 2 3 4 5
| void TIM_TIxExternalclockConfig(TIM TypeDef* TIMx, uint16_t TIM TIxExternalCLKSource uint16_t TIM ICPolarity, uint16 t ICFilter);
|
image-20240316122142003
1 2 3
| void TIM ETRClockModelConfig(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler, uint16 t TIM E uint16 t ExtTRGFilter);
|
image-20240316122220445
1 2 3
| void TIM ETRClockMode2Config(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler, uint16 t TIM ExtTRGPolarity, uint16 t ExtTRGFilter);
|
image-20240316122306004
1 2 3
| void TIM ETRConfig(TIM TypeDef* TIMx, uint16 t TIM ExtTRGPrescaler, uint16 t TIM ExtTRGPolar uint16 t ExtTRGFilter);
|
由于我们这里选择使用内部时钟,所以直接输入如下代码即可
1
| TIM_InternalClockConfig(TIM2);
|
Step 3.时基单元的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
|
5.2 TIM_ITconfig
1
| TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
|
使用该命令可以开启定时器中断
5.3 NVIC的配置
1 2 3 4 5 6 7 8 9 10 11 12
| NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure);
|
5.4 TIM_Cmd
打开TIM
5.5 配置中断函数
每个中断函数都不能由自己定义,都是由stm32自己定义的。如果想要编写中断函数,我们需要做的是查询到这个函数的名称,然后继续写入这个函数。
1 2 3 4 5 6
| void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }
|
全代码:
Timer.c文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include "stm32f10x.h"
void Timer_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_InternalClockConfig(TIM2); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2, ENABLE); }
|
Tim.h文件
1 2 3 4 5 6 7
| #ifndef __TIMER_H #define __TIMER_H
void Timer_Init(void);
#endif
|
main.c文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "Timer.h"
uint16_t Num;
int main(void) { OLED_Init(); Timer_Init(); OLED_ShowString(1, 1, "Num:"); while (1) { OLED_ShowNum(1, 5, Num, 5); } }
void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { Num ++; TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }
|
这里需要注意,我们所引用的Num变量在两个文件中出现过,这种情况要么是在另外一个文件中加上extern
int num,要么是把中断函数移动到main.c中。
此外,在初始化TIM后,程序会立即启动TIM的中断一次,为了防止这一现象,应该加上clearflag这句话。