【STM32】标准库 菜鸟入门教程之定时器中断

TIM(Timer)定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 。比如对72MHz计72个数,一个数就是1MHz就是1微秒的时间,如果记72000个数,就是1KHz就是1ms的时间。

16位计数器(每来一个数,计数器加1)、预分频器(对计数器进行分频,让计数更加灵活)、自动重装寄存器(技术的目标值)的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时(都是16位,2的16次方是65536,如果预分频设置最大,自动重装也最大,那么定时器最大时间就是72MHz/65536/65536,得到的是中断频率) ,如果局的不够大,还可以采取级联的办法,一个定时器的输出作为另一个定时器的输入,要乘上两个65536。

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

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

类型

编号

总线

功能

高级定时器

TIM1、TIM8

APB2

拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能

通用定时器

TIM2、TIM3、TIM4、TIM5

APB1

拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能

基本定时器

TIM6、TIM7

APB1

拥有定时中断、主模式触发DAC的功能

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

基本定时器只能选择内部时钟,所以控制器旁边引出的这根线 可以相当于是接到了内部时钟这跟根线上。来自RCC的TIMxCLK的一般是主频。预分频器如果写0,就是不分频,写1就相当于72MHz/(1+1)=36MHz。写2就是除以3。预分频器是16位的,所以最大值是65535。计数时钟每来一个上升沿,计数器的值就加1,计数器也是16位的,最大值也是65535.再加就是从0开始。只能使用向上计数。自动重装寄存器储存的就是计数目标,当计数器加到目标值时就输出中断信号。向上箭头时更新中断,输送到NVIC触发中断,向下箭头就是产生事件,不会触发中断,一般触发内部其他电路的工作。

主模式触发DAC功能:产生的事件输入到触发控制器中,然后TRGO口接到DAC中,当时间过来的时候直接触发DAC,让他进入下一个电压点。

中间部分大同小异,但是计数方式除了向上计数还有向下计数以及所谓的中央对齐模式,中央对齐就是从开始向上计数到达重装值然后申请中断,之后再从重装值开始自减到达0再申请中断。通用定时器不仅可以选择内部时钟,还可以选择外部时钟,就像上部一样。输入滤波是对输入的进行整形,ETRF那一路叫外部输入时钟模式2,TRGI那一路是外部时钟模式1 ,TRGO连接的是其他定时器ITR引脚,具体连接哪里可以查找手册(14.4.3),这样可以实现级联的功能。TI1f_ED中的ed指的是edge,边沿的意思,意思就是从TIMx_CH1输入的上升沿以及下降沿都有效。TI1FP1连接的是IC1这里的引脚,TI1FP2连接的是IC2这里的引脚。编码器接口的作用是可以读取蒸饺编码器的波形。对于TRGO,我们可以把定时器内部的一些事件映射到上面,用来触发其他定时器,DAC和ADC。

下方右边指的是输出比较电路,左边是输入捕获电路,中间公用部分是捕获/比较寄存器,输入捕获跟输出比较不能同时使用,所以寄存器公共,引脚也公用。

新的部分,右边,重复次数计数器,可以实现每隔几个技术周期才发生一次更新时间以及中断。还可以乘上一个65536,提高了计数时间。DTG死区生成电路,防止直通现象,输出可以输出一对互补的PWM波,用以驱动三相电机。 下面那部分就是当发生故障的时候自动打断电机的输出,防止以外的发生。

 运行控制指的是向上向下计数啊,启动停止啊什么的。高级定时器还会在中断输出之前多一个重复计数寄存器。

预分频寄存器分为预分频控制寄存器以及预分频缓冲寄存器(影子寄存器),如果在一个计数周期内改变了预分频的值,还是不会立即改变,而是会等一个计算周期结束之后再进行改变。实则起作用的就是影子寄存器。上图中有阴影的都是带有1影子寄存器的。目的是让更新事件跟值的变化同步,会出现一点小错误,比如已经加到8了,但是但是你重新设定的值是5,已经超过了,就只能加到FFFF之后再重置。

 无预装就是没有缓冲寄存器的意思。

RCC时钟树

 外部石英振荡器比内部的RC振荡器更加稳定。高速晶振一般用来提供系统时钟,AHB,APB2,APB1等。

首先启动内部时钟,暂时以8MHz时钟运行,然后外部时钟经过PLLMUL锁相环进行倍频9倍得到72MHz,等到输出稳定后,由锁相环输出,把8MHz换位72MHz,如果外部晶振出问题了,时钟会满了十倍。CSS一旦外部时钟失效,换为内部时钟。

无论哪种定时器,他的内部基准时钟都是72MHz,预分频器设置多少就除以多少。

输入滤波的工作原理:在固定的时钟频率下进行采样,如果连续n个采样点是相同的电平,代表输入信号就稳定了。如果采样值全都相同,就把采样值都输出出去,如果采样值不全都相同,说明信号有抖动,保持上一次的输出。频率越低,采样点越多,滤波效果就越好,但是信号延迟就越大。采样频率f可以由内部时钟直接而来,也可以由内部时钟加一个时钟分频而来。

配置定时器中断就是打通那个五颜六色的图。

第一步,RC开启时钟

第二步,选择时基单元的时钟源,对于定时中断,选择内部时钟源

第三步,配置时基单元

第四步,配置输出中断控制,允许更新中断输出到NVIC

第五步,配置NVIC,在NVIC中打开定时器中断的通道,并且分配一个优先级

第六步,运行控制,之后使能一下计数器,写中断函数

void TIM_DeInit(TIM_TypeDef* TIMx);  //恢复配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);  //时基单元初始化

TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); //对应运行控制

TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); //对应中断输出控制

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);  //内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); //
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);  //单独配置ETR引脚的预分频器,极性,滤波器这些参数的

以上六个对应时基单元的时钟选择部分。

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);    //获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);   //获取当前预分频的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);  
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

 TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);  //单独写预分频值

TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode); //新的计数器模式

TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);  //选择有没有预装

TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);  //给计数器写一个值

TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);  //给自动重装器写一个值

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);    //获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);   //获取当前预分频的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);  
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

跨越.c文件的变量,需要在其中一个的前面用extern定义说明。第二种就是把中断函数直接放到主函数中,让两个变量在一个.c文件中。

当外部输入的信号的功率比较小,上拉输入会影响信号的输入的电平的时候,就用浮空输入。

刚初始化完成就会进入中断,如果需要从头开始,就需要手动清除一下更新中断标志位。

TIM_ClearFlag(TIM2,TIM_FLAG_Update);


版权声明:本文为m0_63148816原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。