Rebase on master
This commit is contained in:
commit
7ee7109508
1
ci.sh
1
ci.sh
@ -58,6 +58,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \
|
||||
|
@ -604,6 +604,21 @@ impl FirmwareUpdater {
|
||||
self.dfu.len()
|
||||
}
|
||||
|
||||
/// Obtain the current state.
|
||||
///
|
||||
/// This is useful to check if the bootloader has just done a swap, in order
|
||||
/// to do verifications and self-tests of the new image before calling
|
||||
/// `mark_booted`.
|
||||
pub async fn get_state<F: AsyncNorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> {
|
||||
flash.read(self.state.from as u32, aligned).await?;
|
||||
|
||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||
Ok(State::Swap)
|
||||
} else {
|
||||
Ok(State::Boot)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark to trigger firmware swap on next boot.
|
||||
///
|
||||
/// # Safety
|
||||
@ -660,12 +675,6 @@ impl FirmwareUpdater {
|
||||
) -> Result<(), F::Error> {
|
||||
assert!(data.len() >= F::ERASE_SIZE);
|
||||
|
||||
trace!(
|
||||
"Writing firmware at offset 0x{:x} len {}",
|
||||
self.dfu.from + offset,
|
||||
data.len()
|
||||
);
|
||||
|
||||
flash
|
||||
.erase(
|
||||
(self.dfu.from + offset) as u32,
|
||||
@ -679,7 +688,156 @@ impl FirmwareUpdater {
|
||||
self.dfu.from + offset + data.len()
|
||||
);
|
||||
|
||||
let mut write_offset = self.dfu.from + offset;
|
||||
FirmwareWriter(self.dfu)
|
||||
.write_block(offset, data, flash, block_size)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare for an incoming DFU update by erasing the entire DFU area and
|
||||
/// returning a `FirmwareWriter`.
|
||||
///
|
||||
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||
/// exchange for added complexity.
|
||||
pub async fn prepare_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
|
||||
flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?;
|
||||
|
||||
trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
|
||||
|
||||
Ok(FirmwareWriter(self.dfu))
|
||||
}
|
||||
|
||||
//
|
||||
// Blocking API
|
||||
//
|
||||
|
||||
/// Obtain the current state.
|
||||
///
|
||||
/// This is useful to check if the bootloader has just done a swap, in order
|
||||
/// to do verifications and self-tests of the new image before calling
|
||||
/// `mark_booted`.
|
||||
pub fn get_state_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<State, F::Error> {
|
||||
flash.read(self.state.from as u32, aligned)?;
|
||||
|
||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||
Ok(State::Swap)
|
||||
} else {
|
||||
Ok(State::Boot)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark to trigger firmware swap on next boot.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||
pub fn mark_updated_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
|
||||
assert_eq!(aligned.len(), F::WRITE_SIZE);
|
||||
self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
|
||||
}
|
||||
|
||||
/// Mark firmware boot successful and stop rollback on reset.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||
pub fn mark_booted_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
|
||||
assert_eq!(aligned.len(), F::WRITE_SIZE);
|
||||
self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
|
||||
}
|
||||
|
||||
fn set_magic_blocking<F: NorFlash>(
|
||||
&mut self,
|
||||
aligned: &mut [u8],
|
||||
magic: u8,
|
||||
flash: &mut F,
|
||||
) -> Result<(), F::Error> {
|
||||
flash.read(self.state.from as u32, aligned)?;
|
||||
|
||||
if aligned.iter().any(|&b| b != magic) {
|
||||
aligned.fill(0);
|
||||
|
||||
flash.write(self.state.from as u32, aligned)?;
|
||||
flash.erase(self.state.from as u32, self.state.to as u32)?;
|
||||
|
||||
aligned.fill(magic);
|
||||
flash.write(self.state.from as u32, aligned)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
pub fn write_firmware_blocking<F: NorFlash>(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
data: &[u8],
|
||||
flash: &mut F,
|
||||
block_size: usize,
|
||||
) -> Result<(), F::Error> {
|
||||
assert!(data.len() >= F::ERASE_SIZE);
|
||||
|
||||
flash.erase(
|
||||
(self.dfu.from + offset) as u32,
|
||||
(self.dfu.from + offset + data.len()) as u32,
|
||||
)?;
|
||||
|
||||
trace!(
|
||||
"Erased from {} to {}",
|
||||
self.dfu.from + offset,
|
||||
self.dfu.from + offset + data.len()
|
||||
);
|
||||
|
||||
FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare for an incoming DFU update by erasing the entire DFU area and
|
||||
/// returning a `FirmwareWriter`.
|
||||
///
|
||||
/// Using this instead of `write_firmware_blocking` allows for an optimized
|
||||
/// API in exchange for added complexity.
|
||||
pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
|
||||
flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
|
||||
|
||||
trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
|
||||
|
||||
Ok(FirmwareWriter(self.dfu))
|
||||
}
|
||||
}
|
||||
|
||||
/// FirmwareWriter allows writing blocks to an already erased flash.
|
||||
pub struct FirmwareWriter(Partition);
|
||||
|
||||
impl FirmwareWriter {
|
||||
/// Write data to a flash page.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
pub async fn write_block<F: AsyncNorFlash>(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
data: &[u8],
|
||||
flash: &mut F,
|
||||
block_size: usize,
|
||||
) -> Result<(), F::Error> {
|
||||
trace!(
|
||||
"Writing firmware at offset 0x{:x} len {}",
|
||||
self.0.from + offset,
|
||||
data.len()
|
||||
);
|
||||
|
||||
let mut write_offset = self.0.from + offset;
|
||||
for chunk in data.chunks(block_size) {
|
||||
trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
|
||||
flash.write(write_offset as u32, chunk).await?;
|
||||
@ -702,6 +860,50 @@ impl FirmwareUpdater {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
pub fn write_block_blocking<F: NorFlash>(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
data: &[u8],
|
||||
flash: &mut F,
|
||||
block_size: usize,
|
||||
) -> Result<(), F::Error> {
|
||||
trace!(
|
||||
"Writing firmware at offset 0x{:x} len {}",
|
||||
self.0.from + offset,
|
||||
data.len()
|
||||
);
|
||||
|
||||
let mut write_offset = self.0.from + offset;
|
||||
for chunk in data.chunks(block_size) {
|
||||
trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
|
||||
flash.write(write_offset as u32, chunk)?;
|
||||
write_offset += chunk.len();
|
||||
}
|
||||
/*
|
||||
trace!("Wrote data, reading back for verification");
|
||||
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let mut data_offset = 0;
|
||||
let mut read_offset = self.dfu.from + offset;
|
||||
for chunk in buf.chunks_mut(block_size) {
|
||||
flash.read(read_offset as u32, chunk).await?;
|
||||
trace!("Read chunk at {}: {:?}", read_offset, chunk);
|
||||
assert_eq!(&data[data_offset..data_offset + block_size], chunk);
|
||||
read_offset += chunk.len();
|
||||
data_offset += chunk.len();
|
||||
}
|
||||
*/
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -234,15 +234,15 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<RS: RadioSwitch> PhyRxTx for SubGhzRadio<'static, RS> {
|
||||
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
|
||||
type PhyError = RadioError;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where RS: 'm;
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm;
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
async move { self.do_tx(config, buf).await }
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where RS: 'm;
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm;
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
async move { self.do_rx(config, buf).await }
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ flavors = [
|
||||
|
||||
time = ["dep:embassy-time"]
|
||||
|
||||
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
|
||||
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
|
||||
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
|
||||
|
||||
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
|
||||
@ -70,7 +70,7 @@ embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]}
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||
|
@ -26,7 +26,7 @@ use embassy_sync::waitqueue::WakerRegistration;
|
||||
// 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::gpio::Pin as GpioPin;
|
||||
use crate::gpio::{self, Pin as GpioPin};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
|
||||
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
|
||||
@ -428,21 +428,23 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
|
||||
fn drop(&mut self) {
|
||||
let r = U::regs();
|
||||
|
||||
// TODO this probably deadlocks. do like Uarte instead.
|
||||
|
||||
self.timer.stop();
|
||||
if let RxState::Receiving = self.rx_state {
|
||||
|
||||
r.inten.reset();
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
if let TxState::Transmitting(_) = self.tx_state {
|
||||
r.events_txstopped.reset();
|
||||
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
if let RxState::Receiving = self.rx_state {
|
||||
low_power_wait_until(|| r.events_endrx.read().bits() == 1);
|
||||
}
|
||||
if let TxState::Transmitting(_) = self.tx_state {
|
||||
low_power_wait_until(|| r.events_endtx.read().bits() == 1);
|
||||
}
|
||||
|
||||
while r.events_txstopped.read().bits() == 0 {}
|
||||
while r.events_rxto.read().bits() == 0 {}
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
gpio::deconfigure_pin(r.psel.rxd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.txd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.rts.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.cts.read().bits());
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a,
|
||||
trace!("irq: end");
|
||||
}
|
||||
}
|
||||
|
||||
/// Low power blocking wait loop using WFE/SEV.
|
||||
fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
||||
while !condition() {
|
||||
// WFE might "eat" an event that would have otherwise woken the executor.
|
||||
cortex_m::asm::wfe();
|
||||
}
|
||||
// Retrigger an event to be transparent to the executor.
|
||||
cortex_m::asm::sev();
|
||||
}
|
||||
|
@ -9,9 +9,8 @@ use core::task::Poll;
|
||||
use cortex_m::peripheral::NVIC;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
pub use embassy_usb;
|
||||
use embassy_usb::driver::{self, EndpointError, Event, Unsupported};
|
||||
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||
use embassy_usb_driver as driver;
|
||||
use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported};
|
||||
use pac::usbd::RegisterBlock;
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
@ -243,7 +242,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
let index = self.alloc_in.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::In);
|
||||
Ok(Endpoint::new(EndpointInfo {
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
@ -259,7 +258,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
let index = self.alloc_out.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
|
||||
Ok(Endpoint::new(EndpointInfo {
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
@ -428,8 +427,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
let regs = T::regs();
|
||||
let i = ep_addr.index();
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
|
||||
UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
|
||||
Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(),
|
||||
Direction::In => regs.halted.epin[i].read().getstatus().is_halted(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,7 +441,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled);
|
||||
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
let mut was_enabled = false;
|
||||
regs.epinen.modify(|r, w| {
|
||||
let mut bits = r.bits();
|
||||
@ -466,7 +465,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
|
||||
In::waker(i).wake();
|
||||
}
|
||||
UsbDirection::Out => {
|
||||
Direction::Out => {
|
||||
regs.epouten.modify(|r, w| {
|
||||
let mut bits = r.bits();
|
||||
if enabled {
|
||||
|
@ -12,7 +12,7 @@ flavors = [
|
||||
]
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "embassy-usb?/defmt"]
|
||||
defmt = ["dep:defmt", "embassy-usb-driver?/defmt"]
|
||||
|
||||
# Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
|
||||
@ -23,11 +23,11 @@ unstable-pac = []
|
||||
time-driver = []
|
||||
|
||||
rom-func-cache = []
|
||||
disable-intrinsics = []
|
||||
intrinsics = []
|
||||
rom-v2-intrinsics = []
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"]
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"]
|
||||
|
||||
# Implement embedded-hal 1.0 alpha traits.
|
||||
# Implement embedded-hal-async traits if `nightly` is set as well.
|
||||
@ -41,7 +41,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]}
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||
atomic-polyfill = "1.0.1"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
@ -53,6 +53,7 @@ critical-section = "1.1"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-storage = { version = "0.3" }
|
||||
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
|
||||
|
||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
|
||||
|
556
embassy-rp/src/i2c.rs
Normal file
556
embassy-rp/src/i2c.rs
Normal file
@ -0,0 +1,556 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use pac::i2c;
|
||||
|
||||
use crate::dma::AnyChannel;
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::{pac, peripherals, Peripheral};
|
||||
|
||||
/// I2C error abort reason
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AbortReason {
|
||||
/// A bus operation was not acknowledged, e.g. due to the addressed device
|
||||
/// not being available on the bus or the device not being ready to process
|
||||
/// requests at the moment
|
||||
NoAcknowledge,
|
||||
/// The arbitration was lost, e.g. electrical problems with the clock signal
|
||||
ArbitrationLoss,
|
||||
Other(u32),
|
||||
}
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
Abort(AbortReason),
|
||||
/// User passed in a read buffer that was 0 length
|
||||
InvalidReadBufferLength,
|
||||
/// User passed in a write buffer that was 0 length
|
||||
InvalidWriteBufferLength,
|
||||
/// Target i2c address is out of range
|
||||
AddressOutOfRange(u16),
|
||||
/// Target i2c address is reserved
|
||||
AddressReserved(u16),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
pub frequency: u32,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self { frequency: 100_000 }
|
||||
}
|
||||
}
|
||||
|
||||
const FIFO_SIZE: u8 = 16;
|
||||
|
||||
pub struct I2c<'d, T: Instance, M: Mode> {
|
||||
_tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
_rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
_dma_buf: [u16; 256],
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T, Blocking> {
|
||||
pub fn new_blocking(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(scl, sda);
|
||||
Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
|
||||
fn new_inner(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: PeripheralRef<'d, AnyPin>,
|
||||
sda: PeripheralRef<'d, AnyPin>,
|
||||
_tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
_rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_peri);
|
||||
|
||||
assert!(config.frequency <= 1_000_000);
|
||||
assert!(config.frequency > 0);
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
unsafe {
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
// Select controller mode & speed
|
||||
p.ic_con().modify(|w| {
|
||||
// Always use "fast" mode (<= 400 kHz, works fine for standard
|
||||
// mode too)
|
||||
w.set_speed(i2c::vals::Speed::FAST);
|
||||
w.set_master_mode(true);
|
||||
w.set_ic_slave_disable(true);
|
||||
w.set_ic_restart_en(true);
|
||||
w.set_tx_empty_ctrl(true);
|
||||
});
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0.
|
||||
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
scl.io().ctrl().write(|w| w.set_funcsel(3));
|
||||
sda.io().ctrl().write(|w| w.set_funcsel(3));
|
||||
|
||||
scl.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
sda.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
|
||||
// Configure baudrate
|
||||
|
||||
// There are some subtleties to I2C timing which we are completely
|
||||
// ignoring here See:
|
||||
// https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
|
||||
let clk_base = crate::clocks::clk_peri_freq();
|
||||
|
||||
let period = (clk_base + config.frequency / 2) / config.frequency;
|
||||
let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low
|
||||
let hcnt = period - lcnt; // and 2/5 (40%) of the period high
|
||||
|
||||
// Check for out-of-range divisors:
|
||||
assert!(hcnt <= 0xffff);
|
||||
assert!(lcnt <= 0xffff);
|
||||
assert!(hcnt >= 8);
|
||||
assert!(lcnt >= 8);
|
||||
|
||||
// Per I2C-bus specification a device in standard or fast mode must
|
||||
// internally provide a hold time of at least 300ns for the SDA
|
||||
// signal to bridge the undefined region of the falling edge of SCL.
|
||||
// A smaller hold time of 120ns is used for fast mode plus.
|
||||
let sda_tx_hold_count = if config.frequency < 1_000_000 {
|
||||
// sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /
|
||||
// 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't
|
||||
// fit in uint. Add 1 to avoid division truncation.
|
||||
((clk_base * 3) / 10_000_000) + 1
|
||||
} else {
|
||||
// fast mode plus requires a clk_base > 32MHz
|
||||
assert!(clk_base >= 32_000_000);
|
||||
|
||||
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
|
||||
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
|
||||
// fit in uint. Add 1 to avoid division truncation.
|
||||
((clk_base * 3) / 25_000_000) + 1
|
||||
};
|
||||
assert!(sda_tx_hold_count <= lcnt - 2);
|
||||
|
||||
p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
|
||||
p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
|
||||
p.ic_fs_spklen()
|
||||
.write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));
|
||||
p.ic_sda_hold()
|
||||
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
|
||||
|
||||
// Enable I2C block
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
}
|
||||
|
||||
Self {
|
||||
_tx_dma,
|
||||
_rx_dma,
|
||||
_dma_buf: [0; 256],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(addr: u16) -> Result<(), Error> {
|
||||
if addr >= 0x80 {
|
||||
return Err(Error::AddressOutOfRange(addr));
|
||||
}
|
||||
|
||||
if i2c_reserved_addr(addr) {
|
||||
return Err(Error::AddressReserved(addr));
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
unsafe {
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
p.ic_tar().write(|w| w.set_ic_tar(addr));
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
|
||||
let p = T::regs();
|
||||
unsafe {
|
||||
let abort_reason = p.ic_tx_abrt_source().read();
|
||||
if abort_reason.0 != 0 {
|
||||
// Note clearing the abort flag also clears the reason, and this
|
||||
// instance of flag is clear-on-read! Note also the
|
||||
// IC_CLR_TX_ABRT register always reads as 0.
|
||||
p.ic_clr_tx_abrt().read();
|
||||
|
||||
let reason = if abort_reason.abrt_7b_addr_noack()
|
||||
| abort_reason.abrt_10addr1_noack()
|
||||
| abort_reason.abrt_10addr2_noack()
|
||||
{
|
||||
AbortReason::NoAcknowledge
|
||||
} else if abort_reason.arb_lost() {
|
||||
AbortReason::ArbitrationLoss
|
||||
} else {
|
||||
AbortReason::Other(abort_reason.0)
|
||||
};
|
||||
|
||||
Err(Error::Abort(reason))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
|
||||
if buffer.is_empty() {
|
||||
return Err(Error::InvalidReadBufferLength);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
let lastindex = buffer.len() - 1;
|
||||
for (i, byte) in buffer.iter_mut().enumerate() {
|
||||
let first = i == 0;
|
||||
let last = i == lastindex;
|
||||
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
// wait until there is space in the FIFO to write the next byte
|
||||
while p.ic_txflr().read().txflr() == FIFO_SIZE {}
|
||||
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_restart(restart && first);
|
||||
w.set_stop(send_stop && last);
|
||||
|
||||
w.set_cmd(true);
|
||||
});
|
||||
|
||||
while p.ic_rxflr().read().rxflr() == 0 {
|
||||
self.read_and_clear_abort_reason()?;
|
||||
}
|
||||
|
||||
*byte = p.ic_data_cmd().read().dat();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
|
||||
if bytes.is_empty() {
|
||||
return Err(Error::InvalidWriteBufferLength);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
let last = i == bytes.len() - 1;
|
||||
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_stop(send_stop && last);
|
||||
w.set_dat(*byte);
|
||||
});
|
||||
|
||||
// Wait until the transmission of the address/data from the
|
||||
// internal shift register has completed. For this to function
|
||||
// correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The
|
||||
// TX_EMPTY_CTRL flag was set in i2c_init.
|
||||
while !p.ic_raw_intr_stat().read().tx_empty() {}
|
||||
|
||||
let abort_reason = self.read_and_clear_abort_reason();
|
||||
|
||||
if abort_reason.is_err() || (send_stop && last) {
|
||||
// If the transaction was aborted or if it completed
|
||||
// successfully wait until the STOP condition has occured.
|
||||
|
||||
while !p.ic_raw_intr_stat().read().stop_det() {}
|
||||
|
||||
p.ic_clr_stop_det().read().clr_stop_det();
|
||||
}
|
||||
|
||||
// Note the hardware issues a STOP automatically on an abort
|
||||
// condition. Note also the hardware clears RX FIFO as well as
|
||||
// TX on abort, ecause we set hwparam
|
||||
// IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
|
||||
abort_reason?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Blocking public API
|
||||
// =========================
|
||||
|
||||
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.write_blocking_internal(bytes, true)
|
||||
}
|
||||
|
||||
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.write_blocking_internal(bytes, false)?;
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
// Automatic Stop
|
||||
}
|
||||
}
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, bytes, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
|
||||
impl embedded_hal_1::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
||||
match *self {
|
||||
Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
||||
Self::Abort(AbortReason::NoAcknowledge) => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> {
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, buffer)
|
||||
}
|
||||
|
||||
fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut peekable = bytes.into_iter().peekable();
|
||||
Self::setup(address.into())?;
|
||||
|
||||
while let Some(tx) = peekable.next() {
|
||||
self.write_blocking_internal(&[tx], peekable.peek().is_none())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_iter_read<B>(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let peekable = bytes.into_iter().peekable();
|
||||
Self::setup(address.into())?;
|
||||
|
||||
for tx in peekable {
|
||||
self.write_blocking_internal(&[tx], false)?
|
||||
}
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
}
|
||||
|
||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, wr_buffer, rd_buffer)
|
||||
}
|
||||
|
||||
fn transaction<'a>(
|
||||
&mut self,
|
||||
address: u8,
|
||||
operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
|
||||
) -> Result<(), Self::Error> {
|
||||
Self::setup(address.into())?;
|
||||
for i in 0..operations.len() {
|
||||
let last = i == operations.len() - 1;
|
||||
match &mut operations[i] {
|
||||
embedded_hal_1::i2c::blocking::Operation::Read(buf) => {
|
||||
self.read_blocking_internal(buf, false, last)?
|
||||
}
|
||||
embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
|
||||
{
|
||||
Self::setup(address.into())?;
|
||||
let mut peekable = operations.into_iter().peekable();
|
||||
while let Some(operation) = peekable.next() {
|
||||
let last = peekable.peek().is_none();
|
||||
match operation {
|
||||
embedded_hal_1::i2c::blocking::Operation::Read(buf) => {
|
||||
self.read_blocking_internal(buf, false, last)?
|
||||
}
|
||||
embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
use embassy_cortex_m::interrupt::Interrupt;
|
||||
|
||||
pub trait Instance {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
|
||||
type Interrupt: Interrupt;
|
||||
|
||||
fn regs() -> crate::pac::i2c::I2c;
|
||||
}
|
||||
|
||||
pub trait Mode {}
|
||||
|
||||
pub trait SdaPin<T: Instance> {}
|
||||
pub trait SclPin<T: Instance> {}
|
||||
}
|
||||
|
||||
pub trait Mode: sealed::Mode {}
|
||||
|
||||
macro_rules! impl_mode {
|
||||
($name:ident) => {
|
||||
impl sealed::Mode for $name {}
|
||||
impl Mode for $name {}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Blocking;
|
||||
pub struct Async;
|
||||
|
||||
impl_mode!(Blocking);
|
||||
impl_mode!(Async);
|
||||
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
macro_rules! impl_instance {
|
||||
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
|
||||
impl sealed::Instance for peripherals::$type {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
|
||||
fn regs() -> pac::i2c::I2c {
|
||||
pac::$type
|
||||
}
|
||||
}
|
||||
impl Instance for peripherals::$type {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(I2C0, I2C0_IRQ, 32, 33);
|
||||
impl_instance!(I2C1, I2C1_IRQ, 34, 35);
|
||||
|
||||
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
|
||||
pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $instance:ident, $function:ident) => {
|
||||
impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
|
||||
impl $function<peripherals::$instance> for peripherals::$pin {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIN_0, I2C0, SdaPin);
|
||||
impl_pin!(PIN_1, I2C0, SclPin);
|
||||
impl_pin!(PIN_2, I2C1, SdaPin);
|
||||
impl_pin!(PIN_3, I2C1, SclPin);
|
||||
impl_pin!(PIN_4, I2C0, SdaPin);
|
||||
impl_pin!(PIN_5, I2C0, SclPin);
|
||||
impl_pin!(PIN_6, I2C1, SdaPin);
|
||||
impl_pin!(PIN_7, I2C1, SclPin);
|
||||
impl_pin!(PIN_8, I2C0, SdaPin);
|
||||
impl_pin!(PIN_9, I2C0, SclPin);
|
||||
impl_pin!(PIN_10, I2C1, SdaPin);
|
||||
impl_pin!(PIN_11, I2C1, SclPin);
|
||||
impl_pin!(PIN_12, I2C0, SdaPin);
|
||||
impl_pin!(PIN_13, I2C0, SclPin);
|
||||
impl_pin!(PIN_14, I2C1, SdaPin);
|
||||
impl_pin!(PIN_15, I2C1, SclPin);
|
||||
impl_pin!(PIN_16, I2C0, SdaPin);
|
||||
impl_pin!(PIN_17, I2C0, SclPin);
|
||||
impl_pin!(PIN_18, I2C1, SdaPin);
|
||||
impl_pin!(PIN_19, I2C1, SclPin);
|
||||
impl_pin!(PIN_20, I2C0, SdaPin);
|
||||
impl_pin!(PIN_21, I2C0, SclPin);
|
||||
impl_pin!(PIN_22, I2C1, SdaPin);
|
||||
impl_pin!(PIN_23, I2C1, SclPin);
|
||||
impl_pin!(PIN_24, I2C0, SdaPin);
|
||||
impl_pin!(PIN_25, I2C0, SclPin);
|
||||
impl_pin!(PIN_26, I2C1, SdaPin);
|
||||
impl_pin!(PIN_27, I2C1, SclPin);
|
||||
impl_pin!(PIN_28, I2C0, SdaPin);
|
||||
impl_pin!(PIN_29, I2C0, SclPin);
|
@ -1,4 +1,6 @@
|
||||
#![macro_use]
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs
|
||||
|
||||
/// Generate a series of aliases for an intrinsic function.
|
||||
macro_rules! intrinsics_aliases {
|
||||
@ -14,7 +16,7 @@ macro_rules! intrinsics_aliases {
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
@ -32,7 +34,7 @@ macro_rules! intrinsics_aliases {
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
@ -52,7 +54,7 @@ macro_rules! intrinsics_aliases {
|
||||
/// is to abstract anything special that needs to be done to override an
|
||||
/// intrinsic function. Intrinsic generation is disabled for non-ARM targets
|
||||
/// so things like CI and docs generation do not have problems. Additionally
|
||||
/// they can be disabled with the crate feature `disable-intrinsics` for
|
||||
/// they can be disabled by disabling the crate feature `intrinsics` for
|
||||
/// testing or comparing performance.
|
||||
///
|
||||
/// Like the compiler-builtins macro, it accepts a series of functions that
|
||||
@ -211,13 +213,13 @@ macro_rules! intrinsics {
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
@ -228,7 +230,7 @@ macro_rules! intrinsics {
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
@ -245,13 +247,13 @@ macro_rules! intrinsics {
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
@ -262,7 +264,7 @@ macro_rules! intrinsics {
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
|
@ -8,6 +8,7 @@ mod intrinsics;
|
||||
|
||||
pub mod dma;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
pub mod rom_data;
|
||||
pub mod rtc;
|
||||
@ -76,6 +77,9 @@ embassy_hal_common::peripherals! {
|
||||
SPI0,
|
||||
SPI1,
|
||||
|
||||
I2C0,
|
||||
I2C1,
|
||||
|
||||
DMA_CH0,
|
||||
DMA_CH1,
|
||||
DMA_CH2,
|
||||
|
@ -7,6 +7,8 @@
|
||||
//! > on the device, as well as highly optimized versions of certain key
|
||||
//! > functionality that would otherwise have to take up space in most user
|
||||
//! > binaries.
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs
|
||||
|
||||
/// A bootrom function table code.
|
||||
pub type RomFnTableCode = [u8; 2];
|
||||
|
489
embassy-rp/src/uart/buffered.rs
Normal file
489
embassy-rp/src/uart/buffered.rs
Normal file
@ -0,0 +1,489 @@
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Poll, Waker};
|
||||
|
||||
use atomic_polyfill::{compiler_fence, Ordering};
|
||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance>(StateStorage<FullStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> State<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxState<'d, T: Instance>(StateStorage<RxStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> RxState<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxState<'d, T: Instance>(StateStorage<TxStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> TxState<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
struct RxStateInner<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
|
||||
waker: WakerRegistration,
|
||||
buf: RingBuffer<'d>,
|
||||
}
|
||||
|
||||
struct TxStateInner<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
|
||||
waker: WakerRegistration,
|
||||
buf: RingBuffer<'d>,
|
||||
}
|
||||
|
||||
struct FullStateInner<'d, T: Instance> {
|
||||
rx: RxStateInner<'d, T>,
|
||||
tx: TxStateInner<'d, T>,
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {}
|
||||
|
||||
pub struct BufferedUart<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, FullStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartRx<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, RxStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartTx<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, TxStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
|
||||
impl<'d, T: Instance> Unpin for BufferedUartRx<'d, T> {}
|
||||
impl<'d, T: Instance> Unpin for BufferedUartTx<'d, T> {}
|
||||
|
||||
impl<'d, T: Instance> BufferedUart<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut State<'d, T>,
|
||||
_uart: Uart<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
w.set_txim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner {
|
||||
tx: TxStateInner {
|
||||
phantom: PhantomData,
|
||||
waker: WakerRegistration::new(),
|
||||
buf: RingBuffer::new(tx_buffer),
|
||||
},
|
||||
rx: RxStateInner {
|
||||
phantom: PhantomData,
|
||||
waker: WakerRegistration::new(),
|
||||
buf: RingBuffer::new(rx_buffer),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut RxState<'d, T>,
|
||||
_uart: UartRx<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartRx<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner {
|
||||
phantom: PhantomData,
|
||||
|
||||
buf: RingBuffer::new(rx_buffer),
|
||||
waker: WakerRegistration::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut TxState<'d, T>,
|
||||
_uart: UartTx<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartTx<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner {
|
||||
phantom: PhantomData,
|
||||
|
||||
buf: RingBuffer::new(tx_buffer),
|
||||
waker: WakerRegistration::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
self.rx.on_interrupt();
|
||||
self.tx.on_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
|
||||
// We have data ready in buffer? Return it.
|
||||
let mut do_pend = false;
|
||||
let data = self.buf.pop_buf();
|
||||
if !data.is_empty() {
|
||||
let len = data.len().min(buf.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
|
||||
if self.buf.is_full() {
|
||||
do_pend = true;
|
||||
}
|
||||
self.buf.pop(len);
|
||||
|
||||
return (Poll::Ready(Ok(len)), do_pend);
|
||||
}
|
||||
|
||||
self.waker.register(waker);
|
||||
(Poll::Pending, do_pend)
|
||||
}
|
||||
|
||||
fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll<Result<&'a [u8], Error>> {
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = self.buf.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
self.waker.register(waker);
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) -> bool {
|
||||
let full = self.buf.is_full();
|
||||
self.buf.pop(amt);
|
||||
full
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let ris = r.uartris().read();
|
||||
// Clear interrupt flags
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_rxic(true);
|
||||
w.set_rtic(true);
|
||||
});
|
||||
|
||||
if ris.peris() {
|
||||
warn!("Parity error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_peic(true);
|
||||
});
|
||||
}
|
||||
if ris.feris() {
|
||||
warn!("Framing error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_feic(true);
|
||||
});
|
||||
}
|
||||
if ris.beris() {
|
||||
warn!("Break error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_beic(true);
|
||||
});
|
||||
}
|
||||
if ris.oeris() {
|
||||
warn!("Overrun error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_oeic(true);
|
||||
});
|
||||
}
|
||||
|
||||
if !r.uartfr().read().rxfe() {
|
||||
let buf = self.buf.push_buf();
|
||||
if !buf.is_empty() {
|
||||
buf[0] = r.uartdr().read().data();
|
||||
self.buf.push(1);
|
||||
} else {
|
||||
warn!("RX buffer full, discard received byte");
|
||||
}
|
||||
|
||||
if self.buf.is_full() {
|
||||
self.waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
if ris.rtris() {
|
||||
self.waker.wake();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> TxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
|
||||
let empty = self.buf.is_empty();
|
||||
let tx_buf = self.buf.push_buf();
|
||||
if tx_buf.is_empty() {
|
||||
self.waker.register(waker);
|
||||
return (Poll::Pending, empty);
|
||||
}
|
||||
|
||||
let n = core::cmp::min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
self.buf.push(n);
|
||||
|
||||
(Poll::Ready(Ok(n)), empty)
|
||||
}
|
||||
|
||||
fn flush(&mut self, waker: &Waker) -> Poll<Result<(), Error>> {
|
||||
if !self.buf.is_empty() {
|
||||
self.waker.register(waker);
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let buf = self.buf.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(true);
|
||||
});
|
||||
r.uartdr().write(|w| w.set_data(buf[0].into()));
|
||||
self.buf.pop(1);
|
||||
self.waker.wake();
|
||||
} else {
|
||||
// Disable interrupt until we have something to transmit again
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (res, do_pend) = self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.rx.read(buf, cx.waker())
|
||||
});
|
||||
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (res, do_pend) = self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.read(buf, cx.waker())
|
||||
});
|
||||
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.rx.fill_buf(cx.waker())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| state.rx.consume(amt));
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.fill_buf(cx.waker())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| state.consume(amt));
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker()));
|
||||
if empty {
|
||||
self.inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker()));
|
||||
if empty {
|
||||
self.inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker())))
|
||||
}
|
||||
}
|
@ -346,6 +346,11 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> {
|
||||
w.set_fen(true);
|
||||
});
|
||||
|
||||
r.uartifls().write(|w| {
|
||||
w.set_rxiflsel(0b000);
|
||||
w.set_txiflsel(0b000);
|
||||
});
|
||||
|
||||
r.uartcr().write(|w| {
|
||||
w.set_uarten(true);
|
||||
w.set_rxe(true);
|
||||
@ -423,9 +428,11 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
@ -433,6 +440,7 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
@ -440,9 +448,11 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
@ -475,6 +485,75 @@ mod eh1 {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let dr = r.uartdr().read();
|
||||
|
||||
if dr.oe() {
|
||||
Err(nb::Error::Other(Error::Overrun))
|
||||
} else if dr.be() {
|
||||
Err(nb::Error::Other(Error::Break))
|
||||
} else if dr.pe() {
|
||||
Err(nb::Error::Other(Error::Parity))
|
||||
} else if dr.fe() {
|
||||
Err(nb::Error::Other(Error::Framing))
|
||||
} else if dr.fe() {
|
||||
Ok(dr.data())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_flush().map_err(nb::Error::Other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> {
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_flush().map_err(nb::Error::Other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
@ -532,6 +611,11 @@ mod eha {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod buffered;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use buffered::*;
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
@ -541,6 +625,8 @@ mod sealed {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
|
||||
type Interrupt: crate::interrupt::Interrupt;
|
||||
|
||||
fn regs() -> pac::uart::Uart;
|
||||
}
|
||||
pub trait TxPin<T: Instance> {}
|
||||
@ -572,6 +658,8 @@ macro_rules! impl_instance {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
|
||||
fn regs() -> pac::uart::Uart {
|
||||
pac::$inst
|
||||
}
|
||||
@ -580,8 +668,8 @@ macro_rules! impl_instance {
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(UART0, UART0, 20, 21);
|
||||
impl_instance!(UART1, UART1, 22, 23);
|
||||
impl_instance!(UART0, UART0_IRQ, 20, 21);
|
||||
impl_instance!(UART1, UART1_IRQ, 22, 23);
|
||||
|
||||
pub trait TxPin<T: Instance>: sealed::TxPin<T> + crate::gpio::Pin {}
|
||||
pub trait RxPin<T: Instance>: sealed::RxPin<T> + crate::gpio::Pin {}
|
@ -7,8 +7,10 @@ use core::task::Poll;
|
||||
use atomic_polyfill::compiler_fence;
|
||||
use embassy_hal_common::into_ref;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported};
|
||||
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||
use embassy_usb_driver as driver;
|
||||
use embassy_usb_driver::{
|
||||
Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::{pac, peripherals, Peripheral, RegExt};
|
||||
@ -204,8 +206,8 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
);
|
||||
|
||||
let alloc = match D::dir() {
|
||||
UsbDirection::Out => &mut self.ep_out,
|
||||
UsbDirection::In => &mut self.ep_in,
|
||||
Direction::Out => &mut self.ep_out,
|
||||
Direction::In => &mut self.ep_in,
|
||||
};
|
||||
|
||||
let index = alloc.iter_mut().enumerate().find(|(i, ep)| {
|
||||
@ -254,7 +256,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
};
|
||||
|
||||
match D::dir() {
|
||||
UsbDirection::Out => unsafe {
|
||||
Direction::Out => unsafe {
|
||||
T::dpram().ep_out_control(index - 1).write(|w| {
|
||||
w.set_enable(false);
|
||||
w.set_buffer_address(addr);
|
||||
@ -262,7 +264,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
w.set_endpoint_type(ep_type_reg);
|
||||
})
|
||||
},
|
||||
UsbDirection::In => unsafe {
|
||||
Direction::In => unsafe {
|
||||
T::dpram().ep_in_control(index - 1).write(|w| {
|
||||
w.set_enable(false);
|
||||
w.set_buffer_address(addr);
|
||||
@ -429,14 +431,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
|
||||
let n = ep_addr.index();
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => unsafe {
|
||||
Direction::In => unsafe {
|
||||
T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||
T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| {
|
||||
w.set_pid(0, true); // first packet is DATA0, but PID is flipped before
|
||||
});
|
||||
EP_IN_WAKERS[n].wake();
|
||||
},
|
||||
UsbDirection::Out => unsafe {
|
||||
Direction::Out => unsafe {
|
||||
T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||
|
||||
T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {
|
||||
@ -474,14 +476,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
}
|
||||
|
||||
trait Dir {
|
||||
fn dir() -> UsbDirection;
|
||||
fn dir() -> Direction;
|
||||
fn waker(i: usize) -> &'static AtomicWaker;
|
||||
}
|
||||
|
||||
pub enum In {}
|
||||
impl Dir for In {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::In
|
||||
fn dir() -> Direction {
|
||||
Direction::In
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -492,8 +494,8 @@ impl Dir for In {
|
||||
|
||||
pub enum Out {}
|
||||
impl Dir for Out {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::Out
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -39,7 +39,7 @@ embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features =
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||
@ -73,7 +73,7 @@ quote = "1.0.15"
|
||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
|
||||
sdmmc-rs = ["embedded-sdmmc"]
|
||||
net = ["embassy-net" ]
|
||||
memory-x = ["stm32-metapac/memory-x"]
|
||||
@ -92,7 +92,7 @@ time-driver-tim12 = ["_time-driver"]
|
||||
time-driver-tim15 = ["_time-driver"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"]
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"]
|
||||
|
||||
# Reexport stm32-metapac at `embassy_stm32::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
|
||||
|
@ -12,6 +12,7 @@ pub struct Can<'d, T: Instance> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
@ -31,6 +32,28 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
can: bxcan::Can::builder(BxcanInstance(peri)).enable(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Bxcan instance, keeping the peripheral in sleep mode.
|
||||
/// You must call [Can::enable_non_blocking] to use the peripheral.
|
||||
pub fn new_disabled(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self {
|
||||
can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::cell::RefCell;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::Poll;
|
||||
|
||||
@ -29,7 +30,15 @@ unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {}
|
||||
unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {}
|
||||
|
||||
pub struct BufferedUart<'d, T: BasicInstance> {
|
||||
inner: PeripheralMutex<'d, StateInner<'d, T>>,
|
||||
inner: RefCell<PeripheralMutex<'d, StateInner<'d, T>>>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartTx<'u, 'd, T: BasicInstance> {
|
||||
inner: &'u BufferedUart<'d, T>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartRx<'u, 'd, T: BasicInstance> {
|
||||
inner: &'u BufferedUart<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {}
|
||||
@ -53,14 +62,124 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner {
|
||||
inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
|
||||
phantom: PhantomData,
|
||||
tx: RingBuffer::new(tx_buffer),
|
||||
tx_waker: WakerRegistration::new(),
|
||||
|
||||
rx: RingBuffer::new(rx_buffer),
|
||||
rx_waker: WakerRegistration::new(),
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) {
|
||||
(BufferedUartRx { inner: self }, BufferedUartTx { inner: self })
|
||||
}
|
||||
|
||||
async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, Error> {
|
||||
poll_fn(move |cx| {
|
||||
let mut do_pend = false;
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let res = inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let data = state.rx.pop_buf();
|
||||
if !data.is_empty() {
|
||||
let len = data.len().min(buf.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
|
||||
if state.rx.is_full() {
|
||||
do_pend = true;
|
||||
}
|
||||
state.rx.pop(len);
|
||||
|
||||
return Poll::Ready(Ok(len));
|
||||
}
|
||||
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
});
|
||||
|
||||
if do_pend {
|
||||
inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> {
|
||||
poll_fn(move |cx| {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let (poll, empty) = inner.with(|state| {
|
||||
let empty = state.tx.is_empty();
|
||||
let tx_buf = state.tx.push_buf();
|
||||
if tx_buf.is_empty() {
|
||||
state.tx_waker.register(cx.waker());
|
||||
return (Poll::Pending, empty);
|
||||
}
|
||||
|
||||
let n = core::cmp::min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
state.tx.push(n);
|
||||
|
||||
(Poll::Ready(Ok(n)), empty)
|
||||
});
|
||||
if empty {
|
||||
inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_flush<'a>(&'a self) -> Result<(), Error> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.borrow_mut().with(|state| {
|
||||
if !state.tx.is_empty() {
|
||||
state.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.borrow_mut().with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = state.rx.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::<Result<&[u8], Error>>::Pending
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn inner_consume(&self, amt: usize) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let signal = inner.with(|state| {
|
||||
let full = state.rx.is_full();
|
||||
state.rx.pop(amt);
|
||||
full
|
||||
});
|
||||
if signal {
|
||||
inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,41 +274,31 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let mut do_pend = false;
|
||||
let res = self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let data = state.rx.pop_buf();
|
||||
if !data.is_empty() {
|
||||
let len = data.len().min(buf.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
|
||||
if state.rx.is_full() {
|
||||
do_pend = true;
|
||||
self.inner_read(buf)
|
||||
}
|
||||
state.rx.pop(len);
|
||||
|
||||
return Poll::Ready(Ok(len));
|
||||
}
|
||||
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
});
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.inner.inner_read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,35 +308,26 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T>
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = state.rx.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::<Result<&[u8], Self::Error>>::Pending
|
||||
})
|
||||
})
|
||||
self.inner_fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| {
|
||||
let full = state.rx.is_full();
|
||||
state.rx.pop(amt);
|
||||
full
|
||||
});
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
self.inner_consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
self.inner.inner_fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.inner_consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
@ -236,26 +336,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (poll, empty) = self.inner.with(|state| {
|
||||
let empty = state.tx.is_empty();
|
||||
let tx_buf = state.tx.push_buf();
|
||||
if tx_buf.is_empty() {
|
||||
state.tx_waker.register(cx.waker());
|
||||
return (Poll::Pending, empty);
|
||||
}
|
||||
|
||||
let n = core::cmp::min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
state.tx.push(n);
|
||||
|
||||
(Poll::Ready(Ok(n)), empty)
|
||||
});
|
||||
if empty {
|
||||
self.inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
self.inner_write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
@ -263,15 +344,24 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
if !state.tx.is_empty() {
|
||||
state.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
self.inner_flush()
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
})
|
||||
})
|
||||
impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.inner.inner_write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.inner.inner_flush()
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ use atomic_polyfill::{AtomicBool, AtomicU8};
|
||||
use embassy_hal_common::into_ref;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_time::{block_for, Duration};
|
||||
use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported};
|
||||
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||
use embassy_usb_driver as driver;
|
||||
use embassy_usb_driver::{
|
||||
Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
use pac::common::{Reg, RW};
|
||||
use pac::usb::vals::{EpType, Stat};
|
||||
|
||||
@ -279,8 +281,8 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
}
|
||||
let used = ep.used_out || ep.used_in;
|
||||
let used_dir = match D::dir() {
|
||||
UsbDirection::Out => ep.used_out,
|
||||
UsbDirection::In => ep.used_in,
|
||||
Direction::Out => ep.used_out,
|
||||
Direction::In => ep.used_in,
|
||||
};
|
||||
!used || (ep.ep_type == ep_type && !used_dir)
|
||||
});
|
||||
@ -293,7 +295,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
ep.ep_type = ep_type;
|
||||
|
||||
let buf = match D::dir() {
|
||||
UsbDirection::Out => {
|
||||
Direction::Out => {
|
||||
assert!(!ep.used_out);
|
||||
ep.used_out = true;
|
||||
|
||||
@ -312,7 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
assert!(!ep.used_in);
|
||||
ep.used_in = true;
|
||||
|
||||
@ -504,7 +506,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
// This can race, so do a retry loop.
|
||||
let reg = T::regs().epr(ep_addr.index() as _);
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
loop {
|
||||
let r = unsafe { reg.read() };
|
||||
match r.stat_tx() {
|
||||
@ -523,7 +525,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
}
|
||||
EP_IN_WAKERS[ep_addr.index()].wake();
|
||||
}
|
||||
UsbDirection::Out => {
|
||||
Direction::Out => {
|
||||
loop {
|
||||
let r = unsafe { reg.read() };
|
||||
match r.stat_rx() {
|
||||
@ -549,8 +551,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
let regs = T::regs();
|
||||
let epr = unsafe { regs.epr(ep_addr.index() as _).read() };
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => epr.stat_tx() == Stat::STALL,
|
||||
UsbDirection::Out => epr.stat_rx() == Stat::STALL,
|
||||
Direction::In => epr.stat_tx() == Stat::STALL,
|
||||
Direction::Out => epr.stat_rx() == Stat::STALL,
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,7 +562,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
let reg = T::regs().epr(ep_addr.index() as _);
|
||||
trace!("EPR before: {:04x}", unsafe { reg.read() }.0);
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
loop {
|
||||
let want_stat = match enabled {
|
||||
false => Stat::DISABLED,
|
||||
@ -576,7 +578,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
}
|
||||
EP_IN_WAKERS[ep_addr.index()].wake();
|
||||
}
|
||||
UsbDirection::Out => {
|
||||
Direction::Out => {
|
||||
loop {
|
||||
let want_stat = match enabled {
|
||||
false => Stat::DISABLED,
|
||||
@ -616,14 +618,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
}
|
||||
|
||||
trait Dir {
|
||||
fn dir() -> UsbDirection;
|
||||
fn dir() -> Direction;
|
||||
fn waker(i: usize) -> &'static AtomicWaker;
|
||||
}
|
||||
|
||||
pub enum In {}
|
||||
impl Dir for In {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::In
|
||||
fn dir() -> Direction {
|
||||
Direction::In
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -634,8 +636,8 @@ impl Dir for In {
|
||||
|
||||
pub enum Out {}
|
||||
impl Dir for Out {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::Out
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1,9 +1,11 @@
|
||||
//! A synchronization primitive for passing the latest value to a task.
|
||||
use core::cell::UnsafeCell;
|
||||
use core::cell::Cell;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::mem;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use crate::blocking_mutex::raw::RawMutex;
|
||||
use crate::blocking_mutex::Mutex;
|
||||
|
||||
/// Single-slot signaling primitive.
|
||||
///
|
||||
/// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except
|
||||
@ -20,16 +22,20 @@ use core::task::{Context, Poll, Waker};
|
||||
///
|
||||
/// ```
|
||||
/// use embassy_sync::signal::Signal;
|
||||
/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
///
|
||||
/// enum SomeCommand {
|
||||
/// On,
|
||||
/// Off,
|
||||
/// }
|
||||
///
|
||||
/// static SOME_SIGNAL: Signal<SomeCommand> = Signal::new();
|
||||
/// static SOME_SIGNAL: Signal<CriticalSectionRawMutex, SomeCommand> = Signal::new();
|
||||
/// ```
|
||||
pub struct Signal<T> {
|
||||
state: UnsafeCell<State<T>>,
|
||||
pub struct Signal<M, T>
|
||||
where
|
||||
M: RawMutex,
|
||||
{
|
||||
state: Mutex<M, Cell<State<T>>>,
|
||||
}
|
||||
|
||||
enum State<T> {
|
||||
@ -38,24 +44,27 @@ enum State<T> {
|
||||
Signaled(T),
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Signal<T> {}
|
||||
unsafe impl<T: Send> Sync for Signal<T> {}
|
||||
|
||||
impl<T> Signal<T> {
|
||||
impl<M, T> Signal<M, T>
|
||||
where
|
||||
M: RawMutex,
|
||||
{
|
||||
/// Create a new `Signal`.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: UnsafeCell::new(State::None),
|
||||
state: Mutex::new(Cell::new(State::None)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send> Signal<T> {
|
||||
impl<M, T: Send> Signal<M, T>
|
||||
where
|
||||
M: RawMutex,
|
||||
{
|
||||
/// Mark this Signal as signaled.
|
||||
pub fn signal(&self, val: T) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) {
|
||||
self.state.lock(|cell| {
|
||||
let state = cell.replace(State::Signaled(val));
|
||||
if let State::Waiting(waker) = state {
|
||||
waker.wake();
|
||||
}
|
||||
})
|
||||
@ -63,31 +72,27 @@ impl<T: Send> Signal<T> {
|
||||
|
||||
/// Remove the queued value in this `Signal`, if any.
|
||||
pub fn reset(&self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
*state = State::None
|
||||
})
|
||||
self.state.lock(|cell| cell.set(State::None));
|
||||
}
|
||||
|
||||
/// Manually poll the Signal future.
|
||||
pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||
critical_section::with(|_| unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||
self.state.lock(|cell| {
|
||||
let state = cell.replace(State::None);
|
||||
match state {
|
||||
State::None => {
|
||||
*state = State::Waiting(cx.waker().clone());
|
||||
cell.set(State::Waiting(cx.waker().clone()));
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => {
|
||||
cell.set(State::Waiting(w));
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
|
||||
State::Waiting(w) => {
|
||||
let w = mem::replace(w, cx.waker().clone());
|
||||
cell.set(State::Waiting(cx.waker().clone()));
|
||||
w.wake();
|
||||
Poll::Pending
|
||||
}
|
||||
State::Signaled(_) => match mem::replace(state, State::None) {
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -99,6 +104,14 @@ impl<T: Send> Signal<T> {
|
||||
|
||||
/// non-blocking method to check whether this signal has been signaled.
|
||||
pub fn signaled(&self) -> bool {
|
||||
critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_)))
|
||||
self.state.lock(|cell| {
|
||||
let state = cell.replace(State::None);
|
||||
|
||||
let res = matches!(state, State::Signaled(_));
|
||||
|
||||
cell.set(state);
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
[package]
|
||||
name = "embassy-usb-ncm"
|
||||
name = "embassy-usb-driver"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-ncm-v$VERSION/embassy-usb-ncm/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-ncm/src/"
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/"
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
@ -1,6 +1,104 @@
|
||||
#![no_std]
|
||||
|
||||
use core::future::Future;
|
||||
|
||||
use super::types::*;
|
||||
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
|
||||
/// the perspective of the host, which is backward for devices, but the standard directions are used
|
||||
/// for consistency.
|
||||
///
|
||||
/// The values of the enum also match the direction bit used in endpoint addresses and control
|
||||
/// request types.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Direction {
|
||||
/// Host to device (OUT)
|
||||
Out,
|
||||
/// Device to host (IN)
|
||||
In,
|
||||
}
|
||||
|
||||
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
|
||||
/// transfer bmAttributes transfer type bits.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum EndpointType {
|
||||
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
|
||||
/// used only endpoint 0.
|
||||
Control = 0b00,
|
||||
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
|
||||
Isochronous = 0b01,
|
||||
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
|
||||
Bulk = 0b10,
|
||||
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
|
||||
Interrupt = 0b11,
|
||||
}
|
||||
|
||||
/// Type-safe endpoint address.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAddress(u8);
|
||||
|
||||
impl From<u8> for EndpointAddress {
|
||||
#[inline]
|
||||
fn from(addr: u8) -> EndpointAddress {
|
||||
EndpointAddress(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EndpointAddress> for u8 {
|
||||
#[inline]
|
||||
fn from(addr: EndpointAddress) -> u8 {
|
||||
addr.0
|
||||
}
|
||||
}
|
||||
|
||||
impl EndpointAddress {
|
||||
const INBITS: u8 = Direction::In as u8;
|
||||
|
||||
/// Constructs a new EndpointAddress with the given index and direction.
|
||||
#[inline]
|
||||
pub fn from_parts(index: usize, dir: Direction) -> Self {
|
||||
EndpointAddress(index as u8 | dir as u8)
|
||||
}
|
||||
|
||||
/// Gets the direction part of the address.
|
||||
#[inline]
|
||||
pub fn direction(&self) -> Direction {
|
||||
if (self.0 & Self::INBITS) != 0 {
|
||||
Direction::In
|
||||
} else {
|
||||
Direction::Out
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the direction is IN, otherwise false.
|
||||
#[inline]
|
||||
pub fn is_in(&self) -> bool {
|
||||
(self.0 & Self::INBITS) != 0
|
||||
}
|
||||
|
||||
/// Returns true if the direction is OUT, otherwise false.
|
||||
#[inline]
|
||||
pub fn is_out(&self) -> bool {
|
||||
(self.0 & Self::INBITS) == 0
|
||||
}
|
||||
|
||||
/// Gets the index part of the endpoint address.
|
||||
#[inline]
|
||||
pub fn index(&self) -> usize {
|
||||
(self.0 & !Self::INBITS) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointInfo {
|
||||
pub addr: EndpointAddress,
|
||||
pub ep_type: EndpointType,
|
||||
pub max_packet_size: u16,
|
||||
pub interval: u8,
|
||||
}
|
||||
|
||||
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
|
||||
/// platform.
|
@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "embassy-usb-hid"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-hid-v$VERSION/embassy-usb-hid/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-hid/src/"
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[features]
|
||||
default = ["usbd-hid"]
|
||||
usbd-hid = ["dep:usbd-hid", "ssmarshal"]
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
usbd-hid = { version = "0.6.0", optional = true }
|
||||
ssmarshal = { version = "1.0", default-features = false, optional = true }
|
||||
futures-util = { version = "0.3.21", default-features = false }
|
@ -1,225 +0,0 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
[package]
|
||||
name = "embassy-usb-serial"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-serial/src/"
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
@ -1,225 +0,0 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
@ -9,9 +9,20 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/s
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
|
||||
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
|
||||
default = ["usbd-hid"]
|
||||
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
heapless = "0.7.10"
|
||||
|
||||
# for HID
|
||||
usbd-hid = { version = "0.6.0", optional = true }
|
||||
ssmarshal = { version = "1.0", default-features = false, optional = true }
|
||||
|
@ -1,11 +1,10 @@
|
||||
use heapless::Vec;
|
||||
|
||||
use super::control::ControlHandler;
|
||||
use super::descriptor::{BosWriter, DescriptorWriter};
|
||||
use super::driver::{Driver, Endpoint};
|
||||
use super::types::*;
|
||||
use super::{DeviceStateHandler, UsbDevice, MAX_INTERFACE_COUNT};
|
||||
use crate::{Interface, STRING_INDEX_CUSTOM_START};
|
||||
use crate::control::ControlHandler;
|
||||
use crate::descriptor::{BosWriter, DescriptorWriter};
|
||||
use crate::driver::{Driver, Endpoint, EndpointType};
|
||||
use crate::types::*;
|
||||
use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -1,18 +1,13 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
use core::cell::Cell;
|
||||
use core::mem::{self, MaybeUninit};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
||||
use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
||||
use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use embassy_usb::types::*;
|
||||
use embassy_usb::Builder;
|
||||
|
||||
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use crate::types::*;
|
||||
use crate::Builder;
|
||||
|
||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
||||
pub const USB_CLASS_CDC: u8 = 0x02;
|
||||
@ -268,7 +263,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
|
||||
}
|
||||
|
||||
/// Number of stop bits for LineCoding
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum StopBits {
|
||||
/// 1 stop bit
|
||||
@ -292,7 +287,7 @@ impl From<u8> for StopBits {
|
||||
}
|
||||
|
||||
/// Parity for LineCoding
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ParityType {
|
||||
None = 0,
|
||||
@ -316,7 +311,7 @@ impl From<u8> for ParityType {
|
||||
///
|
||||
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
|
||||
/// be ignored if you don't plan to interface with a physical UART.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct LineCoding {
|
||||
stop_bits: StopBits,
|
@ -1,15 +1,10 @@
|
||||
#![no_std]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
use core::intrinsics::copy_nonoverlapping;
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
|
||||
use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
||||
use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use embassy_usb::types::*;
|
||||
use embassy_usb::Builder;
|
||||
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
|
||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use crate::types::*;
|
||||
use crate::Builder;
|
||||
|
||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
||||
pub const USB_CLASS_CDC: u8 = 0x02;
|
@ -1,23 +1,16 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
//! Implements HID functionality for a usb-device device.
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::Range;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use embassy_usb::control::{ControlHandler, InResponse, OutResponse, Request, RequestType};
|
||||
use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use embassy_usb::Builder;
|
||||
#[cfg(feature = "usbd-hid")]
|
||||
use ssmarshal::serialize;
|
||||
#[cfg(feature = "usbd-hid")]
|
||||
use usbd_hid::descriptor::AsInputReport;
|
||||
|
||||
use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType};
|
||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
||||
use crate::Builder;
|
||||
|
||||
const USB_CLASS_HID: u8 = 0x03;
|
||||
const USB_SUBCLASS_NONE: u8 = 0x00;
|
||||
const USB_PROTOCOL_NONE: u8 = 0x00;
|
||||
@ -204,9 +197,9 @@ pub enum ReadError {
|
||||
Sync(Range<usize>),
|
||||
}
|
||||
|
||||
impl From<embassy_usb::driver::EndpointError> for ReadError {
|
||||
fn from(val: embassy_usb::driver::EndpointError) -> Self {
|
||||
use embassy_usb::driver::EndpointError::*;
|
||||
impl From<EndpointError> for ReadError {
|
||||
fn from(val: EndpointError) -> Self {
|
||||
use EndpointError::*;
|
||||
match val {
|
||||
BufferOverflow => ReadError::BufferOverflow,
|
||||
Disabled => ReadError::Disabled,
|
||||
@ -437,7 +430,7 @@ impl<'d> ControlHandler for Control<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse {
|
||||
fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {
|
||||
trace!("HID control_out {:?} {=[u8]:x}", req, data);
|
||||
if let RequestType::Class = req.request_type {
|
||||
match req.request {
|
3
embassy-usb/src/class/mod.rs
Normal file
3
embassy-usb/src/class/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod cdc_acm;
|
||||
pub mod cdc_ncm;
|
||||
pub mod hid;
|
@ -1,7 +1,8 @@
|
||||
//! USB control data types.
|
||||
use core::mem;
|
||||
|
||||
use super::types::*;
|
||||
use crate::driver::Direction;
|
||||
use crate::types::StringIndex;
|
||||
|
||||
/// Control request type.
|
||||
#[repr(u8)]
|
||||
@ -42,7 +43,7 @@ pub enum Recipient {
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Request {
|
||||
/// Direction of the request.
|
||||
pub direction: UsbDirection,
|
||||
pub direction: Direction,
|
||||
/// Type of the request.
|
||||
pub request_type: RequestType,
|
||||
/// Recipient of the request.
|
||||
@ -105,7 +106,7 @@ impl Request {
|
||||
let recipient = rt & 0b11111;
|
||||
|
||||
Request {
|
||||
direction: rt.into(),
|
||||
direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In },
|
||||
request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },
|
||||
recipient: if recipient <= 3 {
|
||||
unsafe { mem::transmute(recipient) }
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::builder::Config;
|
||||
use super::types::*;
|
||||
use super::CONFIGURATION_VALUE;
|
||||
use crate::builder::Config;
|
||||
use crate::driver::EndpointInfo;
|
||||
use crate::types::*;
|
||||
use crate::CONFIGURATION_VALUE;
|
||||
|
||||
/// Standard descriptor types
|
||||
#[allow(missing_docs)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::descriptor::descriptor_type;
|
||||
use crate::types::EndpointAddress;
|
||||
use crate::driver::EndpointAddress;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -4,23 +4,24 @@
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub use embassy_usb_driver as driver;
|
||||
|
||||
mod builder;
|
||||
pub mod class;
|
||||
pub mod control;
|
||||
pub mod descriptor;
|
||||
mod descriptor_reader;
|
||||
pub mod driver;
|
||||
pub mod types;
|
||||
|
||||
use embassy_futures::select::{select, Either};
|
||||
use heapless::Vec;
|
||||
|
||||
pub use self::builder::{Builder, Config};
|
||||
use self::control::*;
|
||||
use self::descriptor::*;
|
||||
use self::driver::{Bus, Driver, Event};
|
||||
use self::types::*;
|
||||
pub use crate::builder::{Builder, Config};
|
||||
use crate::control::*;
|
||||
use crate::descriptor::*;
|
||||
use crate::descriptor_reader::foreach_endpoint;
|
||||
use crate::driver::ControlPipe;
|
||||
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
|
||||
use crate::types::*;
|
||||
|
||||
/// The global state of the USB device.
|
||||
///
|
||||
@ -247,11 +248,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
async fn handle_control(&mut self, req: [u8; 8]) {
|
||||
let req = Request::parse(&req);
|
||||
|
||||
trace!("control request: {:02x}", req);
|
||||
trace!("control request: {:?}", req);
|
||||
|
||||
match req.direction {
|
||||
UsbDirection::In => self.handle_control_in(req).await,
|
||||
UsbDirection::Out => self.handle_control_out(req).await,
|
||||
Direction::In => self.handle_control_in(req).await,
|
||||
Direction::Out => self.handle_control_out(req).await,
|
||||
}
|
||||
|
||||
if self.inner.set_address_pending {
|
||||
|
@ -1,108 +1,3 @@
|
||||
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
|
||||
/// the perspective of the host, which is backward for devices, but the standard directions are used
|
||||
/// for consistency.
|
||||
///
|
||||
/// The values of the enum also match the direction bit used in endpoint addresses and control
|
||||
/// request types.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum UsbDirection {
|
||||
/// Host to device (OUT)
|
||||
Out = 0x00,
|
||||
/// Device to host (IN)
|
||||
In = 0x80,
|
||||
}
|
||||
|
||||
impl From<u8> for UsbDirection {
|
||||
fn from(value: u8) -> Self {
|
||||
unsafe { core::mem::transmute(value & 0x80) }
|
||||
}
|
||||
}
|
||||
|
||||
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
|
||||
/// transfer bmAttributes transfer type bits.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum EndpointType {
|
||||
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
|
||||
/// used only endpoint 0.
|
||||
Control = 0b00,
|
||||
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
|
||||
Isochronous = 0b01,
|
||||
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
|
||||
Bulk = 0b10,
|
||||
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
|
||||
Interrupt = 0b11,
|
||||
}
|
||||
|
||||
/// Type-safe endpoint address.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAddress(u8);
|
||||
|
||||
impl From<u8> for EndpointAddress {
|
||||
#[inline]
|
||||
fn from(addr: u8) -> EndpointAddress {
|
||||
EndpointAddress(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EndpointAddress> for u8 {
|
||||
#[inline]
|
||||
fn from(addr: EndpointAddress) -> u8 {
|
||||
addr.0
|
||||
}
|
||||
}
|
||||
|
||||
impl EndpointAddress {
|
||||
const INBITS: u8 = UsbDirection::In as u8;
|
||||
|
||||
/// Constructs a new EndpointAddress with the given index and direction.
|
||||
#[inline]
|
||||
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
|
||||
EndpointAddress(index as u8 | dir as u8)
|
||||
}
|
||||
|
||||
/// Gets the direction part of the address.
|
||||
#[inline]
|
||||
pub fn direction(&self) -> UsbDirection {
|
||||
if (self.0 & Self::INBITS) != 0 {
|
||||
UsbDirection::In
|
||||
} else {
|
||||
UsbDirection::Out
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the direction is IN, otherwise false.
|
||||
#[inline]
|
||||
pub fn is_in(&self) -> bool {
|
||||
(self.0 & Self::INBITS) != 0
|
||||
}
|
||||
|
||||
/// Returns true if the direction is OUT, otherwise false.
|
||||
#[inline]
|
||||
pub fn is_out(&self) -> bool {
|
||||
(self.0 & Self::INBITS) == 0
|
||||
}
|
||||
|
||||
/// Gets the index part of the endpoint address.
|
||||
#[inline]
|
||||
pub fn index(&self) -> usize {
|
||||
(self.0 & !Self::INBITS) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointInfo {
|
||||
pub addr: EndpointAddress,
|
||||
pub ep_type: EndpointType,
|
||||
pub max_packet_size: u16,
|
||||
pub interval: u8,
|
||||
}
|
||||
|
||||
/// A handle for a USB interface that contains its number.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -5,7 +5,6 @@
|
||||
#[cfg(feature = "defmt-rtt")]
|
||||
use defmt_rtt::*;
|
||||
use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
|
||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::flash::{Flash, WRITE_SIZE};
|
||||
@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
let flash = Flash::unlock(p.FLASH);
|
||||
let mut flash = BlockingAsync::new(flash);
|
||||
let mut flash = Flash::unlock(p.FLASH);
|
||||
|
||||
let button = Input::new(p.PC13, Pull::Down);
|
||||
let mut button = ExtiInput::new(button, p.EXTI13);
|
||||
@ -27,16 +25,19 @@ async fn main(_spawner: Spawner) {
|
||||
led.set_high();
|
||||
|
||||
let mut updater = FirmwareUpdater::default();
|
||||
let mut writer = updater.prepare_update_blocking(&mut flash).unwrap();
|
||||
button.wait_for_rising_edge().await;
|
||||
let mut offset = 0;
|
||||
let mut buf: [u8; 256 * 1024] = [0; 256 * 1024];
|
||||
for chunk in APP_B.chunks(256 * 1024) {
|
||||
buf[..chunk.len()].copy_from_slice(chunk);
|
||||
updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap();
|
||||
let mut buf = AlignedBuffer([0; 4096]);
|
||||
for chunk in APP_B.chunks(4096) {
|
||||
buf.as_mut()[..chunk.len()].copy_from_slice(chunk);
|
||||
writer
|
||||
.write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len())
|
||||
.unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
|
||||
updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap();
|
||||
led.set_low();
|
||||
cortex_m::peripheral::SCB::sys_reset();
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#[cfg(feature = "defmt-rtt")]
|
||||
use defmt_rtt::*;
|
||||
use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater};
|
||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::flash::{Flash, WRITE_SIZE};
|
||||
@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
let flash = Flash::unlock(p.FLASH);
|
||||
let mut flash = BlockingAsync::new(flash);
|
||||
let mut flash = Flash::unlock(p.FLASH);
|
||||
|
||||
let button = Input::new(p.PC13, Pull::Down);
|
||||
let mut button = ExtiInput::new(button, p.EXTI13);
|
||||
@ -27,19 +25,20 @@ async fn main(_spawner: Spawner) {
|
||||
led.set_high();
|
||||
|
||||
let mut updater = FirmwareUpdater::default();
|
||||
|
||||
let mut writer = updater.prepare_update_blocking(&mut flash).unwrap();
|
||||
button.wait_for_rising_edge().await;
|
||||
let mut offset = 0;
|
||||
let mut buf = AlignedBuffer([0; 128 * 1024]);
|
||||
for chunk in APP_B.chunks(128 * 1024) {
|
||||
let mut buf = AlignedBuffer([0; 4096]);
|
||||
for chunk in APP_B.chunks(4096) {
|
||||
buf.as_mut()[..chunk.len()].copy_from_slice(chunk);
|
||||
updater
|
||||
.write_firmware(offset, buf.as_ref(), &mut flash, 2048)
|
||||
.await
|
||||
writer
|
||||
.write_block_blocking(offset, buf.as_ref(), &mut flash, 4096)
|
||||
.unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut magic = AlignedBuffer([0; WRITE_SIZE]);
|
||||
updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap();
|
||||
updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap();
|
||||
led.set_low();
|
||||
cortex_m::peripheral::SCB::sys_reset();
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"]
|
||||
nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"]
|
||||
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
@ -15,9 +15,6 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
|
||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
|
||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
|
||||
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
|
||||
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true }
|
||||
embedded-io = "0.3.0"
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -15,8 +15,8 @@ use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::{interrupt, pac, peripherals};
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embassy_usb::{Builder, Config, UsbDevice};
|
||||
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embedded_io::asynch::Write;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
@ -12,10 +12,11 @@ use embassy_futures::select::{select, Either};
|
||||
use embassy_nrf::gpio::{Input, Pin, Pull};
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
|
||||
use embassy_usb::control::OutResponse;
|
||||
use embassy_usb::{Builder, Config, DeviceStateHandler};
|
||||
use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State};
|
||||
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -66,7 +67,7 @@ async fn main(_spawner: Spawner) {
|
||||
);
|
||||
|
||||
// Create classes on the builder.
|
||||
let config = embassy_usb_hid::Config {
|
||||
let config = embassy_usb::class::hid::Config {
|
||||
report_descriptor: KeyboardReport::desc(),
|
||||
request_handler: Some(&request_handler),
|
||||
poll_ms: 60,
|
||||
@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) {
|
||||
// Build the builder.
|
||||
let mut usb = builder.build();
|
||||
|
||||
let remote_wakeup = Signal::new();
|
||||
let remote_wakeup: Signal<CriticalSectionRawMutex, _> = Signal::new();
|
||||
|
||||
// Run the USB device.
|
||||
let usb_fut = async {
|
||||
|
@ -10,9 +10,9 @@ use embassy_futures::join::join;
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State};
|
||||
use embassy_usb::control::OutResponse;
|
||||
use embassy_usb::{Builder, Config};
|
||||
use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) {
|
||||
);
|
||||
|
||||
// Create classes on the builder.
|
||||
let config = embassy_usb_hid::Config {
|
||||
let config = embassy_usb::class::hid::Config {
|
||||
report_descriptor: MouseReport::desc(),
|
||||
request_handler: Some(&request_handler),
|
||||
poll_ms: 60,
|
||||
|
@ -9,9 +9,9 @@ use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply};
|
||||
use embassy_nrf::{interrupt, pac};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::{Builder, Config};
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -8,9 +8,9 @@ use defmt::{info, panic, unwrap};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nrf::usb::{Driver, PowerUsb};
|
||||
use embassy_nrf::{interrupt, pac, peripherals};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::{Builder, Config, UsbDevice};
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
|
@ -10,9 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
|
||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
|
||||
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -13,8 +13,8 @@ use embassy_rp::usb::Driver;
|
||||
use embassy_rp::{interrupt, peripherals};
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embassy_usb::{Builder, Config, UsbDevice};
|
||||
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embedded_io::asynch::Write;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
@ -7,9 +7,9 @@ use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::interrupt;
|
||||
use embassy_rp::usb::{Driver, Instance};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::{Builder, Config};
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -9,7 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -10,9 +10,9 @@ use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::usb::{Driver, Instance};
|
||||
use embassy_stm32::{interrupt, Config};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::Builder;
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -9,8 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
|
||||
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -10,9 +10,9 @@ use embassy_stm32::time::mhz;
|
||||
use embassy_stm32::usb::{Driver, Instance};
|
||||
use embassy_stm32::{interrupt, Config};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::Builder;
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -4,11 +4,12 @@
|
||||
|
||||
use defmt::{info, unwrap};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
static SIGNAL: Signal<u32> = Signal::new();
|
||||
static SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn my_sending_task() {
|
||||
|
@ -11,9 +11,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
|
||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
|
||||
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] }
|
||||
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] }
|
||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
usbd-hid = "0.6.0"
|
||||
|
@ -15,8 +15,8 @@ use embassy_stm32::usb::Driver;
|
||||
use embassy_stm32::{interrupt, Config};
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embassy_usb::{Builder, UsbDevice};
|
||||
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||
use embedded_io::asynch::Write;
|
||||
use rand_core::RngCore;
|
||||
use static_cell::StaticCell;
|
||||
|
@ -9,9 +9,9 @@ use embassy_stm32::rcc::*;
|
||||
use embassy_stm32::usb::Driver;
|
||||
use embassy_stm32::{interrupt, Config};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State};
|
||||
use embassy_usb::control::OutResponse;
|
||||
use embassy_usb::Builder;
|
||||
use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) {
|
||||
);
|
||||
|
||||
// Create classes on the builder.
|
||||
let config = embassy_usb_hid::Config {
|
||||
let config = embassy_usb::class::hid::Config {
|
||||
report_descriptor: MouseReport::desc(),
|
||||
request_handler: Some(&request_handler),
|
||||
poll_ms: 60,
|
||||
|
@ -8,9 +8,9 @@ use embassy_futures::join::join;
|
||||
use embassy_stm32::rcc::*;
|
||||
use embassy_stm32::usb::{Driver, Instance};
|
||||
use embassy_stm32::{interrupt, Config};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::driver::EndpointError;
|
||||
use embassy_usb::Builder;
|
||||
use embassy_usb_serial::{CdcAcmClass, State};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -12,6 +12,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||
use embassy_stm32::interrupt;
|
||||
use embassy_stm32::interrupt::{Interrupt, InterruptExt};
|
||||
use embassy_stm32::subghz::*;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) {
|
||||
let button = Input::new(p.PA0, Pull::Up);
|
||||
let mut pin = ExtiInput::new(button, p.EXTI0);
|
||||
|
||||
static IRQ_SIGNAL: Signal<()> = Signal::new();
|
||||
static IRQ_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
let radio_irq = interrupt::take!(SUBGHZ_RADIO);
|
||||
radio_irq.set_handler(|_| {
|
||||
IRQ_SIGNAL.signal(());
|
||||
|
@ -3,7 +3,7 @@ build-std = ["core"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf"
|
||||
#runner = "teleprobe client run --target rpi-pico --elf"
|
||||
runner = "teleprobe local run --chip RP2040 --elf"
|
||||
|
||||
rustflags = [
|
||||
|
@ -7,7 +7,7 @@ version = "0.1.0"
|
||||
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }
|
||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] }
|
||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
defmt = "0.3.0"
|
||||
@ -20,6 +20,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1" }
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
embedded-io = { version = "0.3.0", features = ["async"] }
|
||||
|
||||
[profile.dev]
|
||||
debug = 2
|
||||
|
44
tests/rp/src/bin/uart_buffered.rs
Normal file
44
tests/rp/src/bin/uart_buffered.rs
Normal file
@ -0,0 +1,44 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::interrupt;
|
||||
use embassy_rp::uart::{BufferedUart, Config, State, Uart};
|
||||
use embedded_io::asynch::{Read, Write};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0);
|
||||
|
||||
let config = Config::default();
|
||||
let uart = Uart::new_blocking(uart, tx, rx, config);
|
||||
|
||||
let irq = interrupt::take!(UART0_IRQ);
|
||||
let tx_buf = &mut [0u8; 16];
|
||||
let rx_buf = &mut [0u8; 16];
|
||||
let mut state = State::new();
|
||||
let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf);
|
||||
|
||||
// Make sure we send more bytes than fits in the FIFO, to test the actual
|
||||
// bufferedUart.
|
||||
|
||||
let data = [
|
||||
1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32,
|
||||
];
|
||||
uart.write_all(&data).await.unwrap();
|
||||
info!("Done writing");
|
||||
|
||||
let mut buf = [0; 32];
|
||||
uart.read_exact(&mut buf).await.unwrap();
|
||||
assert_eq!(buf, data);
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
Loading…
Reference in New Issue
Block a user