坚持就是胜利

  • 一、定时器Timer介绍
  • 01 TIM简介
  • 02 定时器类型
  • 03 定时器基本结构
  • 04定时器中断基本结构
  • 05 定时器工作过程
  • 二、HAL库小试定时器
  • 01 题目要求
  • 02 配置CubeMX
  • 03 配置Keil
  • 04 结果展示
  • 三、库函数实现定时器计数
  • 四、总结
  • 源代码
  • 参考资料
  • 一、定时器Timer介绍

    01 TIM简介

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源的选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为高级定时器、通用定时器、基本定时器三种类型
  • 02 定时器类型

    STM32F103C8T6定时器资源只有TIM1、TIM2、TIM3、TIM4

    类型 编号 总线 功能
    高级定时器 TIM1 TIM8 APB2 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
    通用定时器 TIM2 TIM3 TIM4 TIM5 APB1 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
    基本定时器 TIM6 TIM7 APB1 拥有定时中断、主模式触发DAC的功能

    03 定时器基本结构

    可以查阅STM32 F10xxx中文参考手册,理解三种定时器不同的寄存器结构

    1. 高级定时器

    2. 通用定时器

    3. 基本定时器

    04定时器中断基本结构

    05 定时器工作过程

    第一部分首先看到psc预分频器需要接收时钟源,而时钟源共有四种来源,分别如下:

  • RCC寄存器中的APB1外设时钟使能寄存器,经过倍频之后输出时钟源,这是因为该寄存器的位0到位5分别表示的是定时器2到定时器7的使能位。

  • 外部触发引脚TIMx_ETR的外部触发输入ETR,对应的引脚可以通过查数据手册得到。ETR经过分频得到ETRP,在经过滤波得到ETRF作为时钟信号。

  • 内部触发输入(ITRx),来自其他的定时器的时钟,即将,经过后面的选择器,进入到触发控制器。

  • 外部输入引脚Tix,这个主要来自于TIMx_CHx (四个通道)。

  • 第二部分为时基单元,包括PSC预分频器、自动重装载寄存器和CNT计数器。首先由第一部分产生时钟源,进入PSC预分频器进行分频处理,得到新的时钟信号CK_CNT,使得CNT计数器加1或者减1,此时在自动重装载寄存器中有一个预先设定的装载值,当计数器的值达到装载值的时候,会产生溢出事件,然后触发中断。

    第三部分为输入捕获,TIMx_CH1——TIMx_CH4 这四个通道,在芯片中都有对应的引脚,当脉冲从通道口进入时,经过输入滤波器(抗干扰的作用),然后经过边沿检测器检测到上升沿(下降沿),经过分频器,输入到捕获寄存器中,然后捕获寄存器记录此刻CNT计数器的值,当下一次下降沿(上升沿)过来时,也记录下CNT计数器的值,这样就可以计算出输入脉冲的宽度。

    第四部分为输出比较(注意输入捕获和输出比较不可以同时进行),比如在比较寄存器中预先设定一个值,计数器从初始值到装载值之间计数时,当正好等于比较寄存器中的预设值时,控制TIMx_CH1——TIMx_CH4通道输出低电平或者高电平,这样随着计数器不断的计数,就可以获得一个脉冲,通过调整预设值,就可以调整脉冲宽度,调整初始值和装载值就可以调整周期。

    二、HAL库小试定时器

    01 题目要求

    之前的延时功能都是通过循环、delay/Hal_delay函数等实现,本次通过定时器Timer方式实现时间的精准控制,相当于给CPU上了一个闹钟,CPU平时处理其它任务,当定时时间到了以后,处理定时相关的任务。请设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁。

    02 配置CubeMX

    1. 配置RCC为高速时钟,在菜单栏中选Crystal/Ceramic Resonator

    2. 配置SYS,菜单栏中选Serial Wire

    3. 配置端口输出,选择PA1作为LED灯的输出

    4. 配置定时器TIM2和TIM3其次

    注意:分频系数虽然是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟一般会配置为72MHZ,所以72分频后得到1MHZ的时钟;1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒;也就是每隔0.005秒定时器2会产生一次定时中断

    1. 配置定时器中断配置中断优先级

    2. 配置USART1,异步通信Asynchronous

    3. 配置时钟树

    4. 生成项目

    03 配置Keil

    1. 启动定时器的代码,“h”表示HAL库,“tim2”表示定时器2。所以这行代码的意思就是启动定时器2和定时器3。

    2. 添加串口输出变量数组为全局变量,方便在后续回调函数中输出

    3. 配置定时器中断回调。该函数为定时器的中断回调函数,当产生定时中断的时候,会自动调用这个函数。在函数内部定义了定时器的一个静态变量:time_cnt与定时器3 的time_cnt3。回调函数如下:

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    	static uint32_t time_cnt =0;
    	static uint32_t time_cnt3 =0;
    	if(htim->Instance == TIM2)
    	{
    		if(++time_cnt >= 400)
    		{
    			time_cnt =0;
    			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
    		}
    	}
    	if(htim->Instance == TIM3)
    	{
    		if(++time_cnt3 >= 1000)
    		{
    			time_cnt3 =0;
        HAL_UART_Transmit(&huart1,hello,20,100000);
    		}
    			
    	}
    }
    

    例如time_cnt,当它大于等于100的时候,才会执行if里面的代码。也就是说需要发生400次中断,才会让LED的状态翻转。前面已经算过了,一次定时中断的时间是0.005秒,所以400次中断的时间是0.005400=2秒。也就是说每隔2秒,LED的状态翻转一次。
    例如time_cnt3,当它大于等于1000的时候,才会执行if里面的代码。也就是说需要发生1000次中断,才会让串口发一次消息。0.0051000=5秒,符合题目要求。

    04 结果展示

    2s转换灯的状态

    5s串口通信展示

    三、库函数实现定时器计数

    笔者使用B站UP主江科大自化协实现一秒计时的功能,但是没有配置定时器中断,而在主函数循环中对计数的变量进行判断,但是无法完成间隔五秒串口发送信息。具体函数如下。

    	while (1)
    	{
    		//OLED_ShowNum(1, 5, Num, 5);OLED输出Num的值
    		if(Num % 2 == 0)
    			LED1_ON();
    		if(Num % 4 == 0)
    			LED1_OFF();
    		
    		if(Num % 5 == 0)
    			Flag = 1;	
    		while(Flag == 1 ){
    			Flag2 = 0;
    			}
    	}
    

    Num在中断函数中执行Num++操作,笔者在和队友讨论之后认为是硬件在实现过程中在主函数多次循环,当Num由5变为6的时候,主函数执行了很多次串口通信的程序,所以定时器中断是不能没有的。这就是硬件编程和软件编程的很大区别。

    四、总结

    完成定时器中断学习了库函数和HAL库两种操作,这部分的内容有点繁杂,需要我们在之后中多多练习使用,才能加深理解,记得更牢固。如果没有自己动手操作,那么我们学到的知识也就会更少。多学多练才能完成更好。
    非常感谢同学的解答和B站的优秀教程,给了我很大的帮助

    源代码

    1. Gitee仓库地址,HAL库源码
    2. Gitee仓库地址,库函数源码(有小问题)

    参考资料

    1. CSDN-定时器工作原理
    2. 优秀的HAL库定时器操作
    3. 江科大自化协B站教程
    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32配置TIM定时器计数

    发表评论