Skip to content

Commit 226a318

Browse files
Add serial inversion for UART and SerialPIO (#2395)
Use real GPIO pad inversion to allow inverted RX, TX, and controls for the hardware UART and software PIO-emulated serial ports. Adds ``setInvertTX(bool)`` and ``setInvertRX(bool)`` calls to both ports, with ``setInvertControl(bool)`` for the HW UARTS.
1 parent 729163d commit 226a318

File tree

10 files changed

+89
-153
lines changed

10 files changed

+89
-153
lines changed

cores/rp2040/SerialPIO.cpp

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,22 @@ static pio_program_t *pio_make_uart_prog(int repl, const pio_program_t *pg) {
4646
return p;
4747
}
4848

49-
static PIOProgram *_getTxProgram(int bits, bool inverted) {
50-
int key = inverted ? -bits : bits;
51-
auto f = _txMap.find(key);
49+
static PIOProgram *_getTxProgram(int bits) {
50+
auto f = _txMap.find(bits);
5251
if (f == _txMap.end()) {
53-
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_tx_inv_program : &pio_tx_program);
54-
_txMap.insert({key, new PIOProgram(p)});
55-
f = _txMap.find(key);
52+
pio_program_t * p = pio_make_uart_prog(bits, &pio_tx_program);
53+
_txMap.insert({bits, new PIOProgram(p)});
54+
f = _txMap.find(bits);
5655
}
5756
return f->second;
5857
}
5958

60-
static PIOProgram *_getRxProgram(int bits, bool inverted) {
61-
int key = inverted ? -bits : bits;
62-
auto f = _rxMap.find(key);
59+
static PIOProgram *_getRxProgram(int bits) {
60+
auto f = _rxMap.find(bits);
6361
if (f == _rxMap.end()) {
64-
pio_program_t * p = pio_make_uart_prog(bits, inverted ? &pio_rx_inv_program : &pio_rx_program);
65-
_rxMap.insert({key, new PIOProgram(p)});
66-
f = _rxMap.find(key);
62+
pio_program_t * p = pio_make_uart_prog(bits, &pio_rx_program);
63+
_rxMap.insert({bits, new PIOProgram(p)});
64+
f = _rxMap.find(bits);
6765
}
6866
return f->second;
6967
}
@@ -98,7 +96,7 @@ void __not_in_flash_func(SerialPIO::_handleIRQ)() {
9896
return;
9997
}
10098
while (!pio_sm_is_rx_fifo_empty(_rxPIO, _rxSM)) {
101-
uint32_t decode = _rxPIO->rxf[_rxSM] ^ (_rxInverted ? 0xffffffff : 0);
99+
uint32_t decode = _rxPIO->rxf[_rxSM];
102100
decode >>= 33 - _rxBits;
103101
uint32_t val = 0;
104102
for (int b = 0; b < _bits + 1; b++) {
@@ -140,6 +138,8 @@ SerialPIO::SerialPIO(pin_size_t tx, pin_size_t rx, size_t fifoSize) {
140138
_fifoSize = fifoSize + 1; // Always one unused entry
141139
_queue = new uint8_t[_fifoSize];
142140
mutex_init(&_mutex);
141+
_invertTX = false;
142+
_invertRX = false;
143143
}
144144

145145
SerialPIO::~SerialPIO() {
@@ -191,7 +191,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
191191

192192
if (_tx != NOPIN) {
193193
_txBits = _bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1/*start bit*/;
194-
_txPgm = _getTxProgram(_txBits, _txInverted);
194+
_txPgm = _getTxProgram(_txBits);
195195
int off;
196196
if (!_txPgm->prepare(&_txPIO, &_txSM, &off)) {
197197
DEBUGCORE("ERROR: Unable to allocate PIO TX UART, out of PIO resources\n");
@@ -201,6 +201,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
201201

202202
digitalWrite(_tx, HIGH);
203203
pinMode(_tx, OUTPUT);
204+
gpio_set_outover(_tx, _invertTX);
204205

205206
pio_tx_program_init(_txPIO, _txSM, off, _tx);
206207
pio_sm_clear_fifos(_txPIO, _txSM); // Remove any existing data
@@ -218,7 +219,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
218219
_reader = 0;
219220

220221
_rxBits = 2 * (_bits + _stop + (_parity != UART_PARITY_NONE ? 1 : 0) + 1) - 1;
221-
_rxPgm = _getRxProgram(_rxBits, _rxInverted);
222+
_rxPgm = _getRxProgram(_rxBits);
222223
int off;
223224
if (!_rxPgm->prepare(&_rxPIO, &_rxSM, &off)) {
224225
DEBUGCORE("ERROR: Unable to allocate PIO RX UART, out of PIO resources\n");
@@ -249,6 +250,7 @@ void SerialPIO::begin(unsigned long baud, uint16_t config) {
249250
irq_set_exclusive_handler(irqno, _fifoIRQ);
250251
irq_set_enabled(irqno, true);
251252

253+
gpio_set_inover(_rx, _invertRX);
252254
pio_sm_set_enabled(_rxPIO, _rxSM, true);
253255
}
254256

@@ -262,6 +264,7 @@ void SerialPIO::end() {
262264
if (_tx != NOPIN) {
263265
pio_sm_set_enabled(_txPIO, _txSM, false);
264266
pio_sm_unclaim(_txPIO, _txSM);
267+
gpio_set_outover(_tx, 0);
265268
}
266269
if (_rx != NOPIN) {
267270
pio_sm_set_enabled(_rxPIO, _rxSM, false);
@@ -277,6 +280,7 @@ void SerialPIO::end() {
277280
auto irqno = pioNum == 0 ? PIO0_IRQ_0 : PIO1_IRQ_0;
278281
irq_set_enabled(irqno, false);
279282
}
283+
gpio_set_inover(_rx, 0);
280284
}
281285
_running = false;
282286
}
@@ -348,11 +352,6 @@ void SerialPIO::flush() {
348352
delay((1000 * (_txBits + 1)) / _baud);
349353
}
350354

351-
void SerialPIO::setInverted(bool invTx, bool invRx) {
352-
_txInverted = invTx;
353-
_rxInverted = invRx;
354-
}
355-
356355
size_t SerialPIO::write(uint8_t c) {
357356
CoreMutex m(&_mutex);
358357
if (!_running || !m || (_tx == NOPIN)) {
@@ -371,7 +370,7 @@ size_t SerialPIO::write(uint8_t c) {
371370
}
372371
val <<= 1; // Start bit = low
373372

374-
pio_sm_put_blocking(_txPIO, _txSM, _txInverted ? ~val : val);
373+
pio_sm_put_blocking(_txPIO, _txSM, val);
375374

376375
return 1;
377376
}

cores/rp2040/SerialPIO.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,22 @@ class SerialPIO : public HardwareSerial {
4141
void begin(unsigned long baud, uint16_t config) override;
4242
void end() override;
4343

44-
void setInverted(bool invTx = true, bool invRx = true);
44+
void setInverted(bool invTx = true, bool invRx = true) {
45+
setInvertTX(invTx);
46+
setInvertRX(invRx);
47+
}
48+
bool setInvertTX(bool invert = true) {
49+
if (!_running) {
50+
_invertTX = invert;
51+
}
52+
return !_running;
53+
}
54+
bool setInvertRX(bool invert = true) {
55+
if (!_running) {
56+
_invertRX = invert;
57+
}
58+
return !_running;
59+
}
4560

4661
virtual int peek() override;
4762
virtual int read() override;
@@ -65,8 +80,8 @@ class SerialPIO : public HardwareSerial {
6580
int _stop;
6681
bool _overflow;
6782
mutex_t _mutex;
68-
bool _txInverted = false;
69-
bool _rxInverted = false;
83+
bool _invertTX;
84+
bool _invertRX;
7085

7186
PIOProgram *_txPgm;
7287
PIO _txPIO;

cores/rp2040/SerialUART.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx, pin_size
138138
_cts = cts;
139139
mutex_init(&_mutex);
140140
mutex_init(&_fifoMutex);
141+
_invertTX = false;
142+
_invertRX = false;
143+
_invertControl = false;
141144
}
142145

143146
static void _uart0IRQ();
@@ -154,14 +157,18 @@ void SerialUART::begin(unsigned long baud, uint16_t config) {
154157
_fcnTx = gpio_get_function(_tx);
155158
_fcnRx = gpio_get_function(_rx);
156159
gpio_set_function(_tx, GPIO_FUNC_UART);
160+
gpio_set_outover(_tx, _invertTX ? 1 : 0);
157161
gpio_set_function(_rx, GPIO_FUNC_UART);
162+
gpio_set_inover(_rx, _invertRX ? 1 : 0);
158163
if (_rts != UART_PIN_NOT_DEFINED) {
159164
_fcnRts = gpio_get_function(_rts);
160165
gpio_set_function(_rts, GPIO_FUNC_UART);
166+
gpio_set_outover(_rts, _invertControl ? 1 : 0);
161167
}
162168
if (_cts != UART_PIN_NOT_DEFINED) {
163169
_fcnCts = gpio_get_function(_cts);
164170
gpio_set_function(_cts, GPIO_FUNC_UART);
171+
gpio_set_inover(_cts, _invertControl ? 1 : 0);
165172
}
166173

167174
uart_init(_uart, baud);
@@ -246,12 +253,16 @@ void SerialUART::end() {
246253

247254
// Restore pin functions
248255
gpio_set_function(_tx, _fcnTx);
256+
gpio_set_outover(_tx, 0);
249257
gpio_set_function(_rx, _fcnRx);
258+
gpio_set_inover(_rx, 0);
250259
if (_rts != UART_PIN_NOT_DEFINED) {
251260
gpio_set_function(_rts, _fcnRts);
261+
gpio_set_outover(_rts, 0);
252262
}
253263
if (_cts != UART_PIN_NOT_DEFINED) {
254264
gpio_set_function(_cts, _fcnCts);
265+
gpio_set_inover(_cts, 0);
255266
}
256267
}
257268

cores/rp2040/SerialUART.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ class SerialUART : public HardwareSerial {
4343
ret &= setTX(tx);
4444
return ret;
4545
}
46+
47+
bool setInvertTX(bool invert = true) {
48+
if (!_running) {
49+
_invertTX = invert;
50+
}
51+
return !_running;
52+
}
53+
bool setInvertRX(bool invert = true) {
54+
if (!_running) {
55+
_invertRX = invert;
56+
}
57+
return !_running;
58+
}
59+
bool setInvertControl(bool invert = true) {
60+
if (!_running) {
61+
_invertControl = invert;
62+
}
63+
return !_running;
64+
}
65+
4666
bool setFIFOSize(size_t size);
4767
bool setPollingMode(bool mode = true);
4868

@@ -86,6 +106,7 @@ class SerialUART : public HardwareSerial {
86106
bool _polling = false;
87107
bool _overflow;
88108
bool _break;
109+
bool _invertTX, _invertRX, _invertControl;
89110

90111
// Lockless, IRQ-handled circular queue
91112
uint32_t _writer;

cores/rp2040/SoftwareSerial.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,16 @@ class SoftwareSerial : public SerialPIO {
3030
}
3131

3232
~SoftwareSerial() {
33-
if (_invert) {
34-
gpio_set_outover(_tx, 0);
35-
gpio_set_outover(_rx, 0);
36-
}
3733
}
3834

3935
virtual void begin(unsigned long baud = 115200) override {
4036
begin(baud, SERIAL_8N1);
4137
};
4238

4339
void begin(unsigned long baud, uint16_t config) override {
40+
setInvertTX(invert);
41+
setInvertRX(invert);
4442
SerialPIO::begin(baud, config);
45-
if (_invert) {
46-
gpio_set_outover(_tx, GPIO_OVERRIDE_INVERT);
47-
gpio_set_inover(_rx, GPIO_OVERRIDE_INVERT);
48-
}
4943
}
5044

5145
void listen() { /* noop */ }

cores/rp2040/pio_uart.pio

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,6 @@ wait_bit:
3939
jmp y-- wait_bit
4040
jmp x-- bitloop
4141

42-
43-
44-
; inverted-logic version (inverts the stop bit)
45-
46-
.program pio_tx_inv
47-
.side_set 1 opt
48-
49-
50-
; We shift out the start and stop bit as part of the FIFO
51-
set x, 9
52-
53-
pull side 0 ; Force stop bit low
54-
55-
; Send the bits
56-
bitloop:
57-
out pins, 1
58-
mov y, isr ; ISR is loaded by the setup routine with the period-1 count
59-
wait_bit:
60-
jmp y-- wait_bit
61-
jmp x-- bitloop
62-
63-
6442
% c-sdk {
6543

6644
static inline void pio_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx) {
@@ -110,28 +88,6 @@ wait_half:
11088

11189
push ; Stuff it and wait for next start
11290

113-
114-
.program pio_rx_inv
115-
116-
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
117-
118-
start:
119-
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)
120-
wait 1 pin 0 ; Stall until start bit is asserted
121-
122-
bitloop:
123-
; Delay until 1/2 way into the bit time
124-
mov y, osr
125-
wait_half:
126-
jmp y-- wait_half
127-
128-
; Read in the bit
129-
in pins, 1 ; Shift data bit into ISR
130-
jmp x-- bitloop ; Loop all bits
131-
132-
push ; Stuff it and wait for next start
133-
134-
13591
% c-sdk {
13692
static inline void pio_rx_program_init(PIO pio, uint sm, uint offset, uint pin) {
13793
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);

0 commit comments

Comments
 (0)