You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:05:36