Just today I happened to be in the exact same position, and I wrote a program along the lines of what Michael Karas suggests in his answer, using a circular buffer. I used a PIC18, so some code may not compile, but it shows the idea clearly and it should be easy to port this code to AVR, ...
I declared some global variables:
#define EUSART_BUFFER_SIZE 2048
char eusart_rx_buffer[EUSART_BUFFER_SIZE]; // the actual buffer, now 2048 bytes long
uint16_t eusart_rx_buffer_rd = 0; // the current read position
uint16_t eusart_rx_buffer_wr = 0; // the current write position
This assumes stdint.h
is included for the uint16_t
type.
The idea is:
- In the ISR, when we receive a byte, store it at
eusart_rx_buffer[eusart_rx_buffer_wr]
and increment the write position.
- When we want to read the data, you can read from
eusart_rx_buffer_rd
until eusart_rx_buffer_wr
.
Of course, when more than 2048 bytes are stored at the same time, the buffer will be overwritten and you'll lose data. There are some tricks you can use to work around that though. You can change EUSART_BUFFER_SIZE
to suit your needs. A lower value, of course, takes less data memory.
Now, in my ISR, I have:
if (PIR1bits.RCIF) { // EUSART data received
eusart_rx_buffer[eusart_rx_buffer_wr++] = RCREG; // Store the received data
if (eusart_rx_buffer_wr >= EUSART_BUFFER_SIZE) // Increment write pointer
eusart_rx_buffer_wr = 0;
PIR1bits.RCIF = 0; // Clear interrupt flag
}
Of course, on an AVR this code will look slightly different, but the idea is the same.
Then, where you want to read the data, you can do something like:
while (eusart_rx_buffer_rd != eusart_rx_buffer_wr) { // While there's data in the buffer
do_sth(eusart_rx_buffer[eusart_rx_buffer_rd++]); // Do something with it
if (eusart_rx_buffer_rd >= EUSART_BUFFER_SIZE) // Increase read pointer
eusart_rx_buffer_rd = 0;
}
This code was written for PIC18 using the XC8 compiler, but most of it is standard C and can be copied directly or ported easily.