diff --git a/esp-hal-procmacros/src/lp_core.rs b/esp-hal-procmacros/src/lp_core.rs index 96f69bfa489..31baca8eee4 100644 --- a/esp-hal-procmacros/src/lp_core.rs +++ b/esp-hal-procmacros/src/lp_core.rs @@ -6,7 +6,7 @@ use quote::quote; pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { use proc_macro2::{Ident, Span}; use proc_macro_crate::{crate_name, FoundCrate}; - use quote::{format_ident, quote}; + use quote::format_ident; use syn::{ parse::Error, parse_macro_input, diff --git a/esp-lp-hal/CHANGELOG.md b/esp-lp-hal/CHANGELOG.md index 9fa7679eb33..599df738c8b 100644 --- a/esp-lp-hal/CHANGELOG.md +++ b/esp-lp-hal/CHANGELOG.md @@ -17,11 +17,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added basic `LP-I2C` driver for C6 (#1185) - Add remaining GPIO pins for ESP32-S2/S3 (#1695) - Add `wake_hp_core` for ESP32-C6 (#1723) +- Implement `embedded-hal@1.x.x` traits by default instead of `embedded-hal@0.2.x` (#1754) +- Implement `embedded-hal-nb` and `embedded-io` traits for UART driver (#1754) ### Changed - Renamed to `esp-ulp-riscv-hal` (#916) - Remove 2nd level generics from GPIO pin (#1526) +- GPIO Input/Output types have been converted to unit structs (#1754) ### Fixed diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index 46553f68fe9..c2967dc5d3f 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -7,6 +7,10 @@ description = "HAL for low-power RISC-V coprocessors found in ESP32 devices" repository = "https://github.com/esp-rs/esp-hal" license = "MIT OR Apache-2.0" +[lib] +bench = false +test = false + keywords = [ "embedded", "embedded-hal", @@ -21,16 +25,19 @@ categories = [ ] [dependencies] -cfg-if = "1.0.0" -embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", optional = true, features = ["unproven"] } -embedded-hal-1 = { version = "1.0.0", package = "embedded-hal", optional = true } -esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "f5429637f079337eb77bad44fb80bded58478619", features = ["critical-section"], optional = true } -esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "f5429637f079337eb77bad44fb80bded58478619", features = ["critical-section"], optional = true } -esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "f5429637f079337eb77bad44fb80bded58478619", features = ["critical-section"], optional = true } -nb = { version = "1.1.0", optional = true } -paste = { version = "1.0.14", optional = true } -procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } -riscv = { version = "0.11.0", features = ["critical-section-single-hart"] } +cfg-if = "1.0.0" +document-features = "0.2.8" +embedded-hal = { version = "1.0.0", optional = true } +embedded-hal-02 = { version = "0.2.7", optional = true, features = ["unproven"], package = "embedded-hal" } +embedded-hal-nb = { version = "1.0.0", optional = true } +embedded-io = { version = "0.6.1", optional = true } +esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "9c76169", features = ["critical-section"], optional = true } +esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "9c76169", features = ["critical-section"], optional = true } +esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "9c76169", features = ["critical-section"], optional = true } +nb = { version = "1.1.0", optional = true } +paste = { version = "1.0.15", optional = true } +procmacros = { version = "0.11.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } +riscv = { version = "0.11.1", features = ["critical-section-single-hart"] } [dev-dependencies] panic-halt = "0.2.0" @@ -39,31 +46,42 @@ panic-halt = "0.2.0" esp-build = { version = "0.1.0", path = "../esp-build" } [features] -default = ["embedded-hal-02"] - -embedded-hal-02 = ["dep:embedded-hal-02"] -embedded-hal = ["dep:embedded-hal-1"] - -esp32c6 = ["dep:esp32c6-lp", "procmacros/is-lp-core", "dep:nb", "dep:paste"] -esp32s2 = ["dep:esp32s2-ulp", "procmacros/is-ulp-core"] -esp32s3 = ["dep:esp32s3-ulp", "procmacros/is-ulp-core"] +default = ["embedded-hal"] +## Enable debug features in the HAL (used for development). debug = [ "esp32c6-lp?/impl-register-debug", "esp32s2-ulp?/impl-register-debug", "esp32s3-ulp?/impl-register-debug", ] +# Chip Support Feature Flags +# Target the ESP32-C6. +esp32c6 = ["dep:esp32c6-lp", "procmacros/is-lp-core", "dep:nb", "dep:paste"] +# Target the ESP32-S2. +esp32s2 = ["dep:esp32s2-ulp", "procmacros/is-ulp-core"] +# Target the ESP32-S3. +esp32s3 = ["dep:esp32s3-ulp", "procmacros/is-ulp-core"] + +#! ### Trait Implementation Feature Flags +## Implement the traits defined in the `0.2.x` release of `embedded-hal`. +embedded-hal-02 = ["dep:embedded-hal-02"] +## Implement the traits defined in the `1.0.0` releases of `embedded-hal` and +## `embedded-hal-nb` for the relevant peripherals. +embedded-hal = ["dep:embedded-hal", "dep:embedded-hal-nb"] +## Implement the traits defined in `embedded-io` for the relevant peripherals. +embedded-io = ["dep:embedded-io"] + [[example]] name = "blinky" required-features = ["embedded-hal-02"] [[example]] -name = "uart" +name = "i2c" required-features = ["embedded-hal-02", "esp32c6"] [[example]] -name = "i2c" +name = "uart" required-features = ["embedded-hal-02", "esp32c6"] [lints.rust] diff --git a/esp-lp-hal/examples/blinky.rs b/esp-lp-hal/examples/blinky.rs index 94dda529324..e4334ed6fbd 100644 --- a/esp-lp-hal/examples/blinky.rs +++ b/esp-lp-hal/examples/blinky.rs @@ -7,6 +7,8 @@ //! //! Make sure the LP RAM is cleared before loading the code. +//% FEATURES: embedded-hal-02 + #![no_std] #![no_main] diff --git a/esp-lp-hal/examples/i2c.rs b/esp-lp-hal/examples/i2c.rs index f8cbc15a66b..25cf1f8a80e 100644 --- a/esp-lp-hal/examples/i2c.rs +++ b/esp-lp-hal/examples/i2c.rs @@ -8,6 +8,7 @@ //! - SCL => GPIO7 //% CHIPS: esp32c6 +//% FEATURES: embedded-hal-02 #![no_std] #![no_main] diff --git a/esp-lp-hal/examples/uart.rs b/esp-lp-hal/examples/uart.rs index 4e25b83565c..293250ef28a 100644 --- a/esp-lp-hal/examples/uart.rs +++ b/esp-lp-hal/examples/uart.rs @@ -6,6 +6,7 @@ //! logs from LP_UART. Make sure the LP RAM is cleared before loading the code. //% CHIPS: esp32c6 +//% FEATURES: embedded-hal-02 #![no_std] #![no_main] diff --git a/esp-lp-hal/src/delay.rs b/esp-lp-hal/src/delay.rs index 5a47315a9be..ef4dad0ff33 100644 --- a/esp-lp-hal/src/delay.rs +++ b/esp-lp-hal/src/delay.rs @@ -1,11 +1,23 @@ -//! Simple blocking delay functionality. +//! # Delay driver +//! +//! ## Overview +//! +//! The delay driver provides blocking delay functionality. The driver +//! implements the relevant traits from `embedded-hal`. +//! +//! ## Examples +//! +//! ```rust +//! esp_lp_hal::delay::Delay.delay_micros(500); +//! ``` -#[derive(Clone, Copy)] +/// Delay driver +#[derive(Debug, Clone, Copy)] pub struct Delay; impl Delay { /// Delay for at least the number of specific microseconds. - pub fn delay_micros(&mut self, mut us: u32) { + pub fn delay_micros(&self, mut us: u32) { const NANOS_PER_MICRO: u32 = 1_000; const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; @@ -19,7 +31,7 @@ impl Delay { } /// Delay for at least the number of specific nanoseconds. - pub fn delay_nanos(&mut self, ns: u32) { + pub fn delay_nanos(&self, ns: u32) { let ticks_seconds = unsafe { crate::CPU_CLOCK }; let clock = (ns as u64 * (ticks_seconds as u64)) / 1_000_000_000u64; let t0 = cycles(); @@ -72,8 +84,9 @@ impl embedded_hal_02::blocking::delay::DelayMs for Delay { } } -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::delay::DelayNs for Delay { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::delay::DelayNs for Delay { + #[inline(always)] fn delay_ns(&mut self, ns: u32) { self.delay_nanos(ns); } diff --git a/esp-lp-hal/src/gpio.rs b/esp-lp-hal/src/gpio.rs index 61189c3721c..e50c418199a 100644 --- a/esp-lp-hal/src/gpio.rs +++ b/esp-lp-hal/src/gpio.rs @@ -1,34 +1,58 @@ -//! Low-power GPIO driver +//! # General Purpose Input/Output +//! +//! ## Overview //! //! It's assumed that GPIOs are already configured correctly by the HP core. +//! +//! This driver supports various operations on GPIO pins, primarily manipulating +//! the pin state (setting high/low, toggling). +//! +//! This module also implements a number of traits from `embedded-hal` to +//! provide a common interface for GPIO pins. +//! +//! ## Examples +//! +//! ```rust +//! fn main(gpio0: Input<0>, gpio1: Output<1>) -> ! { +//! loop { +//! let input_state: bool = gpio0.input_state(); +//! gpio.set_output(input_state); +//! +//! esp_lp_hal::delay::Delay.delay_millis(50); +//! } +//! } +//! ``` + +cfg_if::cfg_if! { + if #[cfg(feature = "esp32c6")] { + type LpIo = crate::pac::LP_IO; + const MAX_GPIO_PIN: u8 = 7; + } else { + type LpIo = crate::pac::RTC_IO; + const MAX_GPIO_PIN: u8 = 21; + } +} -#[cfg(feature = "esp32c6")] -type LpIo = crate::pac::LP_IO; -#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] -type LpIo = crate::pac::RTC_IO; - -#[cfg(feature = "esp32c6")] -const MAX_GPIO_PIN: u8 = 7; -#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] -const MAX_GPIO_PIN: u8 = 21; - -#[non_exhaustive] -pub struct Input {} +/// GPIO input driver +pub struct Input; impl Input { + /// Read the input state/level of the pin. pub fn input_state(&self) -> bool { unsafe { &*LpIo::PTR }.in_().read().bits() >> PIN & 0x1 != 0 } } -#[non_exhaustive] -pub struct Output {} +/// GPIO output driver +pub struct Output; impl Output { + /// Read the output state/level of the pin. pub fn output_state(&self) -> bool { unsafe { &*LpIo::PTR }.out().read().bits() >> PIN & 0x1 != 0 } + /// Set the output state/level of the pin. pub fn set_output(&mut self, on: bool) { if on { unsafe { &*LpIo::PTR } @@ -48,7 +72,7 @@ pub unsafe fn conjure_output() -> Option> { if PIN > MAX_GPIO_PIN { None } else { - Some(Output {}) + Some(Output) } } @@ -58,7 +82,7 @@ pub unsafe fn conjure_input() -> Option> { if PIN > MAX_GPIO_PIN { None } else { - Some(Input {}) + Some(Input) } } @@ -104,18 +128,18 @@ impl embedded_hal_02::digital::v2::StatefulOutputPin for Output

embedded_hal_02::digital::v2::toggleable::Default for Output {} -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::digital::ErrorType for Input { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::digital::ErrorType for Input { type Error = core::convert::Infallible; } -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::digital::ErrorType for Output { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::digital::ErrorType for Output { type Error = core::convert::Infallible; } -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::digital::InputPin for Input { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::digital::InputPin for Input { fn is_high(&mut self) -> Result { Ok(self.input_state()) } @@ -125,8 +149,8 @@ impl embedded_hal_1::digital::InputPin for Input { } } -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::digital::OutputPin for Output { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::digital::OutputPin for Output { fn set_low(&mut self) -> Result<(), Self::Error> { self.set_output(false); Ok(()) @@ -138,8 +162,8 @@ impl embedded_hal_1::digital::OutputPin for Output { } } -#[cfg(feature = "embedded-hal-1")] -impl embedded_hal_1::digital::StatefulOutputPin for Output { +#[cfg(feature = "embedded-hal")] +impl embedded_hal::digital::StatefulOutputPin for Output { fn is_set_high(&mut self) -> Result { Ok(self.output_state()) } diff --git a/esp-lp-hal/src/i2c.rs b/esp-lp-hal/src/i2c.rs index af0073c0636..a414e4ee803 100644 --- a/esp-lp-hal/src/i2c.rs +++ b/esp-lp-hal/src/i2c.rs @@ -1,6 +1,8 @@ -//! Low-power I2C driver +//! # Inter-Integrated Circuit (I2C) -use esp32c6_lp::LP_I2C0; +#![allow(unused)] // TODO: Remove me when `embedded_hal::i2c::I2c` is implemented + +use crate::pac::{lp_i2c0::COMD, LP_I2C0}; const LP_I2C_TRANS_COMPLETE_INT_ST_S: u32 = 7; const LP_I2C_END_DETECT_INT_ST_S: u32 = 3; @@ -20,8 +22,7 @@ pub unsafe fn conjure() -> LpI2c { } /// I2C-specific transmission errors -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Error { ExceedingFifo, AckCheckFailed, @@ -32,17 +33,19 @@ pub enum Error { InvalidResponse, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum OperationType { Write = 0, Read = 1, } -#[derive(Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Ack { Ack, Nack, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Opcode { RStart = 6, Write = 1, @@ -51,7 +54,7 @@ enum Opcode { End = 4, } -#[derive(PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Command { Start, Stop, @@ -135,62 +138,11 @@ impl From for u16 { impl From for u32 { fn from(c: Command) -> u32 { - let opcode = match c { - Command::Start => Opcode::RStart, - Command::Stop => Opcode::Stop, - Command::End => Opcode::End, - Command::Write { .. } => Opcode::Write, - Command::Read { .. } => Opcode::Read, - }; - - let length = match c { - Command::Start | Command::Stop | Command::End => 0, - Command::Write { length: l, .. } | Command::Read { length: l, .. } => l, - }; - - let ack_exp = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => Ack::Nack, - Command::Write { ack_exp: exp, .. } => exp, - }; - - let ack_check_en = match c { - Command::Start | Command::Stop | Command::End | Command::Read { .. } => false, - Command::Write { - ack_check_en: en, .. - } => en, - }; - - let ack_value = match c { - Command::Start | Command::Stop | Command::End | Command::Write { .. } => Ack::Nack, - Command::Read { ack_value: ack, .. } => ack, - }; - - let mut cmd: u32 = length.into(); - - if ack_check_en { - cmd |= 1 << 8; - } else { - cmd &= !(1 << 8); - } - - if ack_exp == Ack::Nack { - cmd |= 1 << 9; - } else { - cmd &= !(1 << 9); - } - - if ack_value == Ack::Nack { - cmd |= 1 << 10; - } else { - cmd &= !(1 << 10); - } - - cmd |= (opcode as u32) << 11; - - cmd + u16::from(c) as u32 } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum CommandRegister { COMD0, COMD1, @@ -232,8 +184,6 @@ pub struct LpI2c { } impl LpI2c { - /// Send data bytes from the `bytes` array to a target slave with the - /// address `addr` fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { let mut cmd_iterator = CommandRegister::COMD0; @@ -423,16 +373,15 @@ impl LpI2c { bytes: &[u8], buffer: &mut [u8], ) -> Result<(), Error> { - // it would be possible to combine the write and read - // in one transaction but filling the tx fifo with - // the current code is somewhat slow even in release mode - // which can cause issues + // It would be possible to combine the write and read in one transaction, but + // filling the tx fifo with the current code is somewhat slow even in release + // mode which can cause issues. self.master_write(addr, bytes)?; self.master_read(addr, buffer)?; + Ok(()) } - /// Update I2C configuration fn lp_i2c_update(&self) { self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); } @@ -507,49 +456,12 @@ impl LpI2c { command_register: &mut CommandRegister, command: Command, ) -> Result<(), Error> { - match *command_register { - CommandRegister::COMD0 => { - self.i2c - .comd(0) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD1 => { - self.i2c - .comd(1) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD2 => { - self.i2c - .comd(2) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD3 => { - self.i2c - .comd(3) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD4 => { - self.i2c - .comd(4) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD5 => { - self.i2c - .comd(5) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD6 => { - self.i2c - .comd(6) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - CommandRegister::COMD7 => { - self.i2c - .comd(7) - .write(|w| unsafe { w.command().bits(command.into()) }); - } - } + self.i2c + .comd(*command_register as usize) + .write(|w| unsafe { w.command().bits(command.into()) }); + command_register.advance(); + Ok(()) } } diff --git a/esp-lp-hal/src/lib.rs b/esp-lp-hal/src/lib.rs index 084548a0c4f..3fc50b40def 100644 --- a/esp-lp-hal/src/lib.rs +++ b/esp-lp-hal/src/lib.rs @@ -1,3 +1,15 @@ +//! Bare-metal (`no_std`) HAL for the low power and ultra-low power cores found +//! in some Espressif devices. Where applicable, drivers implement the +//! [embedded-hal] traits. +//! +//! ## Choosing a device +//! +//! Depending on your target device, you need to enable the chip feature +//! for that device. +//! +//! ## Feature Flags +#![doc = document_features::document_features!()] +#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] #![allow(asm_sub_register)] #![no_std] @@ -10,15 +22,14 @@ pub mod i2c; #[cfg(esp32c6)] pub mod uart; -pub mod pac { - #[cfg(feature = "esp32c6")] - pub use esp32c6_lp::*; - #[cfg(feature = "esp32s2")] - pub use esp32s2_ulp::*; - #[cfg(feature = "esp32s3")] - pub use esp32s3_ulp::*; -} +#[cfg(feature = "esp32c6")] +pub use esp32c6_lp as pac; +#[cfg(feature = "esp32s2")] +pub use esp32s2_ulp as pac; +#[cfg(feature = "esp32s3")] +pub use esp32s3_ulp as pac; +/// The prelude pub mod prelude { pub use procmacros::entry; } @@ -35,13 +46,14 @@ cfg_if::cfg_if! { } } -pub static mut CPU_CLOCK: u32 = LP_FAST_CLK_HZ; +pub(crate) static mut CPU_CLOCK: u32 = LP_FAST_CLK_HZ; /// Wake up the HP core #[cfg(feature = "esp32c6")] pub fn wake_hp_core() { - let pmu = unsafe { esp32c6_lp::PMU::steal() }; - pmu.hp_lp_cpu_comm().write(|w| w.lp_trigger_hp().set_bit()); + unsafe { &*esp32c6_lp::PMU::PTR } + .hp_lp_cpu_comm() + .write(|w| w.lp_trigger_hp().set_bit()); } #[cfg(feature = "esp32c6")] @@ -49,8 +61,8 @@ global_asm!( r#" .section .init.vector, "ax" /* This is the vector table. It is currently empty, but will be populated - * with exception and interrupt handlers when this is supported - */ + * with exception and interrupt handlers when this is supported + */ .align 0x4, 0xff .global _vector_table diff --git a/esp-lp-hal/src/uart.rs b/esp-lp-hal/src/uart.rs index 95fe7a4af9a..fca510e2864 100644 --- a/esp-lp-hal/src/uart.rs +++ b/esp-lp-hal/src/uart.rs @@ -1,4 +1,38 @@ -//! Low-power UART driver +//! # Universal Asynchronous Receiver/Transmitter (UART) +//! +//! ## Overview +//! +//! The UART is a hardware peripheral which handles communication using serial +//! interfaces. This peripheral provides a cheap and ubiquitous method for full- +//! and half-duplex communication between devices. +//! +//! ## Configuration +//! +//! The usual setting such as baud rate, data bits, parity, and stop bits can +//! easily be configured. See the [config] module documentation for more +//! information. +//! +//! ## Usage +//! +//! The UART driver implements a number of third-party traits, with the +//! intention of making the HAL inter-compatible with various device drivers +//! from the community. This includes the [embedded-hal], [embedded-hal-nb], and +//! [embedded-io] traits. +//! +//! ## Examples +//! +//! ```rust +//! fn main(mut uart: LpUart) -> ! { +//! loop { +//! writeln!(uart, "Hello, world!").ok(); +//! esp_lp_hal::delay::Delay.delay_ms(1000); +//! } +//! } +//! ``` +//! +//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ +//! [embedded-hal-nb]: https://docs.rs/embedded-hal-nb/latest/embedded_hal_nb/ +//! [embedded-io]: https://docs.rs/embedded-io/latest/embedded_io/ use crate::pac::LP_UART; @@ -11,74 +45,105 @@ pub unsafe fn conjure() -> LpUart { } } +/// UART Error #[derive(Debug)] pub enum Error {} +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { + embedded_hal_nb::serial::ErrorKind::Other + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + /// UART configuration pub mod config { /// Number of data bits - #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum DataBits { + /// 5 data bits DataBits5 = 0, + /// 6 data bits DataBits6 = 1, + /// 7 data bits DataBits7 = 2, + /// 8 data bits + #[default] DataBits8 = 3, } /// Parity check - #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum Parity { + /// No parity + #[default] ParityNone = 0, + /// Even parity ParityEven = 1, + /// Odd parity ParityOdd = 2, } /// Number of stop bits - #[derive(PartialEq, Eq, Copy, Clone, Debug)] + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum StopBits { /// 1 stop bit - STOP1 = 1, + #[default] + Stop1 = 1, /// 1.5 stop bits - STOP1P5 = 2, + Stop1p5 = 2, /// 2 stop bits - STOP2 = 3, + Stop2 = 3, } /// UART configuration - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Clone, Copy)] pub struct Config { - pub baudrate: u32, - pub data_bits: DataBits, - pub parity: Parity, - pub stop_bits: StopBits, + baudrate: u32, + data_bits: DataBits, + parity: Parity, + stop_bits: StopBits, } impl Config { + /// Configure the UART's baud rate pub fn baudrate(mut self, baudrate: u32) -> Self { self.baudrate = baudrate; self } + /// Configure the UART to use no parity pub fn parity_none(mut self) -> Self { self.parity = Parity::ParityNone; self } + /// Configure the UART to use even parity pub fn parity_even(mut self) -> Self { self.parity = Parity::ParityEven; self } + /// Configure the UART to use odd parity pub fn parity_odd(mut self) -> Self { self.parity = Parity::ParityOdd; self } + /// Configure the UART's data bits pub fn data_bits(mut self, data_bits: DataBits) -> Self { self.data_bits = data_bits; self } + /// Configure the UART's stop bits pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { self.stop_bits = stop_bits; self @@ -88,10 +153,10 @@ pub mod config { impl Default for Config { fn default() -> Config { Config { - baudrate: 115200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, + baudrate: 115_200, + data_bits: Default::default(), + parity: Default::default(), + stop_bits: Default::default(), } } } @@ -123,8 +188,13 @@ impl LpUart { } } - fn write_bytes(&mut self, data: &[u8]) -> nb::Result<(), Error> { - data.iter().try_for_each(|c| self.write_byte(*c)) + fn write_bytes(&mut self, data: &[u8]) -> Result { + let count = data.len(); + + data.iter() + .try_for_each(|c| nb::block!(self.write_byte(*c)))?; + + Ok(count) } fn flush_tx(&mut self) -> nb::Result<(), Error> { @@ -150,7 +220,9 @@ impl LpUart { impl core::fmt::Write for LpUart { fn write_str(&mut self, s: &str) -> core::fmt::Result { - self.write_bytes(s.as_bytes()).map_err(|_| core::fmt::Error) + self.write_bytes(s.as_bytes()) + .map_err(|_| core::fmt::Error)?; + Ok(()) } } @@ -175,3 +247,78 @@ impl embedded_hal_02::serial::Write for LpUart { self.flush_tx() } } + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::ErrorType for LpUart { + type Error = Error; +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Read for LpUart { + fn read(&mut self) -> nb::Result { + self.read_byte() + } +} + +#[cfg(feature = "embedded-hal")] +impl embedded_hal_nb::serial::Write for LpUart { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.write_byte(word) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.flush_tx() + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ErrorType for LpUart { + type Error = Error; +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Read for LpUart { + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.len() == 0 { + return Ok(0); + } + + while self.get_rx_fifo_count() == 0 { + // Block until we have received at least one byte + } + + let mut count = 0; + while self.get_rx_fifo_count() > 0 && count < buf.len() { + buf[count] = self.uart.fifo().read().rxfifo_rd_byte().bits(); + count += 1; + } + + Ok(count) + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::ReadReady for LpUart { + fn read_ready(&mut self) -> Result { + Ok(self.get_rx_fifo_count() > 0) + } +} + +#[cfg(feature = "embedded-io")] +impl embedded_io::Write for LpUart { + fn write(&mut self, buf: &[u8]) -> Result { + self.write_bytes(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + loop { + match self.flush_tx() { + Ok(_) => break, + Err(nb::Error::WouldBlock) => { /* Wait */ } + Err(nb::Error::Other(e)) => return Err(e), + } + } + + Ok(()) + } +}