25.1 关于定时器

25.1.1 定时器分类

STM32中有众多定时器,如图 25.1.1 所示。按所处的位置可分为核内定时器和外设定时器。核内定时器就是前面 “第11章 基础重点—SysTick定时器”介绍的SysTick定时器,该定时器位于Cortex-M3内核中。外设定时器由芯片半导体厂商设计,如STM32系列,包含常规定时器和专用定时器。常规定时器是本章重点介绍的介绍的内容,专用定时器在后面几章讲解。

  • SysTick定时器

SysTick定时器前面已经详细介绍了,主要用于系统精确延时,不占用其它定时器。在多任务操作系统中,为系统提供时间基准。

  • 看门狗定时器

看门狗定时器主要用于监控系统的运行状态,当系统受外界干扰,程序脱离正常的执行流程时,看门狗将复位系统,尝试恢复正常状态。

看门狗也是定时器,启动后便开始计数,达到计数阈值则复位系统。STM32内置了两个看门狗定时器,即独立看门狗(IWDG)和窗口看门狗(WWDG)。该部分内容,后面章节再详细讲解。

  • 常规定时器

STM32F1系列共用8个定时器,2个基本定时器(TIM6、TIM7)、4个通用定时器(TIM2、TIM3、TIM4、 TIM5)、2个高级定时器(TIM1、TIM8),三者区别如表 25.1.1 所示。

基础定时器最简单,就只是普通的计数、定时功能。通用定时器在基础定时器的功能上,增加了输入捕获和输出比较等功能。高级定时器在通用定时器的功能上,又增加了三相六步电机接口,具有刹车(断路)、死区时间控制等功能,主要用于电机控制。

25.1.2 定时器结构

以通用定时器为例,其结构框架可看作五部分,如图 25.1.2 所示。

①时钟源:通用定时器的时钟源有四个,分别为内部时钟(CK_INT)、外部触发输入(ETR)、内部触发输入(ITRx,x=03)、外部捕获引脚(TixFPy,x=14,y=1~2)。

大多数情况下,都将内部时钟作为时钟源,即来自RCC的TIMxCLK。由前面“图 9.1.1 时钟树”可知,TIM27挂接在APB1下,TIM1和8挂接在APB2,若APBx预分频等于一,则APBx=TIMxCLK,否则TIMxCLK=APBx*2。假设当前系统时钟为72MHz(即AHB=72MHz),如果此时APB1预分频系数为2(即APB1=36MHz),则TIM27的时钟为72MHz;如果此时APB2预分频系数为1(即APB2=72MHz),则TIM1和8的时钟为72MHz。

②控制器:包含触发控制器、从模式控制器和编码器接口。触发控制器用于为其它定时器、DAC/ADC提供触发信号。从模式控制器用于控制计数器复位、使能、计数方式等。编码器接口用于编码器计数。

③计数核心:包含三个寄存器:预分频器(PSC)、计数器(CNT)、自动装载寄存器(ARR)。

  • PSC预分频的范围为0~65535,用于将CK_PSC的时钟进行分频,输出CK_CNT脉冲供计数器计数。
  • CNT计数器的范围为0~65535,可以向上计数、向下计数或向上/向上双向计数。当计数值达到设定值时,会产生溢出事件,发出中断或DMA请求,然后再由自动装载寄存器进行重新加载或更新。
  • ARR自动装载寄存器用来存储预先设定的值,在每次计数器溢出事件后,自动将设定的值重新装载到计数器中。

定时器的定时时间主要取决于预分频系数和定时周期,计算公式为:

假设当前系统时钟频率为72MHz,APB1二分频为36MHz,TIMxCLK则为72MHz。预分频系数任意取一值,假设为PSC=10000-1,自动装载器值假设为ARR=7200-1,则此时定时器定时为:

即,定时器每间隔1s,将产生一次溢出事件,产生中断。

④输入捕获:可实现对输入信号的上升沿、下降沿、双边沿的变化进行捕获(采样或存储),通常用于输入信号的脉宽、频率、占空比的测量。比如后面红外遥控实验中,红外接收头将收到的红外遥控信号转化为脉冲信号,利用输入捕获对脉冲信号进行解析,即可得知是哪一个按键发出的遥控信号。具体的使用方法,在后续红外遥控章节介绍。

⑤输出比较:将计数器当前计数值和设定值进行比较,根据比较结果输出高电平、低电平、翻转等,通常用于波形的输出。比如后面PWM实验中,将通过定时器产生PWM,控制LED灯亮度,同理也可以控制电机转速等。

25.2 硬件设计

定时器为MCU内部资源,常与其它外设配合使用,不涉及新增硬件设计。

25.3 软件设计

25.3.1 软件设计思路

实验目的:本实验通过使用MCU的通用定时器TIM2,实现us延时,通过开发板LED灯或示波器/逻辑分析仪检验实验效果。

  1. 初始化TIM相关参数:设置时钟、工作方式等;
  2. 实现延时函数;
  3. 主函数编写控制逻辑:翻转LED,观察效果;
    本实验配套代码位于“5_程序源码\17_定时器—us延时\”。

25.3.2 软件设计讲解

  1. TIM初始化
    使用任何外设资源,都需要先考虑使能时钟。与前面的示例一样,在一开始就使用“SystemClock_Config()”配置好了系统时钟和各APB分频。TIM2是挂接在APB1上,这里将APB1二分频,此时TIM2的时钟为2*APB1, 即72MHz。

接着设置TIM2的相关参数,如代码段 25.3.1 所示。
代码段 25.3.1 定时器初始化(driver_timer.c)

/*
* 定义全局变量
*/
TIM_HandleTypeDef htim;
/*
* 函数名:void TimerInit(void)
* 输入参数:
* 输出参数:无
* 返回值:无
* 函数作用:初始化定时器
*/
void TimerInit(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};
// 定时器基本功能配置
htim.Instance = TIM2; // 使用定时器 2
htim.Init.Prescaler = 72-1; // 预分频系数 PSC=72-1(范围:0~0xFFFF)
// 72MHz 经过 72 分频后,定时器时钟为 1MHz,即定时器计数 1 次的时间,刚好为 1us
htim.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim.Init.Period = 0; // 自动装载器 ARR 的值 (范围:0~0xFFFF)
htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频(与输入采样相关)
//htim.Init.RepetitionCounter = 0; // 重复计数器值,仅存在于高级定时器
htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 不自动重新装载
if (HAL_TIM_Base_Init(&htim) != HAL_OK)
{Error_Handler(); }
// 定时器时钟源选择
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; // 选用内部时钟作为定时器时钟源
if (HAL_TIM_ConfigClockSource(&htim, &sClockSourceConfig) != HAL_OK)
{Error_Handler(); } }
/*
* 函数名:void HAL_TIM_Base_MspInit(SPI_HandleTypeDef *hspi)
* 输入参数:htim-TIM 句柄
* 输出参数:无
* 返回值:无
* 函数作用:使能 TIM 时钟
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM2)
{__HAL_RCC_TIM2_CLK_ENABLE(); // 使能 TIM2 的时钟
} }
  • 18行:选择配置哪一个定时器;
  • 19行:设置定时器时钟预分频系数PSC,这里设置为72-1,则72MHz经过72分频后,定时器时钟为1MHz,
    即定时器计数1次的时间,刚好为1us;
  • 21行:设置定时器计数方式,支持五种计数方式:
    • 向上计数模式(TIM_COUNTERMODE_UP):计数器从0向上计数(递增)到自动装载值ARR,随后再次回到0开始计数,并产生一个计数器向上溢出事件
    • 向下计数模式(TIM_COUNTERMODE_DOWN):计数器从自动装载值ARR向下计数(递减)到0, 随后再次回到自动装载值开始计数,并产生一个计数器向下溢出事件;
    • 中央对齐模式1/2/3计数器(TIM_COUNTERMODE_CENTERALIGNED1/2/3):计数器从0开始计数到自动装载值ARR-1,并产生一个计数器溢出事件,而后再向下计数到0+1,并产生一个计数溢出事件,随后再向上计数;
  • 22行:设置自动装载器ARR的值,这里设置为任意,后面代码再修改;
  • 23行:设置时钟分频,用于计数器工作时滤除高频干扰,本实验不涉及,任意即可;
  • 24行:设置重复计数器值,仅存在于高级定时器,这里使用的TIM2为通用定时器,不涉及;
  • 25行:设置是否定时器自动重新装载,本实验不需要自动装载,原因后续代码讲解;
  • 27~30行:将定时器按基本功能初始化,同时该函数会调用“HAL_TIM_Base_MspInit()”进行硬件相关初始化;
  • 46-51行:设置内部时钟作为定时器时钟源;
  • 47~53行:覆写“HAL_TIM_Base_MspInit()”,使能TIM2时钟;

本实验想实现us级的延时,考虑到延时时间太短,如果使用中断,会反复进中断,导致无法正常执行其它代码。因此本实验不使用中断,直接操作寄存器设置定时器加载值,如代码段 25.3.2 所示。

代码段 25.3.2 延时函数实现(driver_timer.c)

/*
* 函数名:void us_timer_delay(uint16_t t)
* 输入参数:t-延时时间 us 范围-0~65535us
* 输出参数:无
* 返回值:无
* 函数作用:定时器实现的延时函数,延时时间为 t us,为了缩短时间,函数体使用寄存器操作
*/
void us_timer_delay(uint16_t t)
{uint16_t counter = 0;
__HAL_TIM_SET_AUTORELOAD(&htim, t); // 设置定时器自动加载值
__HAL_TIM_SET_COUNTER(&htim, counter); // 设置定时器初始值
HAL_TIM_Base_Start(&htim); // 启动定时器
while(counter != t) // 直到定时器计数从 0 计数到 t 结束循环,刚好 t us
{counter = __HAL_TIM_GET_COUNTER(&htim); // 获取定时器当前计数
}
HAL_TIM_Base_Stop(&htim); // 停止定时器
}
/*
* 函数名:void ms_timer_delay(uint16_t t)
* 输入参数:t-延时时间 ms 范围-0~65535ms
* 输出参数:无
* 返回值:无
* 函数作用:定时器实现的延时函数,延时时间为 t ms
*/
void ms_timer_delay(uint16_t t)
{int i = 0;
for(; i<t; i++) {us_timer_delay(1000); } }
  • 11行:使用HAL库提供的“__HAL_TIM_SET_AUTORELOAD()”直接设置ARR寄存器的值;前面设置定时器时钟,使其每计数1次,刚好为1us,因此这里要t us,设置ARR为t,即定时器从0计数到t,刚好t us;
  • 12行:使用HAL库提供的“__HAL_TIM_SET_COUNTER ()”直接设置CNT计数器的值,保证定时器从0开始计数;
  • 13行:启动定时器;
  • 14~17行:此时定时器变从0到t开始计数,使用“__HAL_TIM_GET_COUNTER()”获取当前CNT计数器的值,当CNT计数器的值等于t时,时间经历了0到t个1us,实现了延时t us;
  • 18行:停止定时器,无需自动重新装填ARR值;
  • 28~36行:因为实现原理的限制,“us_timer_delay()”最多能延时65535us,即65ms多点,如果想要秒级的延时,需要再封装ms的延时;
  1. 主函数控制逻辑
    在主函数中,先初始化定时器和LED灯,通过LED间歇1s闪烁的效果,粗略感受代码和实验现象是否一致,如代码段 25.3.3 所示。

代码段 25.3.3 主函数控制逻辑(main.c)

// 初始化定时器
TimerInit();
// 在 windows 下字符串\n\r 表示回车
// 如果工程在编译下面这句中文的时候报错,请在“Option for target”->"C/C++"->"Misc Controls"添加“ --locale=english”
printf("**********************************************\n\r");
printf("-->百问科技 www.100ask.net\n\r");
printf("-->定时器 us 延时实验\n\r");
printf("**********************************************\n\r");
// 初始化 LED
LedGpioInit();
while(1) {/* 通过延时一段时间让 LED 亮灭实现 LED 闪烁,可以通过示波器打 LED 的引脚反转周期,精确看时间是否与设置的一致*/
RLED(ON); // 点亮 LED
ms_timer_delay(1000);; // 延时 1ms*1000=1s
RLED(OFF); // 熄灭 LED
ms_timer_delay(1000); // 延时 1ms*1000=1s
}

25.4 实验效果

本实验对应配套资料的“5_程序源码\17_定时器—us延时\”。打开工程后,编译,下载,可以看到LED红色灯,间歇1秒闪烁。

如果读者想精确一点的测试,可参考前面“第11章 基础重点—SysTick定时器”的测试方法,修改主函数代码,使用示波器或逻辑分析仪测试。


百问网技术论坛:
http://bbs.100ask.net/

百问网嵌入式视频官网:
https://www.100ask.net/index

百问网开发板:
淘宝:https://100ask.taobao.com/
天猫:https://weidongshan.tmall.com/

技术交流群2(鸿蒙开发/Linux/嵌入式/驱动/资料下载)
QQ群:752871361

单片机-嵌入式Linux交流群:
QQ群:536785813

韦东山嵌入式培训班交流群③
QQ群:717041375

STM32定时器US延时相关推荐

  1. STM32学习笔记(五)——通用定时器计数延时

    STM32定时器概述 STM32F40x系列总共最多有14个定时器,定时器分为三类:基本定时器.通用定时器和高级定时器.它们的都是通过计数来达到定时的目的,和51的定时器差不多,基本原理都是一样的,就 ...

  2. STM32 us精确延时方式

    STM32精确延时方式 使用TIM定时器方式延时 使用空代码方式延时 F103系列 F429系列 本文纯属记录自己学习历程,如有不妥之处,欢迎指正. 使用TIM定时器方式延时 使用STM32CubeM ...

  3. stm32定时器输出pwmIO口模拟pwm——呼吸灯

    文章目录 前言 一.pwm(脉冲宽度调制) 1.基本原理 2.PWM的优点 3.PWM波的控制方法 二.定时器的相关介绍 1.stm32定时器 2.通用定时器计数模式 3.定时器的基本工作原理 三.定 ...

  4. STM32定时器配置(TIM1、TIM2、TIM3、TIM4、TIM5、TIM8)高级定时器+普通定时器,定时计数模式下总结

    STM32定时器配置(TIM1.TIM2.TIM3.TIM4.TIM5.TIM8)高级定时器+普通定时器,定时计数模式下总结 文章结构: --> 一.定时器基本介绍 --> 二.普通定时器 ...

  5. STM32定时器中断

    目录 一.关于STM32定时器中断 1.定时器分类 2.通用定时器的功能特点 3.定时器中断的触发 4.定时时钟计算方法 二.CubeMX初始化配置 1.芯片选型(我们这里运用的STM32F103C8 ...

  6. stm32定时器实验

    目录 1.定时器简介 2.软件设计 3.代码 main timer.c timer.h 1.定时器简介 stm32定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波 形(输出比较和 P ...

  7. STM32 定时器详解

    STM32 定时器详解 吃了一个猛亏,自己理解花了大半天时间,结果一看代码发现巨简单 算了,把自己理解的放上来吧 目录 STM32 定时器详解 前言 一.定时器种类和区分 二.时钟源 三.计数过程 3 ...

  8. stm32关定时器_【菜鸟必看】stm32定时器的妙用

    摘要:本文为你带来关于stm32定时器的使用的便利和优势之处. 使用定时器去计算获取一条的时间 一.初步了解定时器 stm32定时器时钟图如下: 定时器2-7:普通定时器 定时器1.8:高级定时器 二 ...

  9. STM32定时器的预装寄存器及影子寄存器PSC—ARR-CCRx

    在谈预装寄存器及影子寄存器的差别前,不妨先对STM32定时器的时基单元做个基本了解.STM32各系列的定时器结构和框架基本是一样的,时基单元也一样. 下面时基单元是以STM32F3系列为参考. 时基单 ...

  10. STM32三种延时函数实现方法

    想学习单片机的同学可以关注.私信我或者在评论区回复我要入门.在51入门的时候我们第一个实验就是点亮LED灯,如果没有延时,我们就很难看到亮灭效果. 1. STM32延时函数概述 在产品开发的过程中我们 ...

最新文章

  1. 谈谈“个人电子信息”的保护
  2. Nervos Report (2018年12月)
  3. DockerSwarm 微服务部署
  4. python语言标识符首字符不能是汉字_Python基本语法元素
  5. JAVA 笔记no.2
  6. Centos7_ELK5.4.1配置部署
  7. OC__part11
  8. 关于javascript中私有作用域的预解释
  9. BugkuCTF-WEB题MD5
  10. 有时便去寻找思维以外的精神
  11. c语言中错误为ffblk未定义,C语言中头文件及函数的含意的总分类
  12. HPU 1476: 括号括号
  13. (转)tomcat配置访问项目时不需要加项目名称
  14. python numpy安装步骤-python的numpy模块安装不成功简单解决方法总结
  15. linux sipp 呼叫转移_★★★★盲转接业务的sipp脚本实现
  16. rs232接口_串口,COM口,TTL,RS232,RS485,UART的区别详解
  17. 基于树莓派的视频会议系统
  18. NLP+词法系列(一)︱中文分词技术小结、几大分词引擎的介绍与比较
  19. 动手下载网易课程视频 -- 正式下载
  20. PostgreSQL的XML类型

热门文章

  1. flutter之出现 List is not a subtype of type 问题
  2. 思科模拟器路由表怎么看_Cisco路由配置教程 Cisco路由器静态路由与默认路由的配置方法图解...
  3. 热敏打印计算机,热敏打印头工作原理是什么 热敏打印头原理介绍【详解】
  4. JAVA核心知识点--获取HttpServletRequest请求Body中的内容
  5. 小炫酷的3D旋转立方体相册
  6. 听完计算机知识讲座后感悟,计算机技能培训心得感想
  7. c 语言温度换算的程序,华氏温度换算公式及C语言转换程序代码
  8. python解二元一次方程组 迭代法_解二元一次方程组多种方法
  9. LookaHead优化器
  10. Unable to instantiate service com.baidu.android.pushservice.PushService: java.lang.ClassNotFoundExc