基于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
相关产品推荐
相关产品推荐

