stm32之TIM定时中断详解

news2025/7/19 6:17:11

目录

  • 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线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_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线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_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.coloradmin.cn/o/2372749.html

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

相关文章

【el-admin】el-admin关联数据字典

数据字典使用 一、新增数据字典1、新增【图书状态】和【图书类型】数据字典2、编辑字典值 二、代码生成配置1、表单设置2、关联字典3、验证关联数据字典 三、查询操作1、模糊查询2、按类别查询&#xff08;下拉框&#xff09; 四、数据校验 一、新增数据字典 1、新增【图书状态…

【LUT技术专题】ECLUT代码解读

目录 原文概要 1. 训练 2. 转表 3. 测试 本文是对ECLUT技术的代码解读&#xff0c;原文解读请看ECLUT。 原文概要 ECLUT通过EC模块增大网络感受野&#xff0c;提升超分效果&#xff0c;实现SRLUT的改进&#xff0c;主要是2个创新点&#xff1a; 提出了一个扩展卷积&…

大物重修之浅显知识点

第一章 质点运动学 例1 知识点公式如下&#xff1a; 例2 例3 例4 例5 例6 第四章 刚体的转动 例1 例2 例3 例4 例5 例6 第五章 简谐振动 例1 例2 例3 第六章 机械波 第八章 热力学基础 第九章 静电场 第十一章 恒定磁场…

并发设计模式实战系列(16):屏障(Barrier)

&#x1f31f; 大家好&#xff0c;我是摘星&#xff01; &#x1f31f; 今天为大家带来的是并发设计模式实战系列&#xff0c;第十六章屏障&#xff08;Barrier&#xff09;&#xff0c;废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 屏障的同步机制 2. 关键参数 二…

算法探秘:和为K的子数组问题解析

算法探秘:和为K的子数组问题解析 一、引言 在算法的奇妙世界里,数组相关的问题总是层出不穷。“和为K的子数组”问题,看似简单,实则蕴含着丰富的算法思想和技巧。它要求我们在给定的整数数组中,找出和为特定值K的子数组个数。通过深入研究这个问题,我们不仅能提升对数组…

电力MOSFET的专用集成驱动电路IR2233

IR2233是IR2133/IR2233/IR2235 系列驱动芯片中的一种,是专为高电压、高速度的电力MOSFET和IGBT驱动而设计的。该系列驱动芯片内部集成了互相独立的三组板桥驱动电路,可对上下桥臂提供死区时间,特别适合于三相电源变换等方面的应用。其内部集成了独立的运算放大器可通过外部桥…

Qt 的原理及使用(1)——qt的背景及安装

1. Qt 背景介绍 1.1 什么是 Qt Qt 是⼀个 跨平台的 C 图形⽤⼾界⾯应⽤程序框架 。它为应⽤程序开发者提供了建⽴艺术级图形 界⾯所需的所有功能。它是完全⾯向对象的&#xff0c;很容易扩展。Qt 为开发者提供了⼀种基于组件的开发模 式&#xff0c;开发者可以通过简单的拖拽…

范式之殇-关系代数与参照完整性在 Web 后台的落寞

最近参加了一个PostgreSQL相关的茶会&#xff0c;感慨良多。原本话题是PostgreSQL 在 SELECT 场景中凭借其成熟的查询优化器、丰富的功能特性和灵活的执行策略&#xff0c;展现出显著优势。在窗口函数&#xff08;Window Functions&#xff09;、JOIN 优化、公共表表达式&#…

广西某建筑用花岗岩矿自动化监测

1. 项目简介 某矿业有限公司成立于2021年&#xff0c;是由某建筑材料有限公司与个人共同出资成立&#xff0c;矿区面积0.4069平方公里&#xff0c;可开采筑用花岗岩、建筑用砂岩。建筑用花岗岩、建筑用砂岩可利用资源量分别为6338.69万吨、303.39万吨&#xff0c;设计生产规模…

想更好应对突发网络与业务问题?需要一款“全流量”工具

目录 什么是“全流量”&#xff1f; 为什么“全流量”在突发问题中如此重要&#xff1f; 1. 抓住问题发生的“第一现场” 2. 绕开日志盲区 3. 精准应对安全威胁 实战场景下的“全流量”价值体现 实施“全流量”需要注意哪些点&#xff1f; 1. 数据量巨大&#xff0c;需…

C#里创建一个MaterialDesign3的导航条

本文里主要创建如下的窗口: 在这里就是实现左边的导航窗口的列表。 第一步先要定义下面的代码: <Window x:Class="MDIXWindow.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microso…

Oracle OCP认证考试考点详解083系列09

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 41. 第41题&#xff1a; 题目 解析及答案&#xff1a; 关于应用程序容器&#xff0c;以下哪三项是正确的&#xff1f; A) 它可以包含单个…

如何进行室内VR全景拍摄?

如何进行室内VR全景拍摄&#xff1f; 室内VR全景拍摄作为先进的视觉技术&#xff0c;能够为用户提供沉浸式的空间体验。本文介绍如何进行室内VR全景拍摄&#xff0c;并阐述众趣科技在这一领域的技术支持和服务优势。 室内VR全景拍摄基础 1. 室内VR全景拍摄概述 室内VR全景拍…

C# 综合示例 库存管理系统20 操作员管理(FormAdmin)

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 图99A-35 操作员管理窗口设计 增加操作员或者重置密码&#xff0c;密码都设置为默认的“123456”&#xff0c;操作员可以登录系统后再修…

[JAVAEE]HTTP协议(2.0)

响应报文格式 响应报文格式由首行&#xff0c;响应头&#xff08;header&#xff09;&#xff0c;空行&#xff0c;正文&#xff08;body&#xff09; 组成 响应报文首行包括 1.版本号 如HTTP/1.1 2.状态码(如200) 描述了请求的结果 3.状态码描述(如OK) 首行——状态码…

VUE+ElementUI 使用el-input类型type=“number” 时,取消右边的上下箭头

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 在项目中有时候需要输入框的type“number”&#xff0c;这个时候&#xff0c;输入框的右边就会出现两个按钮&#xff0c;这两个按钮可以递增/递减&#xff0c;但是这样输入框看上去就不太美观&#x…

计算机视觉——MedSAM2医学影像一键实现3D与视频分割的高效解决方案

引言 在乡村医院的傍晚高峰时段&#xff0c;扫描室内传来阵阵低沉的嗡鸣声&#xff0c;仿佛一台老旧冰箱的运转声。一位疲惫的医生正全神贯注地检查着当天的最后一位患者——一位不幸从拖拉机上摔下的农民&#xff0c;此刻正呼吸急促。CT 机器飞速旋转&#xff0c;生成了超过一…

垃圾分类宣教小程序源码介绍

随着环保意识的提升&#xff0c;垃圾分类已成为我们生活中不可或缺的一部分。为了更好地宣传和教育大众关于垃圾分类的知识&#xff0c;一款基于ThinkPHP、FastAdmin和UniApp开发的垃圾分类宣教小程序应运而生。 该小程序源码结合了ThinkPHP的强大后台功能、FastAdmin的高效管…

【wpf】12 在WPF中实现HTTP通信:封装HttpClient的最佳实践

一、背景介绍 在现代桌面应用开发中&#xff0c;网络通信是不可或缺的能力。WPF作为.NET平台下的桌面开发框架&#xff0c;可通过HttpClient轻松实现与后端API的交互。本文将以一个实际的HttpsMessages工具类为例&#xff0c;讲解如何在WPF中安全高效地封装HTTP通信模块。 二、…

【Hive入门】Hive安全管理与权限控制:用户认证与权限管理深度解析

目录 引言 1 Hive安全管理体系概述 2 Hive用户认证机制 2.1 Kerberos集成认证 2.1.1 Kerberos基本原理 2.1.2 Hive集成Kerberos配置步骤 2.1.3 Kerberos认证常见问题排查 2.2 LDAP用户同步 2.2.1 LDAP协议概述 2.2.2 Hive集成LDAP配置 2.2.3 LDAP与Hive用户同步架构…