简体   繁体   中英

AVR - High speed interrupt driven UART code not working

I want to make a interrupt driven uart program, to send large amounts of data at high speeds with the absolute minimal amount of cpu overhead. I combined existing code and reading of the datasheet to make this code. It compiles without errors or warnings in Atmel Studio 7 on an atmega328p (Atmega328p Xplained Mini).

The problem that I'm having is that data is erratic, sometimes it sends 'ello!' sometimes nothing for a while. The 'H' is often skipped, I don't understand this since the ISR shouldn't execute before the 'H' has been copied from UDR0 to be sent.

Any help would be greatly appreciated!

Greetings,

Bert.

#define F_CPU 16000000

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

volatile uint8_t transmit_index = 0;
volatile char str[] = "Hello!\n";
volatile uint8_t len = 6;

int main(void){
    UCSR0A = 0b00000010;
    UCSR0B = 0b00111000;
    UCSR0C = 0b00000110;

//9600 baud
    UBRR0L = 207; 
    UBRR0H = 0;

    DDRD |= 0x02;

    sei();

    //Flash led
    DDRB |= 0b00100000;
    PORTB |= 0b00100000;
    _delay_ms(1000);
    PORTB &= ~0b00100000;
    _delay_ms(1000);

    while (1){  
        transmit_index = 1;

        //Enable udre interrupt
        UCSR0B |= 0b00100000; //enable interrupt

        //Send first byte in main()
        while (!(UCSR0A & 0b00100000)) {} //Wait for register empty
        UDR0 = str[0]; //send first byte

        _delay_ms(1000);
    }
}

ISR(USART_UDRE_vect) {
    //Buffer empty, ready for new data
    if (transmit_index < (len + 1)) {
        UDR0 = str[transmit_index];
        transmit_index++;
    } else {
        UCSR0B &= ~0b00100000; //disable interrupt
    }
}

per the datasheet:

"When the Data Register Empty Interrupt Enable (UDRIE) bit in UCSRnB is written to '1', the USART data register empty interrupt will be executed as long as UDRE is set"

As soon as you enable the interrupt, the ISR is triggered, thus skipping the "H". You have a couple of options. 1) Enable the interrupt after you send the H. 2) Just use the ISR to send the entire message, including the H (eg don't send anything in the main routine. 3) Use the Tramsmit Complete ((TXC) interrupt. If you use this, send the H in the main routine, and once it is transferred, the ISR will trigger and your ISR will send the rest of the message.

Lastly, change "transmit_index < (len + 1)" to transmit_index <= len. There is no need to waste instructions inside an ISR

In your main loop, change this line :

transmit_index = 1;

for this line :

transmit_index = 0;

str[0] = 'H' but you start at index [1]...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM