本文介绍如何使用STM32标准外设库配置并使用定时器,定时器就是设置一个计时器,待计时时间到之后产生一个中断,程序接收到中断之后可以执行特定的程序,跟现实中的闹钟功能类似。与延时功能不同,定时器计时过程中程序可以执行其他程序。最简单直观的应用为定时翻转指定IO引脚。本例程使用通用定时器TIM3,每100ms翻转GPIOB的Pin5输出,如果该引脚外接有LED灯,可以看到LED灯周期性的闪烁。
STM32F103VE系列共有8个定时器,分为基本定时器、通用定时器和高级定时器,其中通用定时器包括TIM2/3/4/5共4个,如果一个定时器不够用,可以启动其他几个定时器。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。
TIM通用定时器分为两部分,初始化和控制。
1. 初始化分两步:通用中断、TIM。
1.1. 通用中断:优先级分组、中断源、优先级、使能
- 优先级分组:设定合适的优先级分组
- 中断源:选择指定的TIM中断源:TIM3_IRQn
- 优先级:设定合适的优先级
- 使能:调用库函数即可
1.2. TIM:时钟、预分频器、定时器周期、分频因子、计数模式、初始化定时器、开启定时器中断、使能计数器。
结构体:
typedef struct{ uint16_t TIM_Prescaler; uint16_t TIM_CounterMode; uint16_t TIM_Period; uint16_t TIM_ClockDivision; uint8_t TIM_RepetitionCounter; } TIM_TimeBaseInitTypeDef;
- 时钟:需要使能定时器时钟
//开启定时器时钟,即内部时钟CK_INT=72M RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
- 预分频器:默认定时器时钟频率为72M,那么预分频器设置为71,那么一次计数为1us
//时钟预分频数为71,则计数器计数一次时间为1us TIM_TimeBaseStructure.TIM_Prescaler = 71;
- 定时器周期:设置为999,那么产生一次定时器中断的时间为1ms
//自动重装载寄存器为999,则产生一次中断时间为1ms TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
- 计数模式:一般选择向上计数模式
// 计数器计数模式,选择向上计数模式 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- 时钟分频因子:一般选择1分频
// 时钟分频因子,选择1分频 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- 重复计数器的值:仅对高级定时器有效,无需设置
- 初始化定时器:调用库函数即可
//初始化定时器 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
- 开启定时器中断
//开启计数器中断 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
- 使能计数器
//使能计数器 TIM_Cmd(TIM3, ENABLE);
2. 处理
2.1. 中断服务函数
定时器TIM3的中断服务函数名称为TIM3_IRQHandler ()。
void TIM3_IRQHandler(void);
2.2. 中断处理
中断服务函数中调用TIM_GetITStatus ()函数判定中断标志位状态以确定中断是否发生,调用TIM_ClearITPendingBit ()函数清除中断标志位。
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); }
2.3. 计时
在定时器中断服务函数中累加一个全局变量,在中断外查询该全局变量,如果达到预期的定时时间,则清除此全局变量,并执行相应的操作。
举例来说,如果想要200ms执行某操作,那么该全局变量累加到200之后,将该全局变量置为0,然后再执行相应的操作。
完整代码(仅自己编写的部分)
1 uint8_t times; 2 3 void GPIO_OutputConfig(void) 4 { 5 //定义一个GPIO_InitTypeDef类型的结构体 6 GPIO_InitTypeDef GPIO_InitStructure; 7 8 //开启指定端口的GPIO外设时钟 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 10 11 //选择要控制的GPIO引脚 12 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 13 14 //设置引脚速率为50MHz 15 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 16 17 //设置引脚模式为通用推挽输出 18 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 19 20 //调用库函数,初始化GPIO 21 GPIO_Init(GPIOB, &GPIO_InitStructure); 22 23 //设置初始状态 24 GPIO_ResetBits(GPIOB, GPIO_Pin_5); 25 } 26 27 //中断优先级配置 28 void TIM_NVICConfig(void) 29 { 30 NVIC_InitTypeDef NVIC_InitStructure; 31 32 // 设置中断组为2 33 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 34 // 设置中断来源为TIM3 35 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ; 36 // 设置抢占优先级为0 37 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 38 // 设置子优先级为3 39 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; 40 // 使能 41 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 42 43 NVIC_Init(&NVIC_InitStructure); 44 } 45 46 //TIM配置 47 void TIM_Config(void) 48 { 49 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 50 51 //开启定时器时钟,即内部时钟CK_INT=72M 52 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 53 54 //时钟预分频数为71,则计数器计数一次时间为1us 55 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; 56 57 //自动重装载寄存器为999,则产生一次中断时间为1ms 58 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; 59 60 // 计数器计数模式,选择向上计数模式 61 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 62 63 // 时钟分频因子,选择1分频 64 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 65 66 // 重复计数器的值,高级定时器有效 67 // TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; 68 69 //初始化定时器 70 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 71 72 //开启计数器中断 73 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); 74 75 //使能计数器 76 TIM_Cmd(TIM3, ENABLE); 77 } 78 79 //定时器初始化函数 80 void TIM_Init(void) 81 { 82 TIM_Config(); 83 TIM_NVICConfig(); 84 } 85 86 //TIM3中断服务函数 87 void TIM3_IRQHandler(void) 88 { 89 if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) 90 { 91 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); 92 times++; 93 } 94 } 95 96 int main(void) 97 { 98 GPIO_OutputConfig(); 99 TIM_Init(); 100 101 //每100ms翻转一次GPIOB pin5 102 while(1) 103 { 104 if(times == 100) 105 { 106 GPIO_SetBits(GPIOB, GPIO_Pin_5); 107 }else if(times >= 200){ 108 GPIO_ResetBits(GPIOB, GPIO_Pin_5); 109 times = 0; 110 } 111 } 112 }
仿真结果
程序编译成功后,点击开始仿真,点击
,显示逻辑分析窗口,点击Setup,显示设置串口,点击新建
,然后输入要查看的端口,可同时查看多个端口,比如要查看PORTB.5,那么输入(PORTB & 0x00000020) >>5,Display type选择Bit,Color中选择合适的颜色,点击Close关闭对话框,点击
运行程序,可在逻辑分析串口看到该端口的波形。通过鼠标滚轮可对显示波形进行缩放。
示例仿真波形如下:
从仿真结果来看,PORTB.5每隔100ms电平会翻转一次,跟程序设计一致,因此TIM定时器有效。
源码下载:(不包括工程文件和库文件)
https://files.cnblogs.com/files/greatpumpkin/TIM_general.rar