STM32基础定时器详解_stm32定时器_strongercjd的博客-程序员秘密

技术标签: STM32F207教程  单片机  

目录

01、定时器介绍

02、时钟源

03、时基单元

04、计数模式

4.1、向上计数模式

4.2、向下计数模式

4.3、中央对齐(向上/向下计数模式)

05、基础定时代码


定时器最基本的功能就是定时处理事情。比如定时发送USART数据、定时采集AD数据、定时检测IO口电位、还可以通过IO口输出波形等。可以实现非常丰富的功能。定时器是一个很强大的外设,不同行业使用的方式不同,知识面很广。

01、定时器介绍

首先我们可以在STM32F207数据手册找到定制器的资源,从下图可以看到STM32F207一共10个通用定时器,2个高级定时器,2个基本定时器。

不同定时器的区别

在STM32F207的用户参考手册中可以看到定时器的基本框图,下图是定时器1&8的看图。

由上图看出,不同寄存器具有不同的参数,位数的区别,计数模式的区别,DMA请求的区别,通道得的区别,互补输出的区别和其他。在具体项目中选择哪个定时器,需要看具体的应用场景。下文主要讲解定时器的基础定时功能,选择定时器3。其他定时器原理是相同的,理解定时器3的定时功能,其他定时器也就能理解了。对于STM32系列的单片机,外设基本都是一致的,并且其他家的MCU也是类似的,国内的有兆易创新、新唐科技、上海灵动微电子等等。

02、时钟源

定时器基本定时功能框图。

①CK_PSC是定时器时钟TIMxCLK,经APB1预分频器后分频提供。

②定时器时钟经过PSC 预分频器之后,即CK_CNT,用来驱动计数器计数。

③计数器CNT 是一个16 位的计数器,向上,向下,向上/下计数模式,最大计数值为65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。

④自动重装载寄存器ARR 是一个16位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。

定时器说白了就是个计数器,就像我们用心跳粗略估算时间一样,心脏跳动粗略可以认为是1s,那么我们计时60次心跳就过了60秒。其中CK_CNT时钟就类似心跳,CNT计数器就类似心跳次数。举一个极端简单的例子,我们要实现60秒定时,CK_CNT是1s,我们设置CNT计数器向上计数开启中断,因为只有溢出时,也就是计数到65535时才会有中断,那么我们设置CNT计数器为65535-60=65475,开始及时,那么60秒后就会产生中断。我们设置自动重装载寄存器ARR也为65475,当CNT计数器溢出时,自动重装载寄存器ARR就会自动装载到CNT计数器中,就能实现自动循环定时60秒。

经过上面分析,精确定时的关键在于CK_CNT的频率,而CK_CNT是由定时器时钟分频而来的。那么我们就要知道timer3的定时器时钟。我们就要看时钟系统部分,具体看文章《STM32F207时钟系统解析》,这篇文章主要讲解了系统的120M时钟如何从外部的25M的晶振得到的。其中说到APB外设时钟的问题。

定时器在APB定时器时钟下,具体在APB1还是在APB2时钟下我们可以从STM32F207数据手册上看到,图片名字STM32F20xblock diagram。

从上文我们看timer3是在APB1下的。

那么我们来分析APB1的频率

从上图看出,APB1定时器的从系统120M时钟(系统时钟可配置的,我们使用默认的120M时钟)经过AHB分频,APB分频得到的。

在这里说上图红框中的“错误”,萌新可能不太理解。首先手册中少了个右括号。修改后应该为:

if(APBx presc == 1)
    X1
else
    X2

也就是说

APB分频系数如果是1,频率不变,APB输出的频率就是APB下面时钟的频率。

APB分频系数不是1,频率X2,APB输出的频率乘以2是APB下面时钟的频率。

下面我们分析APB1时钟,从system_stm32f2xx.c中的SetSysClock函数中如下

/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
    
/* PCLK2 = HCLK / 2*/
RCC->CFGR |=RCC_CFGR_PPRE2_DIV2;
    
/* PCLK1 = HCLK / 4*/
RCC->CFGR|= RCC_CFGR_PPRE1_DIV4;

可以看到AHB分频系数是1,APB1分频系数是4。

timer3的时钟为120M/1/4*2= 60MHZ。

这里有个疑问,ST提供的system_stm32f2xx.c注释为什么是HCLK,PCLK2,PCLK1,却没有上文提到的APB,AHB字眼,具体看下我之前写过的文章《STM32F207时钟系统解析》。

其实我们出了分析代码,system_stm32f2xx.c文件头也是有注释的,方便查看。

当然,这要求我们的外部晶振是25M的,且system_stm32f2xx.c是没有被修改过的,如果大家需要修改这个文件单片机超频运行,建议把文件头的注释修改,养成一个良好的习惯。

03、时基单元

可编程高级定时器控制模块主要是一个带有相关自动重载16位计数器。这个计数器可以向上计数,向下计数或者交替递增和递减计数。计数器时钟可以通过一个分频器分频。

计数器的自动重载寄存器和预分频寄存器可以通过软件读写。即使当计数器正在运行也可以读写。

时基单元包括

  1. 计数器寄存器 (TIMx_CNT)

  2. 预分频器寄存器 (TIMx_PSC)

  3. 自动重载寄存器 (TIMx_ARR)

  4. 重复计数器寄存器 (TIMx_RCR)

自动重载寄存器是预装载的。从自动重载寄存器写入或读取会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件(UEV)时传送到影子寄存器,这取决于TIMx_CR1 寄存器中的自动重载预装载使能位(ARPE)。当计数器达到上溢值(或者在递减计数时达到下溢值)并且TIMx_CR1 寄存器中的UDIS 位为0时,将发送更新事件。该更新事件也可由软件产生。

计数器由预分频器输出CK_CNT 提供时钟,仅当TIMx_CR1 寄存器中的计数器启动位(CEN)置1 时,才会启动计数器。

预分频器说明

预分频器可对计数器时钟频率进行分频,分频系数介于1 和65536 之间。该预分频器基于TIMx_PSC寄存器中的16 位寄存器所控制的16位计数器。由于该控制寄存器具有缓冲功能,因此可对预分频器进行实时更改。而新的预分频比将在下一更新事件发生时被采用。

下图以一些示例说明在预分频比实时变化时计数器的行为。

预分频器分频由1 变为2 时的计数器时序图

预分频器分频由1 变为4 时的计数器时序图

04、计数模式

4.1、向上计数模式

在向上计数模式中,计数器从0增加到自动重载值(TIMx_ARR寄存器的值),然后从0重新开始并产生一个计数器溢出事件。

如果使用重复计数器,则当递增计数的重复次数达到重复计数器寄存器中编程的次数加一次(TIMx_RCR+1)后,将生成更新事件(UEV)。否则,将在每次计数器上溢时产生更新事件。

将TIMx_EGR寄存器的UG位置1通过软件或使用从模式控制器时,也将产生更新事件。

通过软件将TIMx_CR1寄存器中的UDIS位置1可禁止UEV事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在UDIS位写入0之前不会产生任何更新事件。不过,计数器和预分频器计数器都会重新从0开始计数(而预分频比保持不变)。此外,如果TIMx_CR1寄存器中的URS位(更新请求选择)已置1,则将UG位置1会生成更新事件UEV,但不会将UIF标志置1(因此,不会发送任何中断或DMA请求)。这样一来,如果在发生捕获事件时将计数器清零,将不会同时产生更新中断和捕获中断。

发生更新事件时,将更新所有寄存器且将更新标志(TIMx_SR寄存器中的UIF位)置1取决于URS位)

  1. 重复计数器中将重新装载TIMx_RCR寄存器的内容

  2. 自动重载影子寄存器将以预装载值 (TIMx_ARR) 进行更新

  3. 预分频器的缓冲区中将重新装载预装载值(TIMx_PSC寄存器的内容)

计数器时序图,1 分频内部时钟

计数器时序图,2 分频内部时钟

从上面两图看出,中断标志是需要软件清除的

计数器时序图,ARPE=0 时更新事件(TIMx_ARR 未预装载)

从上面两图看出,向上计数,还没有到达0x36,就把自动重载寄存器修改为0x36,就会在计数到0x36时产生动作。

计数器时序图,ARPE=1 时更新事件(TIMx_ARR 预装载)

从上面两图看出,向上计数,还没有到达0x36,就把自动重载预装载寄存器修改为0x36,就不会在计数到0x36时产生动作,会在这个时将自动重载预装载寄存器值赋给自动重载影子寄存器。

4.2、向下计数模式

在向下计数模式中,计数器从自动重载值(TIMx_ARR寄存器的值)向下计数到0,然后从自动重载值(重新开始并产生一个计数器溢出事件。

如果使用重复计数器,则当递减计数的重复次数达到重复计数器寄存器中编程的次数加一次(TIMx_RCR+1)后,将生成更新事件(UEV)。否则,将在每次计数器下溢时产生更新事件。

将TIMx_EGR 寄存器的UG 位置1(通过软件或使用从模式控制器)时,也将产生更新事件。

通过软件将TIMx_CR1 寄存器中的UDIS 位置1 可禁止UEV 更新事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在UDIS 位写入0之前不会产生任何更新事件。不过,计数器会重新从当前自动重载值开始计数,而预分频器计数器则重新从0 开始计数(但预分频比保持不变)。

此外,如果TIMx_CR1 寄存器中的URS 位(更新请求选择)已置1,则将UG 位置1 会生成更新事件UEV,但不会将UIF 标志置1(因此,不会发送任何中断或DMA 请求)。这样一来,如果在发生捕获事件时将计数器清零,将不会同时产生更新中断和捕获中断。

发生更新事件时,将更新所有寄存器且将更新标志(TIMx_SR 寄存器中的UIF 位)置1(取决于 URS 位):

  1. 重复计数器中将重新装载 TIMx_RCR 寄存器的内容

  2. 预分频器的缓冲区中将重新装载预装载值( TIMx_PSC 寄存器的内容)

  3. 自动重载活动寄存器将以预装载值( TIMx_ARR 寄存器的内容)进行更新。注意,自动重载寄存器会在计数器重载之前得到更新,因此,下一个计数周期就是我们所希望的新的周期长度

以下各图以一些示例说明当TIMx_ARR=0x36 时不同时钟频率下计数器的行为

计数器时序图,1 分频内部时钟

计数器时序图,2 分频内部时钟

计数器时序图,未使用重复计数器时更新事件

4.3、中央对齐(向上/向下计数模式)

在中心对齐模式下,计数器从0 开始计数到自动重载值(TIMx_ARR 寄存器的内容)-1,生成计数器上溢事件;然后从自动重载值开始向下计数到1 并生成计数器下溢事件。之后从0开始重新计数。

当TIMx_CR1 寄存器中的CMS位不为“00”时,中心对齐模式有效。将通道配置为输出模式时,其输出比较中断标志将在以下模式下置1,即:计数器递减计数(中心对齐模式1,CMS=“01”)、计数器递增计数(中心对齐模式2,CMS =“10”)以及计数器递增/递减计数(中心对齐模式3,CMS =“11”)。

在此模式下,TIMx_CR1 寄存器的DIR 方向位不可写入值,而是由硬件更新并指示当前计数器方向。

每次发生计数器上溢和下溢时都会生成更新事件,或将TIMx_EGR 寄存器中的UG 位置1(通过软件或使用从模式控制器)也可以生成更新事件。这种情况下,计数器以及预分频器计数器将重新从0 开始计数。

通过软件将TIMx_CR1 寄存器中的UDIS 位置1 可禁止UEV 更新事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在UDIS 位写入0 之前不会产生任何更新事件。不过,计数器仍会根据当前自动重载值进行递增和递减计数。

此外,如果TIMx_CR1 寄存器中的URS 位(更新请求选择)已置1,则将UG 位置1 会生成UEV 更新事件,但不会将UIF 标志置1(因此,不会发送任何中断或DMA 请求)。这样一来,如果在发生捕获事件时将计数器清零,将不会同时产生更新中断和捕获中断。

发生更新事件时,将更新所有寄存器且将更新标志(TIMx_SR 寄存器中的UIF 位)置1(取决于URS 位):

  1. 重复计数器中将重新装载 TIMx_RCR 寄存器的内容

  2. 预分频器的缓冲区中将重新装载预装载值( TIMx_PSC 寄存器的内容)

  3. 自动重载活动寄存器将以预装载值( TIMx_ARR 寄存器的内容)进行更新。注意,如果更新操作是由计数器上溢触发的,则自动重载寄存器在重载计数器之前更新,因此,下一个计数周期就是我们所希望的新的周期长度(计数器被重载新的值)。

以下各图以一些示例说明不同时钟频率下计数器的行为

计数器时序图,1 分频内部时钟,TIMx_ARR = 0x6

计数器时序图,2 分频内部时钟

计数器时序图,ARPE=1 时的更新事件(计数器下溢)

计数器时序图,ARPE=1 时的更新事件(计数器上溢)

05、基础定时代码

10ms中断配置代码

关于设置分频值

TIM3CLK = 2 * PCLK1=2*HCLK / 4= HCLK / 2 = SystemCoreClock /2=60MHZ

所以下图红框内就是TIM3CLK

这里的值是分频系数=TIM3CLK/定时器实际频率,所以定时器频率是10000,也就是说除数就是定时器频率。一个clk是1/10000s。定时时间=1/10000*定时器重载值。根据上面的配置,定时器重载值是100,也就是定时器中断周期是=1/10000*100=0.01s=10ms,也就是100HZ。

如果在定时器翻转LED灯,那么LED灯闪烁频率是50Hz。

上面的的分频值当然可以直接赋值5999,如果想修改为定时器频率为1000,那么还要重新计算。如果按照上面的写法,直接将除数修改为1000即可。

看到这里大家会有疑问,给的重载值明明是99,分频率值也减去1。下面将说明分频值和自动重载周期值都需要减去1的原因。

自动重载值:因为从0开始计算,赋值10,从0开始计数到10是11次。

分频值:在TIMx_PSC寄存器有以下描述。

特别说明

时钟分频因子

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2;

其实仔细看过技术手册后发现这句话与PWM输出实验其实是没关系的,这句话是设置定时器时钟(CK_INT)频率与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例的(与输入捕获相关),0表示滤波器的频率和定时器的频率是一样的。

首先这个colck_division时钟分割系数并不是对定时器的时钟频率进行分割。我们都知道输入捕获模式下有一个数字滤波器,这个数字滤波器可以通过配置寄存器改变他的采样频率,从而将一些频率滤除。

具体细节在输入捕获中详解。

我们也可以根据定时器的计数器的特性,使用查询计数器的方法实现精确延时,具体请看《STM32延时函数的四种方法》。

定时器代码开源地址:

https://github.com/strongercjd/STM32F207VCT6

点击查看本文所在的专辑,STM32F207教程

关注公众号,第一时间收到文章更新。评论区不能及时看到,需要交流可以到公众号沟通

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Firefly_cjd/article/details/108069629

智能推荐

【论文笔记】— Network Compression via Central Filter_feature map相似度_m0_46372081的博客-程序员秘密

文章是基于冗余滤波的剪枝,通过衡量输出feature map之间相似度,剪枝掉相似的filter,进行权重调整。一、方法介绍:(1)符号说明(2)测量Feature之间的相似度采用Pearson相关系数来度量输出的feature map之间的相似度:那么第i层两输出的feature map之间的相似度:此外,文章做了大量的实验观察到输出的feature map之间的相似性是很稳定的,实验结果如下图:其中,横坐标表示不同的featur...

零碎整理_驴行的博客-程序员秘密

一、内存分配中的栈式和堆式分配<br />在高级程序设计语言(如c/c++、java、c#等)中,对于值类型和引用类型变量在内存中分配空间一般是以栈式和堆式来分配的。<br />栈式分配的内存申请和释放都由系统来管理;堆式分配的内存由程序员申请空间大小,至于释放分为由程序员释放(如c/c++)和由编译系统垃圾回收(如java、c#托管型)两种。<br />问题:<br />1、栈式和堆式的各自特点(从数据结构实现角度)、运行时特性(用于存放什么类型变量,速度、效率如何)都需要好好考虑考虑。<br />2、j

vscode注释快捷键失效且无键位冲突时解决方案_vscode快捷键冲突_叫我啊哈的博客-程序员秘密

vscode注释快捷键失效且无键位冲突时解决方案,!+enter无效的也可以试试

GeoMesa-HBase部署实践_geoserver 配置hbase_萧博士的博客-程序员秘密

关于HBase实验环境的搭建请参阅我的另一篇文章:https://blog.csdn.net/xiaof22a/article/details/802130641. GeoMesa源代码编译由于考虑到日后需要基于GeoMesa进行二次开发,所以本文采用的是编译GeoMesa源代码的方式,如果读者仅仅为了学习应用GeoMesa进行空间数据管理,可以直接从官方下载已经编译好的GeoMesa HBase...

23种设计模式彩图《设计模式之禅-附录》_ls0111的博客-程序员秘密

转载地址:https://blog.csdn.net/hellocooper/article/details/48400457

用java编写的一个迪杰斯特拉算法(单源最短路径算法,Dijkstra算法)。_liuhenghui5201的博客-程序员秘密

可以用于有向图和无向图。用负数表示该有向路不通。在EditPlus上写的,所以就一个.java文件。package Test;import java.util.TreeMap;import java.util.ArrayList;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io

随便推点

Linux驱动设计硬件基础(六)硬件时序分析_sram读写时序图详解_静能生悟的博客-程序员秘密

2.6 硬件时序分析2.6.1 时序分析的概念    驱动工程师一般不需要分析硬件的时序,但许多企业内驱动工程师还需要承担电路板调试的任务,因此,掌握时序分析的方法也就比较必要了。    对驱动工程师或硬件工程师而言,时序分析是让芯片之间的访问满足芯片数据手册中时序图信号有效的先后顺序、采样建立时间(Setup Time)和保持时间(Hold Time)的要求,在电路板工作不正常的时候,准确地定位...

Linux内核源码分析之《内存管理架构》_linux内核源码分析之内存_linux大本营的博客-程序员秘密

Linux内核源码分析之《内存管理架构》|虚拟地址空间布局详解、物理地址空间与内存组织、创建与删除内存映射、页表缓存与处理器缓存专注后台服务器开发,包括C/C++,Linux,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂...

服务熔断与降级(Sentinel)_两米以下皆凡人的博客-程序员秘密

1、概述官方Git仓库中文文档①、什么是Sentinel随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。回顾以前的Hystrix,我们做到了服务熔断,服务降级,但是Hystrix没有一套Web界面可以让我们更加细粒化的配置,Sentinel和Nacos一样,不再需要我们手动的去创建某个微服务模块来专门实现对应功能,直接用一套Web界面供我们进行细粒化的统一配置,管理。②、Windows

利用opencv-python绘制多边形框或(半透明)区域填充(可用于分割任务mask可视化)_opencv 多边形mask_Adenialzz的博客-程序员秘密

利用opencv-python绘制多边形框或(半透明)区域填充(可用于分割任务mask可视化)本文主要就少opencv中两个函数polylines和fillPoly分别用于绘制多边形框或区域填充,并会会以常见用途分割任务mask(还是笔者的猪仔数据集^^)可视化举例示范。cv2.polylines()以下是摘自Ref的函数介绍,笔者将在下面结合例子解释其中的参数。cv2.polylines() method is used to draw a polygon on any image.Synta

MySQL忘记密码的正确解决方法_weixin_33875839的博客-程序员秘密

为什么80%的码农都做不了架构师?&gt;&gt;&gt; ...

Android应用调试-BugReport_adb bugreport_weixin_49858366的博客-程序员秘密

文章目录一、BugReport是什么?二、如何获取bug reports1.从手机上获取bug reports2.使用 adb 获取bug reports三、如何对BugReport进行分析?总结一、BugReport是什么?在安卓应用开发中,app程序的调试分析是日常生产中进程会进行的工作。而BugReport中包含了设备日志、堆栈轨迹和其他诊断信息,可以帮助开发人员查找和修复应用中的错误。二、如何获取bug reports一般来说有三种方法可以获取Bug reports,分别是通过安卓手机直接