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

基于S3C2440A的Mini2440板毫秒延迟代码故障排查求助

修复S3C2440定时器毫秒延迟失效问题

作为嵌入式开发者,我刚接触S3C2440时也踩过类似定时器的坑。结合你的代码和打印信息,我帮你梳理出几个核心问题,以及对应的修复方案:

1. 定时器启动顺序完全错误

S3C2440的定时器手动更新(MAN_UPDATE)和启动(START)不能同时置位,正确的流程是先加载初始值,再启动定时器。你的代码同时触发了启动和手动更新,导致计数器没有正确加载TCNTB4的值,自然无法正常计数。

错误代码:

*TCON |= (TIMER4_START | TIMER4_MAN_UPDATE | TIMER4_AUTO_RELOAD); // Autoload, update TCNTB4, start timer 4
*TCON &= ~(TIMER4_MAN_UPDATE); // Clear manual update bit

修复后代码:

// 先重置TCON相关位,避免残留配置
*TCON &= ~(TIMER4_START | TIMER4_MAN_UPDATE | TIMER4_AUTO_RELOAD);
// 开启自动重载
*TCON |= TIMER4_AUTO_RELOAD;
// 手动加载TCNTB4到计数器(此时定时器停止)
*TCON |= TIMER4_MAN_UPDATE;
// 清除手动更新位,准备启动
*TCON &= ~TIMER4_MAN_UPDATE;
// 正式启动定时器
*TCON |= TIMER4_START;

2. 计数值增量逻辑搞反了

S3C2440的定时器是递减计数的,你的代码把步数增量的计算逻辑写反了:

  • 正常递减时,Current应该小于等于Prev,增量是Prev - Current
  • 溢出时(Current > Prev,即计数器从0重载回TCNTB4),增量应该是(Prev + StepsPerMillisecond) - Current(从Prev到0的步数,加上从重载值到Current的步数)

错误代码:

if (Current <= Prev) {
    ElapsedSteps += Current + (StepsPerMillisecond - Prev);
} else
    ElapsedSteps += Current - Prev;

修复后代码:

if (Current <= Prev) {
    ElapsedSteps += Prev - Current;
} else {
    // 处理定时器溢出的情况
    ElapsedSteps += (Prev + StepsPerMillisecond) - Current;
}

3. 初始计数值读取时机过早

启动定时器后立即读取TCNTO4,可能定时器还没开始计数,导致初始值不准确。可以添加几个空指令等待计数器启动:

*TCON |= TIMER4_START;       // 启动定时器
// 等待定时器开始计数,避免初始值错误
asm volatile("nop");
asm volatile("nop");
Prev = *TCNTO4;
Uart_Printf("\nInitial TCNTO4: %u \n", Prev);

4. 可选:修正取整误差

你的StepsPerMillisecond计算结果是50000000/(1000*2*256)=97.65625,直接取整为97会导致每个毫秒少算0.656步,累计1秒就会少算656步,延迟时间偏短。可以用四舍五入修正:

StepsPerMillisecond = (U32)(PLCK/(1000.0*Divider*(Prescaler+1)) + 0.5);

完整修复后的MilliDelay函数

void MilliDelay(unsigned long mills) {
    U32 PrescalerMask = 0xFF << 8; // Prescaler 1 for timers 2, 3, & 4
    U32 DividerMask = 0xF << 16; // Divider (MUX 4) for timer 4
    U32 Prescaler = 255;
    U32 Divider = 2;
    U32 TotSteps;
    U32 StepsPerMillisecond;
    U32 Current;
    U32 Prev;
    U32 ElapsedSteps = 0;

    Util_Printf("mills: %u\n", mills);
    Util_Printf("PLCK: %u\n", PLCK);
    Util_Printf("Divider: %u\n", Divider);
    Util_Printf("Prescaler: %u\n", Prescaler);

    // Timer configuration
    *TCFG0 &= ~PrescalerMask; // Zero out prescaler
    *TCFG0 |= PrescalerMask; // Prescaler 255
    *TCFG1 &= ~DividerMask; // Zero out divider
    *TCFG1 |= TIMER4_MUX_1_2; // Divider 1/2

    // 四舍五入修正取整误差
    StepsPerMillisecond = (U32)(PLCK/(1000.0*Divider*(Prescaler+1)) + 0.5);
    Uart_Printf("Steps per Mil: %u\n", StepsPerMillisecond);
    TotSteps = StepsPerMillisecond*mills;
    Uart_Printf("Tot Steps: %u\n", TotSteps);

    *TCNTB4 = StepsPerMillisecond;

    // 正确的定时器初始化流程
    *TCON &= ~(TIMER4_START | TIMER4_MAN_UPDATE | TIMER4_AUTO_RELOAD);
    *TCON |= TIMER4_AUTO_RELOAD;
    *TCON |= TIMER4_MAN_UPDATE;  // 加载TCNTB4到计数器
    *TCON &= ~TIMER4_MAN_UPDATE; // 清除手动更新位
    *TCON |= TIMER4_START;       // 启动定时器

    // 等待定时器开始计数
    asm volatile("nop");
    asm volatile("nop");
    Prev = *TCNTO4;
    Uart_Printf("\nInitial TCNTO4: %u \n", Prev);

    while(ElapsedSteps < TotSteps) {
        Current = *TCNTO4;
        if (Current <= Prev) {
            ElapsedSteps += Prev - Current;
        } else {
            // 处理溢出情况
            ElapsedSteps += (Prev + StepsPerMillisecond) - Current;
        }
        Prev = Current;
        // 避免过度占用CPU
        asm volatile("nop");
    }

    /* Stop the timer */
    *TCON &= ~(TIMER4_START | TIMER4_AUTO_RELOAD);
    Uart_Printf("Elapsed Steps: %u \n", ElapsedSteps);
}

额外检查点

  • 确认TIMER4_MUX_1_2宏定义是否对应1/2分频(S3C2440的TCFG1寄存器中,定时器4的MUX位是[19:16],值0010对应1/2分频)
  • 验证PLCK实际是否为50MHz:可以查看CLKDIVN寄存器的配置,确保系统时钟分频正确

内容的提问来源于stack exchange,提问作者brunshte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:52:29