STM32标准库——(6)TIM定时中断

1.TIM简介

  • TIM(Timer)定时器
  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

2.定时器类型

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

2.1 基本定时器

2.1.1 TIM6和TIM7简介

基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。

它们可以作为通用定时器提供时间基准,特别地可以为数模转换器(DAC)提供时钟。实际上,它们在芯片内部直接连接到DAC并通过触发输出直接驱动DAC。

这2个定时器是互相独立的,不共享任何资源。

2.1.2 TIM6 和TIM7 的主要特性

TIM6和TIM7定时器的主要功能包括:
● 16位自动重装载累加计数器

● 16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频

● 触发DAC的同步电路

● 在更新事件(计数器溢出)时产生中断/DMA请求

2.1.3 定时器基本框图

  1. 预分频器:可以对计数器的时钟进行分频 让这个计数更加灵活 可以对72MHz的计数时钟进行预分频 比如预分频器写0 那就是不分频 或者说是1分频 这时候输出频率=输入频率=72MHz 如果预分频器写1 那就是2分频 输出频率=输入频率/2=36MHz 如果预分频器写2 那就是3分频 输出频率=输入频率/3=24MHz 以此类推 所以预分频器的值与实际的分配系数相差1 即实际分频系数=预分频器的值+1 该预分频器是16位的 所以最大值可以写65535 也就是65536分频 就是对输入的基准频率提前进行一个分频操作
  2. 计数器:可以对预分频后的计数时钟进行计数 计数时钟每来一个上升沿 计数器的值就加1 计数器也是16位的 里面的值可以从0一直加到65535 如果再加的话 计数器就会回到0重新开始 所以计数器的值在计时过程中会不断地自增运行 当自增运行到目标值时 产生中断 那就完成了定时的任务
  3. 自动重装寄存器:存储目标的寄存器16位的 存的是写入的计数目标 在运行过程中 计数值不断自增 自动重装值是固定的目标 当计数值等于自动重装值时 也就是计时时间到了 那么它就会产生一个中断信号 并且清零计时器  计数器自动开始下一次的计数计时
  4. 向上的折线箭头:代表这里会产生中断信号 像这种计数值等于自动重装值产生的中断 称作“更新中断” 这个更新中断之后就会通往NVIC 我们再配置好NVIC的定时器通道 那么定时器的更新中断就能够得到CPU的响应了 
  5. 向下的折线箭头:代表会产生一个事件 这里对应的事件叫做“更新事件” 更新事件不会触发中断 但可以触发内部其他电路的工作
  6. 从基准时钟到预分频器再到计数器 计数器计数自增 同时不断地与自动重装寄存器进行比较 它两值相等时 即计时时间到 这时会产生一个更新中断和更新事件 CPU响应更新中断 就完成了定时中断的任务

2.2 通用定时器

2.2.1 TIMx 简介

通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。

它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。

使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。

每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作。

2.2.2 TIMx 主要功能

通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:

● 16位向上、向下、向上/向下自动装载计数器

● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值

● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出

● 使用外部信号控制定时器和定时器互连的同步电路

● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较

● 支持针对定位的增量(正交)编码器和霍尔传感器电路

● 触发输入作为外部时钟或者按周期的电流管理

2.2.3 定时器框图

  1. 主从触发模式:让内部硬件再不受程序的控制下实现自动运行 若将该模式掌握好 则在某些情况下可以大幅度减轻CPU的负担 用途是在使用DAC时 可能会用DAC输出一段波形 此时需要每隔一段时间来触发一次DAC 让它输出下一个电压点 正常思路是 先设置好一个定时器产生中断 每隔一段时间在中断程序中调用代码手动触发一次DAC转换 然后用DAC输出 但这样会使主程序处于频繁被中断的状态 这会影响主程序的运行和其他中断的响应 所以定时器设计了一个主模式 使用该主模式可以把这个定时器的更新事件映射到这个触发输出的TRGO(Trigger Out)的位置 然后TRGO直接接到DAC的触发转换引脚上 这样定时器的更新就不需要再通过中断来触发DAC转换了 仅需要把更新时间通过主模式映射到TRGO 然后TRGO就会直接去触发DAC 整个过程不需要软件参与 实现了硬件的自动化

  2. 通用定时器:时钟源不仅可以选择内部的72MHz时钟 还可以选择外部时钟 第一个外部时钟就是来自TIMx_ETR引脚上的外部时钟(可以参考引脚定义表) 表中ETR引脚对应TIM2 在PA0引脚上 可以在ATIM2的ETR引脚也就是PA0上接一个外部方波时钟 接着配置一下内部的极性选择、边沿检测和预分频器电路 再配置一下输入滤波电路(该电路可以对外部时钟进行一定的整形 对输入波形进行滤波) 滤波后的信号兵分两路 上面一路ETRF进入触发控制器 接着就可以选择作为时基单元的时钟 如果想在ETR外部引脚提供时钟 或者想对ETR时钟进行计数 把该定时器当做计数器来用 就可以配置这一路电路 在STM32中 这一路也叫做“外部时钟模式2”
  3. 除了外部ETR引脚可以提供时钟外 下面一路也可以提供时钟 就是TRGI(Trigger In) 主要是用来触发输入的 这个触发输入可以触发定时器的从模式 (后续会讲到 本小节以出发输入为主) 当TRGI当作外部时钟来使用的时候 这一路叫做“外部时钟模式1”  ETR引脚的信号即可以通过上面这一路进来当作时钟 又可以通过下面这一路当作时钟 两种情况对于时钟输入是等价的 只不过下面这一路输入会占用触发输入通道而已 ITR信号这一部分的时钟信号是来自其他定时器的 从右边可以看出 主模式的输出TRGO可以通向其他定时器 通向其他定时器的时候 就接到了其他定时器的ITR引脚上 ITR0-ITR3分别来自其他4个定时器的TRGO输出 通过这一路可以实现定时器的级联 比如先初始化TIM3 然后使用主模式把它的更新事件映射到TRGO上 接着再初始化TIM2 这里选择ITR2 对应的就是TIM3的TRGO 后面再选择时钟为外部时钟模式1 此时TIM3的更新事件就可以驱动TIM2的时基单元 也就实现定时器的级联 这里也可以选择TI1F_ED 这里连接的是输入捕获单元的CH1引脚 也就是从CH1引脚获得时钟 后缀加上一个ED(Edge)就是边沿的意思 也就是通过这一路输入的时钟 上升沿和下降沿均有效 最后这个时钟还能通过TI1FP1和TI2FP2获得 其中TI1FP1连接到CH1引脚的时钟 TI2FP2连接到CH2引脚的时钟 外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚
  4. ITR和定时器的关系

2.3 高级定时器

2.3.1 TIM1 和TIM8 简介

高级控制定时器(TIM1和TIM8)由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。

它适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、PWM、嵌入死区时间的互补PWM等)。

使用定时器预分频器和RCC时钟控制预分频器,可以实现脉冲宽度和波形周期从几个微秒到几个毫秒的调节。

高级控制定时器(TIM1和TIM8)和通用定时器(TIMx)是完全独立的,它们不共享任何资源。它们可以同步操作。

2.3.2 TIM1 和TIM8 主要特性

TIM1和TIM8定时器的功能包括:
● 16位向上、向下、向上/下自动装载计数器

● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值

● 多达4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出

● 死区时间可编程的互补输出

● 使用外部信号控制定时器和定时器互联的同步电路

● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器

● 刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态

● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
─ 刹车信号输入

● 支持针对定位的增量(正交)编码器和霍尔传感器电路

● 触发输入作为外部时钟或者按周期的电流管理

2.3.3 定时器框图

  • 重复次数计数器:实现每隔几个计数周期才发生一次更新事件和更新中断 原来的结构是每个计数周期完成后就都会发生更新 现在有个计数器在这 每隔几个周期发生更新一次 相当于对输出的更新信号又做了一次分频 

3.计数模式

通用、高级定时器可以向上计数、向下计数、向上向下双向计数模式,而基本定时器只有向上计数模式。

1、向上计数模式计数器从0计数到自动加载值(TIMX ARR),然后重新从0开始计数并且产生一个计数器溢出事件
2、向下计数模式。计数器从自动装入的值(TIMX ARR)开始向下计数到,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件
3、中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件,然后再从0开始重新计数。

4.定时器中断基本结构

5.预分频器时序

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

6.计数器时序

  • 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
  • 溢出时间=溢出频率的倒数

7.计数器无预装时序

8.计数器有预装时序

9.RCC时钟树

三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
● HSI振荡器时钟

● HSE振荡器时钟

● PLL时钟

这些设备有以下2种二级时钟源:
● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/待机模式下自动唤醒系统。

● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。

当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功耗。

  • 启动内部时钟 选择内部8MHz为系统时钟 暂时以内部8MHz的时钟运行 然后再启动外部时钟 配置外部时钟这一路 进入PLL锁相环进行倍频 8MHz倍频9倍得到72MHz 等到锁相环输出稳定后 选择锁相环输出为系统时钟 这样就把系统时钟8MHz切换为72MHz 
  • CSS:时钟安全系统 负责切换时钟 可以监测外部时钟的运行状态 一旦外部时钟失效 他就会自动把外部时钟切换回内部时钟 保证系统时钟的运行 防止程序卡死造成事故 

10.相关API

10.1 TIM引脚初始化

10.1.1 TIM_DeInit
void TIM_DeInit(TIM_TypeDef* TIMx);
功能:
	将TIMx外围寄存器反初始化为其默认复位值
参数:
    TIMx: 其中x可以是1到17,以选择TIM外围设备
返回值:
	无        

10.2 时基单元

10.2.1 TIM_TimeBaseInit
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
功能:
	根据 TIM_TimeBaseInitStruct 中指定的参数初始化 TIMx 的时间基数单位
参数:
    TIMx:x 可以是 2,3 或者 4,来选择 TIM 外设
    TIMTimeBase_InitStruct:指向结构 TIM_TimeBaseInitTypeDef 的指针,包含了TIMx 时间基数单位的配置信息    
返回值:
	无       

//此函数比较重要 用来配置基本结构中粉色部分的时基单元
10.2.2 TIM_TimeBaseStructInit
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
功能:
	用默认值填充每个TIM_TimeBaseInitStruct成员
参数:
    TIM_TimeBaseInitStruct:指向TIM_TimeBaseInitTypeDef的指针将被初始化的结构    
返回值:
	无       

10.3 中断输出控制

10.3.1 TIM_ITConfig
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
功能:
	使能或者失能指定的TIM中断。
参数:
    TIMx:其中x为1 ~ 17,表示选择TIMx外设
    TIM_IT:使能或失能的TIM中断源
    NewState: TIM中断的新状态 取值为:ENABLE或DISABLE
返回值:
	无     

//对应基本结构中绿色部分的中断输出控制 

10.4 时钟源选择

10.4.1 TIM_InternalClockConfig
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
功能:
	设置 TIMx 内部时钟
参数:
    TIMx:x 可以是 2,3 或者 4,来选择 TIM 外设    
返回值:
	无   

//对应基本结构的时钟选择部分 这里是RCC内部时钟
10.4.2 TIM_ITRxExternalClockConfig
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
功能:
	配置TIMx内部触发器为其他定时器时钟
参数:
    TIMx:其中x可以是1、2、3、4、5、9、12或15,用来选择TIM外设
    TIM_ITRSource:选择要接入哪个其他的定时器  
返回值:
	无        
    
    
//对应基本结构的时钟选择部分 这里是ITRx其他定时器
10.4.3 TIM_TIxExternalClockConfig
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
功能:
	选择TIX捕获通道的时钟
参数:
    TIMx:其中x可以是1、2、3、4、5、9、12或15,用来选择TIM外设
    TIM_TIxExternalCLKSource:选择TIx某个具体的引脚 
    TIM_ICPolarity:TIx极性 该参数可以是以下值之一:TIM_ICPolarity_Rising(激活高沿或上升沿激活)、TIM_ICPolarity_Falling(激活低沿或下降沿激活)
    ICFilter:过滤值 取值范围为0x0 ~ 0xF
返回值:
	无        
    
    
//对应基本结构的时钟选择部分 这里是TIx捕获通道
10.4.4 TIM_ETRClockMode1Config
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
功能:
	配置外部时钟模式1
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设
    TIM_ExtTRGPrescaler:外部触发预分频器
    TIM_ExtTRGPolarity:外部触发极性 该参数可以是以下值之一:TIM_ExtTRGPolarity_Inverted:激活低沿或下降沿激活、TIM_ExtTRGPolarity_NonInverted:激活高沿或上升沿激活
    ExtTRGFilter:外部触发过滤器 取值范围为0x00 ~ 0x0F
返回值:
	无     

//对应基本结构的时钟部分 这里是ETR外部时钟经过外部时钟模式1再到时基单元  
10.4.5 TIM_ETRClockMode2Config
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
功能:
	配置TIMx外部时钟模式2 
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设
    TIM_ExtTRGPrescaler:外部触发预分频器
    TIM_ExtTRGPolarity:外部时钟极性    
    ExtTRGFilter:外部触发滤波器。该参数取值在0x00和0x0F之间   
返回值:
	无    

//对应基本结构的时钟部分 这里是ETR外部时钟经过外部时钟模式2再到时基单元  
10.4.6 TIM_ETRConfig
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

功能:
	配置TIMx外部触发器(ETR)
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设
    tim_exttrgprecaler:外部触发预分频器
    TIM_ExtTRGPolarity:外部触发极性 该参数可以是以下值之一:TIM_ExtTRGPolarity_Inverted:激活低沿或下降沿激活、TIM_ExtTRGPolarity_NonInverted:激活高沿或上升沿激活
    ExtTRGFilter:外部触发过滤器 取值范围为0x00 ~ 0x0F
返回值:
	无        
 

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

10.5 运行控制 

10.5.1 TIM_Cmd
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
功能:
	使能或者失能 TIMx 外设
参数:
    TIMx:x 可以是 2,3 或者 4,来选择 TIM 外设
    NewState: 外设 TIMx 的新状态   
返回值:
	无

//对应基本结构橙色单独部分的运行控制 用来使能或失能计数器

10.6 单独写预分频值

10.6.1 TIM_PrescalerConfig
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
功能:
	配置TIMx预调度器
参数:
    TIMx:其中x为1 ~ 17,表示选择TIM外设
    Prescaler:写入的预分频器的值  
    TIM_PSCReloadMode:将值写入预分频器的模式 该参数可以是以下值之一:可以是听从安排,在更新事件生效、或者是在写入后手动产生一个更新事件让这个值立刻生效
返回值:
	无        

10.7 单独写计数器计数模式

10.7.1 TIM_CounterModeConfig
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
功能:
	用来改变计数器的计数模式
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设
    TIM_CounterMode:要使用的新的计数器计数模式 该参数可以是TIM Up Counting Mode:向上计数模式、TIM Down Counting Mode:向下计数模式、TIM Center Aligned Mode1:中心对齐模型1、TIM Center Aligned Mode2:中心对齐模型2、TIM Center Aligned Mode3:中心对齐模型3
返回值:
	无        

10.8 自动重装器预装功能配置

10.8.1 TIM_ARRPreloadConfig
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
功能:
	使能或者失能预装模式 使能是有预装 失能是无预装
参数:
    TIMx:其中x为1 ~ 17,表示选择TIM外设
    NewState: TIMx外设预加载寄存器的新状态 可以是ENABLE或DISABLE   
返回值:
	无        

10.9 其他

void GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);//更新中断标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);//清除标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);//清除更新中断标志位
void TIM_GetCounter(TIM_TypeDef* TIMx);//获取CNT计数器值的变化情况

11. 定时器定时中断

11.1 接线图

11.2 相关代码

Timer.c
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)//中断初始化函数
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2的RCC时钟
	
	TIM_InternalClockConfig(TIM2);//时钟选择为RCC内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频 这里的选择的是1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//配置计数模式 这里选择上拉计数模式
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//周期配置 是ARR自动重装值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//配置预分频器PCS的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//这里是重复计数器 是高级定时器才需要用到 而我们这里是通用定时器 随意直接给其配置0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//配置时基单元部分
	
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除挂起标志位
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//配置中断输出控制 第二个参数是更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置优先级分组 这里选择的是分组2(2位抢占2位响应 比较评价)
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//配置中断通道 这里TIM2对应的是TIM2_IQRn
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//指定NVIC线路的抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定NVIC线路的响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2,ENABLE);//启动定时器(对应运行控制)
	
}

/*
void TIM2_IRQHandler(void)//中断函数 从启动文件可以找到TIM2对应的是TIM2_IRQHandler
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//更新中断标志位 第二个参数是选择哪个中断标志位 这里选的是更新中断
	{
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
*/
  1. 定时器外部信号输入引脚 一般会有一个滤波器 滤波器可以滤掉信号的抖动干扰 工作原理是在一个固定的时钟频率f下进行采样 如果连续N个采样点都为相同电平 那就代表输入信号稳定 就把这个采样值输出出去 如果N个采样值不完全相同 那就说明信号有抖动 这时就保持上一次的输出 或者直接输出低电平 这样就能保证输出信号在一定程度上的滤波 这里的采样频率f和采样点数N都是滤波器的参数 频率越低 采样点数越多 滤波效果更好 不过响应的信号延迟就会越大  采样频率f可以由内部时钟直接而来 也可以是由内部时钟加一个时钟分频而来 分频多少由参数ClockDivision决定
  2. 定时1s 即定时频率为1Hz 但定时频率=72M/(PSC+1)/(ARR+1) 所以我们可以给PSC配置7200-1 给ARR配置10000-1 因为这里配置的是PSC、ARR个体 但其整体在公式中分别+1 所以我们我们在配置其个体时需要减1 等到的就是1Hz=72M/7200/10000(72Mhz = 72000000hz 因为预分频器能对72Mhz的计数时钟进行预分频)
  3. 预分频给多一点 自动重装给少一点 就是以一个比较高的频率计比较多的数 预分频给少一点 自动重传给多一点 就是以一个比较低的频率计比较少的数
  4.  调用主函数编译下载后发现Num从1自增 由于TimeBaseInit函数会生成一个更新事件来重新装载预分频器和重装计数器的值 我们知道预分频器有个缓冲寄存器 写的值只有在更新事件时才会起到作用 这里为了让值立刻起作用 在这最后手动生成一个更新事件 这样预分频器的值才有效 但他的副作用就是更新事件和更新中断是同时发生的 更新中断会置更新中断标志位 当我们一旦初始化完毕 更新中断就会立刻进入 所以我们一上电就进入中断 只需在初始化后清除中断标志位即可
Timer.h
#ifndef __Timer_H
#define __Timer_H

void Timer_Init(void);

#endif
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1,1,"Num:");

	
	while (1)
	{
		OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//获取计数器值的变化情况
	}
}

void TIM2_IRQHandler(void)//中断函数 从启动文件可以找到TIM2对应的是TIM2_IRQHandler
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//更新中断标志位 第二个参数是选择哪个中断标志位 这里选的是更新中断
	{
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位
	}
}
  1. 这里有两种方式:第一种是采用定义全局变量 在main.c中定义一个uint16_t Num 此时再到Timer.c中定义一个extern uint16_t Num(extern声明了主函数的Num变量) 便可以使用跨文件的变量 使用extern声明即可 在Timer.c底部的中断函数写Num++就可以实现定时器中断Num自增现象;第二种是将Timer.c中的中断注释掉 将其复制到main.c中 此时只需要在main.c定义一个uint16_t Num即可 对于定时中断而言 中断函数就是为别的文件服务的 中断函数一般放在中断代码模块 但如果其他函数要调用到 我们一般直接注释掉中断模块里的 将其复制到需要用到中断函数的模块 本函数使用的是第二种方法
  2. 自动重装值配置的是10k 即10k记1s 从开始记到9999然后自动重装 此时定时频率为1Hz 若将自动重装值改成720 则1000记0.1s 从开始记到999然后自动重装 此时定时频率为10Hz 若将预分频值改为720 那就是以原来10倍的计数频率记10000个数 这是定时频率也是10Hz 速度比原来快10倍

现象:OLED屏幕第一行每隔1s加1 从0开始计时 第二行显示计数器 从0-9999 频率为1Hz

12.定时器外部时钟

12.1 接线图

12.2 相关代码

12.2.1 Timer.c
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)//中断初始化函数
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//使能TIM2的RCC时钟
	
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);//通过ETR引脚的外部时钟模式2配置
													//这里不需要分频 所以第二个参数配置OFF
													//第三个参数是外部触发的极性
													//第四个参数是滤波器配置 这里不需要 所以配置0x00
	
	GPIO_InitTypeDef GPIO_InitStructure;//这里引脚需要用到GPIO 所以进行GPIO初始化
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频 这里的选择的是1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//配置计数模式 这里选择上拉计数模式
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//周期配置 是ARR自动重装值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;//配置预分频器PCS的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//这里是重复计数器 是高级定时器才需要用到 而我们这里是通用定时器 随意直接给其配置0
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//配置时基单元部分
	
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除挂起标志位
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//配置中断输出控制 第二个参数是更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置优先级分组 这里选择的是分组2(2位抢占2位响应 比较评价)
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//配置中断通道 这里TIM2对应的是TIM2_IQRn
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//指定NVIC线路的抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定NVIC线路的响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2,ENABLE);//启动定时器(对应运行控制)
}

uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);//返回当前计数器的值
}

/*
void TIM2_IRQHandler(void)//中断函数 从启动文件可以找到TIM2对应的是TIM2_IRQHandler
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//更新中断标志位 第二个参数是选择哪个中断标志位 这里选的是更新中断
	{
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}
*/
122.2 Timer.h
#ifndef __Timer_H
#define __Timer_H

void Timer_Init(void);
uint16_t Timer_GetCounter(void);

#endif
12.2.3 main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;

int main(void)
{
	OLED_Init();
	Timer_Init();
	
	OLED_ShowString(1,1,"Num:");
	OLED_ShowString(2,1,"CNT:");

	
	while (1)
	{
		OLED_ShowNum(1,5,Num,5);
		OLED_ShowNum(2,5,Timer_GetCounter(),5);
	}
}

void TIM2_IRQHandler(void)//中断函数 从启动文件可以找到TIM2对应的是TIM2_IRQHandler
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//更新中断标志位 第二个参数是选择哪个中断标志位 这里选的是更新中断
	{
		Num++;
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

现象:遮挡对射式红外传感器模块 计数加1 从0开始加 计数10次 加到9后清零 更新中断 Num从0加1 以此类推 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355929.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LVGL部件

一.标签部件 1.如何创建标签部件以及设置文本 ![2024-01-28T09:54:08.png][3] void my_lvgl(void) {lv_obj_t *lablelv_label_create(lv_scr_act()); //创建一个标签lv_label_set_text(lable,"hello"); //普通更改文字lv_label_set_text_fmt(lab…

Zerosync:构建基于STARK的Bitcoin证明系统

1. 引言 前序博客: BitcoinSTARK: ZeroSync & Khepri Robin Linus、Tino Steffens、Lukas George 等人成立了一个名为 ZeroSync 协会(ZeroSync Association)的瑞士非营利组织,该组织将牵头开发比特币证明系统。ZeroSync 于…

shell

目录 一.运行方式 二.编程习惯 三.变量 3.1变量的命名 3.3普通变量(局部变量) 3.4特殊变量 3.5变量子串 3.6变量赋值 四.运算方式 4.1$(( )) 4.2let 4.3expr 4.4bc(小数运算) 4.5$[ ] 4.6awk 4.7总结运算方式 五.条件测试语句 5.1文件 5.2条件测试表达式…

js实现动漫拼图1.0版

文章目录 1 实现效果视频2 功能实现思路3代码实现 1 实现效果视频 拼图1.0 2 功能实现思路 布局忽略(小白学前端,不献丑了) 左侧拼图格 左侧4*4的拼图小格子 利用表格实现,规划好td的大小,给每个格子加上背景图片&…

计算方法实验2:利用二分法及不动点迭代求解非线性方程

一、问题描述 利用二分法及不动点迭代求解非线性方程。 二、实验目的 掌握二分法及不动点迭代的算法原理;能分析两种方法的收敛性;能熟练编写代码实现利用二分法及不动点迭代来求解非线性方程。 三、实验内容及要求 二分法 (1) 编写代码计算下列数字…

类和对象 第五部分第四小节:赋值运算符重载

C编译器至少给一个类添加4个函数 1.默认构造函数无参,函数体为空 2.默认析构函数无参,函数体为空 3.默认拷贝沟早函数,对属性进行值拷贝 4.赋值运算符“operator”,对属性进行值拷贝 如果类中有属性指向堆区,做赋值操作…

上推加载更多组件

本组件使用的是TaroReact 实现的 ,具体代码如下 一共分为tsx和less文件 //index.tsx /** RefreshLoading* description 上推加载更多组件* param loading boolean* param style* returns*/import { View } from "tarojs/components"; import React, { FC…

[ESP32 IDF] wifi 的应用

目录 背景知识 wifi的基本连接使用 WiFi篇—— WiFi两种模式文章中二、WiFi 的启动(STA 及 AP 模式) 输出现象 通过websocket控制LED 实践验证 实验现象 背景知识 WIFI是ESP32非常重要的一个功能,想要使用一下IDF的API实现将ESP32连…

深度学习核心技术与实践之深度学习研究篇

非书中全部内容,只是写了些自认为有收获的部分。 Batch Normalization 向前传播 (1)三个主要任务:计算出每批训练数据的统计量。 对数据进行标准化 对标…

vue 使用 v-viewer 用于图片浏览的Vue组件,支持旋转、缩放、翻转等操作,基于viewer.js。

作者连接 npm: npm install v-viewerlegacy viewerjs main.js 引入: // 引入Viewer插件 import VueViewer, { directive as viewerDirective } from v-viewer; // 引入Viewer插件的图片预览器的样式 import viewerjs/dist/viewer.css; // 使用Viewer图片…

最新Unity DOTS Instancing合批:如何针对单个渲染实体修改材质参数

最近在做DOTS的教程,由于DOTS(版本1.0.16)目前不支持角色的骨骼动画,我们是将角色的所有动画数据Baker到一个纹理里面,通过修改材质中的参数AnimBegin,AnimEnd来决定动画播放的起点和终点,材质参数AnimTime记录当前过去的动画时间。但是在做大…

RabbitMQ“延时队列“

1.RabbitMQ"延时队列" 延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费 注意RabbitMQ并没有延时队列慨念,…

vite+ts+vue3打包的过程和错误

文章目录 概要vite.config.ts配置tsconfig.json 的配置package.json 的配置路由配置打包打开打包后的文件小结 概要 完成vite的打包,和在本地打开页面 记录一下,vite打包过程中的问题!!! vite.config.ts配置 vite.config.ts配置打包的相关配置 import…

Linux实验记录:使用RAID(独立冗余磁盘阵列)

前言: 本文是一篇关于Linux系统初学者的实验记录。 参考书籍:《Linux就该这么学》 实验环境: VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 目录 前言: 备注: 部署磁盘阵…

菱形打印和十进制ip转二进制

1.菱形打印 用for循环 #!/bin/bashread -p "请输入菱形的大小&#xff1a;" num #打印向上的等腰三角形 for ((i1;i<num;i)) dofor ((jnum-1;j>i;j--))doecho -n " " #打印的是前面的空格donefor ((k1;k<2*i-1;k))doecho -n "*" #打印…

NPDP认证:产品经理的国际专业认证

你是否想证明自己在产品开发与管理方面的专业能力&#xff1f;NPDP认证正是你需要的&#xff01;&#x1f525; NPDP认证&#xff0c;即产品经理国际资格认证&#xff0c;由美国产品开发与管理协会&#xff08;PDMA&#xff09;所发起&#xff0c;是全球公认的新产品开发专业认…

【大厂AI课学习笔记】1.2 人工智能的应用(1)

目录 1.2 人工智能的应用 1.2.1 产业中人工智能的应用 金融 教育 医疗 交通 制造 ——智慧金融 智能风控 智能理赔 智能投研 &#xff08;声明&#xff1a;本学习笔记学习原始资料来自于腾讯&#xff0c;截图等资料&#xff0c;如有不合适摘录的&#xff0c;请与我联…

【Sql Server】新手一分钟看懂在已有表基础上增加字段和说明

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

Python根据Excel表进行文件重命名

一、问题背景 在日常办公过程中&#xff0c;批量重命名是经常使用的操作。之前我们已经进行了初步探索&#xff0c;主要是通过批处理文件、renamer软件或者Python中的pathlib等模块对当前目录下的文件进行批量重命名。 而今天我们要使用的是PythonExcel的方法对指定目录下的文…

开发工具之GIT协同开发流程和微服务部署实践与总结

GIT协同开发流程和微服务部署的实践&#xff0c;并总结经验和教训。通过合理的GIT协同开发流程和良好的微服务部署策略&#xff0c;团队可以更高效地开发和部署软件。 ## 引言 在当今快节奏的软件开发环境中&#xff0c;采用合适的工具和流程对于实现高效协同开发和可靠部署至…