diff --git a/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld b/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld index 8a15697..18bcb89 100644 --- a/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld +++ b/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld @@ -53,9 +53,10 @@ SECTIONS { *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) *(.sdata .sdata.* .sdata* .sdata*.* .gnu.linkonce.s.*) *(.data .data.*) + *(.ramfunc) . = ALIGN(CL_SIZE); - } >ram - + } >ram + __DATA_IMAGE_START__ = LOADADDR(.data); __DATA_IMAGE_END__ = LOADADDR(.data) + SIZEOF(.data); ASSERT(__DATA_IMAGE_END__ < ORIGIN(rom) + LENGTH(rom), "Data image overflows rom section") diff --git a/cores/arduino/trap_handler.c b/cores/arduino/trap_handler.c index 5d01229..f309c87 100644 --- a/cores/arduino/trap_handler.c +++ b/cores/arduino/trap_handler.c @@ -8,10 +8,16 @@ void __attribute__((weak)) wire_handler_wrapper(void) { // dummy function for case when wire library is not in use } +extern void softSerial_handler_wrapper(void); // ---------------------------------------------- // void trap_handler(void) { + // gpio interrupt + if (EPIC_CHECK_GPIO_IRQ()) + // gpio_interrupts_handler(); + softSerial_handler_wrapper(); + // tone timer interrupt if (EPIC_CHECK_TIMER16_1()) tone_interrupt_handler(); @@ -23,10 +29,6 @@ void trap_handler(void) // uart1 interrupt if (EPIC_CHECK_UART_1()) serial_handler_wrapper(1); - - // gpio interrupt - if (EPIC_CHECK_GPIO_IRQ()) - gpio_interrupts_handler(); // i2c interrupt if (EPIC_CHECK_I2C_1()) diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.cpp b/libraries/SoftwareSerial/src/SoftwareSerial.cpp index b1ab6fb..f3611f9 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/src/SoftwareSerial.cpp @@ -17,66 +17,39 @@ The latest version of this library can always be found at http://arduiniana.org. */ -// When set, _DEBUG co-opts pins 11 and 13 for debugging with an -// oscilloscope or logic analyzer. Beware: it also slightly modifies -// the bit times, so don't rely on it too much at high baud rates -#define _DEBUG 0 -#define _DEBUG_PIN1 11 -#define _DEBUG_PIN2 13 - -#define MAX_SOFT_SERIAL_SPEED 115200 -#define MIN_SOFT_SERIAL_SPEED 300 -// -// Includes -// -// #include #include #include #include "mik32_hal_irq.h" #include "mik32_hal_gpio.h" #include "mik32_hal_scr1_timer.h" -// +#define MAX_SOFT_SERIAL_SPEED 115200 +#define MIN_SOFT_SERIAL_SPEED 300 + +// --------- TO LL --------- +// get ticks from systick +#define SYSTICK_GET_TICKS() ((uint64_t)(SCR1_TIMER->MTIMEH)<<32 | SCR1_TIMER->MTIME) +#define GPIO_SET_PIN(GPIO_x, pin) ((GPIO_x)->SET = (pin)) +#define GPIO_CLEAR_PIN(GPIO_x, pin) ((GPIO_x)->CLEAR = (pin)) +#define GPIO_READ_PIN(GPIO_x, pin) (((GPIO_x)->SET & (pin)) != (uint32_t)GPIO_PIN_LOW ? GPIO_PIN_HIGH : GPIO_PIN_LOW) + +// --------- TO LL --------- + + // Statics -// +// static variable is common to all class instances SoftwareSerial *SoftwareSerial::active_object = 0; uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; -// -// Debugging -// -// This function generates a brief pulse -// for debugging or measuring on an oscilloscope. -// #if _DEBUG -// inline void DebugPulse(uint8_t pin, uint8_t count) -// { -// volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); - -// uint8_t val = *pport; -// while (count--) -// { -// *pport = val | digitalPinToBitMask(pin); -// *pport = val; -// } -// } -// #else -// inline void DebugPulse(uint8_t, uint8_t) {} -// #endif - -// // Private methods -// - -// get ticks from systick -#define CURRENT_TICKS ((uint64_t)(SCR1_TIMER->MTIMEH)<<32 | SCR1_TIMER->MTIME) // function placed in ram. Should not call other function placed in flash void SoftwareSerial::tunedDelay(uint32_t delayTicks) { - uint64_t startTicks = CURRENT_TICKS; - while ((CURRENT_TICKS - startTicks) < delayTicks) + uint64_t startTicks = SYSTICK_GET_TICKS(); + while ((SYSTICK_GET_TICKS() - startTicks) < delayTicks) ; } @@ -120,71 +93,68 @@ bool SoftwareSerial::stopListening() // void SoftwareSerial::recv() { - // if (active_object) - // { - // uint8_t data = 0; + uint8_t data = 0; + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) + { + HAL_GPIO_WritePin(GPIO_0, GPIO_PIN_8, GPIO_PIN_HIGH); + // Disable further interrupts during reception, this prevents + // triggering another interrupt directly after we return, which can + // cause problems at higher baudrates. + setRxIntMsk(false); - // // If RX line is high, then we don't see any start bit - // // so interrupt is probably not for us - // if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) - // { - // // Disable further interrupts during reception, this prevents - // // triggering another interrupt directly after we return, which can - // // cause problems at higher baudrates. - // setRxIntMsk(false); + // Wait approximately 1/2 of a bit width to "center" the sample + tunedDelay(_rx_delay_centering); + // DebugPulse(_DEBUG_PIN2, 1); - // // Wait approximately 1/2 of a bit width to "center" the sample - // tunedDelay(_rx_delay_centering); - // // DebugPulse(_DEBUG_PIN2, 1); + // Read each of the 8 bits + for (uint8_t i=8; i > 0; --i) + { + tunedDelay(_rx_delay_intrabit); + data >>= 1; + // DebugPulse(_DEBUG_PIN2, 1); + if (rx_pin_read()) + data |= 0x80; + } - // // Read each of the 8 bits - // for (uint8_t i=8; i > 0; --i) - // { - // tunedDelay(_rx_delay_intrabit); - // data >>= 1; - // // DebugPulse(_DEBUG_PIN2, 1); - // if (rx_pin_read()) - // data |= 0x80; - // } + if (_inverse_logic) + data = ~data; - // if (_inverse_logic) - // data = ~data; + // if buffer full, set the overflow flag and return + uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + if (next != _receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + _receive_buffer[_receive_buffer_tail] = data; // save new byte + _receive_buffer_tail = next; + } + else + { + // DebugPulse(_DEBUG_PIN1, 1); + _buffer_overflow = true; + } - // // if buffer full, set the overflow flag and return - // uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; - // if (next != _receive_buffer_head) - // { - // // save new data in buffer: tail points to where byte goes - // _receive_buffer[_receive_buffer_tail] = data; // save new byte - // _receive_buffer_tail = next; - // } - // else - // { - // // DebugPulse(_DEBUG_PIN1, 1); - // _buffer_overflow = true; - // } + // skip the stop bit + tunedDelay(_rx_delay_stopbit); + // DebugPulse(_DEBUG_PIN1, 1); - // // skip the stop bit - // tunedDelay(_rx_delay_stopbit); - // // DebugPulse(_DEBUG_PIN1, 1); - - // // Re-enable interrupts when we're sure to be inside the stop bit - // setRxIntMsk(true); - // } - // } + // Re-enable interrupts when we're sure to be inside the stop bit + setRxIntMsk(true); + HAL_GPIO_WritePin(GPIO_0, GPIO_PIN_8, GPIO_PIN_LOW); + } + HAL_GPIO_ClearInterrupts(); } uint8_t SoftwareSerial::rx_pin_read() { - return (uint8_t)HAL_GPIO_ReadPin((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask); + return (uint8_t)GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask); } -// // Interrupt handling -// /* static */ -inline void SoftwareSerial::handle_interrupt() // todo унести в обработчик прерываний +inline void SoftwareSerial::handle_interrupt() { if (active_object) { @@ -192,12 +162,11 @@ inline void SoftwareSerial::handle_interrupt() // todo унести в обра } } -// #if defined(PCINT0_vect) -// ISR(PCINT0_vect) -// { -// SoftwareSerial::handle_interrupt(); -// } -// #endif +extern "C" void softSerial_handler_wrapper(void) +{ + SoftwareSerial::handle_interrupt(); +} + // // Constructor @@ -235,10 +204,8 @@ void SoftwareSerial::setRX(uint8_t rx) { _receiveBitMask = (uint16_t)digitalPinToBitMask(rx); // save rx bit mask _receivePortRegister = (uint32_t)digitalPinToPort(rx); // save rx port adress - // attachInterrupt(digitalPinToInterrupt(rx), [this]() { this->recv(); }, _inverse_logic ? RISING : FALLING); if (!_inverse_logic) pinMode(rx, INPUT_PULLUP); // pullup for normal logic - // HAL_GPIO_PinConfig((GPIO_TypeDef*)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask, HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_UP, HAL_GPIO_DS_2MA); else pinMode(rx, INPUT); } @@ -269,78 +236,54 @@ void SoftwareSerial::begin(long speed) // Precalculate the various delays in number of ticks uint32_t bit_delay = F_CPU / speed; - // 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit, - // 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits, - // 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit - // These are all close enough to just use 15 cycles, since the inter-bit - // timings are the most critical (deviations stack 8 times) + // empirical value _tx_delay = subtract_cap(bit_delay, 35); // 15 // Only setup rx when we have a valid INT for this pin if (digitalPinToInterrupt(_receivePin) != NOT_AN_INTERRUPT) { - // #if GCC_VERSION > 40800 - // // Timings counted from gcc 4.8.2 output. This works up to 115200 on - // // 16Mhz and 57600 on 8Mhz. - // // - // // When the start bit occurs, there are 3 or 4 cycles before the - // // interrupt flag is set, 4 cycles before the PC is set to the right - // // interrupt vector address and the old PC is pushed on the stack, - // // and then 75 cycles of instructions (including the RJMP in the - // // ISR vector table) until the first delay. After the delay, there - // // are 17 more cycles until the pin value is read (excluding the - // // delay in the loop). - // // We want to have a total delay of 1.5 bit time. Inside the loop, - // // we already wait for 1 bit time - 23 cycles, so here we wait for - // // 0.5 bit time - (71 + 18 - 22) cycles. - // _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23)); + // When the start bit occurs, there are 3 or 4 cycles before the + // interrupt flag is set, 4 cycles before the PC is set to the right + // interrupt vector address and the old PC is pushed on the stack, + // and then 75 cycles of instructions (including the RJMP in the + // ISR vector table) until the first delay. After the delay, there + // are 17 more cycles until the pin value is read (excluding the + // delay in the loop). + // We want to have a total delay of 1.5 bit time. Inside the loop, + // we already wait for 1 bit time - 23 cycles, so here we wait for + // 0.5 bit time - (71 + 18 - 22) cycles. + // _rx_delay_centering = subtract_cap(bit_delay / 2, (77)); + _rx_delay_centering = subtract_cap(bit_delay / 2, (123)); - // // There are 23 cycles in each loop iteration (excluding the delay) + // There are 23 cycles in each loop iteration (excluding the delay) // _rx_delay_intrabit = subtract_cap(bit_delay, 23); + _rx_delay_intrabit = subtract_cap(bit_delay, 11); - // // There are 37 cycles from the last bit read to the start of - // // stopbit delay and 11 cycles from the delay until the interrupt - // // mask is enabled again (which _must_ happen during the stopbit). - // // This delay aims at 3/4 of a bit time, meaning the end of the - // // delay will be at 1/4th of the stopbit. This allows some extra - // // time for ISR cleanup, which makes 115200 baud at 16Mhz work more - // // reliably - // _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11)); - // #else // Timings counted from gcc 4.3.2 output - // // Note that this code is a _lot_ slower, mostly due to bad register - // // allocation choices of gcc. This works up to 57600 on 16Mhz and - // // 38400 on 8Mhz. - // _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11)); - // _rx_delay_intrabit = subtract_cap(bit_delay, 11); - // _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17)); - // #endif - - // Enable the INT for the entire port here, but never disable it - // (others might also need it, so we disable the interrupt by using - // the per-pin PCMSK register). - // if (_inverse_logic) - // _int_mode = GPIO_INT_MODE_RISING; // start bit: low to high - // else - // _int_mode = GPIO_INT_MODE_FALLING; // start bit: high to low - // // Save gpio_mux and gpio_line for on/off interrupts - // _int_maskMux = digitalPinToGpioIntMux(_receivePin); // gpio mux value - // _int_maskLine = digitalPinToGpioIntLine(_receivePin); // gpio irq line value - // HAL_EPIC_MaskEdgeSet(HAL_EPIC_GPIO_IRQ_MASK); // turn on the mask in EPIC - - // *digitalPinToPCICR((int8_t)_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); - // _pcint_maskreg = digitalPinToPCMSK(_receivePin); - // _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin)); + // There are 37 cycles from the last bit read to the start of + // stopbit delay and 11 cycles from the delay until the interrupt + // mask is enabled again (which _must_ happen during the stopbit). + // This delay aims at 3/4 of a bit time, meaning the end of the + // delay will be at 1/4th of the stopbit. This allows some extra + // time for ISR cleanup, which makes 115200 baud at 16Mhz work more + // reliably + // _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (48)); + _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (61)); + + // Save start bit polarity - low to high if _inverse_logic + _int_mode = _inverse_logic ? (uint8_t)GPIO_INT_MODE_RISING : (uint8_t)GPIO_INT_MODE_FALLING; + // Save gpio_mux and gpio_line for on/off interrupts + _int_maskMux = digitalPinToGpioIntMux(_receivePin); // gpio mux value + _int_maskLine = digitalPinToGpioIntLine(_receivePin); // gpio irq line value + HAL_EPIC_MaskEdgeSet(HAL_EPIC_GPIO_IRQ_MASK); // turn on the mask in EPIC } else ErrorMsgHandler("SoftwareSerial.begin(): Rx pin does not support interrupts, use different pin"); tunedDelay(_tx_delay); // if we were low this establishes the end -// #if _DEBUG -// pinMode(_DEBUG_PIN1, OUTPUT); -// pinMode(_DEBUG_PIN2, OUTPUT); -// #endif + HAL_GPIO_PinConfig(GPIO_0, GPIO_PIN_8, HAL_GPIO_MODE_GPIO_OUTPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + listen(); } @@ -348,11 +291,11 @@ void SoftwareSerial::begin(long speed) void SoftwareSerial::setRxIntMsk(bool enable) { if (enable) + // enable line interrupt HAL_GPIO_InitInterruptLine((HAL_GPIO_Line_Config)_int_maskMux, (HAL_GPIO_InterruptMode)_int_mode); - // *_pcint_maskreg |= _pcint_maskvalue; else + // disable line interrupt HAL_GPIO_DeInitInterruptLine((HAL_GPIO_Line)_int_maskLine); - // *_pcint_maskreg &= ~_pcint_maskvalue; } void SoftwareSerial::end() @@ -373,27 +316,27 @@ size_t SoftwareSerial::write(uint8_t byte) // Write the start bit if (_inverse_logic) - ((GPIO_TypeDef *)_transmitPortRegister)->SET = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); else - ((GPIO_TypeDef *)_transmitPortRegister)->CLEAR = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); tunedDelay(_tx_delay); // wait start bit // Write each of the 8 bits in LSB mode for (uint8_t i = 8; i > 0; --i) { if (byte & 1) // choose bit - ((GPIO_TypeDef *)_transmitPortRegister)->SET = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); else - ((GPIO_TypeDef *)_transmitPortRegister)->CLEAR = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); tunedDelay(_tx_delay); byte >>= 1; } // restore pin to natural state - stop bit if (_inverse_logic) - ((GPIO_TypeDef *)_transmitPortRegister)->CLEAR = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); else - ((GPIO_TypeDef *)_transmitPortRegister)->SET = (HAL_PinsTypeDef)_transmitBitMask; + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); // enable interrupts set_csr(mstatus, MSTATUS_MIE); diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.h b/libraries/SoftwareSerial/src/SoftwareSerial.h index e3bca24..cfb706d 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.h +++ b/libraries/SoftwareSerial/src/SoftwareSerial.h @@ -76,7 +76,7 @@ private: static uint32_t subtract_cap(uint32_t num, uint16_t sub); // private static method for timing - static void tunedDelay(uint32_t delayTicks) __attribute__((noinline, section(".data"))) ; + static void tunedDelay(uint32_t delayTicks) __attribute__((noinline, section(".ramfunc"))); public: // public methods @@ -96,7 +96,7 @@ public: } int peek(); - virtual size_t write(uint8_t byte) __attribute__((noinline, section(".data"))); + virtual size_t write(uint8_t byte) __attribute__((noinline, section(".ramfunc"))); virtual int read(); virtual int available(); virtual void flush();