AVR汇编TIMER0普通模式配置问题:ATmega328P延迟失效且死循环
AVR TIMER0普通模式延迟故障排查与修复
核心问题分析
1. 定时器初始化代码未执行
你定义了init_norm和load_ticks初始化子程序,但主程序main没有调用它们,导致定时器0的预分频配置、计数器清零操作完全没生效,定时器根本不会开始计数,timer_子程序会一直等待TOV0标志位,直接陷入无限循环。
2. 预分频配置注释错误+逻辑误解
代码中ldi r16, (1<<2)|(1<<0)对应的是TCCR0B的CS02=1、CS00=1,这是1024分频(ATmega328P定时器0的分频规则:CS02/CS01/CS00=101时为1024分频),注释写的1023是错误的,会导致后续延迟计算偏差。
3. Delay子程序循环逻辑错误
当前delay的逻辑是先dec r28再等待定时器溢出,当r28初始值为78时,每次循环减1,直到r28变为0后,dec r28会得到0xFF(无符号8位寄存器下0减1溢出为255),此时breq buffer_1永远不会触发,导致循环无法终止,陷入死循环。
4. 定时器溢出标志位清除方式错误
AVR的TIFR0寄存器中,TOV0(位0)的清除规则是向该位写入1,而你用cbi TIFR0,0是写入0,根本无法清除溢出标志。第一次溢出后标志位会一直保持为1,后续timer_会直接返回,失去延迟效果;如果从未溢出,则会一直等待标志位,卡死程序。
5. 引脚方向寄存器重复配置
主循环中每次都执行sbi DDRB,5和cbi DDRB,5,反复切换PB5的引脚方向,这不仅无意义,还会导致输出信号异常,引脚方向只需要初始化一次即可。
修复后的完整代码
.text ;define_addr .org 0x000 .equ TCCR0A, 0x24 .equ TCCR0B, 0x25 .equ TCNT0, 0x26 .equ OCR0A, 0x27 .equ OCR0B, 0x28 .equ TIMSK0, 0x6E .equ TIFR0, 0x15 .equ DDRB, 0x04 .equ PORTB, 0x05 /* --ticks=second/(prescaler*1000/cpu_clock) --ticks=number of times the counter reaches overflow state ; 注:1MHz时钟+1024分频下,Timer0溢出一次耗时≈262.144ms */ ;------------start rjmp init_norm ; 复位后直接跳转到初始化 ;---init_timer init_norm: ; 配置PB5为输出引脚(只初始化一次) sbi DDRB, 5 ; 设置Timer0为普通模式,预分频1024 ldi r16, 0x00 ; TCCR0A清零,普通模式(WGM01/WGM00=0) out TCCR0A, r16 ldi r16, (1<<CS02)|(1<<CS00) ; 预分频1024 out TCCR0B, r16 ldi r16, 0 out TCNT0, r16 ; 计数器清零 load_ticks: ldi r28, 78 ; 设置溢出次数(78次≈20.4s延迟) ldi r29, 0 rjmp main ; 初始化完成跳主程序 ;-------------main main: sbi PORTB, 5 ; PB5置高 call delay cbi PORTB, 5 ; PB5置低 call delay rjmp main ;-----subroutine delay: ; 先判断r28是否为0,再执行递减和等待 cpi r28, 0x00 breq check_r29 dec r28 call timer_wait rjmp delay check_r29: cpi r29, 0x00 breq delay_end dec r29 call timer_wait rjmp delay delay_end: ; 延迟结束后重新加载初始计数值,避免下次调用时计数值为0 ldi r28, 78 ldi r29, 0 ret ; 等待Timer0溢出并清除标志位 timer_wait: sbis TIFR0, TOV0 ; 等待TOV0标志位置1 rjmp timer_wait sbi TIFR0, TOV0 ; 写入1清除溢出标志 out TCNT0, r16 ; 计数器重新清零(可选,确保每次溢出间隔一致) ret
关键修改说明
- 添加初始化调用:复位入口直接跳转到
init_norm,确保定时器配置和引脚方向初始化执行。 - 修正标志位清除方式:用
sbi TIFR0, TOV0替代cbi,符合AVR的标志位清除规则。 - 重构Delay逻辑:先判断寄存器是否为0再递减,避免无符号溢出导致的死循环,同时在延迟结束后重新加载初始计数值,保证每次调用
delay的时长一致。 - 移除冗余配置:将DDRB配置移到初始化代码中,主循环只控制引脚电平。
- 明确定时器模式:显式清零TCCR0A,确保Timer0工作在普通模式(默认也是普通模式,但显式配置更清晰)。
内容的提问来源于stack exchange,提问作者Moh Naufal Hizbullah
相关产品推荐
相关产品推荐

