From 85f401ac44e4ebb8e5ad048e76b7c5a138395039 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Thu, 12 Oct 2023 10:40:29 +0200 Subject: [PATCH 1/9] UART: Removed flushing from `transmit_byte`, added `flush` function --- .../samv71-hal/src/uart/states/transmitter.rs | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs index 6bee0801..6e25c2fd 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs @@ -10,12 +10,8 @@ impl UART { self.registers_ref().cr.write(|w| w.rsttx().set_bit()); } - /// Transmits a single byte. Blocks until the transmission is completed, or timeout - /// is hit. - /// - /// UART has no direct PMC dependency, and PMC doesn't support clock frequency calculations - /// yet, therefore the timeout is specified as amount of CPU cycles to wait until UART - /// finishes the transmission. + /// Transmits a single byte. + /// Does not wait until transmission is completed, use [`UART::flush`] for that. /// /// # Parameters /// * `byte` - Byte to transmit @@ -23,25 +19,19 @@ impl UART { /// This is basically an amount of loop iterations with status checks. /// /// # Returns - /// `Ok(())` on successful transmission, `Err(())` if timeout has been reached. + /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. pub fn transmit_byte(&mut self, byte: u8, timeout_cycles: u32) -> Result<(), Error> { - if let Ok(timeout_cycles) = self.wait_for_transmitter_ready(timeout_cycles) { - self.set_transmitted_byte(byte); - return match self.wait_for_transmission_to_complete(timeout_cycles) { - Ok(_) => Ok(()), - Err(_) => Err(Error::TimedOut), - }; + match self.wait_for_transmitter_ready(timeout_cycles) { + Ok(_) => { + self.set_transmitted_byte(byte); + Ok(()) + } + Err(_) => Err(Error::TimedOut), } - - Err(Error::TimedOut) } /// Transmits multiple bytes. Blocks until the transmission is completed, or timeout - /// is hit. - /// - /// This function is more optimal than calling [`UART::transmit_byte`] in a loop, as - /// it will feed the holding register as soon as possible, instead of waiting for transmission - /// to finish. It should be preferred for this kind of operations. + /// is hit. Flushes the UART after transmitting last byte. /// /// # Parameters /// * `bytes` - Bytes to transmit. @@ -49,7 +39,7 @@ impl UART { /// This is basically an amount of loop iterations with status checks. /// /// # Returns - /// `Ok(())` on successful transmission, `Err(())` if timeout has been reached. + /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. pub fn transmit_bytes(&mut self, bytes: &[u8], timeout_cycles: u32) -> Result<(), Error> { if let Ok(mut timeout_cycles) = self.wait_for_transmitter_ready(timeout_cycles) { for &byte in bytes { @@ -60,15 +50,26 @@ impl UART { } } - return match self.wait_for_transmission_to_complete(timeout_cycles) { - Ok(_) => Ok(()), - Err(_) => Err(Error::TimedOut), - }; + return self.flush(timeout_cycles); } Err(Error::TimedOut) } + /// Flushes the UART by waiting until currently transmitted character is processed. + /// + /// # Parameters + /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait for byte reception. + /// This is basically an amount of loop iterations with status checks. + /// + /// # Returns + /// `Ok(())` on successful flush, `Err(Error::TimedOut)` if timeout has been reached. + pub fn flush(&mut self, timeout_cycles: u32) -> Result<(), Error> { + self.wait_for_transmission_to_complete(timeout_cycles) + .map(|_| ()) + .map_err(|_| Error::TimedOut) + } + /// Writes a byte to be transmitted next into TX holding register. /// /// Doesn't perform any checks. This is simply a wrapper for register write. From 0ef85c26f1e283fcf3999e381b602fb8a11141e9 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Thu, 12 Oct 2023 11:24:42 +0200 Subject: [PATCH 2/9] UART: Fixed comments for transmission functions --- arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs index 6e25c2fd..ee8175bd 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs @@ -15,7 +15,7 @@ impl UART { /// /// # Parameters /// * `byte` - Byte to transmit - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait for byte reception. + /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until transmitter is ready. /// This is basically an amount of loop iterations with status checks. /// /// # Returns @@ -35,7 +35,8 @@ impl UART { /// /// # Parameters /// * `bytes` - Bytes to transmit. - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait for byte reception. + /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until transmission is complete. + /// **Timeout is defined for the whole transaction**, not for single byte's transmission. /// This is basically an amount of loop iterations with status checks. /// /// # Returns @@ -59,7 +60,7 @@ impl UART { /// Flushes the UART by waiting until currently transmitted character is processed. /// /// # Parameters - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait for byte reception. + /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until a single byte is transmitted. /// This is basically an amount of loop iterations with status checks. /// /// # Returns From 50a0626c5209dd2ed7896656b52168a7888b0853 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Thu, 12 Oct 2023 08:42:47 +0200 Subject: [PATCH 3/9] Examples: Renamed 'samv71-hal-uart' to 'samv71-hal-uart-loopback' --- .../.cargo/config.toml | 0 .../{samv71-hal-uart => samv71-hal-uart-loopback}/Cargo.toml | 2 +- examples/{samv71-hal-uart => samv71-hal-uart-loopback}/build.rs | 0 examples/{samv71-hal-uart => samv71-hal-uart-loopback}/memory.x | 0 .../{samv71-hal-uart => samv71-hal-uart-loopback}/src/main.rs | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename examples/{samv71-hal-uart => samv71-hal-uart-loopback}/.cargo/config.toml (100%) rename examples/{samv71-hal-uart => samv71-hal-uart-loopback}/Cargo.toml (94%) rename examples/{samv71-hal-uart => samv71-hal-uart-loopback}/build.rs (100%) rename examples/{samv71-hal-uart => samv71-hal-uart-loopback}/memory.x (100%) rename examples/{samv71-hal-uart => samv71-hal-uart-loopback}/src/main.rs (100%) diff --git a/examples/samv71-hal-uart/.cargo/config.toml b/examples/samv71-hal-uart-loopback/.cargo/config.toml similarity index 100% rename from examples/samv71-hal-uart/.cargo/config.toml rename to examples/samv71-hal-uart-loopback/.cargo/config.toml diff --git a/examples/samv71-hal-uart/Cargo.toml b/examples/samv71-hal-uart-loopback/Cargo.toml similarity index 94% rename from examples/samv71-hal-uart/Cargo.toml rename to examples/samv71-hal-uart-loopback/Cargo.toml index 0ddb4590..28b19785 100644 --- a/examples/samv71-hal-uart/Cargo.toml +++ b/examples/samv71-hal-uart-loopback/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "samv71-hal-uart" +name = "samv71-hal-uart-loopback" authors = ["Wojciech Olech "] edition = "2021" version = "0.1.0" diff --git a/examples/samv71-hal-uart/build.rs b/examples/samv71-hal-uart-loopback/build.rs similarity index 100% rename from examples/samv71-hal-uart/build.rs rename to examples/samv71-hal-uart-loopback/build.rs diff --git a/examples/samv71-hal-uart/memory.x b/examples/samv71-hal-uart-loopback/memory.x similarity index 100% rename from examples/samv71-hal-uart/memory.x rename to examples/samv71-hal-uart-loopback/memory.x diff --git a/examples/samv71-hal-uart/src/main.rs b/examples/samv71-hal-uart-loopback/src/main.rs similarity index 100% rename from examples/samv71-hal-uart/src/main.rs rename to examples/samv71-hal-uart-loopback/src/main.rs From ed7af4ffca741328072270a6e60e8656494da2cf Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Fri, 13 Oct 2023 15:47:37 +0200 Subject: [PATCH 4/9] UART: Metadata refactor * Renamed `UartMetadata` to `UARTMetadata` * Moved `UART::registers_ref()` to `UARTMetadata::registers()` --- arch/cortex-m/samv71-hal/src/uart.rs | 46 ++++++++----------- arch/cortex-m/samv71-hal/src/uart/metadata.rs | 16 +++++-- .../src/uart/states/bidirectional.rs | 6 +-- .../samv71-hal/src/uart/states/configured.rs | 24 +++++----- .../samv71-hal/src/uart/states/receiver.rs | 12 ++--- .../samv71-hal/src/uart/states/transmitter.rs | 8 ++-- 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/arch/cortex-m/samv71-hal/src/uart.rs b/arch/cortex-m/samv71-hal/src/uart.rs index 2f26c9d0..e563e072 100644 --- a/arch/cortex-m/samv71-hal/src/uart.rs +++ b/arch/cortex-m/samv71-hal/src/uart.rs @@ -50,7 +50,6 @@ extern crate embedded_io; use core::marker::PhantomData; use self::config::{bool_to_rx_filter_config, calculate_baudrate}; -use self::metadata::RegisterBlock; pub use embedded_io::ErrorKind as Error; @@ -64,7 +63,7 @@ pub mod status; pub use self::config::{ClockSource, Config, ParityBit, ReceiverConfig}; pub use self::interrupt::Interrupt; -pub use self::metadata::UartMetadata; +pub use self::metadata::UARTMetadata; pub use self::status::Status; /// Constant representing oversampling ratio, which is used in baudrate and @@ -160,7 +159,7 @@ impl ReceiveTransmit for T where T: Receive + Transmit {} /// **and** you're changing main clock frequency using it's prescaler, **you must de-initialize /// UART using [`UART::disable`] before changing clock settings**, and re-initialize it by /// converting it into desired state after the clock is configured. -pub struct UART { +pub struct UART { /// Frequency of the clock driving UART baudrate. /// Required for baudrate calculations. Must be /// manually updated by the user after changing @@ -168,12 +167,12 @@ pub struct UART { /// UART will not work correctly. clock_source_frequency: Option, /// PAC UART instance metadata. - _meta: PhantomData, + _meta: PhantomData, /// State metadata. _state: PhantomData, } -impl UART { +impl UART { /// Creates new UART driver instance, consuming PAC UART instance to prevent creating /// duplicate drivers. /// @@ -199,7 +198,7 @@ impl UART { } } -impl UART { +impl UART { /// Transforms UART into `Transmitter` state. Resets UART status before /// changing the state. Disables loopback and RX filtering. /// @@ -292,7 +291,7 @@ impl UART { /// Disables all UART interrupts. pub fn disable_all_interrupts(&mut self) { - self.registers_ref().idr.write(|w| { + Instance::registers().idr.write(|w| { w.cmp() .set_bit() .txempty() @@ -327,17 +326,6 @@ impl UART { } } - /// Returns reference to UART registers. - /// - /// # Safety - /// This function dereferences a raw pointer. - /// It's safe to use, as long as there aren't multiple instances - /// of UART sharing the same register. - #[inline(always)] - const fn registers_ref(&self) -> &RegisterBlock { - unsafe { &*Instance::REGISTERS } - } - /// Enables UART receiver. /// /// The receiver is automatically enabled on conversion into `Receiver` or `Bidirectional` @@ -346,7 +334,7 @@ impl UART { /// This function is private, as it should be used only in state transition or loopback /// configuration code. pub(super) fn enable_receiver(&mut self) { - self.registers_ref().cr.write(|w| w.rxen().set_bit()); + Instance::registers().cr.write(|w| w.rxen().set_bit()); } /// Disables UART receiver. @@ -356,7 +344,7 @@ impl UART { /// This function is private, as it should be used only in state transition or loopback /// configuration code. pub(super) fn disable_receiver(&mut self) { - self.registers_ref().cr.write(|w| w.rxdis().set_bit()); + Instance::registers().cr.write(|w| w.rxdis().set_bit()); } /// Enables UART transmitter. @@ -367,7 +355,7 @@ impl UART { /// This function is private, as it should be used only in state transition or loopback /// configuration code. pub(super) fn enable_transmitter(&mut self) { - self.registers_ref().cr.write(|w| w.txen().set_bit()); + Instance::registers().cr.write(|w| w.txen().set_bit()); } /// Disables UART transmitter. @@ -378,7 +366,7 @@ impl UART { /// This function is private, as it should be used only in state transition or loopback /// configuration code. pub(super) fn disable_transmitter(&mut self) { - self.registers_ref().cr.write(|w| w.txdis().set_bit()); + Instance::registers().cr.write(|w| w.txdis().set_bit()); } /// Switches UART into normal mode. This is the default mode of operation. @@ -388,7 +376,7 @@ impl UART { /// /// In this mode, transmitter is connected to TX line and receiver to RX line. pub(super) fn internal_switch_to_normal_mode(&mut self) { - self.registers_ref().mr.modify(|_, w| w.chmode().normal()); + Instance::registers().mr.modify(|_, w| w.chmode().normal()); } /// Returns current UART baudrate (in bits per second). @@ -429,7 +417,7 @@ impl UART { /// # Returns /// UART clock divider. If the divider is equal to 0, baud rate clock is disabled. fn internal_get_clock_divider(&self) -> u16 { - self.registers_ref().brgr.read().cd().bits() + Instance::registers().brgr.read().cd().bits() } /// Sets the clock divider. @@ -450,7 +438,9 @@ impl UART { /// side-effect. /// unsafe fn internal_set_clock_divider(&mut self, divider: u16) { - self.registers_ref().brgr.write(|w| w.cd().variant(divider)); + Instance::registers() + .brgr + .write(|w| w.cd().variant(divider)); } /// Sets the RX filtering state. @@ -462,7 +452,7 @@ impl UART { /// * `enabled` - If `true`, RX filtering will be enabled. /// If `false, RX filtering will be disabled. fn internal_set_rx_filter_state(&mut self, enabled: bool) { - self.registers_ref() + Instance::registers() .mr .modify(|_, w| w.filter().variant(bool_to_rx_filter_config(enabled))); } @@ -473,7 +463,7 @@ impl UART { /// This function is private - it might be re-exported (defined in public scope /// without prefix) in concrete state implementation, where it's safe to use. fn internal_reset_status(&mut self) { - self.registers_ref().cr.write(|w| w.rststa().set_bit()); + Instance::registers().cr.write(|w| w.rststa().set_bit()); } /// Configures UART using provided settings. @@ -492,7 +482,7 @@ impl UART { } // Set source clock and parity bit config, disable loopback and RX filtering. - self.registers_ref().mr.write(|w| { + Instance::registers().mr.write(|w| { w.chmode() .normal() .brsrcck() diff --git a/arch/cortex-m/samv71-hal/src/uart/metadata.rs b/arch/cortex-m/samv71-hal/src/uart/metadata.rs index 08de4230..50569b5c 100644 --- a/arch/cortex-m/samv71-hal/src/uart/metadata.rs +++ b/arch/cortex-m/samv71-hal/src/uart/metadata.rs @@ -1,20 +1,30 @@ //! Module containing meta-traits and their implementations for HAL UART driver -pub(super) use crate::pac::uart0::RegisterBlock; +use crate::pac::uart0::RegisterBlock; pub use crate::pac::{UART0, UART1, UART2, UART3, UART4}; /// Trait for PAC UART instances. /// /// This trait erases the type of UART instance, so it can be used as /// generic argument for [`UART`](super::UART) instead of concrete type. -pub trait UartMetadata { +pub trait UARTMetadata { /// Pointer to UART registers. const REGISTERS: *const RegisterBlock; + + /// Returns a reference to UART's register block. + /// + /// # Safety + /// This function dereferences a raw pointer. + /// It's safe to use, as long as there aren't multiple instances of the same UART peripheral. + #[inline(always)] + fn registers() -> &'static RegisterBlock { + unsafe { &*Self::REGISTERS } + } } /// Internal macro used to generate UartMetadata implementations for every available UART. macro_rules! implement_uart_metadata_for { ($uart:ty) => { - impl UartMetadata for $uart { + impl UARTMetadata for $uart { const REGISTERS: *const RegisterBlock = <$uart>::PTR; } }; diff --git a/arch/cortex-m/samv71-hal/src/uart/states/bidirectional.rs b/arch/cortex-m/samv71-hal/src/uart/states/bidirectional.rs index f41204e7..e4dadf93 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/bidirectional.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/bidirectional.rs @@ -1,8 +1,8 @@ //! Module with implementation of UART in bidirectional mode. //! -use crate::uart::{metadata::UartMetadata, Bidirectional, UART}; +use crate::uart::{metadata::UARTMetadata, Bidirectional, UART}; -impl UART { +impl UART { /// Switches UART into local loopback mode. /// /// In this mode, transmitter is internally connected to receiver. @@ -12,7 +12,7 @@ impl UART { /// Effectively, every sent byte should be automatically received by /// the same UART. pub fn switch_to_local_loopback_mode(&mut self) { - self.registers_ref() + Instance::registers() .mr .modify(|_, w| w.chmode().local_loopback()); } diff --git a/arch/cortex-m/samv71-hal/src/uart/states/configured.rs b/arch/cortex-m/samv71-hal/src/uart/states/configured.rs index fb19ae43..bc00ddf6 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/configured.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/configured.rs @@ -2,16 +2,16 @@ use crate::uart::{ config::{calculate_clock_divider, ConfigurationError, LoopbackMode}, - metadata::UartMetadata, + metadata::UARTMetadata, ClockSource, Config, Configured, Frequency, Interrupt, ParityBit, Status, UART, }; -impl UART { +impl UART { /// Returns current UART status. /// /// Error flags **must** be cleared manually by calling [`UART::reset_status`]. pub fn status(&self) -> Status { - let reg = self.registers_ref().sr.read(); + let reg = Instance::registers().sr.read(); Status { comparison_matched: reg.cmp().bit_is_set(), @@ -46,7 +46,7 @@ impl UART { /// /// for details. pub fn loopback_mode(&self) -> LoopbackMode { - self.registers_ref().mr.read().chmode().variant().into() + Instance::registers().mr.read().chmode().variant().into() } /// Switches UART into normal mode. This is the default mode of operation. @@ -65,14 +65,14 @@ impl UART { /// /// Communication is impossible in this mode. pub fn switch_to_remote_loopback_mode(&mut self) { - self.registers_ref() + Instance::registers() .mr .modify(|_, w| w.chmode().remote_loopback()); } /// Returns `true` if specified interrupt is currently enabled. pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - let reg = self.registers_ref().imr.read(); + let reg = Instance::registers().imr.read(); match interrupt { Interrupt::Comparison => reg.cmp().bit_is_set(), @@ -87,7 +87,7 @@ impl UART { /// Enables specified interrupt. pub fn enable_interrupt(&mut self, interrupt: Interrupt) { - self.registers_ref().ier.write(|w| match interrupt { + Instance::registers().ier.write(|w| match interrupt { Interrupt::Comparison => w.cmp().set_bit(), Interrupt::TxEmpty => w.txempty().set_bit(), Interrupt::ParityError => w.pare().set_bit(), @@ -100,7 +100,7 @@ impl UART { /// Disables specified interrupt. pub fn disable_interrupt(&mut self, interrupt: Interrupt) { - self.registers_ref().idr.write(|w| match interrupt { + Instance::registers().idr.write(|w| match interrupt { Interrupt::Comparison => w.cmp().set_bit(), Interrupt::TxEmpty => w.txempty().set_bit(), Interrupt::ParityError => w.pare().set_bit(), @@ -113,7 +113,7 @@ impl UART { /// Returns current UART [`Config`]. pub fn config(&self) -> Config { - let reg = self.registers_ref().mr.read(); + let reg = Instance::registers().mr.read(); // All following `unwraps` are deliberate. If creating Config fails // here, then the internal state of UART (or the driver) is invalid, @@ -164,7 +164,7 @@ impl UART { /// /// Clock source (and it's frequency) can only be changed by re-initializing UART driver. pub fn clock_source(&self) -> ClockSource { - self.registers_ref().mr.read().brsrcck().variant().into() + Instance::registers().mr.read().brsrcck().variant().into() } /// Returns currently configured clock source frequency. @@ -177,7 +177,7 @@ impl UART { /// Returns current parity bit configuration. pub fn parity_bit(&self) -> ParityBit { // If the register holds invalid parity bit configuration, it's reasonable to panic here. - self.registers_ref() + Instance::registers() .mr .read() .par() @@ -191,7 +191,7 @@ impl UART { /// # Parameters /// * `config` - New parity bit config pub fn set_parity_bit(&mut self, config: ParityBit) { - self.registers_ref() + Instance::registers() .mr .modify(|_, w| w.par().variant(config.into())); } diff --git a/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs b/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs index bbfc2396..a6370ab0 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs @@ -1,8 +1,8 @@ //! Module with implementation of UART in receiver mode. -use crate::uart::{config::rx_filter_config_to_bool, metadata::UartMetadata, Error, Receive, UART}; +use crate::uart::{config::rx_filter_config_to_bool, metadata::UARTMetadata, Error, Receive, UART}; -impl UART { +impl UART { /// Receives a single byte. Blocks until a byte is received, or timeout is hit. /// /// # Parameters @@ -25,21 +25,21 @@ impl UART { /// Will return `0` if no data has been received yet. #[inline(always)] fn get_received_byte(&self) -> u8 { - self.registers_ref().rhr.read().rxchr().bits() + Instance::registers().rhr.read().rxchr().bits() } /// Resets UART receiver. /// /// Any pending byte reception is aborted when the receiver is reset. pub fn reset_receiver(&mut self) { - self.registers_ref().cr.write(|w| w.rstrx().set_bit()); + Instance::registers().cr.write(|w| w.rstrx().set_bit()); } /// Returns true if receive line is filtered. /// /// Filtering is done using a three-sample filter (16x-bit clock, 2 over 3 majority). pub fn is_rx_filter_enabled(&self) -> bool { - rx_filter_config_to_bool(self.registers_ref().mr.read().filter().variant()) + rx_filter_config_to_bool(Instance::registers().mr.read().filter().variant()) } /// Sets receive line filtering state. @@ -56,7 +56,7 @@ impl UART { /// **Transmitter is disconnected from TX line, and therefore unusable in this mode**. /// Receiver operates normally in this mode. pub fn switch_to_automatic_echo_mode(&mut self) { - self.registers_ref() + Instance::registers() .mr .modify(|_, w| w.chmode().automatic()); } diff --git a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs index ee8175bd..f55b5586 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs @@ -1,13 +1,13 @@ //! Module with implementation of UART in transmitter mode. //! -use crate::uart::{metadata::UartMetadata, Error, Transmit, UART}; +use crate::uart::{metadata::UARTMetadata, Error, Transmit, UART}; -impl UART { +impl UART { /// Resets UART transmitter. /// /// Any pending byte transmission is aborted when the transmitter is reset. pub fn reset_transmitter(&mut self) { - self.registers_ref().cr.write(|w| w.rsttx().set_bit()); + Instance::registers().cr.write(|w| w.rsttx().set_bit()); } /// Transmits a single byte. @@ -76,7 +76,7 @@ impl UART { /// Doesn't perform any checks. This is simply a wrapper for register write. #[inline(always)] fn set_transmitted_byte(&mut self, byte: u8) { - self.registers_ref().thr.write(|w| w.txchr().variant(byte)); + Instance::registers().thr.write(|w| w.txchr().variant(byte)); } /// Blocks the CPU until either the transmission is complete, or timeout is hit. From 0ae713668416fd499dcd9870bce5fe0a0025198c Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 16 Oct 2023 08:48:57 +0200 Subject: [PATCH 5/9] HAL: Added utils module --- arch/cortex-m/samv71-hal/src/lib.rs | 1 + arch/cortex-m/samv71-hal/src/utils.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 arch/cortex-m/samv71-hal/src/utils.rs diff --git a/arch/cortex-m/samv71-hal/src/lib.rs b/arch/cortex-m/samv71-hal/src/lib.rs index e63c67ab..e1ec8205 100644 --- a/arch/cortex-m/samv71-hal/src/lib.rs +++ b/arch/cortex-m/samv71-hal/src/lib.rs @@ -19,4 +19,5 @@ pub mod pio; pub mod pmc; pub mod timer; pub mod uart; +pub mod utils; pub mod watchdog; diff --git a/arch/cortex-m/samv71-hal/src/utils.rs b/arch/cortex-m/samv71-hal/src/utils.rs new file mode 100644 index 00000000..ca188f25 --- /dev/null +++ b/arch/cortex-m/samv71-hal/src/utils.rs @@ -0,0 +1,23 @@ +//! Utility functions that provide abstraction over common HAL operations. + +/// Waits until provided functor returns `true`. +/// +/// # Parameters +/// * `check_function` - Functor that returns `true` when operation finishes, and `false` otherwise. +/// * `max_checks_amount` - Maximum amount of `functor` executions. +/// +/// # Returns +/// `Some(u32)`, with amount of checks left before "timeout", or `None` if maximum checks amount +/// has been reached. +pub fn wait_until(check_function: F, max_checks_amount: u32) -> Option +where + F: Fn() -> bool, +{ + for wasted_cycles in 0..max_checks_amount { + if check_function() { + return Some(max_checks_amount - wasted_cycles); + } + } + + None +} From a8342b850befc8a7acfb11fa6cee612c77a61e61 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 16 Oct 2023 09:37:12 +0200 Subject: [PATCH 6/9] UART: Moved I/O functions from main struct to Reader and Writer structs --- arch/cortex-m/samv71-hal/src/uart.rs | 16 ++ arch/cortex-m/samv71-hal/src/uart/reader.rs | 108 ++++++++++++ .../samv71-hal/src/uart/states/configured.rs | 50 +----- .../samv71-hal/src/uart/states/receiver.rs | 52 ++---- .../samv71-hal/src/uart/states/transmitter.rs | 111 ++---------- arch/cortex-m/samv71-hal/src/uart/status.rs | 16 ++ arch/cortex-m/samv71-hal/src/uart/writer.rs | 162 ++++++++++++++++++ 7 files changed, 343 insertions(+), 172 deletions(-) create mode 100644 arch/cortex-m/samv71-hal/src/uart/reader.rs create mode 100644 arch/cortex-m/samv71-hal/src/uart/writer.rs diff --git a/arch/cortex-m/samv71-hal/src/uart.rs b/arch/cortex-m/samv71-hal/src/uart.rs index e563e072..ff017ba3 100644 --- a/arch/cortex-m/samv71-hal/src/uart.rs +++ b/arch/cortex-m/samv71-hal/src/uart.rs @@ -50,6 +50,8 @@ extern crate embedded_io; use core::marker::PhantomData; use self::config::{bool_to_rx_filter_config, calculate_baudrate}; +use self::reader::Reader; +use self::writer::Writer; pub use embedded_io::ErrorKind as Error; @@ -58,8 +60,10 @@ pub use super::time::HertzU32 as Frequency; pub mod config; pub mod interrupt; pub mod metadata; +pub mod reader; pub mod states; pub mod status; +pub mod writer; pub use self::config::{ClockSource, Config, ParityBit, ReceiverConfig}; pub use self::interrupt::Interrupt; @@ -166,6 +170,14 @@ pub struct UART { /// the clock source or it's frequency, otherwise /// UART will not work correctly. clock_source_frequency: Option, + /// UART Reader instance. + /// Can be taken using [`UART::take_reader`] in Receiver mode. + /// Can be put here after taking it using [`UART::put_reader`] in Receiver mode. + reader: Option>, + /// UART Writer instance. + /// Can be taken using [`UART::take_writer`] in Transmitter mode. + /// Can be put here after taking it using [`UART::put_writer`] in Transmitter mode. + writer: Option>, /// PAC UART instance metadata. _meta: PhantomData, /// State metadata. @@ -192,6 +204,8 @@ impl UART { pub fn new(_uart: Instance) -> Self { Self { clock_source_frequency: None, + reader: Some(Reader::new()), + writer: Some(Writer::new()), _meta: PhantomData, _state: PhantomData, } @@ -321,6 +335,8 @@ impl UART { const fn transform(uart: UART) -> Self { Self { clock_source_frequency: uart.clock_source_frequency, + reader: uart.reader, + writer: uart.writer, _meta: PhantomData, _state: PhantomData, } diff --git a/arch/cortex-m/samv71-hal/src/uart/reader.rs b/arch/cortex-m/samv71-hal/src/uart/reader.rs new file mode 100644 index 00000000..c305dd8a --- /dev/null +++ b/arch/cortex-m/samv71-hal/src/uart/reader.rs @@ -0,0 +1,108 @@ +//! UART Reader implementation. +//! +//! Reader can be used to receive data via UART. + +use core::marker::PhantomData; + +use crate::utils::wait_until; + +use super::Error; +use super::Status; +use super::UARTMetadata; + +/// This structure can be used to receive data via UART. +/// +/// Reader instance is created by [`UART`](super::UART) and can be taken from it using +/// [`UART::take_reader`](super::UART::take_reader) method. +/// Once taken, it can be put inside UART again using [`UART::put_reader`](super::UART::put_reader) +/// for storage. +/// +/// # Safety +/// If Reader is used while UART receiver is disabled, it will always return [`Error::TimedOut`] on +/// blocking operations. +/// +/// Reader is thread-safe, as it doesn't share any (mutable) state with UART or Writer, and +/// there can be only a single instance of Reader per UART. +pub struct Reader { + /// UART instance marker. + _uart: PhantomData, +} + +impl Reader { + /// Receives a single byte. Blocks until a byte is received, or timeout is hit. + /// + /// If you check "receiver ready" flag manually (for example, in IRQ handler), you could use + /// [`Reader::get_received_byte`] instead, as it doesn't perform the additional status check. + /// However, this function will also work fine in that context, it'll just double-check that. + /// + /// # Parameters + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Ok(u8)` if reception was successful, with the value of received byte. + /// `Err(())` on timeout. + pub fn receive_byte(&self, timeout: u32) -> Result { + self.wait_for_byte_reception(timeout) + // This is safe, as we just verified that receiver is ready and RX holding register + // contains a received byte. + .map_or(Err(Error::TimedOut), |_| unsafe { + Ok(self.get_received_byte()) + }) + } + + /// Returns the byte currently stored in received character register. + /// + /// This function is meant to be used primarily in interrupt handlers, as a slightly faster + /// version of [`Reader::receive_byte`] that avoids double-checking the status register. + /// + /// # Safety + /// This function doesn't wait for UART to indicate that there's data in RX register, and will + /// return `0` if there's no received data there, instead of an error. + /// Therefore, it's reasonable to use only if you manually checked if there's new data in UART + /// RX register (by checking "receiver ready" status flag). If you do that, then this function + /// becomes safe to use. + /// + /// # Returns + /// Received byte, if UART status flag indicates that there's one in RX register. + /// `0`` otherwise. + #[inline(always)] + pub unsafe fn get_received_byte(&self) -> u8 { + Instance::registers().rhr.read().rxchr().bits() + } + + /// Returns current UART status. + /// + /// Error flags **must** be cleared manually by calling [`Reader::reset_status`]. + pub fn status(&self) -> Status { + Instance::registers().sr.read().into() + } + + /// Resets UART status by clearing status flags. + /// **This function should usually be called immediately after reading the status.** + #[inline(always)] + pub fn reset_status(&mut self) { + Instance::registers().cr.write(|w| w.rststa().set_bit()); + } + + /// Creates new Reader instance. + /// + /// This function should be called only once for each UART instance. + pub(super) fn new() -> Self { + Self { _uart: PhantomData } + } + + /// Blocks the CPU until either a byte is received, or timeout is hit. + /// + /// # Parameters + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Some(u32)`, with amount of checks left before "timeout" is hit, or `None` if maximum + /// checks amount has been reached. + fn wait_for_byte_reception(&self, timeout: u32) -> Option { + wait_until( + || Instance::registers().sr.read().rxrdy().bit_is_set(), + timeout, + ) + } +} diff --git a/arch/cortex-m/samv71-hal/src/uart/states/configured.rs b/arch/cortex-m/samv71-hal/src/uart/states/configured.rs index bc00ddf6..af375ca7 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/configured.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/configured.rs @@ -9,26 +9,10 @@ use crate::uart::{ impl UART { /// Returns current UART status. /// - /// Error flags **must** be cleared manually by calling [`UART::reset_status`]. + /// In order to clear reception error flags, you must use [`Reader`](crate::uart::Reader) + /// object. pub fn status(&self) -> Status { - let reg = Instance::registers().sr.read(); - - Status { - comparison_matched: reg.cmp().bit_is_set(), - transmitter_empty: reg.txempty().bit_is_set(), - parity_error: reg.pare().bit_is_set(), - framing_error: reg.frame().bit_is_set(), - overrun_error: reg.ovre().bit_is_set(), - transmitter_ready: reg.txrdy().bit_is_set(), - receiver_ready: reg.rxrdy().bit_is_set(), - } - } - - /// Resets UART status by clearing status flags. - /// **This function should usually be called immediately after reading the status.** - #[inline(always)] - pub fn reset_status(&mut self) { - self.internal_reset_status() + Instance::registers().sr.read().into() } /// Returns current loopback mode of UART. @@ -232,32 +216,4 @@ impl UART { pub unsafe fn set_clock_divider(&mut self, divider: u16) { self.internal_set_clock_divider(divider) } - - /// Waits until provided functor returns `true`. Functor receives UART status and should return - /// the specific flag (or their combination). - /// - /// # Parameters - /// * `status_checker` - Functor, returning flag or combination of status flags to wait for - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to spend on waiting for the flag. - /// This is basically an amount of loop iterations with status checks. - /// - /// # Returns - /// `Ok(u32)` if functor returned `true` before timeout, `Err(())` if timeout has been reached. - /// The value returned on success indicates how much "cycles" are left for next timeout. - pub(super) fn wait_for_status_flag( - &self, - status_checker: F, - timeout_cycles: u32, - ) -> Result - where - F: Fn(Status) -> bool, - { - for wasted_cycles in 0..timeout_cycles { - if status_checker(self.status()) { - return Ok(timeout_cycles - wasted_cycles); - } - } - - Err(()) - } } diff --git a/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs b/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs index a6370ab0..3c9d61a9 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/receiver.rs @@ -1,31 +1,30 @@ //! Module with implementation of UART in receiver mode. -use crate::uart::{config::rx_filter_config_to_bool, metadata::UARTMetadata, Error, Receive, UART}; +use crate::uart::{ + config::rx_filter_config_to_bool, metadata::UARTMetadata, reader::Reader, Receive, UART, +}; impl UART { - /// Receives a single byte. Blocks until a byte is received, or timeout is hit. - /// - /// # Parameters - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to spend on waiting for the flag. - /// This is basically an amount of loop iterations with status checks. + /// Takes [`Reader`] instance out of UART. + /// There can only be a single instance of Reader per UART, and this is the only way to get it. + /// You can check whether UART stores Reader instance using [`UART::has_reader`]. + /// Reader can be put back into UART using [`UART::put_reader`]. /// /// # Returns - /// `Ok(u8)` if reception was successful, the value is the received byte. - /// `Err(())` on timeout. - pub fn receive_byte(&self, timeout_cycles: u32) -> Result { - match self.wait_for_byte_reception(timeout_cycles) { - Ok(_) => Ok(self.get_received_byte()), - Err(_) => Err(Error::TimedOut), - } + /// `Some(Reader)` if instance of a reader is currently stored in UART. + /// `None` if it was already taken. + pub fn take_reader(&mut self) -> Option> { + self.reader.take() } - /// Returns the byte currently stored in received character register. - /// - /// Doesn't perform any checks. This is simply a wrapper for register read. - /// Will return `0` if no data has been received yet. - #[inline(always)] - fn get_received_byte(&self) -> u8 { - Instance::registers().rhr.read().rxchr().bits() + /// Stores [`Reader`] instance inside UART. + pub fn put_reader(&mut self, reader: Reader) { + self.reader.replace(reader); + } + + /// Returns `true` if UART currently has [`Reader`] instance. + pub fn has_reader(&self) -> bool { + self.reader.is_some() } /// Resets UART receiver. @@ -60,17 +59,4 @@ impl UART { .mr .modify(|_, w| w.chmode().automatic()); } - - /// Blocks the CPU until a byte is received. - /// - /// # Parameters - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait for byte reception. - /// This is basically an amount of loop iterations with status checks. - /// - /// # Returns - /// `Ok(u32)` if byte was received before timeout, `Err(())` if timeout has been reached. - /// The value returned on success indicates how much CPU cycles are left for next timeout. - fn wait_for_byte_reception(&self, timeout_cycles: u32) -> Result { - self.wait_for_status_flag(|status| status.receiver_ready, timeout_cycles) - } } diff --git a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs index f55b5586..e8b5219e 100644 --- a/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs +++ b/arch/cortex-m/samv71-hal/src/uart/states/transmitter.rs @@ -1,107 +1,34 @@ //! Module with implementation of UART in transmitter mode. //! -use crate::uart::{metadata::UARTMetadata, Error, Transmit, UART}; +use crate::uart::{metadata::UARTMetadata, writer::Writer, Transmit, UART}; impl UART { - /// Resets UART transmitter. - /// - /// Any pending byte transmission is aborted when the transmitter is reset. - pub fn reset_transmitter(&mut self) { - Instance::registers().cr.write(|w| w.rsttx().set_bit()); - } - - /// Transmits a single byte. - /// Does not wait until transmission is completed, use [`UART::flush`] for that. - /// - /// # Parameters - /// * `byte` - Byte to transmit - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until transmitter is ready. - /// This is basically an amount of loop iterations with status checks. - /// - /// # Returns - /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. - pub fn transmit_byte(&mut self, byte: u8, timeout_cycles: u32) -> Result<(), Error> { - match self.wait_for_transmitter_ready(timeout_cycles) { - Ok(_) => { - self.set_transmitted_byte(byte); - Ok(()) - } - Err(_) => Err(Error::TimedOut), - } - } - - /// Transmits multiple bytes. Blocks until the transmission is completed, or timeout - /// is hit. Flushes the UART after transmitting last byte. - /// - /// # Parameters - /// * `bytes` - Bytes to transmit. - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until transmission is complete. - /// **Timeout is defined for the whole transaction**, not for single byte's transmission. - /// This is basically an amount of loop iterations with status checks. - /// - /// # Returns - /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. - pub fn transmit_bytes(&mut self, bytes: &[u8], timeout_cycles: u32) -> Result<(), Error> { - if let Ok(mut timeout_cycles) = self.wait_for_transmitter_ready(timeout_cycles) { - for &byte in bytes { - self.set_transmitted_byte(byte); - match self.wait_for_transmitter_ready(timeout_cycles) { - Ok(remaining_timeout) => timeout_cycles = remaining_timeout, - Err(_) => return Err(Error::TimedOut), - } - } - - return self.flush(timeout_cycles); - } - - Err(Error::TimedOut) - } - - /// Flushes the UART by waiting until currently transmitted character is processed. - /// - /// # Parameters - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" to wait until a single byte is transmitted. - /// This is basically an amount of loop iterations with status checks. + /// Takes [`Writer`] instance out of UART. + /// There can only be a single instance of Writer per UART, and this is the only way to get it. + /// You can check whether UART stores Writer instance using [`UART::has_writer`]. + /// Writer can be put back into UART using [`UART::put_writer`]. /// /// # Returns - /// `Ok(())` on successful flush, `Err(Error::TimedOut)` if timeout has been reached. - pub fn flush(&mut self, timeout_cycles: u32) -> Result<(), Error> { - self.wait_for_transmission_to_complete(timeout_cycles) - .map(|_| ()) - .map_err(|_| Error::TimedOut) + /// `Some(Writer)` if instance of a writer is currently stored in UART. + /// `None` if it was already taken. + pub fn take_writer(&mut self) -> Option> { + self.writer.take() } - /// Writes a byte to be transmitted next into TX holding register. - /// - /// Doesn't perform any checks. This is simply a wrapper for register write. - #[inline(always)] - fn set_transmitted_byte(&mut self, byte: u8) { - Instance::registers().thr.write(|w| w.txchr().variant(byte)); + /// Stores [`Writer`] instance inside UART. + pub fn put_writer(&mut self, writer: Writer) { + self.writer.replace(writer); } - /// Blocks the CPU until either the transmission is complete, or timeout is hit. - /// - /// # Parameters - /// * `timeout_cycles` - Maximum amount of arbitrary "cycles" the transmission should take. - /// This is basically an amount of loop iterations with status checks. - /// - /// # Returns - /// `Ok(u32)` if transmission was completed before timeout, `Err(())` if timeout has been reached. - /// The value returned on success indicates how much CPU cycles are left for next timeout. - fn wait_for_transmission_to_complete(&self, timeout_cycles: u32) -> Result { - self.wait_for_status_flag(|status| status.transmitter_empty, timeout_cycles) + /// Returns `true` if UART currently has [`Writer`] instance. + pub fn has_writer(&self) -> bool { + self.writer.is_some() } - /// Blocks the CPU until transmit holding register is empty and ready for next byte. - /// - /// # Parameters - /// * `timeout_cycles` - Maximum amount arbitrary "cycles" to wait for the transmitter. - /// This is basically an amount of loop iterations with status checks. + /// Resets UART transmitter. /// - /// # Returns - /// `Ok(u32)` if transmitter became ready before timeout, `Err(())` if timeout has been reached. - /// The value returned on success indicates how much CPU cycles are left for next timeout. - fn wait_for_transmitter_ready(&self, timeout_cycles: u32) -> Result { - self.wait_for_status_flag(|status| status.transmitter_ready, timeout_cycles) + /// Any pending byte transmission is aborted when the transmitter is reset. + pub fn reset_transmitter(&mut self) { + Instance::registers().cr.write(|w| w.rsttx().set_bit()); } } diff --git a/arch/cortex-m/samv71-hal/src/uart/status.rs b/arch/cortex-m/samv71-hal/src/uart/status.rs index 3a585606..5d7815f1 100644 --- a/arch/cortex-m/samv71-hal/src/uart/status.rs +++ b/arch/cortex-m/samv71-hal/src/uart/status.rs @@ -1,5 +1,7 @@ //! Module with structures and enumerations representing UART status. +use samv71q21_pac::uart0::sr; + /// Structure representing UART status. pub struct Status { /// `true` if received character matches the programmed comparison criteria. @@ -20,3 +22,17 @@ pub struct Status { /// `true` if RX holding register contains a complete character, ready to be read. pub receiver_ready: bool, } + +impl From for Status { + fn from(reg: sr::R) -> Self { + Status { + comparison_matched: reg.cmp().bit_is_set(), + transmitter_empty: reg.txempty().bit_is_set(), + parity_error: reg.pare().bit_is_set(), + framing_error: reg.frame().bit_is_set(), + overrun_error: reg.ovre().bit_is_set(), + transmitter_ready: reg.txrdy().bit_is_set(), + receiver_ready: reg.rxrdy().bit_is_set(), + } + } +} diff --git a/arch/cortex-m/samv71-hal/src/uart/writer.rs b/arch/cortex-m/samv71-hal/src/uart/writer.rs new file mode 100644 index 00000000..b5409a85 --- /dev/null +++ b/arch/cortex-m/samv71-hal/src/uart/writer.rs @@ -0,0 +1,162 @@ +//! UART Writer implementation. +//! +//! Writer can be used to transmit data via UART. + +use core::marker::PhantomData; + +use crate::utils::wait_until; + +use super::Error; +use super::Status; +use super::UARTMetadata; + +/// This structure can be used to transmit data via UART. +/// +/// Writer instance is created by [`UART`](super::UART) and can be taken from it using +/// [`UART::take_writer`](super::UART::take_writer) method. +/// Once taken, it can be put inside UART again using [`UART::put_writer`](super::UART::put_writer) +/// for storage. +/// +/// # Safety +/// If Writer is used while UART transmitter is disabled, it will always return [`Error::TimedOut`] +/// on blocking operations. +/// +/// Writer is thread-safe, as it doesn't share any (mutable) state with UART or Reader, and +/// there can be only a single instance of Writer per UART. +pub struct Writer { + /// UART instance marker. + _uart: PhantomData, +} + +impl Writer { + /// Transmits a single byte. + /// Waits for UART TX register to be empty. + /// Does not wait until transmission is completed, use [`Writer::flush`] if you want to make sure + /// of that. + /// + /// If you check "transmitter ready" flag manually (for example, in IRQ handler), you could use + /// [`Writer::set_transmitted_byte`] instead, as it doesn't perform the additional status check. + /// However, this function will also work fine in that context, it'll just double-check that. + /// + /// # Parameters + /// * `byte` - Byte to transmit + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. + pub fn transmit_byte(&mut self, byte: u8, timeout: u32) -> Result<(), Error> { + self.wait_for_transmitter_ready(timeout) + // Safety: this is safe, as we just verified that transmitter is ready. + .map_or(Err(Error::TimedOut), |_| unsafe { + self.set_transmitted_byte(byte); + Ok(()) + }) + } + + /// Transmits multiple bytes. Blocks until the transmission is completed, or timeout + /// is hit. Automatically flushes the UART after transmitting last byte. + /// + /// # Parameters + /// * `bytes` - Bytes to transmit. + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// Timeout is defined for **the whole transaction**, not single byte transmission. + /// + /// # Returns + /// `Ok(())` on successful transmission, `Err(Error::TimedOut)` if timeout has been reached. + pub fn transmit_bytes(&mut self, bytes: &[u8], timeout: u32) -> Result<(), Error> { + if let Some(mut timeout_cycles) = self.wait_for_transmitter_ready(timeout) { + for &byte in bytes { + // Safety: this is safe, as we just verified that transmitter is ready. + unsafe { + self.set_transmitted_byte(byte); + } + + match self.wait_for_transmitter_ready(timeout_cycles) { + Some(remaining_timeout) => timeout_cycles = remaining_timeout, + None => return Err(Error::TimedOut), + } + } + + return self.flush(timeout_cycles); + } + + Err(Error::TimedOut) + } + + /// Flushes the UART by waiting until currently transmitted character is processed. + /// + /// # Parameters + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Ok(())` on successful flush, `Err(Error::TimedOut)` if timeout has been reached. + pub fn flush(&mut self, timeout: u32) -> Result<(), Error> { + self.wait_for_transmission_to_complete(timeout) + .map_or(Err(Error::TimedOut), |_| Ok(())) + } + + /// Writes a byte to be transmitted next into TX holding register. + /// + /// This function is meant to be used primarily in interrupt handlers, as a slightly faster + /// version of [`Writer::transmit_byte`] that avoids double-checking the status register. + /// + /// # Safety + /// This function doesn't wait for TX holding register to become empty, unlike + /// [`Writer::transmit_byte`]. Therefore, it's safe to use only if you do that manually by + /// checking the "transmitter ready" status flag. + /// + /// "Transmitter empty" flag can also be used for that purpose, but be aware that it's set when + /// there's no data in TX holding register **and** transmitter is idle (flushed), so unless + /// you have to make sure that each byte placed in TX holding register is transmitted before + /// trying to transmit the next one, it will induce unnecessary performance penalty and may + /// potentially cause communication issues, depending on receiver's code. + /// + /// If transmitted byte is set while there's already a byte in TX holding register, existing + /// byte will be overwritten and not sent. + #[inline(always)] + pub unsafe fn set_transmitted_byte(&mut self, byte: u8) { + Instance::registers().thr.write(|w| w.txchr().variant(byte)); + } + + /// Returns current UART status. + pub fn status(&self) -> Status { + Instance::registers().sr.read().into() + } + + /// Creates new Reader instance. + /// + /// This function should be called only once for each UART instance. + pub(super) fn new() -> Self { + Self { _uart: PhantomData } + } + + /// Blocks the CPU until either the transmission is complete, or timeout is hit. + /// + /// # Parameters + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Some(u32)`, with amount of checks left before "timeout" is hit, or `None` if maximum + /// checks amount has been reached. + fn wait_for_transmission_to_complete(&self, timeout: u32) -> Option { + wait_until( + || Instance::registers().sr.read().txrdy().bit_is_set(), + timeout, + ) + } + + /// Blocks the CPU until transmit holding register is empty and ready for next byte. + /// + /// # Parameters + /// * `timeout` - Maximum amount of UART status checks before declaring timeout. + /// + /// # Returns + /// `Some(u32)`, with amount of checks left before "timeout" is hit, or `None` if maximum + /// checks amount has been reached. + fn wait_for_transmitter_ready(&self, timeout: u32) -> Option { + wait_until( + || Instance::registers().sr.read().txempty().bit_is_set(), + timeout, + ) + } +} From 1d9def1d9e8b4b817b9b47d1aa9cdecea85903d4 Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 16 Oct 2023 09:37:40 +0200 Subject: [PATCH 7/9] Examples: Fixed UART loopback example post-refactor --- examples/samv71-hal-uart-loopback/src/main.rs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/samv71-hal-uart-loopback/src/main.rs b/examples/samv71-hal-uart-loopback/src/main.rs index 3bc1e0d8..601dbed5 100644 --- a/examples/samv71-hal-uart-loopback/src/main.rs +++ b/examples/samv71-hal-uart-loopback/src/main.rs @@ -7,6 +7,8 @@ extern crate panic_rtt_target; use aerugo::hal::drivers::pio::{pin::Peripheral, Port}; use aerugo::hal::drivers::pmc::config::PeripheralId; +use aerugo::hal::drivers::uart::reader::Reader; +use aerugo::hal::drivers::uart::writer::Writer; use aerugo::hal::drivers::uart::{Bidirectional, Config, NotConfigured, ReceiverConfig, UART}; use aerugo::hal::user_peripherals::{PIOD, PMC, UART4}; use aerugo::time::RateExtU32; @@ -17,11 +19,11 @@ use aerugo::{ use rt::entry; fn uart_task(_: (), context: &mut UartTaskContext, _: &'static dyn RuntimeApi) { - let uart = &mut context.uart; - - uart.transmit_byte(context.byte_to_transmit, 1_000_000) + context + .writer + .transmit_byte(context.byte_to_transmit, 1_000_000) .unwrap(); - let received_byte = uart.receive_byte(1_000_000).unwrap(); + let received_byte = context.reader.receive_byte(1_000_000).unwrap(); logln!( "Transmitted {:#02X}, received {:#02X}", @@ -33,7 +35,8 @@ fn uart_task(_: (), context: &mut UartTaskContext, _: &'static dyn RuntimeApi) { } struct UartTaskContext { - pub uart: UART, + pub writer: Writer, + pub reader: Reader, pub byte_to_transmit: u8, } @@ -62,7 +65,7 @@ fn init_uart(uart: UART) -> UART { uart } -fn init_tasks(aerugo: &'static impl InitApi, uart: UART) { +fn init_tasks(aerugo: &'static impl InitApi, mut uart: UART) { logln!("Initializing tasks..."); let uart_task_config = TaskletConfig { @@ -70,8 +73,12 @@ fn init_tasks(aerugo: &'static impl InitApi, uart: UART) { ..Default::default() }; + let reader = uart.take_reader().unwrap(); + let writer = uart.take_writer().unwrap(); + let uart_task_context = UartTaskContext { - uart, + reader, + writer, byte_to_transmit: 0, }; From 71f98534d299f22924d57110059b4c94ca28e0ec Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Mon, 16 Oct 2023 13:41:12 +0200 Subject: [PATCH 8/9] UART: Reader functions now require `&mut self` because of side-effects --- arch/cortex-m/samv71-hal/src/uart/reader.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/cortex-m/samv71-hal/src/uart/reader.rs b/arch/cortex-m/samv71-hal/src/uart/reader.rs index c305dd8a..1cad5bf8 100644 --- a/arch/cortex-m/samv71-hal/src/uart/reader.rs +++ b/arch/cortex-m/samv71-hal/src/uart/reader.rs @@ -35,13 +35,16 @@ impl Reader { /// [`Reader::get_received_byte`] instead, as it doesn't perform the additional status check. /// However, this function will also work fine in that context, it'll just double-check that. /// + /// This function requires mutable access to Reader, as reading the character from RX holding + /// register while "receiver ready" flag is set will reset it's state and clear this flag. + /// /// # Parameters /// * `timeout` - Maximum amount of UART status checks before declaring timeout. /// /// # Returns /// `Ok(u8)` if reception was successful, with the value of received byte. /// `Err(())` on timeout. - pub fn receive_byte(&self, timeout: u32) -> Result { + pub fn receive_byte(&mut self, timeout: u32) -> Result { self.wait_for_byte_reception(timeout) // This is safe, as we just verified that receiver is ready and RX holding register // contains a received byte. @@ -55,6 +58,9 @@ impl Reader { /// This function is meant to be used primarily in interrupt handlers, as a slightly faster /// version of [`Reader::receive_byte`] that avoids double-checking the status register. /// + /// This function requires mutable access to Reader, as reading the character from RX holding + /// register while "receiver ready" flag is set will reset it's state and clear this flag. + /// /// # Safety /// This function doesn't wait for UART to indicate that there's data in RX register, and will /// return `0` if there's no received data there, instead of an error. @@ -66,7 +72,7 @@ impl Reader { /// Received byte, if UART status flag indicates that there's one in RX register. /// `0`` otherwise. #[inline(always)] - pub unsafe fn get_received_byte(&self) -> u8 { + pub unsafe fn get_received_byte(&mut self) -> u8 { Instance::registers().rhr.read().rxchr().bits() } From 2de0018384197bab9910d921b3c8506df56e4a6e Mon Sep 17 00:00:00 2001 From: Wojciech Olech Date: Thu, 19 Oct 2023 10:55:50 +0200 Subject: [PATCH 9/9] UART: Documentation tweaks Co-authored-by: Filip Demski (Glamhoth) --- arch/cortex-m/samv71-hal/src/uart/reader.rs | 1 + arch/cortex-m/samv71-hal/src/uart/writer.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/cortex-m/samv71-hal/src/uart/reader.rs b/arch/cortex-m/samv71-hal/src/uart/reader.rs index 1cad5bf8..8836f90f 100644 --- a/arch/cortex-m/samv71-hal/src/uart/reader.rs +++ b/arch/cortex-m/samv71-hal/src/uart/reader.rs @@ -84,6 +84,7 @@ impl Reader { } /// Resets UART status by clearing status flags. + /// /// **This function should usually be called immediately after reading the status.** #[inline(always)] pub fn reset_status(&mut self) { diff --git a/arch/cortex-m/samv71-hal/src/uart/writer.rs b/arch/cortex-m/samv71-hal/src/uart/writer.rs index b5409a85..2c4365c3 100644 --- a/arch/cortex-m/samv71-hal/src/uart/writer.rs +++ b/arch/cortex-m/samv71-hal/src/uart/writer.rs @@ -30,6 +30,7 @@ pub struct Writer { impl Writer { /// Transmits a single byte. + /// /// Waits for UART TX register to be empty. /// Does not wait until transmission is completed, use [`Writer::flush`] if you want to make sure /// of that.