Closed paulies-project-pages closed 2 months ago
/**** PROGRAM DESCRIPTION *** A hardware based interrupt driven UART recieve and transmit driver based on the ATMega328p microcontroller. Uses the ring buffer technique for both transmit and receive functions.
Default data transfer settings (Hard Coded): Processor: ATMega328p Baud Rate: 9600 bits per second (baud). Packet : 8 data, No parity, 1 Stop bit (8N1).
References:
ATMEL Application Note AVR306
****/
/**** DEFINITIONS **/
// Use Asynchronous Normal mode (U2Xn = 0) - table 24.1 of ATmega328 datasheet
// brief receive ringbuffer overflow
static unsigned char UART_RxBuf[UART_RX_BUFFER_SIZE]; static volatile unsigned char UART_RxHead; static volatile unsigned char UART_RxTail; static unsigned char UART_TxBuf[UART_TX_BUFFER_SIZE]; static volatile unsigned char UART_TxHead; static volatile unsigned char UART_TxTail; static volatile unsigned char UART_LastRxError; char received_byte;
void usart_config(void); unsigned char receive_byte(void); void transmit_byte(unsigned char data); unsigned char data_in_receive_buffer(void); void capture_byte();
void usart_config(void) { // Ensure global interrupts are cleared during USART initialisation cli();
// Define the Baud rate in the ATmega328 chip UBRR0H = UBBRn >> 8; //shift upper 8 bits into the UBBR0H register UBRR0L = UBBRn; //and lower 8 bits into the UBBR0L register.
// Set Frame format to 8 bits, No parity and 1 stop bit (8N1) UCSR0C = (1 << UCSZ01) | (1 <<UCSZ00);
// Enable UART transmit + receive and receive complete interrupt UCSR0B = (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0);
// Flush receive and transmit buffers
UART_RxHead = 0;
UART_RxTail = 0;
UART_TxHead = 0;
UART_TxTail = 0;
}
ISR (USART_RX_vect) { unsigned char data; unsigned char tmphead; unsigned char errorStatus; unsigned char lastRxError;
// Copy status/contents of buffer and error. data = UDR0;
// calculate buffer index tmphead = (UART_RxHead + 1) & UART_RX_BUFFER_MASK;
if(tmphead == UART_RxTail) { // ERROR! Receive buffer overfow lastRxError = UART_BUFFER_OVERFLOW >> 8; }
else { // store new index UART_RxHead = tmphead; // store received data in buffer UART_RxBuf[tmphead] = data; } }
ISR (USART_UDRE_vect) { unsigned char tmptail;
if(UART_TxHead != UART_TxTail) { // Calculate Buffer Index tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK; UART_TxTail = tmptail;
// Start Transmission
UDR0 = UART_TxBuf[tmptail];
}
else // Tx buffer is empty { // Disable UDRE interrupt UCSR0B &= ~(1 << UDRIE0); } }
unsigned char receive_byte(void) { unsigned char tmptail;
// Wait for incoming data while(UART_RxHead == UART_RxTail);
// Calculate buffer index tmptail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK; UART_RxTail = tmptail;
// Return data
return UART_RxBuf[tmptail];
}
void transmit_byte(unsigned char data) { unsigned char tmphead;
// Calculate buffer index tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;
// Wait for free space in buffer while(tmphead == UART_TxTail);
// Store data in buffer UART_TxBuf[tmphead] = data; UART_TxHead = tmphead;
// Enable UDRE interrupt
UCSR0B |= (1<<UDRIE0);
}
unsigned char data_in_receive_buffer(void) { // FALSE if the receive buffer is empty return (UART_RxHead != UART_RxTail); }
// Obtain a single byte of data from ATMega328p UART void capture_byte() { sei(); received_byte = receive_byte(); cli(); }// end function capture_byte()
Heavily based off the AVR application note 306, implements the USART functionality of the ATMega328p using interrupt handler routines.