Skip to content

Commit 9aa2843

Browse files
committed
Add support for hardware flow control.
1 parent 66046da commit 9aa2843

File tree

1 file changed

+150
-63
lines changed

1 file changed

+150
-63
lines changed

adafruit_pio_uart.py

Lines changed: 150 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class UART:
3333
Parity = busio.UART.Parity
3434

3535
def __init__(
36-
self, tx=None, rx=None, baudrate=9600, bits=8, parity=None, stop=1, timeout=1
36+
self, tx=None, rx=None, baudrate=9600, bits=8, parity=None, stop=1, timeout=1, cts=None, rts=None
3737
): # pylint: disable=invalid-name, too-many-arguments
3838
self.bitcount = bits + (1 if parity else 0)
3939
self.bits = bits
@@ -43,72 +43,159 @@ def __init__(
4343
self._timeout = timeout
4444
self.rx_pio = None
4545
if rx:
46-
# Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
47-
# with the correct timing.
48-
# IN pin 0 is mapped to the GPIO used as UART RX.
49-
# Autopush must be enabled, with a threshold of 8.
50-
51-
# Line by line explanation:
52-
# * Wait for start bit
53-
# * Preload bit counter, delay until eye of first data bit
54-
# * Loop 8 times
55-
# * Sample data
56-
# * Each iteration is 8 cycles
57-
rx_code = adafruit_pioasm.assemble(
58-
".program uart_rx_mini\n"
59-
+ "start:\n"
60-
+ " wait 0 pin 0\n"
61-
+ f" set x, {self.bitcount - 1} [10]\n"
62-
+ "bitloop:\n"
63-
+ " in pins, 1\n"
64-
+ " jmp x-- bitloop [6]\n"
65-
+ " jmp pin good_stop\n"
66-
+ " wait 1 pin 0\n" # Skip IRQ
67-
+ " jmp start\n"
68-
+ "good_stop:\n"
69-
+ " push\n"
70-
)
71-
self.rx_pio = rp2pio.StateMachine(
72-
rx_code,
73-
first_in_pin=rx,
74-
jmp_pin=rx,
75-
frequency=8 * baudrate,
76-
auto_push=False,
77-
push_threshold=self.bitcount,
78-
)
46+
if rts:
47+
# Fleshed-out 8n1 UART receiver with hardware flow control handling
48+
# framing errors and break conditions more gracefully.
49+
# Wait for the start bit whilst updating rts with the FIFO level
50+
# then sample 8 bits with the correct timing.
51+
# IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
52+
# OUT pin 0 is mapped to the GPIO used as UART RTS# (Request To Send).
53+
54+
# Line by line explanation:
55+
# * Update rts pin with status of fifo level (high when full).
56+
# * Loop back to start whilst waiting for the start bit (low).
57+
# * Preload bit counter, then delay until eye of first data bit.
58+
# * Shift data bit into ISR
59+
# * Loop bitcount times, each loop iteration is 8 cycles.
60+
# * Jump to good_stop if there's a stop bit (high)
61+
# * otherwise wait for line to return to idle state.
62+
# * Don't push data if we didn't see good framing and start again.
63+
# * Push valid data and return to the start.
64+
rx_code = adafruit_pioasm.assemble(
65+
".program uart_rx_mini\n"
66+
+ "start:\n"
67+
+ " mov pins !status\n"
68+
+ " jmp pin start\n"
69+
+ f" set x, {self.bitcount - 1} [10]\n"
70+
+ "bitloop:\n"
71+
+ " in pins, 1\n"
72+
+ " jmp x-- bitloop [6]\n"
73+
+ " jmp pin good_stop\n"
74+
+ " wait 1 pin 0\n" # Skip IRQ
75+
+ " jmp start\n"
76+
+ "good_stop:\n"
77+
+ " push\n"
78+
)
79+
80+
# mov_status_n is set to 7 allowing 2 further
81+
# entries (8 in the joined FIFO and one in ISR).
82+
self.rx_pio = rp2pio.StateMachine(
83+
rx_code,
84+
first_in_pin=rx,
85+
jmp_pin=rx,
86+
frequency=8 * baudrate,
87+
auto_push=False,
88+
push_threshold=self.bitcount,
89+
first_out_pin=rts,
90+
mov_status_type='rxfifo',
91+
mov_status_n=7,
92+
)
93+
else:
94+
# Fleshed-out 8n1 UART receiver which handles
95+
# framing errors and break conditions more gracefully.
96+
# IN pin 0 is mapped to the GPIO used as UART RX.
97+
98+
# Line by line explanation:
99+
# * Wait for start bit
100+
# * Preload bit counter, then delay until eye of first data bit.
101+
# * Shift data bit into ISR
102+
# * Loop bitcount times, each loop iteration is 8 cycles.
103+
# * Jump to good_stop if there's a stop bit (high)
104+
# * otherwise wait for line to return to idle state.
105+
# * Don't push data if we didn't see good framing and start again.
106+
# * Push valid data and return to the start.
107+
rx_code = adafruit_pioasm.assemble(
108+
".program uart_rx_mini\n"
109+
+ "start:\n"
110+
+ " wait 0 pin 0\n"
111+
+ f" set x, {self.bitcount - 1} [10]\n"
112+
+ "bitloop:\n"
113+
+ " in pins, 1\n"
114+
+ " jmp x-- bitloop [6]\n"
115+
+ " jmp pin good_stop\n"
116+
+ " wait 1 pin 0\n" # Skip IRQ
117+
+ " jmp start\n"
118+
+ "good_stop:\n"
119+
+ " push\n"
120+
)
121+
self.rx_pio = rp2pio.StateMachine(
122+
rx_code,
123+
first_in_pin=rx,
124+
jmp_pin=rx,
125+
frequency=8 * baudrate,
126+
auto_push=False,
127+
push_threshold=self.bitcount,
128+
)
79129

80130
self.tx_pio = None
81131
if tx:
82132
stop_delay = stop * 8 - 1
83-
# An 8n1 UART transmit program.
84-
# OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
85-
86-
# Line by line explanation:
87-
# * Assert stop bit, or stall with line in idle state
88-
# * Preload bit counter, assert start bit for 8 clocks
89-
# * This loop will run 8 times (8n1 UART)
90-
# * Shift 1 bit from OSR to the first OUT pin
91-
# * Each loop iteration is 8 cycles.
92-
tx_code = adafruit_pioasm.Program(
93-
".program uart_tx\n"
94-
+ ".side_set 1 opt\n"
95-
+ f" pull side 1 [{stop_delay}]\n"
96-
+ f" set x, {self.bitcount - 1} side 0 [7]\n"
97-
+ "bitloop:\n"
98-
+ " out pins, 1\n"
99-
+ " jmp x-- bitloop [6]\n"
100-
)
101-
self.tx_pio = rp2pio.StateMachine(
102-
tx_code.assembled,
103-
first_out_pin=tx,
104-
first_sideset_pin=tx,
105-
frequency=8 * baudrate,
106-
initial_sideset_pin_state=1,
107-
initial_sideset_pin_direction=1,
108-
initial_out_pin_state=1,
109-
initial_out_pin_direction=1,
110-
**tx_code.pio_kwargs,
111-
)
133+
if cts:
134+
# An 8n1 UART transmit program with hardware flow control
135+
# OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
136+
# IN pin 0 is mapped to GPIO pin used as CTS# (Clear To Send),
137+
138+
# Line by line explanation:
139+
# * Assert stop bit, or stall with line in idle state
140+
# * Wait for CTS# before transmitting
141+
# * Preload bit counter, assert start bit for 8 clocks
142+
# * This loop will run 8 times (8n1 UART)
143+
# * Shift 1 bit from OSR to the first OUT pin
144+
# * Each loop iteration is 8 cycles.
145+
tx_code = adafruit_pioasm.Program(
146+
".program uart_tx\n"
147+
+ ".side_set 1 opt\n"
148+
+ f" pull side 1 [{stop_delay}]\n"
149+
+ "wait 0 pin 0\n"
150+
+ f" set x, {self.bitcount - 1} side 0 [7]\n"
151+
+ "bitloop:\n"
152+
+ " out pins, 1\n"
153+
+ " jmp x-- bitloop [6]\n"
154+
)
155+
self.tx_pio = rp2pio.StateMachine(
156+
tx_code.assembled,
157+
first_out_pin=tx,
158+
first_sideset_pin=tx,
159+
frequency=8 * baudrate,
160+
initial_sideset_pin_state=1,
161+
initial_sideset_pin_direction=1,
162+
initial_out_pin_state=1,
163+
initial_out_pin_direction=1,
164+
first_in_pin=cts,
165+
pull_in_pin_up=True,
166+
wait_for_txstall=False,
167+
**tx_code.pio_kwargs,
168+
)
169+
else:
170+
# An 8n1 UART transmit program.
171+
# OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
172+
173+
# Line by line explanation:
174+
# * Assert stop bit, or stall with line in idle state
175+
# * Preload bit counter, assert start bit for 8 clocks
176+
# * This loop will run 8 times (8n1 UART)
177+
# * Shift 1 bit from OSR to the first OUT pin
178+
# * Each loop iteration is 8 cycles.
179+
tx_code = adafruit_pioasm.Program(
180+
".program uart_tx\n"
181+
+ ".side_set 1 opt\n"
182+
+ f" pull side 1 [{stop_delay}]\n"
183+
+ f" set x, {self.bitcount - 1} side 0 [7]\n"
184+
+ "bitloop:\n"
185+
+ " out pins, 1\n"
186+
+ " jmp x-- bitloop [6]\n"
187+
)
188+
self.tx_pio = rp2pio.StateMachine(
189+
tx_code.assembled,
190+
first_out_pin=tx,
191+
first_sideset_pin=tx,
192+
frequency=8 * baudrate,
193+
initial_sideset_pin_state=1,
194+
initial_sideset_pin_direction=1,
195+
initial_out_pin_state=1,
196+
initial_out_pin_direction=1,
197+
**tx_code.pio_kwargs,
198+
)
112199

113200
def deinit(self):
114201
"""De-initialize the UART object."""

0 commit comments

Comments
 (0)