Rewrite SerialPIO receive path, ensure proper edge (#2929)
The SerialPIO(SoftwareSerial) receive path was convoluted and required a lot of work on the host to get the actual data out. It also wasn't always sampling on the proper edge leading to errors whenever clocks or hold times shifted slightly. Rewrite the SerialPIO RX path to explicitily wait for start bit, pause 1/2 bit time, then idle for a full bit time for all bits. Takes more PIO instruction memory but works flawlessly even with mismatched clocks. Tested with a loopback from HW UART to SW UART with a 5% clock mismatch @ 19200 baud without reception errors, whereas the original code would fail with less than a 0.5% variation. Fixes #2928 ```` SoftwareSerial s(15, -1); void setup() { Serial1.setTX(0); Serial1.begin(19200 + 1920/2, SERIAL_8N1); s.begin(19200, SERIAL_8N1); } void loop() { Serial.println("---"); Serial1.write("Had we but world enough and time", 32); uint32_t now = millis(); while (millis() - now < 500) { while (s.available()) { auto c = s.read(); Serial.printf("%02x '%c'\n", c, c); } } } ````
This commit is contained in:
parent
8d58a9207f
commit
5e74bbbbb2
3 changed files with 27 additions and 22 deletions
|
|
@ -69,7 +69,7 @@ static PIOProgram *_getRxProgram(int bits) {
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
// TODO - this works, but there must be a faster/better way...
|
||||
static int _parity(int bits, int data) {
|
||||
static int __not_in_flash_func(_parity)(int bits, int data) {
|
||||
int p = 0;
|
||||
for (int b = 0; b < bits; b++) {
|
||||
p ^= (data & (1 << b)) ? 1 : 0;
|
||||
|
|
@ -98,11 +98,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
|
|||
}
|
||||
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
|
||||
uint32_t decode = _rxPIO->rxf[_rxSM];
|
||||
decode >>= 33 - _rxBits;
|
||||
uint32_t val = 0;
|
||||
for (int b = 0; b < _bits + 1; b++) {
|
||||
val |= (decode & (1 << (b * 2))) ? 1 << b : 0;
|
||||
}
|
||||
uint32_t val = decode >> (32 - _rxBits - 1);
|
||||
if (_parity == UART_PARITY_EVEN) {
|
||||
int p = ::_parity(_bits, val);
|
||||
int r = (val & (1 << _bits)) ? 1 : 0;
|
||||
|
|
@ -234,7 +230,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
|||
_writer = 0;
|
||||
_reader = 0;
|
||||
|
||||
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
|
||||
_rxBits = _bits + (_parity != UART_PARITY_NONE ? 1 : 0);
|
||||
_rxPgm = _getRxProgram(_rxBits);
|
||||
int off;
|
||||
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off, _rx, 1)) {
|
||||
|
|
@ -249,7 +245,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
|
|||
pio_sm_clear_fifos(_rxPIO, _rxSM); // Remove any existing data
|
||||
|
||||
// Put phase divider into OSR w/o using add'l program memory
|
||||
pio_sm_put_blocking(_rxPIO, _rxSM, clock_get_hz(clk_sys) / (_baud * 2) - 7 /* insns in PIO halfbit loop */);
|
||||
pio_sm_put_blocking(_rxPIO, _rxSM, clock_get_hz(clk_sys) / (_baud * 2) - 3);
|
||||
pio_sm_exec(_rxPIO, _rxSM, pio_encode_pull(false, false));
|
||||
|
||||
// Join the TX FIFO to the RX one now that we don't need it
|
||||
|
|
|
|||
|
|
@ -73,20 +73,25 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
|||
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
|
||||
|
||||
start:
|
||||
set x, 18 ; Preload bit counter...we'll shift in the start bit and stop bit, and each bit will be double-recorded (to be fixed by RP2040 code)
|
||||
set x, 18 ; Preload bit counter...overwritten by the app
|
||||
wait 0 pin 0 ; Stall until start bit is asserted
|
||||
|
||||
bitloop:
|
||||
; Delay until 1/2 way into the bit time
|
||||
mov y, osr
|
||||
wait_half:
|
||||
jmp y-- wait_half
|
||||
wait_mid_start:
|
||||
jmp y-- wait_mid_start
|
||||
|
||||
; Read in the bit
|
||||
in pins, 1 ; Shift data bit into ISR
|
||||
jmp x-- bitloop ; Loop all bits
|
||||
|
||||
push ; Stuff it and wait for next start
|
||||
bitloop:
|
||||
mov y, osr
|
||||
bitloop1:
|
||||
jmp y-- bitloop1
|
||||
mov y, osr
|
||||
bitloop2:
|
||||
jmp y-- bitloop2
|
||||
|
||||
in pins, 1
|
||||
jmp x-- bitloop
|
||||
push
|
||||
|
||||
% c-sdk {
|
||||
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_t
|
|||
// ------ //
|
||||
|
||||
#define pio_rx_wrap_target 0
|
||||
#define pio_rx_wrap 6
|
||||
#define pio_rx_wrap 10
|
||||
#define pio_rx_pio_version 0
|
||||
|
||||
static const uint16_t pio_rx_program_instructions[] = {
|
||||
|
|
@ -80,16 +80,20 @@ static const uint16_t pio_rx_program_instructions[] = {
|
|||
0x2020, // 1: wait 0 pin, 0
|
||||
0xa047, // 2: mov y, osr
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x4001, // 4: in pins, 1
|
||||
0x0042, // 5: jmp x--, 2
|
||||
0x8020, // 6: push block
|
||||
0xa047, // 4: mov y, osr
|
||||
0x0085, // 5: jmp y--, 5
|
||||
0xa047, // 6: mov y, osr
|
||||
0x0087, // 7: jmp y--, 7
|
||||
0x4001, // 8: in pins, 1
|
||||
0x0044, // 9: jmp x--, 4
|
||||
0x8020, // 10: push block
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pio_rx_program = {
|
||||
.instructions = pio_rx_program_instructions,
|
||||
.length = 7,
|
||||
.length = 11,
|
||||
.origin = -1,
|
||||
.pio_version = 0,
|
||||
#if PICO_PIO_VERSION > 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue