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

ATMega128的AVR代码在Proteus中异常循环问题排查

ATmega128代码仿真问题排查

我有一段用于ATmega128的代码,部分库和函数还没用到(代码尚未完成)——这个问题已经卡了我好几天。

在Proteus中仿真这段代码时,发现main函数陷入无限循环,虚拟终端持续重复执行ConfigureWatchdogTimer()函数。但我预期的行为是:配置函数仅运行一次,CPU进入睡眠模式,仅在USART0接收字节触发中断时唤醒。另外,编写的中断仅部分生效:持续发送字符时,字符偶尔才会出现在另一个终端中。

Proteus运行表现:虚拟终端持续重复显示看门狗配置菜单


代码片段

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define F_CPU       8000000UL
#define BAUDRATE    9600
#define UBRR_VALUE  ((F_CPU)/(16UL*BAUDRATE))-1 //For UBRR registers

#define MASTER_WATCHDOG_MENU    "\rEnter MS WD Choice (& period):\rA-30ms\rB-250ms\rC-500ms\rYour input:\0"
#define SLAVE_WATCHDOG_MENU     "\rEnter SLV WD Choice (& period):\rA-0.5s\rB-1.0s\rC-2.0s\rYour input:\0"
#define USER_MENU               "\rEnter choice (and period): 1-Mem Dump 2-Last Entry 3-Restart\rYour input:\0"

void ConfigureXMEMMode(void){
    MCUCR |= (1 << SRE); 
    XMCRA = 0;           
    XMCRB = (1 << XMM2) | (0 << XMM1) | (1 << XMM0);
}

void ConfigureUSARTInterfaces(void){

    UBRR0H = (unsigned char)(UBRR_VALUE >> 8);
    UBRR0L = (unsigned char)(UBRR_VALUE);
    
    UBRR1H = (unsigned char)(UBRR_VALUE >> 8);
    UBRR1L = (unsigned char)(UBRR_VALUE);
    
    UCSR0B = (1<<RXEN0) | (1<<RXCIE0) | (1<<TXEN0) | (1<<TXCIE0); 
    UCSR1B = (1<<RXEN1) | (1<<RXCIE1) | (1<<TXEN1) | (1<<TXCIE1);
    
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
    UCSR1C = (1 << UCSZ01) | (1 << UCSZ00);
}

void USART0_TransmitByte(unsigned char byteToTransmit){
    while(!(UCSR0A & (1<<UDRE0))); 
    UDR0 = byteToTransmit;
}

void USART0_TransmitString(unsigned char *stringToTransmit){
    unsigned char i = 0;
    for(i = 0; stringToTransmit[i] != '\0'; i++){
        USART0_TransmitByte(stringToTransmit[i]);
    }
}

void USART1_TransmitByte(unsigned char byteToTransmit){
    while(!(UCSR1A & (1<<UDRE1)));
    UDR1 = byteToTransmit;
}

void USART1_TransmitString(unsigned char *stringToTransmit){
    unsigned char i = 0;
    for(i = 0; stringToTransmit[i] != '\0'; i++){
        USART1_TransmitByte(stringToTransmit[i]);
    }
}

unsigned char USART0_ReceiveByte(void){
    while(!(UCSR0A & (1<<RXC0))); 
    return UDR0; 
}

unsigned char USART1_ReceiveByte(void){
    while(!(UCSR1A & (1<<RXC1)));
    return UDR1;
}

void ConfigureWatchdogTimer(void){
    while(EECR & (1 << EEWE)); 
    EEARH = 0x00; 
    EEARL = 0x00; 
    EECR  = 1 << EERE; 

    if(EEDR == 0xFF){
        EEARL = 0x01;
        if(EEDR == 0xFF){
            EECR = 0 << EERE;
            ConfigureMasterWD();
        }
    }
    
    while(EECR & (1 << EEWE));
    EEARH = 0x00;
    EEARL = 0x02;
    EECR  = 1 << EERE;
    
    if(EEDR == 0xFF){
        EEARL = 0x03;
        if (EEDR == 0xFF){
            EECR = 0 << EERE;
            ConfigureSlaveWD();
        }
    }
}

void ConfigureMasterWD(){
    USART0_TransmitString(MASTER_WATCHDOG_MENU);
    
    unsigned char userInput = USART0_ReceiveByte();
    switch(userInput){
        case 'A': case 'a':
            WDTCR = (1 << WDE) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0); //30ms
            break;
        case 'B': case 'b':
            WDTCR = (1 << WDE) | (1 << WDP2) | (0 << WDP1) | (0 << WDP0); //250ms
            break;
        case 'C': case 'c':
            WDTCR = (1 << WDE) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0); //500ms
            break;
    }
    USART0_TransmitString("\rDone!\r");
}

void ConfigureSlaveWD(){
    USART0_TransmitString(SLAVE_WATCHDOG_MENU);
    
    unsigned char userInput = USART0_ReceiveByte();
    switch(userInput){
        case 'A': case 'a':
            WDTCR = (1 << WDE) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0); //500ms
            break;
        case 'B': case 'b':
            WDTCR = (1 << WDE) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0); //1.0s
            break;
        case 'C': case 'c':
            WDTCR = (1 << WDE) | (1 << WDP2) | (1 << WDP1) | (1 << WDP0); //2.0s
            break;
    }
    USART0_TransmitString("\rDone!\r");
}


void Sleep(){
    sei();
    set_sleep_mode(SLEEP_MODE_IDLE);
    sleep_enable();
    sleep_cpu();
}

void WakeUp(){
    sleep_disable();
    cli();
}

int main(void){
    ConfigureXMEMMode();
    ConfigureUSARTInterfaces();
    ConfigureWatchdogTimer();

    USART0_TransmitString("ATMEGA128 TO USART0");
    USART1_TransmitString("ATMEGA128 TO USART1");
    
    Sleep();
    while(1);
}

ISR(USART0_RX_vect){
    WakeUp();
    new_user_read_char = UDR0;
    USART1_TransmitByte(UDR0);
    Sleep();
}

问题根源与修复方案

1. 看门狗触发MCU复位,导致main重复执行

你在ConfigureMasterWD和ConfigureSlaveWD中设置了WDE位(看门狗复位使能),但没有添加喂狗逻辑。看门狗超时后会直接触发MCU复位,导致main函数从头执行,因此ConfigureWatchdogTimer会反复运行。

  • 修复:
    • 如果需要看门狗用于睡眠唤醒而非复位,将WDE替换为WDIE(看门狗中断使能):
      // 示例:配置30ms看门狗中断(无复位)
      WDTCR = (1<<WDIE) | (0<<WDP2)|(0<<WDP1)|(1<<WDP0);
      
    • 如果必须保留看门狗复位功能,需在主循环中定期喂狗:
      // 喂狗操作
      WDTCR |= (1<<WDCE)|(1<<WDE);
      WDTCR = (1<<WDE) | 你的分频配置;
      

2. EEPROM读取逻辑错误

在ConfigureWatchdogTimer中,切换EEPROM地址后未重新触发读操作,导致EEDR始终是上一个地址的数据:

  • 修复:每次切换地址后,重新设置EECR |= (1<<EERE)触发读取:
    void ConfigureWatchdogTimer(void){
        while(EECR & (1 << EEWE)); 
        EEARH = 0x00; 
        EEARL = 0x00; 
        EECR |= (1 << EERE); // 触发读
        if(EEDR == 0xFF){
            EEARL = 0x01;
            EECR |= (1 << EERE); // 重新触发读地址0x01
            if(EEDR == 0xFF){
                ConfigureMasterWD();
            }
        }
        
        while(EECR & (1 << EEWE));
        EEARH = 0x00;
        EEARL = 0x02;
        EECR |= (1 << EERE); // 触发读
        if(EEDR == 0xFF){
            EEARL = 0x03;
            EECR |= (1 << EERE); // 重新触发读地址0x03
            if (EEDR == 0xFF){
                ConfigureSlaveWD();
            }
        }
    }
    

3. 中断服务程序(ISR)异常

  • new_user_read_char未声明,会导致编译错误或运行异常;

  • 重复读取UDR0,第一次读取后数据已被清空,第二次读取的是无效值;

  • ISR中调用Sleep()的逻辑不合理,容易导致中断嵌套或睡眠状态混乱。

  • 修复:

    1. 全局声明unsigned char new_user_read_char;
    2. 修改ISR逻辑,避免重复读取,并将睡眠逻辑放回主循环:
      ISR(USART0_RX_vect){
          new_user_read_char = UDR0; // 仅读取一次
          USART1_TransmitByte(new_user_read_char);
      }
      
    3. 调整main和Sleep函数:
      void Sleep(){
          set_sleep_mode(SLEEP_MODE_IDLE);
          sleep_enable();
          sleep_cpu();
          sleep_disable(); // 唤醒后自动退出睡眠
      }
      
      int main(void){
          ConfigureXMEMMode();
          ConfigureUSARTInterfaces();
          ConfigureWatchdogTimer();
      
          USART0_TransmitString("ATMEGA128 TO USART0");
          USART1_TransmitString("ATMEGA128 TO USART1");
          
          sei(); // 全局开中断
          while(1){
              Sleep(); // 每次唤醒后重新进入睡眠
          }
      }
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:29:59