ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32F407高级定时器输入捕获:频率与脉宽测量驱动详解

STM32F407高级定时器输入捕获:频率与脉宽测量驱动详解 简介STM32F407高级定时器输入捕获驱动基于ARM Cortex-M4平台面向嵌入式开发者和高校学生解决精确测量外部信号频率、周期及边沿时刻的需求适用于电机测速、编码器接口与PWM控制等场景。压缩包仅1.56MB包含229个文件以117个.h头文件和103个.c源文件为主涵盖HAL库定时器、I2C、RCC等驱动源码另附Keil工程配置uvprojx/uvoptx及编译好的bin/hex固件目录结构紧凑便于查阅。已有265人学习下载。项目从时钟配置、定时器初始化、输入捕获通道滤波与极性设置到中断/DMA处理及事件计算均有完整实现并充分考虑了STM32F4系列间的移植性可直接编译运行也可作为学习高级定时器驱动开发的参考范例。1. STM32F407高级定时器输入捕获驱动要解决的第一个难题在STM32F407项目里测转速、测频率、测占空比的场景很多但真正把高级定时器输入捕获当作核心驱动来做的人不多。多数人拿通用定时器的捕获例程改引脚直接用结果在电机控制板或PMSM驱动板上经常出问题TIM2到TIM5挂在APB1上时钟倍频逻辑容易算错高级定时器捕获通道与互补输出、刹车输入共享引脚配置稍有不慎就影响死区逻辑。“高级定时器”四个字决定了不能用普通定时器套路它带死区生成、刹车输入、重复计数等模块影响时钟分配、中断设计和引脚复用。如果只需要测普通方波频率用TIM2更省事标题强调高级定时器通常是因为这颗芯片同时要输出PWM或做电机控制能空出来的只剩TIM1/TIM8。这时首要问题不是捕获怎么写而是在已占用的定时器上安全增加一路捕获。2. 高级定时器在F407上的输入捕获能力边界与选择依据2.1 TIM1/TIM8与通用定时器在输入捕获上的差异STM32F407中的TIM1和TIM8挂在APB2总线TIM2到TIM5挂在APB1总线。APB1最高42MHzAPB2最高84MHz定时器时钟在PCLK与定时器需求不一致时会自动倍频很多人直接在驱动里写72MHz作为计数频率就是忘了这套倍增规则。TIM1/TIM8各有4个输入捕获通道支持边沿检测、数字滤波、捕获分频和DMA请求与通用定时器能力相当但多出互补通道、死区生成、重复计数器和刹车输入这几个模块。特性TIM1/TIM8高级TIM2-TIM5通用总线APB284MHzAPB142MHz捕获通道44互补输出与死区有无重复计数器 RCR有无刹车输入有无中心对齐捕获有有在驱动代码里真正起作用的是两块。一是重复计数器它决定更新事件的产生节奏。RCR保持0时行为与通用定时器相同一旦配置成非零值计数器每经过RCR1次上溢才产生一次更新事件溢出次数统计逻辑会完全不同。二是中心对齐模式CMS位配置为01或10时更新事件发生在计数器上溢和下溢两个点如果不清楚这个行为测脉宽时会发现中断频率比预期多了一倍。同一个捕获通道上既接霍尔传感器边沿又输出互补PWM时引脚复用和中断优先级的设计也需要整体考虑。选型时的通常做法是只要占用TIM1/TIM8做捕获就要先确认同一时刻其他通道在做什么。例如TIM1_CH2做PWM输出、TIM1_CH1做捕获那么在捕获中断里就不要频繁调整PSC否则两个通道共享的预分频器会把PWM周期打乱。这部分是高级定时器比通用定时器更需要整体考量的地方。2.2 输入捕获路径上的硬件环节与参数拆解输入捕获驱动从引脚到数据实际经过的链路为GPIO复用、输入滤波器ICxF、捕获分频器ICPS、边沿检测器、捕获寄存器CCR再到中断或DMA请求。每一级都有对应的控制位。参考《STM32F4xx参考手册》定时器一章把TIM1配置为捕获通道1的典型设置如下TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x0; TIM_ICInit(TIM1, TIM_ICInitStructure);直连输入的意思是信号源来自同编号的TIx引脚交叉选择在PWM输入模式里才会用到后面验证章节会看到。捕获分频器可选1、2、4、8档它的作用是把捕获动作本身降频在信号频率超出中断承受能力时改用降频捕获。滤波器取值0到15控制采样频率和采样次数并不是越大越好强噪声环境才需要加大代价是沿定位延迟变大。这个链路里决定测量精度的分界点在同步电路与捕获寄存器之间。捕获值会在有效边沿之后的某个节拍锁存因此CCR值最多引入一个计数时钟的量化误差。计数时钟越快量化误差相对越小代价是同样的信号周期被分配更多计数节拍可测周期上限变小。要范围还是要精度需要通过PSC来折中具体组合在第四章展开。2.3 中断捕获和DMA捕获的取舍驱动里最常见的做法是开捕获中断进中断后读CCR并保存。这个套路在信号频率不高时完全够用STM32F407处理几十kHz的边沿中断没有压力但超过100kHz或同时启用多个捕获通道时中断服务函数频繁抢占会导致主循环里的ADC采集和通信处理被大幅延迟此时改用DMA把CCR值搬到内存环形缓冲区更合适。DMA捕获方式下捕获比较事件作为DMA请求源硬件直接把CCR寄存器内容搬进内存不经过内核不会造成流水线等待。而且DMA多通道可以连读配合定时器分频后连续记录多个周期再做软件滤波得到的结果比中断方式更稳定。代价是失去了在中断里做“当前沿有效时切换极性”这种逻辑的能力如果要做脉宽测量而需要上升沿和下降沿交替捕获依然必须走中断或复用两个通道。所以实际驱动里通常把DMA用在单边沿频率测量把中断用在脉宽和PWM输入模式。3. STM32F407标准库输入捕获驱动的初始化与中断参数设置3.1 最小工程初始化RCC、GPIO和TIM1通道用一个标准库工程的最小代码演示搭建过程。工程基于STM32F4xx标准外设库主频168MHzAPB2时钟84MHzTIM1定时器时钟自动倍频到168MHz。TIM1_CH1复用引脚为PA8把PA8配置为复用功能并连接到TIM1void IC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1); GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Prescaler 168 - 1; TIM_TimeBaseStructure.TIM_Period 0xFFFF; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV2; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); TIM_ClearFlag(TIM1, TIM_FLAG_Update); TIM_ITConfig(TIM1, TIM_IT_Update | TIM_IT_CC1, ENABLE); NVIC_InitStructure.NVIC_IRQChannel TIM1_CC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM1, ENABLE); }预分频器设为168-1计数时钟正好1MHz每个计数代表1微秒对后来的频率换算非常直观。TIM_Period设为0xFFFF接近理论最大量程。时钟分频TIM_CKD_DIV2只影响死区发生器与数字滤波器采样时钟不直接影响输入捕获边沿检测的计数节拍这个位在很多例程里被随手写成DIV1其实在只用输入捕获时不必过度关注。NVIC只开了CC1和更新两个中断更新中断用于处理16位计数器回绕。配置项取值影响TIM_Prescaler168-1计数频率1MHz1us/计数TIM_Period0xFFFF单次最大量程65535usTIM_ICPrescalerDIV1每次有效沿都捕获TIM_ICFilter0x0不开启数字滤波3.2 捕获中断服务函数里做频率测量频率测量的思路是对相邻两次有效沿的CCR差值做倒数换算计数时钟1MHz时频率数值就是1000000除以节拍数。由于计数器是16位相邻沿间隔时间超过65535us时计数器会回绕需要借助更新中断里的溢出计数来补偿volatile uint16_t g_LastEdge 0; volatile uint16_t g_ThisEdge 0; volatile uint32_t g_Ticks 0; volatile uint32_t g_OverflowCnt 0; volatile uint32_t g_MeasuredFreq 0; void TIM1_CC_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_CC1) ! RESET) { g_LastEdge g_ThisEdge; g_ThisEdge TIM_GetCapture1(TIM1); TIM_ClearITPendingBit(TIM1, TIM_IT_CC1); if (g_ThisEdge g_LastEdge) { g_Ticks (uint32_t)(g_ThisEdge - g_LastEdge); } else { g_Ticks (uint32_t)(0x10000 - g_LastEdge g_ThisEdge); } if (g_OverflowCnt 0) { g_Ticks (uint32_t)g_OverflowCnt * 0x10000; g_OverflowCnt 0; } if (g_Ticks 0) { g_MeasuredFreq 1000000UL / g_Ticks; } } }代码里的两个if分支是处理计数器回绕问题的核心。当本次捕获值大于上次说明没有跨越回绕点直接相减当本次值小于上次说明计数器已经越过FFFF用65536加上本次值再减上次值。溢出计数器在两次边沿之间每发生一次更新中断就累加一次补偿时乘以65536后加到节拍数中。这个方案的测量精度受两个因素影响一是CCR锁存的量化误差最多1个计数时钟二是溢出标志在捕获中断里清零下一轮溢出前需要保证更新中断不被遗漏。3.3 溢出中断一起参与测量时的参数注意工程里同时使能了更新中断它的服务函数如下void TIM1_UP_TIM10_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update) ! RESET) { g_OverflowCnt; TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } }一个容易被忽略的细节是定时器溢出计数和捕获寄存器锁存是不同步的。捕获标志请求的是CCR值快照溢出标志体现的是计数器当前状态。当溢出发生在捕获之前的一两个周期内读取的CCR可能仍是回绕后的值溢出计数却已经累加导致多算一个0x10000周期。稳妥做法是先清掉更新标志再读捕获值。代码中每次捕获中断都会清理更新标志目的就在这里。解读这种边界行为时不要用通用定时器的惯性思路套高级定时器很多F407例程在TIM2上能正常工作的代码换到TIM1上就出现频率漂移排除时钟误差后多半是溢出与捕获不同步导致的。注意先清更新标志再读捕获值避免多算一个回绕周期。4. 高级定时器输入捕获测频率与脉宽必调参数和溢出处理4.1 PSC和ARR怎么配合决定测量范围与分辨率输入捕获的计数时钟由定时器时钟除以(PSC1)得到测量量程由ARR决定。以TIM1的168MHz定时器时钟为例想得到1MHz计数频率就把PSC设为167此时每个计数1us想测更低频的转速信号就把计数频率调到50kHzPSC设为3359但代价是每个计数的分辨率从1us变成20us。这里有个容易忽略的点捕获寄存器只有16位ARR的上限是65535因此测量最大周期等于ARR对应的最大时间而不是PSC单独决定的。配置位作用范围参数策略TIM_Prescaler计数频率与分辨率先算量程再定系数TIM_PeriodARR最大可测周期高频测频可适当缩小TIM_ICFilter信号抗干扰有毛刺时逐档增大TIM_ICPrescaler中断负载高频占用高时加大分频比如做伺服电机的霍尔测速额定转速3000rpm4对极一个霍尔信号周期对应机械旋转一圈的四分之一电频率约200Hz一个周期5ms采用1MHz计数频率时对应5000个计数完全落在65535量程内应优先保证分辨率。如果做低速风力发电转速监测最低转速0.5Hz周期2s远超1MHz计数下的65535us量程就需要把PSC调到计数频率100Hz左右这时分辨率降到10ms对慢变信号完全够用。频率下限和分辨率是一对矛盾先按被测信号的极限周期算出量程再回头定PSC。4.2 数字滤波器和捕获分频器的正确应用场合数字滤波器会引入额外的信号延迟ICxF值每增加一档延迟时间和采样窗口都变大。低转速电机霍尔信号在换相时刻会产生抖动脉冲适合开启滤波但要注意霍尔信号直接出现在速度环反馈路径上滤波器延迟会表现为反馈相位滞后转速闭环参数需要重新收敛。增量编码器的Z信号作为绝对位置零点经常进捕获通道机械安装偏差引起的Z脉冲宽度抖动可以通过滤波器压制但滤波强度增加后沿抖动也随之增加需要折中。捕获分频器ICPS是把捕获动作按1、2、4、8分频。信号频率高导致进中断太频繁时使用例如电机驱动板上的编码器Z信号或PM SM霍尔信号在转速超过一定阈值后可以配置DIV4代价是每次测量的结果反映的是多个周期的平均频率瞬时抖动信息被抹掉。在视觉驱动平台这类需要精确记录事件时刻的场景里宁可提高中断优先级也不要轻易开捕获分频事件时间的抖动会影响后续算法对时刻戳的依赖。4.3 中心对齐模式下更新事件的释放位置高级定时器的中心对齐模式在输入捕获场景里经常被误解。当CR1的CMS位设为向上/向下模式时更新事件在上溢和下溢都出现更新中断频率翻倍溢出计数会同步加倍。如果代码里还用向上计数模式时的溢出补偿系数测量出的周期会偏小一半。中心对齐模式常见于三相PWM控制此时高级定时器的四个捕获通道通常同时承担霍尔换相信号和相电流采样触发。例如在计数器匹配点触发ADC采样依赖的正是更新事件和比较事件在上下溢对称式产生。应用这个特性时可以把ADC采样时刻锁定在捕获沿附近让电流采样点避开开关噪声窗口。驱动里如果同时使用捕获和PWM输出需要把中心对齐模式下的溢出次数计算单独抽成函数和向上计数模式分开处理。注意中心对齐模式下更新事件出现在上溢和下溢两个位置溢出计数统计要按两倍计算。5. 用PWM输入模式验证F407捕获驱动频率与占空比的准确性5.1 PWM输入模式和普通捕获的配置差异PWM输入模式把同一输入信号TI1同时路由到两个捕获通道IC1在上升沿捕获计数值IC2在下降沿捕获计数值一个得到周期一个得到高电平脉宽。配置时使用TIM_ICSelection_IndirectTI或把IC1、IC2都指向TI1。高级定时器和通用定时器都能这么做F407上没有额外硬件限制但TIM1做这件事时要注意四个通道里其他通道的状态不能因为复用关系改变其他PWM输出通道的极性。5.2 验证步骤与示例代码验证驱动最直接的方法是先让另一个定时器输出已知频率和占空比的PWM再用目标驱动去测。用TIM2产生1kHz、50%占空比的方波配置关键点如下TIM_TimeBaseStructure.TIM_Prescaler 168 - 1; TIM_TimeBaseStructure.TIM_Period 1000 - 1; // TIM2_CH1输出比较配置略PA0作为TIM2_CH1输出然后在TIM1捕获中断里同时读取IC1和IC2uint32_t period_ticks TIM_GetCapture1(TIM1); uint32_t duty_ticks TIM_GetCapture2(TIM1); uint32_t freq 1000000UL / period_ticks; uint16_t duty (uint16_t)(duty_ticks * 10000UL / period_ticks);得到period_ticks约为1000、duty_ticks约为500换算出的频率和占空比与信号源一致驱动即通过验证。这个闭环测试比只接信号发生器更可靠因为它能排除外部信号源本身的抖动。测试时把PA0和PA8用短线直接相连避免杜邦线过长引入容性串扰。5.3 用示波器和串口输出辅助判断故障位置如果实测频率偏差很大先把串口打印出的原始CCR值发出来对照逻辑分析仪的边沿时间逐个核对。偏差固定比例时优先怀疑PSC或定时器时钟算错偏差随机跳动时优先排查滤波器设置和地线噪声占空比对但频率差一倍时检查更新中断里有没有额外把溢出次数多算一次。提示先用内部定时器自测再接入外部信号源能更快定位是驱动问题还是信号质量问题。做这些判断时从计数器路径一步步推回比反复改参数快得多。驱动里保留原始CCR值的调试接口在当前阶段比直接交出计算后的频率值更有用后续接入真实编码器或霍尔信号时还可以拿这套验证逻辑继续复用。本文还有配套的精品资源点击获取
返回列表