Skip to content

Commit

Permalink
implement embedded-io for the UART module and improve the UART module (
Browse files Browse the repository at this point in the history
  • Loading branch information
kiffie authored Dec 29, 2024
1 parent 60de132 commit ba01fe5
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 12 deletions.
3 changes: 2 additions & 1 deletion pic32-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ pic32mx47x = ["pic32mx470/pic32mx47xfxxxl", "device-selected"]
device-selected = []

[dependencies]
nb = "1.0.0"
nb = "1.1.0"
embedded-hal = "1.0.0"
embedded_hal_0_2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
embedded-io = "0.6.1"
mips-mcu = "0.3.0"
mips-rt = "0.3.0"
critical-section = "1.0.0"
Expand Down
251 changes: 240 additions & 11 deletions pic32-hal/src/uart.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! UART driver
use core::convert::Infallible;
use core::fmt;
use core::marker::PhantomData;

Expand All @@ -8,11 +9,12 @@ use crate::pac::{UART1, UART2};
use crate::pps::{input, output, IsConnected, MappedPin};

use embedded_hal_0_2::prelude::*;
use embedded_io::{ReadReady, WriteReady};
use nb::block;

/// Uart
pub struct Uart<UART, RX, TX> {
_uart: PhantomData<UART>,
uart: UART,
rx: RX,
tx: TX,
}
Expand All @@ -27,26 +29,108 @@ pub struct Tx<UART> {
_uart: PhantomData<UART>,
}

/// UART read errors
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ReadError {
/// RX FIFO overrun
Overrun,

/// Parity error
Parity,

/// Framing error
Framing,

/// Break detected
Break,
}

/// UART configuration
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Config {
/// Baudrate
pub baudrate: u32,

/// Parity
pub parity: Parity,

/// Stop bits
pub stopbits: StopBits,
}

impl Config {
/// Create a new UART configuration
pub const fn new(baudrate: u32, parity: Parity, stopbits: StopBits) -> Self {
Config {
baudrate,
parity,
stopbits,
}
}
}

pub const CONFIG_115200_8N1: Config = Config {
baudrate: 115200,
parity: Parity::None,
stopbits: StopBits::One,
};

/// UART Parity settings
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Parity {
/// No parity
None = 0,

/// Even parity
Even = 1,

/// Odd parity
Odd = 2,
}

/// Number of stop bits
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum StopBits {
/// 1 stop bit
One = 0,

/// 2 stop bits
Two = 1,
}

impl embedded_io::Error for ReadError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}

macro_rules! uart_impl {
($Id:ident, $Uart:ident, $Rx:ty, $Tx:ty) => {
impl<RX, TX> Uart<$Uart, MappedPin<RX, $Rx>, MappedPin<TX, $Tx>> {
pub fn $Id(
uart: $Uart,
osc: &Osc,
baudrate: u32,
config: Config,
rx: MappedPin<RX, $Rx>,
tx: MappedPin<TX, $Tx>,
) -> Uart<$Uart, MappedPin<RX, $Rx>, MappedPin<TX, $Tx>>
where
MappedPin<RX, $Rx>: IsConnected,
MappedPin<TX, $Tx>: IsConnected,
{
let brg = osc.pb_clock().0 / (4 * baudrate) - 1;
let brg = osc.pb_clock().0 / (4 * config.baudrate) - 1;
let has_rx = rx.is_connected();
let has_tx = tx.is_connected();
unsafe {
uart.mode.write(|w| w.bits(0));
uart.mode.write(|w| w.brgh().bit(true));
uart.mode.write(|w| {
w.brgh()
.bit(true)
.pdsel()
.bits(config.parity as u8)
.stsel()
.bit(config.stopbits as u8 != 0)
});
uart.sta.write(|w| {
w.urxen()
.bit(has_rx)
Expand All @@ -58,23 +142,34 @@ macro_rules! uart_impl {
uart.brg.write(|w| w.bits(brg));
uart.modeset.write(|w| w.on().bit(true));
}
Uart {
_uart: PhantomData,
rx,
tx,
}
Uart { uart, rx, tx }
}

/// Flush the transmit buffer and then send a break character.
pub fn transmit_break(&mut self) {
let mut tx: Tx<$Uart> = Tx { _uart: PhantomData };
tx.transmit_break();
}

pub fn free(self) -> (MappedPin<RX, $Rx>, MappedPin<TX, $Tx>) {
pub fn free(self) -> ($Uart, MappedPin<RX, $Rx>, MappedPin<TX, $Tx>) {
unsafe { (*$Uart::ptr()).modeclr.write(|w| w.on().bit(true)) };
(self.rx, self.tx)
(self.uart, self.rx, self.tx)
}

pub fn split(self) -> (Tx<$Uart>, Rx<$Uart>) {
(Tx { _uart: PhantomData }, Rx { _uart: PhantomData })
}
}

impl Tx<$Uart> {
/// Flush the transmit buffer and then send a break character.
pub fn transmit_break(&mut self) {
let _ = embedded_io::Write::flush(self);
unsafe { (*$Uart::ptr()).staset.write(|w| w.utxbrk().bit(true)) };
let _ = embedded_io::Write::write_all(self, &[0]);
}
}

impl embedded_hal_0_2::serial::Write<u8> for Uart<$Uart, $Rx, $Tx> {
type Error = ();

Expand Down Expand Up @@ -140,6 +235,140 @@ macro_rules! uart_impl {
result
}
}

impl embedded_io::ErrorType for Tx<$Uart> {
type Error = Infallible;
}

impl embedded_io::WriteReady for Tx<$Uart> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
let utxbf = unsafe { (*$Uart::ptr()).sta.read().utxbf().bit() };
Ok(!utxbf)
}
}

impl embedded_io::Write for Tx<$Uart> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
while !self.write_ready()? {}
let mut len = 0;
for byte in buf {
if !self.write_ready()? {
break;
}
unsafe {
(*$Uart::ptr())
.txreg
.write(|w| w.txreg().bits(*byte as u16));
}
len += 1;
}
Ok(len)
}

fn flush(&mut self) -> Result<(), Self::Error> {
loop {
let trmt = unsafe { (*$Uart::ptr()).sta.read().trmt().bit() };
if trmt {
break;
}
}
Ok(())
}
}

impl embedded_io::ErrorType for Rx<$Uart> {
type Error = ReadError;
}

impl embedded_io::ReadReady for Rx<$Uart> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
let data_avail = unsafe { (*$Uart::ptr()).sta.read().urxda().bit() };
Ok(data_avail)
}
}

impl embedded_io::Read for Rx<$Uart> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}

// check for FIFO overrun
if unsafe { (*$Uart::ptr()).sta.read().oerr().bit() } {
// clear overrun error condition and RX FIFO
unsafe {
(*$Uart::ptr()).staclr.write(|w| w.oerr().bit(true));
}
return Err(ReadError::Overrun);
}

// wait until data is available
while !self.read_ready()? {}

// read as many bytes as possible without blocking
let mut len = 0;
for bufbyte in buf {
if !self.read_ready()? {
break;
}
// check for framing error or break skipping the erroneous byte
if unsafe { (*$Uart::ptr()).sta.read().ferr().bit() } {
let word = unsafe { (*$Uart::ptr()).rxreg.read().bits() };
if word == 0 {
return Err(ReadError::Break);
} else {
return Err(ReadError::Framing);
}
}
// check for parity error skipping the erroneous byte
if unsafe { (*$Uart::ptr()).sta.read().perr().bit() } {
let _skip = unsafe { (*$Uart::ptr()).rxreg.read().bits() };
return Err(ReadError::Parity);
}
*bufbyte = unsafe { (*$Uart::ptr()).rxreg.read().bits() } as u8;
len += 1;
}
Ok(len)
}
}

impl<RX, TX> embedded_io::ErrorType for Uart<$Uart, RX, TX> {
type Error = ReadError;
}

impl<RX, TX> embedded_io::WriteReady for Uart<$Uart, RX, TX> {
fn write_ready(&mut self) -> Result<bool, Self::Error> {
let mut tx: Tx<$Uart> = Tx { _uart: PhantomData };
Ok(tx.write_ready().unwrap_or(false))
}
}

impl<RX, TX> embedded_io::Write for Uart<$Uart, RX, TX> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let mut tx: Tx<$Uart> = Tx { _uart: PhantomData };
Ok(embedded_io::Write::write(&mut tx, buf).unwrap_or(0))
}

fn flush(&mut self) -> Result<(), Self::Error> {
let mut tx: Tx<$Uart> = Tx { _uart: PhantomData };
let _ = embedded_io::Write::flush(&mut tx);
Ok(())
}
}

impl<RX, TX> embedded_io::ReadReady for Uart<$Uart, RX, TX> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
let mut rx: Rx<$Uart> = Rx { _uart: PhantomData };
rx.read_ready()
}
}

impl<RX, TX> embedded_io::Read for Uart<$Uart, RX, TX> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let mut rx: Rx<$Uart> = Rx { _uart: PhantomData };
embedded_io::Read::read(&mut rx, buf)
}
}
};
}

Expand Down

0 comments on commit ba01fe5

Please sign in to comment.