146 lines
3.1 KiB
C
146 lines
3.1 KiB
C
#include "serial.h"
|
|
|
|
#include "ringbuffer.h"
|
|
#include "arduino.h"
|
|
|
|
#define BUFSIZE 64 + sizeof(ringbuffer)
|
|
#define BAUD 115200
|
|
|
|
#define ASCII_XOFF 19
|
|
#define ASCII_XON 17
|
|
|
|
volatile uint8_t _rx_buffer[BUFSIZE];
|
|
volatile uint8_t _tx_buffer[BUFSIZE];
|
|
|
|
volatile uint8_t flowflags = 0;
|
|
#define FLOWFLAG_SEND_XOFF 1
|
|
#define FLOWFLAG_SEND_XON 2
|
|
#define FLOWFLAG_SENT_XOFF 4
|
|
#define FLOWFLAG_SENT_XON 8
|
|
|
|
#define rx_buffer ((ringbuffer *) _rx_buffer)
|
|
#define tx_buffer ((ringbuffer *) _tx_buffer)
|
|
|
|
void serial_init()
|
|
{
|
|
ringbuffer_init(rx_buffer, BUFSIZE);
|
|
ringbuffer_init(tx_buffer, BUFSIZE);
|
|
|
|
#if BAUD > 38401
|
|
UCSR0A = MASK(U2X0);
|
|
#else
|
|
UCSR0A = 0;
|
|
#endif
|
|
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
|
|
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
|
|
|
|
#if BAUD > 38401
|
|
UBRR0 = (((F_CPU / 8) / BAUD) - 0.5);
|
|
#else
|
|
UBRR0 = (((F_CPU / 16) / BAUD) - 0.5);
|
|
#endif
|
|
|
|
UCSR0B |= (1 << RXCIE0) | (1 << UDRIE0);
|
|
}
|
|
|
|
ISR(USART_RX_vect)
|
|
{
|
|
ringbuffer_writechar(rx_buffer, UDR0);
|
|
}
|
|
|
|
ISR(USART_UDRE_vect)
|
|
{
|
|
if (flowflags & FLOWFLAG_SEND_XOFF) {
|
|
UDR0 = ASCII_XOFF;
|
|
flowflags = (flowflags & ~FLOWFLAG_SEND_XOFF) | FLOWFLAG_SENT_XOFF;
|
|
}
|
|
else if (flowflags & FLOWFLAG_SEND_XON) {
|
|
UDR0 = ASCII_XON;
|
|
flowflags = (flowflags & ~FLOWFLAG_SEND_XON) | FLOWFLAG_SENT_XON;
|
|
}
|
|
else if (ringbuffer_canread(tx_buffer))
|
|
UDR0 = ringbuffer_readchar(tx_buffer);
|
|
else
|
|
UCSR0B &= ~(1 << UDRIE0);
|
|
}
|
|
|
|
uint16_t serial_rxchars()
|
|
{
|
|
return ringbuffer_canread(rx_buffer);
|
|
}
|
|
|
|
uint16_t serial_txchars()
|
|
{
|
|
return ringbuffer_canread(tx_buffer);
|
|
}
|
|
|
|
uint8_t serial_popchar()
|
|
{
|
|
return ringbuffer_readchar(rx_buffer);
|
|
}
|
|
|
|
uint16_t serial_recvblock(uint8_t *block, int blocksize)
|
|
{
|
|
return ringbuffer_readblock(rx_buffer, block, blocksize);
|
|
}
|
|
|
|
void serial_writechar(uint8_t data)
|
|
{
|
|
// check if interrupts are enabled
|
|
if (SREG & MASK(SREG_I)) {
|
|
// if they are, we should be ok to block
|
|
for (;ringbuffer_canwrite(tx_buffer) == 0;);
|
|
ringbuffer_writechar(tx_buffer, data);
|
|
}
|
|
else {
|
|
// interrupts are disabled- maybe we're in one?
|
|
// anyway, instead of blocking, only write if we have room
|
|
if (ringbuffer_canwrite(tx_buffer))
|
|
ringbuffer_writechar(tx_buffer, data);
|
|
}
|
|
// enable TX interrupt so we can send this character
|
|
UCSR0B |= (1 << UDRIE0);
|
|
}
|
|
|
|
void serial_writeblock(void *data, int datalen)
|
|
{
|
|
for (int i = 0; i < datalen; i++)
|
|
serial_writechar(((uint8_t *) data)[i]);
|
|
}
|
|
|
|
void serial_writechar_P(PGM_P data)
|
|
{
|
|
for (;ringbuffer_canwrite(tx_buffer) == 0;);
|
|
ringbuffer_writechar(tx_buffer, pgm_read_byte(data));
|
|
UCSR0B |= (1 << UDRIE0);
|
|
}
|
|
|
|
void serial_writeblock_P(PGM_P data, int datalen)
|
|
{
|
|
for (int i = 0; i < datalen; i++)
|
|
serial_writechar_P(&data[i]);
|
|
}
|
|
|
|
void serial_writestr_P(PGM_P data)
|
|
{
|
|
uint8_t i = 0;
|
|
// yes, this is *supposed* to be assignment rather than comparison
|
|
for (uint8_t r; (r = pgm_read_byte(&data[i])); i++)
|
|
serial_writechar(r);
|
|
}
|
|
|
|
#ifdef XONXOFF
|
|
void xon() {
|
|
if (flowflags & FLOWFLAG_SENT_XOFF)
|
|
flowflags = FLOWFLAG_SEND_XON;
|
|
// enable TX interrupt so we can send this character
|
|
UCSR0B |= (1 << UDRIE0);
|
|
}
|
|
|
|
void xoff() {
|
|
flowflags = FLOWFLAG_SEND_XOFF;
|
|
// enable TX interrupt so we can send this character
|
|
UCSR0B |= (1 << UDRIE0);
|
|
}
|
|
#endif
|