hondartp-1.2.0/src/usart.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 */