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 @@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add embassy async `read` support for `uart` (#620)
- Add bare-bones support to run code on ULP-RISCV / LP core (#631)
- Add ADC calibration implementation for a riscv chips (#555)
- Add `async` implementation for `USB Serial/JTAG`(#632)

### Changed

Expand Down
137 changes: 136 additions & 1 deletion esp-hal-common/src/usb_serial_jtag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::{
peripherals::{usb_device::RegisterBlock, USB_DEVICE},
system::PeripheralClockControl,
};

/// USB Serial JTAG driver
pub struct UsbSerialJtag<'d> {
usb_serial: PeripheralRef<'d, USB_DEVICE>,
Expand Down Expand Up @@ -151,6 +150,15 @@ impl<'d> UsbSerialJtag<'d> {
.int_clr
.write(|w| w.serial_out_recv_pkt_int_clr().set_bit())
}
#[cfg(feature = "async")]
pub(crate) fn inner(&self) -> &USB_DEVICE {
&self.usb_serial
}

#[cfg(feature = "async")]
pub(crate) fn inner_mut(&mut self) -> &mut USB_DEVICE {
&mut self.usb_serial
}
}

/// USB Serial JTAG peripheral instance
Expand Down Expand Up @@ -192,6 +200,14 @@ pub trait Instance {

(wr_addr - rd_addr).into()
}

fn txfifo_empty(&self) -> bool {
self.register_block()
.jfifo_st
.read()
.out_fifo_empty()
.bit_is_clear()
}
}

impl Instance for USB_DEVICE {
Expand Down Expand Up @@ -249,3 +265,122 @@ impl embedded_hal_nb::serial::Write for UsbSerialJtag<'_> {
self.flush_tx_nb()
}
}

#[cfg(feature = "async")]
mod asynch {
use core::task::Poll;

use embassy_sync::waitqueue::AtomicWaker;
use procmacros::interrupt;

use super::{Error, Instance};
use crate::UsbSerialJtag;

// Single static instance of the waker
static WAKER: AtomicWaker = AtomicWaker::new();

pub(crate) struct UsbSerialJtagFuture<'d> {
instance: &'d crate::peripherals::USB_DEVICE,
}

impl<'d> UsbSerialJtagFuture<'d> {
pub fn new(instance: &'d crate::peripherals::USB_DEVICE) -> Self {
// Set the interrupt enable bit for the USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT
// interrupt
instance
.register_block()
.int_ena
.modify(|_, w| w.serial_in_empty_int_ena().set_bit());

Self { instance }
}

fn event_bit_is_clear(&self) -> bool {
self.instance
.register_block()
.int_ena
.read()
.serial_in_empty_int_ena()
.bit_is_clear()
}
}

impl<'d> core::future::Future for UsbSerialJtagFuture<'d> {
type Output = ();

fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
WAKER.register(cx.waker());
if self.event_bit_is_clear() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}

impl UsbSerialJtag<'_> {
async fn write(&mut self, words: &[u8]) -> Result<(), Error> {
let reg_block = self.usb_serial.register_block();
for chunk in words.chunks(64 as usize) {
unsafe {
for &b in chunk {
reg_block.ep1.write(|w| w.rdwr_byte().bits(b.into()))
}
reg_block.ep1_conf.write(|w| w.wr_done().set_bit());
UsbSerialJtagFuture::new(self.inner()).await;
}
}
Ok(())
}

async fn flush(&mut self) -> Result<(), Error> {
if self.inner().txfifo_empty() {
UsbSerialJtagFuture::new(self.inner()).await;
}
Ok(())
}
}

impl embedded_hal_async::serial::Write for UsbSerialJtag<'_> {
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write(words).await
}

async fn flush(&mut self) -> Result<(), Self::Error> {
self.flush().await
}
}

#[cfg(esp32c3)]
#[interrupt]
fn USB_SERIAL_JTAG() {
let usb_serial_jtag = unsafe { &*crate::peripherals::USB_DEVICE::ptr() };
usb_serial_jtag
.int_ena
.modify(|_, w| w.serial_in_empty_int_ena().clear_bit());
WAKER.wake();
}

#[cfg(esp32s3)]
#[interrupt]
fn USB_DEVICE() {
let usb_serial_jtag = unsafe { &*crate::peripherals::USB_DEVICE::ptr() };
usb_serial_jtag
.int_ena
.modify(|_, w| w.serial_in_empty_int_ena().clear_bit());
WAKER.wake();
}

#[cfg(any(esp32c6, esp32h2))]
#[interrupt]
fn USB() {
let usb_serial_jtag = unsafe { &*crate::peripherals::USB_DEVICE::ptr() };
usb_serial_jtag
.int_ena
.modify(|_, w| w.serial_in_empty_int_ena().clear_bit());
WAKER.wake();
}
}