From 58fc0d17cc2d7c5dfb4ce268b7e43d35eebac342 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Sun, 18 Dec 2011 17:52:35 -0500 Subject: 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 | 67 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 16 deletions(-) (limited to 'cores/arduino/CDC.cpp') 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) { } +void Serial_::accept(void) +{ + 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) { - u8 avail = USB_Available(CDC_RX); - if (_serialPeek != -1) - avail++; - return avail; + 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) -- cgit v1.2.3-18-g5258 From 45d3b102956c74f837eb0297af45939ca46b09a0 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Tue, 24 Jan 2012 18:04:10 -0500 Subject: Caterina now cleanly hands off operation to the sketch had to remove TIMER1 operation from bootloader - was interfering with normal sketch operation --- cores/arduino/CDC.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 8605ce3..eb1b2c5 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -27,7 +27,10 @@ void Reboot() { USB.detach(); cli(); - asm volatile("jmp 0x7800"); // jump to bootloader - DiskLoader takes up last 2 kB + + // Reset the microcontroller to run the bootloader + wdt_enable(WDTO_250MS); + for (;;); } // Define constants and variables for buffering incoming serial data. We're -- cgit v1.2.3-18-g5258 From d7b0507e8a226a639f000fd1c6e7de70117c7c84 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Fri, 3 Feb 2012 21:42:46 -0500 Subject: fixed starting Leonardo bootloader from sketch AvrdudeUploader class opens and closes Leonardo port at the magic baudrate before starting avrdude; reduced reset timeout from 250 ms to 15 ms --- cores/arduino/CDC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index eb1b2c5..6dd1cbe 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -29,7 +29,7 @@ void Reboot() cli(); // Reset the microcontroller to run the bootloader - wdt_enable(WDTO_250MS); + wdt_enable(WDTO_15MS); for (;;); } -- cgit v1.2.3-18-g5258 From 924e5a48465cf8a9ba3af38e6cf4c2e1a2c0733c Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Mon, 13 Feb 2012 00:56:06 -0500 Subject: Leonardo auto-reset-and-upload changes for Windows (explanation below) On Windows COM port changes when board switched between bootloader and sketch. No way to prevent this so now Windows users have to select the upload port separate from the comm port. Also, handling of reset into bootloader was broken on Windows. Would occasionally leave the original COM port completely unusable. Changed the way this reset is initiated. Finally, had to add upload.disable.flushing=true flag to boards.txt so IDE wouldn't try to flush the original COM port after it disappeared. --- cores/arduino/CDC.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 6dd1cbe..deda5c0 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -23,20 +23,6 @@ #if defined(USBCON) #ifdef CDC_ENABLED -void Reboot() -{ - USB.detach(); - cli(); - - // Reset the microcontroller to run the bootloader - wdt_enable(WDTO_15MS); - for (;;); -} - -// 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 @@ -114,9 +100,14 @@ bool WEAK CDC_Setup(Setup& setup) if (CDC_SET_CONTROL_LINE_STATE == r) { - if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) // auto-reset is triggered when the port, already open at 1200 bps, is closed - Reboot(); _usbLineInfo.lineState = setup.wValueL; + // auto-reset into the bootloader is triggered when the port, already + // open at 1200 bps, is closed. this is the signal to start the watchdog + // with a relatively long period so it can finish housekeeping tasks + // like servicing endpoints before the sketch ends + if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) { + wdt_enable(WDTO_2S); + } return true; } } -- cgit v1.2.3-18-g5258 From 79481252081da0bdc4501cf039c060efcdf85c95 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Tue, 14 Feb 2012 12:17:30 -0500 Subject: Leonardo now checks whether bootloader should be run after a WDT event. Before the sketch initiates an auto-reset for upload it pokes a magic word into a specific RAM address. On starting the bootloader checks this address. If it finds the magic word it knows the bootloader code should run. If not it jumps straight back to sketch. Test in a sketch by adding to setup(): wdt_enable(WDTO_2S); Sketch should upload, start, run for two seconds, WDT, and sketch should restart (not bootloader). Had to cut out unused descriptor code to make the bootloader still fit in 4k. --- cores/arduino/CDC.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index deda5c0..0fa06bc 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -105,7 +105,8 @@ bool WEAK CDC_Setup(Setup& setup) // open at 1200 bps, is closed. this is the signal to start the watchdog // with a relatively long period so it can finish housekeeping tasks // like servicing endpoints before the sketch ends - if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) { + if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) { + *(uint16_t *)0x0A00 = 0x7777; wdt_enable(WDTO_2S); } return true; -- cgit v1.2.3-18-g5258 From 0138ee9b0167da66a9fb16602e57dc11bf02a563 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Wed, 22 Feb 2012 22:33:44 -0500 Subject: shortened the watchdog period for resetting Leonardo from 2 s to 250 ms. Reset into bootloader is much snappier. --- cores/arduino/CDC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 0fa06bc..7f33a6e 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -107,7 +107,7 @@ bool WEAK CDC_Setup(Setup& setup) // like servicing endpoints before the sketch ends if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) { *(uint16_t *)0x0A00 = 0x7777; - wdt_enable(WDTO_2S); + wdt_enable(WDTO_250MS); } return true; } -- cgit v1.2.3-18-g5258 From 73066a4ca24699820580f15520bf07d3f34068a2 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 27 Feb 2012 17:41:38 +0100 Subject: Autoreset 1200 bps fix. --- cores/arduino/CDC.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 7f33a6e..6d3929e 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -101,13 +101,26 @@ bool WEAK CDC_Setup(Setup& setup) if (CDC_SET_CONTROL_LINE_STATE == r) { _usbLineInfo.lineState = setup.wValueL; + // auto-reset into the bootloader is triggered when the port, already // open at 1200 bps, is closed. this is the signal to start the watchdog // with a relatively long period so it can finish housekeeping tasks // like servicing endpoints before the sketch ends - if (0 != _usbLineInfo.lineState && 1200 == _usbLineInfo.dwDTERate) { + + // We check DTR state to determine if host port is open (bit 0 of lineState). + // Serial1.print(">"); Serial1.println(_usbLineInfo.lineState, HEX); + if ((_usbLineInfo.lineState & 0x01) == 0 && _usbLineInfo.dwDTERate == 1200) { *(uint16_t *)0x0A00 = 0x7777; wdt_enable(WDTO_250MS); + } else { + // Most OSs do some intermediate steps when configuring ports and DTR can + // twiggle more than once before stabilizing. + // To avoid spurious resets we set the watchdog to 250ms and eventually + // cancel if DTR goes back high. + + wdt_disable(); + wdt_reset(); + *(uint16_t *)0x0A00 = 0x0; } return true; } @@ -202,4 +215,4 @@ size_t Serial_::write(uint8_t c) Serial_ Serial; #endif -#endif /* if defined(USBCON) */ \ No newline at end of file +#endif /* if defined(USBCON) */ -- cgit v1.2.3-18-g5258 From c8ca7f96366d53b139ded479bcc690332eb8382d Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Thu, 1 Mar 2012 08:51:16 -0500 Subject: changed auto-reset logic for Leonardo. only do WDT manipulation if the port is opened at 1200 bps. (Dave Mellis) --- cores/arduino/CDC.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 6d3929e..94faf2e 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -106,21 +106,22 @@ bool WEAK CDC_Setup(Setup& setup) // open at 1200 bps, is closed. this is the signal to start the watchdog // with a relatively long period so it can finish housekeeping tasks // like servicing endpoints before the sketch ends - - // We check DTR state to determine if host port is open (bit 0 of lineState). - // Serial1.print(">"); Serial1.println(_usbLineInfo.lineState, HEX); - if ((_usbLineInfo.lineState & 0x01) == 0 && _usbLineInfo.dwDTERate == 1200) { - *(uint16_t *)0x0A00 = 0x7777; - wdt_enable(WDTO_250MS); - } else { - // Most OSs do some intermediate steps when configuring ports and DTR can - // twiggle more than once before stabilizing. - // To avoid spurious resets we set the watchdog to 250ms and eventually - // cancel if DTR goes back high. - - wdt_disable(); - wdt_reset(); - *(uint16_t *)0x0A00 = 0x0; + if (1200 == _usbLineInfo.dwDTERate) { + // We check DTR state to determine if host port is open (bit 0 of lineState). + // Serial1.print(">"); Serial1.println(_usbLineInfo.lineState, HEX); + if ((_usbLineInfo.lineState & 0x01) == 0) { + *(uint16_t *)0x0A00 = 0x7777; + wdt_enable(WDTO_250MS); + } else { + // Most OSs do some intermediate steps when configuring ports and DTR can + // twiggle more than once before stabilizing. + // To avoid spurious resets we set the watchdog to 250ms and eventually + // cancel if DTR goes back high. + + wdt_disable(); + wdt_reset(); + *(uint16_t *)0x0A00 = 0x0; + } } return true; } -- cgit v1.2.3-18-g5258 From 8c618773b56a302b9fda0a0c14da34e09bba25fa Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Wed, 14 Mar 2012 18:17:22 -0400 Subject: reduced delay before starting the Leonardo bootloader Since we use a magic RAM flag to signal to the bootloader there's a risk of the sketch overwriting the magic RAM location before the bootloader starts. By reducing the watchdog timeout we reduce the chance of this happening. --- cores/arduino/CDC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 94faf2e..7206aa6 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -111,7 +111,7 @@ bool WEAK CDC_Setup(Setup& setup) // Serial1.print(">"); Serial1.println(_usbLineInfo.lineState, HEX); if ((_usbLineInfo.lineState & 0x01) == 0) { *(uint16_t *)0x0A00 = 0x7777; - wdt_enable(WDTO_250MS); + wdt_enable(WDTO_120MS); } else { // Most OSs do some intermediate steps when configuring ports and DTR can // twiggle more than once before stabilizing. -- cgit v1.2.3-18-g5258 From a984b581a8ad093b55ec9f2d4677afdd77bf4705 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Sun, 1 Apr 2012 12:54:35 -0400 Subject: added Boolean operators to HardwareSerial and CDC to test whether the port is ready to send data. Mostly useful for Leonardo - simple way to test whether the port is actually opened by an application and ready to receive data. For Serial objects attached to real UARTs always returns true. --- cores/arduino/CDC.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 7206aa6..1275304 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -213,6 +213,12 @@ size_t Serial_::write(uint8_t c) return 0; } +Serial_::operator bool() { + if (_usbLineInfo.lineState > 0) + return true; + return false; +} + Serial_ Serial; #endif -- cgit v1.2.3-18-g5258 From dd55096901b163b315948e0ddee3706464b3ec26 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Tue, 3 Apr 2012 10:52:38 -0400 Subject: added a short delay and comment to boolean operator in CDC Delay fixes problem where the port has been configured but not quite opened. Federico found that 10 ms was the minimum time needed to avoid problems. --- cores/arduino/CDC.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 1275304..c1e646d 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -213,10 +213,19 @@ size_t Serial_::write(uint8_t c) return 0; } +// This operator is a convenient way for a sketch to check whether the +// port has actually been configured and opened by the host (as opposed +// to just being connected to the host). It can be used, for example, in +// setup() before printing to ensure that an application on the host is +// actually ready to receive and display the data. +// We add a short delay before returning to fix a bug observed by Federico +// where the port is configured (lineState != 0) but not quite opened. Serial_::operator bool() { - if (_usbLineInfo.lineState > 0) - return true; - return false; + bool result = false; + if (_usbLineInfo.lineState > 0) + result = true; + delay(10); + return result; } Serial_ Serial; -- cgit v1.2.3-18-g5258 From 02c5849501294f2965051c41e1236899a55f7bfc Mon Sep 17 00:00:00 2001 From: Federico Vanzati Date: Fri, 6 Apr 2012 17:36:09 +0200 Subject: Revert "added a short delay and comment to boolean operator in CDC" This reverts commit ade4893f585e3e94fa6cf683620e1d12afc88ecd. --- cores/arduino/CDC.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index c1e646d..1275304 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -213,19 +213,10 @@ size_t Serial_::write(uint8_t c) return 0; } -// This operator is a convenient way for a sketch to check whether the -// port has actually been configured and opened by the host (as opposed -// to just being connected to the host). It can be used, for example, in -// setup() before printing to ensure that an application on the host is -// actually ready to receive and display the data. -// We add a short delay before returning to fix a bug observed by Federico -// where the port is configured (lineState != 0) but not quite opened. Serial_::operator bool() { - bool result = false; - if (_usbLineInfo.lineState > 0) - result = true; - delay(10); - return result; + if (_usbLineInfo.lineState > 0) + return true; + return false; } Serial_ Serial; -- cgit v1.2.3-18-g5258 From ee611dc194fad72d2db9a901208577857365778f Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Fri, 6 Apr 2012 21:23:17 -0400 Subject: Revert "Revert "added a short delay and comment to boolean operator in CDC"" This reverts commit 200eefb4e2ac7796c5c901e8fd9369c85ec544c5. --- cores/arduino/CDC.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index 1275304..c1e646d 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -213,10 +213,19 @@ size_t Serial_::write(uint8_t c) return 0; } +// This operator is a convenient way for a sketch to check whether the +// port has actually been configured and opened by the host (as opposed +// to just being connected to the host). It can be used, for example, in +// setup() before printing to ensure that an application on the host is +// actually ready to receive and display the data. +// We add a short delay before returning to fix a bug observed by Federico +// where the port is configured (lineState != 0) but not quite opened. Serial_::operator bool() { - if (_usbLineInfo.lineState > 0) - return true; - return false; + bool result = false; + if (_usbLineInfo.lineState > 0) + result = true; + delay(10); + return result; } Serial_ Serial; -- cgit v1.2.3-18-g5258 From 49f7fb00fd7578fe5ecd78d60165fc2570aec1e2 Mon Sep 17 00:00:00 2001 From: Zach Eveland Date: Wed, 11 Apr 2012 23:19:05 -0400 Subject: fixed logic bug in Caterina that could stop the bootloader from entering self-programming mode --- cores/arduino/CDC.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'cores/arduino/CDC.cpp') diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp index c1e646d..1ee3a48 100644 --- a/cores/arduino/CDC.cpp +++ b/cores/arduino/CDC.cpp @@ -108,9 +108,8 @@ bool WEAK CDC_Setup(Setup& setup) // like servicing endpoints before the sketch ends if (1200 == _usbLineInfo.dwDTERate) { // We check DTR state to determine if host port is open (bit 0 of lineState). - // Serial1.print(">"); Serial1.println(_usbLineInfo.lineState, HEX); if ((_usbLineInfo.lineState & 0x01) == 0) { - *(uint16_t *)0x0A00 = 0x7777; + *(uint16_t *)0x0800 = 0x7777; wdt_enable(WDTO_120MS); } else { // Most OSs do some intermediate steps when configuring ports and DTR can @@ -120,7 +119,7 @@ bool WEAK CDC_Setup(Setup& setup) wdt_disable(); wdt_reset(); - *(uint16_t *)0x0A00 = 0x0; + *(uint16_t *)0x0800 = 0x0; } } return true; -- cgit v1.2.3-18-g5258