Skip to content

Commit

Permalink
implement embedded-io v0.6 Read & Write
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rursprung committed Dec 28, 2023
1 parent dfc94f6 commit 3679a64
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 9 deletions.
1 change: 1 addition & 0 deletions avr-hal-generic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
152 changes: 143 additions & 9 deletions avr-hal-generic/src/usart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -183,21 +186,21 @@ pub trait UsartOps<H, RX, TX> {
/// 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<u8, core::convert::Infallible>;
fn raw_read(&mut self) -> nb::Result<u8, Infallible>;

/// Enable/Disable a certain interrupt.
///
Expand Down Expand Up @@ -335,7 +338,7 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> Usart<H, USART, RX, TX, CLOCK
}

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ufmt::uWrite for Usart<H, USART, RX, TX, CLOCK> {
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() {
Expand All @@ -348,7 +351,7 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ufmt::uWrite for Usart<H, USA
impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Write<u8>
for Usart<H, USART, RX, TX, CLOCK>
{
type Error = core::convert::Infallible;
type Error = Infallible;

fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
self.p.raw_write(byte)
Expand All @@ -359,16 +362,78 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Write<u8>
}
}

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ErrorType for Usart<H, USART, RX, TX, CLOCK> { type Error = Infallible; }

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> embedded_io::Write for Usart<H, USART, RX, TX, CLOCK> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
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<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Read<u8>
for Usart<H, USART, RX, TX, CLOCK>
{
type Error = core::convert::Infallible;
type Error = Infallible;

fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.p.raw_read()
}
}

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> embedded_io::Read for Usart<H, USART, RX, TX, CLOCK> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// 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
Expand Down Expand Up @@ -412,6 +477,14 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> UsartWriter<H, USART, RX, TX,
_h: marker::PhantomData,
}
}

/// Transmit a byte.
///
/// This method will block until the byte has been enqueued for transmission but **not** until
/// it was entirely sent.
fn write_byte(&mut self, byte: u8) {
nb::block!(self.p.raw_write(byte)).unwrap()
}
}

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> UsartReader<H, USART, RX, TX, CLOCK> {
Expand All @@ -433,7 +506,7 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> UsartReader<H, USART, RX, TX,
impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ufmt::uWrite
for UsartWriter<H, USART, RX, TX, CLOCK>
{
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() {
Expand All @@ -446,7 +519,7 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ufmt::uWrite
impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Write<u8>
for UsartWriter<H, USART, RX, TX, CLOCK>
{
type Error = core::convert::Infallible;
type Error = Infallible;

fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
self.p.raw_write(byte)
Expand All @@ -457,16 +530,77 @@ impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Write<u8>
}
}

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ErrorType for UsartWriter<H, USART, RX, TX, CLOCK> { type Error = Infallible; }

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> embedded_io::Write for UsartWriter<H, USART, RX, TX, CLOCK> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
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<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> hal::serial::Read<u8>
for UsartReader<H, USART, RX, TX, CLOCK>
{
type Error = core::convert::Infallible;
type Error = Infallible;

fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.p.raw_read()
}
}


impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> ErrorType for UsartReader<H, USART, RX, TX, CLOCK> { type Error = Infallible; }

impl<H, USART: UsartOps<H, RX, TX>, RX, TX, CLOCK> embedded_io::Read for UsartReader<H, USART, RX, TX, CLOCK> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
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 {
(
Expand Down
1 change: 1 addition & 0 deletions examples/arduino-uno/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
29 changes: 29 additions & 0 deletions examples/arduino-uno/src/bin/uno-usart-embedded-io.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 3679a64

Please sign in to comment.