Skip to content

Commit

Permalink
Merge pull request #71 from SteelPh0enix/hal-uart-receiver-transmitte…
Browse files Browse the repository at this point in the history
…r-split

UART: Split into Reader/Writer
  • Loading branch information
SteelPh0enix authored Oct 19, 2023
2 parents 0700d2e + 2de0018 commit 565f83f
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 228 deletions.
1 change: 1 addition & 0 deletions arch/cortex-m/samv71-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ pub mod pio;
pub mod pmc;
pub mod timer;
pub mod uart;
pub mod utils;
pub mod watchdog;
62 changes: 34 additions & 28 deletions arch/cortex-m/samv71-hal/src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ extern crate embedded_io;
use core::marker::PhantomData;

use self::config::{bool_to_rx_filter_config, calculate_baudrate};
use self::metadata::RegisterBlock;
use self::reader::Reader;
use self::writer::Writer;

pub use embedded_io::ErrorKind as Error;

Expand All @@ -59,12 +60,14 @@ 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;
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
Expand Down Expand Up @@ -160,20 +163,28 @@ impl<T> 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<Metadata: UartMetadata, CurrentState: State> {
pub struct UART<Instance: UARTMetadata, CurrentState: State> {
/// Frequency of the clock driving UART baudrate.
/// Required for baudrate calculations. Must be
/// manually updated by the user after changing
/// the clock source or it's frequency, otherwise
/// UART will not work correctly.
clock_source_frequency: Option<Frequency>,
/// 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<Reader<Instance>>,
/// 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<Writer<Instance>>,
/// PAC UART instance metadata.
_meta: PhantomData<Metadata>,
_meta: PhantomData<Instance>,
/// State metadata.
_state: PhantomData<CurrentState>,
}

impl<Instance: UartMetadata> UART<Instance, NotConfigured> {
impl<Instance: UARTMetadata> UART<Instance, NotConfigured> {
/// Creates new UART driver instance, consuming PAC UART instance to prevent creating
/// duplicate drivers.
///
Expand All @@ -193,13 +204,15 @@ impl<Instance: UartMetadata> UART<Instance, NotConfigured> {
pub fn new(_uart: Instance) -> Self {
Self {
clock_source_frequency: None,
reader: Some(Reader::new()),
writer: Some(Writer::new()),
_meta: PhantomData,
_state: PhantomData,
}
}
}

impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
impl<Instance: UARTMetadata, AnyState: State> UART<Instance, AnyState> {
/// Transforms UART into `Transmitter` state. Resets UART status before
/// changing the state. Disables loopback and RX filtering.
///
Expand Down Expand Up @@ -292,7 +305,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {

/// 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()
Expand Down Expand Up @@ -322,22 +335,13 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
const fn transform<NewState: State>(uart: UART<Instance, NewState>) -> Self {
Self {
clock_source_frequency: uart.clock_source_frequency,
reader: uart.reader,
writer: uart.writer,
_meta: PhantomData,
_state: PhantomData,
}
}

/// 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`
Expand All @@ -346,7 +350,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -356,7 +360,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -367,7 +371,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -378,7 +382,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -388,7 +392,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
///
/// 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).
Expand Down Expand Up @@ -429,7 +433,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// # 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.
Expand All @@ -450,7 +454,9 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -462,7 +468,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// * `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)));
}
Expand All @@ -473,7 +479,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
/// 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.
Expand All @@ -492,7 +498,7 @@ impl<Instance: UartMetadata, AnyState: State> UART<Instance, AnyState> {
}

// 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()
Expand Down
16 changes: 13 additions & 3 deletions arch/cortex-m/samv71-hal/src/uart/metadata.rs
Original file line number Diff line number Diff line change
@@ -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;
}
};
Expand Down
115 changes: 115 additions & 0 deletions arch/cortex-m/samv71-hal/src/uart/reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! 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<Instance: UARTMetadata> {
/// UART instance marker.
_uart: PhantomData<Instance>,
}

impl<Instance: UARTMetadata> Reader<Instance> {
/// 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.
///
/// 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(&mut self, timeout: u32) -> Result<u8, Error> {
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.
///
/// 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.
/// 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(&mut 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<u32> {
wait_until(
|| Instance::registers().sr.read().rxrdy().bit_is_set(),
timeout,
)
}
}
6 changes: 3 additions & 3 deletions arch/cortex-m/samv71-hal/src/uart/states/bidirectional.rs
Original file line number Diff line number Diff line change
@@ -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<Instance: UartMetadata> UART<Instance, Bidirectional> {
impl<Instance: UARTMetadata> UART<Instance, Bidirectional> {
/// Switches UART into local loopback mode.
///
/// In this mode, transmitter is internally connected to receiver.
Expand All @@ -12,7 +12,7 @@ impl<Instance: UartMetadata> UART<Instance, Bidirectional> {
/// 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());
}
Expand Down
Loading

0 comments on commit 565f83f

Please sign in to comment.