Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement embedded-io for the UART module and improve the UART module #20

Merged
merged 2 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading