Rust edition bumped & UART implementation
- Also adds UART example
This commit is contained in:
parent
541524c59b
commit
b18e32e0cc
10
CHANGELOG.md
10
CHANGELOG.md
@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### Added
|
||||
|
||||
- UART implementation
|
||||
- UART example
|
||||
- Some bugfixes for GPIO implementation
|
||||
- Rust edition updated to 2021
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
### Added
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "va108xx-hal"
|
||||
version = "0.1.0"
|
||||
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://github.com/robamu-org/va108xx-hal-rs"
|
||||
repository = "https://github.com/robamu-org/va108xx-hal-rs"
|
||||
@ -17,6 +17,7 @@ nb = "1"
|
||||
embedded-hal = { features = ["unproven"], version = "0.2.6" }
|
||||
void = { version = "1.0", default-features = false }
|
||||
once_cell = { version = "1.8.0", default-features = false }
|
||||
libm = "0.2.1"
|
||||
|
||||
[dependencies.va108xx]
|
||||
version = "0.1"
|
||||
|
@ -72,7 +72,9 @@ fn main() -> ! {
|
||||
}
|
||||
TestCase::TestPullup => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let input = porta.pa1.into_pull_up_input(&mut dp.IOCONFIG);
|
||||
let input = porta
|
||||
.pa1
|
||||
.into_pull_up_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
assert!(input.is_high().unwrap());
|
||||
let mut out = porta
|
||||
.pa0
|
||||
|
44
examples/uart.rs
Normal file
44
examples/uart.rs
Normal file
@ -0,0 +1,44 @@
|
||||
//! UART example application. Sends a test string over a UART and then enters
|
||||
//! echo mode
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::Write;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{pac, prelude::*, uart};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx UART test application--");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
let gpiob = dp.PORTB.split(&mut dp.SYSCONFIG).unwrap();
|
||||
let tx = gpiob.pb21.into_funsel_1(&mut dp.IOCONFIG);
|
||||
let rx = gpiob.pb20.into_funsel_1(&mut dp.IOCONFIG);
|
||||
|
||||
let uartb = uart::Uart::uartb(
|
||||
dp.UARTB,
|
||||
(tx, rx),
|
||||
115200.bps(),
|
||||
&mut dp.SYSCONFIG,
|
||||
50.mhz().into(),
|
||||
);
|
||||
let (mut tx, mut rx) = uartb.split();
|
||||
writeln!(tx, "Hello World\r").unwrap();
|
||||
loop {
|
||||
// Echo what is received on the serial link.
|
||||
match rx.read() {
|
||||
Ok(recv) => {
|
||||
nb::block!(tx.write(recv)).expect("TX send error");
|
||||
}
|
||||
Err(nb::Error::WouldBlock) => (),
|
||||
Err(nb::Error::Other(uart_error)) => {
|
||||
rprintln!("UART receive error {:?}", uart_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,14 @@ use va108xx::SYSCONFIG;
|
||||
|
||||
static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum PeripheralClocks {
|
||||
PortA = 0,
|
||||
PortB = 1,
|
||||
Spi0 = 4,
|
||||
Spi1 = 5,
|
||||
Spi2 = 6,
|
||||
UArt0 = 8,
|
||||
Uart0 = 8,
|
||||
Uart1 = 9,
|
||||
I2c0 = 16,
|
||||
I2c1 = 17,
|
||||
|
32
src/gpio.rs
32
src/gpio.rs
@ -1,3 +1,8 @@
|
||||
//! API for the GPIO pins
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/blinky.rs)
|
||||
use crate::pac::SYSCONFIG;
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
@ -71,7 +76,7 @@ pub struct FUNSEL2;
|
||||
pub struct FUNSEL3;
|
||||
|
||||
/// Function select (type state)
|
||||
pub struct Funsel<FUN> {
|
||||
pub struct AltFunc<FUN> {
|
||||
_mode: PhantomData<FUN>,
|
||||
}
|
||||
|
||||
@ -207,7 +212,7 @@ macro_rules! gpio {
|
||||
use core::marker::PhantomData;
|
||||
use core::convert::Infallible;
|
||||
use super::{
|
||||
FUNSEL1, FUNSEL2, FUNSEL3, Floating, Funsel, GpioExt, Input, OpenDrain,
|
||||
FUNSEL1, FUNSEL2, FUNSEL3, Floating, AltFunc, GpioExt, Input, OpenDrain,
|
||||
PullUp, Output, FilterType, FilterClkSel, Pin, GpioRegExt, PushPull,
|
||||
PinModeError, PinState, PortId, singleton
|
||||
};
|
||||
@ -247,11 +252,9 @@ macro_rules! gpio {
|
||||
}
|
||||
|
||||
fn _set_alternate_mode(iocfg: &mut IOCONFIG, index: usize, mode: u8) {
|
||||
unsafe {
|
||||
iocfg.$portx[index].modify(|_, w| {
|
||||
w.funsel().bits(mode)
|
||||
})
|
||||
}
|
||||
iocfg.$portx[index].modify(|_, w| unsafe {
|
||||
w.funsel().bits(mode)
|
||||
});
|
||||
}
|
||||
|
||||
$(
|
||||
@ -260,15 +263,15 @@ macro_rules! gpio {
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<MODE> {
|
||||
pub fn into_funsel_1(self, iocfg: &mut IOCONFIG) -> $PXi<Funsel<FUNSEL1>> {
|
||||
pub fn into_funsel_1(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL1>> {
|
||||
_set_alternate_mode(iocfg, $i, 1);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
pub fn into_funsel_2(self, iocfg: &mut IOCONFIG) -> $PXi<Funsel<FUNSEL2>> {
|
||||
pub fn into_funsel_2(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL2>> {
|
||||
_set_alternate_mode(iocfg, $i, 2);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
pub fn into_funsel_3(self, iocfg: &mut IOCONFIG) -> $PXi<Funsel<FUNSEL3>> {
|
||||
pub fn into_funsel_3(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL3>> {
|
||||
_set_alternate_mode(iocfg, $i, 3);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
@ -312,7 +315,7 @@ macro_rules! gpio {
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_up_input(self, iocfg: &mut IOCONFIG) -> $PXi<Input<PullUp>> {
|
||||
pub fn into_pull_up_input(self, iocfg: &mut IOCONFIG, port: &mut $PORTX) -> $PXi<Input<PullUp>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
@ -320,14 +323,13 @@ macro_rules! gpio {
|
||||
w.plevel().set_bit();
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
let port_reg = &(*$PORTX::ptr());
|
||||
port_reg.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_down_input(
|
||||
self, iocfg: &mut IOCONFIG, port_reg: &mut $PORTX
|
||||
self, iocfg: &mut IOCONFIG, port: &mut $PORTX
|
||||
) -> $PXi<Input<PullUp>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
@ -336,7 +338,7 @@ macro_rules! gpio {
|
||||
w.plevel().clear_bit();
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
port_reg.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
@ -7,5 +7,10 @@ pub mod gpio;
|
||||
pub mod prelude;
|
||||
pub mod time;
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
|
||||
pub use va108xx as pac;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
//! API for the TIM peripherals
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs)
|
||||
use crate::{
|
||||
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||
time::Hertz,
|
||||
|
417
src/uart.rs
Normal file
417
src/uart.rs
Normal file
@ -0,0 +1,417 @@
|
||||
//! API for the UART peripheral
|
||||
use core::{convert::Infallible, ptr};
|
||||
use core::{marker::PhantomData, ops::Deref};
|
||||
use libm::floorf;
|
||||
|
||||
use crate::clock::enable_peripheral_clock;
|
||||
use crate::{
|
||||
clock,
|
||||
gpio::porta::{PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, PA31, PA8, PA9},
|
||||
gpio::portb::{PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9},
|
||||
gpio::{AltFunc, FUNSEL1, FUNSEL2, FUNSEL3},
|
||||
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
|
||||
prelude::*,
|
||||
time::{Bps, Hertz},
|
||||
};
|
||||
|
||||
use embedded_hal::{blocking, serial};
|
||||
|
||||
pub trait Pins<UART> {}
|
||||
|
||||
impl Pins<UARTA> for (PA9<AltFunc<FUNSEL2>>, PA8<AltFunc<FUNSEL2>>) {}
|
||||
impl Pins<UARTA> for (PA17<AltFunc<FUNSEL3>>, PA16<AltFunc<FUNSEL3>>) {}
|
||||
impl Pins<UARTA> for (PA31<AltFunc<FUNSEL3>>, PA30<AltFunc<FUNSEL3>>) {}
|
||||
|
||||
impl Pins<UARTA> for (PB9<AltFunc<FUNSEL1>>, PB8<AltFunc<FUNSEL1>>) {}
|
||||
impl Pins<UARTA> for (PB23<AltFunc<FUNSEL1>>, PB22<AltFunc<FUNSEL1>>) {}
|
||||
|
||||
impl Pins<UARTB> for (PA3<AltFunc<FUNSEL2>>, PA2<AltFunc<FUNSEL2>>) {}
|
||||
impl Pins<UARTB> for (PA19<AltFunc<FUNSEL3>>, PA18<AltFunc<FUNSEL3>>) {}
|
||||
impl Pins<UARTB> for (PA27<AltFunc<FUNSEL3>>, PA26<AltFunc<FUNSEL3>>) {}
|
||||
|
||||
impl Pins<UARTB> for (PB7<AltFunc<FUNSEL1>>, PB6<AltFunc<FUNSEL1>>) {}
|
||||
impl Pins<UARTB> for (PB19<AltFunc<FUNSEL2>>, PB18<AltFunc<FUNSEL2>>) {}
|
||||
impl Pins<UARTB> for (PB21<AltFunc<FUNSEL1>>, PB20<AltFunc<FUNSEL1>>) {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Overrun,
|
||||
FramingError,
|
||||
ParityError,
|
||||
BreakCondition,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Event {
|
||||
// Receiver FIFO interrupt enable. Generates interrupt
|
||||
// when FIFO is at least half full. Half full is defined as FIFO
|
||||
// count >= RXFIFOIRQTRG
|
||||
RxFifoHalfFull,
|
||||
// Framing error, Overrun error, Parity Error and Break error
|
||||
RxError,
|
||||
// Event for timeout condition: Data in the FIFO and no receiver
|
||||
// FIFO activity for 4 character times
|
||||
RxTimeout,
|
||||
|
||||
// Transmitter FIFO interrupt enable. Generates interrupt
|
||||
// when FIFO is at least half full. Half full is defined as FIFO
|
||||
// count >= TXFIFOIRQTRG
|
||||
TxFifoHalfFull,
|
||||
// FIFO overflow error
|
||||
TxError,
|
||||
// Generate interrupt when transmit FIFO is empty and TXBUSY is 0
|
||||
TxEmpty,
|
||||
// Interrupt when CTSn changes value
|
||||
TxCts,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Parity {
|
||||
None,
|
||||
Odd,
|
||||
Even,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum StopBits {
|
||||
One = 0,
|
||||
Two = 1,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum WordSize {
|
||||
Five = 0,
|
||||
Six = 1,
|
||||
Seven = 2,
|
||||
Eight = 3,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub baudrate: Bps,
|
||||
pub parity: Parity,
|
||||
pub stopbits: StopBits,
|
||||
// When false, use standard 16x baud clock, other 8x baud clock
|
||||
pub baud8: bool,
|
||||
pub wordsize: WordSize,
|
||||
pub enable_tx: bool,
|
||||
pub enable_rx: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn baudrate(mut self, baudrate: Bps) -> Self {
|
||||
self.baudrate = baudrate;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn parity_none(mut self) -> Self {
|
||||
self.parity = Parity::None;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn parity_even(mut self) -> Self {
|
||||
self.parity = Parity::Even;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn parity_odd(mut self) -> Self {
|
||||
self.parity = Parity::Odd;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stopbits(mut self, stopbits: StopBits) -> Self {
|
||||
self.stopbits = stopbits;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn wordsize(mut self, wordsize: WordSize) -> Self {
|
||||
self.wordsize = wordsize;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn baud8(mut self, baud: bool) -> Self {
|
||||
self.baud8 = baud;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
let baudrate = 115_200_u32.bps();
|
||||
Config {
|
||||
baudrate,
|
||||
parity: Parity::None,
|
||||
stopbits: StopBits::One,
|
||||
baud8: false,
|
||||
wordsize: WordSize::Eight,
|
||||
enable_tx: true,
|
||||
enable_rx: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bps> for Config {
|
||||
fn from(baud: Bps) -> Self {
|
||||
Config::default().baudrate(baud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serial abstraction
|
||||
pub struct Uart<UART, PINS> {
|
||||
uart: UART,
|
||||
pins: PINS,
|
||||
tx: Tx<UART>,
|
||||
rx: Rx<UART>,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<UART> {
|
||||
_usart: PhantomData<UART>,
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct Tx<UART> {
|
||||
_usart: PhantomData<UART>,
|
||||
}
|
||||
|
||||
impl<UART> Rx<UART> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
_usart: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART> Tx<UART> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
_usart: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
|
||||
fn ptr() -> *const uart_base::RegisterBlock;
|
||||
}
|
||||
|
||||
impl<UART, PINS> Uart<UART, PINS>
|
||||
where
|
||||
UART: Instance,
|
||||
{
|
||||
/// This function assumes that the peripheral clock was alredy enabled
|
||||
/// in the SYSCONFIG register
|
||||
fn init(self, config: Config, sys_clk: Hertz) -> Self {
|
||||
let baud_multiplier = match config.baud8 {
|
||||
false => 16,
|
||||
true => 8,
|
||||
};
|
||||
let x = sys_clk.0 as f32 / (config.baudrate.0 * baud_multiplier) as f32;
|
||||
let integer_part = floorf(x) as u32;
|
||||
let frac = floorf((64.0 * (x - integer_part as f32) + 0.5) as f32) as u32;
|
||||
self.uart
|
||||
.clkscale
|
||||
.write(|w| unsafe { w.bits(integer_part * 64 + frac) });
|
||||
|
||||
let (paren, pareven) = match config.parity {
|
||||
Parity::None => (false, false),
|
||||
Parity::Odd => (true, false),
|
||||
Parity::Even => (true, true),
|
||||
};
|
||||
let stopbits = match config.stopbits {
|
||||
StopBits::One => false,
|
||||
StopBits::Two => true,
|
||||
};
|
||||
let wordsize = config.wordsize as u8;
|
||||
let baud8 = config.baud8;
|
||||
self.uart.ctrl.write(|w| {
|
||||
w.paren().bit(paren);
|
||||
w.pareven().bit(pareven);
|
||||
w.stopbits().bit(stopbits);
|
||||
w.baud8().bit(baud8);
|
||||
unsafe { w.wordsize().bits(wordsize) }
|
||||
});
|
||||
let (txenb, rxenb) = (config.enable_tx, config.enable_rx);
|
||||
// Clear the FIFO
|
||||
self.uart.fifo_clr.write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
self.uart.enable.write(|w| {
|
||||
w.rxenable().bit(rxenb);
|
||||
w.txenable().bit(txenb)
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn listen(self, event: Event) -> Self {
|
||||
self.uart.irq_enb.modify(|_, w| match event {
|
||||
Event::RxError => w.irq_rx_status().set_bit(),
|
||||
Event::RxFifoHalfFull => w.irq_rx().set_bit(),
|
||||
Event::RxTimeout => w.irq_rx_to().set_bit(),
|
||||
Event::TxEmpty => w.irq_tx_empty().set_bit(),
|
||||
Event::TxError => w.irq_tx_status().set_bit(),
|
||||
Event::TxFifoHalfFull => w.irq_tx().set_bit(),
|
||||
Event::TxCts => w.irq_tx_cts().set_bit(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unlisten(self, event: Event) -> Self {
|
||||
self.uart.irq_enb.modify(|_, w| match event {
|
||||
Event::RxError => w.irq_rx_status().clear_bit(),
|
||||
Event::RxFifoHalfFull => w.irq_rx().clear_bit(),
|
||||
Event::RxTimeout => w.irq_rx_to().clear_bit(),
|
||||
Event::TxEmpty => w.irq_tx_empty().clear_bit(),
|
||||
Event::TxError => w.irq_tx_status().clear_bit(),
|
||||
Event::TxFifoHalfFull => w.irq_tx().clear_bit(),
|
||||
Event::TxCts => w.irq_tx_cts().clear_bit(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn release(self) -> (UART, PINS) {
|
||||
// Clear the FIFO
|
||||
self.uart.fifo_clr.write(|w| {
|
||||
w.rxfifo().set_bit();
|
||||
w.txfifo().set_bit()
|
||||
});
|
||||
self.uart.enable.write(|w| {
|
||||
w.rxenable().clear_bit();
|
||||
w.txenable().clear_bit()
|
||||
});
|
||||
(self.uart, self.pins)
|
||||
}
|
||||
|
||||
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! uart_impl {
|
||||
($($UARTX:ident: ($uartx:ident, $clk_enb_enum:path),)+) => {
|
||||
$(
|
||||
impl Instance for $UARTX {
|
||||
fn ptr() -> *const uart_base::RegisterBlock {
|
||||
$UARTX::ptr() as *const _
|
||||
}
|
||||
}
|
||||
|
||||
impl<PINS: Pins<$UARTX>> Uart<$UARTX, PINS> {
|
||||
pub fn $uartx(
|
||||
uart: $UARTX,
|
||||
pins: PINS,
|
||||
config: impl Into<Config>,
|
||||
syscfg: &mut SYSCONFIG,
|
||||
sys_clk: Hertz
|
||||
) -> Self
|
||||
{
|
||||
enable_peripheral_clock(syscfg, $clk_enb_enum);
|
||||
Uart { uart, pins, tx: Tx::new(), rx: Rx::new() }.init(
|
||||
config.into(), sys_clk
|
||||
)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
uart_impl! {
|
||||
UARTA: (uarta, clock::PeripheralClocks::Uart0),
|
||||
UARTB: (uartb, clock::PeripheralClocks::Uart1),
|
||||
}
|
||||
|
||||
impl<UART> Tx<UART> where UART: Instance {}
|
||||
|
||||
impl<UART, PINS> serial::Write<u8> for Uart<UART, PINS>
|
||||
where
|
||||
UART: Instance,
|
||||
{
|
||||
type Error = Infallible;
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
self.tx.write(word)
|
||||
}
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.tx.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Instance, PINS> blocking::serial::write::Default<u8> for Uart<UART, PINS> {}
|
||||
|
||||
impl<UART: Instance> serial::Write<u8> for Tx<UART> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
let reader = unsafe { &(*UART::ptr()) }.txstatus.read();
|
||||
if reader.wrrdy().bit_is_clear() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
} else {
|
||||
// DPARITY bit not supported yet
|
||||
unsafe {
|
||||
// NOTE(unsafe) atomic write to data register
|
||||
// NOTE(write_volatile) 8-bit write that's not
|
||||
// possible through the svd2rust API
|
||||
ptr::write_volatile(&(*UART::ptr()).data as *const _ as *mut _, word);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
let reader = unsafe { &(*UART::ptr()) }.txstatus.read();
|
||||
if reader.wrbusy().bit_is_clear() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Instance, PINS> serial::Read<u8> for Uart<UART, PINS> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Error> {
|
||||
self.rx.read()
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: Instance> serial::Read<u8> for Rx<UART> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Error> {
|
||||
let uart = unsafe { &(*UART::ptr()) };
|
||||
let status_reader = uart.rxstatus.read();
|
||||
let err = if status_reader.rxovr().bit_is_set() {
|
||||
Some(Error::Overrun)
|
||||
} else if status_reader.rxfrm().bit_is_set() {
|
||||
Some(Error::FramingError)
|
||||
} else if status_reader.rxpar().bit_is_set() {
|
||||
Some(Error::ParityError)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(err) = err {
|
||||
// The status code is always related to the next bit for the framing
|
||||
// and parity status bits. We have to read the DATA register
|
||||
// so that the next status reflects the next DATA word
|
||||
// For overrun error, we read as well to clear the peripheral
|
||||
uart.data.read().bits();
|
||||
Err(err.into())
|
||||
} else if status_reader.rdavl().bit_is_set() {
|
||||
let data = uart.data.read().bits();
|
||||
Ok((data & 0xff) as u8)
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART> core::fmt::Write for Tx<UART>
|
||||
where
|
||||
Tx<UART>: embedded_hal::serial::Write<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
s.as_bytes()
|
||||
.iter()
|
||||
.try_for_each(|c| nb::block!(self.write(*c)))
|
||||
.map_err(|_| core::fmt::Error)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user