news 2026/4/16 13:42:17

简单理解:计算高电平时间(当前重载值 - 第一个上升沿值) + (重载值 * (溢出次数-1)) + 下降沿值

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
简单理解:计算高电平时间(当前重载值 - 第一个上升沿值) + (重载值 * (溢出次数-1)) + 下降沿值

以下是对注释对应的高电平时间计算逻辑的详细解析表格及核心说明,该逻辑适用于嵌入式定时器捕获模式(测量信号高电平持续时间,兼容 XT 系列 / STM32 等 MCU),核心解决 “高电平跨定时器溢出” 的计算问题:

计算项(注释拆解)语法 / 变量说明核心功能含义适用场景与示例
当前重载值定时器自动重载寄存器值(XT 系列:XT_GPTM0->CRR;STM32:htim->Instance->ARR定时器计数的最大值(如 16 位定时器默认65535),计数到该值后溢出归零是计算 “不完整周期计数” 的基准,所有跨溢出场景都需基于该值拆分计算
第一个上升沿值全局变量(volatile uint16_t,如G_RisingEdgeCnt信号高电平起始点的定时器捕获值(上升沿触发时锁存的计数值)高电平的 “起点标记”,由定时器捕获中断赋值(如G_RisingEdgeCnt = GPTM_GetCaptureCompare0(...)
(当前重载值 - 第一个上升沿值)无符号整数减法表达式计算 “从第一个上升沿到定时器第一次溢出” 的计数值(起点到溢出的高电平计数)例:CRR=65535,上升沿值 = 50000 → 结果 = 15535(50000~65535 共 15535 次计数)
溢出次数全局变量(volatile uint16_t,如G_OverFlowCnt高电平持续期间,定时器发生溢出的总次数(由溢出中断累加)高电平跨 N 个完整定时器周期时,溢出次数 = N+1(如跨 1 个完整周期,溢出次数 = 2)
(溢出次数 - 1)整数减法表达式计算高电平期间 “完整的定时器周期数”(排除首尾不完整周期)例:溢出次数 = 3 → 完整周期数 = 2(即 0~65535 重复 2 次)
(重载值 * (溢出次数 - 1))乘法表达式计算 “完整溢出周期” 对应的总计数(高电平在完整周期内的计数)例:CRR=65535,溢出次数 = 3 → 65535 * 2 = 131070(2 个完整周期的总计数)
下降沿值全局变量(volatile uint16_t,如G_FallingEdgeCnt信号高电平结束点的定时器捕获值(下降沿触发时锁存的计数值)高电平的 “终点标记”,由定时器捕获中断赋值(需配置为 “下降沿捕获” 或 “双边沿捕获”)
整体计算逻辑高电平计数 = (CRR - 上升沿值) + (CRR*(溢出次数-1)) + 下降沿值拆分高电平为 3 段计数求和:起点→第一次溢出 + 完整溢出周期 + 最后溢出→下降沿解决 “高电平持续时间> 定时器单次计数周期” 的计算误差,确保跨溢出场景下结果准确

核心补充说明(逻辑原理、场景与代码)

1. 为什么需要这样拆分计算?

定时器是 “循环计数”(0→CRR→0→CRR...),若高电平持续时间超过 1 个定时器周期(即发生溢出),直接用下降沿值 - 上升沿值会得到错误结果(无符号减法借位导致)。通过 “拆分三段” 可覆盖所有场景:

  • 第一段:上升沿(起点)→ 第一次溢出(不完整周期);
  • 第二段:多次完整溢出周期(若有);
  • 第三段:最后一次溢出→下降沿(终点,不完整周期)。
2. 典型示例(直观理解)

假设:

  • 定时器配置:16 位(CRR=65535),时钟 = 1MHz(1μs / 次计数);
  • 高电平起点(上升沿):G_RisingEdgeCnt=50000
  • 高电平期间溢出 2 次(G_OverFlowCnt=3);
  • 高电平终点(下降沿):G_FallingEdgeCnt=20000

计算过程:

高电平计数 = (65535 - 50000) + (65535*(3-1)) + 20000 = 15535 + (65535*2) + 20000 = 15535 + 131070 + 20000 = 166605 次计数

实际高电平时间 = 166605 μs = 166.605 ms(与物理时间完全一致)。

3. 无溢出场景的兼容性

若高电平未溢出(G_OverFlowCnt=1),公式自动简化为:

高电平计数 = (CRR - 上升沿值) + 0 + 下降沿值

因无溢出时下降沿值 > 上升沿值,根据无符号整数模运算特性:(CRR - 上升沿值) + 下降沿值 = 下降沿值 - 上升沿值 + CRR?→ 此处需注意:无溢出时需单独判断优化!若G_OverFlowCnt=1,直接用下降沿值 - 上升沿值即可(例:上升沿 = 10000,下降沿 = 30000 → 30000-10000=20000 次计数,正确)。完整代码中需增加判断,避免冗余计算:

if (G_OverFlowCnt == 1) { G_HighLevelCnt = G_FallingEdgeCnt - G_RisingEdgeCnt; // 未溢出,直接相减 } else { G_HighLevelCnt = (XT_GPTM0->CRR - G_RisingEdgeCnt) + (XT_GPTM0->CRR*(G_OverFlowCnt-1)) + G_FallingEdgeCnt; // 跨溢出计算 }
4. 关键前提(代码配置要求)

要使该公式生效,需满足 3 个核心配置:

配置项要求说明
定时器捕获模式通道配置为 “双边沿捕获”(上升沿 + 下降沿都触发捕获),或分别配置上升沿、下降沿捕获通道
中断使能使能 “捕获中断”(获取上升沿 / 下降沿值)和 “溢出中断”(累加溢出次数)
全局变量修饰上升沿值、下降沿值、溢出次数、高电平计数均需加volatile(避免编译器优化)
5. 完整代码片段(结合中断与计算)
// 全局变量(volatile 不可少) volatile uint32_t G_HighLevelCnt = 0; // 高电平总计数(32位避免溢出) volatile uint16_t G_RisingEdgeCnt = 0; // 上升沿捕获值(高电平起点) volatile uint16_t G_FallingEdgeCnt = 0; // 下降沿捕获值(高电平终点) volatile uint16_t G_OverFlowCnt = 0; // 溢出次数 volatile uint8_t G_CaptureFlag = 0; // 捕获标志:0=未捕获上升沿,1=已捕获上升沿 // 定时器0中断服务程序(捕获中断+溢出中断) void GPTM0_IRQHandler(void) { // 1. 捕获中断(通道0,双边沿触发) if (GPTM_GetIntStatus((XT_GPTM_TypeDef*)XT_GPTM0, GPTM_INT_CC0) == SET) { if (G_CaptureFlag == 0) { // 第一次捕获:上升沿(高电平起点) G_RisingEdgeCnt = GPTM_GetCaptureCompare0((XT_GPTM_TypeDef*)XT_GPTM0); G_OverFlowCnt = 0; // 重置溢出次数 G_CaptureFlag = 1; // 标记已捕获上升沿 } else { // 第二次捕获:下降沿(高电平终点) G_FallingEdgeCnt = GPTM_GetCaptureCompare0((XT_GPTM_TypeDef*)XT_GPTM0); // 核心计算:高电平时间(处理跨溢出) if (G_OverFlowCnt == 1) { G_HighLevelCnt = G_FallingEdgeCnt - G_RisingEdgeCnt; // 未溢出 } else { G_HighLevelCnt = (XT_GPTM0->CRR - G_RisingEdgeCnt) + (XT_GPTM0->CRR * (G_OverFlowCnt - 1)) + G_FallingEdgeCnt; // 跨溢出 } G_CaptureFlag = 0; // 重置标志,等待下一次测量 } GPTM_ClearIntPendingBit((XT_GPTM0, GPTM_INT_CC0); // 清除捕获中断标志 } // 2. 溢出中断(更新事件中断) if (GPTM_GetIntStatus((XT_GPTM_TypeDef*)XT_GPTM0, GPTM_INT_UEV) == SET) { if (G_CaptureFlag == 1) { // 已捕获上升沿,才累加溢出次数 G_OverFlowCnt++; } GPTM_ClearIntPendingBit((XT_GPTM_TypeDef*)XT_GPTM0, GPTM_INT_UEV); // 清除溢出中断标志 } } // 主程序:换算高电平实际时间 void main(void) { GPTM_Init(); // 初始化:GPTM0通道0双边沿捕获,使能捕获+溢出中断(省略配置) _EINT(); // 使能全局中断 uint32_t timer_clk = 1000000; // 定时器时钟=1MHz(1μs/次) float high_level_time; // 高电平时间(单位:ms) while(1) { if (G_HighLevelCnt != 0) { // 换算为实际时间:计数次数 / 时钟频率(Hz)= 秒 → 转毫秒 high_level_time = (float)G_HighLevelCnt / timer_clk * 1000; printf("高电平时间:%.2f ms\n", high_level_time); // 打印结果 G_HighLevelCnt = 0; // 清零,等待下一次测量 } } }
6. 跨平台兼容性(XT 系列 vs STM32)

该计算逻辑的核心是 “定时器计数原理”,与具体 MCU 无关,仅需替换寄存器 / 函数命名:

功能项XT 系列(示例)STM32(HAL 库示例)
自动重载值XT_GPTM0->CRRhtim1.Instance->ARR
读取捕获值GPTM_GetCaptureCompare0(XT_GPTM0)HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_0)
捕获中断标志GPTM_INT_CC0TIM_IT_CC1(通道 1 为例)
溢出中断标志GPTM_INT_UEVTIM_IT_UPDATE

总结

该注释对应的公式是嵌入式高电平时间测量的核心逻辑,核心价值是:

  1. 解决 “高电平跨定时器溢出” 的计算误差,覆盖所有时长场景;
  2. 基于定时器自动重载值拆分计数,确保结果精准;
  3. 需配合 “双边沿捕获 + 溢出中断” 的硬件配置,及volatile修饰的全局变量,才能正常工作。

实际使用时,需根据 MCU 型号调整寄存器 / 函数命名,并在无溢出时优化计算(直接相减),提升效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 10:56:29

YOLOv8知识库构建:常见问题自助查询系统

YOLOv8知识库构建:常见问题自助查询系统 在智能视觉应用加速落地的今天,一个现实问题始终困扰着开发者:为什么同一个模型代码,在A机器上跑得飞快,换到B环境却频频报错? 这背后往往不是算法本身的问题&#…

作者头像 李华
网站建设 2026/4/16 12:21:32

揭秘PHP在工业自动化中的应用:如何实现毫秒级数据采集与实时响应

第一章:PHP在工业自动化中的角色与挑战PHP 作为一种广泛应用于Web开发的脚本语言,近年来在工业自动化系统中也展现出独特的集成潜力。尽管传统上工业控制更依赖C、Python或专用PLC语言,但PHP凭借其快速开发能力、丰富的Web接口支持以及与数据…

作者头像 李华
网站建设 2026/4/7 7:32:35

C#跨平台监控为何难落地:90%团队忽略的4个核心陷阱及应对策略

第一章:C#跨平台性能监控的现状与挑战随着 .NET Core 和 .NET 5 的发布,C# 已成为真正意义上的跨平台开发语言,广泛应用于 Windows、Linux 和 macOS 等多种操作系统。然而,在跨平台环境下实现高效、统一的性能监控仍面临诸多挑战。…

作者头像 李华
网站建设 2026/4/16 11:06:22

Java毕设项目推荐-基于springboot的社区智慧养老系统基于springboot社区智慧医疗养老系统【附源码+文档,调试定制服务】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/4/16 10:42:49

YOLOv8模型融合多个类别:自定义names字段方法

YOLOv8模型融合多个类别:自定义names字段方法 在智能安防、工业质检等实际项目中,我们常常面临一个现实问题:标准的目标检测模型虽然能识别“人”或“车辆”,却无法区分“戴安全帽的人”和“未戴安全帽的人”。这种精细化识别需求…

作者头像 李华