mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-27 12:21:02 +00:00
Overhaul UART to support single wire transmitters/receivers (#164)
* overhaul UART to support single wire trasimitters/receivers * add missing methods for uart transmitter/receiver * fix `uart_config_t`'s `source_clk` being part of a union in `esp-idf v4.x` * add `esp32c2`'s `PLL F40M` `APB` clock * revert `uart_wait_tx_done` to timeout of 0 * fix incorrect order in `uart_driver_install`, `rx_buffer` shou always exceed `UART_FIFO_SIZE` * Refactor `Owner` enum * Fix double drop on `into_split` * Add missing `count` methods for the UART drivers, change their return to be able to fit the maximum allowed value, don't run `drop` on temporary internal borrows, update example
This commit is contained in:
parent
01246762f1
commit
3f532d7224
@ -24,7 +24,7 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
println!("Starting UART loopback test");
|
||||
let config = config::Config::new().baudrate(Hertz(115_200));
|
||||
let mut uart = UartDriver::new(
|
||||
let uart = UartDriver::new(
|
||||
peripherals.uart1,
|
||||
tx,
|
||||
rx,
|
||||
|
601
src/uart.rs
601
src/uart.rs
@ -38,7 +38,9 @@
|
||||
//! - Address errata 3.17: UART fifo_cnt is inconsistent with FIFO pointer
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::ManuallyDrop;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use crate::delay::NON_BLOCK;
|
||||
use crate::gpio::*;
|
||||
@ -48,7 +50,7 @@ use esp_idf_sys::*;
|
||||
|
||||
use crate::peripheral::Peripheral;
|
||||
|
||||
const UART_FIFO_SIZE: i32 = 128;
|
||||
const UART_FIFO_SIZE: i32 = SOC_UART_FIFO_LEN as i32;
|
||||
|
||||
pub type UartConfig = config::Config;
|
||||
|
||||
@ -189,6 +191,113 @@ pub mod config {
|
||||
}
|
||||
}
|
||||
|
||||
/// UART source clock
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum SourceClock {
|
||||
/// UART source clock from `APB`
|
||||
#[cfg(any(
|
||||
esp_idf_soc_uart_support_apb_clk,
|
||||
esp_idf_soc_uart_support_pll_f40m_clk,
|
||||
esp_idf_version_major = "4",
|
||||
))]
|
||||
APB,
|
||||
/// UART source clock from `RTC`
|
||||
#[cfg(esp_idf_soc_uart_support_rtc_clk)]
|
||||
RTC,
|
||||
/// UART source clock from `XTAL`
|
||||
#[cfg(esp_idf_soc_uart_support_xtal_clk)]
|
||||
Crystal,
|
||||
/// UART source clock from `REF_TICK`
|
||||
#[cfg(esp_idf_soc_uart_support_ref_tick)]
|
||||
RefTick,
|
||||
}
|
||||
|
||||
impl SourceClock {
|
||||
#[cfg(not(esp_idf_version_major = "4"))]
|
||||
pub fn frequency(self) -> Result<Hertz, EspError> {
|
||||
let mut frequency: u32 = 0;
|
||||
esp_result! {
|
||||
unsafe { uart_get_sclk_freq(self.into(), &mut frequency) },
|
||||
Hertz(frequency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(esp_idf_version_major = "4"), esp_idf_soc_uart_support_apb_clk))]
|
||||
const APB_SCLK: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_APB;
|
||||
#[cfg(all(
|
||||
not(esp_idf_version_major = "4"),
|
||||
not(esp_idf_soc_uart_support_apb_clk),
|
||||
esp_idf_soc_uart_support_pll_f40m_clk,
|
||||
))]
|
||||
const APB_SCLK: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_PLL_F40M;
|
||||
#[cfg(esp_idf_version_major = "4")]
|
||||
const APB_SCLK: uart_sclk_t = uart_sclk_t_UART_SCLK_APB;
|
||||
|
||||
#[cfg(all(not(esp_idf_version_major = "4"), esp_idf_soc_uart_support_rtc_clk))]
|
||||
const RTC_SCLK: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_RTC;
|
||||
#[cfg(all(esp_idf_version_major = "4", esp_idf_soc_uart_support_rtc_clk))]
|
||||
const RTC_SCLK: uart_sclk_t = uart_sclk_t_UART_SCLK_RTC;
|
||||
|
||||
#[cfg(all(not(esp_idf_version_major = "4"), esp_idf_soc_uart_support_xtal_clk))]
|
||||
const XTAL_SCLK: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_XTAL;
|
||||
#[cfg(all(esp_idf_version_major = "4", esp_idf_soc_uart_support_xtal_clk))]
|
||||
const XTAL_SCLK: uart_sclk_t = uart_sclk_t_UART_SCLK_XTAL;
|
||||
|
||||
#[cfg(all(not(esp_idf_version_major = "4"), esp_idf_soc_uart_support_ref_tick))]
|
||||
const REF_TICK_SCLK: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_REF_TICK;
|
||||
#[cfg(all(esp_idf_version_major = "4", esp_idf_soc_uart_support_ref_tick))]
|
||||
const REF_TICK_SCLK: uart_sclk_t = uart_sclk_t_UART_SCLK_REF_TICK;
|
||||
|
||||
impl Default for SourceClock {
|
||||
fn default() -> Self {
|
||||
#[cfg(not(esp_idf_version_major = "4"))]
|
||||
const DEFAULT: uart_sclk_t = soc_periph_uart_clk_src_legacy_t_UART_SCLK_DEFAULT;
|
||||
#[cfg(esp_idf_version_major = "4")]
|
||||
const DEFAULT: uart_sclk_t = uart_sclk_t_UART_SCLK_APB;
|
||||
Self::from(DEFAULT)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SourceClock> for uart_sclk_t {
|
||||
fn from(source_clock: SourceClock) -> Self {
|
||||
match source_clock {
|
||||
#[cfg(any(
|
||||
esp_idf_soc_uart_support_apb_clk,
|
||||
esp_idf_soc_uart_support_pll_f40m_clk,
|
||||
esp_idf_version_major = "4",
|
||||
))]
|
||||
SourceClock::APB => APB_SCLK,
|
||||
#[cfg(esp_idf_soc_uart_support_rtc_clk)]
|
||||
SourceClock::RTC => RTC_SCLK,
|
||||
#[cfg(esp_idf_soc_uart_support_xtal_clk)]
|
||||
SourceClock::Crystal => XTAL_SCLK,
|
||||
#[cfg(esp_idf_soc_uart_support_ref_tick)]
|
||||
SourceClock::RefTick => REF_TICK_SCLK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uart_sclk_t> for SourceClock {
|
||||
fn from(source_clock: uart_sclk_t) -> Self {
|
||||
match source_clock {
|
||||
#[cfg(any(
|
||||
esp_idf_soc_uart_support_apb_clk,
|
||||
esp_idf_soc_uart_support_pll_f40m_clk,
|
||||
esp_idf_version_major = "4",
|
||||
))]
|
||||
APB_SCLK => SourceClock::APB,
|
||||
#[cfg(esp_idf_soc_uart_support_rtc_clk)]
|
||||
RTC_SCLK => SourceClock::RTC,
|
||||
#[cfg(esp_idf_soc_uart_support_xtal_clk)]
|
||||
XTAL_SCLK => SourceClock::Crystal,
|
||||
#[cfg(esp_idf_soc_uart_support_ref_tick)]
|
||||
REF_TICK_SCLK => SourceClock::RefTick,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// UART configuration
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Config {
|
||||
@ -198,6 +307,7 @@ pub mod config {
|
||||
pub stop_bits: StopBits,
|
||||
pub flow_control: FlowControl,
|
||||
pub flow_control_rts_threshold: u8,
|
||||
pub source_clock: SourceClock,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -255,6 +365,12 @@ pub mod config {
|
||||
self.flow_control_rts_threshold = flow_control_rts_threshold;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn source_clock(mut self, source_clock: SourceClock) -> Self {
|
||||
self.source_clock = source_clock;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -266,6 +382,7 @@ pub mod config {
|
||||
stop_bits: StopBits::STOP1,
|
||||
flow_control: FlowControl::None,
|
||||
flow_control_rts_threshold: 122,
|
||||
source_clock: SourceClock::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,7 +399,6 @@ crate::embedded_hal_error!(
|
||||
);
|
||||
|
||||
/// Serial abstraction
|
||||
///
|
||||
pub struct UartDriver<'d> {
|
||||
port: u8,
|
||||
_p: PhantomData<&'d mut ()>,
|
||||
@ -291,62 +407,28 @@ pub struct UartDriver<'d> {
|
||||
/// Serial receiver
|
||||
pub struct UartRxDriver<'d> {
|
||||
port: u8,
|
||||
owner: Owner,
|
||||
_p: PhantomData<&'d mut ()>,
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct UartTxDriver<'d> {
|
||||
port: u8,
|
||||
owner: Owner,
|
||||
_p: PhantomData<&'d mut ()>,
|
||||
}
|
||||
|
||||
impl<'d> UartDriver<'d> {
|
||||
/// Create a new serial driver
|
||||
pub fn new<UART: Uart>(
|
||||
_uart: impl Peripheral<P = UART> + 'd,
|
||||
uart: impl Peripheral<P = UART> + 'd,
|
||||
tx: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
rx: impl Peripheral<P = impl InputPin> + 'd,
|
||||
cts: Option<impl Peripheral<P = impl InputPin> + 'd>,
|
||||
rts: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
crate::into_ref!(tx, rx);
|
||||
|
||||
let cts = cts.map(|cts| cts.into_ref());
|
||||
let rts = rts.map(|rts| rts.into_ref());
|
||||
|
||||
let uart_config = uart_config_t {
|
||||
baud_rate: config.baudrate.0 as i32,
|
||||
data_bits: config.data_bits.into(),
|
||||
parity: config.parity.into(),
|
||||
stop_bits: config.stop_bits.into(),
|
||||
flow_ctrl: config.flow_control.into(),
|
||||
rx_flow_ctrl_thresh: config.flow_control_rts_threshold,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
esp!(unsafe { uart_param_config(UART::port(), &uart_config) })?;
|
||||
|
||||
esp!(unsafe {
|
||||
uart_set_pin(
|
||||
UART::port(),
|
||||
tx.pin(),
|
||||
rx.pin(),
|
||||
rts.as_ref().map_or(-1, |p| p.pin()),
|
||||
cts.as_ref().map_or(-1, |p| p.pin()),
|
||||
)
|
||||
})?;
|
||||
|
||||
esp!(unsafe {
|
||||
uart_driver_install(
|
||||
UART::port(),
|
||||
UART_FIFO_SIZE * 2,
|
||||
UART_FIFO_SIZE * 2,
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
)
|
||||
})?;
|
||||
new_common(uart, Some(tx), Some(rx), cts, rts, config)?;
|
||||
|
||||
Ok(Self {
|
||||
port: UART::port() as _,
|
||||
@ -355,51 +437,33 @@ impl<'d> UartDriver<'d> {
|
||||
}
|
||||
|
||||
/// Change the number of stop bits
|
||||
pub fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> Result<&mut Self, EspError> {
|
||||
esp_result!(
|
||||
unsafe { uart_set_stop_bits(self.port(), stop_bits.into()) },
|
||||
self
|
||||
)
|
||||
pub fn change_stop_bits(&self, stop_bits: config::StopBits) -> Result<&Self, EspError> {
|
||||
change_stop_bits(self.port(), stop_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Retruns the current number of stop bits
|
||||
/// Returns the current number of stop bits
|
||||
pub fn stop_bits(&self) -> Result<config::StopBits, EspError> {
|
||||
let mut stop_bits: uart_stop_bits_t = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_stop_bits(self.port(), &mut stop_bits) },
|
||||
stop_bits.into()
|
||||
)
|
||||
stop_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the number of data bits
|
||||
pub fn change_data_bits(&mut self, data_bits: config::DataBits) -> Result<&mut Self, EspError> {
|
||||
esp_result!(
|
||||
unsafe { uart_set_word_length(self.port(), data_bits.into()) },
|
||||
self
|
||||
)
|
||||
pub fn change_data_bits(&self, data_bits: config::DataBits) -> Result<&Self, EspError> {
|
||||
change_data_bits(self.port(), data_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Return the current number of data bits
|
||||
pub fn data_bits(&self) -> Result<config::DataBits, EspError> {
|
||||
let mut data_bits: uart_word_length_t = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_word_length(self.port(), &mut data_bits) },
|
||||
data_bits.into()
|
||||
)
|
||||
data_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the type of parity checking
|
||||
pub fn change_parity(&mut self, parity: config::Parity) -> Result<&mut Self, EspError> {
|
||||
esp_result!(unsafe { uart_set_parity(self.port(), parity.into()) }, self)
|
||||
pub fn change_parity(&self, parity: config::Parity) -> Result<&Self, EspError> {
|
||||
change_parity(self.port(), parity).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current type of parity checking
|
||||
pub fn parity(&self) -> Result<config::Parity, EspError> {
|
||||
let mut parity: uart_parity_t = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_parity(self.port(), &mut parity) },
|
||||
parity.into()
|
||||
)
|
||||
parity(self.port())
|
||||
}
|
||||
|
||||
/// Change the baudrate.
|
||||
@ -409,54 +473,67 @@ impl<'d> UartDriver<'d> {
|
||||
/// However if one of the clock frequencies is below 10MHz or if the baudrate is above
|
||||
/// the reference clock or if the baudrate cannot be set within 1.5%
|
||||
/// then use the APB clock.
|
||||
pub fn change_baudrate<T: Into<Hertz> + Copy>(
|
||||
&mut self,
|
||||
baudrate: T,
|
||||
) -> Result<&mut Self, EspError> {
|
||||
esp_result!(
|
||||
unsafe { uart_set_baudrate(self.port(), baudrate.into().into()) },
|
||||
self
|
||||
)
|
||||
pub fn change_baudrate<T: Into<Hertz> + Copy>(&self, baudrate: T) -> Result<&Self, EspError> {
|
||||
change_baudrate(self.port(), baudrate).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current baudrate
|
||||
pub fn baudrate(&self) -> Result<Hertz, EspError> {
|
||||
let mut baudrate: u32 = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_baudrate(self.port(), &mut baudrate) },
|
||||
baudrate.into()
|
||||
)
|
||||
baudrate(self.port())
|
||||
}
|
||||
|
||||
/// Split the serial driver in separate TX and RX drivers
|
||||
pub fn split(&mut self) -> (UartTxDriver<'_>, UartRxDriver<'_>) {
|
||||
pub fn split(&self) -> (UartTxDriver<'_>, UartRxDriver<'_>) {
|
||||
(
|
||||
UartTxDriver {
|
||||
port: self.port() as _,
|
||||
port: self.port,
|
||||
owner: Owner::Borrowed,
|
||||
_p: PhantomData,
|
||||
},
|
||||
UartRxDriver {
|
||||
port: self.port() as _,
|
||||
port: self.port,
|
||||
owner: Owner::Borrowed,
|
||||
_p: PhantomData,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Split the serial driver in separate TX and RX drivers.
|
||||
///
|
||||
/// Unlike [`split`], the halves are owned and reference counted.
|
||||
pub fn into_split(self) -> (UartTxDriver<'d>, UartRxDriver<'d>) {
|
||||
let port = self.port;
|
||||
let _ = ManuallyDrop::new(self);
|
||||
REFS[port as usize].fetch_add(2, Ordering::SeqCst);
|
||||
(
|
||||
UartTxDriver {
|
||||
port,
|
||||
owner: Owner::Shared,
|
||||
_p: PhantomData,
|
||||
},
|
||||
UartRxDriver {
|
||||
port,
|
||||
owner: Owner::Shared,
|
||||
_p: PhantomData,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Read multiple bytes into a slice
|
||||
pub fn read(&mut self, buf: &mut [u8], delay: TickType_t) -> Result<usize, EspError> {
|
||||
pub fn read(&self, buf: &mut [u8], delay: TickType_t) -> Result<usize, EspError> {
|
||||
self.rx().read(buf, delay)
|
||||
}
|
||||
|
||||
/// Write multiple bytes from a slice
|
||||
pub fn write(&mut self, buf: &[u8]) -> Result<usize, EspError> {
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize, EspError> {
|
||||
self.tx().write(buf)
|
||||
}
|
||||
|
||||
pub fn flush_read(&mut self) -> Result<(), EspError> {
|
||||
pub fn flush_read(&self) -> Result<(), EspError> {
|
||||
self.rx().flush()
|
||||
}
|
||||
|
||||
pub fn flush_write(&mut self) -> Result<(), EspError> {
|
||||
pub fn flush_write(&self) -> Result<(), EspError> {
|
||||
self.tx().flush()
|
||||
}
|
||||
|
||||
@ -464,29 +541,39 @@ impl<'d> UartDriver<'d> {
|
||||
self.port as _
|
||||
}
|
||||
|
||||
fn rx(&mut self) -> UartRxDriver<'_> {
|
||||
UartRxDriver {
|
||||
port: self.port() as _,
|
||||
_p: PhantomData,
|
||||
}
|
||||
/// Get count of remaining bytes in the receive ring buffer
|
||||
pub fn remaining_read(&self) -> Result<u32, EspError> {
|
||||
remaining_unread_bytes(self.port())
|
||||
}
|
||||
|
||||
fn tx(&mut self) -> UartTxDriver<'_> {
|
||||
UartTxDriver {
|
||||
port: self.port() as _,
|
||||
/// Get count of remaining capacity in the transmit ring buffer
|
||||
pub fn remaining_write(&self) -> Result<u32, EspError> {
|
||||
remaining_write_capacity(self.port())
|
||||
}
|
||||
|
||||
fn rx(&self) -> ManuallyDrop<UartRxDriver<'_>> {
|
||||
ManuallyDrop::new(UartRxDriver {
|
||||
port: self.port,
|
||||
owner: Owner::Borrowed,
|
||||
_p: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn tx(&self) -> ManuallyDrop<UartTxDriver<'_>> {
|
||||
ManuallyDrop::new(UartTxDriver {
|
||||
port: self.port,
|
||||
owner: Owner::Borrowed,
|
||||
_p: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for UartDriver<'d> {
|
||||
fn drop(&mut self) {
|
||||
esp!(unsafe { uart_driver_delete(self.port()) }).unwrap();
|
||||
delete_driver(self.port()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d> Send for UartDriver<'d> {}
|
||||
|
||||
impl<'d> embedded_hal::serial::ErrorType for UartDriver<'d> {
|
||||
type Error = SerialError;
|
||||
}
|
||||
@ -495,13 +582,13 @@ impl<'d> embedded_hal_0_2::serial::Read<u8> for UartDriver<'d> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
embedded_hal_0_2::serial::Read::read(&mut self.rx())
|
||||
embedded_hal_0_2::serial::Read::read(&mut *self.rx())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal_nb::serial::Read<u8> for UartDriver<'d> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
embedded_hal_nb::serial::Read::read(&mut self.rx())
|
||||
embedded_hal_nb::serial::Read::read(&mut *self.rx())
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,21 +596,21 @@ impl<'d> embedded_hal_0_2::serial::Write<u8> for UartDriver<'d> {
|
||||
type Error = SerialError;
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
embedded_hal_0_2::serial::Write::flush(&mut self.tx())
|
||||
embedded_hal_0_2::serial::Write::flush(&mut *self.tx())
|
||||
}
|
||||
|
||||
fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
|
||||
embedded_hal_0_2::serial::Write::write(&mut self.tx(), byte)
|
||||
embedded_hal_0_2::serial::Write::write(&mut *self.tx(), byte)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal_nb::serial::Write<u8> for UartDriver<'d> {
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
embedded_hal_nb::serial::Write::flush(&mut self.tx())
|
||||
embedded_hal_nb::serial::Write::flush(&mut *self.tx())
|
||||
}
|
||||
|
||||
fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
|
||||
embedded_hal_nb::serial::Write::write(&mut self.tx(), byte)
|
||||
embedded_hal_nb::serial::Write::write(&mut *self.tx(), byte)
|
||||
}
|
||||
}
|
||||
|
||||
@ -538,17 +625,71 @@ impl<'d> embedded_hal::serial::ErrorType for UartRxDriver<'d> {
|
||||
}
|
||||
|
||||
impl<'d> UartRxDriver<'d> {
|
||||
/// Get count of bytes in the receive FIFO
|
||||
pub fn count(&self) -> Result<u8, EspError> {
|
||||
let mut size = 0_u32;
|
||||
esp_result!(
|
||||
unsafe { uart_get_buffered_data_len(self.port(), &mut size) },
|
||||
size as u8
|
||||
)
|
||||
/// Create a new serial receiver
|
||||
pub fn new<UART: Uart>(
|
||||
uart: impl Peripheral<P = UART> + 'd,
|
||||
rx: impl Peripheral<P = impl InputPin> + 'd,
|
||||
cts: Option<impl Peripheral<P = impl InputPin> + 'd>,
|
||||
rts: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
new_common(uart, None::<AnyOutputPin>, Some(rx), cts, rts, config)?;
|
||||
|
||||
Ok(Self {
|
||||
port: UART::port() as _,
|
||||
owner: Owner::Owned,
|
||||
_p: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Change the number of stop bits
|
||||
pub fn change_stop_bits(&self, stop_bits: config::StopBits) -> Result<&Self, EspError> {
|
||||
change_stop_bits(self.port(), stop_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current number of stop bits
|
||||
pub fn stop_bits(&self) -> Result<config::StopBits, EspError> {
|
||||
stop_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the number of data bits
|
||||
pub fn change_data_bits(&self, data_bits: config::DataBits) -> Result<&Self, EspError> {
|
||||
change_data_bits(self.port(), data_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Return the current number of data bits
|
||||
pub fn data_bits(&self) -> Result<config::DataBits, EspError> {
|
||||
data_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the type of parity checking
|
||||
pub fn change_parity(&self, parity: config::Parity) -> Result<&Self, EspError> {
|
||||
change_parity(self.port(), parity).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current type of parity checking
|
||||
pub fn parity(&self) -> Result<config::Parity, EspError> {
|
||||
parity(self.port())
|
||||
}
|
||||
|
||||
/// Change the baudrate.
|
||||
///
|
||||
/// Will automatically select the clock source. When possible the reference clock (1MHz) will
|
||||
/// be used, because this is constant when the clock source/frequency changes.
|
||||
/// However if one of the clock frequencies is below 10MHz or if the baudrate is above
|
||||
/// the reference clock or if the baudrate cannot be set within 1.5%
|
||||
/// then use the APB clock.
|
||||
pub fn change_baudrate<T: Into<Hertz> + Copy>(&self, baudrate: T) -> Result<&Self, EspError> {
|
||||
change_baudrate(self.port(), baudrate).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current baudrate
|
||||
pub fn baudrate(&self) -> Result<Hertz, EspError> {
|
||||
baudrate(self.port())
|
||||
}
|
||||
|
||||
/// Read multiple bytes into a slice; block until specified timeout
|
||||
pub fn read(&mut self, buf: &mut [u8], delay: TickType_t) -> Result<usize, EspError> {
|
||||
pub fn read(&self, buf: &mut [u8], delay: TickType_t) -> Result<usize, EspError> {
|
||||
// uart_read_bytes() returns error (-1) or how many bytes were read out
|
||||
// 0 means timeout and nothing is yet read out
|
||||
let len = unsafe {
|
||||
@ -576,6 +717,17 @@ impl<'d> UartRxDriver<'d> {
|
||||
pub fn port(&self) -> uart_port_t {
|
||||
self.port as _
|
||||
}
|
||||
|
||||
/// Get count of remaining bytes in the receive ring buffer
|
||||
pub fn count(&self) -> Result<u32, EspError> {
|
||||
remaining_unread_bytes(self.port())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for UartRxDriver<'d> {
|
||||
fn drop(&mut self) {
|
||||
self.owner.drop_impl(self.port()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal_0_2::serial::Read<u8> for UartRxDriver<'d> {
|
||||
@ -584,7 +736,7 @@ impl<'d> embedded_hal_0_2::serial::Read<u8> for UartRxDriver<'d> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let mut buf = [0_u8];
|
||||
|
||||
let result = self.read(&mut buf, NON_BLOCK);
|
||||
let result = UartRxDriver::read(self, &mut buf, NON_BLOCK);
|
||||
|
||||
check_nb(result, buf[0])
|
||||
}
|
||||
@ -594,13 +746,76 @@ impl<'d> embedded_hal_nb::serial::Read<u8> for UartRxDriver<'d> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let mut buf = [0_u8];
|
||||
|
||||
let result = self.read(&mut buf, NON_BLOCK);
|
||||
let result = UartRxDriver::read(self, &mut buf, NON_BLOCK);
|
||||
|
||||
check_nb(result, buf[0])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> UartTxDriver<'d> {
|
||||
/// Create a new serial transmitter
|
||||
pub fn new<UART: Uart>(
|
||||
uart: impl Peripheral<P = UART> + 'd,
|
||||
tx: impl Peripheral<P = impl OutputPin> + 'd,
|
||||
cts: Option<impl Peripheral<P = impl InputPin> + 'd>,
|
||||
rts: Option<impl Peripheral<P = impl OutputPin> + 'd>,
|
||||
config: &config::Config,
|
||||
) -> Result<Self, EspError> {
|
||||
new_common(uart, Some(tx), None::<AnyInputPin>, cts, rts, config)?;
|
||||
|
||||
Ok(Self {
|
||||
port: UART::port() as _,
|
||||
owner: Owner::Owned,
|
||||
_p: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Change the number of stop bits
|
||||
pub fn change_stop_bits(&self, stop_bits: config::StopBits) -> Result<&Self, EspError> {
|
||||
change_stop_bits(self.port(), stop_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current number of stop bits
|
||||
pub fn stop_bits(&self) -> Result<config::StopBits, EspError> {
|
||||
stop_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the number of data bits
|
||||
pub fn change_data_bits(&self, data_bits: config::DataBits) -> Result<&Self, EspError> {
|
||||
change_data_bits(self.port(), data_bits).map(|_| self)
|
||||
}
|
||||
|
||||
/// Return the current number of data bits
|
||||
pub fn data_bits(&self) -> Result<config::DataBits, EspError> {
|
||||
data_bits(self.port())
|
||||
}
|
||||
|
||||
/// Change the type of parity checking
|
||||
pub fn change_parity(&self, parity: config::Parity) -> Result<&Self, EspError> {
|
||||
change_parity(self.port(), parity).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current type of parity checking
|
||||
pub fn parity(&self) -> Result<config::Parity, EspError> {
|
||||
parity(self.port())
|
||||
}
|
||||
|
||||
/// Change the baudrate.
|
||||
///
|
||||
/// Will automatically select the clock source. When possible the reference clock (1MHz) will
|
||||
/// be used, because this is constant when the clock source/frequency changes.
|
||||
/// However if one of the clock frequencies is below 10MHz or if the baudrate is above
|
||||
/// the reference clock or if the baudrate cannot be set within 1.5%
|
||||
/// then use the APB clock.
|
||||
pub fn change_baudrate<T: Into<Hertz> + Copy>(&self, baudrate: T) -> Result<&Self, EspError> {
|
||||
change_baudrate(self.port(), baudrate).map(|_| self)
|
||||
}
|
||||
|
||||
/// Returns the current baudrate
|
||||
pub fn baudrate(&self) -> Result<Hertz, EspError> {
|
||||
baudrate(self.port())
|
||||
}
|
||||
|
||||
/// Write multiple bytes from a slice
|
||||
pub fn write(&mut self, bytes: &[u8]) -> Result<usize, EspError> {
|
||||
// `uart_write_bytes()` returns error (-1) or how many bytes were written
|
||||
@ -624,6 +839,17 @@ impl<'d> UartTxDriver<'d> {
|
||||
pub fn port(&self) -> uart_port_t {
|
||||
self.port as _
|
||||
}
|
||||
|
||||
/// Get count of remaining capacity in the transmit ring buffer
|
||||
pub fn count(&self) -> Result<u32, EspError> {
|
||||
remaining_write_capacity(self.port())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for UartTxDriver<'d> {
|
||||
fn drop(&mut self) {
|
||||
self.owner.drop_impl(self.port()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal::serial::ErrorType for UartTxDriver<'d> {
|
||||
@ -638,7 +864,7 @@ impl<'d> embedded_hal_0_2::serial::Write<u8> for UartTxDriver<'d> {
|
||||
}
|
||||
|
||||
fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
|
||||
check_nb(self.write(&[byte]), ())
|
||||
check_nb(UartTxDriver::write(self, &[byte]), ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -648,7 +874,7 @@ impl<'d> embedded_hal_nb::serial::Write<u8> for UartTxDriver<'d> {
|
||||
}
|
||||
|
||||
fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
|
||||
check_nb(self.write(&[byte]), ())
|
||||
check_nb(UartTxDriver::write(self, &[byte]), ())
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,6 +891,141 @@ impl<'d> core::fmt::Write for UartTxDriver<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_common<UART: Uart>(
|
||||
_uart: impl Peripheral<P = UART>,
|
||||
tx: Option<impl Peripheral<P = impl OutputPin>>,
|
||||
rx: Option<impl Peripheral<P = impl InputPin>>,
|
||||
cts: Option<impl Peripheral<P = impl InputPin>>,
|
||||
rts: Option<impl Peripheral<P = impl OutputPin>>,
|
||||
config: &config::Config,
|
||||
) -> Result<(), EspError> {
|
||||
let tx = tx.map(|tx| tx.into_ref());
|
||||
let rx = rx.map(|rx| rx.into_ref());
|
||||
let cts = cts.map(|cts| cts.into_ref());
|
||||
let rts = rts.map(|rts| rts.into_ref());
|
||||
|
||||
#[allow(clippy::needless_update)]
|
||||
let uart_config = uart_config_t {
|
||||
baud_rate: config.baudrate.0 as i32,
|
||||
data_bits: config.data_bits.into(),
|
||||
parity: config.parity.into(),
|
||||
stop_bits: config.stop_bits.into(),
|
||||
flow_ctrl: config.flow_control.into(),
|
||||
rx_flow_ctrl_thresh: config.flow_control_rts_threshold,
|
||||
#[cfg(not(esp_idf_version_major = "4"))]
|
||||
source_clk: config.source_clock.into(),
|
||||
#[cfg(esp_idf_version_major = "4")]
|
||||
__bindgen_anon_1: uart_config_t__bindgen_ty_1 {
|
||||
source_clk: config.source_clock.into(),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
esp!(unsafe { uart_param_config(UART::port(), &uart_config) })?;
|
||||
|
||||
esp!(unsafe {
|
||||
uart_set_pin(
|
||||
UART::port(),
|
||||
tx.as_ref().map_or(-1, |p| p.pin()),
|
||||
rx.as_ref().map_or(-1, |p| p.pin()),
|
||||
rts.as_ref().map_or(-1, |p| p.pin()),
|
||||
cts.as_ref().map_or(-1, |p| p.pin()),
|
||||
)
|
||||
})?;
|
||||
|
||||
esp!(unsafe {
|
||||
uart_driver_install(
|
||||
UART::port(),
|
||||
UART_FIFO_SIZE * 2,
|
||||
if tx.is_some() { UART_FIFO_SIZE * 2 } else { 0 },
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop_bits(port: uart_port_t) -> Result<config::StopBits, EspError> {
|
||||
let mut stop_bits: uart_stop_bits_t = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_stop_bits(port, &mut stop_bits) },
|
||||
stop_bits.into()
|
||||
)
|
||||
}
|
||||
|
||||
fn change_stop_bits(port: uart_port_t, stop_bits: config::StopBits) -> Result<(), EspError> {
|
||||
esp!(unsafe { uart_set_stop_bits(port, stop_bits.into()) })
|
||||
}
|
||||
|
||||
fn data_bits(port: uart_port_t) -> Result<config::DataBits, EspError> {
|
||||
let mut data_bits: uart_word_length_t = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_word_length(port, &mut data_bits) },
|
||||
data_bits.into()
|
||||
)
|
||||
}
|
||||
|
||||
fn change_data_bits(port: uart_port_t, data_bits: config::DataBits) -> Result<(), EspError> {
|
||||
esp!(unsafe { uart_set_word_length(port, data_bits.into()) })
|
||||
}
|
||||
|
||||
fn parity(port: uart_port_t) -> Result<config::Parity, EspError> {
|
||||
let mut parity: uart_parity_t = 0;
|
||||
esp_result!(unsafe { uart_get_parity(port, &mut parity) }, parity.into())
|
||||
}
|
||||
|
||||
fn change_parity(port: uart_port_t, parity: config::Parity) -> Result<(), EspError> {
|
||||
esp!(unsafe { uart_set_parity(port, parity.into()) })
|
||||
}
|
||||
|
||||
fn baudrate(port: uart_port_t) -> Result<Hertz, EspError> {
|
||||
let mut baudrate: u32 = 0;
|
||||
esp_result!(
|
||||
unsafe { uart_get_baudrate(port, &mut baudrate) },
|
||||
baudrate.into()
|
||||
)
|
||||
}
|
||||
|
||||
fn change_baudrate<T: Into<Hertz> + Copy>(port: uart_port_t, baudrate: T) -> Result<(), EspError> {
|
||||
esp!(unsafe { uart_set_baudrate(port, baudrate.into().into()) })
|
||||
}
|
||||
|
||||
fn delete_driver(port: uart_port_t) -> Result<(), EspError> {
|
||||
esp!(unsafe { uart_driver_delete(port) })
|
||||
}
|
||||
|
||||
pub fn remaining_unread_bytes(port: uart_port_t) -> Result<u32, EspError> {
|
||||
let mut size = 0_u32;
|
||||
esp_result!(unsafe { uart_get_buffered_data_len(port, &mut size) }, size)
|
||||
}
|
||||
|
||||
pub fn remaining_write_capacity(port: uart_port_t) -> Result<u32, EspError> {
|
||||
let mut size = 0_u32;
|
||||
esp_result!(
|
||||
unsafe { uart_get_tx_buffer_free_size(port, &mut size) },
|
||||
size
|
||||
)
|
||||
}
|
||||
|
||||
enum Owner {
|
||||
Owned,
|
||||
Borrowed,
|
||||
Shared,
|
||||
}
|
||||
|
||||
impl Owner {
|
||||
fn drop_impl(&self, port: uart_port_t) -> Result<(), EspError> {
|
||||
let needs_drop = match self {
|
||||
Owner::Owned => true,
|
||||
Owner::Borrowed => false,
|
||||
Owner::Shared => REFS[port as usize].fetch_sub(1, Ordering::SeqCst) == 0,
|
||||
};
|
||||
needs_drop.then(|| delete_driver(port)).unwrap_or(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_uart {
|
||||
($uart:ident: $port:expr) => {
|
||||
crate::impl_peripheral!($uart);
|
||||
@ -698,3 +1059,7 @@ impl_uart!(UART0: 0);
|
||||
impl_uart!(UART1: 1);
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
impl_uart!(UART2: 2);
|
||||
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const NO_REFS: AtomicU8 = AtomicU8::new(0);
|
||||
static REFS: [AtomicU8; UART_NUM_MAX as usize] = [NO_REFS; UART_NUM_MAX as usize];
|
||||
|
Loading…
x
Reference in New Issue
Block a user