cmicrocontrolleravratmega

Why does this AVR code for ATMega128 behave the way it does?


I have the following code for ATMega128:

Some libraries and functions are not used as I haven't completed the code completely because I am stuck at this problem for days now.

#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();
}

When simulate this code on Proteus, I see that the whole main function is in an infinite loop as the virtual terminal continuously repeats the ConfigureWatchdogTimer() function. However, the behavior that I expect is, configuration functions only run once, the CPU goes to sleep mode and only wakes up when an interrupt happens, which for this code is receiving a byte on usart0. What am I missing here or what is it that I am not understanding? The interrupt that I wrote somewhat works as if I constantly spam a character, that character does show up in the other terminal every once in a while.

Behavior that I get on Proteus when I run this code on ATMega128 Behavior that I get on Proteus when I run this code on ATMega128


Solution

  • Occasional code restart is a usual sign of some missed interrupt handler.

    For example, you have receive (RXCIEn) and transmission (TXCIEn) interrupts enabled for both USARTs in ConfigureUSARTInterfaces:

        UCSR0B = (1<<RXEN0) | (1<<RXCIE0) | (1<<TXEN0) | (1<<TXCIE0); 
        UCSR1B = (1<<RXEN1) | (1<<RXCIE1) | (1<<TXEN1) | (1<<TXCIE1);
    

    but have only handler for USART0_RX_vect.

    Either define ISRs for other USARTs interrupts, or disable (do not set) corresponding TXCIE/RXCIE-bits in USART configurations