Skip to content

Instantly share code, notes, and snippets.

@kierdavis
Created August 14, 2018 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kierdavis/d114cd6c2fde2f7e44df33c37da8c176 to your computer and use it in GitHub Desktop.
Save kierdavis/d114cd6c2fde2f7e44df33c37da8c176 to your computer and use it in GitHub Desktop.
// Buffers for incoming and outgoing bytes respectively.
static RingBuffer recv_buffer;
static RingBuffer send_buffer;
// True when we've sent an XOFF and the peer has (hopefully) stopped sending data.
static bool recv_paused = false;
// True when the peer has sent an XOFF and we should stop sending data.
static bool send_paused = false;
// Adds a byte to the end of the receive queue, sending an XOFF if it's starting to get full.
// Returns true if there was space in the buffer or false if there was not.
static bool recv_buffer_push(char c) {
if (!recv_buffer.push_back(c)) {
return false;
}
if (recv_buffer.nearly_full()) {
send_buffer.push_front(XOFF);
attempt_transmit();
recv_paused = true;
}
return true;
}
// Removes the oldest byte in the receive queue and stores it into dest, sending an XON if the buffer is nearly empty and we've previously sent an XOFF.
// Returns true if there was a byte in the queue to retrieve or false if there was not.
static bool recv_buffer_pop(char *dest) {
if (!recv_buffer.pop_front(dest)) {
return false;
}
if (recv_paused && recv_buffer.nearly_empty()) {
send_buffer.push_front(XON);
attempt_transmit();
recv_paused = false;
}
return true;
}
// Interrupt handler called whenever the hardware USART has finished receiving a byte.
ISR(USART_RX_vect) {
char c = UDR0;
// Handle special control codes.
if (c == XOFF) {
send_paused = true;
return;
} else if (c == XON) {
send_paused = false;
attempt_transmit();
return;
}
// Not a control code, add it to the buffer.
if (!recv_buffer_push(c)) {
// Buffer is full :(
panic();
}
}
// Attempt to start transmitting a byte, if all of the following conditions are true:
// 1) the hardware is not currently transmitting another byte
// 2) the peer has not paused our transmitting by sending an XOFF
// 3) there is data in the send_buffer
// This function should be called whenever any of these conditions may transition from false to true; that is, when:
// 1) the UDRE (USART data register empty) interrupt is received
// 2) an XON is received from the peer
// 3) data is added to the send_buffer, either by serial_write or when we enqueue XOFFs/XONs for transmission
static void attempt_transmit() {
if (!(UCSR0A & _BV(UDRE0))) {
return; // Hardware is still transmitting the previous byte.
}
if (send_paused) {
return; // Peer has sent an XOFF, obey it.
}
char c;
if (!send_buffer.pop_front(&c)) {
return; // Nothing to send.
}
// Write byte into hardware register, which automatically triggers transmission.
UDR0 = c;
}
// Interrupt handler called whenever the hardware USART is ready to accept another byte to transmit.
ISR(USART_UDRE_vect) {
attempt_transmit();
}
//////////////////////////////
// Public interface follows //
//////////////////////////////
// Returns the number of bytes that are ready to be returned by serial_read.
int serial_available() {
return (int) recv_buffer.len();
}
// Reads one byte from the serial port.
// Returns -1 if no data was available.
int serial_read() {
char c;
if (recv_buffer_pop(&c)) {
return c;
} else {
return -1;
}
}
// Reads one byte from the serial port.
// Blocks until data is available.
int serial_read_blocking() {
while (recv_buffer.empty()) {
// TODO: enter a low-power state
}
return serial_read();
}
// Writes one byte to the serial port.
// Returns false if no space is available in the buffer.
bool serial_write(char c) {
if (!send_buffer.push_back(c)) {
return false;
}
attempt_transmit();
return true;
}
// Writes one byte to the serial port.
// Blocks until space is available in the buffer.
void serial_write_blocking(char c) {
while (send_buffer.full()) {
// TODO: enter a low-power state
}
serial_write(c);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment