尧图精选

STM32定时器本质:脉冲计数原理与五级时钟链精度解析

🕒 发布时间:2026/10/1 17:46:26 📁 来源:尧图网络
1. 这不是“数时间”是在数脉冲STM32定时器的本质真相你写过HAL_Delay(1000)也调过TIM_SetCounter(TIM2, 0)甚至用过__HAL_TIM_SET_COUNTER(htim3, 0)——但有没有哪一刻突然愣住这个“1000毫秒”到底是谁在算它凭什么知道1秒就是1000毫秒它靠的真是墙上挂钟的秒针还是实验室里那台原子钟都不是。STM32的定时器从不直接“数时间”它只认一件事脉冲。一个接一个、稳定跳动的电平翻转。它数的是APB总线送来的时钟脉冲个数再把“数到多少个脉冲1毫秒”这个换算关系硬编码进寄存器里。这就是所有困惑的起点我们以为自己在配置“时间”实际是在配置“计数规则”。我第一次搞懂这点是在调试一个超声波测距项目时。明明设置了TIM2为1微秒计数周期结果测出的距离总是偏大15%。查了三天寄存器最后发现是RCC配置里把APB1预分频器设成了2而TIM2挂在APB1上——这意味着定时器实际接收的时钟频率比我代码里写的SystemCoreClock / 2 / 72少了一半。它没坏也没偷懒它只是忠实地数着被“打折”后的脉冲。这件事让我彻底扔掉了“定时器电子表”的幻想。它不是时间的翻译官而是脉冲的计数员它的精度完全取决于上游时钟源的纯净度、分频链路的准确性、以及你对整个时钟树的理解深度。所以这篇文章不讲怎么让LED闪烁也不教怎么生成PWM波形。我们要回到最底层STM32的时间基准究竟从哪里来它经过了几道“关卡”每一道关卡又如何影响最终的定时精度你会看到从晶振焊接到PCB那一刻起到你在main()里写下第一行HAL_TIM_Base_Start_IT(htim2)中间至少有5层硬件与软件的耦合。任何一层出错你的“1秒”就不再是1秒。这解释了为什么有人用同样的库函数别人能精准控温他却连电机转速都稳不住也解释了为什么在STOP模式下某些定时器会“失联”——不是中断没触发而是它根本没收到脉冲可数。如果你正被“延时不准”、“捕获抖动”、“低功耗唤醒失败”这些问题反复折磨那说明你还没真正摸清这条时间流水线的源头活水。接下来我们就一节一节拆开看这条从石英晶体出发、最终变成你代码里ms单位的完整路径。2. 时间的源头从石英晶体到CPU核心的五级传导链STM32的时间基准绝非凭空产生。它是一条严格依赖物理器件、逐级分频、层层传递的硬性链条。这条链路上任何一个环节的偏差或配置错误都会被指数级放大。我们把它拆解为五个关键层级每一层都决定着下一层的“心跳节奏”。2.1 第一级外部高速晶振HSE——时间的物理锚点一切始于那颗小小的、封装在金属壳里的石英晶体。它通常标称8MHz或25MHz焊接在STM32芯片的OSC_IN和OSC_OUT引脚之间。它的价值不在于频率多高而在于稳定性。一块优质AT-cut石英晶体在常温下频率漂移可以控制在±10ppm以内——也就是每秒误差不超过0.00001秒。这是整个系统时间精度的物理天花板。但请注意晶体本身不产生信号它只提供谐振条件。真正起振并输出方波的是芯片内部的振荡电路Oscillator Circuit。这个电路需要精确匹配晶体的负载电容通常为12pF或20pF这正是原理图上必须在晶振两端并联两个匹配电容如22pF的原因。我见过太多新手为了“省一个电容”直接把晶振一端悬空结果系统冷启动失败或者在温度变化时频繁复位——因为振荡电路无法在偏离设计负载的条件下稳定工作。提示不要迷信“无源晶振内部电容”方案。STM32内部提供的可编程负载电容如STM32F1系列的RCC_CR寄存器中HSEBYP位配合RCC_CFGR的PLLMUL等精度有限且不同批次芯片差异较大。工业级应用务必外接匹配电容并用示波器实测OSC_OUT引脚波形——理想状态是干净、无过冲、占空比接近50%的方波。若波形畸变严重首要排查PCB走线是否过长、是否靠近高频干扰源如DC-DC开关节点。2.2 第二级PLL锁相环——从“基础心跳”到“超频脉搏”HSE提供的8MHz对CPU来说太慢了。我们需要把它“加速”。这时PLLPhase-Locked Loop登场。它像一个精密的电子齿轮箱将输入频率乘以一个整数倍如×9输出更高频率的时钟。以STM32F103为例典型配置是HSE (8MHz) → PLL (×9) → 72MHz SYSCLK。但PLL不是简单乘法器。它包含三个核心部件鉴相器PFD、环路滤波器Loop Filter和压控振荡器VCO。PFD持续比较参考时钟HSE分频后与反馈时钟PLL输出分频后的相位差输出一个误差电压环路滤波器平滑这个电压VCO则根据该电压调整自身振荡频率直到相位差趋近于零——此时输出锁定。这个过程需要时间即PLL锁定时间Lock Time典型值为100μs。这意味着你在使能PLL后必须等待RCC_CR寄存器中的PLLRDY标志置位才能切换主时钟源否则CPU会因时钟丢失而复位。注意PLL倍频系数PLLMUL并非越大越好。过高的倍频会引入更多相位噪声降低时钟边沿陡峭度进而影响高速外设如USB、FSMC的采样裕量。STM32F103手册明确建议当SYSCLK 64MHz时需将AHB预分频器HPRE设为/2以保证Flash等待周期Latency与CPU频率匹配。否则CPU取指会出错——这不是软件bug是硬件时序违例。2.3 第三级总线预分频器AHB/APB——为不同外设定制“步调”72MHz的SYSCLK不能一股脑全塞给所有外设。内存总线AHB需要高带宽UART这种串口只需几MHz就足够。因此STM32设计了多级预分频器AHB预分频器HPRE直接分频SYSCLK供给Cortex-M3内核、SRAM、FSMC、DMA等高速模块。常见设置为/172MHz或/236MHz。APB1预分频器PPRE1分频AHB时钟供给低速外设TIM2-7、USART2-3、I2C1、SPI2等。最大频率为36MHz。APB2预分频器PPRE2分频AHB时钟供给高速外设GPIO、USART1、SPI1、TIM1、ADC等。最大频率为72MHz。关键陷阱在于APBx预分频器的分频系数决定了其下属定时器的时钟源频率但有一个特殊规则。当PPRE1或PPRE2设置为/1时对应APB总线上的定时器如TIM2在APB1上时钟频率等于APBx时钟但当PPRE1或PPRE2设置为/2、/4、/8或/16时定时器时钟频率会被自动加倍这是ST为了补偿低速总线带来的定时器分辨率损失而做的硬件优化。例如若PPRE1 /2则APB1时钟 72MHz / 2 36MHz但TIM2时钟 36MHz × 2 72MHz。若PPRE1 /4则APB1时钟 18MHzTIM2时钟 18MHz × 2 36MHz。这个“自动加倍”规则是绝大多数初学者配置定时器时算错计数周期的根本原因。你查手册看到“TIM2 clock PCLK1”却忘了PCLK1只是总线时钟而TIM2实际用的是PCLK1 * (2 if PPRE1 ! 1 else 1)。我曾帮一个客户修复一个电机FOC控制项目他们用TIM1APB2做PWM用TIM8APB2做电流采样触发两者频率要求严格同步。结果发现TIM8的中断总是晚几个微秒——根源就是PPRE2被误设为/2导致TIM8时钟比TIM1快了一倍TIM1不受此规则影响因其时钟源为APB2且PPRE2设为/1。2.4 第四级定时器内部时钟分频器CKD PSC——把“高速脉冲”变成“可用节拍”到了定时器模块内部时钟信号还要经历两次分频才能成为我们熟悉的“计数脉冲”。第一级时钟分频器CKD位于定时器时钟输入端用于处理输入时钟的极性与分频。它有三种模式00不分频直接使用输入时钟CK_INT。01二分频且上升沿有效。10二分频且下降沿有效。这个寄存器TIMx_CR1的CKD[1:0]位极少被主动配置多数情况下保持默认00。它的存在主要是为了兼容某些需要特定边沿触发的场合如编码器接口对通用定时器影响甚微。第二级预分频器PSC这才是我们天天打交道的“粗调旋钮”。它是一个16位可编程寄存器TIMx_PSC对CK_INT进行PSC 1分频。例如若PSC 7199则分频系数为7200。这是决定定时器基本计数周期T_cnt的最关键参数。计算公式为T_cnt (PSC 1) * T_ck_int其中T_ck_int是进入定时器的时钟周期即1 / TIMx_CLK。举个实例STM32F103SYSCLK 72MHzPPRE1 /2→PCLK1 36MHz→TIM2_CLK 36MHz * 2 72MHz→T_ck_int 1 / 72MHz ≈ 13.89ns。若想让TIM2产生1ms定时中断则T_cnt 1ms (PSC 1) * 13.89ns → PSC 1 1ms / 13.89ns ≈ 72000 → PSC 71999这个计算看似简单但背后隐藏着精度陷阱。72000不是质数它能被2、3、5等整除意味着你可以用更小的PSC值配合更大的自动重装载值ARR来实现同样效果。但PSC越小ARR越大计数器溢出中断的响应延迟从计数器归零到CPU执行中断服务程序就越长对实时性要求高的场合如PWM死区控制不利。因此工程实践中我们倾向于让PSC尽可能大ARR尽可能小通常设为ARR 999对应1ms中断PSC则根据TIMx_CLK反推以最小化中断延迟。2.5 第五级自动重装载寄存器ARR与计数器CNT——最终的“时间刻度尺”PSC分频后得到的时钟驱动着一个16位或32位的向上计数器CNT。CNT从0开始计数每来一个脉冲就加1直到等于ARR寄存器的值。此时计数器硬件自动清零或根据模式置为0并置位更新事件标志UIF触发中断或DMA请求。ARR就是我们定义的“时间刻度尺”的长度。CNT则是这把尺子上的游标。ARR的值决定了“数到多少个PSC分频后的脉冲就算过了一‘格’时间”。例如PSC 7199分频7200ARR 999则计数周期T_period (PSC 1) * (ARR 1) * T_ck_int 7200 * 1000 * 13.89ns 1.00008ms理论误差 0.00008ms / 1ms 0.008%约80ppm。这个误差已经远超石英晶体本身的精度±10ppm主要来源于ARR 1的整数约束。你想得到绝对精确的1ms除非T_ck_int恰好是1ns的整数倍而这在72MHz系统时钟下不可能。因此所有基于通用定时器的“精确延时”本质上都是在容忍一个微小的、可计算的量化误差。真正的高精度时间测量如超声波测距的微秒级回波时间必须使用输入捕获Input Capture功能直接测量两个事件如GPIO电平跳变之间的脉冲个数而非依赖ARR设定的周期。3. 定时器的“身份”与“权限”不同定时器的时钟源与能力边界STM32家族中定时器绝非千篇一律。它们被划分为三类每类拥有不同的时钟源、寄存器结构和专属能力。混淆它们的定位是配置失败的另一大根源。3.1 基本定时器TIM6/TIM7——纯粹的“计数引擎”TIM6和TIM7是“裸机”定时器。它们没有输入捕获、输出比较、PWM生成功能甚至连外部时钟输入ETR都没有。它们只有一个核心任务在指定周期产生更新事件Update Event。这个事件可以触发中断也可以作为DAC或高级定时器的触发源。时钟源仅能由PCLK1APB1提供且不享受APB1预分频器的“自动加倍”规则。即若PPRE1 /2PCLK1 36MHz则TIM6_CLK 36MHz而非72MHz。能力边界16位自动重装载计数器支持中断、DMA请求。是实现简单周期性任务如SysTick替代、状态机心跳的理想选择资源占用最小功耗最低。我习惯用TIM7做系统心跳。在HAL_TIM_Base_Start_IT(htim7)后每10ms进一次中断在里面调用HAL_IncTick()如果不用HAL库则手动维护一个全局毫秒计数器。这样HAL_GetTick()的返回值就完全由TIM7驱动与SysTick无关避免了在RTOS环境下SysTick被抢占导致的滴答不准问题。3.2 通用定时器TIM2-TIM5, TIM9-TIM14——功能全面的“瑞士军刀”这是最常用的一类。它们具备完整的输入捕获IC、输出比较OC、PWM输出、单脉冲模式OPM、编码器接口Encoder Mode等功能。每个通道CH1-CH4均可独立配置为输入或输出。时钟源TIM2-TIM5挂载在APB1上享受PPRE1的“自动加倍”TIM9-TIM14挂载在APB2上享受PPRE2的“自动加倍”。这是关键区别。能力边界16位计数器TIM9-TIM14为32位支持多种时钟选择内部时钟、外部时钟模式1/2、编码器时钟支持重复计数器RCR实现多周期PWM。它们是电机控制FOC/PID、信号测量频率/占空比、多路PWM输出的主力。一个经典误区试图用TIM2APB1去生成一个需要72MHz时钟精度的PWM波。由于PPRE1最大只能到/236MHz即使启用“自动加倍”TIM2最高也只能达到72MHz但此时PSC最小只能设为0ARR必须设为71999才能得到1kHz PWM这会导致CNT计数范围极大中断响应延迟不可控。正确做法是将高精度PWM需求交给TIM1或TIM8APB2PPRE2 /1时钟直达72MHz而用TIM2做低速外设如UART波特率发生器或状态监控。3.3 高级定时器TIM1/TIM8——电机与电源的“指挥中枢”TIM1和TIM8是为复杂控制场景设计的。它们在通用定时器基础上增加了互补PWM输出CH1/CH1N, CH2/CH2N, CH3/CH3N支持死区插入Dead-Time Insertion这是驱动三相逆变桥IGBT/MOSFET的必备功能。刹车功能Break Input当检测到硬件故障如过流、过温时能立即强制所有输出通道进入安全状态高阻或低电平。重复计数器RCR允许计数器在到达ARR后不立即更新而是等待RCR次重复后才更新用于生成多周期波形。同步机制支持多个定时器间的主从模式Master-Slave实现精确的时序协同。时钟源挂载在APB2上PPRE2 /1时TIM1_CLK SYSCLK 72MHz且不享受“自动加倍”规则因为它本身就是APB2的最高时钟。这是TIM1能实现纳秒级死区控制的基础。能力边界16位计数器但通过BDTRBreak and Dead-Time Register可精细配置死区时间单位为T_ck_int精度极高。它们是FOC算法、数字电源、伺服驱动的核心。我在一个BLDC无感FOC项目中用TIM1的CH1/CH1N、CH2/CH2N、CH3/CH3N输出三相互补PWM死区时间设为500nsPSC0,ARR35因T_ck_int13.89ns35*13.89ns≈486ns。同时用TIM8的CH1做霍尔传感器输入捕获测量转子位置。两个定时器通过TIM1的TRGO信号同步启动确保PWM更新与位置采样严格对齐。若将霍尔捕获任务交给TIM2由于APB1时钟与APB2时钟异步且TIM2的“自动加倍”规则引入额外不确定性整个控制环路的相位就会漂移导致电机抖动。4. 实操现场手把手配置一个精准1ms中断的TIM2APB1现在我们把前面所有理论落地到一个具体操作在STM32F103C8T6俗称“蓝 pill”上使用HAL库配置TIM2产生精确1ms的周期性中断。这个过程将暴露所有关键配置点。4.1 步骤一确认RCC时钟树——一切的起点打开STM32CubeMX或直接查看你的system_stm32f1xx.c文件。找到SetSysClock()函数。标准配置如下// HSE 8MHz // PLL HSE * 9 72MHz // SYSCLK 72MHz // AHB SYSCLK / 1 72MHz // APB1 SYSCLK / 2 36MHz // APB2 SYSCLK / 1 72MHz验证方法在main()开头加入uint32_t sysclk HAL_RCC_GetSysClockFreq(); uint32_t pclk1 HAL_RCC_GetPCLK1Freq(); uint32_t pclk2 HAL_RCC_GetPCLK2Freq(); // 打印出来应为 72000000, 36000000, 72000000如果打印值不符说明RCC配置有误必须先修正。这是后续所有定时器配置正确的前提。4.2 步骤二计算TIM2时钟频率——解开“自动加倍”之谜TIM2挂载在APB1上。根据前述规则PCLK1 36MHzPPRE1 /2即RCC_CFGR寄存器中PPRE1[2:0] 100b因此TIM2_CLK PCLK1 * 2 72MHz实测验证用示波器探头接在TIM2的CH1输出引脚需先配置为PWM输出设置PSC0,ARR0此时CNT每来一个时钟就溢出一次输出频率为TIM2_CLK / 2 36MHz因为PWM占空比为50%一个周期包含一次上升沿和一次下降沿。若测得频率为18MHz则说明PPRE1实际为/4需检查RCC配置。4.3 步骤三计算PSC与ARR——精度与实时性的平衡目标1ms中断周期。T_clk 1 / 72MHz ≈ 13.8889ns要求T_period 1ms 1,000,000nsTotal_Counts 1,000,000ns / 13.8889ns ≈ 72,000由于CNT是16位最大计数值为6553572,000 65535因此不能将全部分频放在ARR里。必须使用PSC进行预分频。设PSC 1 X,ARR 1 Y则X * Y 72,000。寻找满足Y 65535且X 65535的整数解。最优解是X 72,Y 1000因为72 * 1000 72,000。PSC 72 - 1 71ARR 1000 - 1 999此时T_period (71 1) * (999 1) * 13.8889ns 72 * 1000 * 13.8889ns 1,000,000.8ns ≈ 1.0000008ms误差 0.0000008ms / 1ms 0.00008%完全可以忽略。注意为什么选Y1000而不是Y72000因为ARR999时CNT只需计数到999就溢出中断响应延迟极短通常1μs。若ARR71999CNT要数到71999中断延迟会显著增加且CNT值很大时读取__HAL_TIM_GET_COUNTER(htim2)的开销也更大。4.4 步骤四HAL库配置详解——寄存器背后的真相在MX_TIM2_Init()函数中关键配置如下htim2.Instance TIM2; htim2.Init.Prescaler 71; // 对应 PSC 寄存器 htim2.Init.CounterMode TIM_COUNTERMODE_UP; // 向上计数 htim2.Init.Period 999; // 对应 ARR 寄存器 htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; // CKD 00, 不分频 htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; // 关闭影子寄存器立即生效Prescaler 71写入TIM2_PSC寄存器值为71分频系数为72。Period 999写入TIM2_ARR寄存器值为999计数上限为999。AutoReloadPreload DISABLE这是关键。当启用影子寄存器ENABLE时ARR的更新会在下一个更新事件UEV时才生效这会导致定时周期出现一个周期的跳变。对于需要严格周期的应用如音频采样应启用但对于简单的1ms心跳DISABLE可确保配置后立即生效避免首次中断延迟。初始化后启动定时器HAL_TIM_Base_Start_IT(htim2); // 使能更新中断并启动计数这行代码会设置TIM2_CR1的CEN位使能计数器。设置TIM2_DIER的UIE位使能更新中断。调用HAL_NVIC_EnableIRQ(TIM2_IRQn)使能NVIC中断通道。4.5 步骤五中断服务程序ISR——最小化开销的实践TIM2_IRQHandler必须极度精简void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(htim2); // 这个函数会检查 UIF 标志调用用户回调 }而用户回调函数HAL_TIM_PeriodElapsedCallback()则放在main.c中void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { // 在这里放你的1ms任务如 LED 翻转、传感器读取 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); } }实操心得永远不要在HAL_TIM_PeriodElapsedCallback()里做耗时操作如printf、HAL_Delay、复杂浮点运算。这些操作会阻塞中断导致后续中断丢失。正确做法是在此处仅设置一个volatile标志位如tick_flag 1;然后在main()的while(1)循环中轮询该标志并执行耗时任务。或者使用FreeRTOS的xSemaphoreGiveFromISR()在中断中释放信号量由高优先级任务处理。5. 常见问题与排查技巧实录那些让你抓狂的“时间错觉”在真实项目中定时器问题往往表现为“看起来不对”而非“完全不工作”。以下是我在十年嵌入式开发中遇到频率最高、最隐蔽的五类问题及其排查逻辑。5.1 问题一“HAL_Delay()不准有时快有时慢”现象调用HAL_Delay(1000)期望等待1秒但实测只有950ms或在不同温度下波动。根因分析HAL_Delay()底层依赖HAL_GetTick()而HAL_GetTick()又依赖SysTick定时器。SysTick的时钟源是SYSCLK72MHz但它不经过APB预分频器所以SysTick_CLK SYSCLK。如果RCC配置中SYSCLK不稳定如PLL未锁定、HSE启振失败降级到HSIHAL_GetTick()的基准就错了。更隐蔽的是HAL_GetTick()是一个uint32_t变量每49.7天会溢出一次。如果代码中有if (now - start 1000)这样的判断且now start溢出后条件会永远为假导致“无限等待”。排查步骤用示波器测量SysTick-VAL寄存器的重装载值SysTick-LOAD确认其是否为72000000 - 1对应1秒。检查HAL_Init()是否被正确调用它会初始化SysTick。在HAL_GetTick()函数内添加日志观察其返回值是否线性增长。将HAL_Delay()替换为基于通用定时器如TIM6的自定义延时函数绕过SysTick。独家技巧在main()开头添加一段“时钟健康检查”uint32_t start HAL_GetTick(); HAL_Delay(1000); uint32_t end HAL_GetTick(); if (end - start ! 1000) { // 时钟异常进入安全模式 Error_Handler(); }5.2 问题二“输入捕获测频率结果总是偏高10%”现象用TIM2的CH1捕获方波上升沿计算周期但结果比示波器实测值高约10%。根因分析输入捕获的精度取决于定时器时钟频率和信号边沿的抖动。最常见的原因是GPIO引脚未配置为“高速”或“极高速”模式。STM32F103的GPIO速度等级GPIO_SPEED_FREQ_HIGH决定了其输出驱动能力和输入滤波带宽。若设为GPIO_SPEED_FREQ_LOW输入信号经过内部施密特触发器时会引入额外的传播延迟典型值10-20ns在高频信号下累积成显著误差。另一个原因是捕获中断服务程序ISR执行时间过长导致第二次捕获时CNT值已因中断延迟而增加。排查步骤检查GPIO初始化代码确认GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH;。在捕获ISR中第一时间读取__HAL_TIM_GET_COUNTER(htim2)并立即清除捕获标志避免后续操作影响。使用__HAL_TIM_SET_COUNTER(htim2, 0)在每次捕获前清零计数器消除累积误差。独家技巧对于高精度频率测量采用“门控计数法”而非“周期测量法”。即用另一个定时器如TIM3产生一个精确的1秒门控信号高电平在此期间让TIM2自由计数输入信号的上升沿个数。1秒内的计数值就是频率Hz。这种方法消除了单周期测量的边沿抖动影响精度可达0.1%。5.3 问题三“STOP模式下定时器中断不唤醒MCU”现象调用HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI)进入STOP模式后预期由TIM2更新中断唤醒但MCU一直休眠。根因分析STOP模式下SYSCLK、HCLK、PCLK全部停止只有LSE32.768kHz、LSI~32kHz和HSI1414MHz可作为唤醒源。TIM2的时钟源是PCLK1在STOP模式下为0因此TIM2完全停止工作无法产生中断。能在STOP模式下工作的定时器只有低功耗定时器LPTIM1/LPTIM2和RTC。它们有独立的低速时钟源LSE/LSI。排查步骤查阅芯片手册的“Power control”章节确认哪些外设支持STOP模式下的唤醒。将唤醒任务迁移到LPTIM1配置LPTIM1使用LSE时钟ARR 32768对应1秒使能LPTIM_IT_UP中断。在进入STOP前调用HAL_LPTIM_TimeOut_Start_IT(hlptim1, 0xFFFF, 1)。独家技巧如果必须用通用定时器唤醒唯一办法是使用待机模式STANDBY的RTC闹钟唤醒但这需要更长的唤醒时间ms级。STOP模式的精髓在于“快速唤醒”因此必须接受其外设限制选择正确的唤醒源。5.4 问题四“PWM波形占空比跳变不平稳”现象用TIM3的CH2输出PWM__HAL_TIM_SET_COMPARE(htim3,
上一篇/下一篇内容由系统自动关联 返回资讯列表 →