Skip to content

Commit 9225d09

Browse files
authored
Merge pull request #7 from burtyb/hardware_flow_control
Add support for hardware flow control.
2 parents 66046da + 532e27d commit 9225d09

File tree

1 file changed

+159
-63
lines changed

1 file changed

+159
-63
lines changed

adafruit_pio_uart.py

Lines changed: 159 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,16 @@ 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,
37+
tx=None,
38+
rx=None,
39+
baudrate=9600,
40+
bits=8,
41+
parity=None,
42+
stop=1,
43+
timeout=1,
44+
cts=None,
45+
rts=None,
3746
): # pylint: disable=invalid-name, too-many-arguments
3847
self.bitcount = bits + (1 if parity else 0)
3948
self.bits = bits
@@ -43,72 +52,159 @@ def __init__(
4352
self._timeout = timeout
4453
self.rx_pio = None
4554
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-
)
55+
if rts:
56+
# Fleshed-out 8n1 UART receiver with hardware flow control handling
57+
# framing errors and break conditions more gracefully.
58+
# Wait for the start bit whilst updating rts with the FIFO level
59+
# then sample 8 bits with the correct timing.
60+
# IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
61+
# OUT pin 0 is mapped to the GPIO used as UART RTS# (Request To Send).
62+
63+
# Line by line explanation:
64+
# * Update rts pin with status of fifo level (high when full).
65+
# * Loop back to start whilst waiting for the start bit (low).
66+
# * Preload bit counter, then delay until eye of first data bit.
67+
# * Shift data bit into ISR
68+
# * Loop bitcount times, each loop iteration is 8 cycles.
69+
# * Jump to good_stop if there's a stop bit (high)
70+
# * otherwise wait for line to return to idle state.
71+
# * Don't push data if we didn't see good framing and start again.
72+
# * Push valid data and return to the start.
73+
rx_code = adafruit_pioasm.assemble(
74+
".program uart_rx_mini\n"
75+
+ "start:\n"
76+
+ " mov pins !status\n"
77+
+ " jmp pin start\n"
78+
+ f" set x, {self.bitcount - 1} [10]\n"
79+
+ "bitloop:\n"
80+
+ " in pins, 1\n"
81+
+ " jmp x-- bitloop [6]\n"
82+
+ " jmp pin good_stop\n"
83+
+ " wait 1 pin 0\n" # Skip IRQ
84+
+ " jmp start\n"
85+
+ "good_stop:\n"
86+
+ " push\n"
87+
)
88+
89+
# mov_status_n is set to 7 allowing 2 further
90+
# entries (8 in the joined FIFO and one in ISR).
91+
self.rx_pio = rp2pio.StateMachine(
92+
rx_code,
93+
first_in_pin=rx,
94+
jmp_pin=rx,
95+
frequency=8 * baudrate,
96+
auto_push=False,
97+
push_threshold=self.bitcount,
98+
first_out_pin=rts,
99+
mov_status_type="rxfifo",
100+
mov_status_n=7,
101+
)
102+
else:
103+
# Fleshed-out 8n1 UART receiver which handles
104+
# framing errors and break conditions more gracefully.
105+
# IN pin 0 is mapped to the GPIO used as UART RX.
106+
107+
# Line by line explanation:
108+
# * Wait for start bit
109+
# * Preload bit counter, then delay until eye of first data bit.
110+
# * Shift data bit into ISR
111+
# * Loop bitcount times, each loop iteration is 8 cycles.
112+
# * Jump to good_stop if there's a stop bit (high)
113+
# * otherwise wait for line to return to idle state.
114+
# * Don't push data if we didn't see good framing and start again.
115+
# * Push valid data and return to the start.
116+
rx_code = adafruit_pioasm.assemble(
117+
".program uart_rx_mini\n"
118+
+ "start:\n"
119+
+ " wait 0 pin 0\n"
120+
+ f" set x, {self.bitcount - 1} [10]\n"
121+
+ "bitloop:\n"
122+
+ " in pins, 1\n"
123+
+ " jmp x-- bitloop [6]\n"
124+
+ " jmp pin good_stop\n"
125+
+ " wait 1 pin 0\n" # Skip IRQ
126+
+ " jmp start\n"
127+
+ "good_stop:\n"
128+
+ " push\n"
129+
)
130+
self.rx_pio = rp2pio.StateMachine(
131+
rx_code,
132+
first_in_pin=rx,
133+
jmp_pin=rx,
134+
frequency=8 * baudrate,
135+
auto_push=False,
136+
push_threshold=self.bitcount,
137+
)
79138

80139
self.tx_pio = None
81140
if tx:
82141
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-
)
142+
if cts:
143+
# An 8n1 UART transmit program with hardware flow control
144+
# OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
145+
# IN pin 0 is mapped to GPIO pin used as CTS# (Clear To Send),
146+
147+
# Line by line explanation:
148+
# * Assert stop bit, or stall with line in idle state
149+
# * Wait for CTS# before transmitting
150+
# * Preload bit counter, assert start bit for 8 clocks
151+
# * This loop will run 8 times (8n1 UART)
152+
# * Shift 1 bit from OSR to the first OUT pin
153+
# * Each loop iteration is 8 cycles.
154+
tx_code = adafruit_pioasm.Program(
155+
".program uart_tx\n"
156+
+ ".side_set 1 opt\n"
157+
+ f" pull side 1 [{stop_delay}]\n"
158+
+ "wait 0 pin 0\n"
159+
+ f" set x, {self.bitcount - 1} side 0 [7]\n"
160+
+ "bitloop:\n"
161+
+ " out pins, 1\n"
162+
+ " jmp x-- bitloop [6]\n"
163+
)
164+
self.tx_pio = rp2pio.StateMachine(
165+
tx_code.assembled,
166+
first_out_pin=tx,
167+
first_sideset_pin=tx,
168+
frequency=8 * baudrate,
169+
initial_sideset_pin_state=1,
170+
initial_sideset_pin_direction=1,
171+
initial_out_pin_state=1,
172+
initial_out_pin_direction=1,
173+
first_in_pin=cts,
174+
pull_in_pin_up=True,
175+
wait_for_txstall=False,
176+
**tx_code.pio_kwargs,
177+
)
178+
else:
179+
# An 8n1 UART transmit program.
180+
# OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
181+
182+
# Line by line explanation:
183+
# * Assert stop bit, or stall with line in idle state
184+
# * Preload bit counter, assert start bit for 8 clocks
185+
# * This loop will run 8 times (8n1 UART)
186+
# * Shift 1 bit from OSR to the first OUT pin
187+
# * Each loop iteration is 8 cycles.
188+
tx_code = adafruit_pioasm.Program(
189+
".program uart_tx\n"
190+
+ ".side_set 1 opt\n"
191+
+ f" pull side 1 [{stop_delay}]\n"
192+
+ f" set x, {self.bitcount - 1} side 0 [7]\n"
193+
+ "bitloop:\n"
194+
+ " out pins, 1\n"
195+
+ " jmp x-- bitloop [6]\n"
196+
)
197+
self.tx_pio = rp2pio.StateMachine(
198+
tx_code.assembled,
199+
first_out_pin=tx,
200+
first_sideset_pin=tx,
201+
frequency=8 * baudrate,
202+
initial_sideset_pin_state=1,
203+
initial_sideset_pin_direction=1,
204+
initial_out_pin_state=1,
205+
initial_out_pin_direction=1,
206+
**tx_code.pio_kwargs,
207+
)
112208

113209
def deinit(self):
114210
"""De-initialize the UART object."""

0 commit comments

Comments
 (0)