stm32之TIM定时中断详解

目录

  • 1.引入
    • 1.1 简介
    • 1.2 类型
      • 1.2.1 基本定时器
      • 1.2.2 通用定时器
        • 1. 触发控制单元 (Trigger Control Unit)
        • 2. 输入捕获单元 (Input Capture Unit)
        • 3. 输出比较单元 (Output Compare Unit)
        • 4. CNT 计数器
        • 5. 自动重装载寄存器 (ARR)
        • 6. 预分频器 (PSC)
        • 7. 中断与 DMA 事件
        • 8. 刹车功能 (Break Input, BRK)
      • 1.2.3 高级定时器
        • 1. 触发控制单元 (Trigger Control Unit)
        • 2. 时基单元 (Counter and ARR)
        • 3. 输入捕获单元 (Input Capture Unit)
        • 4. 输出比较单元 (Output Compare Unit)
        • 5. 刹车输入单元 (Break Input)
        • 6. 死区时间控制单元 (Dead-Time Generator, DTG)
        • 7. 主从模式控制 (Master-Slave Mode Control)
        • 8. 事件生成与中断
        • 9.重复计数器 (Repetition Counter, REP)
    • 1.3 定时中断基本结构图
    • 1.4 时序
      • 1.4.1 PSC预分频器
      • 1.4.2 计时器时序
      • 1.4.3 计数器无预装时序
      • 1.4.4 计数器有预装时序
    • 1.5 RCC时钟树
  • 2.基本使用
    • 2.1 步骤
    • 2.2 相关函数
    • 2.3 结构体
    • 2.4 编写
      • 2.4.1 定时器内部中断
      • 2.4.2 定时器外部中断

img

1.引入

1.1 简介

TIM(Timer)定时器

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

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

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

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

所谓定时器,就是闹钟,时间到后你就要做某些事。有 2 个要素:时间、做事,换成程序员的话就是:超时时间、函数。

img

比如以按键为例,使用定时器可以解决抖动现象,未超时就发生中断,就重新计时,当抖动停止了,超过定时的时间,就可以取调用中断函数去处理稳定后的按键中断事件。(注:上图的提到的函数是linux开发板的,但是原理其实是差不多的)

1.2 类型

类型编号总线功能
高级定时器TIM1、TIM8APB2拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器TIM2、TIM3、TIM4、TIM5APB1拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器TIM6、TIM7APB1拥有定时中断、主模式触发DAC的功能

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

1.2.1 基本定时器

img

预分频器:

  • 连接的是基准计数时钟的输入,其一般来自RCC的TIMxCLK,一般是系统的主频72MHz。
  • 而预分频器则是对输入的基准时钟72MHz进行预分配,比如为0时,72/1=72MHz;为1时,72/2=36MHz
  • 这个分配器是16位的,也就是最多可以分频2的16次方

CNT计数器:

  • 对预分屏后的计数时钟进行计数,计数时钟每来一个上升沿,则+1

自动重装载寄存器:

  • 存储目标时钟计数,CNT计数器计的时钟数和其存储的目标时钟计数相同时,就发送中断信号(UI)或更新事件(U)。
  • 同时CNT计数器会清0

三者构成了时基单元。

同时还提到了主模式触发DAC的功能,主从触发模式是stm32的一大特色,它能让内部的硬件在不受程序的控制下实现自动运行。

在框图中有个部分写着至DAC,在我们使用DAC的时候,可能会用DAC(数字模拟信号转换器)输出一段波形,那么就需要每隔一段时间来触发一次DAC,让它输出下一个电压点。

知道了定时器后我们可能会想到先设置一个定时器,每隔一段时间产生中断,在中断函数中去让DAC进行转换、输出。虽然这样没问题,但是会造成CPU负担,因为需要中断的次数不少,主程序也老是处于被频段中断的状态。这时候就可以使用主模式触发DAC

也就是将更新事件(U)映射到TRGC(Trogger Out)的位置,然后接到DAC转换器上,这样就可以采用事件触发的方式去通知DAC进行转换。

其实就是和Linux开发板中的MSI或MSI-X的消息中断方式是有点相似的,不是采用传统的INTx引脚中断触发方式,而是event消息来触发中断。虽然stm32这种不算是中断,但其实也是用event事件来让某些设备开始工作。

1.2.2 通用定时器

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

img

中间的还是时基单元,和基本定时器是一样的功能。不过对于CNT计数器,在这里不是只有向上计数的模式了(自增),在这里还支持:

  • 向下计数:自减到重装值,产生中断,清0
  • 中央对齐计数:先先上自增到重装值,产生中断,然后再向下自减到0,产生中断
1. 触发控制单元 (Trigger Control Unit)

这个模块用于生成和选择不同的触发信号,包括:

  • 内部触发输入 (ITR):可以从其他定时器或模块获取触发信号(例如 ITR0、ITR1、ITR2、ITR3)。可以实现定时器级联。比如初始化好TIM3,将计数器处的事件更新触发(U)映射到TRGO,然后TRGO再去连接到TIM2的ITR2,实现计时器的级联,这样TIM3的更新事件就能去驱动TIM2的计数单元。

img

  • 外部触发输入 (ETR):可以从外部引脚获取触发信号,通过 ETRP 控制输入极性,经过输入滤波器后,得到触发信号 ETRF

    • 就是可以在一些支持TIM功能的引脚上接一个外部时钟(ETR),这样就不是像基本通用定时器那样通过CK_INT来接入系统内部的来自RCC的CK_TIM18内部时钟。
    • 可以选择通过ETRF触发信号到触发控制器,再到PSC分频器。
    • 也可以通过TRQI到从模式控制器,触发定时器的从模式,再到PSC分频器。
  • TRGO 输出信号:将触发信号输出至 DAC 或 ADC,进行协调同步。

  • 从模式控制:允许定时器在主从模式下工作,通过从模式寄存器配置触发信号(例如复位、使能、向上/向下计数等)。

对于TRGI,获得时钟,其中除了TRC的ITR使用级联定时器的方式还有通过TIIF_ED,也就是通过CH1引脚获得时钟,这种方式获得时钟上升沿和下降沿都有效。除此之外TRGI除了TRC,还能通过TI1FP1和TI2FP2获得,分别连接到IC1和IC2。

img

其实主要就是时钟的来源,除了基本定时器的来自系统RCC的内部时钟,还能有其它方式:

img

2. 输入捕获单元 (Input Capture Unit)
  • 通用定时器支持四路输入捕获通道(TI1、TI2、TI3、TI4),用于捕获外部输入信号的事件,主要用于频率测量、信号周期或占空比测量等。

  • 每个输入捕获通道具有以下功能组件:

    • 输入滤波器:对输入信号进行去抖和滤波处理,以减少噪声的干扰。
    • 预分频器 (ICxPS):可以将输入信号分频后再捕获,以支持不同频率信号的测量。
    • 捕获比较寄存器 (CCRx):当发生输入事件时,捕获当前的 CNT 值并存储在对应的 CCR 寄存器中。
3. 输出比较单元 (Output Compare Unit)
  • 通用定时器的输出比较单元支持四个输出通道(OC1、OC2、OC3、OC4),用于比较 CNT 计数器和预设的比较值 (CCRx),主要用于产生 PWM 信号、输出定时事件等。

  • 输出比较单元通过比较 CNT 计数器和指定的比较值 (CCRx) 来控制输出行为,支持多种模式:

    • PWM 模式:通过设置比较值实现 PWM 输出,用于调节输出信号的占空比。
    • 定时模式:在计数器值等于比较值时生成输出事件,适合用于定时控制。
    • 单脉冲模式:产生单个脉冲信号,可以用于控制单一事件。
4. CNT 计数器
  • 这是整个定时器的核心部件,负责记录时钟的计数值。计数器有多种计数模式:

    • 向上计数模式:计数器值从 0 增加到自动重装载寄存器 (ARR) 的值,然后生成更新事件 (U) 或中断 (UI),并清零重新计数。
    • 向下计数模式:计数器值从 ARR 的值减少到 0,产生更新事件或中断,然后重新从 ARR 开始计数。
    • 中心对齐计数模式:计数器值从 0 增加到 ARR,然后反向减少到 0,在上升沿和下降沿分别生成中断或更新事件。
5. 自动重装载寄存器 (ARR)
  • ARR 存储目标计数值,即计数器达到该值时会触发更新事件。ARR 的配置值决定了计数器的计数周期,可以用来设置定时器的周期。
6. 预分频器 (PSC)
  • 预分频器用于分配基准计数时钟 (CK_PSC),将输入的时钟频率降低到合适的计数频率。比如输入时钟为 72MHz,预分频器设置为 1 时,分频为 72MHz;若设置为 71,则分频为 72/72 = 1MHz。预分频器最大支持 16 位设置。
7. 中断与 DMA 事件
  • 通用定时器可以在一些事件发生时(例如计数器达到 ARR 值,或输入捕获发生时)触发中断信号 UI 和更新事件 U,用于通知 CPU 或其他模块进行处理。
  • 也支持 DMA 传输,在捕获事件触发时可以将数据自动传输到内存,减少 CPU 干预。
8. 刹车功能 (Break Input, BRK)
  • 刹车功能可以通过 BRK 引脚直接触发,用于紧急停止输出。常用于电机控制场景,检测到过流或故障时可以迅速停机,保护电路和设备。

1.2.3 高级定时器

img

高级定时器在功能上相较于通用定时器更为复杂,主要用于电机控制等要求较高的场景。以下是高级定时器的详细结构功能解析:

1. 触发控制单元 (Trigger Control Unit)
  • 该单元类似于通用定时器的触发控制单元,但功能更为丰富,支持多种触发信号和触发模式。
  • 内部触发 (ITR0 - ITR3):可以接收其他定时器的触发信号,用于实现定时器的主从模式同步。
  • 外部触发 (ETR):来自外部引脚的触发信号,经过极性设置和滤波后输出到复位或从模式控制。
  • ETRF 信号:经过滤波的 ETR 信号用于控制计数的复位、使能等。
  • TRGI/ TRGO 信号:用于与其他外部模块(如 ADC、DAC 等)进行同步。
2. 时基单元 (Counter and ARR)
  • 预分频器 (PSC):控制时基单元的输入时钟,进行分频以降低时钟频率。
  • CNT 计数器:高级定时器支持多种计数模式,除了向上、向下计数,还支持中心对齐模式,用于产生对称的 PWM 信号。中心对齐模式会使 CNT 计数器在到达自动重装载值 (ARR) 后反向计数。
  • 自动重装载寄存器 (ARR):存储计数器的最大计数值,到达该值时产生更新事件或中断,用于控制 PWM 周期。
3. 输入捕获单元 (Input Capture Unit)
  • 高级定时器支持四个输入通道 (TI1-TI4),每个通道可以独立捕获外部输入信号。
  • 每个输入通道配有输入滤波器、极性控制和分频器,用于捕获外部事件,主要用于测量信号的频率、周期或占空比。
  • 输入捕获结果存储在捕获/比较寄存器 (CCRx) 中,供后续计算和处理。
4. 输出比较单元 (Output Compare Unit)
  • 高级定时器支持四路输出比较单元,用于 PWM 信号生成和定时输出。

  • 每个输出比较通道配有比较寄存器 (CCRx) 和比较模式配置,可以生成多种 PWM 信号:

    • PWM 模式 1/模式 2:设置占空比,支持高精度的 PWM 信号输出。
    • 强制输出模式:可强制改变输出状态,适用于需要特殊信号控制的场景。
  • 输出比较单元还配备死区控制器 (DTG),用于在双向控制场景下添加死区时间,以避免上下桥驱动器的短路。

5. 刹车输入单元 (Break Input)
  • 刹车输入 (BRK) 是高级定时器的一项重要功能,用于紧急停止。通常用于电机控制,避免因故障或过流导致的损坏。
  • BI 信号:当检测到 BRK 引脚信号时,输出立即被关闭或进入安全状态。
  • 刹车输入功能可配置触发级别,如有效电平、响应时间等。
6. 死区时间控制单元 (Dead-Time Generator, DTG)
  • DTG 单元用于在桥式控制场景下生成死区时间。死区时间是上下桥臂之间的安全间隔时间,防止两个桥臂同时导通导致短路。
  • DTG 的设置决定了 PWM 输出信号的上升沿和下降沿之间的延迟时间。
  • 可以通过编程调整死区时间,以适应不同功率电路的需求。
7. 主从模式控制 (Master-Slave Mode Control)
  • 高级定时器支持主从模式控制,用于多定时器同步。
  • 可以选择一个定时器作为主定时器,其产生的信号(如 TRGO)可以作为其他从定时器的触发源,实现多定时器之间的同步控制。
  • 常用于多通道 PWM 信号同步或电机控制应用中,以实现复杂的控制逻辑。
8. 事件生成与中断
  • 高级定时器可以在多种事件(如计数溢出、捕获事件、比较匹配等)发生时产生中断,供 CPU 或 DMA 进行相应处理。
  • 更新事件 (U):计数器溢出时产生的事件。
  • 比较中断:当 CNT 计数值与比较寄存器匹配时触发,用于定时输出。
  • 这些事件可以独立配置,使高级定时器具备更灵活的控制方式。
9.重复计数器 (Repetition Counter, REP)
  • 在一些定时器(如高级定时器)中,支持重复计数功能,可以设定一个重复计数值,经过设定次数的更新事件后才触发一次输出,这在电机控制中较为常见。

相比通用定时器,主要就是多了死区时间控制单元 (Dead-Time Generator, DTG)、刹车输入单元 (Break Input)、重复计数器 (Repetition Counter, REP)。

1.3 定时中断基本结构图

img

看了上面的介绍下后再来看这个基本结构体,其实一目了然了。就是对于通用定时器来说,时钟可以有多种来源,

1.4 时序

1.4.1 PSC预分频器

img

img

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

  • CK_PSC:输入的未分频的计数时钟。

  • CNT_EN:计时器使能,高电平正常运行,低电平停止运行。

  • CK_CNT:分频后的计数时钟,图中以红虚线可以看出分为了两个频率,前半和未分频的时钟频率一样,也就是PSC=1;后半段则产生的分频,PSC是2,CK_CNT = CK_PSC / (2 + 1) = CK_PSC / 3

  • 计数器寄存器:顾名思义,进行计数的,当计数达到目标值就产生中断。计数的频率和分频后的时钟频率是正比的。可以看出红线左边的自动重装值就是FC,计数达到后清0,FC --> 00

    • CNT 寄存器用于保存计数值,每当 CK_CNT 上升沿到来时,CNT 加一。
    • CNT 达到自动重装载寄存器 (ARR) 的值时,会触发中断,并将 CNT 清零,重新开始计数。
    • CNT 的计数频率与 CK_CNT 是正比的,分频后的时钟频率决定了 CNT 的更新速度。
  • 更新事件(UEV):计时器计数达到重装值了,同时下一个时钟来了,分频后进入到CNT计数器,产生一个更新事件(图中唯一一个高电平),开始新的计时。

    • 更新事件(UEV)是在 CNT 到达自动重装载寄存器值并清零时触发的事件。
    • UEV 通常用于同步其他模块或者产生中断,告知外部系统计时周期已经完成。
    • 提到的图中唯一的高电平(高电平代表触发更新事件)符合描述。
  • 预分频控制寄存器( PSC Register) ):供我们读写用的。比如将0改成了1,那么在时钟周期结束后,预分配缓冲器也会从0–>1,此时预分频率计数器就会按照0、1、0、1来输出,一个0、1就对应到一个分频器输出的CK_CNT,也就是对应到一个计数。到1 CK_CNT就输出高电平。

    • PSC 是一个可以读写的控制寄存器,用于设置分频系数 PSC + 1
    • 修改 PSC 值会改变 CK_CNT 的频率。在下一个计时周期开始时,新值会加载到预分频缓冲器,确保分频变更生效。
  • 预分频缓冲器:真正起作用的寄存器,如果没有这个寄存器,我们在某个时间内对预分频控制寄存器寄存器进行写入,把0改成1,此时还在进行着一个周期的时钟,这就会导致该周期的时钟的前半部分和后半部分频率不一样了,这样是不行的。因此就有了预分频缓冲器,即使我们对某一个正在进行的时钟周期内对预分频控制寄存器寄存器改变了值,也会等到该周期结束了,才对预分频缓冲器的值进行更改,产生新的事件,开始新的时钟周期。

    • 预分频缓冲器在某个周期结束后更新为新值,避免在计数周期中途修改 PSC 导致分频不一致。
    • 当修改 PSC 值时,新值会先存储在控制寄存器中,当前计数周期继续使用旧值。
    • 当当前周期结束时,预分频缓冲器会同步更新为新的 PSC 值,并在下一计数周期生效。这样可以确保时钟频率的连续性和稳定性,不会因为修改 PSC 而引起不必要的频率波动。

1.4.2 计时器时序

imgimg

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

1.4.3 计数器无预装时序

就是自动重装载寄存器是否启用了缓冲寄存器,其实和PSC预分频器的缓冲器道理是一样的。

对于有阴影部分的器件,其都是有缓冲寄存器的功能的。

imgimg

对于没有启用缓冲器的重装载寄存器,在某一个时钟周期内(红虚线左边),对目标计数进行了更新,也就是原本是FF改为了36,那么原来该时钟周期的计数目标是到FF才产生申请中断,到由于更改了,计数到36就申请中断,计数清0

这和PSC是一样的,如果不开启缓冲器,那么就会导致在某一个周期内产生新的分频,造成时钟的计数频率不一样。

1.4.4 计数器有预装时序

img

这种在某一个周期内更改了目标计数,只会在下一个时钟周期生效,不会影响到当前的周期

1.5 RCC时钟树

img

红色左边的都是时钟的产生电路,右边的则是时钟的分配电路。中间的SYSCLK就是系统的时钟72MHz,不过时钟一开始输出的并不是72MHz,在时钟产生电路中,是有4个振荡源,从上往下分别是:

  • 8 MHz HSI RC:内部的8MHz的高速RC振荡器
  • 4-16 MHz HSE OSC:外部的4-16MHz高速石英晶体振荡器,也就是晶振,一般都是接8MHz
  • LSE OSC 32.768 kHz:外部的 32.768 kHz的低速晶振,一般是给RTC使用的
  • LSI RC 40 KHz:内部的40 KHz低速RC晶振,给看门狗提供时钟的

对于前两个是给系统提供戏中的,AHB、APB1、APB2总线的时钟都是由其提供的。一个内部一个外部,都是可以使用的,1只不过外部的石英晶体振荡器会比内部的RC振荡器更稳定。

对于系统内部的时钟,是有提供的init函数来进行初始化的,其思路是这样的,先启用的第一个8 MHz HSI RC作为系统时钟,之后再去配置第二个外部4-16 MHz HSE OSC,等其经过PLLMUL锁相环进行倍频,8MHz倍频9倍,也就是得到稳定的72MHz后,再启用其作为系统的时钟,原本的第一个时钟关闭,实现了从8MHz切换到了72MHz。

所以如果在某些时刻,明明你定的时钟是1s,结果大约10s后才发生中断,那么就有可能此时使用的还是第一个内部的8MHz的高速RC振荡器作为系统的时钟,第二个时钟的晶振可能出现问题了。

  • 此外里面还有一个CSS的器件,也就是时钟安全系统,负责切换时钟,可以检测外部时钟的运行状态,一旦外部时钟失效就会切换到内部时钟,防止程序一直卡死在那。

2.基本使用

2.1 步骤

img

按照这个结构体去进行配置定时器:

  1. RCC开启时钟,在模块程序编写的时候都是基本会执行的。打开后定时器的基准时钟和整个外设时钟都会打开
  2. 选择时基单元的时钟源,这里我们选择的是RCC内部时钟,因此配置为内部时钟模式
  3. 配置时基单元:PSC预分频器、CNT计数器、ARP自动重装器,这一部分用结构体配置就好了
  4. 配置中断输出控制,允许更新中断输出到NVCI
  5. 配置NVIC,在NVIC中打开定时器中断的通道,并配置一个优先级
  6. 运行控制配置,也就是使能定时器
  7. 定义定时器中断函数

2.2 相关函数

按照配置步骤,去调用相关的库函数,有哪些库函数可以去查看stm32f10x_tim.h文件

库函数手册中也已经详细的讲解了。下面根据配置步骤,给出所需要的相关函数:

  1. RCC开启时钟,在模块程序编写的时候都是基本会执行的。打开后定时器的基准时钟和整个外设时钟都会打开
  2. 选择时基单元的时钟源,这里我们选择的是RCC内部时钟,因此配置为内部时钟模式
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
将定时器的时钟源配置为内部时钟,即系统时钟(比如72 MHz)。void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
配置定时器 TIMx 的外部时钟源为某个内部触发源(ITRx)。
TIM_InputTriggerSource 用于指定触发源(如TRGI信号)。void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);
配置定时器 TIMx 的外部时钟为 TIx 引脚的输入信号。
参数包括输入通道、极性(上升沿或下降沿触发)以及输入滤波器配置。void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定时器 TIMx 的外部时钟模式1,使用 ETR(外部触发)作为时钟源。
可以设置 ETR 的预分频、极性和滤波器。oid TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定时器 TIMx 的外部时钟模式2,使用 ETR(外部触发)作为时钟源。
与模式1类似,但具体行为略有不同,通常用于更复杂的同步需求。

img

  1. 配置时基单元:PSC预分频器、CNT计数器、ARP自动重装器,这一部分用结构体配置就好了
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
初始化定时器的基本时间基配置。
TIM_TimeBaseInitStruct 是一个结构体,包含定时器的预分频系数、计数模式、计数器周期、时钟分频、重复计数等参数。、void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
将 TIM_TimeBaseInitTypeDef 结构体初始化为默认值。------------------------------------------------------------------------------
//下面是和时基单元另外相关的函数,基础知识在上面对定时器的介绍的时候也讲过:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
配置定时器的预分频器值 Prescaler。
TIM_PSCReloadMode 指定是否立即加载新的分频值(一般为 TIM_PSCReloadMode_Immediate)。void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
设置定时器的计数器值,即直接对 CNT 寄存器赋值。void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
配置自动重装载寄存器(ARR)的预装载功能,控制 ARR 值的更新方式。
若启用预装载,ARR 值将在下一个更新事件时加载到计数器。void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
设置定时器的计数器值,即直接对 CNT 寄存器赋值。void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
设置自动重装载值(ARR),用于控制计数周期的长度。uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
获取当前计数器的值(CNT寄存器的值)。uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
获取当前的预分频器值(PSC寄存器的值)。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);
清除指定的定时器中断挂起位,即重置中断标志,使得该中断能够再次触发。
  1. 配置中断输出控制,允许更新中断输出到NVCI
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
配置定时器的中断功能,使能或禁用指定的中断。
该函数用于控制定时器的特定中断源的使能状态。当 NewState 为 ENABLE 时,开启指定的中断源;当 NewState 为 DISABLE 时,关闭指定的中断源。
  1. 配置NVIC,在NVIC中打开定时器中断的通道,并配置一个优先级。 — 这一部分的函数在之前讲解NVIC的基本一致。
  2. 运行控制配置,也就是使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
用于开启或关闭指定的定时器。当 NewState 为 ENABLE 时,定时器开始运行;当 NewState 为 DISABLE 时,定时器停止运行。
  1. 定义定时器中断函数

2.3 结构体

以下是结构体 TIM_TimeBaseInitTypeDef 的成员变量的中文注释,并附上详细的解释:

typedef struct
{uint16_t TIM_Prescaler;         /*!< 指定用于分频定时器时钟的预分频器值。此参数可以是 0x0000 到 0xFFFF 之间的一个数值 */uint16_t TIM_CounterMode;       /*!< 指定计数器模式。此参数可以是 @ref TIM_Counter_Mode 的一个值 */uint16_t TIM_Period;            /*!< 指定将在下一个更新事件时装载到活动自动重装载寄存器中的周期值。此参数必须是 0x0000 到 0xFFFF 之间的一个数值 */ uint16_t TIM_ClockDivision;     /*!< 指定时钟分频系数。此参数可以是 @ref TIM_Clock_Division_CKD 的一个值 */uint8_t TIM_RepetitionCounter;  /*!< 指定重复计数器的值。每当 RCR 递减计数器达到零时,生成一个更新事件并从 RCR 值 (N) 重新开始计数。在 PWM 模式下,这意味着 (N+1) 对应于:- 边沿对齐模式下的 PWM 周期数- 中心对齐模式下的半个 PWM 周期数此参数必须是 0x00 到 0xFF 之间的一个数值。@note 此参数仅对 TIM1 和 TIM8 有效 */
} TIM_TimeBaseInitTypeDef;
  1. TIM_Prescaler (预分频器)
    • 作用:设置定时器时钟的预分频值。
    • 范围:可以取 0x0000 到 0xFFFF 之间的数值。
    • 解释:定时器的时钟频率是通过预分频器分频得到的,即 CK_CNT = CK_PSC / (TIM_Prescaler + 1),其中 CK_PSC 为输入的时钟频率。预分频值越大,CK_CNT 的频率就越低。通过控制预分频器,可以更灵活地控制定时器的计数速度。
  1. TIM_CounterMode (计数器模式)
    • 作用:设置计数器的计数模式。
    • 取值:可以是 TIM_Counter_Mode 的枚举值,常见的值包括:
      • 向上计数模式:TIM_CounterMode_Up
      • 向下计数模式:TIM_CounterMode_Down
      • 中心对齐计数模式(可以产生对称信号,常用于 PWM):TIM_CounterMode_CenterAligned1、TIM_CounterMode_CenterAligned2、TIM_CounterMode_CenterAligned3
    • 解释:在不同的模式下,计数器可以进行向上计数(从 0 计数到设定的周期值)或向下计数(从设定的周期值计数到 0)。中心对齐模式则会先向上计数到设定值,然后再向下计数到 0。
  1. TIM_Period (周期)
    • 作用:设置自动重装载寄存器的周期值。
    • 范围:0x0000 到 0xFFFF 之间的数值。
    • 解释:计数器计数到 TIM_Period 的值后会产生更新事件(UEV),然后计数器会重置为 0。周期值的大小决定了计数器溢出所需的时间,因此可以通过调整周期值来设置计时周期。
  1. TIM_ClockDivision (时钟分频)

img

    • 作用:控制定时器时钟的分频系数。
    • 取值:可以是 TIM_Clock_Division_CKD 的枚举值,通常包括以下选项:
      • 不分频 (0):TIM_CKD_DIV1
      • 分频系数为 2 (1):TIM_CKD_DIV2
      • 分频系数为 4 (2):TIM_CKD_DIV4
    • 解释:在某些情况下,可以对定时器的内部时钟进行分频,以满足不同应用场景的需求。通常用于一些特殊的计时应用中。还有就是对于一些外部的时钟,要达到时基单元是要经过滤波器进行滤波的,滤波器的原理就是在固定的时钟频率下对波形进行采样,如果连续N个采样点都是相同的,则代表信号稳定,就将采样值输出出去。而这个固定的时钟采样频率就是可以由内部的时钟频率而来,也可以是由内部时钟加一个分频器后分频而来,后者这个分频是多少,就是由该成员决定。
  1. TIM_RepetitionCounter (重复计数器)
    • 作用:设置重复计数器值。
    • 范围:0x00 到 0xFF 之间的数值。
    • 解释:每当重复计数器递减至零时,会生成一个更新事件(UEV),然后重复计数器会重新加载设定的值。在 PWM 模式下,这一参数用于控制 PWM 信号的重复周期数。对于边沿对齐模式,重复计数器的值加 1 就是 PWM 周期数;而对于中心对齐模式,则是半个 PWM 周期数。注意,重复计数器只适用于高级定时器(如 TIM1 和 TIM8),在普通定时器中无效。

2.4 编写

2.4.1 定时器内部中断

User:

  • 📎main.c

System:

  • 📎Timer.h
  • 📎Timer.c

Hardware:

  • 📎OLED_Font.h
  • 📎OLED.c
  • 📎OLED.h
#include "stm32f10x.h"                  // Device header/*** 函    数:定时中断初始化*/
void Timer_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟/*配置时钟源*/TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	/*中断输出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位//TIM_TimeBaseInit函数末尾,手动产生了更新事件//若不清除此标志位,则开启中断后,会立刻进入一次中断//如果不介意此问题,则不清除此标志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/

对72MHz(72000000Hz)进行7200的分频,就是10KHz的频率,也就是1/10K s记一次数,那么计数10000次不就是刚好1s,所以这个时钟中断是每1s申请一次中断。

2.4.2 定时器外部中断

img

User:

  • 📎main.c

Hardware:

  • 📎OLED_Font.h
  • 📎OLED.c
  • 📎OLED.h

System:

  • 📎Timer.h
  • 📎Timer.c
#include "stm32f10x.h"                  // Device header/*** 函    数:定时中断初始化* 参    数:无* 返 回 值:无* 注意事项:此函数配置为外部时钟,定时器相当于计数器*/
void Timer_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;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);						//将PA0引脚初始化为上拉输入/*外部时钟配置*/TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//选择外部时钟模式2,时钟从TIM_ETR引脚输入//注意TIM2的ETR引脚固定为PA0,无法随意更改//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动/*时基单元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	/*中断输出配置*/TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位//TIM_TimeBaseInit函数末尾,手动产生了更新事件//若不清除此标志位,则开启中断后,会立刻进入一次中断//如果不介意此问题,则不清除此标志位也可TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2//即抢占优先级范围:0~3,响应优先级范围:0~3//此分组配置在整个工程中仅需调用一次//若有多个中断,可以把此代码放在main函数内,while循环之前//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设/*TIM使能*/TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}/*** 函    数:返回定时器CNT的值* 参    数:无* 返 回 值:定时器CNT的值,范围:0~65535*/
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2);	//返回定时器TIM2的CNT
}/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos8源码安装openssl

前言&#xff1a; 在使用python3.11部署运行FastAPI时&#xff0c;由于其uvicorn需要使用openssl模块&#xff0c;导致没有安装openssl的服务器项目运行不起来. 【第一步】 我的网盘下载openssl-1.1.1n.tar.gz 提取码: vay9 【第二步】 上传到服务器解压 tar -zxvf opens…

vue3 动态修改系统title

vue3 动态修改系统title 修改前 修改后 1、封装 useTitle 工具函数 创建组合式 API&#xff0c;通过 watchEffect 监听标题变化&#xff1a; // composables/useTitle.js import { ref, watchEffect } from vue;export function useTitle(initialTitle) {const title r…

比较两种判断相同二叉树的方法:递归与遍历序列对比

在二叉树操作中&#xff0c;判断两棵树是否相同是一个常见的问题。本文将对比两种不同的解决方案&#xff1a;递归法和遍历序列对比法&#xff0c;分析它们的优缺点&#xff0c;并探讨为何递归法是更优的选择。 问题描述 给定两棵二叉树的根节点 p 和 q&#xff0c;判断它们是…

从0开始学习大模型--Day01--大模型是什么

初识大模型 在平时遇到问题时&#xff0c;我们总是习惯性地去运用各种搜索引擎如百度、知乎、CSDN等平台去搜索答案&#xff0c;但由于搜索到的内容质量参差不齐&#xff0c;检索到的内容只是单纯地根据关键字给出内容&#xff0c;往往看了几个网页都找不到答案&#xff1b;而…

【AI大模型】SpringBoot整合Spring AI 核心组件使用详解

目录 一、前言 二、Spring AI介绍 2.1 Spring AI介绍 2.2 Spring AI主要特点 2.3 Spring AI核心组件 2.4 Spring AI应用场景 2.5 Spring AI优势 2.5.1 与 Spring 生态无缝集成 2.5.2 模块化设计 2.5.3 简化 AI 集成 2.5.4 支持云原生和分布式计算 2.5.5 安全性保障…

洛谷 P9007 [入门赛 #9] 最澄澈的空与海 (Hard Version)

这道题可不入门。 [Problem Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription] 给定 n n n&#xff0c;求有多少组 ( x , y , z ) (x,y,z) (x,y,z) 满足&#xff1a; x − y z n ! x-\dfrac{y}{z}n! x−zy​n! x − y z n ! n \dfrac{x-y…

PostgreSQL 的 pg_stat_file 函数

PostgreSQL 的 pg_stat_file 函数 pg_stat_file 是 PostgreSQL 提供的一个系统管理函数&#xff0c;用于获取文件系统上文件的元数据信息。这个函数对于数据库管理员进行文件级别的监控和诊断非常有用。 一 函数基本语法 pg_stat_file(filename text [, missing_ok boolean …

关于麒麟服务器实现docker-compose服务开机自启

我本地服务器环境是麒麟V10版本&#xff1a; 首先确定docker-compose服务绝对路径命令&#xff1a; which docker-compose我这里输出是&#xff1a;/usr/bin/docker-compose 编辑服务文件&#xff1a; sudo vim /etc/systemd/system/docker-compose-webup.service[Unit] Desc…

基于 jQuery 实现复选框全选与选中项查询功能

在 Web 开发中&#xff0c;复选框是常见的交互元素&#xff0c;尤其是在涉及批量操作、数据筛选等场景时&#xff0c;全选功能和选中项查询功能显得尤为重要。本文将介绍如何使用 HTML、CSS 和 jQuery 实现一个具备全选、反选以及选中项查询功能的复选框组&#xff0c;帮助开发…

AfuseKt2.4.2 | 支持阿里云盘、Alist等平台视频播放,具备自动海报墙刮削功能的强大播放器

AfuseKt是一款功能强大的安卓端在线视频播放器&#xff0c;支持播放阿里云盘、Alist、WebDAV等平台的视频内容。它具备自动海报墙刮削功能&#xff0c;能自动生成影片信息和海报墙&#xff0c;提供良好的视觉体验。此外&#xff0c;它还支持倍速播放、字幕、音轨切换等多种实用…

Netlink在SONiC中的应用

Netlink在SONiC中的应用 Netlink介绍 Netlink 是 Linux 内核态程序与用户空间程序之间进行通信的机制之一&#xff0c;原本是用于传递网络协议栈中的各种控制消息。它采用和套接字&#xff08;socket&#xff09;编程接口相同的形式&#xff0c;常用于配置内核网络子系统&…

语音合成之十一 提升TTS语音合成效果:低质量数据清洗、增强与数据扩增

低质量数据清洗、增强与数据扩增 1. 引言&#xff1a;TTS的基石——数据质量2. 基础&#xff1a;TTS数据准备工作流2.1 规划&#xff1a;定义蓝图2.2 执行&#xff1a;从原始数据到训练就绪格式2.3 最佳实践与可复现性 3. 攻克缺陷&#xff1a;低质量语音数据的清洗与增强3.2 手…

Java IO流分类与记忆方法

Java IO流分类与记忆方法 在Java IO流体系中,理解节点流和包装流的区别是掌握IO编程的关键。 一、核心分类标准 1. 节点流(Node Stream) 直接对接数据源:直接连接物理IO设备(文件、网络、内存等)基础功能:提供最基础的读写能力命名特征:通常包含数据源类型名称(如Fi…

架构师如何构建个人IP:职业规划与业务战略的双重提升

在数字化时代&#xff0c;软件架构师的角色已从单纯的技术专家转变为兼具技术领导力和业务影响力的复合型人才。如何构建个人IP&#xff0c;提升行业影响力&#xff0c;成为架构师职业发展的关键课题。本文从个人认知、业务战略、架构决策、产品思维四个维度&#xff0c;探讨架…

vscode运行python的快捷键

以下是一些在 VS Code 中运行 Python 代码的常用快捷键&#xff1a; 运行 Python 文件 Windows/Linux &#xff1a;Ctrl F5。此快捷键会直接运行当前打开的 Python 文件&#xff0c;不会自动进入调试模式。若之前有配置过终端&#xff0c;一般会使用配置好的终端来运行&…

使用OpenCV 和 Dlib 实现疲劳检测

文章目录 引言1.相关技术介绍2. 系统原理2.1 眼睛纵横比(EAR)算法2.2 系统工作流程 3.代码解析3.1 关键函数说明3.2 主循环逻辑 4.实际应用效果5.参数调优建议6.总结 引言 疲劳驾驶是交通事故的主要原因之一。本文将介绍如何使用Python和计算机视觉技术构建一个实时疲劳驾驶检…

VBA实现后入先出(LIFO)库存统计

先入先出&#xff08;FIFO&#xff09;比较容易理解&#xff0c;买入早的优先卖出。与之对应的是后人先出&#xff08;LIFO&#xff09;&#xff0c;就是优先卖出最近买入的&#xff0c;例如&#xff1a;第8行卖出2K&#xff0c;当天还没有买入记录&#xff0c;只能找前一天的买…

Python中的客户端和服务端交互的基本内容

目录 网络协议 网络的通信方式 需要安装的组件和需要导入的包模块 安装的组件 导入包模块 如何创建客户端 如何创建服务端 网络协议 IPV4&#xff1a;是互联网协议的第四版&#xff0c;也是目前广泛使用的网络协议。它使用32位地址格式&#xff0c;理论上可以提供约43亿…

【硬核攻坚】告别CUDA OOM!DeepSeek部署显存瓶颈终极解决方案:三大策略高效落地

目录 引言:大模型落地的“甜蜜”与“烦恼”DeepSeek剖析:为何它如此“吃”显存?CUDA OOM的“幽灵”:现象、根因与诊断破局之道:三大策略驯服显存“猛兽” 策略一:模型量化 - 给模型“瘦身”的艺术策略二:动态优化 - 榨干硬件潜能策略三:分布式扩展 - 集群的力量实战演练…

JavaSE核心知识点01基础语法01-01(关键字、标识符、变量)

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 JavaSE核心知识点01基础语法01-01&#xff0…