aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src/pio_programs/uart.rs
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-10-09 10:04:35 -0400
committerCaleb Jamison <[email protected]>2024-10-09 10:18:00 -0400
commit57c1fbf3089e2a2dc9fe5b7d1f1e094596566395 (patch)
tree833856b7da855b8de56dec1494c2da88ac29e415 /embassy-rp/src/pio_programs/uart.rs
parent456c226b29799f7db56ab60b0ae3d95cc7d6a32a (diff)
Move pio programs into embassy-rp
Diffstat (limited to 'embassy-rp/src/pio_programs/uart.rs')
-rw-r--r--embassy-rp/src/pio_programs/uart.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs
new file mode 100644
index 000000000..f4b3b204e
--- /dev/null
+++ b/embassy-rp/src/pio_programs/uart.rs
@@ -0,0 +1,186 @@
1//! Pio backed uart drivers
2
3use crate::{
4 clocks::clk_sys_freq,
5 gpio::Level,
6 pio::{
7 Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection,
8 StateMachine,
9 },
10};
11use core::convert::Infallible;
12use embedded_io_async::{ErrorType, Read, Write};
13use fixed::traits::ToFixed;
14
15/// This struct represents a uart tx program loaded into pio instruction memory.
16pub struct PioUartTxProgram<'a, PIO: Instance> {
17 prg: LoadedProgram<'a, PIO>,
18}
19
20impl<'a, PIO: Instance> PioUartTxProgram<'a, PIO> {
21 /// Load the uart tx program into the given pio
22 pub fn new(common: &mut Common<'a, PIO>) -> Self {
23 let prg = pio_proc::pio_asm!(
24 r#"
25 .side_set 1 opt
26
27 ; An 8n1 UART transmit program.
28 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
29
30 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
31 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
32 bitloop: ; This loop will run 8 times (8n1 UART)
33 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
34 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
35 "#
36 );
37
38 let prg = common.load_program(&prg.program);
39
40 Self { prg }
41 }
42}
43
44/// PIO backed Uart transmitter
45pub struct PioUartTx<'a, PIO: Instance, const SM: usize> {
46 sm_tx: StateMachine<'a, PIO, SM>,
47}
48
49impl<'a, PIO: Instance, const SM: usize> PioUartTx<'a, PIO, SM> {
50 /// Configure a pio state machine to use the loaded tx program.
51 pub fn new(
52 baud: u32,
53 common: &mut Common<'a, PIO>,
54 mut sm_tx: StateMachine<'a, PIO, SM>,
55 tx_pin: impl PioPin,
56 program: &PioUartTxProgram<'a, PIO>,
57 ) -> Self {
58 let tx_pin = common.make_pio_pin(tx_pin);
59 sm_tx.set_pins(Level::High, &[&tx_pin]);
60 sm_tx.set_pin_dirs(PioDirection::Out, &[&tx_pin]);
61
62 let mut cfg = Config::default();
63
64 cfg.set_out_pins(&[&tx_pin]);
65 cfg.use_program(&program.prg, &[&tx_pin]);
66 cfg.shift_out.auto_fill = false;
67 cfg.shift_out.direction = ShiftDirection::Right;
68 cfg.fifo_join = FifoJoin::TxOnly;
69 cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();
70 sm_tx.set_config(&cfg);
71 sm_tx.set_enable(true);
72
73 Self { sm_tx }
74 }
75
76 /// Write a single u8
77 pub async fn write_u8(&mut self, data: u8) {
78 self.sm_tx.tx().wait_push(data as u32).await;
79 }
80}
81
82impl<PIO: Instance, const SM: usize> ErrorType for PioUartTx<'_, PIO, SM> {
83 type Error = Infallible;
84}
85
86impl<PIO: Instance, const SM: usize> Write for PioUartTx<'_, PIO, SM> {
87 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
88 for byte in buf {
89 self.write_u8(*byte).await;
90 }
91 Ok(buf.len())
92 }
93}
94
95/// This struct represents a Uart Rx program loaded into pio instruction memory.
96pub struct PioUartRxProgram<'a, PIO: Instance> {
97 prg: LoadedProgram<'a, PIO>,
98}
99
100impl<'a, PIO: Instance> PioUartRxProgram<'a, PIO> {
101 /// Load the uart rx program into the given pio
102 pub fn new(common: &mut Common<'a, PIO>) -> Self {
103 let prg = pio_proc::pio_asm!(
104 r#"
105 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
106 ; break conditions more gracefully.
107 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
108
109 start:
110 wait 0 pin 0 ; Stall until start bit is asserted
111 set x, 7 [10] ; Preload bit counter, then delay until halfway through
112 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
113 in pins, 1 ; Shift data bit into ISR
114 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
115 jmp pin good_rx_stop ; Check stop bit (should be high)
116
117 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
118 wait 1 pin 0 ; and wait for line to return to idle state.
119 jmp start ; Don't push data if we didn't see good framing.
120
121 good_rx_stop: ; No delay before returning to start; a little slack is
122 in null 24
123 push ; important in case the TX clock is slightly too fast.
124 "#
125 );
126
127 let prg = common.load_program(&prg.program);
128
129 Self { prg }
130 }
131}
132
133/// PIO backed Uart reciever
134pub struct PioUartRx<'a, PIO: Instance, const SM: usize> {
135 sm_rx: StateMachine<'a, PIO, SM>,
136}
137
138impl<'a, PIO: Instance, const SM: usize> PioUartRx<'a, PIO, SM> {
139 /// Configure a pio state machine to use the loaded rx program.
140 pub fn new(
141 baud: u32,
142 common: &mut Common<'a, PIO>,
143 mut sm_rx: StateMachine<'a, PIO, SM>,
144 rx_pin: impl PioPin,
145 program: &PioUartRxProgram<'a, PIO>,
146 ) -> Self {
147 let mut cfg = Config::default();
148 cfg.use_program(&program.prg, &[]);
149
150 let rx_pin = common.make_pio_pin(rx_pin);
151 sm_rx.set_pins(Level::High, &[&rx_pin]);
152 cfg.set_in_pins(&[&rx_pin]);
153 cfg.set_jmp_pin(&rx_pin);
154 sm_rx.set_pin_dirs(PioDirection::In, &[&rx_pin]);
155
156 cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();
157 cfg.shift_in.auto_fill = false;
158 cfg.shift_in.direction = ShiftDirection::Right;
159 cfg.shift_in.threshold = 32;
160 cfg.fifo_join = FifoJoin::RxOnly;
161 sm_rx.set_config(&cfg);
162 sm_rx.set_enable(true);
163
164 Self { sm_rx }
165 }
166
167 /// Wait for a single u8
168 pub async fn read_u8(&mut self) -> u8 {
169 self.sm_rx.rx().wait_pull().await as u8
170 }
171}
172
173impl<PIO: Instance, const SM: usize> ErrorType for PioUartRx<'_, PIO, SM> {
174 type Error = Infallible;
175}
176
177impl<PIO: Instance, const SM: usize> Read for PioUartRx<'_, PIO, SM> {
178 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
179 let mut i = 0;
180 while i < buf.len() {
181 buf[i] = self.read_u8().await;
182 i += 1;
183 }
184 Ok(i)
185 }
186}