概述
用来定时的器件
定时器的基本原理
在 STM32 上,一般来说,定时器由三个部分组成
(1)时基单元:Time Base Unit
定时器的基本部件。所有的定时器都有这个部件。
由一个“计数器”组成。“计数器”可以设置为从一个值按照 一定的时钟频率递减到0,也可以设置从0按照一定的时钟 频率递增到一个值。然后产生一个溢出事件/中断,从而达到 定时的功能。
例子1
(2)输入捕获单元:可以对一个或多个输入信号进行处理
有些定时器没有。
可以捕获一个或多个输入信号(看你这个定时器有几个输入通道 CHx_IN)
有什么用呢?如: 可以计算输入信号的频率输入信号经过“输入捕获阶段”(数字滤波、多路复用、预分频等等), 到信号检测,当检测到需要的信号状态变化时(上升沿/下降沿), 就会把定时器时基单元中计数器的值,锁定到“捕获/比较寄存器” 这样,就可能根据预先设定的定时器的参数(时钟频率,N值等等), 就可以算出从开始到捕获这个信号状态的这一段的时间。
(3)输出比较单元:可以输出一个或多个信号
有些定时器没有。
定时器还可以输出一个电平、 并且可以根据“比较寄存器”的值,翻转电平的状态。 如: CCR: Capture Compare Register捕获/比较寄存器 定时器的第(2)输入捕获单元 和第(3) 输出比较单元 不同时用。 CNT >= CCR 输出一个高电平 CNT < CCR 输出一个低电平 PWM方波
STM32F4xx 定时器概述
SysTick2
系统定时器, 所有 Cortex M3/M4 中都会内置于 NVIC 一个 SysTick 的定时器。
这个定时器,被称为系统定时器。这个定时器只有一个时基单元。
并且在溢出时,会产生一个 SysTick 异常(中断) 。
为什么要内置一个这个玩意?
因为大多数操作系统,都需要一个硬件定时器来产生操作系统需要的
时钟嘀哒中断。用于操作系统计时。这个定时器作为整个系统的基本
的时间单元。假设我不跑操作系统呢? 裸奔呢
延时 -> 可以用 SysTick 来实现
SysTick 定时器说明及寄存器设置: 《ARM Cortex M4 权威指南》
It is integrated as a part of the NVIC and can generate the SysTick exception (exception type #15). The SysTick timer is a simple decrement 24-bit timer, and can run on processor clock frequency or from a reference clock frequency (normally an on-chip clock source). 它被内置于NVIC中,它能够产生一个SysTick异常(异常类型 #15).这个SysTick定时器 是一个简单的24bits的递减定时器,它可以运行在处理器的时钟频率上,也可以运行在一个 外部的时钟频率上(通常在片上的时钟源) ……。
基本定时器
TIM6, TIM7:
- 只有时基单元
- 无输入输出引脚
- 计数器只有 16bits,而且只支持递增模式
通用定时器
TIM2~TIM5:
-
16bits(TIM3,TIM4)或 32bits(TIM2,TIM5)计数器,可以配置为递增,递减,先递增后递减(0->N->0)的模式
-
多达 4 个独立通道,可用于:
- 输入捕获
- 输出比较
- PWM 生成(边沿或中心对齐模式)
- 单脉冲模式输出
TIM9~TIM14:
-
16bits 自动加载递增计数器
-
2 个独立通道,可用于:
- 输入捕获
- 输出比较
- PWM 生成(边沿或中心对齐模式)
- 单脉冲模式输出
高级定时器
TIM1, TIM8:
-
16bits 计数器,可以可以配置为递增,递减,先递增后递减(0->N->0)的模式
-
多达 4 个独立通道,可用于:
- 输入捕获
- 输出比较
- PWM 生成(边沿或中心对齐模式)
- 单脉冲模式输出
“高级”
重复计数器(TIMx_RCR) : Repeation Counter Register
如果使用重复计数,则当计数器重复溢出的次数达到设定的
重复计数器的值 +1 时,才会产生溢出事件/中断。
如果不用重复计数器,在每次计数器溢出时都会产生事件/中断。
这个时候,和通用定时器没有区别。....
信号处理方面有高级的地方...
看门狗 watch dog
单片机的独立看门狗(Independent Watchdog Timer,简称 IWDG)是一种用于监控和保护微控制器(MCU)系统的硬件组件。 它的主要功能是检测系统是否在正常工作,并在发现系统出现故障时自动进行复位操作,以确保系统的稳定性和可靠性。
本质:一个能够产生复位信号的计数器。
复位就是使 MCU 回到初始状态。对于单片机来说,开机的时候需要复位,以便使得 CPU 和其他功能部件处于一个正确的初始状态,并以此为起点开始工作,当出现死机的情况也应当对其进行复位,用以摆脱死机状态。
系统复位方式共有 5 种,分别为:
1、硬件复位。
2、WWGD 复位。
3、IWDG 复位。
4、软件复位。
5、低功耗复位。
独立看门狗复位:这种方式使用独立的看门狗来监控单片机系统的工作状态,当单片机工作异常时,看门狗会产生复位信号,将单片机系统复位。
特征:1、独立看门狗是一个递减计数器产生的复位。2、时钟信号是有独立 RC 振荡器产生。3、可以在待机和停止模式下运行。4、当看门狗被激活后,当递减为 0 是产生复位。
喂狗:如果在计数没减到 0 之前,重置计数器的值的话,那么就不会产生复位信号,这个动作我们称为。
作用:检测外界电磁干扰或硬件导致程序跑飞的问题。
工作原理
IWDG 有一个输入(时钟 LSI) ,为内部专门的 40Khz 低速时钟(LSI) 驱动 ,经过分频器进行预分频,分频为工作时钟,然后提供给递减计数器,当递减计数器减为 0 时产生复位信号。为递减计数器赋值的过程称为喂狗,喂狗的作用就是不产生复位信号。
实例
(1) 配置定时器的通道(输入/输出)引脚
TIM通道引脚是由GPIO口来复用的
1.1 RCC_AHB1... =>使能GPIO分组的时钟
1.2 GPIO_Init => 配置GPIO引脚的功能 AF: Alternate Function 复用功能
1.3 选择GPIO口具体的复用功能
GPIO_PinAFConfig
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx,
uint16_t GPIO_PinSource,
uint8_t GPIO_AF);
GPIOx: 指定具体的GPIO分组
GPIO_PinSource:指定具体的GPIO引脚编号
GPIO_AF: 指定具体的复用功能
(2) 定时器时基单元(Time Base Unit)配置
1.1 RCC_APBx => 使能定时器的时钟
1.2 TIM_TimeBaseInit => 初始化定时器的时基单元
void TIM_TimeBaseInit(TIM_TypeDef* TIMx,
TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
TIMx: 指定要初始化的定时器,如:
TIM1, TIM2,....
TIM_TimeBaseInitStruct: 指向要初始化的结构体。
typedef struct
{
uint16_t TIM_Prescaler;
指定定时器的预分频
TIM_CLK: 计数器(定时器)的时钟
TIM_CLK = Fin(定时器的输入时钟频率)/ (TIM_Prescaler + 1)
if APBx prescaler == 1 则:
Fin = APBx BUS clock
else
Fin = APBx Bus clock * 2
TIM13 在总线APB1上,
APB1 prescaler == 4
APB1 BUS clock = 168M/4 = 42M
so
Fin = 42M*2 = 84M
uint16_t TIM_CounterMode;
指定计数器模式(递增,递减,先递增后递减 ....)
TIM_CounterMode_Up 递增 0 --> N
TIM_CounterMode_Down 递减 N --> 0
0 ---> N --> 0
0 ---> n-1 -> 0
0 ----> N + 1 --> 0
有一点细微的区别,大家可以查看datasheet
TIM_CounterMode_CenterAligned1 中心对齐模式1
TIM_CounterMode_CenterAligned2 中心对齐模式2
TIM_CounterMode_CenterAligned3 中心对齐模式3
uint32_t TIM_Period;
指定自动加载的起始值。 N值, 定时器周期。
uint16_t TIM_ClockDivision;
指定输入捕获阶段的数字滤波的采样频率, 分频值
可以是如下值:
TIM_CKD_DIV1 数字滤波采样频率=定时器频率
TIM_CKD_DIV2 数字滤波采样频率=定时器频率*2
TIM_CKD_DIV4 数字滤波采样频率=定时器频率*4
uint8_t TIM_RepetitionCounter;
计数器溢出重复次数,只有高级定时器TIM1和TIM8才有。
计数器溢出重复次数是指只当把“计数器”溢出的次数=重复的次数时
才会产生一个定时器的溢出中断。
} TIM_TimeBaseInitTypeDef;
(3) 定时器输出通道/输入通道配置
(3.1) TIM_OC1Init:
Output Channel 1: 输出通道1
void TIM_OC1Init(TIM_TypeDef* TIMx,
TIM_OCInitTypeDef* TIM_OCInitStruct);
TIMx: 指定定时器,如: TIM1, TIM2, ...
TIM_OCInitStruct: 指向OC1的初始化结构体
typedef struct
{
uint16_t TIM_OCMode;
指定输出通道的模式
TIM_OCMode_Timing
输出比较寄存器(TIMx_CCRn)与计数器(TIMx_CNT)的比较不影响输出
TIM_OCMode_Active
当TIMx_CNT = TIMx_CCRn时,输出高电平
TIM_OCMode_Inactive
当TIMx_CNT = TIMx_CCRn时,输出低电平
TIM_OCMode_Toggle
toggle:翻转
当TIMx_CNT = TIMx_CCRn时,翻转电平
TIM_OCMode_PWM1
当TIMx_CNT < TIMx_CCRn时, 输出高电平
否则,输出低电平
TIM_OCMode_PWM2
当TIMx_CNT < TIMx_CCRn时, 输出低电平
否则,输出高电平
uint16_t TIM_OutputState;
输出信号 状态(enable/disable)
TIM_OutputState_Disable 不输出信号
TIM_OutputState_Enable 输出信号
uint16_t TIM_OutputNState;
互补输出信号的 状态(enable/disable)
"互补输出信号" 只针对高级定时器 TIM1, TIM8
TIM_OutputNState_Disable 不输出互补信号
TIM_OutputNState_Enable 输出互补信号
uint32_t TIM_Pulse;
指定要比较的值
uint16_t TIM_OCPolarity;
Polarity: 极性
指定输出极性
TIM_OCPolarity_High: 高电平为有效电平
TIM_OCPolarity_Low: 低电平有效电平
uint16_t TIM_OCNPolarity;
指定互补输出信号的极性,仅针对TIM1,TIM8
uint16_t TIM_OCIdleState;
Idle: 空闲。
输出引脚在空闲状态的输出,仅针对TIM1和TIM8
uint16_t TIM_OCNIdleState;
互补输出引脚在空闲状态的输出,仅针对TIM1和TIM8
} TIM_OCInitTypeDef;
(3.2) 使能/禁止预装载
TIM_OC1PreloadConfig
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
TIM_OCPreload:
TIM_OCPreload_Enable 每次自动加载比较寄存器的值
TIM_OCPreload_Disable 不自动加载比较寄存器的值
是否使能 每次自动加载比较寄存器
(4) 定时器使能
(4.1) 使能自动加载 ARR,计数值
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
TIMx:指定定时器,如:TIM1, TIM2,...
NewState:
ENABLE
DISABLE
(4.2) TIM_Cmd
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
TIMx:指定定时器,如:TIM1, TIM2,...
NewState:
ENABLE
DISABLE
例子
假设一个定时器 TIM 的时钟频率为 f, (1/f)s 它就会振动一次
设定的计数值为 N,按照递减的方式来计数每隔 (1/f)s 就会 -1 N -> N-1 (1/f)s N-1 -> N-2 (1/f)s ... N -> 0 => (N+1) * (1/f) s 到0,产生一个中断, 时间为: (N+1) * (1/f) s =>就可以达到“定时”功能。 ------ 假设: 一个定时器的时钟频率为 8M HZ, 1s之后, 我有一个非常重要的事情,请到时通知我, 该如何设置这个定时器? f = 8M Hz T = (1/f)s = (1/8M) s = (1/8) us N =? N * (1/8M)s = 1s => N = 8000000 N = 8000000 - 1 = 799999 Time Base Unit 核心部件就是一个“计数器”,该“计数器” 内部有三个寄存器: TIMx_ARR: Auto Reload Register 自动加载寄存器 <- N TIMx_CNT: Counter 计数器 TIMx_PSC: PreSCaler 时钟预分频 “溢出事件/中断” 单向递增 0 -> N => 产生溢出事件/中断 单向递减 N -> 0 => 产生溢出事件/中断
Systick 系统定时器
概述
定时器是 STM32 中非常常用的一个外设,对于 STM32 来说,提供多种定时器供用户使用,比如高级定时器、通用定时器、基本定时器在使用时比较繁琐,所以内核就提供一款定时器,叫做系统定时器,也被称为 Systick 定时器(嘀嗒定时器)。
Systick 定时器是属于内核中的一个外设,内嵌在 NVIC 中,在 Cortex M3/M4 内核中都存在,方便用户在使用不同类型的芯片的时候进行移植。
Systick 定时器是一个 24bit 的倒计时(向下计数)定时器,功能就是实现简单的延时。
-
裸机模式:提供简单的延时 实现微秒、毫秒级别的延时 闹钟、秒表、洗衣机、微波炉。
-
操作系统:提供给操作系统一个稳定的时基,因为操作系统的架构是并行的。
系统定时器的时钟源
Systick 定时器有两个时钟源可以选择,可以选择内部时钟(168MHZ),或者外部时钟(8 分频之后的系统时钟 168MHZ/8 = 21MHZ)。
选择不同的时钟源会影响延时时间的长短
如果选择 168MHZ 作为 Systick 定时器的时钟源:2^24-1 最大延时时间为 99.86ms
1s (1000000us)振荡 168000000 次,振荡 1 次的周期是 1/168us,也就是振荡 168 次花费 1us
如果选择 21MHZ 作为 Systick 定时器的时钟源:2^24-1 最大延时时间为 798.9ms
1s (1000000us)振荡 21000000 次,振荡 1 次的周期是 1/21us,也就是振荡 21 次花费 1us
内核中提供了一个函数接口可修改 Systick 的时钟源,函数
SysTick_CLKSourceConfig()
/** * @brief Configures the SysTick clock source. * @param SysTick_CLKSource: specifies the SysTick clock source. * This parameter can be one of the following values: * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source. * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source. * @retval None */ void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); 函数原型 void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) 函数参数 参数一:SysTick_CLKSource 选择时钟源 一般选SysTick_CLKSource_HCLK_Div8 21MHZ
Systick 定时器的应用
内核提供两种方案来使用 systick 定时器,分别是 中断方式 + 非中断方式
-
中断方式
-
设置 Systick 定时器的中断周期
-
编写延时函数
-
-
非中断方式
直接操作 Systick 定时器的 4 个寄存器
↩
-
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于