【STM32学习】Timer与Delay

小白最近在学习STM32F103RCT6,使用的是正点原子mini板。板子2018年买的,到现在都六年了,终于有时间倒腾这玩意了,现将最近整理的内容记录如下。基本上是阅读正点原子自带的《STM32不完全手册》的,大家也可以多多研究一下。未尽知识点或者描述有误之处,还请各位大佬多多批评指正!

Timer通用定时器

通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器(CNT)
构成,可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
STM32 的通用 TIMx (TIM2、TIM3、TIM4 和 TIM5)定时器功能包括
【1】 16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
【2】16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。
【3】4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C.PWM 生成(边缘或中间对齐模式)
D.单脉冲模式输出
【4】可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
【5】如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理

Timer通用定时器的常用寄存器

控制寄存器1 TIMx_CR1

在这里插入图片描述常用的是TIMx_CR1 的最低位(计数器使能位),该位必须置 1,才能让定时器开始计数。

DMA/中断使能寄存器 TIMx_ DIER

常用的为最低位(更新中断允许位),若需要使用定时器的更新中断,则要设置该位为 1,用于允许由于更新事件所产生的中断。

预分频寄存器 TIMx_PSC

用于对时钟进行分频,然后提供给计数器,作为计数器的时钟
定时器的时钟来源有 4 个,这些时钟,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置:

  1. 内部时钟(CK_INT)。
    其从 APB1 倍频的来的,STM32 中除非 APB1 的时钟分频数设置为 1,否则通用定时器 TIMx
    的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx 的时钟就等于
    APB1 的时钟。高级定时器的时钟不是来自 APB1,而是来自 APB2 的
  2. 外部时钟模式 1:外部输入脚(TIx)
  3. 外部时钟模式 2:外部触发输入(ETR)
  4. 内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。

TIMx_CNT

其为定时器的计数器,该寄存器存储了当前定时器的计数值。

自动重装载寄存器(TIMx_ARR)

在这里插入图片描述
分为2个寄存器:

  1. 程序员可以直接操作的;
  2. 程序员看不到的(影子寄存器),它是真正起作用的。
    根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。

状态寄存器(TIMx_SR)

在这里插入图片描述
标记当前与定时器相关的各种事件/中断是否发生。
在这里插入图片描述
在这里插入图片描述

代码示例

typedef struct
{
 uint16_t TIM_Prescaler;       // 设置分频系数
 uint16_t TIM_CounterMode;     // 设置计数方式:向上计数,向下计数,中央对齐计数
 uint16_t TIM_Period;          // 设置自动重载计数周期值
 uint16_t TIM_ClockDivision;   // 时钟分频因子
 uint8_t TIM_RepetitionCounter; 
} TIM_TimeBaseInitTypeDef;

/* 1. 时钟使能。 */

/* 2. 初始化定时器参数,设置自动重装值,分频系数,计数方式等 */
void TIM_TimeBaseInit(TIM_TypeDef*TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

/* 3. 设置 允许更新中断 
     @TIM_IT : 指明我们使能的定时器中断的类型,包括更新中断 TIM_IT_Update,触发中断 TIM_IT_Trigger,以及输入捕获中断等等
*/
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

/* 4. 中断优先级设置 */

/* 5. 允许 TIM3 工作,也就是使能 TIM3。 */
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
/* 6. 编写中断服务函数 */
// 读取中断状态寄存器的值判断中断类型,判断定时器 TIMx 的中断类型 TIM_IT 是否发生中断。
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t);
// 清除中断标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

Timer初始化示例(TIM3)

/* 1. TIM3 时钟使能。 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
/* 2. 初始化定时器参数,设置自动重装值,分频系数,计数方式等 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 5000;
TIM_TimeBaseStructure.TIM_Prescaler =7199; 
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* 3. 设置 TIM3_DIER 允许更新中断 */
TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE);
/* 4. TIM3 中断优先级设置。 */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级 0 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级 3 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器
/* 5. 使能定时器3 */
TIM_Cmd(TIM3, ENABLE);
/* 6. 编写中断服务函数 */
void TIM3_IRQHandler(void) //TIM3 中断
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
        // 硬件置了1,需要软件来清0
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        /* add your code here */
    }
}

拓展资料

《不完全手册 V3.1 库函数版》 P121

Systick

SysTick 是一个 24 位的倒计数定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
若利用 STM32 的内部 SysTick 来实现延时,则既不占用中断,也不占用系统定时器。

通过UCOS-II,理解操作系统和我们的 delay 函数共用 SysTick 定时器的实现方法:

ucos 运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定的(由 OS_TICKS_PER_SEC 宏定义设置),比如要求 5ms 一次(即可设置:OS_TICKS_PER_SEC=200),在 STM32 上面,一般是由 SysTick 来提供这个节拍,也就是 SysTick要设置为 5ms 中断一次,为 ucos 提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了),因为在 ucos 下 SysTick 不能再被随意更改。
如果我们还想利用 SysTick 来做 delay_us 或者delay_ms 的延时,就必须想点办法了。
这里我们利用的是时钟摘取法。以 delay_us 为例,比如delay_us(50),在刚进入 delay_us 的时候先计算好这段延时需要等待的 SysTick 计数次数,这里为 509(假设系统时钟为 72Mhz,那么 SysTick 每增加 1,就是 1/9us,因为Systick又SYSCLK8分频而来,72Mhz / 8 = 9 Mhz,所以systick增1,就过了1/9us),然后我们就一直统计SysTick 的计数变化,直到这个值变化了 509,一旦检测到变化达到或者超过这个值,就说明延时 50us 时间到了。这样,我们只是抓取 SysTick 计数器的变化,并不需要修改 SysTick 的任何状态,完全不影响 SysTick 作为 UCOS 时钟节拍的功能,这就是实现 delay 和操作系统共用SysTick 定时器的原理。

详情见《不完全手册 V3.1 库函数版》 P197

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值