Search code examples
carduinoavr-gccatmega32

ATmega328P USART Transmit character printed repeatedly


I am encountering an issue with USART communication on an ATmega328P microcontroller and would appreciate some assistance with debugging.

The problem I'm facing is as follows: I have implemented USART communication on an ATmega328P microcontroller to transmit the string "HE" over serial. However, when using the following code snippet, the output on the serial console repeats "H H H H H ..." endlessly:


#define FOSC 16000000UL
#define BAUD 9600
#define UBRR FOSC/16/BAUD-1

void USART_init(unsigned int ubrr)
{
    // Setting Baud rate
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;

    //Enable receiver and transmitter
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);

    //Set frame format: 8data, 2stop bits
    UCSR0C = (1 << USBS0) | (1 << UCSZ00) | (1 << UCSZ01);

    return;
}

void USART_Transmit(unsigned char data)
{
    while(!(UCSR0A & (1  << UDRE0)));

    UDR0 = data;

    return;
}

void print_serial(char* data)
{ 
    USART_Transmit(data[0]);
    USART_Transmit(data[1]);
    USART_Transmit(data[2]);

    return;
}

int main()
{
    USART_init(UBRR);
    
    print_serial("HE");

    return 0;
}

However, when using the following modified code snippet, the output is as expected, printing "HE" only once:


#define FOSC 16000000UL
#define BAUD 9600
#define UBRR FOSC/16/BAUD-1

void USART_init(unsigned int ubrr)
{
    // Setting Baud rate
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;

    //Enable receiver and transmitter
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);

    //Set frame format: 8data, 2stop bits
    UCSR0C = (1 << USBS0) | (1 << UCSZ00) | (1 << UCSZ01);

    return;
}

void USART_Transmit(unsigned char data)
{
    while(!(UCSR0A & (1  << UDRE0)));

    UDR0 = data;

    return;
}

void print_serial()
{
    char *data = "HE"; 
    USART_Transmit(data[0]);
    USART_Transmit(data[1]);
    USART_Transmit(data[2]);

    return;
}

int main()
{
    USART_init(UBRR);
    
    print_serial();

    return 0;
}

It seems that the issue arises when passing the string "HE" as an argument to the print_serial function. However, when defining the string within the function itself, the output is correct.

Can anyone please help me identify the root cause of this issue and suggest any potential fixes?

Thank you in advance for your assistance.

Observation: It seems there might be an issue related to the string's scope. Allocating memory for the string allows the correct output "HE" to be printed on the serial console.

{
    USART_init(UBRR);
    
    char *data = (char*)malloc(3);
    data[0] = 'H';
    data[1] = 'E';
    data[2] = '\0';

    print_serial(data);

    free(data);

    return;
}

Solution

  • I discovered that the issue was not in the code itself, but rather in the build steps that I blindly followed from this youtube video

    Here were the build commands from the video:

    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c main.c -o main.o
    avr-gcc -o main.o main.bin
    avr-objcopy -O ihex -R .eeprom main.bin main.hex
    

    Upon closer examination, I realized that in the second command avr-gcc -o main.o main.bin, I needed to specify the target frequency and controller (-DF_CPU=16000000UL -mmcu=atmega328p). However, this second command is unnecessary if the -c flag is removed from the first command since there are no multiple c files or libraries. This issue became evident when I carefully inspected the data memory using SimulIDE, revealing that the string characters were being written to general-purpose and GPIO registers, resulting in unexpected behavior.

    After identifying the problem, I made the following modifications to the commands:

    avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p main.c -o main.bin
    avr-objcopy -O ihex -R .eeprom main.bin main.hex
    

    With these adjustments, everything is now functioning as expected. Thank you all for your time.