带你了解STM32:TIM定时器(第一部分)
讲解STM32中功能最强大、结构最复杂的一个外设——定时器
目录
0.STM32中功能最强大、结构最复杂的一个外设——定时器(四部分学习)
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,第二个清除中断挂起标志位
更多推荐









所有评论(0)