From aa1163f8598b7c71dd4a317d7e3aed303122fe41 Mon Sep 17 00:00:00 2001 From: klassents Date: Tue, 17 Sep 2024 17:09:11 +0700 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B8=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=BE=D1=80=D0=BC=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=82=D0=B0=D0=B9=D0=BC=D0=B8=D0=BD=D0=B3=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B8=20=D0=BD=D0=BE=D1=80=D0=BC=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=20=D1=81=D1=82=D0=B0=D1=80=D1=82-=D0=B1=D0=B8=D1=82=D0=B0,=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E=D1=82=20=D0=B1=D0=B8?= =?UTF-8?q?=D1=82=D1=80=D0=B5=D0=B9=D1=82=D1=8B=20=D0=B2=D0=BF=D0=BB=D0=BE?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B4=D0=BE=20115200,=20=D1=82=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=D0=BD=D0=B0=20115200=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D1=8B=D0=B9=20=D0=B1=D0=B0=D0=B9=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=20=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D1=87=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=D1=81=D1=8F=20=D0=BA=D1=80=D0=B8=D0=B2=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SoftwareSerial/src/SoftwareSerial.cpp | 336 ++++++++---------- libraries/SoftwareSerial/src/SoftwareSerial.h | 6 +- 2 files changed, 154 insertions(+), 188 deletions(-) diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.cpp b/libraries/SoftwareSerial/src/SoftwareSerial.cpp index 98709da..aa15f0f 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.cpp +++ b/libraries/SoftwareSerial/src/SoftwareSerial.cpp @@ -32,7 +32,7 @@ http://arduiniana.org. // #include #include #include -#include "mik32_hal_irq.h" +// #include "mik32_hal_irq.h" #include "mik32_hal_gpio.h" // @@ -69,10 +69,13 @@ volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; // /* static */ -inline void SoftwareSerial::tunedDelay(uint16_t delayTicks) +#include "mik32_hal_scr1_timer.h" +#define CURRENT_TICKS ((uint64_t)(SCR1_TIMER->MTIMEH)<<32 | SCR1_TIMER->MTIME) + +void SoftwareSerial::tunedDelay(uint32_t delayTicks) { - uint64_t startTicks = SysTick_GetTicks(); - while ((SysTick_GetTicks() - startTicks) < delayTicks) + uint64_t startTicks = CURRENT_TICKS; + while ((CURRENT_TICKS - startTicks) < delayTicks) ; } @@ -116,88 +119,58 @@ bool SoftwareSerial::stopListening() // void SoftwareSerial::recv() { - -// #if GCC_VERSION < 40302 -// // Work-around for avr-gcc 4.3.0 OSX version bug -// // Preserve the registers that the compiler misses -// // (courtesy of Arduino forum user *etracer*) -// asm volatile( -// "push r18 \n\t" -// "push r19 \n\t" -// "push r20 \n\t" -// "push r21 \n\t" -// "push r22 \n\t" -// "push r23 \n\t" -// "push r26 \n\t" -// "push r27 \n\t" -// ::); -// #endif - - // 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()) + // if (active_object) // { - // // Disable further interrupts during reception, this prevents - // // triggering another interrupt directly after we return, which can - // // cause problems at higher baudrates. - // setRxIntMsk(false); + // uint8_t data = 0; - // // 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) + // // 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()) // { - // tunedDelay(_rx_delay_intrabit); - // data >>= 1; - // DebugPulse(_DEBUG_PIN2, 1); - // if (rx_pin_read()) - // data |= 0x80; + // // 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); + + // // 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 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); + + // // Re-enable interrupts when we're sure to be inside the stop bit + // setRxIntMsk(true); // } - - // 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; - // } - - // // 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); - // } - -// #if GCC_VERSION < 40302 -// // Work-around for avr-gcc 4.3.0 OSX version bug -// // Restore the registers that the compiler misses -// asm volatile( -// "pop r27 \n\t" -// "pop r26 \n\t" -// "pop r23 \n\t" -// "pop r22 \n\t" -// "pop r21 \n\t" -// "pop r20 \n\t" -// "pop r19 \n\t" -// "pop r18 \n\t" -// ::); -// #endif } uint8_t SoftwareSerial::rx_pin_read() @@ -259,15 +232,17 @@ void SoftwareSerial::setTX(uint8_t tx) void SoftwareSerial::setRX(uint8_t rx) { - if (!_inverse_logic) - pinMode(rx, INPUT_PULLUP); // pullup for normal logic - else - pinMode(rx, INPUT); _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); } -uint16_t SoftwareSerial::subtract_cap(uint16_t num, uint16_t sub) +uint32_t SoftwareSerial::subtract_cap(uint32_t num, uint16_t sub) { if (num > sub) return num - sub; @@ -292,74 +267,75 @@ 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) - _tx_delay = subtract_cap(bit_delay, 15); + _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)); + // #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. - // There are 23 cycles in each loop iteration (excluding the delay) - _rx_delay_intrabit = subtract_cap(bit_delay, 23); + // _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23)); - // 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 + // // There are 23 cycles in each loop iteration (excluding the delay) + // _rx_delay_intrabit = subtract_cap(bit_delay, 23); + + // // 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 + // 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)); - - tunedDelay(_tx_delay); // if we were low this establishes the end } 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); @@ -383,6 +359,50 @@ void SoftwareSerial::end() stopListening(); } +size_t SoftwareSerial::write(uint8_t byte) +{ + if (_tx_delay == 0) + { + setWriteError(); + return 0; + } + + if (_inverse_logic) + byte = ~byte; + + noInterrupts(); // turn off interrupts for a clean txmit + + // Write the start bit + if (_inverse_logic) + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_HIGH); + else + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_LOW); + + tunedDelay(_tx_delay); + + // Write each of the 8 bits + for (uint8_t i = 8; i > 0; --i) + { + if (byte & 1) // choose bit + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_HIGH); + else + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_LOW); + + tunedDelay(_tx_delay); + byte >>= 1; + } + + // restore pin to natural state + if (_inverse_logic) + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_LOW); + else + HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask, GPIO_PIN_HIGH); + + interrupts(); // enable interrupts + tunedDelay(_tx_delay); + + return 1; +} // Read data from buffer int SoftwareSerial::read() @@ -408,60 +428,6 @@ int SoftwareSerial::available() return ((unsigned int)(_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head)) % _SS_MAX_RX_BUFF; } -size_t SoftwareSerial::write(uint8_t byte) -{ - if (_tx_delay == 0) - { - setWriteError(); - return 0; - } - - // By declaring these as local variables, the compiler will put them - // in registers _before_ disabling interrupts and entering the - // critical timing sections below, which makes it a lot easier to - // verify the cycle timings - uint8_t bit_mask = _transmitBitMask; // битовая маска в порту - - bool inv = _inverse_logic; - uint16_t delay = _tx_delay; - - if (inv) - byte = ~byte; - - noInterrupts(); // turn off interrupts for a clean txmit - - // Write the start bit - if (inv) - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_HIGH); - else - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_LOW); - - tunedDelay(delay); - - // Write each of the 8 bits - for (uint8_t i = 8; i > 0; --i) - { - if (byte & 1) // choose bit - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_HIGH); - else - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_LOW); - - tunedDelay(delay); - byte >>= 1; - } - - // restore pin to natural state - if (inv) - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_LOW); - else - HAL_GPIO_WritePin((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)bit_mask, GPIO_PIN_HIGH); - - interrupts(); // enable interrupts - tunedDelay(_tx_delay); - - return 1; -} - void SoftwareSerial::flush() { // There is no tx buffering, simply return diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.h b/libraries/SoftwareSerial/src/SoftwareSerial.h index 7d89f40..fa9970f 100644 --- a/libraries/SoftwareSerial/src/SoftwareSerial.h +++ b/libraries/SoftwareSerial/src/SoftwareSerial.h @@ -54,7 +54,7 @@ private: uint16_t _rx_delay_centering; uint16_t _rx_delay_intrabit; uint16_t _rx_delay_stopbit; - uint16_t _tx_delay; + uint32_t _tx_delay; uint16_t _buffer_overflow:1; uint16_t _inverse_logic:1; @@ -73,10 +73,10 @@ private: inline void setRxIntMsk(bool enable) __attribute__((__always_inline__)); // Return num - sub, or 1 if the result would be < 1 - static uint16_t subtract_cap(uint16_t num, uint16_t sub); + static uint32_t subtract_cap(uint32_t num, uint16_t sub); // private static method for timing - static inline void tunedDelay(uint16_t delayTicks); + static void __attribute__( ( noinline, section(".data") ) ) tunedDelay(uint32_t delayTicks); public: // public methods