Merge #833
833: nrf/uart: add support for tx-only and rx-only uart. r=Dirbaio a=Dirbaio Allow creating UarteRx/UarteTx directly. This allows using uart unidirectionally (rx-only or tx-only), without having to 'waste' a pin for the unused direction. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
commit
ffc32d3ddb
@ -19,9 +19,10 @@ use core::task::Poll;
|
|||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_common::drop::OnDrop;
|
||||||
use embassy_hal_common::unborrow;
|
use embassy_hal_common::unborrow;
|
||||||
use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values.
|
use futures::future::poll_fn;
|
||||||
pub use pac::uarte0::baudrate::BAUDRATE_A as Baudrate;
|
use pac::uarte0::RegisterBlock;
|
||||||
pub use pac::uarte0::config::PARITY_A as Parity;
|
// Re-export SVD variants to allow user to directly set values.
|
||||||
|
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
|
||||||
|
|
||||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
@ -32,6 +33,7 @@ use crate::timer::{Frequency, Instance as TimerInstance, Timer};
|
|||||||
use crate::util::slice_in_ram_or;
|
use crate::util::slice_in_ram_or;
|
||||||
use crate::{pac, Unborrow};
|
use crate::{pac, Unborrow};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub parity: Parity,
|
pub parity: Parity,
|
||||||
@ -144,37 +146,18 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||||||
}
|
}
|
||||||
r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
|
r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
|
||||||
|
|
||||||
// Configure
|
irq.set_handler(Self::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
|
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
|
||||||
(false, false) => false,
|
(false, false) => false,
|
||||||
(true, true) => true,
|
(true, true) => true,
|
||||||
_ => panic!("RTS and CTS pins must be either both set or none set."),
|
_ => panic!("RTS and CTS pins must be either both set or none set."),
|
||||||
};
|
};
|
||||||
r.config.write(|w| {
|
configure(r, config, hardware_flow_control);
|
||||||
w.hwfc().bit(hardware_flow_control);
|
|
||||||
w.parity().variant(config.parity);
|
|
||||||
w
|
|
||||||
});
|
|
||||||
r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
|
|
||||||
|
|
||||||
// Disable all interrupts
|
|
||||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
|
||||||
|
|
||||||
// Reset rxstarted, txstarted. These are used by drop to know whether a transfer was
|
|
||||||
// stopped midway or not.
|
|
||||||
r.events_rxstarted.reset();
|
|
||||||
r.events_txstarted.reset();
|
|
||||||
|
|
||||||
irq.set_handler(Self::on_interrupt);
|
|
||||||
irq.unpend();
|
|
||||||
irq.enable();
|
|
||||||
|
|
||||||
// Enable
|
|
||||||
apply_workaround_for_enable_anomaly(&r);
|
|
||||||
r.enable.write(|w| w.enable().enabled());
|
|
||||||
|
|
||||||
let s = T::state();
|
let s = T::state();
|
||||||
|
|
||||||
s.tx_rx_refcount.store(2, Ordering::Relaxed);
|
s.tx_rx_refcount.store(2, Ordering::Relaxed);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -238,7 +221,87 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
|
||||||
|
r.config.write(|w| {
|
||||||
|
w.hwfc().bit(hardware_flow_control);
|
||||||
|
w.parity().variant(config.parity);
|
||||||
|
w
|
||||||
|
});
|
||||||
|
r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
|
||||||
|
|
||||||
|
// Disable all interrupts
|
||||||
|
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||||
|
|
||||||
|
// Reset rxstarted, txstarted. These are used by drop to know whether a transfer was
|
||||||
|
// stopped midway or not.
|
||||||
|
r.events_rxstarted.reset();
|
||||||
|
r.events_txstarted.reset();
|
||||||
|
|
||||||
|
// Enable
|
||||||
|
apply_workaround_for_enable_anomaly(&r);
|
||||||
|
r.enable.write(|w| w.enable().enabled());
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> UarteTx<'d, T> {
|
impl<'d, T: Instance> UarteTx<'d, T> {
|
||||||
|
/// Create a new tx-only UARTE without hardware flow control
|
||||||
|
pub fn new(
|
||||||
|
uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
txd: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(txd);
|
||||||
|
Self::new_inner(uarte, irq, txd.degrade(), None, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new tx-only UARTE with hardware flow control (RTS/CTS)
|
||||||
|
pub fn new_with_rtscts(
|
||||||
|
uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
txd: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
cts: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(txd, cts);
|
||||||
|
Self::new_inner(uarte, irq, txd.degrade(), Some(cts.degrade()), config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(
|
||||||
|
_uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
txd: AnyPin,
|
||||||
|
cts: Option<AnyPin>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(irq);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
txd.set_high();
|
||||||
|
txd.conf().write(|w| w.dir().output().drive().s0s1());
|
||||||
|
r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) });
|
||||||
|
|
||||||
|
if let Some(pin) = &cts {
|
||||||
|
pin.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
|
}
|
||||||
|
r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
|
||||||
|
|
||||||
|
r.psel.rxd.write(|w| w.connect().disconnected());
|
||||||
|
r.psel.rts.write(|w| w.connect().disconnected());
|
||||||
|
|
||||||
|
let hardware_flow_control = cts.is_some();
|
||||||
|
configure(r, config, hardware_flow_control);
|
||||||
|
|
||||||
|
irq.set_handler(Uarte::<T>::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
let s = T::state();
|
||||||
|
s.tx_rx_refcount.store(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
Self { phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||||
match self.write_from_ram(buffer).await {
|
match self.write_from_ram(buffer).await {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
@ -372,6 +435,65 @@ impl<'a, T: Instance> Drop for UarteTx<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> UarteRx<'d, T> {
|
impl<'d, T: Instance> UarteRx<'d, T> {
|
||||||
|
/// Create a new rx-only UARTE without hardware flow control
|
||||||
|
pub fn new(
|
||||||
|
uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
rxd: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(rxd);
|
||||||
|
Self::new_inner(uarte, irq, rxd.degrade(), None, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rx-only UARTE with hardware flow control (RTS/CTS)
|
||||||
|
pub fn new_with_rtscts(
|
||||||
|
uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
rxd: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
rts: impl Unborrow<Target = impl GpioPin> + 'd,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(rxd, rts);
|
||||||
|
Self::new_inner(uarte, irq, rxd.degrade(), Some(rts.degrade()), config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(
|
||||||
|
_uarte: impl Unborrow<Target = T> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
rxd: AnyPin,
|
||||||
|
rts: Option<AnyPin>,
|
||||||
|
config: Config,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(irq);
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
|
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||||
|
|
||||||
|
if let Some(pin) = &rts {
|
||||||
|
pin.set_high();
|
||||||
|
pin.conf().write(|w| w.dir().output().drive().h0h1());
|
||||||
|
}
|
||||||
|
r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
|
||||||
|
|
||||||
|
r.psel.txd.write(|w| w.connect().disconnected());
|
||||||
|
r.psel.cts.write(|w| w.connect().disconnected());
|
||||||
|
|
||||||
|
irq.set_handler(Uarte::<T>::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
let hardware_flow_control = rts.is_some();
|
||||||
|
configure(r, config, hardware_flow_control);
|
||||||
|
|
||||||
|
let s = T::state();
|
||||||
|
s.tx_rx_refcount.store(1, Ordering::Relaxed);
|
||||||
|
|
||||||
|
Self { phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
if buffer.len() == 0 {
|
if buffer.len() == 0 {
|
||||||
return Err(Error::BufferZeroLength);
|
return Err(Error::BufferZeroLength);
|
||||||
|
Loading…
Reference in New Issue
Block a user