diff options
| -rw-r--r-- | embassy-nrf/src/uarte.rs | 215 | ||||
| -rw-r--r-- | examples/nrf/src/bin/uart_split.rs | 68 |
2 files changed, 220 insertions, 63 deletions
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index ff781cabd..17417c0e2 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs | |||
| @@ -54,6 +54,20 @@ impl Default for Config { | |||
| 54 | /// Interface to the UARTE peripheral | 54 | /// Interface to the UARTE peripheral |
| 55 | pub struct Uarte<'d, T: Instance> { | 55 | pub struct Uarte<'d, T: Instance> { |
| 56 | phantom: PhantomData<&'d mut T>, | 56 | phantom: PhantomData<&'d mut T>, |
| 57 | tx: UarteTx<'d, T>, | ||
| 58 | rx: UarteRx<'d, T>, | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Transmitter interface to the UARTE peripheral obtained | ||
| 62 | /// via [Uarte]::split. | ||
| 63 | pub struct UarteTx<'d, T: Instance> { | ||
| 64 | phantom: PhantomData<&'d mut T>, | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Receiver interface to the UARTE peripheral obtained | ||
| 68 | /// via [Uarte]::split. | ||
| 69 | pub struct UarteRx<'d, T: Instance> { | ||
| 70 | phantom: PhantomData<&'d mut T>, | ||
| 57 | } | 71 | } |
| 58 | 72 | ||
| 59 | impl<'d, T: Instance> Uarte<'d, T> { | 73 | impl<'d, T: Instance> Uarte<'d, T> { |
| @@ -119,11 +133,24 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 119 | apply_workaround_for_enable_anomaly(&r); | 133 | apply_workaround_for_enable_anomaly(&r); |
| 120 | r.enable.write(|w| w.enable().enabled()); | 134 | r.enable.write(|w| w.enable().enabled()); |
| 121 | 135 | ||
| 136 | let s = T::state(); | ||
| 137 | |||
| 138 | s.tx_rx_refcount.store(2, Ordering::Relaxed); | ||
| 139 | |||
| 122 | Self { | 140 | Self { |
| 123 | phantom: PhantomData, | 141 | phantom: PhantomData, |
| 142 | tx: UarteTx::new(), | ||
| 143 | rx: UarteRx::new(), | ||
| 124 | } | 144 | } |
| 125 | } | 145 | } |
| 126 | 146 | ||
| 147 | /// Split the Uarte into a transmitter and receiver, which is | ||
| 148 | /// particuarly useful when having two tasks correlating to | ||
| 149 | /// transmitting and receiving. | ||
| 150 | pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { | ||
| 151 | (self.tx, self.rx) | ||
| 152 | } | ||
| 153 | |||
| 127 | fn on_interrupt(_: *mut ()) { | 154 | fn on_interrupt(_: *mut ()) { |
| 128 | let r = T::regs(); | 155 | let r = T::regs(); |
| 129 | let s = T::state(); | 156 | let s = T::state(); |
| @@ -139,72 +166,72 @@ impl<'d, T: Instance> Uarte<'d, T> { | |||
| 139 | } | 166 | } |
| 140 | } | 167 | } |
| 141 | 168 | ||
| 142 | impl<'a, T: Instance> Drop for Uarte<'a, T> { | 169 | impl<'d, T: Instance> Read for Uarte<'d, T> { |
| 143 | fn drop(&mut self) { | 170 | #[rustfmt::skip] |
| 144 | info!("uarte drop"); | 171 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; |
| 145 | |||
| 146 | let r = T::regs(); | ||
| 147 | |||
| 148 | let did_stoprx = r.events_rxstarted.read().bits() != 0; | ||
| 149 | let did_stoptx = r.events_txstarted.read().bits() != 0; | ||
| 150 | info!("did_stoprx {} did_stoptx {}", did_stoprx, did_stoptx); | ||
| 151 | 172 | ||
| 152 | // Wait for rxto or txstopped, if needed. | 173 | fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { |
| 153 | while (did_stoprx && r.events_rxto.read().bits() == 0) | 174 | self.rx.read(rx_buffer) |
| 154 | || (did_stoptx && r.events_txstopped.read().bits() == 0) | 175 | } |
| 155 | {} | 176 | } |
| 156 | 177 | ||
| 157 | // Finally we can disable! | 178 | impl<'d, T: Instance> Write for Uarte<'d, T> { |
| 158 | r.enable.write(|w| w.enable().disabled()); | 179 | #[rustfmt::skip] |
| 180 | type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; | ||
| 159 | 181 | ||
| 160 | gpio::deconfigure_pin(r.psel.rxd.read().bits()); | 182 | fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { |
| 161 | gpio::deconfigure_pin(r.psel.txd.read().bits()); | 183 | self.tx.write(tx_buffer) |
| 162 | gpio::deconfigure_pin(r.psel.rts.read().bits()); | 184 | } |
| 163 | gpio::deconfigure_pin(r.psel.cts.read().bits()); | 185 | } |
| 164 | 186 | ||
| 165 | info!("uarte drop: done"); | 187 | impl<'d, T: Instance> UarteTx<'d, T> { |
| 188 | pub fn new() -> Self { | ||
| 189 | Self { | ||
| 190 | phantom: PhantomData, | ||
| 191 | } | ||
| 166 | } | 192 | } |
| 167 | } | 193 | } |
| 168 | 194 | ||
| 169 | impl<'d, T: Instance> Read for Uarte<'d, T> { | 195 | impl<'d, T: Instance> Write for UarteTx<'d, T> { |
| 170 | #[rustfmt::skip] | 196 | #[rustfmt::skip] |
| 171 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; | 197 | type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; |
| 172 | 198 | ||
| 173 | fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { | 199 | fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { |
| 174 | async move { | 200 | async move { |
| 175 | let ptr = rx_buffer.as_ptr(); | 201 | let ptr = tx_buffer.as_ptr(); |
| 176 | let len = rx_buffer.len(); | 202 | let len = tx_buffer.len(); |
| 177 | assert!(len <= EASY_DMA_SIZE); | 203 | assert!(len <= EASY_DMA_SIZE); |
| 204 | // TODO: panic if buffer is not in SRAM | ||
| 178 | 205 | ||
| 179 | let r = T::regs(); | 206 | let r = T::regs(); |
| 180 | let s = T::state(); | 207 | let s = T::state(); |
| 181 | 208 | ||
| 182 | let drop = OnDrop::new(move || { | 209 | let drop = OnDrop::new(move || { |
| 183 | info!("read drop: stopping"); | 210 | info!("write drop: stopping"); |
| 184 | |||
| 185 | r.intenclr.write(|w| w.endrx().clear()); | ||
| 186 | r.events_rxto.reset(); | ||
| 187 | r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); | ||
| 188 | 211 | ||
| 189 | while r.events_endrx.read().bits() == 0 {} | 212 | r.intenclr.write(|w| w.endtx().clear()); |
| 213 | r.events_txstopped.reset(); | ||
| 214 | r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); | ||
| 190 | 215 | ||
| 191 | info!("read drop: stopped"); | 216 | // TX is stopped almost instantly, spinning is fine. |
| 217 | while r.events_endtx.read().bits() == 0 {} | ||
| 218 | info!("write drop: stopped"); | ||
| 192 | }); | 219 | }); |
| 193 | 220 | ||
| 194 | r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); | 221 | r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); |
| 195 | r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | 222 | r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); |
| 196 | 223 | ||
| 197 | r.events_endrx.reset(); | 224 | r.events_endtx.reset(); |
| 198 | r.intenset.write(|w| w.endrx().set()); | 225 | r.intenset.write(|w| w.endtx().set()); |
| 199 | 226 | ||
| 200 | compiler_fence(Ordering::SeqCst); | 227 | compiler_fence(Ordering::SeqCst); |
| 201 | 228 | ||
| 202 | trace!("startrx"); | 229 | trace!("starttx"); |
| 203 | r.tasks_startrx.write(|w| unsafe { w.bits(1) }); | 230 | r.tasks_starttx.write(|w| unsafe { w.bits(1) }); |
| 204 | 231 | ||
| 205 | poll_fn(|cx| { | 232 | poll_fn(|cx| { |
| 206 | s.endrx_waker.register(cx.waker()); | 233 | s.endtx_waker.register(cx.waker()); |
| 207 | if r.events_endrx.read().bits() != 0 { | 234 | if r.events_endtx.read().bits() != 0 { |
| 208 | return Poll::Ready(()); | 235 | return Poll::Ready(()); |
| 209 | } | 236 | } |
| 210 | Poll::Pending | 237 | Poll::Pending |
| @@ -212,7 +239,7 @@ impl<'d, T: Instance> Read for Uarte<'d, T> { | |||
| 212 | .await; | 239 | .await; |
| 213 | 240 | ||
| 214 | compiler_fence(Ordering::SeqCst); | 241 | compiler_fence(Ordering::SeqCst); |
| 215 | r.events_rxstarted.reset(); | 242 | r.events_txstarted.reset(); |
| 216 | drop.defuse(); | 243 | drop.defuse(); |
| 217 | 244 | ||
| 218 | Ok(()) | 245 | Ok(()) |
| @@ -220,46 +247,71 @@ impl<'d, T: Instance> Read for Uarte<'d, T> { | |||
| 220 | } | 247 | } |
| 221 | } | 248 | } |
| 222 | 249 | ||
| 223 | impl<'d, T: Instance> Write for Uarte<'d, T> { | 250 | impl<'a, T: Instance> Drop for UarteTx<'a, T> { |
| 251 | fn drop(&mut self) { | ||
| 252 | info!("uarte tx drop"); | ||
| 253 | |||
| 254 | let r = T::regs(); | ||
| 255 | |||
| 256 | let did_stoptx = r.events_txstarted.read().bits() != 0; | ||
| 257 | info!("did_stoptx {}", did_stoptx); | ||
| 258 | |||
| 259 | // Wait for txstopped, if needed. | ||
| 260 | while did_stoptx && r.events_txstopped.read().bits() == 0 {} | ||
| 261 | |||
| 262 | let s = T::state(); | ||
| 263 | |||
| 264 | drop_tx_rx(&r, &s); | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | impl<'d, T: Instance> UarteRx<'d, T> { | ||
| 269 | pub fn new() -> Self { | ||
| 270 | Self { | ||
| 271 | phantom: PhantomData, | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | impl<'d, T: Instance> Read for UarteRx<'d, T> { | ||
| 224 | #[rustfmt::skip] | 277 | #[rustfmt::skip] |
| 225 | type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; | 278 | type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), TraitError>> + 'a; |
| 226 | 279 | ||
| 227 | fn write<'a>(&'a mut self, tx_buffer: &'a [u8]) -> Self::WriteFuture<'a> { | 280 | fn read<'a>(&'a mut self, rx_buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { |
| 228 | async move { | 281 | async move { |
| 229 | let ptr = tx_buffer.as_ptr(); | 282 | let ptr = rx_buffer.as_ptr(); |
| 230 | let len = tx_buffer.len(); | 283 | let len = rx_buffer.len(); |
| 231 | assert!(len <= EASY_DMA_SIZE); | 284 | assert!(len <= EASY_DMA_SIZE); |
| 232 | // TODO: panic if buffer is not in SRAM | ||
| 233 | 285 | ||
| 234 | let r = T::regs(); | 286 | let r = T::regs(); |
| 235 | let s = T::state(); | 287 | let s = T::state(); |
| 236 | 288 | ||
| 237 | let drop = OnDrop::new(move || { | 289 | let drop = OnDrop::new(move || { |
| 238 | info!("write drop: stopping"); | 290 | info!("read drop: stopping"); |
| 239 | 291 | ||
| 240 | r.intenclr.write(|w| w.endtx().clear()); | 292 | r.intenclr.write(|w| w.endrx().clear()); |
| 241 | r.events_txstopped.reset(); | 293 | r.events_rxto.reset(); |
| 242 | r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); | 294 | r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); |
| 243 | 295 | ||
| 244 | // TX is stopped almost instantly, spinning is fine. | 296 | while r.events_endrx.read().bits() == 0 {} |
| 245 | while r.events_endtx.read().bits() == 0 {} | 297 | |
| 246 | info!("write drop: stopped"); | 298 | info!("read drop: stopped"); |
| 247 | }); | 299 | }); |
| 248 | 300 | ||
| 249 | r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); | 301 | r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); |
| 250 | r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); | 302 | r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); |
| 251 | 303 | ||
| 252 | r.events_endtx.reset(); | 304 | r.events_endrx.reset(); |
| 253 | r.intenset.write(|w| w.endtx().set()); | 305 | r.intenset.write(|w| w.endrx().set()); |
| 254 | 306 | ||
| 255 | compiler_fence(Ordering::SeqCst); | 307 | compiler_fence(Ordering::SeqCst); |
| 256 | 308 | ||
| 257 | trace!("starttx"); | 309 | trace!("startrx"); |
| 258 | r.tasks_starttx.write(|w| unsafe { w.bits(1) }); | 310 | r.tasks_startrx.write(|w| unsafe { w.bits(1) }); |
| 259 | 311 | ||
| 260 | poll_fn(|cx| { | 312 | poll_fn(|cx| { |
| 261 | s.endtx_waker.register(cx.waker()); | 313 | s.endrx_waker.register(cx.waker()); |
| 262 | if r.events_endtx.read().bits() != 0 { | 314 | if r.events_endrx.read().bits() != 0 { |
| 263 | return Poll::Ready(()); | 315 | return Poll::Ready(()); |
| 264 | } | 316 | } |
| 265 | Poll::Pending | 317 | Poll::Pending |
| @@ -267,7 +319,7 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { | |||
| 267 | .await; | 319 | .await; |
| 268 | 320 | ||
| 269 | compiler_fence(Ordering::SeqCst); | 321 | compiler_fence(Ordering::SeqCst); |
| 270 | r.events_txstarted.reset(); | 322 | r.events_rxstarted.reset(); |
| 271 | drop.defuse(); | 323 | drop.defuse(); |
| 272 | 324 | ||
| 273 | Ok(()) | 325 | Ok(()) |
| @@ -275,6 +327,24 @@ impl<'d, T: Instance> Write for Uarte<'d, T> { | |||
| 275 | } | 327 | } |
| 276 | } | 328 | } |
| 277 | 329 | ||
| 330 | impl<'a, T: Instance> Drop for UarteRx<'a, T> { | ||
| 331 | fn drop(&mut self) { | ||
| 332 | info!("uarte rx drop"); | ||
| 333 | |||
| 334 | let r = T::regs(); | ||
| 335 | |||
| 336 | let did_stoprx = r.events_rxstarted.read().bits() != 0; | ||
| 337 | info!("did_stoprx {}", did_stoprx); | ||
| 338 | |||
| 339 | // Wait for rxto, if needed. | ||
| 340 | while did_stoprx && r.events_rxto.read().bits() == 0 {} | ||
| 341 | |||
| 342 | let s = T::state(); | ||
| 343 | |||
| 344 | drop_tx_rx(&r, &s); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 278 | #[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] | 348 | #[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] |
| 279 | pub(in crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { | 349 | pub(in crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { |
| 280 | // Do nothing | 350 | // Do nothing |
| @@ -328,6 +398,21 @@ pub(in crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::Reg | |||
| 328 | } | 398 | } |
| 329 | } | 399 | } |
| 330 | 400 | ||
| 401 | pub(in crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { | ||
| 402 | if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 { | ||
| 403 | // Finally we can disable, and we do so for the peripheral | ||
| 404 | // i.e. not just rx concerns. | ||
| 405 | r.enable.write(|w| w.enable().disabled()); | ||
| 406 | |||
| 407 | gpio::deconfigure_pin(r.psel.rxd.read().bits()); | ||
| 408 | gpio::deconfigure_pin(r.psel.txd.read().bits()); | ||
| 409 | gpio::deconfigure_pin(r.psel.rts.read().bits()); | ||
| 410 | gpio::deconfigure_pin(r.psel.cts.read().bits()); | ||
| 411 | |||
| 412 | info!("uarte tx and rx drop: done"); | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 331 | /// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, | 416 | /// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, |
| 332 | /// allowing it to implement the ReadUntilIdle trait. | 417 | /// allowing it to implement the ReadUntilIdle trait. |
| 333 | pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { | 418 | pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { |
| @@ -487,6 +572,8 @@ impl<'d, U: Instance, T: TimerInstance> Write for UarteWithIdle<'d, U, T> { | |||
| 487 | } | 572 | } |
| 488 | 573 | ||
| 489 | pub(crate) mod sealed { | 574 | pub(crate) mod sealed { |
| 575 | use core::sync::atomic::AtomicU8; | ||
| 576 | |||
| 490 | use embassy::waitqueue::AtomicWaker; | 577 | use embassy::waitqueue::AtomicWaker; |
| 491 | 578 | ||
| 492 | use super::*; | 579 | use super::*; |
| @@ -494,12 +581,14 @@ pub(crate) mod sealed { | |||
| 494 | pub struct State { | 581 | pub struct State { |
| 495 | pub endrx_waker: AtomicWaker, | 582 | pub endrx_waker: AtomicWaker, |
| 496 | pub endtx_waker: AtomicWaker, | 583 | pub endtx_waker: AtomicWaker, |
| 584 | pub tx_rx_refcount: AtomicU8, | ||
| 497 | } | 585 | } |
| 498 | impl State { | 586 | impl State { |
| 499 | pub const fn new() -> Self { | 587 | pub const fn new() -> Self { |
| 500 | Self { | 588 | Self { |
| 501 | endrx_waker: AtomicWaker::new(), | 589 | endrx_waker: AtomicWaker::new(), |
| 502 | endtx_waker: AtomicWaker::new(), | 590 | endtx_waker: AtomicWaker::new(), |
| 591 | tx_rx_refcount: AtomicU8::new(0), | ||
| 503 | } | 592 | } |
| 504 | } | 593 | } |
| 505 | } | 594 | } |
diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf/src/bin/uart_split.rs new file mode 100644 index 000000000..4b5dbb21f --- /dev/null +++ b/examples/nrf/src/bin/uart_split.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | use embassy::blocking_mutex::kind::Noop; | ||
| 8 | use embassy::channel::mpsc::{self, Channel, Sender}; | ||
| 9 | use embassy::util::Forever; | ||
| 10 | use embassy_nrf::peripherals::UARTE0; | ||
| 11 | use embassy_nrf::uarte::UarteRx; | ||
| 12 | use example_common::*; | ||
| 13 | |||
| 14 | use embassy::executor::Spawner; | ||
| 15 | use embassy::traits::uart::{Read, Write}; | ||
| 16 | use embassy_nrf::gpio::NoPin; | ||
| 17 | use embassy_nrf::{interrupt, uarte, Peripherals}; | ||
| 18 | |||
| 19 | static CHANNEL: Forever<Channel<Noop, [u8; 8], 1>> = Forever::new(); | ||
| 20 | |||
| 21 | #[embassy::main] | ||
| 22 | async fn main(spawner: Spawner, p: Peripherals) { | ||
| 23 | let mut config = uarte::Config::default(); | ||
| 24 | config.parity = uarte::Parity::EXCLUDED; | ||
| 25 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 26 | |||
| 27 | let irq = interrupt::take!(UARTE0_UART0); | ||
| 28 | let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, NoPin, NoPin, config); | ||
| 29 | let (mut tx, rx) = uart.split(); | ||
| 30 | |||
| 31 | let c = CHANNEL.put(Channel::new()); | ||
| 32 | let (s, mut r) = mpsc::split(c); | ||
| 33 | |||
| 34 | info!("uarte initialized!"); | ||
| 35 | |||
| 36 | // Spawn a task responsible purely for reading | ||
| 37 | |||
| 38 | unwrap!(spawner.spawn(reader(rx, s))); | ||
| 39 | |||
| 40 | // Message must be in SRAM | ||
| 41 | { | ||
| 42 | let mut buf = [0; 23]; | ||
| 43 | buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); | ||
| 44 | |||
| 45 | unwrap!(tx.write(&buf).await); | ||
| 46 | info!("wrote hello in uart!"); | ||
| 47 | } | ||
| 48 | |||
| 49 | // Continue reading in this main task and write | ||
| 50 | // back out the buffer we receive from the read | ||
| 51 | // task. | ||
| 52 | loop { | ||
| 53 | if let Some(buf) = r.recv().await { | ||
| 54 | info!("writing..."); | ||
| 55 | unwrap!(tx.write(&buf).await); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | #[embassy::task] | ||
| 61 | async fn reader(mut rx: UarteRx<'static, UARTE0>, s: Sender<'static, Noop, [u8; 8], 1>) { | ||
| 62 | let mut buf = [0; 8]; | ||
| 63 | loop { | ||
| 64 | info!("reading..."); | ||
| 65 | unwrap!(rx.read(&mut buf).await); | ||
| 66 | unwrap!(s.send(buf).await); | ||
| 67 | } | ||
| 68 | } | ||
