STM32之TIM通用定时器


本文介绍如何使用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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM