dsPIC33CK512MP608读取SCCP定时器寄存器值异常排查
问题根源与解决方案
核心问题分析
你遇到的毫秒值异常(偶尔出现负数、数值差65536微秒),本质是SCCP1的32位计数器由两个独立的16位寄存器(CCP1TMRH、CCP1TMRL)组成,读取时未做同步处理导致的寄存器快照不一致:
- 当读取完低寄存器
CCP1TMRL后,定时器刚好发生16位溢出,高寄存器CCP1TMRH自动加1;此时再读取高寄存器,得到的是更新后的值,组合出的32位数值会比实际值少2^16(65536)微秒。 - 若你的
millis()函数使用有符号整数计算时间差,就会出现timestamp < tick100ms的负数情况。
针对性解决方案
1. 实现同步的32位定时器读取逻辑
必须确保读取的高低寄存器是同一时刻的快照,可通过重复读取高寄存器直到两次值一致来实现:
#include <stdint.h> uint32_t SCCP1_GetSyncTimerValue(void) { uint16_t high_first, high_second, low; // 重复读取直到高寄存器在读取低寄存器前后无变化 do { high_first = CCP1TMRH; low = CCP1TMRL; high_second = CCP1TMRH; } while (high_first != high_second); // 组合成32位微秒数 return ((uint32_t)high_first << 16) | low; }
替换原有的寄存器读取或SCCP1_Timer_CounterGet()调用,即可避免跨溢出的错误读取。
2. 使用无符号整数处理时间计算
将毫秒值、时间戳变量统一改为uint32_t类型,利用无符号整数的自动溢出特性,即使定时器发生32位溢出,时间差计算仍能得到正确结果:
uint32_t millis(void) { // 读取同步后的微秒数,转换为毫秒 return SCCP1_GetSyncTimerValue() / 1000; } // 示例延时判断逻辑 void checkDelay(void) { static uint32_t tick100ms = 0; uint32_t timestamp = millis(); // 无符号差值计算,自动处理溢出 if (timestamp - tick100ms >= 100) { // 执行定时任务 tick100ms = timestamp; } }
3. 验证定时器配置的正确性
确认SCCP1确实以1微秒为步长递增:
- 若系统时钟
Fosc为64MHz,则指令周期Fcy = Fosc/2 = 32MHz; - 需将SCCP1的预分频器设置为
1:32,这样定时器每32个Fcy周期加1,对应1微秒的步长; - 确保SCCP1配置为自由运行的32位定时器模式(
PRH和PRL均设为0xFFFF,让计数器无上限循环)。
验证测试
替换读取逻辑后,单独测试核心计时代码:
- 连续读取1000次定时器值,检查是否再出现跳变65536的情况;
- 运行延时判断逻辑,观察是否还会出现负数的时间差输出。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

