// Based on ap note AVR306 by ATMEL
// Routines for interrupt controlled USART
// Last modified: 26 October 2006
// Changed to work with WinAVR 20060421
// Changed : 3 Nov 2005
// changed rx buffer access, added checks for non-atomic interrupt pointer modification
// changed function name DataInReceiveBuffer to USART_DataRx
// modified: 10 APR 2005
// added double speed baud support;
// Modified by: Murray Horn
// this file includes the tx and rx routines for a single uart mega avr.
// seperate tx and rx ques are available and can be sized in the .inc file
// a string tx feature from rom has been added (uart_puts_p).
// Includes
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h> // used for the string printing feature
#include <uart001.h> // Prototypes
#define circular_t uint8_t
// Static Variables
static uint8_t USART_RxBuf[USART_RX_BUFS];
static volatile circular_t USART_RxHead;
static volatile circular_t USART_RxTail;
static uint8_t USART_TxBuf[USART_TX_BUFS];
static volatile circular_t USART_TxHead;
static volatile circular_t USART_TxTail;
//-----------------------------------------------------
// Initialize USART
void USART_Init( uint16_t baudrate,uint8_t setup)
{
//------------------------------
// the doublespeed selector
if (baudrate > 0x7ff)
{
baudrate += 1;
baudrate = baudrate >> 1;
baudrate -= 1;
UCSRA = 0;
}
else
UCSRA = (1<<U2X);
//------------------------------
// Set the baud rate
UBRRH = (uint8_t)(0x0f &(baudrate >> 8));
UBRRL = (uint8_t) baudrate;
// Enable UART receiver and transmmitter and receive int
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
// Set frame format: data bits, stop bits,parity etc
UCSRC = (1<<URSEL)| setup ; // set the uart bits,baudrate,stop bits
// Flush buffers
USART_RxTail = 0;
USART_RxHead = 0;
USART_TxTail = 0;
USART_TxHead = 0;
}
//-----------------------------------------------------
//-----------------------------------------------------
// RX Interrupt handler
// the only function that is allowed to change rx_head
SIGNAL(SIG_UART_RECV)
{
uint8_t data;
circular_t tmp_head,nxt_head,tmp_tail;
// Read the received data
data = UDR;
// Calculate buffer index
tmp_head = USART_RxHead;
nxt_head = ( tmp_head + 1 ) & USART_RX_BUFFER_MASK;
// non atomic protection
do
tmp_tail = USART_RxTail;
while (tmp_tail != USART_RxTail);
if ( nxt_head == tmp_tail)
{
// ERROR! Receive buffer overflow
}
else
{
USART_RxBuf[tmp_head] = data; // Store received data in buffer
USART_RxHead = nxt_head; // Store new index
}
}
//-----------------------------------------------------
//-----------------------------------------------------
// TX Interrupt handler
// the only function that is allowed to change tx_tail
SIGNAL(SIG_UART_DATA)
{
circular_t tmp_tail,tmp_head;
// non atomic protection
do
tmp_head = USART_TxHead;
while (tmp_head != USART_TxHead);
tmp_tail = USART_TxTail;
// Check if all data is transmitted
if ( tmp_head != tmp_tail )
{
// Calculate buffer index
UDR = USART_TxBuf[tmp_tail]; // Start transmition
tmp_tail = ( tmp_tail + 1 ) & USART_TX_BUFFER_MASK;
USART_TxTail = tmp_tail; // Store new index
}
else
{
UCSRB &= ~(1<<UDRIE); // Disable UDRE interrupt
}
}
//-----------------------------------------------------
//-----------------------------------------------------
// RX function
// the only function that is allowed to change rx_tail
uint8_t USART_Receive( void )
{
circular_t tmp_tail,tmp_head;
uint8_t b;
do
{
tmp_tail = USART_RxTail; //
// double get the head to ensure no interrupt based corruption of the pointer
do
tmp_head = USART_RxHead;
while (tmp_head != USART_RxHead); // non atomic protection
}
while (tmp_head == tmp_tail); // Wait for incomming data
b = USART_RxBuf[tmp_tail];
tmp_tail = ( tmp_tail + 1 ) & USART_RX_BUFFER_MASK; // Calculate buffer index
USART_RxTail = tmp_tail; // Store new index
return b; // Return data
}
//-----------------------------------------------------
//-----------------------------------------------------
// TX function
// the only function that is allowed to change USART_TxHead
void USART_Transmit( uint8_t data )
{
circular_t tmp_head,nxt_head,tmp_tail;
// Calculate buffer index
tmp_head = USART_TxHead;
nxt_head = ( tmp_head + 1 ) & USART_TX_BUFFER_MASK; // Next point in buffer
// wait for free space
do
{
// non atomic protection
do tmp_tail = USART_TxTail;
while (tmp_tail != USART_TxTail);
}
while (nxt_head == tmp_tail);
USART_TxBuf[tmp_head] = data; // Store data in buffer
USART_TxHead = nxt_head; // Store new index
UCSRB |= (1<<UDRIE); // Enable UDRE interrupt
}
//-----------------------------------------------------
//-----------------------------------------------------
uint8_t USART_DataRx( void )
{
uint8_t again;
circular_t tmp_tail,tmp_head;
// protection against interrupt interference (non atomic access)
do {
again = 0;
tmp_head = USART_RxHead;
tmp_tail = USART_RxTail;
if (tmp_head != USART_RxHead) again = 1;
if (tmp_tail != USART_RxTail) again = 1;
}
while (again);
if ( tmp_head == tmp_tail )
return(0);
else
return(1);
}
//-----------------------------------------------------
//-----------------------------------------------------
// write a string to the usart from the program memory
void uart_puts_p(const char *progmem_s )
{
register char c;
while ( (c = pgm_read_byte(progmem_s++)) )
USART_Transmit(c);
}
//-----------------------------------------------------