From 3679a64b48f133e6487c8805b6b942bed2060c14 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Thu, 28 Dec 2023 19:53:18 +0100 Subject: [PATCH] implement `embedded-io` v0.6 `Read` & `Write` with `embedded-hal` v1 the USART traits have been removed in favour of the new `embedded-io` crate. this adds a (very basic) implementation for `Read` and `Write`. other traits (such as the `*Ready` or `BufRead` traits) have not (yet) been implemented and some (like `Seek`) probably can't be implemented for this HAL. a better implementation might use a buffer in the background to receive more than one byte at once. see also #249 for a related PR. this is part of #468 --- avr-hal-generic/Cargo.toml | 1 + avr-hal-generic/src/usart.rs | 152 ++++++++++++++++-- examples/arduino-uno/Cargo.toml | 1 + .../src/bin/uno-usart-embedded-io.rs | 29 ++++ 4 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 examples/arduino-uno/src/bin/uno-usart-embedded-io.rs diff --git a/avr-hal-generic/Cargo.toml b/avr-hal-generic/Cargo.toml index 9bd3e8fddc..25cbf0c22d 100644 --- a/avr-hal-generic/Cargo.toml +++ b/avr-hal-generic/Cargo.toml @@ -12,6 +12,7 @@ paste = "1.0.0" avr-device = "0.5.3" embedded-storage = "0.2" embedded-hal = "1.0.0-rc.3" +embedded-io = "0.6.1" [dependencies.embedded-hal-v0] version = "0.2.3" diff --git a/avr-hal-generic/src/usart.rs b/avr-hal-generic/src/usart.rs index 76474eb08e..4e52713538 100644 --- a/avr-hal-generic/src/usart.rs +++ b/avr-hal-generic/src/usart.rs @@ -3,8 +3,11 @@ //! Check the documentation of [`Usart`] for details. use core::cmp::Ordering; +use core::convert::Infallible; use core::marker; +use embedded_io::ErrorType; + use crate::port; /// Representation of a USART baudrate @@ -183,21 +186,21 @@ pub trait UsartOps { /// was flushed yet. /// /// **Warning**: This is a low-level method and should not be called directly from user code. - fn raw_flush(&mut self) -> nb::Result<(), core::convert::Infallible>; + fn raw_flush(&mut self) -> nb::Result<(), Infallible>; /// Write a byte to the TX buffer. /// /// This operation must be non-blocking and return [`nb::Error::WouldBlock`] until the byte is /// enqueued. The operation should not wait for the byte to have actually been sent. /// /// **Warning**: This is a low-level method and should not be called directly from user code. - fn raw_write(&mut self, byte: u8) -> nb::Result<(), core::convert::Infallible>; + fn raw_write(&mut self, byte: u8) -> nb::Result<(), Infallible>; /// Read a byte from the RX buffer. /// /// This operation must be non-blocking and return [`nb::Error::WouldBlock`] if no incoming /// byte is available. /// /// **Warning**: This is a low-level method and should not be called directly from user code. - fn raw_read(&mut self) -> nb::Result; + fn raw_read(&mut self) -> nb::Result; /// Enable/Disable a certain interrupt. /// @@ -335,7 +338,7 @@ impl, RX, TX, CLOCK> Usart, RX, TX, CLOCK> ufmt::uWrite for Usart { - type Error = core::convert::Infallible; + type Error = Infallible; fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { for b in s.as_bytes().iter() { @@ -348,7 +351,7 @@ impl, RX, TX, CLOCK> ufmt::uWrite for Usart, RX, TX, CLOCK> hal::serial::Write for Usart { - type Error = core::convert::Infallible; + type Error = Infallible; fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { self.p.raw_write(byte) @@ -359,16 +362,78 @@ impl, RX, TX, CLOCK> hal::serial::Write } } +impl, RX, TX, CLOCK> ErrorType for Usart { type Error = Infallible; } + +impl, RX, TX, CLOCK> embedded_io::Write for Usart { + fn write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + // block for first byte + self.write_byte(buf[0]); + let mut i = 1; + + // write more bytes if it's possible + for byte in buf[1..].iter() { + match self.p.raw_write(*byte) { + Ok(_) => { + i += 1; + } + Err(nb::Error::WouldBlock) => { + return Ok(i); + } + Err(_) => { + unreachable!(); // `raw_write` is `Infallible` + } + } + } + Ok(i) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.p.raw_flush().unwrap(); // `raw_write` is `Infallible` + Ok(()) + } +} + impl, RX, TX, CLOCK> hal::serial::Read for Usart { - type Error = core::convert::Infallible; + type Error = Infallible; fn read(&mut self) -> nb::Result { self.p.raw_read() } } +impl, RX, TX, CLOCK> embedded_io::Read for Usart { + fn read(&mut self, buf: &mut [u8]) -> Result { + // block for first byte + buf[0] = self.read_byte(); + let mut i = 1; + + // grab more bytes if available + loop { + match self.p.raw_read() { + Ok(byte) => { + buf[i] = byte; + i += 1; + + if i == buf.len() { + return Ok(i); + } + } + Err(nb::Error::WouldBlock) => { + return Ok(i); + } + Err(_) => { + unreachable!(); // `raw_read` is `Infallible` + } + } + } + } +} + /// Writer half of a [`Usart`] peripheral. /// /// Created by calling [`Usart::split`]. Splitting a peripheral into reader and writer allows @@ -412,6 +477,14 @@ impl, RX, TX, CLOCK> UsartWriter, RX, TX, CLOCK> UsartReader { @@ -433,7 +506,7 @@ impl, RX, TX, CLOCK> UsartReader, RX, TX, CLOCK> ufmt::uWrite for UsartWriter { - type Error = core::convert::Infallible; + type Error = Infallible; fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { for b in s.as_bytes().iter() { @@ -446,7 +519,7 @@ impl, RX, TX, CLOCK> ufmt::uWrite impl, RX, TX, CLOCK> hal::serial::Write for UsartWriter { - type Error = core::convert::Infallible; + type Error = Infallible; fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { self.p.raw_write(byte) @@ -457,16 +530,77 @@ impl, RX, TX, CLOCK> hal::serial::Write } } +impl, RX, TX, CLOCK> ErrorType for UsartWriter { type Error = Infallible; } + +impl, RX, TX, CLOCK> embedded_io::Write for UsartWriter { + fn write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + // block for first byte + self.write_byte(buf[0]); + let mut i = 1; + + // write more bytes if it's possible + for byte in buf[1..].iter() { + match self.p.raw_write(*byte) { + Ok(_) => { + i += 1; + } + Err(nb::Error::WouldBlock) => { + return Ok(i); + } + Err(_) => { + unreachable!(); // `raw_write` is `Infallible` + } + } + } + Ok(i) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.p.raw_flush().unwrap(); // `raw_flush` is `Infallible` + Ok(()) + } +} + impl, RX, TX, CLOCK> hal::serial::Read for UsartReader { - type Error = core::convert::Infallible; + type Error = Infallible; fn read(&mut self) -> nb::Result { self.p.raw_read() } } + +impl, RX, TX, CLOCK> ErrorType for UsartReader { type Error = Infallible; } + +impl, RX, TX, CLOCK> embedded_io::Read for UsartReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + loop { + match self.p.raw_read() { + Ok(byte) => { + buf[i] = byte; + i += 1; + + if i == buf.len() { + return Ok(i); + } + } + Err(nb::Error::WouldBlock) => { + return Ok(i); + } + Err(_) => { + unreachable!(); // `raw_read` is `Infallible` + } + } + } + } +} + #[macro_export] macro_rules! impl_usart_traditional { ( diff --git a/examples/arduino-uno/Cargo.toml b/examples/arduino-uno/Cargo.toml index 33b016ef31..be53b6f2a1 100644 --- a/examples/arduino-uno/Cargo.toml +++ b/examples/arduino-uno/Cargo.toml @@ -13,6 +13,7 @@ embedded-hal = "1.0.0-rc.3" pwm-pca9685 = "0.3.1" infrared = "0.14.1" embedded-storage = "0.2" +embedded-io = "0.6.1" [dependencies.embedded-hal-v0] version = "0.2.3" diff --git a/examples/arduino-uno/src/bin/uno-usart-embedded-io.rs b/examples/arduino-uno/src/bin/uno-usart-embedded-io.rs new file mode 100644 index 0000000000..752d92c814 --- /dev/null +++ b/examples/arduino-uno/src/bin/uno-usart-embedded-io.rs @@ -0,0 +1,29 @@ +/*! + * Demonstration of writing to and reading from the serial console. + */ +#![no_std] +#![no_main] + +use panic_halt as _; + +use embedded_io::{Read, Write}; + +fn usart_handler(serial: &mut (impl Read + Write)) -> ! { + serial.write_all("Hello from Arduino!\r\n".as_bytes()).unwrap(); + + loop { + let mut rx_buf: [u8; 16] = [0; 16]; + let len = serial.read(&mut rx_buf).unwrap(); + + writeln!(serial, "Got {:?} (which is {} bytes long)", &rx_buf[..len], len).unwrap(); + } +} + +#[arduino_hal::entry] +fn main() -> ! { + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); + let mut serial = arduino_hal::default_serial!(dp, pins, 57600); + + usart_handler(&mut serial); +}