mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 04:40:52 +00:00
311 lines
7.9 KiB
Rust
311 lines
7.9 KiB
Rust
//! # Universal Asynchronous Receiver/Transmitter (UART)
|
|
//!
|
|
//! ## Overview
|
|
//!
|
|
//! The UART is a hardware peripheral which handles communication using serial
|
|
//! interfaces. This peripheral provides a cheap and ubiquitous method for full-
|
|
//! and half-duplex communication between devices.
|
|
//!
|
|
//! ## Configuration
|
|
//!
|
|
//! The usual setting such as baud rate, data bits, parity, and stop bits can
|
|
//! easily be configured. See the [config] module documentation for more
|
|
//! information.
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! The UART driver implements a number of third-party traits, with the
|
|
//! intention of making the HAL inter-compatible with various device drivers
|
|
//! from the community. This includes the [embedded-hal], [embedded-hal-nb], and
|
|
//! [embedded-io] traits.
|
|
//!
|
|
//! ## Examples
|
|
//!
|
|
//! ```rust
|
|
//! fn main(mut uart: LpUart) -> ! {
|
|
//! loop {
|
|
//! writeln!(uart, "Hello, world!").ok();
|
|
//! esp_lp_hal::delay::Delay.delay_ms(1000);
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/
|
|
//! [embedded-hal-nb]: https://docs.rs/embedded-hal-nb/latest/embedded_hal_nb/
|
|
//! [embedded-io]: https://docs.rs/embedded-io/latest/embedded_io/
|
|
|
|
use crate::pac::LP_UART;
|
|
|
|
const UART_FIFO_SIZE: u16 = property!("lp_uart.ram_size");
|
|
|
|
#[doc(hidden)]
|
|
pub unsafe fn conjure() -> LpUart {
|
|
unsafe {
|
|
LpUart {
|
|
uart: LP_UART::steal(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// UART Error
|
|
#[derive(Debug)]
|
|
pub enum Error {}
|
|
|
|
#[cfg(feature = "embedded-hal")]
|
|
impl embedded_hal_nb::serial::Error for Error {
|
|
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
|
|
embedded_hal_nb::serial::ErrorKind::Other
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-io")]
|
|
impl embedded_io::Error for Error {
|
|
fn kind(&self) -> embedded_io::ErrorKind {
|
|
embedded_io::ErrorKind::Other
|
|
}
|
|
}
|
|
|
|
/// UART configuration
|
|
pub mod config {
|
|
/// Number of data bits
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
pub enum DataBits {
|
|
/// 5 data bits
|
|
DataBits5 = 0,
|
|
/// 6 data bits
|
|
DataBits6 = 1,
|
|
/// 7 data bits
|
|
DataBits7 = 2,
|
|
/// 8 data bits
|
|
#[default]
|
|
DataBits8 = 3,
|
|
}
|
|
|
|
/// Parity check
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
pub enum Parity {
|
|
/// No parity
|
|
#[default]
|
|
ParityNone = 0,
|
|
/// Even parity
|
|
ParityEven = 1,
|
|
/// Odd parity
|
|
ParityOdd = 2,
|
|
}
|
|
|
|
/// Number of stop bits
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
pub enum StopBits {
|
|
/// 1 stop bit
|
|
#[default]
|
|
Stop1 = 1,
|
|
/// 1.5 stop bits
|
|
Stop1p5 = 2,
|
|
/// 2 stop bits
|
|
Stop2 = 3,
|
|
}
|
|
|
|
/// UART configuration
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Config {
|
|
baudrate: u32,
|
|
data_bits: DataBits,
|
|
parity: Parity,
|
|
stop_bits: StopBits,
|
|
}
|
|
|
|
impl Config {
|
|
/// Configure the UART's baud rate
|
|
pub fn baudrate(mut self, baudrate: u32) -> Self {
|
|
self.baudrate = baudrate;
|
|
self
|
|
}
|
|
|
|
/// Configure the UART to use no parity
|
|
pub fn parity_none(mut self) -> Self {
|
|
self.parity = Parity::ParityNone;
|
|
self
|
|
}
|
|
|
|
/// Configure the UART to use even parity
|
|
pub fn parity_even(mut self) -> Self {
|
|
self.parity = Parity::ParityEven;
|
|
self
|
|
}
|
|
|
|
/// Configure the UART to use odd parity
|
|
pub fn parity_odd(mut self) -> Self {
|
|
self.parity = Parity::ParityOdd;
|
|
self
|
|
}
|
|
|
|
/// Configure the UART's data bits
|
|
pub fn data_bits(mut self, data_bits: DataBits) -> Self {
|
|
self.data_bits = data_bits;
|
|
self
|
|
}
|
|
|
|
/// Configure the UART's stop bits
|
|
pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
|
|
self.stop_bits = stop_bits;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Config {
|
|
Config {
|
|
baudrate: 115_200,
|
|
data_bits: Default::default(),
|
|
parity: Default::default(),
|
|
stop_bits: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// LP-UART driver
|
|
pub struct LpUart {
|
|
uart: LP_UART,
|
|
}
|
|
|
|
impl LpUart {
|
|
/// Read a single byte from the UART in a non-blocking manner.
|
|
pub fn read_byte(&mut self) -> nb::Result<u8, Error> {
|
|
if self.rx_fifo_count() > 0 {
|
|
let byte = self.uart.fifo().read().rxfifo_rd_byte().bits();
|
|
Ok(byte)
|
|
} else {
|
|
Err(nb::Error::WouldBlock)
|
|
}
|
|
}
|
|
|
|
/// Write a single byte to the UART in a non-blocking manner.
|
|
pub fn write_byte(&mut self, byte: u8) -> nb::Result<(), Error> {
|
|
if self.tx_fifo_count() < UART_FIFO_SIZE {
|
|
self.uart
|
|
.fifo()
|
|
.write(|w| unsafe { w.rxfifo_rd_byte().bits(byte) });
|
|
Ok(())
|
|
} else {
|
|
Err(nb::Error::WouldBlock)
|
|
}
|
|
}
|
|
|
|
/// Write one or more byte to the UART, blocking until the write has
|
|
/// completed.
|
|
pub fn write_bytes(&mut self, data: &[u8]) -> Result<usize, Error> {
|
|
let count = data.len();
|
|
|
|
data.iter()
|
|
.try_for_each(|c| nb::block!(self.write_byte(*c)))?;
|
|
|
|
Ok(count)
|
|
}
|
|
|
|
/// Flush the UART's transmit buffer in a non-blocking manner.
|
|
pub fn flush_tx(&mut self) -> nb::Result<(), Error> {
|
|
if self.is_tx_idle() {
|
|
Ok(())
|
|
} else {
|
|
Err(nb::Error::WouldBlock)
|
|
}
|
|
}
|
|
|
|
fn rx_fifo_count(&mut self) -> u16 {
|
|
self.uart.status().read().rxfifo_cnt().bits().into()
|
|
}
|
|
|
|
fn tx_fifo_count(&mut self) -> u16 {
|
|
self.uart.status().read().txfifo_cnt().bits().into()
|
|
}
|
|
|
|
fn is_tx_idle(&self) -> bool {
|
|
self.uart.fsm_status().read().st_utx_out().bits() == 0
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Write for LpUart {
|
|
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
|
self.write_bytes(s.as_bytes())
|
|
.map_err(|_| core::fmt::Error)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-hal")]
|
|
impl embedded_hal_nb::serial::ErrorType for LpUart {
|
|
type Error = Error;
|
|
}
|
|
|
|
#[cfg(feature = "embedded-hal")]
|
|
impl embedded_hal_nb::serial::Read for LpUart {
|
|
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
|
self.read_byte()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-hal")]
|
|
impl embedded_hal_nb::serial::Write for LpUart {
|
|
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
|
self.write_byte(word)
|
|
}
|
|
|
|
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
|
self.flush_tx()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-io")]
|
|
impl embedded_io::ErrorType for LpUart {
|
|
type Error = Error;
|
|
}
|
|
|
|
#[cfg(feature = "embedded-io")]
|
|
impl embedded_io::Read for LpUart {
|
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
|
if buf.is_empty() {
|
|
return Ok(0);
|
|
}
|
|
|
|
while self.rx_fifo_count() == 0 {
|
|
// Block until we have received at least one byte
|
|
}
|
|
|
|
let mut count = 0;
|
|
while self.rx_fifo_count() > 0 && count < buf.len() {
|
|
buf[count] = self.uart.fifo().read().rxfifo_rd_byte().bits();
|
|
count += 1;
|
|
}
|
|
|
|
Ok(count)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-io")]
|
|
impl embedded_io::ReadReady for LpUart {
|
|
fn read_ready(&mut self) -> Result<bool, Self::Error> {
|
|
Ok(self.rx_fifo_count() > 0)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embedded-io")]
|
|
impl embedded_io::Write for LpUart {
|
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
|
self.write_bytes(buf)
|
|
}
|
|
|
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
|
loop {
|
|
match self.flush_tx() {
|
|
Ok(_) => break,
|
|
Err(nb::Error::WouldBlock) => { /* Wait */ }
|
|
#[allow(unreachable_patterns)]
|
|
Err(nb::Error::Other(e)) => return Err(e),
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|