aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorMichael van Niekerk <[email protected]>2023-07-28 11:37:38 +0200
committerMichael van Niekerk <[email protected]>2023-07-28 11:37:38 +0200
commite947aa01533b7fd41133678ed8444a16c9c341e0 (patch)
treeb7919a06acfa735a2b934508e0f7a7cb630e3e3a /examples
parenta60d92cfbbacc909ba781802cad04fe00e849026 (diff)
Comments
Diffstat (limited to 'examples')
-rw-r--r--examples/rp/Cargo.toml1
-rw-r--r--examples/rp/src/bin/pio_uart.rs351
2 files changed, 261 insertions, 91 deletions
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index c812cb3ee..2a018ad04 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0"
7 7
8[dependencies] 8[dependencies]
9embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } 9embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
10embassy-hal-common = { version = "0.1.0", path = "../../embassy-hal-common", features = ["defmt"] }
10embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 12embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } 13embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] }
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index 14d05f4db..eeb213e1b 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -1,23 +1,35 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. 1//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.
2//! The PIO module is a very powerful peripheral that can be used to implement many different
3//! protocols. It is a very flexible state machine that can be programmed to do almost anything.
2//! 4//!
3//! This creates a USB serial port that echos. 5//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the
6//! PIO module to implement a UART that is connected to the USB serial port. This allows you to
7//! communicate with a device connected to the RP2040 over USB serial.
4 8
5#![no_std] 9#![no_std]
6#![no_main] 10#![no_main]
7#![feature(type_alias_impl_trait)] 11#![feature(type_alias_impl_trait)]
12#![feature(async_fn_in_trait)]
8 13
9use defmt::{info, panic}; 14use defmt::{info, panic, trace};
10use embassy_executor::Spawner; 15use embassy_executor::Spawner;
11use embassy_futures::join::join; 16use embassy_futures::join::{join, join3};
12use embassy_rp::bind_interrupts; 17use embassy_rp::bind_interrupts;
13use embassy_rp::peripherals::{PIO0, USB}; 18use embassy_rp::peripherals::{PIO0, USB};
19use embassy_rp::pio::InterruptHandler as PioInterruptHandler;
14use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 20use embassy_rp::usb::{Driver, Instance, InterruptHandler};
15use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; 21use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
22use embassy_sync::channel::Channel;
23use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
16use embassy_usb::driver::EndpointError; 24use embassy_usb::driver::EndpointError;
17use embassy_usb::{Builder, Config}; 25use embassy_usb::{Builder, Config};
18use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; 26use embedded_io::asynch::{Read, Write};
19use {defmt_rtt as _, panic_probe as _}; 27use {defmt_rtt as _, panic_probe as _};
20 28
29use crate::uart::PioUart;
30use crate::uart_rx::PioUartRx;
31use crate::uart_tx::PioUartTx;
32
21bind_interrupts!(struct UsbIrqs { 33bind_interrupts!(struct UsbIrqs {
22 USBCTRL_IRQ => InterruptHandler<USB>; 34 USBCTRL_IRQ => InterruptHandler<USB>;
23}); 35});
@@ -69,7 +81,7 @@ async fn main(_spawner: Spawner) {
69 ); 81 );
70 82
71 // Create classes on the builder. 83 // Create classes on the builder.
72 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); 84 let class = CdcAcmClass::new(&mut builder, &mut state, 64);
73 85
74 // Build the builder. 86 // Build the builder.
75 let mut usb = builder.build(); 87 let mut usb = builder.build();
@@ -77,19 +89,50 @@ async fn main(_spawner: Spawner) {
77 // Run the USB device. 89 // Run the USB device.
78 let usb_fut = usb.run(); 90 let usb_fut = usb.run();
79 91
80 // Do stuff with the class! 92 // PIO UART setup
81 let echo_fut = async { 93 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5).await;
94 let (mut uart_tx, mut uart_rx) = uart.split();
95
96 // Channels setup
97 static USB_CHANNEL_TX: Channel<ThreadModeRawMutex, u8, 20> = Channel::<ThreadModeRawMutex, u8, 20>::new();
98 let mut usb_channel_tx_send = USB_CHANNEL_TX.sender();
99 let mut usb_channel_tx_recv = USB_CHANNEL_TX.receiver();
100
101 static UART_CHANNEL_TX: Channel<ThreadModeRawMutex, u8, 20> = Channel::<ThreadModeRawMutex, u8, 20>::new();
102 let mut uart_channel_tx_send = UART_CHANNEL_TX.sender();
103 let mut uart_channel_tx_recv = UART_CHANNEL_TX.receiver();
104
105 let (mut usb_tx, mut usb_rx) = class.split();
106
107 // Read + write from USB
108 let usb_future = async {
82 loop { 109 loop {
83 class.wait_connection().await; 110 info!("Wait for USB connection");
111 usb_rx.wait_connection().await;
84 info!("Connected"); 112 info!("Connected");
85 let _ = echo(&mut class).await; 113 let _ = join(
114 usb_read(&mut usb_rx, &mut uart_channel_tx_send),
115 usb_write(&mut usb_tx, &mut usb_channel_tx_recv),
116 )
117 .await;
86 info!("Disconnected"); 118 info!("Disconnected");
87 } 119 }
88 }; 120 };
89 121
122 // Read + write from UART
123 let uart_future = async {
124 loop {
125 let _ = join(
126 uart_read(&mut uart_rx, &mut usb_channel_tx_send),
127 uart_write(&mut uart_tx, &mut uart_channel_tx_recv),
128 )
129 .await;
130 }
131 };
132
90 // Run everything concurrently. 133 // Run everything concurrently.
91 // If we had made everything `'static` above instead, we could do this using separate tasks instead. 134 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
92 join(usb_fut, echo_fut).await; 135 join3(usb_fut, usb_future, uart_future).await;
93} 136}
94 137
95struct Disconnected {} 138struct Disconnected {}
@@ -103,28 +146,79 @@ impl From<EndpointError> for Disconnected {
103 } 146 }
104} 147}
105 148
106async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { 149/// Read from the USB and write it to the UART TX send channel
150async fn usb_read<'d, T: Instance + 'd>(
151 usb_rx: &mut Receiver<'d, Driver<'d, T>>,
152 uart_tx_send: &mut embassy_sync::channel::Sender<'d, ThreadModeRawMutex, u8, 20>,
153) -> Result<(), Disconnected> {
107 let mut buf = [0; 64]; 154 let mut buf = [0; 64];
108 loop { 155 loop {
109 let n = class.read_packet(&mut buf).await?; 156 let n = usb_rx.read_packet(&mut buf).await?;
110 let data = &buf[..n]; 157 let data = &buf[..n];
111 info!("data: {:x}", data); 158 trace!("USB IN: {:x}", data);
112 class.write_packet(data).await?; 159 for byte in data {
160 uart_tx_send.send(*byte).await;
161 }
162 }
163}
164
165/// Read from the USB TX receive channel and write it to the USB
166async fn usb_write<'d, T: Instance + 'd>(
167 usb_tx: &mut Sender<'d, Driver<'d, T>>,
168 usb_tx_recv: &mut embassy_sync::channel::Receiver<'d, ThreadModeRawMutex, u8, 20>,
169) -> Result<(), Disconnected> {
170 loop {
171 let n = usb_tx_recv.recv().await;
172 let data = [n];
173 trace!("USB OUT: {:x}", data);
174 usb_tx.write_packet(&data).await?;
175 }
176}
177
178/// Read from the UART and write it to the USB TX send channel
179async fn uart_read<'a>(
180 uart_rx: &mut PioUartRx<'a>,
181 usb_tx_send: &mut embassy_sync::channel::Sender<'a, ThreadModeRawMutex, u8, 20>,
182) -> Result<(), Disconnected> {
183 let mut buf = [0; 1];
184 loop {
185 let n = uart_rx.read(&mut buf).await.expect("UART read error");
186 if n == 0 {
187 continue;
188 }
189 trace!("UART IN: {:x}", buf);
190 usb_tx_send.send(buf[0]).await;
191 }
192}
193
194/// Read from the UART TX receive channel and write it to the UART
195async fn uart_write<'a>(
196 uart_tx: &mut PioUartTx<'a>,
197 uart_rx_recv: &mut embassy_sync::channel::Receiver<'a, ThreadModeRawMutex, u8, 20>,
198) -> Result<(), Disconnected> {
199 loop {
200 let n = uart_rx_recv.recv().await;
201 let data = [n];
202 trace!("UART OUT: {:x}", data);
203 let _ = uart_tx.write(&data).await;
113 } 204 }
114} 205}
115 206
116mod uart { 207mod uart {
208 use core::fmt::Debug;
209
117 use embassy_rp::peripherals::PIO0; 210 use embassy_rp::peripherals::PIO0;
118 use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; 211 use embassy_rp::pio::{Pio, PioPin};
119 use embassy_rp::Peripheral; 212 use embassy_rp::Peripheral;
213 use embedded_io::ErrorKind;
120 214
215 use crate::uart_rx::PioUartRx;
216 use crate::uart_tx::PioUartTx;
121 use crate::PioIrqs; 217 use crate::PioIrqs;
122 218
123 pub struct PioUart<'a> { 219 pub struct PioUart<'a> {
124 baud: u64, 220 tx: PioUartTx<'a>,
125 pio: Common<'a, PIO0>, 221 rx: PioUartRx<'a>,
126 sm0: StateMachine<'a, PIO0, 0>,
127 sm1: StateMachine<'a, PIO0, 1>,
128 } 222 }
129 223
130 impl<'a> PioUart<'a> { 224 impl<'a> PioUart<'a> {
@@ -135,21 +229,25 @@ mod uart {
135 rx_pin: impl PioPin, 229 rx_pin: impl PioPin,
136 ) -> PioUart<'a> { 230 ) -> PioUart<'a> {
137 let Pio { 231 let Pio {
138 mut common, 232 mut common, sm0, sm1, ..
139 mut sm0,
140 mut sm1,
141 ..
142 } = Pio::new(pio, PioIrqs); 233 } = Pio::new(pio, PioIrqs);
143 234
144 crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); 235 let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None);
145 crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); 236 let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin));
146 237
147 PioUart { 238 PioUart { tx, rx }
148 baud, 239 }
149 pio: common, 240
150 sm0, 241 pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) {
151 sm1, 242 (self.tx, self.rx)
152 } 243 }
244 }
245 #[derive(defmt::Format, Debug)]
246 pub struct PioUartError {}
247
248 impl embedded_io::Error for PioUartError {
249 fn kind(&self) -> ErrorKind {
250 ErrorKind::Other
153 } 251 }
154 } 252 }
155} 253}
@@ -159,18 +257,27 @@ mod uart_tx {
159 use embassy_rp::peripherals::PIO0; 257 use embassy_rp::peripherals::PIO0;
160 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; 258 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
161 use embassy_rp::relocate::RelocatedProgram; 259 use embassy_rp::relocate::RelocatedProgram;
260 use embedded_io::asynch::Write;
261 use embedded_io::Io;
162 use fixed::traits::ToFixed; 262 use fixed::traits::ToFixed;
163 use fixed_macro::types::U56F8; 263 use fixed_macro::types::U56F8;
164 264
165 pub fn setup_uart_tx_on_sm0<'a>( 265 use crate::uart::PioUartError;
166 common: &mut Common<'a, PIO0>, 266
167 sm_tx: &mut StateMachine<'a, PIO0, 0>, 267 pub struct PioUartTx<'a> {
168 tx_pin: impl PioPin, 268 sm_tx: StateMachine<'a, PIO0, 0>,
169 baud: u64, 269 }
170 ) { 270
171 let prg = pio_proc::pio_asm!( 271 impl<'a> PioUartTx<'a> {
172 r#" 272 pub fn new(
173 ;.program uart_tx 273 common: &mut Common<'a, PIO0>,
274 mut sm_tx: StateMachine<'a, PIO0, 0>,
275 tx_pin: impl PioPin,
276 baud: u64,
277 origin: Option<u8>,
278 ) -> (Self, u8) {
279 let mut prg = pio_proc::pio_asm!(
280 r#"
174 .side_set 1 opt 281 .side_set 1 opt
175 282
176 ; An 8n1 UART transmit program. 283 ; An 8n1 UART transmit program.
@@ -182,23 +289,55 @@ mod uart_tx {
182 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin 289 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
183 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. 290 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
184 "# 291 "#
185 ); 292 );
186 let tx_pin = common.make_pio_pin(tx_pin); 293 prg.program.origin = origin;
187 sm_tx.set_pins(Level::High, &[&tx_pin]); 294 let tx_pin = common.make_pio_pin(tx_pin);
188 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); 295 sm_tx.set_pins(Level::High, &[&tx_pin]);
189 296 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
190 let relocated = RelocatedProgram::new(&prg.program); 297
191 let mut cfg = Config::default(); 298 let relocated = RelocatedProgram::new(&prg.program);
192 299
193 cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); 300 let mut cfg = Config::default();
194 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); 301
195 cfg.shift_out.auto_fill = false; 302 cfg.set_out_pins(&[&tx_pin]);
196 cfg.shift_out.direction = ShiftDirection::Right; 303 cfg.use_program(&common.load_program(&relocated), &[&tx_pin]);
197 cfg.fifo_join = FifoJoin::TxOnly; 304 cfg.shift_out.auto_fill = false;
198 cfg.set_out_pins(&[&tx_pin]); 305 cfg.shift_out.direction = ShiftDirection::Right;
199 cfg.set_set_pins(&[&tx_pin]); 306 cfg.fifo_join = FifoJoin::TxOnly;
200 sm_tx.set_config(&cfg); 307 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
201 sm_tx.set_enable(true) 308 sm_tx.set_config(&cfg);
309 sm_tx.set_enable(true);
310
311 // The 4 state machines of the PIO each have their own program counter that starts taking
312 // instructions at an offset (origin) of the 32 instruction "space" the PIO device has.
313 // It is up to the programmer to sort out where to place these instructions.
314 // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin
315 // which takes an Option<u8>.
316 //
317 // When you load more than one RelocatedProgram into the PIO,
318 // you load your first program at origin = 0.
319 // The RelocatedProgram has .code().count() which returns a usize,
320 // for which you can then use as your next program's origin.
321 let offset = relocated.code().count() as u8 + origin.unwrap_or_default();
322 (Self { sm_tx }, offset)
323 }
324
325 pub async fn write_u8(&mut self, data: u8) {
326 self.sm_tx.tx().wait_push(data as u32).await;
327 }
328 }
329
330 impl Io for PioUartTx<'_> {
331 type Error = PioUartError;
332 }
333
334 impl Write for PioUartTx<'_> {
335 async fn write(&mut self, buf: &[u8]) -> Result<usize, PioUartError> {
336 for byte in buf {
337 self.write_u8(*byte).await;
338 }
339 Ok(buf.len())
340 }
202 } 341 }
203} 342}
204 343
@@ -207,19 +346,27 @@ mod uart_rx {
207 use embassy_rp::peripherals::PIO0; 346 use embassy_rp::peripherals::PIO0;
208 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; 347 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
209 use embassy_rp::relocate::RelocatedProgram; 348 use embassy_rp::relocate::RelocatedProgram;
349 use embedded_io::asynch::Read;
350 use embedded_io::Io;
210 use fixed::traits::ToFixed; 351 use fixed::traits::ToFixed;
211 use fixed_macro::types::U56F8; 352 use fixed_macro::types::U56F8;
212 353
213 pub fn setup_uart_rx_on_sm1<'a>( 354 use crate::uart::PioUartError;
214 common: &mut Common<'a, PIO0>, 355
215 sm_rx: &mut StateMachine<'a, PIO0, 1>, 356 pub struct PioUartRx<'a> {
216 rx_pin: impl PioPin, 357 sm_rx: StateMachine<'a, PIO0, 1>,
217 baud: u64, 358 }
218 ) {
219 let prg = pio_proc::pio_asm!(
220 r#"
221 ;.program uart_rx
222 359
360 impl<'a> PioUartRx<'a> {
361 pub fn new(
362 common: &mut Common<'a, PIO0>,
363 mut sm_rx: StateMachine<'a, PIO0, 1>,
364 rx_pin: impl PioPin,
365 baud: u64,
366 origin: Option<u8>,
367 ) -> (Self, u8) {
368 let mut prg = pio_proc::pio_asm!(
369 r#"
223 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and 370 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
224 ; break conditions more gracefully. 371 ; break conditions more gracefully.
225 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. 372 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
@@ -227,36 +374,58 @@ mod uart_rx {
227 start: 374 start:
228 wait 0 pin 0 ; Stall until start bit is asserted 375 wait 0 pin 0 ; Stall until start bit is asserted
229 set x, 7 [10] ; Preload bit counter, then delay until halfway through 376 set x, 7 [10] ; Preload bit counter, then delay until halfway through
230 bitloop: ; the first data bit (12 cycles incl wait, set). 377 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
231 in pins, 1 ; Shift data bit into ISR 378 in pins, 1 ; Shift data bit into ISR
232 jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles 379 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
233 jmp pin good_stop ; Check stop bit (should be high) 380 jmp pin good_rx_stop ; Check stop bit (should be high)
234 381
235 irq 4 rel ; Either a framing error or a break. Set a sticky flag, 382 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
236 wait 1 pin 0 ; and wait for line to return to idle state. 383 wait 1 pin 0 ; and wait for line to return to idle state.
237 jmp start ; Don't push data if we didn't see good framing. 384 jmp start ; Don't push data if we didn't see good framing.
238 385
239 good_stop: ; No delay before returning to start; a little slack is 386 good_rx_stop: ; No delay before returning to start; a little slack is
240 push ; important in case the TX clock is slightly too fast. 387 push ; important in case the TX clock is slightly too fast.
241 "# 388 "#
242 ); 389 );
243 390 prg.program.origin = origin;
244 let rx_pin = common.make_pio_pin(rx_pin); 391 let relocated = RelocatedProgram::new(&prg.program);
245 sm_rx.set_pins(Level::High, &[&rx_pin]); 392 let mut cfg = Config::default();
246 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); 393 cfg.use_program(&common.load_program(&relocated), &[]);
247 394
248 let relocated = RelocatedProgram::new(&prg.program); 395 let rx_pin = common.make_pio_pin(rx_pin);
249 let mut cfg = Config::default(); 396 sm_rx.set_pins(Level::High, &[&rx_pin]);
250 397 cfg.set_in_pins(&[&rx_pin]);
251 cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); 398 cfg.set_jmp_pin(&rx_pin);
252 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); 399 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]);
253 cfg.shift_out.auto_fill = false; 400
254 cfg.shift_out.direction = ShiftDirection::Right; 401 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
255 cfg.fifo_join = FifoJoin::RxOnly; 402 cfg.shift_out.auto_fill = false;
256 cfg.set_in_pins(&[&rx_pin]); 403 cfg.shift_out.direction = ShiftDirection::Right;
257 cfg.set_jmp_pin(&rx_pin); 404 cfg.fifo_join = FifoJoin::RxOnly;
258 // cfg.set_set_pins(&[&rx_pin]); 405 sm_rx.set_config(&cfg);
259 sm_rx.set_config(&cfg); 406 sm_rx.set_enable(true);
260 sm_rx.set_enable(true) 407
408 let offset = relocated.code().count() as u8 + origin.unwrap_or_default();
409 (Self { sm_rx }, offset)
410 }
411
412 pub async fn read_u8(&mut self) -> u8 {
413 self.sm_rx.rx().wait_pull().await as u8
414 }
415 }
416
417 impl Io for PioUartRx<'_> {
418 type Error = PioUartError;
419 }
420
421 impl Read for PioUartRx<'_> {
422 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, PioUartError> {
423 let mut i = 0;
424 while i < buf.len() {
425 buf[i] = self.read_u8().await;
426 i += 1;
427 }
428 Ok(i)
429 }
261 } 430 }
262} 431}