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

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

关键修改说明

  1. 添加初始化调用:复位入口直接跳转到init_norm,确保定时器配置和引脚方向初始化执行。
  2. 修正标志位清除方式:用sbi TIFR0, TOV0替代cbi,符合AVR的标志位清除规则。
  3. 重构Delay逻辑:先判断寄存器是否为0再递减,避免无符号溢出导致的死循环,同时在延迟结束后重新加载初始计数值,保证每次调用delay的时长一致。
  4. 移除冗余配置:将DDRB配置移到初始化代码中,主循环只控制引脚电平。
  5. 明确定时器模式:显式清零TCCR0A,确保Timer0工作在普通模式(默认也是普通模式,但显式配置更清晰)。

内容的提问来源于stack exchange,提问作者Moh Naufal Hizbullah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:31:27