From 58fc0d17cc2d7c5dfb4ce268b7e43d35eebac342 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Sun, 18 Dec 2011 17:52:35 -0500 Subject: [PATCH] added asynchronous buffering of received CDC characters This fixes the issue Federico reported where bytes written by host but not read by sketch would cause serial connection to lock up. Ring buffer implementation is based on HardwareSerial.cpp. Adds public accept() method to CDC. --- cores/arduino/CDC.cpp | 69 +++++++++++++++++++++++++++++---------- cores/arduino/USBAPI.h | 3 ++ cores/arduino/USBCore.cpp | 2 ++ 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 14a0eae..8605ce3 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -30,6 +30,25 @@ void Reboot() asm volatile("jmp 0x7800"); // jump to bootloader - DiskLoader takes up last 2 kB } +// Define constants and variables for buffering incoming serial data. We're +// using a ring buffer (I think), in which head is the index of the location +// to which to write the next incoming character and tail is the index of the +// location from which to read. +#if (RAMEND < 1000) +#define SERIAL_BUFFER_SIZE 16 +#else +#define SERIAL_BUFFER_SIZE 64 +#endif + +struct ring_buffer +{ + unsigned char buffer[SERIAL_BUFFER_SIZE]; + volatile int head; + volatile int tail; +}; + +ring_buffer cdc_rx_buffer = { { 0 }, 0, 0}; + typedef struct { u32 dwDTERate; @@ -111,33 +130,49 @@ void Serial_::end(void) { } -int Serial_::available(void) +void Serial_::accept(void) { - u8 avail = USB_Available(CDC_RX); - if (_serialPeek != -1) - avail++; - return avail; + ring_buffer *buffer = &cdc_rx_buffer; + int c = USB_Recv(CDC_RX); + int i = (unsigned int)(buffer->head+1) % SERIAL_BUFFER_SIZE; + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != buffer->tail) { + buffer->buffer[buffer->head] = c; + buffer->head = i; + } +} + +int Serial_::available(void) +{ + ring_buffer *buffer = &cdc_rx_buffer; + return (unsigned int)(SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % SERIAL_BUFFER_SIZE; } -// peek is nasty int Serial_::peek(void) { - if (_serialPeek == -1) - _serialPeek = read(); - return _serialPeek; + ring_buffer *buffer = &cdc_rx_buffer; + if (buffer->head == buffer->tail) { + return -1; + } else { + return buffer->buffer[buffer->tail]; + } } int Serial_::read(void) { - int c; - if (_serialPeek != -1) - { - c = _serialPeek; - _serialPeek = -1; + ring_buffer *buffer = &cdc_rx_buffer; + // if the head isn't ahead of the tail, we don't have any characters + if (buffer->head == buffer->tail) { + return -1; } else { - c = USB_Recv(CDC_RX); - } - return c; + unsigned char c = buffer->buffer[buffer->tail]; + buffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE; + return c; + } } void Serial_::flush(void) diff --git a/cores/arduino/USBAPI.h b/cores/arduino/USBAPI.h index 26a2032..1772c3c 100644 --- a/cores/arduino/USBAPI.h +++ b/cores/arduino/USBAPI.h @@ -27,11 +27,14 @@ extern USB_ USB; class Serial_ : public Stream { +private: + ring_buffer *_cdc_rx_buffer; public: void begin(uint16_t baud_count); void end(void); virtual int available(void); + virtual void accept(void); virtual int peek(void); virtual int read(void); virtual void flush(void); diff --git a/cores/arduino/USBCore.cpp b/cores/arduino/USBCore.cpp index 398bc73..7924078 100644 --- a/cores/arduino/USBCore.cpp +++ b/cores/arduino/USBCore.cpp @@ -599,6 +599,8 @@ ISR(USB_GEN_vect) { #ifdef CDC_ENABLED USB_Flush(CDC_TX); // Send a tx frame if found + while (USB_Available(CDC_RX)) // Handle received bytes (if any) + Serial.accept(); #endif // check whether the one-shot period has elapsed. if so, turn off the LED