当前位置: 首页 > news >正文

【STM32】定时器

定时器就像Qt的QTimer,还是硬件级的,超好用。不过有一说一,基本定时器更符合定时器的定义,通用定时器和高级定时器的作用已经不是“定时器”三个字可以概括的了。

大部分图片来源:正点原子HAL库课程

 专栏目录:记录自己的嵌入式学习之路-CSDN博客


目录

1    定时器类型

2    计时器的几种溢出模式

3    芯片定时器参数及性质查询

4    关于计数器计数值的计算

5    常用公共函数

5.1    定时器计数器清零

5.2    获取计数器当前值

5.3    开启中断

5.4    启用xx功能及其对应中断

5.5    启用xx功能但不启用其对应中断

5.6    清除标志位

6    基本定时器

6.1    特性

6.2    原理

6.3    溢出时间计算方法

6.4    配置步骤

6.5    用到的函数

6.6    注意事项⚠️

7    输出比较模式

7.1    PWM1/PWM2模式

7.2    翻转模式

8    通用定时器

8.1    特性

8.2    原理

8.3    配置PWM输出模式

8.4    配置PWM输出模式相关函数

8.5    输入捕获

8.6    输入捕获配置步骤

8.7    脉冲计数

8.8    脉冲计数配置步骤

9    高级定时器

9.1    特性

9.2    重复计数器

9.3    输出指定个数的PWM

9.4    输出比较模式配置步骤(输出翻转模式实验)

9.5    带死区控制的互补输出

9.6    带死区控制的互补输出配置步骤

9.7    刹车(断路)功能

9.8    PWM输入模式

10    公共注意事项

10.1    BASE_Init和功能_Init之间的MsInit矛盾

10.2    HAL_TIM_IC_Start和HAL_TIM_IC_Start_IT的区别

10.3    while等待状态变化和中断回调不要同时使用


1    定时器类型


2    计时器的几种溢出模式


3    芯片定时器参数及性质查询

芯片资料文档,如:《STM32F103ZET6.pdf》


4    关于计数器计数值的计算

  • 计数次数实际上是计数器值+1,因为计数器是从0开始计数的。我的理解是它计的第一次是0,后面是1,所以实际计数次数才是要加一的;
  • 关于PWM占空比的计算,由于占空比是由ARR和CCR共同决定的,而计算式子中ARR总是会被+1,同理计算时CCR应当也要加一,因此设置占空比计算一般都是这样的:
    • 占空比 = (CRR+1) / (ARR+1)

5    常用公共函数

5.1    定时器计数器清零

__HAL_TIM_SET_COUNTER(定时器句柄地址, 0);

5.2    获取计数器当前值

__HAL_TIM_GET_COUNTER(定时器句柄地址);

5.3    开启中断

__HAL_TIM_ENABLE_IT(定时器句柄地址, 中断类型);

5.4    启用xx功能及其对应中断

HAL_TIM_xx_Start_IT (定时器句柄, 通道);

5.5    启用xx功能但不启用其对应中断

HAL_TIM_xx_Start (定时器句柄, 通道);

5.6    清除标志位

__HAL_TIM_CLEAR_FLAG(定时器句柄, 标志位类型);

常见用于清除比较捕获的标志位:

__HAL_TIM_CLEAR_FLAG(&tim_ic_handle, TIM_FLAG_CC2);


6    基本定时器

        TIM6/TIM7

6.1    特性

  • 16位递增计数器(0~65535)
  • 16位预分频器(1~65536)
  • 可用于触发DAC
  • 在更新事件(计数器溢出)时,会产生中断/DMA请求
  • 并不与具体的IO绑定,只能用作普通的计时器(时基),就像Qt里面的QTimer

6.2    原理

  • 16位递增计数器
  • 时钟源:内部RCC时钟的TIMx_CLK
  • 当CNT计数器的值等于重载影子寄存器的值时,发生溢出。溢出后可产生:①更新事件(默认产生);②中断和DMA输出(默认不产生)。

        其具有一个影子寄存器的概念,即图上的影子,自动重装载寄存器和PSC寄存器都有其对应的影子寄存器。它实际上是真正起作用的寄存器,需要有更新事件后才会将其原生寄存器的值加载至影子寄存器中(除非将ARPE位设置为无缓冲,那就设置了原生就直接加载到ARR寄存器)。我个人觉得可以理解为:新的配置需要在原有定时器结束后才会生效

        影子寄存器开启缓冲的作用:假设需要LED灯先亮1秒再灭2秒,那要是等一秒后再让它灭,再修改ARR寄存器,那就会存在一定的误差。而如果开启ARR的缓冲,就可以在等待1秒期间就设置后面的2秒,等1秒完毕后它自己就自动配置了。

6.3    溢出时间计算方法

6.4    配置步骤

6.5    用到的函数

6.6    注意事项⚠️

  • TIM句柄中需要关注的结构体成员:TIM_TypeDef、TIM_Base_InitTypeDef
  • 对于基本定时器来说TIM_Base_InitTypeDef结构体中的计数模式CounterMode、时钟分频因子ClockDivision、重复计数器寄存器RepetitionCounter都是无效的
  • 其中AutoReloadPreload就是是否启用预装载,即是否使用影子寄存器的意思,启用了就有一个对ARR寄存器的缓冲作用,不启用就一修改就直接改变其影子寄存器;
  • 使用TIM时外设需要导入的除了stm32f1xx_hal_tim.c外,还有stm32f1xx_hal_tim_ex.c,否则会编译失败。

7    输出比较模式

PWM有八种模式

  • TIM_OCMODE_TIMING:冻结,输出比较不起作用
  • TIM_OCMODE_ACTIVE:当计数值为比较/捕获寄存器值相同时,强制输出为高电平
  • TIM_OCMODE_INACTIVE:当计数值为比较/捕获寄存器值相同时,强制输出为低电平
  • TIM_OCMODE_TOGGLE:当计数值与比较/捕获寄存器值相同时,翻转输出引脚的电平
  • TIM_OCMODE_PWM1:向上计数时,当TIMx_CNT < TIMx_CCR*时,输出电平有效,否则为无效;向下计数时,当TIMx_CNT > TIMx_CCR*时,输出电平无效,否则为有效
  • TIM_OCMODE_PWM2:与PWM1相反
  • TIM_OCMODE_FORCED_ACTIVE:强制为有效电平
  • TIM_OCMODE_FORCED_INACTIVE:强制为无效电平

7.1    PWM1/PWM2模式

  • PWM1和PWM2,其实就是什么时候输出有/无效电平的差别;
  • PWM的占空比由CCRx决定。
     

7.2    翻转模式

  • 关于周期
    • 从上图可看出一个周期计数为2*(ARR+1)次,而计数一次的时间为(PSC+1)/Ft,因此周期为Ft/(2×(ARR+1)×(PSC+1) ),也就是说PWM的周期是溢出周期的二分之一。
  • 关于占空比
    • 占空比是50%,只要是反转模式就是50%
  • 关于相位
    • 相位由CCR决定

8    通用定时器

        TIM2/TIM3/TIM4/TIM5

8.1    特性

  • 16位递增、递减、中心对齐计数器(0~65535)
  • 16位预分频器(1~65536)
  • 可用于触发DAC和ADC
  • 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
  • 4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
  • 使用外部信号控制定时器且可实现多个定时器互连的同步电路
  • 支持编码器和霍尔传感器电路等

8.2    原理

        16位递增计数器

  • 时钟源(4个):

        (1)    内部时钟:RCC时钟的TIMx_CLK(APB)

        (2)    外部时钟模式1:TIx(通道1和通道2的引脚信号)【注意⚠️!外部时钟模式1仅CH1和CH2可用】

        (3)    外部时钟模式2:外部IO口复用触发输入TIMx_ETR,每个定时器只有一个ETR引脚功能

        (4)    内部触发输入ITRx:与内部或外部其他定时器进行级联

        内部时钟模式意味着使用内部时钟频率做计数,就像基本定时器一样;外部时钟模式即使用外部的上升沿、下降沿信号来触发计数器的改变

  • 滤波器原理:

        要采样到N次相同的信号才输出一个信号

8.3    配置PWM输出模式

  • 时钟来源:内部时钟;
  • PWM的占空比由CCRx决定。


  • 其中使能通道预装载即启用CCR影子寄存器的缓冲功能,与ARR的预装载功能类似。

8.4    配置PWM输出模式相关函数

  • 需要关注的结构体:TIM_OC_InitTypeDef
     
    • 仅需要关注前三个结构体成员;
    • OCPolarity是用来定义有效电平是高电平还是低电平的;
    • 注意⚠️:赋值OCPolarity的时候千万别写成OCNPolarity

8.5    输入捕获

  • 作用:
    • 测量脉宽
  • 原理:
    • 四个通道皆可用于输入捕获
    • 时钟来源:内部时钟
    • 在检测到上升沿和下降沿时读到捕获寄存器的值,通过两次读取间捕获寄存器的差值(实际上是计数器的差值)以及记一次数所需时间,就可以知道脉宽。

  • 计一个数所花时间:(PSC+1) / Ft 【(分频系数+1)/定时器时钟源频率】

8.6    输入捕获配置步骤


  • 需要关注的结构体:TIM_IC_InitTypeDef

        分频系数:就是设置多少个上升沿/下降沿才产生一次计数;

  • 重点:与基本定时器及PWM输出不同,输入捕获并不是很在意计数器溢出的时间(4.3),其更关注计数频率,计数频率越高得到的脉宽精度才会越高,4.3这条式子就不是用来倒着用来求移出时间了,而是:
    • 先决定计数频率,再决定溢出条件ARR。其中我觉得ARR可以设置为最大值65535,因为其越大,计算脉宽是需要存储的溢出次数就越小;
  • 如果要捕获脉宽,就需要在捕获回调里面写切换的代码,在捕获到上升沿后,将捕获模式改成下降沿检测。
    • 注意⚠️!修改配置前为了保险可先关闭定时器,修改完毕后再打开
    • 注意⚠️!修改配置时必须先清除上一配置!
  • 获取捕获值的函数为:HAL_TIM_ReadCapturedValue

8.7    脉冲计数

  • 原理:
    • 时钟来源:外部时钟模式1,即通道1或通道2的输入。或TIMx_ETR引脚的输入,每个定时器只有一个ETR引脚功能;
    •  特性(外部1):来源于通道1或通道2的输入,有经边缘检测和不经边缘检测两种可选的信号作为时钟源,分别为TIxFPy和TI1F_ED。若为不经检测,则上升沿和下降沿都会分别触发一次计数,若经,就只会触发一个。
    • 特性(外部2):一定会经过边缘检测器,所以无需考虑上面的情况。

8.8    脉冲计数配置步骤


  • 需要关注的函数和结构体
    • HAL_TIM_SlaveConfigSynchro函数,用于配置从模式控制器,使得计数器使用外部时钟;
    • TIM_SlaveConfigTypeDef结构体,用于配置从模式
      • 从模式选择:用于选择外部时钟模式1;
      • 输入触发源选择:可选择TIxFP1(单边)、TIxFP2(单边)、TI1F_ED(双边);
      • 输入触发极性:上升、下降或者双边沿;
      • 预分频:外1模式没有,外2模式才有;
      • 滤波器:选择滤波;

9    高级定时器

        TIM1/TIM8

9.1    特性

  • 16位递增、递减、中心对齐计数器(0~65535)
  • 16位预分频器(1~65536)
  • 可用于触发DAC和ADC
  • 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求
  • 4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式
  • 使用外部信号控制定时器且可实现多个定时器互连的同步电路
  • 支持编码器和霍尔传感器电路等
  • 重复计数器(比通用定时器多的功能)
  • 死区时间带可编程的互补输出,但仅通道1到通道3有互补输出通道,通道4没有(比通用定时器多的功能)
  • 断路(刹车)输入,用于将定时器的输出信号置于用户可选的安全配置中(比通用定时器多的功能)
  • 使用高级定时器的输出功能时,必须将TIMx_BDTR寄存器的MOE位置1,否则无法输出

9.2    重复计数器

高级定时器与普通定时器不同,其可设置为单纯产生溢出时并不产生定时器更新事件,而是几次溢出后才产生一次更新事件。具体的更新事件发出条件由TIMx的RCR寄存器进行设置。如果设置RCR为N,则更新事件将在N+1次溢出时发生。

9.3    输出指定个数的PWM

  • 原理:
    • 因为在边沿对齐模式下,定时器溢出周期对应着PWM周期,我们只要在更新事件发生时,停止输出PWM就行。


  • 关键结构体
  • 需要注意的点
    • 高级定时器需要对其句柄.Init.RepetitinCounter(即重复计数器的初始值)进行设定

9.4    输出比较模式配置步骤(输出翻转模式实验)


  • 需要关注的结构体
    • 依然是只关注前三个就足够了

9.5    带死区控制的互补输出

  • 概念
    • 死区
      • 在死区中,输出通道和互补输出通道的电平都是无效电平;
      • 死区控制的存在是为了解决元器件传输电平过程中造成的延时。以电机控制为例,就是使用互补输出使得下图交叉导通分别实现正传和反转,但是正反转切换过程中由于元器件的延时特性,有可能会造成在某一时刻双边的三极管同时导通导致VCC直接连接到了GND,因此需要人为在电机切换正反转方向时创造一个死区,使得电机是先关闭再切换方向,从而避免了短路烧毁。
    • 注意⚠️
      • 任何时候输出和互补输出都不能同时处于有效电平,这个硬件上就决定了,若软件上同时输出有效电平,则都会变成无效电平;
  • 死区时间计算

9.6    带死区控制的互补输出配置步骤


  • 关键结构体1
    • 这次除了OCFastMode以外全都用上了;
    • 其中最后两个成员就是上文所说的刹车完毕后空闲状态的电平设置;
    • 注意⚠️:上述最后两个不能都是其自身的有效电平,否则会被硬件强制赋值为两个无效电平,就像上文说的一样:“任何时候输出和互补输出都不能同时处于有效电平,这个硬件上就决定了,若软件上同时输出有效电平,则都会变成无效电平”
  • 关键结构体2
    • 寄存器锁定:一般用不到
    • 刹车输入极性:指定高电平是刹车还是低电平是刹车
    • 自动恢复输出使能:允许刹车结束后自动恢复输出
    • 注意⚠️!死区时间DeadTime是DTG寄存器的值,并不是真正的死区时间,真正的死区时间需要通过死区时间计算公式算出来。
  • 值得注意的是,按照例程跑出来的死区时间是在输出信号的下降沿后上升沿前的,如下图,要考虑一下如果要实现上升沿后下降沿前,可能就不是这样了。按我的理解来说,是将输出和互补输出的极性都设置为低电平有效应该就可以实现了。但是也不是那么确定,毕竟没有示波器做实验。⚠️⚠️⚠️因此,后续要使用到死区时间的时候必须弄清楚这个问题才行!!!

9.7    刹车(断路)功能

  • 刹车发生后:
  • 关于刹车互补输出的设置可以看STM32F1XX参考手册的13.4.9,重点是表75,其中关于空闲状态的描述挺有意思,就是刹车后会进入空闲状态,而空闲状态的输出和互补输出电平由OISx和OISxN寄存器来决定。

9.8    PWM输入模式

  • 作用:测量输入PWM波的参数;
  • 测量精度:由计数器工作频率决定,要是计数器选择内部时钟源,而APB对计数器时钟又没有分频,那么测量精度就是1/72000000s(计一次数的时间数值是这样,但是单纯这样表示精度我不知道对不对,我瞎写的)
  • 原理:记录PWM信号的上升沿、下降沿、下一个上升沿的对应计数值,再用它们和计一次数的时间相乘,即可计算其占空比和周期了;
  • 例程中选用的方法:
    • 将IC1和IC2同时映射到TIMx_CH1上;
    • IC1使用上升沿触发,IC2使用下降沿触发;
    • 从模式控制器设置为复位模式,利用TI1FP1的上升沿信号触发。当从模式控制器为复位模式时,TI1FP1的信号会使计数器CNT重新初始化为0(递增计数)或者ARR(递减计数,一般用递增,因为好算);
  • 注意⚠️:
    • 只有通道1和通道2可以用于测量,因为通道3和通道4没有响应的TI1FP1信号用于触发CNT复位;
  • 配置方法:

  • 关键结构体
  • 仅需使用前两个成员;
    • 从模式选择:选复位模式
    • 触发源:TI1FP1或TI1FP2
    • 触发极性:就是用于配置TI1FP1或TI1FP2产生前的上升沿、下降沿检测器
    • 剩下的两个用不到;
  • 注意⚠️:
    • 需要使用定时器的捕获比较中断:TIMx_CC_IRQHandler
    • TIM_IC_InitTypeDef中的ICSelection,意思为选择ICx的映射关系,如IC1映射到TI1上,就选TIM_ICSELECTION_DIRECTTI,如IC2也映射到TI1上,就选TIM_ICSELECTION_INDIRECTTI,索引到其注释可以看到下图所述的内容,即映射关系,根据映射关系选就行了,也是一个分组选取的东西。
    • 在中断处理回调函数中,可通过htim->Channel来判断中断处理函数响应的通道,来区分TI1和TI2,如下图:
    • 获取捕获值:HAL_TIM_ReadCapturedValue

10    公共注意事项

10.1    BASE_Init和功能_Init之间的MsInit矛盾

当HAL_TIM_PWM_Init和HAL_TIM_Base_Init函数先后调用时,后调用的函数的MsInit函数将不被执行,因为外设Init函数中有对外设进行状态判断的代码,只要执行过一次Init,那么其State就不是Reset状态的了,因此就不会执行另一个Init函数的MsInit函数。

注意⚠️:

        对同一个外设执行多次不同层级的Init函数的话,只能在第一个Init函数的MsInit函数中写相关的初始化函数。

10.2    HAL_TIM_IC_Start和HAL_TIM_IC_Start_IT的区别

HAL_TIM_IC_Start是只启动输入捕获,不使能其捕获中断;而HAL_TIM_IC_Start_IT是在启动输入捕获的同时也启动输入捕获的中断,相当于在HAL_TIM_IC_Start后同时调用__HAL_TIM_ENABLE_IT(htim, TIM_IT_CCx)而已。

如果是使用HAL_TIM_IC_Start_IT,那就必须定义其中断处理函数以及回调处理函数了。不然中断产生后,程序会一直中断,无法执行下去,这俩任意少写一个都会这样。

10.3    while等待状态变化和中断回调不要同时使用

标题说可能说不太清楚,这里举一个例子:

假设需要输入捕获一个上升沿,如果你开启了输入捕获的中断,并编写了中断服务函数和输入捕获回调函数(哪怕没有内容,当然公共中断处理函数还是要调用的),而同时你又有一个while循环用来计算输入捕获的产生时间,那么这个while就是不准的了。如下图这样的while,在启用了IC中断后这个函数返回的就不是TPAD_TIMX_CAP_CHY_CCRX值了,而是中间那个CNT。

  • 原因分析:

        在中断产生后,中断服务中的外设公共中断服务函数会将中断的标志位清除,从而使得while循环多次直至满足超时条件。

  • 更严重的后果:

        若在所述的while循环体中,加入了一点耗时的函数,哪怕就是单纯的读取CNT,就有可能造成连超时条件都是难以达成。因为稍微耗时一点点的函数都有可能导致CNT溢出,从而重新计数,极大可能最终导致一直卡在循环中,一直无法满足超时条件而退出。

        因此,假设真的需要添加耗时函数在其中,最好是使能一下TIM的Update事件,从而记录下溢出的次数,再从而记录下真实的累积CNT来进行超时判断。 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 谷歌云AI新作:CROME,跨模态适配器高效多模态大语言模型
  • Python算法工程师面试整理-线性代数
  • 动态规划:从记忆化搜索到递推 打家劫舍
  • Java接口interface(内含练习)
  • 树莓派开发笔记13-树莓派环境下的CSI摄像头实验
  • centos 虚拟机器刚刚安装没有ip地址的问题
  • 微软AI人工智能认证有哪些?
  • ChatGPT不同模型在论文写作中的优势和应用
  • 044—pandas 按组将属性和值转为行
  • GRL CVPR2023图像修复 使用笔记
  • IDM是海外加速器吗 IDM在国内好用吗
  • leetcode 438.找到字符串中所有字母异位词
  • 突破编程:C++中的组合模式(Composite Pattern)
  • linux下搭建MySQL8.0.25一主一从
  • rust 日志记录与跟踪
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • ES6语法详解(一)
  • Java程序员幽默爆笑锦集
  • js ES6 求数组的交集,并集,还有差集
  • leetcode386. Lexicographical Numbers
  • mongodb--安装和初步使用教程
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • PAT A1092
  • text-decoration与color属性
  • VuePress 静态网站生成
  • 包装类对象
  • 老板让我十分钟上手nx-admin
  • 理清楚Vue的结构
  • 如何编写一个可升级的智能合约
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 微信公众号开发小记——5.python微信红包
  • 微信小程序开发问题汇总
  • 我这样减少了26.5M Java内存!
  • 想写好前端,先练好内功
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 鱼骨图 - 如何绘制?
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • ######## golang各章节终篇索引 ########
  • ###STL(标准模板库)
  • #、%和$符号在OGNL表达式中经常出现
  • #define
  • #QT(智能家居界面-界面切换)
  • (Matlab)使用竞争神经网络实现数据聚类
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (回溯) LeetCode 131. 分割回文串
  • (接口自动化)Python3操作MySQL数据库
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (强烈推荐)移动端音视频从零到上手(下)
  • (三)uboot源码分析
  • (五)MySQL的备份及恢复
  • (一) storm的集群安装与配置
  • (一)Linux+Windows下安装ffmpeg