diff --git a/board-package-source/boards.txt b/board-package-source/boards.txt index cf4083e..bd3c783 100644 --- a/board-package-source/boards.txt +++ b/board-package-source/boards.txt @@ -147,6 +147,18 @@ arduboy-homemade.menu.display.ssd1306.usb_product_postfix=1306 arduboy-homemade.menu.display.ssd1306.bootloader_display= arduboy-homemade.menu.display.ssd1306.build.extra_flags=-DARDUBOY_10 -DOLED_SSD1306 {build.flash_cs} {build.usb_flags} +arduboy-homemade.menu.display.ssd1306i2c=SSD1306-I2C (2 Mbps) +arduboy-homemade.menu.display.ssd1306i2c.build.display=-ssd1306i2c +arduboy-homemade.menu.display.ssd1306i2c.usb_product_postfix=I2C +arduboy-homemade.menu.display.ssd1306i2c.bootloader_display= +arduboy-homemade.menu.display.ssd1306i2c.build.extra_flags=-DARDUBOY_10 -DOLED_SSD1306_I2C {build.flash_cs} {build.usb_flags} + +arduboy-homemade.menu.display.ssd1306i2cx=SSD1306-I2C (2.66 Mbps) +arduboy-homemade.menu.display.ssd1306i2cx.build.display=-ssd1306i2cf +arduboy-homemade.menu.display.ssd1306i2cx.usb_product_postfix=I2CX +arduboy-homemade.menu.display.ssd1306i2cx.bootloader_display= +arduboy-homemade.menu.display.ssd1306i2cx.build.extra_flags=-DARDUBOY_10 -DOLED_SSD1306_I2CX {build.flash_cs} {build.usb_flags} + arduboy-homemade.menu.display.ssd1309=SSD1309 arduboy-homemade.menu.display.ssd1309.build.display=-ssd1309 arduboy-homemade.menu.display.ssd1309.usb_product_postfix=1309 diff --git a/board-package-source/cores/arduboy/CDC.cpp b/board-package-source/cores/arduboy/CDC.cpp index 9fa12b6..15b3e70 100644 --- a/board-package-source/cores/arduboy/CDC.cpp +++ b/board-package-source/cores/arduboy/CDC.cpp @@ -36,7 +36,7 @@ static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; static volatile int32_t breakValue = -1; #ifndef ARDUBOY_CORE -bool _updatedLUFAbootloader = false; +static u8 wdtcsr_save; #else extern volatile unsigned char bootloader_timer; #endif @@ -62,6 +62,11 @@ const CDCDescriptor _cdcInterface = D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0) }; +bool isLUFAbootloader() +{ + return pgm_read_word(FLASHEND - 1) == NEW_LUFA_SIGNATURE; +} + int CDC_GetInterface(u8* interfaceNum) { interfaceNum[0] += 2; // uses 2 @@ -97,10 +102,7 @@ bool CDC_Setup(USBSetup& setup) if (CDC_SET_CONTROL_LINE_STATE == r) { _usbLineInfo.lineState = setup.wValueL; - } - if (CDC_SET_LINE_CODING == r || CDC_SET_CONTROL_LINE_STATE == r) - { // 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 @@ -114,41 +116,43 @@ bool CDC_Setup(USBSetup& setup) #if MAGIC_KEY_POS != (RAMEND-1) // For future boards save the key in the inproblematic RAMEND // Which is reserved for the main() return value (which will never return) - if (_updatedLUFAbootloader) { + if (isLUFAbootloader()) { // horray, we got a new bootloader! magic_key_pos = (RAMEND-1); } #endif #endif + // We check DTR state to determine if host port is open (bit 0 of lineState). if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0) - { #ifndef ARDUBOY_CORE + { #if MAGIC_KEY_POS != (RAMEND-1) // Backup ram value if its not a newer bootloader. // This should avoid memory corruption at least a bit, not fully - if (magic_key_pos != (RAMEND-1)) { + if (magic_key_pos != (RAMEND-1) && *(uint16_t *)magic_key_pos != MAGIC_KEY) { *(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos; } #endif // Store boot key *(uint16_t *)magic_key_pos = MAGIC_KEY; + // Save the watchdog state in case the reset is aborted. + wdtcsr_save = WDTCSR; wdt_enable(WDTO_120MS); -#else - bootloader_timer = 120; //ms - power_timer0_enable(); //power timer0 is disabled by flashlight/safemode in older Arduboy2 libraries -#endif } - else + else if (*(uint16_t *)magic_key_pos == MAGIC_KEY) { -#ifndef ARDUBOY_CORE // 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 + // To avoid spurious resets we set the watchdog to 120ms and eventually // cancel if DTR goes back high. + // Cancellation is only done if an auto-reset was started, which is + // indicated by the magic key having been set. - wdt_disable(); wdt_reset(); + // Restore the watchdog state in case the sketch was using it. + WDTCSR |= (1< #include #include +#include #include "Arduino.h" #include "HardwareSerial.h" @@ -76,6 +77,13 @@ void serialEventRun(void) #endif } +// macro to guard critical sections when needed for large TX buffer sizes +#if (SERIAL_TX_BUFFER_SIZE>256) +#define TX_BUFFER_ATOMIC ATOMIC_BLOCK(ATOMIC_RESTORESTATE) +#else +#define TX_BUFFER_ATOMIC +#endif + // Actual interrupt handlers ////////////////////////////////////////////////////////////// void HardwareSerial::_tx_udr_empty_irq(void) @@ -89,8 +97,14 @@ void HardwareSerial::_tx_udr_empty_irq(void) // clear the TXC bit -- "can be cleared by writing a one to its bit // location". This makes sure flush() won't return until the bytes - // actually got written - sbi(*_ucsra, TXC0); + // actually got written. Other r/w bits are preserved, and zeroes + // written to the rest. + +#ifdef MPCM0 + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); +#else + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); +#endif if (_tx_buffer_head == _tx_buffer_tail) { // Buffer empty, so disable interrupts @@ -177,15 +191,13 @@ int HardwareSerial::read(void) int HardwareSerial::availableForWrite(void) { -#if (SERIAL_TX_BUFFER_SIZE>256) - uint8_t oldSREG = SREG; - cli(); -#endif - tx_buffer_index_t head = _tx_buffer_head; - tx_buffer_index_t tail = _tx_buffer_tail; -#if (SERIAL_TX_BUFFER_SIZE>256) - SREG = oldSREG; -#endif + tx_buffer_index_t head; + tx_buffer_index_t tail; + + TX_BUFFER_ATOMIC { + head = _tx_buffer_head; + tail = _tx_buffer_tail; + } if (head >= tail) return SERIAL_TX_BUFFER_SIZE - 1 - head + tail; return tail - head - 1; } @@ -218,8 +230,22 @@ size_t HardwareSerial::write(uint8_t c) // significantly improve the effective datarate at high (> // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) { - *_udr = c; - sbi(*_ucsra, TXC0); + // If TXC is cleared before writing UDR and the previous byte + // completes before writing to UDR, TXC will be set but a byte + // is still being transmitted causing flush() to return too soon. + // So writing UDR must happen first. + // Writing UDR and clearing TC must be done atomically, otherwise + // interrupts might delay the TXC clear so the byte written to UDR + // is transmitted (setting TXC) before clearing TXC. Then TXC will + // be cleared when no bytes are left, causing flush() to hang + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + *_udr = c; +#ifdef MPCM0 + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0); +#else + *_ucsra = ((*_ucsra) & ((1 << U2X0) | (1 << TXC0))); +#endif + } return 1; } tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE; @@ -240,9 +266,14 @@ size_t HardwareSerial::write(uint8_t c) } _tx_buffer[_tx_buffer_head] = c; - _tx_buffer_head = i; - - sbi(*_ucsrb, UDRIE0); + + // make atomic to prevent execution of ISR between setting the + // head pointer and setting the interrupt flag resulting in buffer + // retransmission + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + _tx_buffer_head = i; + sbi(*_ucsrb, UDRIE0); + } return 1; } diff --git a/board-package-source/cores/arduboy/USBCore.cpp b/board-package-source/cores/arduboy/USBCore.cpp index ce44f8b..52f84ee 100644 --- a/board-package-source/cores/arduboy/USBCore.cpp +++ b/board-package-source/cores/arduboy/USBCore.cpp @@ -35,9 +35,6 @@ extern const u16 STRING_LANGUAGE[] PROGMEM; extern const u8 STRING_PRODUCT[] PROGMEM; extern const u8 STRING_MANUFACTURER[] PROGMEM; extern const DeviceDescriptor USB_DeviceDescriptorIAD PROGMEM; -#ifndef ARDUBOY_CORE -extern bool _updatedLUFAbootloader; -#endif const u16 STRING_LANGUAGE[2] = { (3<<8) | (2+2), @@ -821,14 +818,6 @@ void USBDevice_::attach() UDIEN = (1<