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

STM32 TIM 定时器深度剖析:结构、时基、中断与应用开发(超形象详解)

文章目录

  • 定时器(TIM)
    • 定时器种类与分布
    • 定时器的基本结构
    • 时基单元
      • 时基单元基本结构
      • 计数器计数方向
      • 时基单元时钟来源计算
      • 寄存器预加载机制
    • 自制延时函数
      • 获取单片机当前时间
      • 实现延迟函数
      • 初始化定时器3的时基单元
      • 配置中断
      • 编写中断响应函数
      • 测试延迟函数

定时器(TIM)

定时器种类与分布

  • 定时器种类:单片机里定时器分高级、通用、基本三种,功能上高级定时器最全,通用定时器是在高级定时器基础上阉割部分功能,基本定时器则是在通用定时器基础上再阉割部分功能。
  • 分布情况:STM32F1 系列芯片最多有 14 个定时器,定时器 1 和 8 是高级定时器,定时器 6 和 7 是基本定时器,定时器 2 到 5 以及定时器 9 到 14 是通用定时器。实际使用芯片中有 1 个高级定时器(定时器 1)和 3 个通用定时器(定时器 2 - 4),学习编程一般以高级定时器 1 为例。

在这里插入图片描述

定时器的基本结构

时基单元,输出比较,输入捕获,从模式控制器四个部分。

在这里插入图片描述

时基单元

时基单元基本结构

  • 时钟源:时钟信号主要有两个来源,通常认为来自上一章学习的时钟树,一个为从模式控制器。
  • 预分频器:因输入时钟信号频率高,需用预分频器降频,其分频系数等于 PSC 的值加 1 。
  • 计数器 CNT:对经过分频后的脉冲信号计数,每来一个脉冲,计数器的值增加 1。
  • 自动重装寄存器 ARR:用于设置定时周期,定时周期的值等于 ARR 的值加 1。当计数器 CNT 的值增长到与 ARR 相等时,会发生溢出,然后从 0 开始重新计数。
  • 重复计数器 RCR:设置重复计数的次数。没有 RCR 时,计数器 CNT 每溢出一次产生一个 update 事件;加入 RCR 后,计数器 CNT 需要溢出 RCR + 1 次才产生一个 update 事件。
    在这里插入图片描述

和手表类似,手表有石英晶振提供时钟信号,还有其他电路装置进行降频,秒针相当于一个计数器,ARR为59,周期为60,RCR为0,每一个周期产生一个事件。

在这里插入图片描述

计数器计数方向

  • 上计数(count up):从 0 开始,左边每来一个脉冲,计数器 CNT 的值增加 1,直到增长到和自动重装计算器 ARR 相等时发生溢出,然后从 0 开始重新计数。
  • 下计数(count down):与上计数相反,从 ARR 开始计数,左边每来一个脉冲,计数器 CNT 的值减少 1,直到减少到 0 发生溢出,然后从 ARR 开始重新计数。
  • 中心对齐(center line):首先从 0 开始上计数到 ARR,然后转变成下计数,从 ARR 开始又计数到 0,完成一个定时周期。下计数和中心对齐用得较少,一般用上计数。

在这里插入图片描述

时基单元时钟来源计算

  • 分辨率和周期概念:类比手表,分辨率是计时的最小间隔,周期是转一圈所消耗的时间。对于定时器,计数器 CNT 每跳一下所消耗的时间是分辨率,从某点到计数器溢出是计数周期。
    在这里插入图片描述

  • 计算过程:以设置定时器 3 的 11 单元分辨率为 1 微秒,周期为 1 毫秒为例。先根据时钟树确定输入信号频率,当前配置下所有定时器时钟频率为 72 兆赫兹。要得到 1 微秒分辨率即 1 兆赫兹信号,设置预分频器 PSC 值为 71 ;要设置 1 毫秒周期,自动重装寄存器 AR 值为 999,重复计数器 RCR 值为 0。

    TIM_CLK是系统自动配置的,如果APB2和APB1的分频系数为1,TIM_CLK = PCLK2,如果其他系数 TIM_CLK = 2 * PCLK2,右面同理。

在这里插入图片描述

寄存器预加载机制

预加载机制,如果阴影为黑色,表示默认为开启,灰色表示默认为关闭。

  • 概念:向寄存器写值时,先写入影子寄存器,值不会立即生效,等计数器 CNT 溢出产生 update 事件时,影子寄存器的值才进入活动寄存器并生效,这种缓存机制叫寄存器预加载。

在这里插入图片描述

  • 作用:以自动重装寄存器 AR 为例,在定时器运行中改变 AR 值,不使用预加载机制会导致定时器跑飞,加入预加载机制后,可避免该问题,使计数周期正常过渡 。

    未使用预加载寄存器

在这里插入图片描述

使用了预加载寄存器

在这里插入图片描述

自制延时函数

使用定时器3和中断函数自制延时函数,因为使用的是while轮询等待所以还是会造成程序阻塞。

获取单片机当前时间

  • 定时器配置:假设时基单元输入时钟频率 72 兆赫兹,设置 PSC 值为 71 对输入时钟 72 分频,得到 1 兆赫兹时钟频率,周期 1 微秒;设置定时周期为 1000(999 + 1),即 1 毫秒;设置重复计数器 RCR 值为 0,使 update 事件频率 1 毫秒一次,激活 update 标志位产生中断。

  • 变量声明与作用:声明无符号 32 位整型变量 current tick ,赋初值 0,用于记录单片机当前时间(单位毫秒),变量前加 volatile 关键字(作用与中断有关,可自行百度学习)。

    在这里插入图片描述

  • 中断响应操作:中断每毫秒产生一次,在中断响应函数中让 current tick 值增加 1,从而记录单片机开机后的时间。
    在这里插入图片描述

实现延迟函数

  • 函数设计:定义延迟函数 APP delay,参数为要延迟的毫秒数。
  • 实现思路:用 current tick 获取当前时间,加上要延迟的时间得到延迟结束时间 expire time ,通过 while 循环等待当前时间超过延迟结束时间,函数结束即实现延迟。

在这里插入图片描述

初始化定时器3的时基单元

  • 第一步:使能时钟:定时器 3 在 APB1 总线上,调用 RCC_APB1PeriphClockCmd 开启其时钟。
  • 第二步:配置参数:学习 TIM_TimeBaseInit 编程接口,声明结构体变量 TM_TimeBaseInitTypeDef 并赋值,设置预分频器 PSC 值 71、周期值 999、计数方向为上计数、重复计数器 RCR 值 0,调用该接口完成参数配置。
  • 第三步:闭合开关:学习 TIM_Cmd 编程接口,用该接口闭合开关使定时器运行。

在这里插入图片描述

配置中断

  • 使能 update 中断:学习 TIM_ITConfig 编程接口,通过该接口闭合 update 标志位产生中断的开关。
  • 配置 NVIC 模块
    • 先在 main 方法开头配置中断优先级分组;
    • 再声明结构体变量 NVIC_InitTypeDef 并赋值,设置定时器 3 中断相关参数,调用 NVIC_Init 完成 NVIC 模块配置。

在这里插入图片描述

编写中断响应函数

  • 找到函数名:在启动文件的中断向量表中找到定时器 3 的中断响应函数 TIM3_IRQHandler ,复制到 main.c 文件进行实现。
  • 函数内容:先通过 TM_GetFlagStatus 检查是否由 update 标志位触发中断,若是则用 TM_ClearFlag 清除标志位,再让 current tick 值加 1。

在这里插入图片描述

测试延迟函数

  • 初始化板载 LED:写函数初始化板载 LED(PC13 引脚),开启 GPIOC 时钟,声明结构体变量并设置相关参数,初始化完成后关灯。
  • 编写闪灯程序:在 main 方法的 while 循环中编写闪灯代码,调用 APP delay 延迟函数,实现不同闪烁效果(如亮 500 毫秒灭 500 毫秒、两次短闪一次长闪等),编译下载代码到单片机测试,验证延迟函数可用 。

完整main.c代码

#include "stm32f10x.h"                  // Device headervolatile uint32_t currentTick = 0;
void App_delay(uint32_t ms);
void App_TIM3_BaseInit(void);
void App_GpioInit(void);
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);App_TIM3_BaseInit();App_GpioInit();while(1){GPIO_ResetBits(GPIOC, GPIO_Pin_13);App_delay(100);GPIO_SetBits(GPIOC, GPIO_Pin_13);App_delay(100);GPIO_ResetBits(GPIOC, GPIO_Pin_13);App_delay(500);GPIO_SetBits(GPIOC, GPIO_Pin_13);App_delay(500);}
}
//自制延时函数逻辑
void App_delay(uint32_t ms)
{uint32_t extime = ms + currentTick;while(extime > currentTick);
}
//定时器3初始化
void App_TIM3_BaseInit(void)
{//开启定时器3的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//配置时基单元参数TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 999;TIM_TimeBaseInitStruct.TIM_Prescaler = 71;TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);//闭合时基单元开关TIM_Cmd(TIM3, ENABLE);//使能updateTIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//配置NVIC模块NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStruct);}
void TIM3_IRQHandler(void)
{//先判断是不是要的中断if(TIM_GetFlagStatus(TIM3, TIM_FLAG_Update) == SET){//将中断标志位清除TIM_ClearFlag(TIM3, TIM_FLAG_Update);//操作currentTick++;}
}
void App_GpioInit(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStruct);
}

相关文章:

  • 20250523在荣品的PRO-RK3566开发板的Android13下解决千兆网异常的问题【硬件部分】
  • InonoDB(一)
  • threadPool.submit() 和 threadPool.execute()的区别
  • 2008年EJOR SCI2区,连续蚁群优化算法ACOR,深度解析+性能实测
  • 主成分分析基本概念及python代码使用
  • Flask框架
  • 智能体与大模型的区别联系
  • 卷积神经网络(CNN)可视化技术详解:从特征学到演化分析
  • 《P3375 【模板】KMP》
  • 深度学习笔记24-LSTM火灾预测(Ptorch)
  • Python排序函数全面指南:从基础到高级
  • 电子电路:什么是电磁耦合?
  • PotPlayer 4K 本地万能影音播放器
  • [特殊字符] 在线音频剪辑网站上线啦!
  • CSS3动画
  • java使用aspose合并exl单元格
  • 掌握递归:编程中的优雅艺术
  • 智能建筑时代来临,楼宇自控技术成智能建筑标配新趋势
  • R语言开始绘图--柱状图
  • Linux——网络基础概念
  • 企业平台网站制作/品牌软文营销案例
  • 企业管理咨询网站模板/windows优化大师如何卸载
  • 如何在网站做旅游产品/广州营销seo
  • 网站建设添加视频/游戏推广
  • 网站可信/旅游推广赚佣金哪个平台好
  • 官方网站平台下载/seo全网图文推广