#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/cpufunc.h>
#include <util/delay.h>
#define UART_RXBUF_SZ 32 //receive and transmit buffer sizes
#define UART_TXBUF_SZ 32
#define UART_RXBUF_MASK (UART_RXBUF_SZ - 1) //buffer index bitmasks for
#define UART_TXBUF_MASK (UART_TXBUF_SZ - 1) // cheap modulo operation
static uint8_t rxbuf[UART_RXBUF_SZ]; //receive ring buffer
static uint8_t txbuf[UART_TXBUF_SZ]; //transmit ring buffer
volatile uint8_t rx_count = 0; //number of bytes in receive buffer
volatile uint8_t tx_count = 0; //number of bytes in transmit buffer
ISR(USART_UDRE_vect) //data register empty - can add next char to be transmitted
{
static uint8_t tx_cons_ind; //uart_recv() gets next byte(s) from this index
if (tx_count) { //more data to be transmitted
UDR = txbuf[tx_cons_ind++ & UART_TXBUF_MASK];
tx_count--;
} else
UCSRB &= ~(1<<UDRIE); //no more data - disable this interrupt
}
ISR(USART_RXC_vect) //incoming data, put into receive buffer
{
static uint8_t rx_prod_ind; //receiver ISR stores next byte at this index
rxbuf[rx_prod_ind++ & UART_RXBUF_MASK] = UDR;
if (++rx_count > UART_RXBUF_SZ)
rx_count = UART_RXBUF_SZ; //overflow, data has been overwritten
}
uint8_t //read at most `num` bytes from recv buffer into `data`, return num read
uart_recv(uint8_t *data, uint8_t num)
{
static uint8_t rx_cons_ind; //transmitter ISR transmits byte at this index next
uint8_t numgot = 0; //count how many bytes we got so far
uint8_t rxcnt = rx_count; //cache volatile
while(rxcnt > 0 && numgot < num) {
data[numgot++] = rxbuf[rx_cons_ind++ & UART_RXBUF_MASK];
rxcnt--;
}
cli();
rx_count -= numgot;//rx_count = rxcnt would be wrong
sei();
return numgot;
}
uint8_t //buffer up to `num` bytes for transmit, return num. actually buffered
uart_send(uint8_t *data, uint8_t num)
{
static uint8_t tx_prod_ind; //uart_send() queues next byte(s) at this index
uint8_t numput = 0; //count how many bytes we processed so far
uint8_t txcnt = tx_count; //cache volatile
while(num-- && txcnt < UART_TXBUF_SZ) {
txbuf[tx_prod_ind++ & UART_TXBUF_MASK] = *data++;
numput++;
txcnt++;
}
cli();
tx_count += numput;
sei();
UCSRB |= (1<<UDRIE); //enable interrupt so that transmission starts
return numput;
}
int
main(void)
{
uint16_t ubrr = 25; //38400 baud @ 16MHz clock
UBRRH = (uint8_t)(ubrr>>8); //initialize uart
UBRRL = (uint8_t)ubrr;
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // frame format: 8N1
DDRC = (1<<DDC7); // a debug led
for(int x = 0; x < 3; x++) { //flash the led to indicate startup
PORTC ^= (1<<PORTC7); _delay_ms(500);
PORTC ^= (1<<PORTC7); _delay_ms(100);
}
UCSRB = (1<<TXEN)|(1<<UDRIE)|(1<<RXEN)|(1<<RXCIE); // usart enable
sei();
uint8_t buf[32];
for(;;) { //now just read...
uint8_t n = uart_recv(buf, sizeof buf);
if (n) //...and echo back
uart_send(buf, n);
}
}