title: STM32——外设Timer定时器

  date: 2020-05-20 14:43:5 telegram官方下载2

  tags:

  categories: STM32学习记录

  先来看看这种MCU有多少定时器:

  STM32定时器分类

  定时器分为3类:

  基本定时器的功能最少,只能充当基本的时基,甚至都没有外部引脚。通用定时器拥有基本定时器的全部功能,同时有输入捕获模式,用以接收外部的PWM,脉冲之类的信息,也有高级定时器又有通用定时器的全部功能,又有互补输出模式,功能最为强大

  先具体看一下夹在中间的通用定时器的官方文档中的描述:

  

  为什么需要定时器,并且需要这么多定时器呢?这是因为STM32的处理器是一种单线程的模式,这这时如果没有一个专门的外设,那么在软件定时期间就无法处理其他的工作。于是ST就提供了这些可以独立工作的定时器来完成需要的定时工作。

  定时器的心脏:时基

  1、内部时钟(CK_INT)

  2、外部时钟模式 1:外部输入脚(TIx)

  3、外部时钟模式 telegram的的官网的最新地方在哪里 2:外部触发输入(ETR)

  4、内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。 这些时钟,具体选择哪个可以通过telegram的中文是多少 TIMx_SMCR 寄存器的相关位来设置。

  定时器时基

  在这里呢,最基本的定时功能就是功能1,如上图,基本定时器和通用定时器是挂载在APB1低速总线的,和PCLK共用来自APB1预分频器的时钟源,这个总线的最大频率是36M,但是由于预分频器分频倍数是1,所以在这里就由系统自动给双倍频telegram官网下载方法在哪呢率了,因此基本定时器和通用定时器的时钟频率都是72MHz的。

  原理

  这个功能就和传统8051单片机的定时器功能一样,计数,溢出,触发中断,简单明了,多了个自动重装载功能,应该是最简单的定时器功能了,甚至基本定时器都能够完成,我就从这里开始学习定时器吧。

  和GPIO,串口,外部中断一样,创建并添加src文件和inc文件:

  TIMER.h

  TIMER.c

  首先是初始化程序,一切是那么熟悉,从GPIO起,我们都使用HAL库提供的初始化结构体来初始化外设。

  追究底层原理的话,这些给结构体成员赋值的过程中,主要就是在操作CR1,CR2控制寄存器,完成定时器最基本的设置基础计时频率的过程,经过初始化函数上,就完成了这些配置。

  CounterMode:TIM_COUNTERMODE_UP TIM_COUNTERMODE_DOWN下

  Channel:用来设置活跃通道。每个定时器最多有四个通道可 以用来做输出比较,输入捕获等功能之用。这里的 Channel 就是用来设置活跃通道的,取值范 围为:HAL_TIM_ACTIVE_CHANNEL_1~ HAL_TIM_ACTIVE_CHANNEL_4。

  这里呢,和串口类似,在时基初始化函数中,会调用一个__WEAK弱定义过的空函数:

  这样,初始化才正式完成,然后是中断向量表中的中断服务函数:

  又是熟悉的感觉,直接调用HAL库的处理函数,我们按照惯例,进去看看:

  HAL库定时器中断处理函数

  这个函数居然有300多行,不过还好,机智的我把它折叠起来了,如果忽略具体实现细节,这个其实很清晰了,这个函数的作用是,通过判断定时器的寄存器,判断触发中断的到底是什么事件,是捕获比较1234,还是定时器更新事件,还是输入捕获事件,然后调用相应的回调函数。

  在这里我写的是一个闪灯程序(我在前面包含了LED.h头文件)

  在主程序中初始化:

  可以看到初始化的两个重要参数:

  自动重载值:指的是计数多少次自动重装,16位寄存器储存这个值,范围是0 – 65535,这个是向上计数,就是0到arr就会触发重装中断事件时钟预分频数:指的是相对于时钟基础频率(这里是72MHz),的分频数,这个也很强大,也是16位寄存器储存这个值,范围仍然是0 – 65535。

  专门有2个寄存器:自动重载寄存器 预分频寄存器,看看官方的公式:
telegram官网的最新的下载的方法是什么
  自动重载寄存器 预分频寄存器
telegram的官方下载方法在哪里
  f_CK_PSC就是时钟频率,这里是2倍频后的APB1时钟,72MHz。

  再来看看这个初始化函数:

  72e-6 / (7199+1) = 1e-4 (s) 就是0.1ms的计数间隔。计数范围是0 – 9999,一共1e4一万个计数次数,相当于1s一次,触发中断,自动装填,重新运行。

  ### 原理

  先来看看原理,怎么输出占空比不同的,稳定频率的PWM波(节选自正点原子):

  PWM比较示意图

  假定定时器工作在向上计数 PWM 模式,且当 CNT=CCRx 时输出 1。那么就可以得到如上的 PWM 示意图:当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候, IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环telegram官网的最新下载地址是多少。 改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。

  前面也看到了,只有基本定时器是没有外部引脚的,不能作为输出,其他的定时器都可以输出PWM波,这里以通用定时器作为工具,操作单片机输出PWM波。

  除了实践1中用到的寄存器,还有3种和捕获/比较有关的寄存器:2个配置用的模式寄存器TIMx_CCMR1/2

  TIMx_CCMR1/2

  4个工作时用的状态寄存器TIMx_CCR1/2/3/4,其实这里只用到一个,因为通用定时器有4个通道,一个通道对应一个这种寄存器,列出来一个看看

  TIMx_CCER

  1个使能寄存器TIMx_CCER

  TIMx_CCR1/2/3/4

  可以看到每两位控制一个通道,然后隔一个没用的保留位。

  然后前面我们了解到除了基本定时器,其他的定时器都是有对应引脚的,这就是重映射复用功能:

  TIM3复用功能重映射

  PWM.h

  PWM.c

  这一篇属于PWM的应用方面:节选自我的另一篇博客

  MOTOR.h

  这里干了几件事:

  电机控制引脚宏定义

  PWM占空比改变宏定义

  两个初始化函数宏定义

  MOTOR.c

  首先是对4个控制引脚的初始化,高速下拉推挽输出:
telegram的官网的最新的下载的地址在哪
  然后是对PWM外设的初始化,这里用定时器1的1、4通道,映射到PA8和PA11两个引脚用来控制两个电机的转速:

  在HAL_TIM_PWM_Init()中会调用端口初始化MSP函数,需要用户重定义覆盖HAL库的若定义函数:

  然后是中断服务函数,等等,我们设置了自动重装填,不需要中断控制,OK结束。

  定时器初始化

  us015timer.h

  us015timer.c

  这里由于篇幅因素,只展示定时器的设置,端口设置另一个下拉高速推挽输出,实现对超声波模块的触发即可。

  这个要比输出PWM要复杂一点,主要多了中断服务函数,以及中断回调函数上,因为超声波模块要求在接收到一个上升沿的回声数据之后立刻进行数据的处理,便于在主循环或者其他中断中实现相应的避障操作。

  对于编码电机,以前用51这种功能简单的单片机,只能使用定时器,配合特定的编码器解算算法,才能得到相对精确的电机码盘信息,但是STM32的通用定时器和高级定时器,自带编码器模式,简直是太方便了,如图:

  编码器接口模式

  通过描述,可以看到,我们可以通过配置通用定时器的一个通道,实现单编码器的读取,也可以通过设置一个定时器的12两个通道,相当于硬件四倍频技术,实现高精度的编码器读取。

  编码器模式

  编码器模式的配置

  这一次我使用cubeMX进行配置,使配置过程简单明了:

  编码器cubeMX配置

  GPIO引脚复用为高级定时器TIM1的两个通道CH1,CH2选择定时器1模式为:编码器模式分频:不分频计数值:0xFFFF,取最大,减小循环误差(我认为是这样的)很重要,使用12双通道,进行AB相正交编码器的硬件四倍频。

  生成代码(记得勾选生成独立的h文件和C文件):

  tim.h

  tim.c

  我把原来cubeMX的注释删掉了,省略了MSP的失能函数,在重要的语句后面写了自己的注释:

seo