367 lines
8.8 KiB
C
367 lines
8.8 KiB
C
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* INCLUDES
|
|
***************************************************************************/
|
|
|
|
#include <avr/interrupt.h>
|
|
#include <avr/io.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <math.h>
|
|
|
|
#include "usart.h"
|
|
|
|
/***************************************************************************
|
|
* DEFINITIONS
|
|
***************************************************************************/
|
|
|
|
/* check for receive/transmit buffer size */
|
|
#if (USART_RBUF_SIZE >= 0xFF | USART_TBUF_SIZE >= 0xFF)
|
|
#error "USART: buffers too big (max = 256 bytes)"
|
|
#endif
|
|
|
|
#if (USART_RBUF_SIZE & USART_RMASK)
|
|
#error "USART: RX buffer size is not power of 2"
|
|
#endif
|
|
|
|
#if (USART_TBUF_SIZE & USART_TMASK)
|
|
#error "USART: TX buffer size is not power of 2"
|
|
#endif
|
|
|
|
/* transmission buffers */
|
|
static volatile BYTE rx_buff[USART_RBUF_SIZE];
|
|
static volatile BYTE tx_buff[USART_TBUF_SIZE];
|
|
|
|
/* buffer indexes */
|
|
static volatile UINT8 rx_head, rx_tail;
|
|
static volatile UINT8 tx_head, tx_tail;
|
|
|
|
/* usart status */
|
|
static volatile e_usartstatus_t usartstatus;
|
|
|
|
#ifdef USART_USE_TIMER
|
|
/* timeout counter for read timeout */
|
|
volatile UINT16 usart_timeout = 0;
|
|
#endif /* USART_USE_TIMER */
|
|
|
|
/* incoming byte interrupt handler */
|
|
ISR (USART_INT_RX)
|
|
{
|
|
#ifdef USART_USE_STATUS
|
|
/* set status of received byte */
|
|
if (USART_UCSRA & BV(FE))
|
|
sbi(usartstatus, RX_STAT_FE);
|
|
if (USART_UCSRA & BV(DOR))
|
|
sbi(usartstatus, RX_STAT_DOR);
|
|
/* Atmegas have different name of this flag */
|
|
#ifdef UPE
|
|
if (USART_UCSRA & BV(UPE))
|
|
sbi(usartstatus, RX_STAT_PE);
|
|
#else
|
|
if (USART_UCSRA & BV(PE))
|
|
sbi(usartstatus, RX_STAT_PE);
|
|
#endif /* UPE */
|
|
#endif /* USART_USE_STATUS */
|
|
|
|
/* discard last byte in buffer if buffer overflow occured */
|
|
if (usartstatus & RX_OVR)
|
|
rx_tail = (rx_tail + 1) & USART_RMASK;
|
|
|
|
/* insert byte to ring buffer */
|
|
rx_buff[rx_head] = USART_UDR;
|
|
rx_head = (rx_head + 1) & USART_RMASK;
|
|
|
|
/* indicate buffer overflow */
|
|
if (rx_head == rx_tail)
|
|
sbi(usartstatus, RX_STAT_OVR);
|
|
}
|
|
|
|
/* outgoing byte interrupt handler */
|
|
ISR (USART_INT_UDRE)
|
|
{
|
|
if ((tx_tail != tx_head) || (usartstatus & TX_FULL))
|
|
{
|
|
USART_UDR = tx_buff[tx_tail];
|
|
tx_tail = (tx_tail + 1) & USART_TMASK;
|
|
|
|
cbi(usartstatus, TX_STAT_FULL);
|
|
}
|
|
else
|
|
cbi(USART_UCSRB, USART_UDRIE);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
BOOL usartReceiveBufferOverflow()
|
|
{
|
|
return (TO_BOOL((usartstatus & RX_OVR)));
|
|
}
|
|
|
|
#ifndef USART_USE_TIMER
|
|
/* ----------------------------------------------------------------------- */
|
|
BOOL usartRead(BYTE *str, const UINT16 count)
|
|
{
|
|
#ifdef USART_USE_STATUS
|
|
static BOOL b_byte_read_ok;
|
|
#endif /* USART_USE_STATUS */
|
|
|
|
for (UINT16 i = 0; i < count; ++i)
|
|
{
|
|
/* if any timeout is present, wait for incomming byte */
|
|
while (!usartUnreadBytes());
|
|
|
|
#ifdef USART_USE_STATUS
|
|
*str = usartReadByte(&b_byte_read_ok);
|
|
#endif /* USART_USE_STATUS */
|
|
++str;
|
|
|
|
#ifdef USART_USE_STATUS
|
|
if (!b_byte_read_ok)
|
|
return (false);
|
|
#endif /* USART_USE_STATUS */
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
#else
|
|
/* ----------------------------------------------------------------------- */
|
|
BOOL usartRead(BYTE *str, const UINT16 count, const UINT16 timeout)
|
|
{
|
|
#ifdef USART_USE_STATUS
|
|
static BOOL b_byte_read_ok;
|
|
#endif /* USART_USE_STATUS */
|
|
|
|
for (UINT16 i = 0; i < count; ++i)
|
|
{
|
|
usart_timeout = 0;
|
|
|
|
/* if any timeout is present, wait for incomming byte */
|
|
while (!usartUnreadBytes() && timeout)
|
|
{
|
|
if (usart_timeout == timeout)
|
|
return (false);
|
|
}
|
|
|
|
#ifdef USART_USE_STATUS
|
|
*str = usartReadByte(&b_byte_read_ok);
|
|
#endif /* USART_USE_STATUS */
|
|
++str;
|
|
#ifdef USART_USE_STATUS
|
|
|
|
if (!b_byte_read_ok)
|
|
return (false);
|
|
#endif /* USART_USE_STATUS */
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
#endif /* USART_USE_TIMER */
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
#ifdef USART_USE_STATUS
|
|
BYTE usartReadByte(BOOL *ok)
|
|
#else
|
|
BYTE usartReadByte()
|
|
#endif /* USART_USE_STATUS */
|
|
{
|
|
static BYTE b;
|
|
|
|
if (usartUnreadBytes())
|
|
{
|
|
b = rx_buff[rx_tail];
|
|
rx_tail = (rx_tail + 1) & USART_RMASK;
|
|
cbi(usartstatus, RX_STAT_OVR);
|
|
|
|
#ifdef USART_USE_STATUS
|
|
if (ok)
|
|
{
|
|
if (usartstatus & (RX_FE | RX_PE | RX_DOR))
|
|
{
|
|
*ok = false;
|
|
|
|
/* clear error flags */
|
|
cbi(usartstatus, RX_STAT_FE);
|
|
cbi(usartstatus, RX_STAT_PE);
|
|
cbi(usartstatus, RX_STAT_DOR);
|
|
}
|
|
else
|
|
*ok = true;
|
|
}
|
|
|
|
return (b);
|
|
}
|
|
else
|
|
{
|
|
if (ok)
|
|
*ok = false;
|
|
|
|
return (0);
|
|
}
|
|
#else
|
|
}
|
|
return (b);
|
|
#endif /* USART_USE_STATUS */
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
UINT8 usartUnreadBytes()
|
|
{
|
|
if (usartstatus & RX_OVR)
|
|
return (USART_RBUF_SIZE);
|
|
if (rx_head > rx_tail)
|
|
return (rx_head - rx_tail);
|
|
if (rx_head < rx_tail)
|
|
return (USART_RBUF_SIZE - (rx_tail - rx_head));
|
|
return (0);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void initUsart(const UINT16 baudrate, const BOOL double_speed, const BOOL two_stop_bits, const UINT8 parity)
|
|
{
|
|
UINT16 usart_const;
|
|
UINT8 ucsrc;
|
|
|
|
float f_usart_const;
|
|
|
|
rx_head = 0;
|
|
rx_tail = 0;
|
|
tx_head = 0;
|
|
tx_tail = 0;
|
|
usartstatus = 0;
|
|
|
|
/* set speed */
|
|
if (double_speed)
|
|
{
|
|
/* compute USART clock and round to nearest value */
|
|
f_usart_const = (float)(USART_F_CPU) / (float)(8);
|
|
f_usart_const /= (float)(baudrate);
|
|
f_usart_const -= 1;
|
|
|
|
usart_const = (UINT16)(lrint(f_usart_const));
|
|
USART_UCSRA = BV(UDRE) | BV(U2X);
|
|
}
|
|
else
|
|
{
|
|
/* compute USART clock and round to nearest value */
|
|
f_usart_const = (float)(USART_F_CPU) / (float)(16);
|
|
f_usart_const /= (float)(baudrate);
|
|
f_usart_const -= 1;
|
|
|
|
usart_const = (UINT16)(lrint(f_usart_const));
|
|
USART_UCSRA = BV(UDRE);
|
|
}
|
|
USART_UBRRH = (UINT8)(usart_const >> 8);
|
|
USART_UBRRL = (UINT8)usart_const;
|
|
|
|
/* set rx and tx enabled, allow interrupt on new byte received */
|
|
USART_UCSRB = BV(RXEN) | BV(TXEN) | BV(RXCIE);
|
|
|
|
/* set frame format: asynchronous, 8bit data */
|
|
ucsrc = BV(UCSZ0) | BV(UCSZ1);
|
|
|
|
/* set frame format: 2 stop bits */
|
|
if (two_stop_bits)
|
|
ucsrc |= BV(USBS);
|
|
|
|
/* set frame format: parity */
|
|
if (parity == USART_PARITY_EVEN)
|
|
ucsrc |= BV(UPM1);
|
|
else if (parity == USART_PARITY_ODD)
|
|
ucsrc |= BV(UPM0) | BV(UPM1);
|
|
|
|
/* URSEL for access to UCSRC insetad of UBRRH */
|
|
#ifdef URSEL
|
|
ucsrc |= BV(URSEL);
|
|
#endif /* URSEL */
|
|
|
|
USART_UCSRC = ucsrc;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void usartFlush()
|
|
{
|
|
rx_tail = rx_head;
|
|
cbi(usartstatus, RX_STAT_OVR);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void usartSend(BYTE *str, const UINT16 length)
|
|
{
|
|
static UINT16 real_length;
|
|
|
|
/* compute max length of data to send */
|
|
if (!length)
|
|
real_length = USART_SEND_MAX_LENGTH;
|
|
else
|
|
real_length = (length > USART_SEND_MAX_LENGTH)?(USART_SEND_MAX_LENGTH):(length);
|
|
|
|
/* start sending data */
|
|
for (UINT16 i = 0; i < real_length; ++i)
|
|
{
|
|
usartSendByte(*str);
|
|
++str;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void usartSendByte(const BYTE b)
|
|
{
|
|
/* wait for free space in buffer */
|
|
while (usartstatus & TX_FULL);
|
|
|
|
/* insert byte to transmit buffer */
|
|
tx_buff[tx_head] = b;
|
|
tx_head = (tx_head + 1) & USART_TMASK;
|
|
|
|
if (tx_head == tx_tail)
|
|
sbi(usartstatus, TX_STAT_FULL);
|
|
|
|
sbi(USART_UCSRB, USART_UDRIE);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void usartSendString(BYTE *str, const UINT16 length)
|
|
{
|
|
static UINT16 real_length;
|
|
|
|
/* compute max length of data to send */
|
|
if (!length)
|
|
real_length = USART_SEND_MAX_LENGTH;
|
|
else
|
|
real_length = (length > USART_SEND_MAX_LENGTH)?(USART_SEND_MAX_LENGTH):(length);
|
|
|
|
/* start sending data */
|
|
for (UINT16 i = 0; (i < real_length) && *str; ++i)
|
|
{
|
|
usartSendByte(*str);
|
|
++str;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
void usartSendString_P(const BYTE *str)
|
|
{
|
|
BYTE c;
|
|
|
|
while ((c = pgm_read_byte(str)))
|
|
{
|
|
usartSendByte(c);
|
|
++str;
|
|
}
|
|
}
|
|
|
|
|
|
/* END */
|