diff --git a/avr-hal-generic/Cargo.toml b/avr-hal-generic/Cargo.toml index ceacf1bd6e..5a29861b71 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" +embedded-io = "0.6.1" unwrap-infallible = "0.1.5" [dependencies.embedded-hal-v0] diff --git a/avr-hal-generic/src/usart.rs b/avr-hal-generic/src/usart.rs index b5fe96295e..9712e26125 100644 --- a/avr-hal-generic/src/usart.rs +++ b/avr-hal-generic/src/usart.rs @@ -3,9 +3,12 @@ //! Check the documentation of [`Usart`] for details. use core::cmp::Ordering; +use core::convert::Infallible; use core::marker; use crate::prelude::*; +use embedded_io::ErrorType; + use crate::port; /// Representation of a USART baudrate @@ -184,21 +187,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. /// @@ -336,7 +339,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() { @@ -349,7 +352,7 @@ impl, RX, TX, CLOCK> ufmt::uWrite for Usart, RX, TX, CLOCK> embedded_hal_v0::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) @@ -360,16 +363,78 @@ impl, RX, TX, CLOCK> embedded_hal_v0::serial::Writ } } +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> embedded_hal_v0::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 @@ -413,6 +478,14 @@ impl, RX, TX, CLOCK> UsartWriter, RX, TX, CLOCK> UsartReader { @@ -434,7 +507,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() { @@ -447,7 +520,7 @@ impl, RX, TX, CLOCK> ufmt::uWrite impl, RX, TX, CLOCK> embedded_hal_v0::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) @@ -458,16 +531,77 @@ impl, RX, TX, CLOCK> embedded_hal_v0::serial::Writ } } +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> embedded_hal_v0::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 b75f01bbde..69ae34da24 100644 --- a/examples/arduino-uno/Cargo.toml +++ b/examples/arduino-uno/Cargo.toml @@ -13,6 +13,7 @@ embedded-hal = "1.0" 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); +}