Skip to content

定时器

定时器概述

软件定时原理

使用纯软件(CPU死等)的方式实现定时器(延时)功能

这一个是不精准的, 因为函数调用是有一定时间及, 并且stm32有流水线, 语句执行的时间是不精确的

定时器定时原理

使用精准的时基, 通过硬件的方式, 实现定时功能

image-20231201090147291

stm32定时器分类

  • 常规定时器

有三种功能, 定时, 输出比较, 输出捕获, 互补输出

有三种: 基本定时器: 定时

通用定时器: 没有互补

高级定时器: 都有

image-20230717150629149

image-20231201090840167

H7

image-20231201091021022

  • 专用定时器

独立看门狗

窗口看门狗

实时时钟

低功耗定时器

  • 内核定时器

SysTick定时器

溢出时间

image-20231201194745187

基本定时器

特点

16bit, 只能向上计数, 只有TIM6, TIM7

没有GPIO, 是内部资源, 时钟来自PCLK1, 72M时钟16位分频可以实现1-65535分频,

image-20230717151044639

控制器: 复位, 使能, 计数, 触发DAC, 涉及的寄存器是CR1/2, DIER, SR

在计数器(ANT)的值等于重装载寄存器(ARR(影子))的时候会发生溢出

U=>Update更新事件, 除了溢出更新还可以通过UG位进行软件更新, 会把预装载的值加载到影子寄存器

image-20231201192844792

时基单元: 包括预分频器, 计数器, 自动重装载寄存器, 计数时钟CK_CNT = CK_PSC/(PSC+1)

影子寄存器: 起到缓冲寄存器, 有一个缓冲的功能, 用户值->寄存器->影子寄存器->起作用, 不使用的话用户的值写到之后起作用, 使用的话这一周期计数之后起作用, TIMx_CR1:APRE控制

影子寄存器是实际上起作用的寄存器, 并且是不可直接访问的

一般是一个事件以后才会进行写入

计数模式

image-20231201193335520

image-20231201193615116

递增计数模式

image-20231201193726151

递减计数模式

image-20231201193802947

中心对齐模式

通用定时器

特点

F1: TIM2, 3, 4, 5

16位递增, 递减, 中心对齐计数器

16位预分频器

可以用于触发DAC, ADC

在更新事件, 触发事件, 捕获输入, 输出比较的时候会产生DMA请求

四个独立通道, 可以用于输入捕获, 输出比较, 输出PWM和单脉冲模式

可实现外部信号控制定时器以及多个定时器相互连接的同步电路(级联)

时钟

image-20231202115920002

4+5模式: 信号通过外部的引脚输入, 经过滤波以后捕获边沿, 之后通过某一个通道分频以后会产生一个捕获信号, 这一个信号会把CNT计数器的值保存在捕获寄存器里面

5+6模式: 向比较寄存器里面写入比较值, 之后计数器正常的计数, 当两个的值相等的时候(影子寄存器), 之后会产生一个输出参考OC1REF信号到通道, OC1信号会受到输出模式和ETRF(控制输出参考信号)的控制

两个模式是分时复用的关系, 不能同时使用

时钟源

  1. 内部时钟CK_INT, 来自APB总线提供的时钟
  2. 外部时钟模式1, 来自外部的TI1F ED会检测上升沿和下降沿, 产生两个脉冲, 或者是TI1FP1, TI2PF2为单边沿捕获
  3. 外部时钟模式2, 外引脚触发ETR, 来自可以复用为TIMx_ETR的引脚
  4. 内部触发输入(ITRx), 来自其他定时器的TRGO信号

image-20231202193149278

  • 外部时钟

TIMx_SMCR寄存器, 使用内部时钟的时候ECE也要设置为0

image-20231202194333358

  • 外部时钟模式1

当TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿 或下降沿计数。

image-20231202194603955

设置SMCR寄存器的TS位为100, 101, 110的时候选择不同的通道

使用滤波器以后只有在采样的时候连续N次采样的结果是相同的时候才会产生跳变, 否则的话不会发生变化

image-20231202195346212

  • 外部时钟模式2

选定此模式的方法为:令TIMx_SMCR寄存器中的ECE=1 计数器能够在外部触发ETR的每一个上升沿或下降沿计数。

还有一种是把SMS设置为111, TS也是111的时候效果是相同的

image-20231202195425243

ETRP的频率最高是CK_INT频率的四分之一, 外部时钟的输入比较快的时候需要使用预分频

  • 一个时钟为另一个时钟时钟源

image-20231202201742996

MMS会设置产生一个TRGO信号

TS设置为0xx可以选择时钟的来源

image-20231202202010978

输出PWM(输出比较)

image-20231202204229664

image-20231202204909891

image-20231202205029034

ETRF信号是外部引脚的输入信号经过滤波器以后的一个分支

image-20231202205722129

模式

  • PWM模式1

TIMx_CCMRx寄存器的COMx位进行控制,有三位

image-20231202210338177

image-20231202210632464

输入捕获

image-20231202220143648

image-20231202220834030

image-20231202220904796

在读操作完成以后会把捕获的值存放到可以读取的寄存器里面

脉冲计数

定时器有四种输入模式, 这一个需要使用的是外部输入模式1和2

image-20231203185735621

image-20231203185817677

高级定时器

多了重复计数器, 死区时间, 可以编程的互补输出

断路输入, 用于将计时器的输出信号置于用户可选择的安全配置

时钟

image-20231203194526203

image-20231203194843573

重复计数器

image-20231203194955190

输出指定数量的PWM

image-20231203195825105

输出比较

image-20231203210048759

占空比是百分之五十

互补输出带死区控制

image-20231203221140348

互补输出, 两条路的电平相反, 死区时间, 输出之间有一个延迟

  • 应用: H桥

image-20231203221355271

四个都是高电平的时候会导通, 如果输出通道和互补输出通道同时设置为1的时候电源会直接接地

实际的使用的时候不会是这么简单的, 元器件之间的电气特性是有一定的延时的, 所以需要死区时间

image-20231203223514040

image-20231204145141334

刹车短路

image-20231204145334416

image-20231204150110016

image-20231204151019659

image-20231204152428700

image-20231204152447404

image-20231204152355340

PWM输入模式

  • 原理

image-20231204165858442

image-20231204170032988

寄存器

  • TIMx_CR1: 控制寄存器

image-20230717153402120

image-20230717154321210

  • TIMx_CR2
  • TIMx_SMCR: 从模式控制寄存器
  • TIMx_DIER: DMA/中断使能寄存器

image-20231201194349232

  • TIMx_SR: 状态寄存器

image-20231201194421395

  • TIMx_EGR: 事件产生寄存器
  • TIMx_CCMR1: 捕获/比较模式寄存器

image-20230717172716414

image-20230717172857298

  • TIMx_CCMR2
  • TIMx_CCER: 捕获/比较使能寄存器

image-20230717173212170

  • TIMx_CNT: 计数器

image-20230717154825674

  • TIMx_PSC: 预分频器

image-20230717154814765

  • TIMx_ARR: 自动重装载寄存器

image-20230717154804279

  • TIMx_RCR

image-20230717155356355

计数器的值到达之后不发生中断, 到达这个值的时候才产生

  • TIMx_CCR1: 捕获/比较寄存器
  • TIMx_CCR2
  • TIMx_CCR3
  • TIMx_CCR4
  • TIMx_BDTR: TIM1 和TIM8 刹车和死区寄存器

image-20230719100615309

  • TIMx_DCR: DMA控制寄存器
  • TIMx_DMAR: 连续模式的DMA地址

固件库

c
typedef struct
{
  uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between 0x0000 and 0xFFFF 
                                       分频因子*/

  uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode
                                       计数模式, 基础寄存器只能向下计数*/

  uint16_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter must be a number between 0x0000 and 0xFFFF.
                                       自动重装载*/ 

  uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                      This parameter can be a value of @ref TIM_Clock_Division_CKD
                                      外部输入时钟分频因子, 基础定时器没有*/

  uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                       reaches zero, an update event is generated and counting restarts
                                       from the RCR value (N).
                                       This means in PWM mode that (N+1) corresponds to:
                                          - the number of PWM periods in edge-aligned mode
                                          - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                       @note This parameter is valid only for TIM1 and TIM8.
                                       重复计数器, 高级计数器的*/
} TIM_TimeBaseInitTypeDef;

实际使用

c
void BASIC_TIM_Config(void)
{
	TIM_TimeBaseInitTypeDef TIM_BaseStructure;
	BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
	
	
	TIM_BaseStructure.TIM_Period = BASIC_TIM_Period;
	
	TIM_BaseStructure.TIM_Prescaler = BASIC_TIM_Prescaler;
	/****基础定时器没有****/
	TIM_BaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_BaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_BaseStructure.TIM_RepetitionCounter = 0;
	/********************/
	
	TIM_TimeBaseInit(BASIC_TIM, &TIM_BaseStructure);
    //清除中断标志位
	TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
	TIM_ITConfig(BASIC_TIM, TIM_FLAG_Update, ENABLE);
	TIM_Cmd(BASIC_TIM, ENABLE);
	
}

高级定时器

定时, 输出比较, 输出捕获, 输入捕获, 互补输出, 断路输出

16bit, 上/下/两边计数, 重复计数器, 四个GPIO, 通道1-3还有互补输出GPIO, 时钟使用PCLK2, 72M

image-20230717171657107

image-20230717171928852

image-20230717171950102

时钟源: CK_INT(TIMx_CLK在PSC分频之后), 内部时钟, 72MHz,

image-20230717172438019

来自外部GPIO, 对应TIMx_CH1/2/3/4, TIM_CCMRx的CCxS位控制, 经过滤波(TIMx_CCMR:ICxF)之后, 之后进行边沿检测, 控制有效的边沿, TIM_CCER:CCxP和CCxNP, 最后进行触发选择, 很少使用

image-20230717194237657

f~DTS~, TIMx_CR1:CKD决定,

内部触发输入

使用一个定时器作为另一个定时器的预分频器, 实现级联, TIMx_SMCR:TS

**总结: **有四种输入源, 内部时钟CK_INT, 外部时钟1引脚TIx, 外部触发输入ETR, 内部触发输入

  • 时基同普通

image-20230717200012120

  • 输入捕获

image-20230717200024126

对信号的上升下降或者双边捕获, 大概的原理就是跳变的时候把CNT计数器的值捕获到CCR寄存器

① 输入通道, 输入的信号从定时器外部引脚TIMx_CH1/2/3/4进入, 通常叫TI1/2/3/4

② 输入滤波以及边沿检测, 存在高频干扰的信号进行滤波

③ 捕获通道, 捕获的时候CNTx计入CCRx, 一个输入通道可以进入两个捕获通道(PWM捕获, 只能使用IC1, IC2)

image-20230717201101387

④ 预分频器

⑤ 捕获寄存器, 会产生 CCxI 中断,相应的中断位 CCxIF(在 SR 寄存器中)会 被置位,通过软件或者读取 CCR 中的值可以将 CCxIF 清 0。如果发生第二次捕获(即重复捕获: CCR 寄存器中已捕获到计数器值且 CCxIF 标志已置 1),则捕获溢出标志位 CCxOF(在 SR 寄存 器中)会被置位,CCxOF 只能通过软件清零。

  • 输出比较

image-20230717201347531

计数到CCR的时候发生电平跳变, 计数到ARR的时候重装载切跳变

DTG: 死区寄存器, 一般输出的PWM不可以直接驱动电机, 需要一个半桥驱动电路, 需要互补的PWM信号, 加入PWM的时候由于MOS管打开关闭需要一定时间, 所以需要预留一定的时间, 这就是死区

输出控制, 不需要了解

image-20230717202403178

实际使用

  • 测量脉宽和频率

当测试到跳变的时候会发生中断, 测量脉宽的时候需要不断地改变触发的边沿

  • PWM输入模式

通道一通道二, IC1是周期, IC2是占空比, 也可以反过来叫做非直连

在第一个上升沿发生跳变, IC2在第一个下降沿发生捕获, IC1在下一个上升沿发生捕获

image-20230718183208629

计数的值是从零开始, 计算的时候需要加一

  • 输出比较

一共有八种, )CCMRx的OCxM配置

一般使用为PWM模式, 频率是ARR控制, 占空比CRR

image-20230718183715608

中心对齐模式, 计数器达到重装载的值之后向下计数, 重新回到0之后开始递增

PWM

image-20230718184739691

原理, 输出的信号主要控制CCR以及ARR, 以及PSC进行分频

c
typedef struct
{
  uint16_t TIM_OCMode;        /*!< Specifies the TIM mode.
                                   This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes
                                   使用的PWM模式*/

  uint16_t TIM_OutputState;   /*!< Specifies the TIM Output Compare state.
                                   This parameter can be a value of @ref TIM_Output_Compare_state
                                   设置的是正通道*/

  uint16_t TIM_OutputNState;  /*!< Specifies the TIM complementary Output Compare state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_state
                                   @note This parameter is valid only for TIM1 and TIM8.
                                   设置的是互补通道*/

  uint16_t TIM_Pulse;         /*!< Specifies the pulse value to be loaded into the Capture Compare Register. 
                                   This parameter can be a number between 0x0000 and 0xFFFF
                                   设置的是比较寄存器的值*/

  uint16_t TIM_OCPolarity;    /*!< Specifies the output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_Polarity
                                   设置的是极性, 设置是高电平有效还是低电平有效*/

  uint16_t TIM_OCNPolarity;   /*!< Specifies the complementary output polarity.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
                                   @note This parameter is valid only for TIM1 and TIM8. */

  uint16_t TIM_OCIdleState;   /*!< Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. 
                                   输出的时候空闲电平极性设置, 用到断路功能的时候使用*/

  uint16_t TIM_OCNIdleState;  /*!< Specifies the TIM Output Compare pin state during Idle state.
                                   This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
                                   @note This parameter is valid only for TIM1 and TIM8. */
} TIM_OCInitTypeDef;

输出比较控制结构体

c
typedef struct
{

  uint16_t TIM_Channel;      /*!< Specifies the TIM channel.
                                  This parameter can be a value of @ref TIM_Channel */

  uint16_t TIM_ICPolarity;   /*!< Specifies the active edge of the input signal.
                                  This parameter can be a value of @ref TIM_Input_Capture_Polarity */

  uint16_t TIM_ICSelection;  /*!< Specifies the input.
                                  This parameter can be a value of @ref TIM_Input_Capture_Selection */

  uint16_t TIM_ICPrescaler;  /*!< Specifies the Input Capture Prescaler.
                                  This parameter can be a value of @ref TIM_Input_Capture_Prescaler */

  uint16_t TIM_ICFilter;     /*!< Specifies the input capture filter.
                                  This parameter can be a number between 0x0 and 0xF */
} TIM_ICInitTypeDef;

输入

c
typedef struct
{

  uint16_t TIM_OSSRState;        /*!< Specifies the Off-State selection used in Run mode.
                                      This parameter can be a value of @ref 
                                      OSSR_Off_State_Selection_for_Run_mode_state 
                                      运行模式下关闭状态的选择
                                      0:当定时器不工作时,禁止OC/OCN输出(OC/OCN使能输出信号=0);
								   1:当定时器不工作时,一旦CCxE=1或CCxNE=1,首先开启OC/OCN并输出无效电平,然后
								   置OC/OCN使能输出信号=1。*/

  uint16_t TIM_OSSIState;        /*!< Specifies the Off-State used in Idle state.
                                      This parameter can be a value of @ref 
                                      OSSI_Off_State_Selection_for_Idle_mode_state 
                                      空闲模式下关闭状态的选择*/

  uint16_t TIM_LOCKLevel;        /*!< Specifies the LOCK level parameters.
                                      This parameter can be a value of @ref Lock_level
                                      写保护寄存器*/ 

  uint16_t TIM_DeadTime;         /*!< Specifies the delay time between the switching-off and the
                                      switching-on of the outputs.
                                      This parameter can be a number between 0x00 and 0xFF 
                                      死区时间*/

  uint16_t TIM_Break;            /*!< Specifies whether the TIM Break input is enabled or not. 
                                      This parameter can be a value of @ref Break_Input_enable_disable
                                      刹车是否使能*/

  uint16_t TIM_BreakPolarity;    /*!< Specifies the TIM Break Input pin polarity.
                                      This parameter can be a value of @ref Break_Polarity
                                      引脚为什么电平的时候进行刹车*/

  uint16_t TIM_AutomaticOutput;  /*!< Specifies whether the TIM Automatic Output feature is enabled or not. 
                                      This parameter can be a value of @ref TIM_AOE_Bit_Set_Reset 
                                      自动使能输出*/
} TIM_BDTRInitTypeDef;

刹车死区控制

实际PWM

c
#include "bsp_pwm.h"

static void ADVANCE_TIM_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// 输出比较通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_CH1_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);

	// 输出比较通道互补通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_CH1N_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);

	// 输出比较通道刹车通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);
	GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_BKIN_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
	// BKIN引脚默认先输出低电平
	GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);	
}

static void ADVANCE_TIM_MODE_Config(void)
{
	  // 开启定时器时钟,即内部时钟CK_INT=72M
	ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);

/*--------------------时基结构体初始化-------------------------*/
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;	
	// 驱动CNT计数器的时钟频率 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;	
	// 时钟分频因子 ,配置死区时间时需要用到,配置的是CK_INT,死区时间采用的分频
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// 重复计数器的值,没用到不用管
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	// 初始化定时器
	TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);

	/*--------------------输出比较结构体初始化-------------------*/		
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	// 配置为PWM模式1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 互补输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
	// 设置占空比大小
	TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_PULSE;
	// 输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	// 互补输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	// 输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
	// 互补输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);

	/*-------------------刹车和死区结构体初始化-------------------*/
	// 有关刹车和死区结构体的成员具体可参考BDTR寄存器的描述
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
	// 输出比较信号死区时间配置,具体如何计算可参考 BDTR:UTG[7:0]的描述
	// 这里配置的死区时间为152ns
  TIM_BDTRInitStructure.TIM_DeadTime = 11;
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
	// 当BKIN引脚检测到高电平的时候,输出比较信号被禁止,就好像是刹车一样
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
  TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
	
	// 使能计数器
	TIM_Cmd(ADVANCE_TIM, ENABLE);	
	// 主输出使能,当使用的是通用定时器时,这句不需要
	TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
	
}

void ADVANCE_TIM_Init(void)
{
	ADVANCE_TIM_GPIO_Config();
	ADVANCE_TIM_MODE_Config();
	
}