目录

0.STM32中功能最强大、结构最复杂的一个外设——定时器(四部分学习)

第一部分:

第二部分:

第三部分:

第四部分:

1.TIM简介

2.定时器类型

3.基本定时器

时基单元:

预分频器:

计数器:

自动重装寄存器:

UI向上的箭头:

U之后向下的箭头:

基本定时器定时中断的全流程:

4.通用定时器

内外时钟源选择和主从触发模式的结构

时钟输入

外部时钟模式1路径选择

总结:

定时器的编码器接口:

TRGO:

输出比较电路和输入捕获电路

5.高级定时器

重复次数计数器:

输出比较模块升级版:

6.定时中断基本结构

7.预分频器时序

8.计数器时序

9.计数器无预装时序

10.计数器有预装时序

11.RCC时钟树

时钟产生电路

时钟分配电路

12.定时器定时中断代码

第一步:按图接线到面包板上

第二步:复制OLED工程代码,模块化定时器代码

第三步:初始化定时器

初始化步骤:

定时器基本库函数

第四步:写中断函数和头文件声明

第五步:主函数调用

13.定时器外部时钟代码

第一步:按图接线到面包板上

第二步:复制定时器定时中断代码,改名为本工程名

第三步:修改代码,将内部时钟改为外部时钟

Timer.c

Timer.h

main.c

14.定时器基本定时的功能相关库函数


0.STM32中功能最强大、结构最复杂的一个外设——定时器(四部分学习)

第一部分:

主要是定时器基本定时的功能,定一个时间,然后让定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段时间的目的,比如:时钟、秒表、一些程序算法时

第二部分:

主要是定时器输出比较的功能,输出比较这个模块最常见的用途是产生PWM波形,用于驱动舵机、直流电机等设备

第三部分:

主要是定时器输入捕获的功能,使用输入捕获这个模块来实现测量方波频率的例子

第四部分:

主要是定时器的编码器接口,使用这个编码器接口,能够更加方便地读取正交编码器的输出波形,在编码电机测速中,应用非常广泛


1.TIM简介

TIM(Timer)定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s(72M/65536/65536)的定时,如果觉得不够长,STM32的定时器还支持级联(一个定时器的输出当做另一个定时器的输入)的模式,级联一个后最大定时间就是59.62*65536*65536(八千多年),以此类推

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型


2.定时器类型

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4


3.基本定时器

时基单元:

预分频器:

写0,不分频(1分频),输出频率=输入频率=72MHz;写1,2分频,输出频率=输入频率/2=36MHz,写2,3分频,输出=输入/3,以此类推

即实际分频系数=预分频器的值+1,这个预分频器是16位,所以最大值可以为65535,也就是65536分频

计数器:

对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值就加1,这个计数器也是16位的,值可以从0一直加到65535,65535之后归0

自动重装寄存器:

存储目标值的寄存器,16位,存的是写入的计数目标,在运行的过程中,计数值不断自增,自动重装值是固定的目标,当计数值等于自动重装值时,也就是计时时间到,产生一个中断信号(更新中断),并且清零计数器

UI向上的箭头:

更新中断之后会通往NVIC,再配置好NVIC的定时器通道,这样定时器的更新中断就能够得到CPU的响应

U之后向下的箭头:

产生一个事件,这里对应的事件叫做“更新事件”,更新事件不会触发中断,但可以触发内部其他电路的工作

基本定时器定时中断的全流程:

从基准时钟,到预分频器,再到计数器,计数器计数自增,同时不断地与自动重装寄存器进行比较,计时器和自动重装寄存器的值相等时,即时间到,这时对产生一个更新中断和更新事件,CPU响应更新中断


4.通用定时器

不仅支持向上计数模式(计数器自增到重装值,重新计数)还支持向下计数模式(从重装值开始,向下自减,减到0之后,回到重装值同时申请中断)和中央对齐模式(从0开始,先向上自增,计到重装值申请中断,再向下自减,到0之后申请中断)

内外时钟源选择和主从触发模式的结构

时钟输入

通用时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟,第一个外部时钟TIMx_ETR引脚上的外部时钟,接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路,这些对输入的波形滤波整形,最后滤波后的信号,兵分两路,上面一路ETRF进入触发控制器,就跟着选择就可以选择作为时基单元的时钟(外部时钟模式2),下面一路到数据选择器,到TRGI当做外部时钟来使用,也可以选择作为时基单元的时钟(外部时钟模式1)

外部时钟模式1路径选择

ITR信号,这一部分的时钟信号是来自其他定时器的,从右边可以,这个主模式的输出TRGO可以通向其他定时器,,通向其他定时器的时候,就接到了其他定时器的ITR引脚上,通过这一路就可以实现定时器级联的功能。比如:先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上接着再初始化TIM2(这里选择ITR2),对应的就是TIM3的TRGO,选择外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联

TI1F_ED(ED(Edge)边沿的意思),连接的是输入捕获单元的CH1引脚,也就是从CH1引脚边沿获得时钟,上升沿下降沿均有效

TI1FP1和TI2FP2:时钟还能通过TI1FP1和TI2FP2获得,TI1FP1连接到CH1,TI2FP2连接到CH2

总结:

外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚边沿、CH1引脚和CH2引脚

外部时钟模式2使用外部时钟首选

定时器的编码器接口:

可以读取正交编码器的输出波形

TRGO:

定时器的主模式1输出,这部分电路可以把内部的一些事件映射到这个TRGO引脚上,比如:基本定时器。将更新事件映射到TRGO,用于触发DAC

输出比较电路和输入捕获电路

输出比较电路:总共有四个通道CH1~CH4引脚,可以用于输出PWM波形,驱动电机

输入捕获电路:也有四个通道CH1~CH4引脚,可以用于测输入方波的频率

中间的寄存器是共用的,因为输出比较电路和输入捕获电路,不能同时一起使用


5.高级定时器

重复次数计数器:

这个计数器可以实现每隔几个计数周期,才发生一次更新事件和更新中断

输出比较模块升级版:

DTG是死区生成电路

右边的引脚由原来的一个变为了两个互补的输出,可以输出一对互补的PWM波,这些电路是为了驱动三相无刷电机(四驱飞行器、电动车的后轮、电钻等)

BKIN(刹车输入功能的引脚):产生刹车信号或者内部时钟失效,产生了故障,控制电路会自动切断电机的输出,防止意外的发生


6.定时中断基本结构


7.预分频器时序

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)


8.计数器时序

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)


9.计数器无预装时序


10.计数器有预装时序


11.RCC时钟树

时钟产生电路

SYSCLK就是系统时钟

在时钟产生电路,有四个震荡源:内部的8MHz高速RC震荡器、外部的4-16MHz高速石英晶体振荡器(晶振)一般接8MHz、外部的32.768KHz低速晶振,这个一般给RTC提供时钟,内部的40KHz低速RC振荡器,这个可以给看门狗提供时钟。前面两个高速晶振,是用来提供系统时钟的AHB、APB2、APB1的时钟都是来源于这两个高速晶振

在Systemlnit函数里,ST是这样配置时钟的,首先它会启动内部时钟,选择内部8MHz为系统时钟,暂时以8MHz的时钟运行,然后在启动外部时钟,配置外部时钟到PLL锁相欢进行倍频,8MHz倍频9倍,得到72MHz,等到锁相环输出稳定后,选择锁相环输出为系统时钟,这样8MHz切换为72MHz

CSS:时钟安全系统。负责切换时钟的,它可以监测外部时钟的运行状态,一旦外部时钟失效,就会切换为内部时钟,防止程序卡死

时钟分配电路

首先系统时钟72MHz进入AHB总线,AHB总线一个预分频器,在SystemInit里配置的分配系数为1,AHB的时钟就是72MHz,进入APB1总线,这里配置的分配系数是2,所以APB1总线的时钟位36MHz,下面还有一条支路让36MHz乘2为72MHz,APB2的时钟和AHB一样,都是72MHz


12.定时器定时中断代码

第一步:按图接线到面包板上

第二步:复制OLED工程代码,模块化定时器代码

第三步:初始化定时器

初始化步骤:

1.RCC开启时钟,定时器的基准时钟和整个外设的工作时钟就都会同时打开了;2.选择时基单元的时钟源,对于定时中断,选择内部时钟源;3.配置时基单元,用一个结构体就可以;4.配置输出中断控制,允许更新中断输出到NVIC;5.配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级;6.运行控制,整个模块配置完后,还需要使能计数器。之后还要写一个中断函数

定时器基本库函数

TIM_DeInit:恢复缺省配置

TIM_TimeBaseInit:时基单元初始化,参数(选择某个定时器,结构体(包含了配置时基单元的一些参数))

时基单元(结构体)参数:

CLockDivision:

指定时钟分频(滤波器子在工作时,会在一个固定的时钟频率f(时钟分频)下进行采样,如果连续N个采样点都为相同的电平,那就代表输入信号稳定,就把这个采样值输出,如果N个采样点不全都相同,那就说明信号有抖动,就保持上一个输出或低电平输出);

CounterMode:

计数器模式(向上计数、向下计数、中央对齐);

Period:

周期(ARR自动重装器的值);

Prescaler:

PSC预分频器的值;

RepetitionCounter:

重复计数器的值

TIM_TimeBaseStructInit:给结构体变量赋一个默认值

TIM_Cmd:使能计数器,参数(选择定时器,新的状态(使能或失能))

TIM_ITConfig:使能中断输出信号,参数(选择定时器,选择配置哪个中断输出,新的状态(使能或失能))

TIM_InternalClockConfig:选择内部时钟,参数(选择定时器)

TIM_ITRxExternalClockConfig:选择其他定时器的时钟,参数(选择定时器,选择要接入哪个其他的定时器)

TIM_TIxExternalClockConfig:选择捕获通道的时钟,参数(选择定时器,选择定时器具有的某个引脚,输入的极性,滤波器)

TIM_ETRClockMode1Config:选择ETR通过外部时钟模式1输入的时钟,参数(选择定时器,外部触发预分频器,极性,滤波器)

TIM_ETRClockMode2Config:选择ETR通过外部时钟模式2输入的时钟,参数(选择定时器,外部触发预分频器,极性,滤波器)

TIM_ETRConfig::单独配置ETR引脚的预分频器、极性、滤波器这些参数

TIM_PrescalerConfig:单独写预分频值的,参数(选择定时器,要写入的预分频值,写入模式)

TIM_CounterModeConfig:改变计数器的计数模式,参数(选择定时器,选择新的计数器模式)

TIM_ARRPreloadConfig:自动重装器预装功能配置,参数(选择定时器,新的状态(使能或失能))

TIM_SetCounter:给计数器写入一个值,参数(选择定时器,值)

TIM_SetAutoreload:给自动重装器写入一个值

TIM_GetCounter:获取当前计数器的值

TIM_GetPrescaler:获取当前预分频器的值

获取标志位和清除标志位的函数

前两个函数,在主程序查看和清除标志位,第一个可以获取指定的标志位是否被置为1,第二个可以对置1的标志位进行清除

后两个函数,在中断函数里查看和清除标志位,第一个获取中断标志位是否被置1,第二个清除中断挂起标志位

第四步:写中断函数和头文件声明

第五步:主函数调用

复位后计数会从1开始,因为TIM_TimeBaseInit函数的最后一句是TIMx->EGR = TIM_PSCReloadMode_Immediate(生成一个更新事件,来重新装载预分频器和重复计数器的值)这一句加上是因为预分频器是有一个缓冲寄存器的,写的值只有在更新事件时,才会真正起作用,所以为了让值立刻起作用,就在最后手动生成一个更新事件,这样的副作用就会造成更新事件和更新中断同时发生,相当于复位的时候中断函数在初始化之后就立刻进入了一次,所以复位后会从1开始。解决办法在定时器模块里TIM_TimeBaseInit函数后面调用清除标志位的函数

定时器定时中断完整代码


13.定时器外部时钟代码

第一步:按图接线到面包板上

第二步:复制定时器定时中断代码,改名为本工程名

第三步:修改代码,将内部时钟改为外部时钟

Timer.c

Timer.h

main.c

定时器外部中断完整代码


14.定时器基本定时的功能相关库函数

TIM_DeInit:恢复缺省配置

TIM_TimeBaseInit:时基单元初始化,参数(选择某个定时器,结构体(包含了配置时基单元的一些参数))

时基单元(结构体)参数:

CLockDivision:

指定时钟分频(滤波器子在工作时,会在一个固定的时钟频率f(时钟分频)下进行采样,如果连续N个采样点都为相同的电平,那就代表输入信号稳定,就把这个采样值输出,如果N个采样点不全都相同,那就说明信号有抖动,就保持上一个输出或低电平输出);

CounterMode:

计数器模式(向上计数、向下计数、中央对齐);

Period:

周期(ARR自动重装器的值);

Prescaler:

PSC预分频器的值;

RepetitionCounter:

重复计数器的值

TIM_TimeBaseStructInit:给结构体变量赋一个默认值

TIM_Cmd:使能计数器,参数(选择定时器,新的状态(使能或失能))

TIM_ITConfig:使能中断输出信号,参数(选择定时器,选择配置哪个中断输出,新的状态(使能或失能))

TIM_InternalClockConfig:选择内部时钟,参数(选择定时器)

TIM_ITRxExternalClockConfig:选择其他定时器的时钟,参数(选择定时器,选择要接入哪个其他的定时器)

TIM_TIxExternalClockConfig:选择捕获通道的时钟,参数(选择定时器,选择定时器具有的某个引脚,输入的极性,滤波器)

TIM_ETRClockMode1Config:选择ETR通过外部时钟模式1输入的时钟,参数(选择定时器,外部触发预分频器,极性,滤波器)

TIM_ETRClockMode2Config:选择ETR通过外部时钟模式2输入的时钟,参数(选择定时器,外部触发预分频器,极性,滤波器)

TIM_ETRConfig::单独配置ETR引脚的预分频器、极性、滤波器这些参数

TIM_PrescalerConfig:单独写预分频值的,参数(选择定时器,要写入的预分频值,写入模式)

TIM_CounterModeConfig:改变计数器的计数模式,参数(选择定时器,选择新的计数器模式)

TIM_ARRPreloadConfig:自动重装器预装功能配置,参数(选择定时器,新的状态(使能或失能))

TIM_SetCounter:给计数器写入一个值,参数(选择定时器,值)

TIM_SetAutoreload:给自动重装器写入一个值

TIM_GetCounter:获取当前计数器的值

TIM_GetPrescaler:获取当前预分频器的值

获取标志位和清除标志位的函数

前两个函数,在主程序查看和清除标志位,第一个可以获取指定的标志位是否被置为1,第二个可以对置1的标志位进行清除

后两个函数,在中断函数里查看和清除标志位,第一个获取中断标志位是否被置1,第二个清除中断挂起标志位

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐