добилась нормальных таймингов и нормального размера старт-бита, работают битрейты вплоть до 115200, только на 115200 первый байт после инициализации читается криво

This commit is contained in:
klassents committed 2024-09-17 17:09:11 +07:00
1 parent 7f0b704b05
commit aa1163f859
2 files changed
+153 -187

No files matched your search

+150 -184
View File
@@ -32,7 +32,7 @@ http://arduiniana.org.
// #include <avr/pgmspace.h> // #include <avr/pgmspace.h>
#include <Arduino.h> #include <Arduino.h>
#include <SoftwareSerial.h> #include <SoftwareSerial.h>
#include "mik32_hal_irq.h" // #include "mik32_hal_irq.h"
#include "mik32_hal_gpio.h" #include "mik32_hal_gpio.h"
// //
@@ -69,10 +69,13 @@ volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
// //
/* static */ /* 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(); uint64_t startTicks = CURRENT_TICKS;
while ((SysTick_GetTicks() - startTicks) < delayTicks) while ((CURRENT_TICKS - startTicks) < delayTicks)
; ;
} }
@@ -116,88 +119,58 @@ bool SoftwareSerial::stopListening()
// //
void SoftwareSerial::recv() void SoftwareSerial::recv()
{ {
// if (active_object)
// #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())
// { // {
// // Disable further interrupts during reception, this prevents // uint8_t data = 0;
// // 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 // // If RX line is high, then we don't see any start bit
// tunedDelay(_rx_delay_centering); // // so interrupt is probably not for us
// DebugPulse(_DEBUG_PIN2, 1); // if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
// // Read each of the 8 bits
// for (uint8_t i=8; i > 0; --i)
// { // {
// tunedDelay(_rx_delay_intrabit); // // Disable further interrupts during reception, this prevents
// data >>= 1; // // triggering another interrupt directly after we return, which can
// DebugPulse(_DEBUG_PIN2, 1); // // cause problems at higher baudrates.
// if (rx_pin_read()) // setRxIntMsk(false);
// data |= 0x80;
// // 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() uint8_t SoftwareSerial::rx_pin_read()
@@ -259,15 +232,17 @@ void SoftwareSerial::setTX(uint8_t tx)
void SoftwareSerial::setRX(uint8_t rx) 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 _receiveBitMask = (uint16_t)digitalPinToBitMask(rx); // save rx bit mask
_receivePortRegister = (uint32_t)digitalPinToPort(rx); // save rx port adress _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) if (num > sub)
return num - sub; return num - sub;
@@ -298,68 +273,69 @@ void SoftwareSerial::begin(long speed)
// 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit // 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 // These are all close enough to just use 15 cycles, since the inter-bit
// timings are the most critical (deviations stack 8 times) // 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 // Only setup rx when we have a valid INT for this pin
if (digitalPinToInterrupt(_receivePin) != NOT_AN_INTERRUPT) if (digitalPinToInterrupt(_receivePin) != NOT_AN_INTERRUPT)
{ {
#if GCC_VERSION > 40800 // #if GCC_VERSION > 40800
// Timings counted from gcc 4.8.2 output. This works up to 115200 on // // Timings counted from gcc 4.8.2 output. This works up to 115200 on
// 16Mhz and 57600 on 8Mhz. // // 16Mhz and 57600 on 8Mhz.
// // //
// When the start bit occurs, there are 3 or 4 cycles before the // // 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 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, // // interrupt vector address and the old PC is pushed on the stack,
// and then 75 cycles of instructions (including the RJMP in the // // and then 75 cycles of instructions (including the RJMP in the
// ISR vector table) until the first delay. After the delay, there // // ISR vector table) until the first delay. After the delay, there
// are 17 more cycles until the pin value is read (excluding the // // are 17 more cycles until the pin value is read (excluding the
// delay in the loop). // // delay in the loop).
// We want to have a total delay of 1.5 bit time. Inside 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 // // we already wait for 1 bit time - 23 cycles, so here we wait for
// 0.5 bit time - (71 + 18 - 22) cycles. // // 0.5 bit time - (71 + 18 - 22) cycles.
_rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23));
// There are 23 cycles in each loop iteration (excluding the delay) // _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23));
_rx_delay_intrabit = subtract_cap(bit_delay, 23);
// There are 37 cycles from the last bit read to the start of // // There are 23 cycles in each loop iteration (excluding the delay)
// stopbit delay and 11 cycles from the delay until the interrupt // _rx_delay_intrabit = subtract_cap(bit_delay, 23);
// 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 // // There are 37 cycles from the last bit read to the start of
// delay will be at 1/4th of the stopbit. This allows some extra // // stopbit delay and 11 cycles from the delay until the interrupt
// time for ISR cleanup, which makes 115200 baud at 16Mhz work more // // mask is enabled again (which _must_ happen during the stopbit).
// reliably // // This delay aims at 3/4 of a bit time, meaning the end of the
_rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11)); // // delay will be at 1/4th of the stopbit. This allows some extra
#else // Timings counted from gcc 4.3.2 output // // time for ISR cleanup, which makes 115200 baud at 16Mhz work more
// Note that this code is a _lot_ slower, mostly due to bad register // // reliably
// allocation choices of gcc. This works up to 57600 on 16Mhz and // _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11));
// 38400 on 8Mhz. // #else // Timings counted from gcc 4.3.2 output
_rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11)); // // Note that this code is a _lot_ slower, mostly due to bad register
_rx_delay_intrabit = subtract_cap(bit_delay, 11); // // allocation choices of gcc. This works up to 57600 on 16Mhz and
_rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17)); // // 38400 on 8Mhz.
#endif // _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 // Enable the INT for the entire port here, but never disable it
// (others might also need it, so we disable the interrupt by using // (others might also need it, so we disable the interrupt by using
// the per-pin PCMSK register). // the per-pin PCMSK register).
if (_inverse_logic) // if (_inverse_logic)
_int_mode = GPIO_INT_MODE_RISING; // start bit: low to high // _int_mode = GPIO_INT_MODE_RISING; // start bit: low to high
else // else
_int_mode = GPIO_INT_MODE_FALLING; // start bit: high to low // _int_mode = GPIO_INT_MODE_FALLING; // start bit: high to low
// Save gpio_mux and gpio_line for on/off interrupts // // Save gpio_mux and gpio_line for on/off interrupts
_int_maskMux = digitalPinToGpioIntMux(_receivePin); // gpio mux value // _int_maskMux = digitalPinToGpioIntMux(_receivePin); // gpio mux value
_int_maskLine = digitalPinToGpioIntLine(_receivePin); // gpio irq line value // _int_maskLine = digitalPinToGpioIntLine(_receivePin); // gpio irq line value
HAL_EPIC_MaskEdgeSet(HAL_EPIC_GPIO_IRQ_MASK); // turn on the mask in EPIC // HAL_EPIC_MaskEdgeSet(HAL_EPIC_GPIO_IRQ_MASK); // turn on the mask in EPIC
// *digitalPinToPCICR((int8_t)_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin)); // *digitalPinToPCICR((int8_t)_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));
// _pcint_maskreg = digitalPinToPCMSK(_receivePin); // _pcint_maskreg = digitalPinToPCMSK(_receivePin);
// _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin)); // _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin));
tunedDelay(_tx_delay); // if we were low this establishes the end
} }
else else
ErrorMsgHandler("SoftwareSerial.begin(): Rx pin does not support interrupts, use different pin"); 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 // #if _DEBUG
// pinMode(_DEBUG_PIN1, OUTPUT); // pinMode(_DEBUG_PIN1, OUTPUT);
// pinMode(_DEBUG_PIN2, OUTPUT); // pinMode(_DEBUG_PIN2, OUTPUT);
@@ -383,6 +359,50 @@ void SoftwareSerial::end()
stopListening(); 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 // Read data from buffer
int SoftwareSerial::read() 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; 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() void SoftwareSerial::flush()
{ {
// There is no tx buffering, simply return // There is no tx buffering, simply return
@@ -54,7 +54,7 @@ private:
uint16_t _rx_delay_centering; uint16_t _rx_delay_centering;
uint16_t _rx_delay_intrabit; uint16_t _rx_delay_intrabit;
uint16_t _rx_delay_stopbit; uint16_t _rx_delay_stopbit;
uint16_t _tx_delay; uint32_t _tx_delay;
uint16_t _buffer_overflow:1; uint16_t _buffer_overflow:1;
uint16_t _inverse_logic:1; uint16_t _inverse_logic:1;
@@ -73,10 +73,10 @@ private:
inline void setRxIntMsk(bool enable) __attribute__((__always_inline__)); inline void setRxIntMsk(bool enable) __attribute__((__always_inline__));
// Return num - sub, or 1 if the result would be < 1 // 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 // private static method for timing
static inline void tunedDelay(uint16_t delayTicks); static void __attribute__( ( noinline, section(".data") ) ) tunedDelay(uint32_t delayTicks);
public: public:
// public methods // public methods