2

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;
}
7
  • What happens on this platform if you return from main()? Does it just run your code again? Most embedded systems do not return from main. They would have some kind of infinite loop. Commented Mar 25, 2024 at 12:10
  • @pmacfarlane It will indeed jump back to the start of main(). I do not see why the two examples would produce a different result. But IIRC, the mega32 has a 2-Byte UART FIFO. So I would expect above code to write two bytes into the FIFO, wait until one byte has been shifted out, write the third byte and the return to main(). However, since there is no loop, main will re-initialize the UART before all bytes are shifted out. So seeing "H H H..." being printed makes sense. Commented Mar 25, 2024 at 15:20
  • @pmacfarlane and @Rev, I even tried blocking return in main using infinite loop. But, still the output is "H H H..." void main() { USART_init(UBRR); print_serial("HE"); while(1); return; } Commented Mar 26, 2024 at 5:35
  • Not sure, if an empty while loop may be optimized out. Try putting asm volatile ("nop"); inside the while-loop. Commented Mar 26, 2024 at 7:51
  • I tried with nop still same issue. Commented Mar 26, 2024 at 8:07

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.