Search code examples
arduinoavratmegausart

AVR USART transmitting only 2 chars


Below is a code ran on an ATmega328P. It's supposed to send "abcdef" to my computer every second. However, it sent me only "ab" every second. What is wrong here?

#include <avr/io.h>
#include <util/delay.h>

void USART_transmit(unsigned char data);
void print(const unsigned char *buffer, size_t n);
void print(const char* str);

int main(void) {
    // Serial.begin(115200)
    UCSR0B |= (1<<TXEN0);
    UBRR0L = 8;

    while(1){
        // Serial.write(i)
        print("abcdef");

        _delay_ms(1000);
    }
}

void USART_transmit(const uint8_t data) {
    /* wait for empty transmit buffer */
    while (!UCSR0A & (1<<UDRE0));
    UDR0 = data;
}

void print(const uint8_t *buffer, size_t n) {
    while(n--){
        USART_transmit(*buffer++); //**
    }
}

void print(const char *str) {
    if(strlen(str) != 0) print((const uint8_t *) str, strlen(str));
}

The code resulted in:

ababababababababababab...

Changing from USART_transmit(*buffer++); to USART_transmit(n + 48); (+48 to convert to char) resulted in:

5454545454545454545454545454...

So I guess the loop shouldn't be stopping?


Solution

  • The "data register empty" check is wrong.

    while (!UCSR0A & (1<<UDRE0));
    

    should be

    while (!(UCSR0A & (1 << UDRE0)));
    

    In your case, the check is not blocking until the buffer is empty. I think one byte is buffered in the USART output buffer and one byte is pending in UDR. Every additional byte is then discarded, this is why you see only "ab".