STM32 学习 —— 个人学习笔记6-1(TIM 定时中断 & 外部时钟)

声明

文中内容为观看 BiliBili 视频【STM32入门教程-2023版 细致讲解 中文字幕】后学习并扩展总结。

本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。

一、定时器简介

1.1 TIM 简介

STM32 系列微控制器中的 TIM(Timer)定时器是其片上外设体系中核心的时序控制模块,其核心工作机制是对输入的系统时钟或外部时钟信号进行可编程计数操作,当计数器的计数值递增或递减至预先配置的阈值(自动重装值)时,能够精准触发中断请求或产生硬件事件,从而实现对各类时序相关任务的精准控制。该定时器的时基单元由 16 位可编程计数器、16 位预分频器和 16 位自动重装寄存器构成,这三个核心组件的协同工作决定了定时周期的精度与范围,以 72MHz 的系统计数时钟为例,通过合理配置预分频器与自动重装寄存器的数值,可实现从微秒级到最大约 59.65 秒的宽范围定时,满足不同场景下的时序需求。

STM32 TIM 定时器并非仅具备基础的定时中断功能,而是一套功能丰富的时序处理系统,其支持灵活的内外时钟源选择机制,可适配不同精度与来源的时钟输入;同时集成了输入捕获、输出比较、编码器接口以及主从触发模式等扩展功能,能够实现脉冲宽度测量、精准波形生成、电机转速检测、多定时器同步联动等复杂应用。

依据功能复杂度与适用场景的差异,STM32 TIM 定时器被划分为三个类别:高级定时器 具备最完整的功能集,支持互补输出、刹车功能等面向电机控制的特殊特性;通用定时器 兼顾功能丰富性与资源占用,可满足绝大多数工业控制、通信时序控制等通用场景需求;基本定时器则精简了扩展功能,仅保留核心定时与 DAC 触发功能,适用于简单的定时中断与基础时序控制场景。

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

基本定时器的运行以来自 RCC 的 TIMxCLK 作为基础时钟输入,该时钟首先被送入预分频器(PSC),预分频器通过对输入时钟进行分频处理生成计数器时钟 CK_CNT,以此调整计数速率。分频后的 CK_CNT 驱动 16 位 CNT 计数器进行递增计数,当计数器的计数值达到自动重装寄存器中预设的数值时,计数器会触发溢出事件,并在触发控制器的作用下自动复位并重新开始计数,同时可通过 TRGO 引脚输出触发信号以驱动 DAC 等外设。整个过程由内部控制器进行协调,负责控制计数器的启停、清零与递增动作,而自动重装寄存器则通过持续向计数器提供重载值,确保计数周期的稳定性与可配置性,结合预分频器的灵活配置,基本定时器能够在 72MHz 的输入时钟下实现从微秒级到约 59.65 秒的宽范围定时,并可通过溢出事件触发定时中断,从而完成基础的时序控制任务。

1.3 通用定时器

STM32 通用定时器的运行以来自 RCC 的 TIMxCLK 作为基础时钟源,同时支持通过 TIMx_ETR 引脚接入外部时钟,或通过 ITRx 接口接收其他定时器的触发信号,其输入时钟可经极性选择、边沿检测与分频滤波处理后进入触发控制器,结合从模式控制器与编码器接口,可实现计数器的复位、使能及向上 / 向下 / 中央对齐等多种计数模式切换。基础时钟首先送入预分频器(PSC)分频得到 CK_CNT,驱动 16 位 CNT 计数器进行计数,当计数值达到自动重装寄存器的预设值时,计数器将自动重载并触发更新事件,同时可通过 TRGO 引脚输出触发信号以同步 DAC、ADC 或其他外设。此外,通用定时器配备了四路独立的捕获 / 比较通道,每一路通道均可对 TIMx_CHx 引脚的输入信号进行滤波、边沿检测与预分频处理,实现输入捕获以测量脉冲宽度与周期,也可通过输出比较模式生成精准的 PWM 波形或电平信号,捕获 / 比较寄存器将实时与计数器值进行比较,匹配时可触发中断或直接控制输出引脚状态,结合主从触发模式与编码器接口,通用定时器可适配电机转速检测、多外设同步触发、复杂波形生成等多样化的工业控制与时序管理场景。

1.4 高级定时器

STM32 高级控制定时器在通用定时器的功能基础上进行了扩展与增强,其运行以来自 RCC 的 CK_TIM18 作为核心时钟源,同时支持外部 ETR 时钟、其他定时器触发信号等多源输入,经极性选择、边沿检测与滤波处理后进入触发控制器,配合从模式控制器与编码器接口,可实现灵活的计数模式切换与多外设同步。基础时钟经预分频器(PSC)分频后生成 CK_CNT,驱动 16 位 CNT 计数器进行递增 / 递减计数,当计数值达到自动重装寄存器的预设值时,除触发标准更新事件外,还可通过重复计数器(REP 寄存器)配置多次重载周期,以实现更长时间的连续定时或多周期波形输出。在输出通道方面,高级定时器的前两路通道(TIMx_CH1、TIMx_CH2)具备互补输出能力,可通过 DTG 寄存器生成可编程死区时间,避免半桥或全桥电路中的上下管直通风险,同时支持通过 TIMx_BKIN 引脚接入刹车信号,在检测到故障时可快速封锁输出并触发硬件保护,这一特性使其特别适配电机驱动等对安全性要求较高的场景。此外,其四路捕获 / 比较通道不仅支持输入捕获与输出比较功能,还可结合死区生成与互补输出,生成带死区的精准 PWM 波形,配合时钟安全系统(CSS)的时钟失效事件检测,高级定时器能够在工业控制、伺服驱动等复杂场景中提供高可靠性的时序控制与故障保护能力。

1.5 定时中断的基本结构

在基于该定时中断基本结构图进行程序开发时,需遵循分层配置、逐步初始化的工程化流程,以确保定时器中断功能的稳定运行。

首先是时钟源配置阶段,需通过 RCC(Reset and Clock Control)外设初始化,为目标定时器、对应 GPIO 引脚(若使用外部时钟或捕获通道)及 NVIC 中断控制器提供稳定的时钟输入。这一步要通过配置 RCC 寄存器,使能定时器外设时钟与 GPIO 端口时钟,以保障后续模块的正常工作。

其次是 NVIC(Nested Vectored Interrupt Controller)配置,需根据定时器中断的优先级需求,通过 NVIC_SetPriorityGrouping 设置中断优先级分组,再调用 NVIC_Init 函数配置目标定时器中断通道的抢占优先级与响应优先级,最后使能该中断通道,确保中断请求能被正确响应与嵌套处理。

接下来是定时器核心配置,需针对时基单元完成三级参数配置:

(1)预分频器(PSC) :根据系统时钟频率与目标计数时钟需求,计算并设置分频系数,将输入时钟分频为合适的计数器时钟 CK_CNT。

(2)自动重装寄存器(ARR) :根据所需定时周期,结合 CK_CNT 的频率计算重载值,以确定计数器的溢出周期。

(3)计数器(CNT) :选择向上计数模式,并通过 TIM_Cmd 函数使能计数器开始运行。

同时,需通过 TIM_ITConfig 函数使能定时器更新中断,确保计数器溢出时能触发中断请求。在中断服务函数层面,需在对应的定时器中断向量入口实现中断处理逻辑。进入中断服务函数后,首先通过 TIM_GetITStatus 函数判断中断源是否为更新事件,确认后执行用户自定义的定时任务,完成后需调用 TIM_ClearITPendingBit 函数清除中断标志位,以避免中断重复触发。

最后,在主程序中,需完成系统初始化与上述配置函数的调用,通过主循环实现非中断任务的调度,从而构建一个完整的定时中断应用程序。

1.6 预分频器时序

STM32 定时器预分频器的工作时序以输入时钟 CK_PSC 为基础,其核心作用是通过分频处理生成计数器驱动时钟 CK_CNT,该过程由预分频控制寄存器、预分频缓冲器与预分频计数器协同完成。

当计数器使能信号 CNT_EN 置高时,预分频器开始工作。初始状态下,预分频控制寄存器与缓冲器均加载数值 0,预分频计数器对 CK_PSC 进行计数,此时分频系数为 0+1=1,CK_CNT 与 CK_PSC 频率一致,驱动计数器寄存器以 CK_PSC 的频率递增。

当向 TIMx_PSC 寄存器写入新的预分频值 1 时,该数值并不会立即生效,而是先存入预分频缓冲器中。这一设计可避免运行中修改分频系数导致的时钟抖动,保障时序稳定性。只有当更新事件(UEV)触发时,缓冲器中的新值才会被同步至预分频控制寄存器,此时预分频计数器开始以新的分频系数 1+1=2 对 CK_PSC 进行计数:每输入 2 个 CK_PSC 周期,预分频计数器溢出一次,并生成一个 CK_CNT 脉冲。

从时序图可见,更新事件触发后,CK_CNT 的频率降低为 CK_PSC 的 1/2,计数器寄存器的递增速率也随之减半。这一机制确保了预分频系数的修改仅在完整计数周期结束后生效,有效避免了分频切换过程中的计数错误,同时也揭示了 CK_CNT 的频率公式 CK_CNT=CK_PSC / (PSC+1) 的硬件实现逻辑。

1.7 计数器时序

STM32 定时器计数器的工作时序以预分频器输出的 CK_CNT 为驱动时钟,其核心是通过递增计数与溢出触发机制实现定时周期的精准控制。

当计数器使能信号CNT_EN置高后,计数器开始响应 CK_CNT 时钟,以 CK_CNT 的频率从当前值(如图中初始值 0034)递增。每一个 CK_CNT 上升沿触发一次计数,计数器寄存器值依次更新为 0035、0036,直至达到自动重装寄存器(ARR)设定的阈值(图中为 0036)。此时计数器产生溢出事件,并触发更新事件(UEV),计数器寄存器随即被自动重置为 0,并重新开始新一轮递增计数。

从时序图可见,更新事件触发的同时,会置位更新中断标志位(UIF),该标志可用于触发中断请求或作为硬件同步信号。整个计数周期的溢出频率由公式 CK_CNT_OV = CK_CNT / (ARR+1) 决定,结合预分频器的分频关系,可进一步推导为 CK_CNT_OV = CK_PSC / (PSC+1) / (ARR+1)。

图中 CK_CNT 为 CK_INT 经预分频后的时钟,当计数器从 0034 递增至 0036(即 ARR 值为 0036)时,完成一个计数周期并产生溢出,此时 CK_CNT_OV 为 CK_CNT 频率的 1/(0036+1),这一过程精准反映了公式的硬件实现逻辑,也体现了计数器与自动重装寄存器协同工作以实现稳定定时的核心机制。

1.8 计数器无预装时序

在无预装(缓冲)模式下,STM32 定时器自动重装寄存器(TIMx_ARR)的数值修改会直接生效,无需等待更新事件(UEV)的触发,这一特性在时序图中得到了清晰体现。

当计数器使能信号 CNT_EN 置高后,计数器以 CK_CNT 为驱动时钟,从初始值 31 开始递增计数。此时自动重装寄存器的初始值为 FF,理论上计数器需计数至FF才会产生溢出,但在计数过程中向 TIMx_ARR 写入了新值 36。由于无预装机制,该新值会立即覆盖自动重装寄存器的原有值,使得计数器的溢出阈值被实时更新为 36。

当计数器递增至 36 时,立即触发计数器溢出与更新事件(UEV),计数器寄存器随即复位为 0 并重新开始计数,同时更新中断标志位(UIF)被置位。这一过程表明,无预装模式下自动重装值的修改无需等待当前计数周期结束,而是直接作用于正在进行的计数过程,因此计数周期的溢出频率公式 CK_CNT_OV = CK_CNT/(ARR+1) 中的 ARR 会实时响应寄存器的写入操作,从而实现定时周期的动态调整。

需要注意的是,这种即时生效的机制虽然提升了灵活性,但也可能导致当前计数周期的长度发生变化,在对时序连续性要求较高的场景中需谨慎使用。

1.9 计数器有预装时序

当定时器配置为有预装模式(ARPE=1)时,自动重装寄存器(TIMx_ARR)与自动重装影子寄存器构成两级缓冲结构,确保定时周期的修改仅在当前计数周期结束后生效,以保障时序稳定性。

在初始状态下,自动重装寄存器与影子寄存器均加载数值 F5,计数器以 CK_CNT 为驱动时钟,从初始值 F0 开始递增计数。当向 TIMx_ARR 写入新的自动重装值 36 时,该数值不会立即作用于当前计数过程,而是暂存至自动重装寄存器中,影子寄存器仍保持原有的 F5,因此当前计数周期的溢出阈值仍为 F5。

当计数器递增至F5时,触发计数器溢出与更新事件(UEV),此时影子寄存器才会从自动重装寄存器中加载新值 36,完成阈值的更新;同时计数器寄存器复位为 0,开始以新的阈值 36 进行下一轮计数。更新事件触发时,更新中断标志位(UIF)同步置位,可用于触发中断或硬件同步操作。

这一机制确保了自动重装值的修改不会中断当前计数周期,使得溢出频率公式 CK_CNT_OV = CK_CNT / (ARR+1) 中的 ARR 仅在完整计数周期结束后更新,有效避免了周期切换过程中的计数错误,特别适用于对时序连续性要求较高的场景。

1.10 RCC 时钟树

STM32 的 RCC(Reset and Clock Control)时钟树是一套分层级的时钟分配与管理系统,其核心作用是为芯片内核、外设及各类功能模块提供稳定且可配置的时钟源。

时钟树的源头包含四类时钟源:高速内部时钟 HSI(8MHz RC 振荡器)、高速外部时钟 HSE(4-16MHz 晶体振荡器)、低速内部时钟 LSI(40kHz RC 振荡器)和低速外部时钟 LSE(32.768kHz 晶体振荡器)。其中 HSI 与 HSE 作为系统主时钟的主要来源,可通过 PLL(锁相环)进行倍频处理,倍频系数可配置为 ×2 至 ×16,最高可生成 72MHz 的 PLLCLK,再经系统时钟切换器(SW)选择后作为系统时钟(SYSCLK)输出,为整个芯片提供基础时钟。

系统时钟 SYSCLK 经 AHB 预分频器分频后生成 AHB 总线时钟(HCLK),最高频率为 72MHz,用于驱动 Cortex 内核、存储器及 DMA 等高速模块。HCLK 进一步经 APB1 和 APB2 预分频器分频,分别生成 APB1 外设时钟(PCLK1,最高 36MHz)和 APB2 外设时钟(PCLK2,最高 72MHz)。特别地,当 APB 预分频系数为 1 时,定时器时钟(TIMxCLK)与对应 APB 总线时钟频率一致;若预分频系数大于 1,则定时器时钟会被自动倍频至总线时钟的 2 倍,以此保障定时器模块的计数精度。

此外,时钟树还包含独立的分支:LSE 与 LSI 分别为 RTC 实时时钟和独立看门狗(IWDG)提供专用时钟;PLLCLK 可经分频后生成 48MHz 的 USB 专用时钟(USBCLK);ADC 时钟则由 PCLK2 经 2/4/6/8 分频得到,最高限制为 14MHz 以保证转换精度。整个时钟树通过 CSS(时钟安全系统)实现故障检测,当 HSE 时钟失效时会自动切换至 HSI,从而提升系统的可靠性。

二、TIM 定时中断与外部时钟的实现

2.1 TIM 定时中断
  • 首先,按下图接线方式,搭建面包板电路并连接 OLED 显示屏,然后将 DAP-Link / ST-Link 连接到 STM32 最小系统板上,为使 OLED 显示屏的 VCC 和 GND 正确连接正负极,请先连接对应正负极跳线(或直接使用 GPIO 口进行供电)。

  • 直接复制先前演示的已有文件目录,重命名并双击后缀名为 .uvprojx 的文件打开工程文件,并对 main.c 进行修改,工程中所使用的全部头文件其详细内容已放于文末,Timer.h头文件中封装了 STM32F103 系列单片机 TIM2 定时器定时中断功能实现所需的全部底层配置逻辑,具体涵盖 STM32F103 系列单片机的定时器外设时钟使能、定时器内部时钟源选择、时基单元(预分频器、自动重装寄存器、计数模式、时钟分频因子)参数配置、定时器更新中断标志位手动清除与中断使能配置,以及 NVIC(嵌套向量中断控制器)优先级分组、定时器中断通道优先级与使能配置,同时包含定时器启动控制逻辑与 TIM2 更新中断服务函数的实现。

    #include "stm32f10x.h" // Device header
    #include <OLED.h>
    #include <Timer.h>

    uint16_t Num;

    int main(void){
    OLED_Init();
    Timer_Init();

    复制代码
      OLED_ShowString(1, 1, "Count:");
      
      while(1){
      	OLED_ShowNum(1, 8, Num, 5);
      	OLED_ShowNum(2, 8, TIM_GetCounter(TIM2), 5);
      }

    }

2.2 TIM 外部时钟的实现
  • 首先,按下图接线方式,搭建面包板电路并连接 OLED 显示屏和对射式红外传感器,然后将 DAP-Link / ST-Link 连接到 STM32 最小系统板上,为使 OLED 显示屏的 VCC 和 GND 正确连接正负极,请先连接对应正负极跳线(或直接使用 GPIO 口进行供电)。

  • 直接复制先前演示的已有文件目录,重命名并双击后缀名为 .uvprojx 的文件打开工程文件,并对 main.c 进行修改,工程中所使用的全部头文件其详细内容已放于文末。

    #include "stm32f10x.h" // Device header
    #include <OLED.h>
    #include <EXT_Timer.h>

    int main(void){
    OLED_Init();
    Timer_Init();

    复制代码
      OLED_ShowString(1, 1, "CNT:");
      
      while(1){
      	OLED_ShowNum(1, 5, Timer_GetCounter(), 5);
      }

    }

  • 由于对射式红外传感器的 DO 引脚在 "挡光→透光" 或 "透光→挡光" 的瞬间,电平不是瞬间稳定切换的,而是会出现短暂的高低电平跳变(即 "抖动"),比如遮挡一次,DO 引脚可能快速跳变 3-5 次,PA2 引脚会把这些抖动都传给 TIM2,导致计数多次增加。为解决这一电平抖动导致的计数误差,在配置定时器外部时钟模式 2 时,需对输入信号进行数字滤波处理:通过修改 TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); 函数的最后一个参数(滤波系数)为 0x0F,可使定时器对 PA2 引脚输入的外部时钟信号进行 16 个定时器时钟周期的采样滤波。该配置下,只有当输入信号在连续 16 个时钟周期内保持稳定电平状态时,才会被判定为有效触发信号,从而滤除短暂的电平抖动,确保 TIM2 计数器仅对传感器的真实状态切换进行计数,有效提升对射式红外传感器计数的准确性与稳定性。

三、演示代码关联的头文件与源文件说明

  • OLED 相关头文件请从 STM32 学习 ------ 个人学习笔记4(OLED 显示屏及调试工具) 文末查看,此处不重复展示。

  • Timer.c

    #include "stm32f10x.h" // Device header

    // 声明其它文件已经定义过的变量
    extern uint16_t Num;

    void Timer_Init(void){
    // 提前声明需要使用的结构体
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStrucutre;

    复制代码
      // 配置时钟
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
      
      // 选择为内部时钟
      TIM_InternalClockConfig(TIM2);
      
      // 初始化时基单元	
      TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
      // CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1);  CK_PSC = 72M;
      TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
      TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
      TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
      
      // 手动清除更新中断标志位
      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
      
      // 更新中断
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
      
      // 配置 NVIC
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      NVIC_InitStrucutre.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStrucutre.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStrucutre.NVIC_IRQChannelPreemptionPriority = 2;
      NVIC_InitStrucutre.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStrucutre);
      
      // 启动定时器
      TIM_Cmd(TIM2, ENABLE);

    }

    void TIM2_IRQHandler(void){
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){
    Num ++;
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
    }

  • Timer.h

    #ifndef __TIMER_H
    #define __TIMER_H

    void Timer_Init(void);
    void TIM2_IRQHandler(void);

    #endif

  • EXT_Timer.c

    #include "stm32f10x.h" // Device header
    #include <Delay.h>

    void Timer_Init(void){
    // 提前声明需要使用的结构体
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStrucutre;
    GPIO_InitTypeDef GPIO_InitStructure;

    复制代码
      // 配置时钟
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      // 配置 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_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);	
      
      
      // 初始化时基单元	
      TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
      // CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1);  CK_PSC = 72M;
      TIM_TimeBaseInitStructure.TIM_Period = 500 - 1;
      TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
      TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
      
      // 手动清除更新中断标志位
      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
      
      // 更新中断
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
      
      // 配置 NVIC
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      NVIC_InitStrucutre.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStrucutre.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStrucutre.NVIC_IRQChannelPreemptionPriority = 2;
      NVIC_InitStrucutre.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStrucutre);
      
      // 启动定时器
      TIM_Cmd(TIM2, ENABLE);

    }

    uint16_t Timer_GetCounter(void){
    return TIM_GetCounter(TIM2);
    }

  • EXT_Timer.h

    #include "stm32f10x.h" // Device header
    #ifndef __EXT_TIMER_H
    #define __EXT_TIMER_H

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

    #endif


文中部分知识参考:B 站 ------ 江协科技;百度百科

相关推荐
时代的凡人13 小时前
0208晨间笔记
笔记
今天只学一颗糖13 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
testpassportcn14 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
不做无法实现的梦~15 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
游乐码17 小时前
c#变长关键字和参数默认值
学习·c#
饭碗、碗碗香18 小时前
【Python学习笔记】:Python的hashlib算法简明指南:选型、场景与示例
笔记·python·学习
Wils0nEdwards18 小时前
初中化学1
笔记
魔力军18 小时前
Rust学习Day4: 所有权、引用和切片介绍
开发语言·学习·rust
wubba lubba dub dub75018 小时前
第三十六周 学习周报
学习
学编程的闹钟19 小时前
PHP字符串表示方式全解析
学习