Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix rom::crc docs
- Add octal PSRAM support for ESP32-S3 (#610)
- Add MD5 functions from ESP ROM (#618)
- Add embassy async `read` support for `uart` (#620)

### Changed

Expand Down
135 changes: 109 additions & 26 deletions esp-hal-common/src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const UART_FIFO_SIZE: u16 = 128;

/// Custom serial error type
#[derive(Debug)]
pub enum Error {}
pub enum Error {
#[cfg(feature = "async")]
ReadBufferFull,
}

/// UART configuration
pub mod config {
Expand Down Expand Up @@ -994,11 +997,15 @@ mod asynch {
use core::task::Poll;

use cfg_if::cfg_if;
use embassy_futures::select::select;
use embassy_sync::waitqueue::AtomicWaker;
use procmacros::interrupt;

use super::{Error, Instance};
use crate::{uart::UART_FIFO_SIZE, Uart};
use crate::{
uart::{RegisterBlock, UART_FIFO_SIZE},
Uart,
};

cfg_if! {
if #[cfg(all(uart0, uart1, uart2))] {
Expand All @@ -1016,6 +1023,8 @@ mod asynch {
pub(crate) enum Event {
TxDone,
TxFiFoEmpty,
RxFifoFull,
RxCmdCharDetected,
}

pub(crate) struct UartFuture<'a, T: Instance> {
Expand All @@ -1034,6 +1043,14 @@ mod asynch {
.register_block()
.int_ena
.modify(|_, w| w.txfifo_empty_int_ena().set_bit()),
Event::RxFifoFull => instance
.register_block()
.int_ena
.modify(|_, w| w.rxfifo_full_int_ena().set_bit()),
Event::RxCmdCharDetected => instance
.register_block()
.int_ena
.modify(|_, w| w.at_cmd_char_det_int_ena().set_bit()),
}

Self { event, instance }
Expand All @@ -1055,6 +1072,20 @@ mod asynch {
.read()
.txfifo_empty_int_ena()
.bit_is_clear(),
Event::RxFifoFull => self
.instance
.register_block()
.int_ena
.read()
.rxfifo_full_int_ena()
.bit_is_clear(),
Event::RxCmdCharDetected => self
.instance
.register_block()
.int_ena
.read()
.at_cmd_char_det_int_ena()
.bit_is_clear(),
}
}
}
Expand All @@ -1079,11 +1110,42 @@ mod asynch {
where
T: Instance,
{
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
let mut read_bytes = 0;

select(
UartFuture::new(Event::RxCmdCharDetected, self.inner()),
UartFuture::new(Event::RxFifoFull, self.inner()),
)
.await;

while let Ok(byte) = self.read_byte() {
if read_bytes < buf.len() {
buf[read_bytes] = byte;
read_bytes += 1;
} else {
return Err(Error::ReadBufferFull);
}
}

Ok(read_bytes)
}

async fn write(&mut self, words: &[u8]) -> Result<(), Error> {
for chunk in words.chunks(UART_FIFO_SIZE as usize) {
for &byte in chunk {
self.write_byte(byte).unwrap() // should never fail
let mut offset: usize = 0;
loop {
let mut next_offset =
offset + (UART_FIFO_SIZE - self.uart.get_tx_fifo_count()) as usize;
if next_offset > words.len() {
next_offset = words.len();
}
for &byte in &words[offset..next_offset] {
self.write_byte(byte).unwrap(); // should never fail
}
if next_offset == words.len() {
break;
}
offset = next_offset;
UartFuture::new(Event::TxFiFoEmpty, self.inner()).await;
}
Ok(())
Expand Down Expand Up @@ -1111,42 +1173,63 @@ mod asynch {
}
}

fn intr_handler(uart: &RegisterBlock) -> bool {
let int_ena_val = uart.int_ena.read();
let int_raw_val = uart.int_raw.read();
if int_ena_val.txfifo_empty_int_ena().bit_is_set()
&& int_raw_val.txfifo_empty_int_raw().bit_is_set()
{
uart.int_ena.write(|w| w.txfifo_empty_int_ena().clear_bit());
return true;
}
if int_ena_val.tx_done_int_ena().bit_is_set() && int_raw_val.tx_done_int_raw().bit_is_set()
{
uart.int_ena.write(|w| w.tx_done_int_ena().clear_bit());
return true;
}
if int_ena_val.at_cmd_char_det_int_ena().bit_is_set()
&& int_raw_val.at_cmd_char_det_int_raw().bit_is_set()
{
uart.int_clr
.write(|w| w.at_cmd_char_det_int_clr().set_bit());
uart.int_ena
.write(|w| w.at_cmd_char_det_int_ena().clear_bit());
return true;
}
if int_ena_val.rxfifo_full_int_ena().bit_is_set()
&& int_raw_val.rxfifo_full_int_raw().bit_is_set()
{
uart.int_clr.write(|w| w.rxfifo_full_int_clr().set_bit());
uart.int_ena.write(|w| w.rxfifo_full_int_ena().clear_bit());
return true;
}
false
}

#[cfg(uart0)]
#[interrupt]
fn UART0() {
let uart = unsafe { &*crate::peripherals::UART0::ptr() };
uart.int_ena.modify(|_, w| {
w.txfifo_empty_int_ena()
.clear_bit()
.tx_done_int_ena()
.clear_bit()
});
WAKERS[0].wake();
if intr_handler(uart) {
WAKERS[0].wake();
}
}

#[cfg(uart1)]
#[interrupt]
fn UART1() {
let uart = unsafe { &*crate::peripherals::UART1::ptr() };
uart.int_ena.modify(|_, w| {
w.txfifo_empty_int_ena()
.clear_bit()
.tx_done_int_ena()
.clear_bit()
});
WAKERS[1].wake();
if intr_handler(uart) {
WAKERS[1].wake();
}
}

#[cfg(uart2)]
#[interrupt]
fn UART2() {
let uart = unsafe { &*crate::peripherals::UART2::ptr() };
uart.int_ena.modify(|_, w| {
w.txfifo_empty_int_ena()
.clear_bit()
.tx_done_int_ena()
.clear_bit()
});
WAKERS[2].wake();
if intr_handler(uart) {
WAKERS[2].wake();
}
}
}
1 change: 1 addition & 0 deletions esp32-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ sha2 = { version = "0.10.6", default-features = false}
smart-leds = "0.3.0"
ssd1306 = "0.7.1"
static_cell = "1.0.0"
heapless = "0.7.16"

[features]
default = ["rt", "vectored", "xtal40mhz"]
Expand Down
75 changes: 70 additions & 5 deletions esp32-hal/examples/embassy_serial.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
//! embassy serial
//!
//! This is an example of running the embassy executor and asynchronously
//! writing to a uart.
//! writing to and reading from uart

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use core::fmt::Write;

use embassy_executor::Executor;
use embassy_time::{Duration, Timer};
use embassy_time::{with_timeout, Duration};
use esp32_hal::{
clock::ClockControl,
embassy,
Expand All @@ -20,18 +22,79 @@ use esp32_hal::{
Uart,
};
use esp_backtrace as _;
use esp_hal_common::uart::config::AtCmdConfig;
use heapless::Vec;
use static_cell::StaticCell;

// rx_fifo_full_threshold
const READ_BUF_SIZE: usize = 128;
// EOT (CTRL-D)
const AT_CMD: u8 = 0x04;

#[embassy_executor::task]
async fn run(mut uart: Uart<'static, UART0>) {
// max message size to receive
// leave some extra space for AT-CMD characters
const MAX_BUFFER_SIZE: usize = 10 * READ_BUF_SIZE + 16;
// timeout read
const READ_TIMEOUT: Duration = Duration::from_secs(10);

let mut rbuf: Vec<u8, MAX_BUFFER_SIZE> = Vec::new();
let mut wbuf: Vec<u8, MAX_BUFFER_SIZE> = Vec::new();
loop {
embedded_hal_async::serial::Write::write(&mut uart, b"Hello async write!!!\r\n")
if rbuf.is_empty() {
embedded_hal_async::serial::Write::write(
&mut uart,
b"Hello async serial. Enter something ended with EOT (CTRL-D).\r\n",
)
.await
.unwrap();
} else {
wbuf.clear();
write!(&mut wbuf, "\r\n-- received {} bytes --\r\n", rbuf.len()).unwrap();
embedded_hal_async::serial::Write::write(&mut uart, wbuf.as_slice())
.await
.unwrap();
embedded_hal_async::serial::Write::write(&mut uart, rbuf.as_slice())
.await
.unwrap();
embedded_hal_async::serial::Write::write(&mut uart, b"\r\n")
.await
.unwrap();
}
embedded_hal_async::serial::Write::flush(&mut uart)
.await
.unwrap();
Timer::after(Duration::from_millis(1_000)).await;

// set rbuf full capacity
rbuf.resize_default(rbuf.capacity()).ok();
let mut offset = 0;
loop {
match with_timeout(READ_TIMEOUT, uart.read(&mut rbuf[offset..])).await {
Ok(r) => {
if let Ok(len) = r {
offset += len;
if offset == 0 {
rbuf.truncate(0);
break;
}
// if set_at_cmd is used than stop reading
if len < READ_BUF_SIZE {
rbuf.truncate(offset);
break;
}
} else {
// buffer is full
break;
}
}
Err(_) => {
// Timeout
rbuf.truncate(offset);
break;
}
}
}
}
}

Expand Down Expand Up @@ -66,7 +129,9 @@ fn main() -> ! {
#[cfg(feature = "embassy-time-timg0")]
embassy::init(&clocks, timer_group0.timer0);

let uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control);
let mut uart0 = Uart::new(peripherals.UART0, &mut system.peripheral_clock_control);
uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None));
uart0.set_rx_fifo_full_threshold(READ_BUF_SIZE as u16);

interrupt::enable(Interrupt::UART0, interrupt::Priority::Priority1).unwrap();

Expand Down
1 change: 1 addition & 0 deletions esp32c2-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ lis3dh-async = "0.7.0"
sha2 = { version = "0.10.6", default-features = false}
ssd1306 = "0.7.1"
static_cell = "1.0.0"
heapless = "0.7.16"

[features]
default = ["rt", "vectored", "xtal40mhz"]
Expand Down
Loading