1#![no_std]
51#![deny(missing_docs)]
52
53use rp2040_hal::{
54 gpio::{Pin, PinId, PullNone, PullUp},
55 pio::{
56 self, InstallError, InstalledProgram, PIOBuilder, PIOExt, ShiftDirection, StateMachine,
57 StateMachineIndex, UninitStateMachine,
58 },
59};
60
61pub fn install_rx_program<PIO: PIOExt>(
63 pio: &mut pio::PIO<PIO>,
64) -> Result<RxProgram<PIO>, InstallError> {
65 let program_with_defines = pio_proc::pio_file!("src/uart_rx.pio", select_program("uart_rx"));
66 let program = program_with_defines.program;
67 pio.install(&program).map(|program| RxProgram { program })
68}
69pub fn install_tx_program<PIO: PIOExt>(
71 pio: &mut pio::PIO<PIO>,
72) -> Result<TxProgram<PIO>, InstallError> {
73 let program_with_defines = pio_proc::pio_file!("src/uart_tx.pio",);
74 let program = program_with_defines.program;
75 pio.install(&program).map(|program| TxProgram { program })
76}
77
78pub struct PioUart<RXID: PinId, TXID: PinId, PIO: PIOExt, State> {
86 rx: PioUartRx<RXID, PIO, pio::SM0, State>,
87 tx: PioUartTx<TXID, PIO, pio::SM1, State>,
88 _rx_program: RxProgram<PIO>,
90 _tx_program: TxProgram<PIO>,
91 _pio: pio::PIO<PIO>,
92 _sm2: UninitStateMachine<(PIO, pio::SM2)>,
93 _sm3: UninitStateMachine<(PIO, pio::SM3)>,
94}
95
96pub struct PioUartRx<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex, State> {
103 rx: pio::Rx<(PIO, SM)>,
104 sm: StateMachine<(PIO, SM), State>,
105 _rx_pin: Pin<PinID, PIO::PinFunction, PullUp>,
107 _tx: pio::Tx<(PIO, SM)>,
108}
109pub struct PioUartTx<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex, State> {
116 tx: pio::Tx<(PIO, SM)>,
117 sm: StateMachine<(PIO, SM), State>,
118 _tx_pin: Pin<PinID, PIO::PinFunction, PullNone>,
120 _rx: pio::Rx<(PIO, SM)>,
121}
122
123pub struct RxProgram<PIO: PIOExt> {
125 program: InstalledProgram<PIO>,
126}
127pub struct TxProgram<PIO: PIOExt> {
129 program: InstalledProgram<PIO>,
130}
131
132impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> PioUartRx<PinID, PIO, SM, pio::Stopped> {
133 pub fn new(
143 rx_pin: Pin<PinID, PIO::PinFunction, PullUp>,
144 rx_sm: UninitStateMachine<(PIO, SM)>,
145 rx_program: &mut RxProgram<PIO>,
146 baud: fugit::HertzU32,
147 system_freq: fugit::HertzU32,
148 ) -> Self {
149 let div = system_freq.to_Hz() as f32 / (8f32 * baud.to_Hz() as f32);
150 let rx_id = rx_pin.id().num;
151
152 let (rx_sm, rx, tx) = Self::build_rx(rx_program, rx_id, rx_sm, div);
153
154 Self {
155 rx,
156 sm: rx_sm,
157 _rx_pin: rx_pin,
158 _tx: tx,
159 }
160 }
161 fn build_rx(
162 token: &mut RxProgram<PIO>,
163 rx_id: u8,
164 sm: UninitStateMachine<(PIO, SM)>,
165 div: f32,
166 ) -> (
167 StateMachine<(PIO, SM), pio::Stopped>,
168 pio::Rx<(PIO, SM)>,
169 pio::Tx<(PIO, SM)>,
170 ) {
171 let program = unsafe { token.program.share() };
173 let builder = PIOBuilder::from_installed_program(program);
174 let (mut sm, rx, tx) = builder
175 .in_pin_base(rx_id)
176 .jmp_pin(rx_id)
177 .in_shift_direction(ShiftDirection::Right)
178 .autopush(false)
179 .push_threshold(32)
180 .buffers(pio::Buffers::OnlyRx)
181 .build(sm);
182 sm.set_pindirs([(rx_id, pio::PinDir::Input)].into_iter());
183 sm.set_clock_divisor(div);
184 (sm, rx, tx)
185 }
186 #[inline]
191 pub fn enable(self) -> PioUartRx<PinID, PIO, SM, pio::Running> {
192 PioUartRx {
193 sm: self.sm.start(),
194 rx: self.rx,
195 _rx_pin: self._rx_pin,
196 _tx: self._tx,
197 }
198 }
199 pub fn free(
204 self,
205 ) -> (
206 UninitStateMachine<(PIO, SM)>,
207 Pin<PinID, PIO::PinFunction, PullUp>,
208 ) {
209 let (rx_sm, _) = self.sm.uninit(self.rx, self._tx);
210 (rx_sm, self._rx_pin)
211 }
212}
213
214impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> PioUartTx<PinID, PIO, SM, pio::Stopped> {
215 pub fn new(
225 tx_pin: Pin<PinID, PIO::PinFunction, PullNone>,
226 sm: UninitStateMachine<(PIO, SM)>,
227 tx_program: &mut TxProgram<PIO>,
228 baud: fugit::HertzU32,
229 system_freq: fugit::HertzU32,
230 ) -> Self {
231 let div = system_freq.to_Hz() as f32 / (8f32 * baud.to_Hz() as f32);
232 let tx_id = tx_pin.id().num;
233
234 let (tx_sm, rx, tx) = Self::build_tx(tx_program, tx_id, sm, div);
235
236 Self {
237 tx,
238 sm: tx_sm,
239 _tx_pin: tx_pin,
240 _rx: rx,
241 }
242 }
243 fn build_tx(
244 token: &mut TxProgram<PIO>,
245 tx_id: u8,
246 sm: UninitStateMachine<(PIO, SM)>,
247 div: f32,
248 ) -> (
249 StateMachine<(PIO, SM), pio::Stopped>,
250 pio::Rx<(PIO, SM)>,
251 pio::Tx<(PIO, SM)>,
252 ) {
253 let program = unsafe { token.program.share() };
255 let builder = PIOBuilder::from_installed_program(program);
256 let (mut sm, rx, tx) = builder
257 .out_shift_direction(ShiftDirection::Right)
258 .autopull(false)
259 .pull_threshold(32)
260 .buffers(pio::Buffers::OnlyTx)
261 .out_pins(tx_id, 1)
262 .side_set_pin_base(tx_id)
263 .build(sm);
264 sm.set_pindirs([(tx_id, pio::PinDir::Output)].into_iter());
265 sm.set_clock_divisor(div);
266 (sm, rx, tx)
267 }
268 #[inline]
273 pub fn enable(self) -> PioUartTx<PinID, PIO, SM, pio::Running> {
274 PioUartTx {
275 sm: self.sm.start(),
276 tx: self.tx,
277 _tx_pin: self._tx_pin,
278 _rx: self._rx,
279 }
280 }
281 pub fn free(
286 self,
287 ) -> (
288 UninitStateMachine<(PIO, SM)>,
289 Pin<PinID, PIO::PinFunction, PullNone>,
290 ) {
291 let (tx_sm, _) = self.sm.uninit(self._rx, self.tx);
292 (tx_sm, self._tx_pin)
293 }
294}
295
296impl<RXID: PinId, TXID: PinId, PIO: PIOExt> PioUart<RXID, TXID, PIO, pio::Stopped> {
297 pub fn new(
309 pio: PIO,
310 rx_pin: Pin<RXID, <PIO as PIOExt>::PinFunction, PullUp>,
311 tx_pin: Pin<TXID, <PIO as PIOExt>::PinFunction, PullNone>,
312 resets: &mut rp2040_hal::pac::RESETS,
313 baud: fugit::HertzU32,
314 system_freq: fugit::HertzU32,
315 ) -> Self {
316 let (mut pio, sm0, sm1, sm2, sm3) = pio.split(resets);
317 let mut rx_program = install_rx_program(&mut pio).ok().unwrap(); let mut tx_program = install_tx_program(&mut pio).ok().unwrap(); let rx = PioUartRx::new(rx_pin, sm0, &mut rx_program, baud, system_freq);
320 let tx = PioUartTx::new(tx_pin, sm1, &mut tx_program, baud, system_freq);
321 Self {
322 rx,
323 tx,
324 _rx_program: rx_program,
325 _tx_program: tx_program,
326 _pio: pio,
327 _sm2: sm2,
328 _sm3: sm3,
329 }
330 }
331
332 #[inline]
337 pub fn enable(self) -> PioUart<RXID, TXID, PIO, pio::Running> {
338 PioUart {
339 rx: self.rx.enable(),
340 tx: self.tx.enable(),
341 _rx_program: self._rx_program,
342 _tx_program: self._tx_program,
343 _pio: self._pio,
344 _sm2: self._sm2,
345 _sm3: self._sm3,
346 }
347 }
348 pub fn free(
354 mut self,
355 ) -> (
356 PIO,
357 Pin<RXID, <PIO as PIOExt>::PinFunction, PullUp>,
358 Pin<TXID, <PIO as PIOExt>::PinFunction, PullNone>,
359 ) {
360 let (tx_sm, tx_pin) = self.tx.free();
361 let (rx_sm, rx_pin) = self.rx.free();
362 self._pio.uninstall(self._rx_program.program);
363 self._pio.uninstall(self._tx_program.program);
364 let pio = self._pio.free(rx_sm, tx_sm, self._sm2, self._sm3);
365 (pio, rx_pin, tx_pin)
366 }
367}
368
369impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> PioUartRx<PinID, PIO, SM, pio::Running> {
370 pub fn read_raw(&mut self, mut buf: &mut [u8]) -> Result<usize, ()> {
379 let buf_len = buf.len();
380 while let Some(b) = self.rx.read() {
381 buf[0] = (b >> 24) as u8;
382 buf = &mut buf[1..];
383 if buf.len() == 0 {
384 break;
385 }
386 }
387 Ok(buf_len - buf.len())
388 }
389 #[inline]
394 pub fn stop(self) -> PioUartRx<PinID, PIO, SM, pio::Stopped> {
395 PioUartRx {
396 sm: self.sm.stop(),
397 rx: self.rx,
398 _rx_pin: self._rx_pin,
399 _tx: self._tx,
400 }
401 }
402}
403impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> PioUartTx<PinID, PIO, SM, pio::Running> {
404 pub fn write_raw(&mut self, buf: &[u8]) -> Result<(), ()> {
413 for b in buf {
414 while self.tx.is_full() {
415 core::hint::spin_loop()
416 }
417 self.tx.write(*b as u32);
418 }
419 Ok(())
420 }
421 fn flush(&mut self) {
423 while !self.tx.is_empty() {
424 core::hint::spin_loop()
425 }
426 cortex_m::asm::delay(500 * 125);
428 }
429 #[inline]
434 pub fn stop(self) -> PioUartTx<PinID, PIO, SM, pio::Stopped> {
435 PioUartTx {
436 sm: self.sm.stop(),
437 tx: self.tx,
438 _tx_pin: self._tx_pin,
439 _rx: self._rx,
440 }
441 }
442}
443
444#[derive(core::fmt::Debug, defmt::Format)]
446#[non_exhaustive]
447pub enum PioSerialError {
448 IO,
450}
451
452impl embedded_io::Error for PioSerialError {
453 fn kind(&self) -> embedded_io::ErrorKind {
454 embedded_io::ErrorKind::Other
455 }
456}
457impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> embedded_io::ErrorType
458 for PioUartRx<PinID, PIO, SM, pio::Running>
459{
460 type Error = PioSerialError;
461}
462impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> embedded_io::ErrorType
463 for PioUartTx<PinID, PIO, SM, pio::Running>
464{
465 type Error = PioSerialError;
466}
467impl<RXID: PinId, TXID: PinId, PIO: PIOExt> embedded_io::ErrorType
468 for PioUart<RXID, TXID, PIO, pio::Running>
469{
470 type Error = PioSerialError;
471}
472impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> embedded_io::Read
473 for PioUartRx<PinID, PIO, SM, pio::Running>
474{
475 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
476 self.read_raw(buf).map_err(|_| PioSerialError::IO)
477 }
478}
479impl<PinID: PinId, PIO: PIOExt, SM: StateMachineIndex> embedded_io::Write
480 for PioUartTx<PinID, PIO, SM, pio::Running>
481{
482 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
483 self.write_raw(buf)
484 .map(|_| buf.len())
485 .map_err(|_| PioSerialError::IO)
486 }
487 fn flush(&mut self) -> Result<(), Self::Error> {
488 self.flush();
489 Ok(())
490 }
491}
492
493impl<RXID: PinId, TXID: PinId, PIO: PIOExt> embedded_io::Read
494 for PioUart<RXID, TXID, PIO, pio::Running>
495{
496 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
497 self.rx.read(buf)
498 }
499}
500impl<RXID: PinId, TXID: PinId, PIO: PIOExt> embedded_io::Write
501 for PioUart<RXID, TXID, PIO, pio::Running>
502{
503 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
504 self.tx.write(buf)
505 }
506 fn flush(&mut self) -> Result<(), Self::Error> {
507 embedded_io::Write::flush(&mut self.tx)
508 }
509}