Rebase
This commit is contained in:
commit
cd539ba3a0
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 \
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![no_std]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../../README.md")]
|
||||
@ -222,10 +221,7 @@ impl BootLoader {
|
||||
page: &mut [u8],
|
||||
) -> Result<State, BootError> {
|
||||
// Ensure we have enough progress pages to store copy progress
|
||||
assert_eq!(self.active.len() % page.len(), 0);
|
||||
assert_eq!(self.dfu.len() % page.len(), 0);
|
||||
assert!(self.dfu.len() - self.active.len() >= page.len());
|
||||
assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE);
|
||||
assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE);
|
||||
assert_eq!(magic.len(), P::STATE::WRITE_SIZE);
|
||||
|
||||
// Copy contents from partition N to active
|
||||
@ -409,6 +405,13 @@ impl BootLoader {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) {
|
||||
assert_eq!(active.len() % page_size, 0);
|
||||
assert_eq!(dfu.len() % page_size, 0);
|
||||
assert!(dfu.len() - active.len() >= page_size);
|
||||
assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size);
|
||||
}
|
||||
|
||||
/// Convenience provider that uses a single flash for all partitions.
|
||||
pub struct SingleFlashConfig<'a, F>
|
||||
where
|
||||
@ -447,24 +450,24 @@ where
|
||||
}
|
||||
|
||||
/// A flash wrapper implementing the Flash and embedded_storage traits.
|
||||
pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF>
|
||||
pub struct BootFlash<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF>
|
||||
where
|
||||
F: NorFlash + ReadNorFlash,
|
||||
{
|
||||
flash: &'a mut F,
|
||||
flash: F,
|
||||
}
|
||||
|
||||
impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
|
||||
impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
|
||||
where
|
||||
F: NorFlash + ReadNorFlash,
|
||||
{
|
||||
/// Create a new instance of a bootable flash
|
||||
pub fn new(flash: &'a mut F) -> Self {
|
||||
pub fn new(flash: F) -> Self {
|
||||
Self { flash }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
|
||||
impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
|
||||
where
|
||||
F: NorFlash + ReadNorFlash,
|
||||
{
|
||||
@ -472,14 +475,14 @@ where
|
||||
const ERASE_VALUE: u8 = ERASE_VALUE;
|
||||
}
|
||||
|
||||
impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
|
||||
impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
|
||||
where
|
||||
F: ReadNorFlash + NorFlash,
|
||||
{
|
||||
type Error = F::Error;
|
||||
}
|
||||
|
||||
impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
|
||||
impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
|
||||
where
|
||||
F: ReadNorFlash + NorFlash,
|
||||
{
|
||||
@ -487,26 +490,26 @@ where
|
||||
const ERASE_SIZE: usize = F::ERASE_SIZE;
|
||||
|
||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||
F::erase(self.flash, from, to)
|
||||
F::erase(&mut self.flash, from, to)
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
F::write(self.flash, offset, bytes)
|
||||
F::write(&mut self.flash, offset, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE>
|
||||
impl<F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<F, BLOCK_SIZE, ERASE_VALUE>
|
||||
where
|
||||
F: ReadNorFlash + NorFlash,
|
||||
{
|
||||
const READ_SIZE: usize = F::READ_SIZE;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
F::read(self.flash, offset, bytes)
|
||||
F::read(&mut self.flash, offset, bytes)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
F::capacity(self.flash)
|
||||
F::capacity(&self.flash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,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
|
||||
@ -657,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,
|
||||
@ -676,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?;
|
||||
@ -699,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)]
|
||||
@ -919,6 +1124,15 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_range_asserts() {
|
||||
const ACTIVE: Partition = Partition::new(4096, 4194304);
|
||||
const DFU: Partition = Partition::new(4194304, 2 * 4194304);
|
||||
const STATE: Partition = Partition::new(0, 4096);
|
||||
assert_partitions(ACTIVE, DFU, STATE, 4096, 4);
|
||||
}
|
||||
|
||||
struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
|
||||
|
||||
impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../../README.md")]
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../../README.md")]
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::ptr::NonNull;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::poll_fn;
|
||||
|
||||
use super::raw;
|
||||
|
||||
/// Token to spawn a newly-created task in an executor.
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(generic_associated_types)]
|
||||
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
|
||||
//! crate's async LoRaWAN MAC implementation.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! A radio driver integration for the radio found on STM32WL family devices.
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
@ -11,7 +11,6 @@ use embassy_stm32::subghz::{
|
||||
Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
|
||||
};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
|
||||
use lorawan_device::async_device::Timings;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use embassy_time::{Instant, Timer};
|
||||
use futures::future::poll_fn;
|
||||
use futures::pin_mut;
|
||||
use heapless::Vec;
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures::future::poll_fn;
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
use smoltcp::socket::tcp;
|
||||
use smoltcp::time::Duration;
|
||||
@ -103,7 +103,7 @@ impl<'a> TcpSocket<'a> {
|
||||
Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),
|
||||
}
|
||||
|
||||
futures::future::poll_fn(|cx| unsafe {
|
||||
poll_fn(|cx| unsafe {
|
||||
self.io.with_mut(|s, _| match s.state() {
|
||||
tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)),
|
||||
tcp::State::Listen => unreachable!(),
|
||||
@ -128,7 +128,7 @@ impl<'a> TcpSocket<'a> {
|
||||
Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),
|
||||
}
|
||||
|
||||
futures::future::poll_fn(|cx| unsafe {
|
||||
poll_fn(|cx| unsafe {
|
||||
self.io.with_mut(|s, _| match s.state() {
|
||||
tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {
|
||||
s.register_send_waker(cx.waker());
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures::future::poll_fn;
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
use smoltcp::socket::udp::{self, PacketMetadata};
|
||||
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
@ -23,11 +23,10 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag
|
||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use futures::future::poll_fn;
|
||||
// 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};
|
||||
@ -429,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.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
if let TxState::Transmitting(_) = self.tx_state {
|
||||
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);
|
||||
}
|
||||
|
||||
r.inten.reset();
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
r.events_txstopped.reset();
|
||||
r.tasks_stoptx.write(|w| unsafe { w.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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -549,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();
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::convert::Infallible;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
|
||||
|
@ -43,7 +43,7 @@
|
||||
//! mutable slices always reside in RAM.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "nrf51",
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Quadrature decoder interface
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
|
@ -1,11 +1,11 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::ptr;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::DropBomb;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::{self, Pin as GpioPin};
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::future::poll_fn;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
use core::task::Poll;
|
||||
@ -5,7 +6,6 @@ use core::task::Poll;
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::RNG;
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
use pac::{saadc, SAADC};
|
||||
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
|
||||
// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
use futures::future::poll_fn;
|
||||
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
||||
|
||||
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! Temperature sensor interface.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use fixed::types::I30F2;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::TEMP;
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::ppi::{Event, Task};
|
||||
|
@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! - nRF52832: Section 33
|
||||
//! - nRF52840: Section 6.31
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
use core::task::Poll;
|
||||
@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::Pin as GpioPin;
|
||||
|
@ -13,12 +13,12 @@
|
||||
//! memory may be used given that buffers are passed in directly to its read and write
|
||||
//! methods.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::poll_fn;
|
||||
use pac::uarte0::RegisterBlock;
|
||||
// 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};
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||||
@ -9,10 +10,9 @@ 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 futures::future::poll_fn;
|
||||
use futures::Future;
|
||||
use embassy_usb::driver::{
|
||||
self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
use pac::usbd::RegisterBlock;
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
@ -244,7 +244,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,
|
||||
@ -260,7 +260,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,
|
||||
@ -429,8 +429,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,7 +443,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();
|
||||
@ -467,7 +467,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 {
|
||||
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits"]
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"]
|
||||
flavors = [
|
||||
{ name = "rp2040", target = "thumbv6m-none-eabi" },
|
||||
]
|
||||
@ -20,6 +20,12 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"]
|
||||
# There are no plans to make this stable.
|
||||
unstable-pac = []
|
||||
|
||||
time-driver = []
|
||||
|
||||
rom-func-cache = []
|
||||
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", "dep:embedded-io"]
|
||||
|
||||
@ -31,6 +37,7 @@ unstable-traits = ["embedded-hal-1"]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
||||
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" }
|
||||
@ -44,6 +51,7 @@ cortex-m-rt = ">=0.6.15,<0.8"
|
||||
cortex-m = "0.7.6"
|
||||
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-io = { version = "0.3.0", features = ["async"], optional = true }
|
||||
|
||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||
|
@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 {
|
||||
125_000_000
|
||||
}
|
||||
|
||||
pub(crate) fn _clk_rtc_freq() -> u32 {
|
||||
pub(crate) fn clk_rtc_freq() -> u32 {
|
||||
46875
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::{Context, Poll};
|
||||
@ -5,7 +6,6 @@ use core::task::{Context, Poll};
|
||||
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
||||
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::Future;
|
||||
use pac::dma::vals::DataSize;
|
||||
|
||||
use crate::pac::dma::vals;
|
||||
@ -75,6 +75,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
to: *mut W,
|
||||
len: usize,
|
||||
dreq: u8,
|
||||
) -> Transfer<'a, C> {
|
||||
let dummy: u32 = 0;
|
||||
copy_inner(
|
||||
ch,
|
||||
&dummy as *const u32,
|
||||
to as *mut u32,
|
||||
len,
|
||||
W::size(),
|
||||
false,
|
||||
false,
|
||||
dreq,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn copy<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: &[W],
|
||||
|
@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() {
|
||||
w.set_edge_low(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin);
|
||||
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin);
|
||||
w.set_level_high(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
critical_section::with(|_| {
|
||||
pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level {
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin());
|
||||
debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin());
|
||||
w.set_level_high(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> {
|
||||
// the pin and if it has been disabled that means it was done by the
|
||||
// interrupt service routine, so we then know that the event/trigger
|
||||
// happened and Poll::Ready will be returned.
|
||||
debug!("{:?} for pin {}\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {}", self.level, self.pin.pin());
|
||||
match self.level {
|
||||
InterruptTrigger::AnyEdge => {
|
||||
if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
if !inte.level_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
if !inte.level_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeHigh => {
|
||||
if !inte.edge_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeLow => {
|
||||
if !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("InputFuture::poll return Poll::Pending\n");
|
||||
debug!("InputFuture::poll return Poll::Pending");
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
276
embassy-rp/src/intrinsics.rs
Normal file
276
embassy-rp/src/intrinsics.rs
Normal file
@ -0,0 +1,276 @@
|
||||
#![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 {
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The macro used to define overridden intrinsics.
|
||||
///
|
||||
/// This is heavily inspired by the macro used by compiler-builtins. The idea
|
||||
/// 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 by disabling the crate feature `intrinsics` for
|
||||
/// testing or comparing performance.
|
||||
///
|
||||
/// Like the compiler-builtins macro, it accepts a series of functions that
|
||||
/// looks like normal Rust code:
|
||||
///
|
||||
/// intrinsics! {
|
||||
/// extern "C" fn foo(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// #[nonstandard_attribute]
|
||||
/// extern "C" fn bar(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// Each function can also be decorated with nonstandard attributes to control
|
||||
/// additional behaviour:
|
||||
///
|
||||
/// * `slower_than_default` - indicates that the override is slower than the
|
||||
/// default implementation. Currently this just disables the override
|
||||
/// entirely.
|
||||
/// * `bootrom_v2` - indicates that the override is only available
|
||||
/// on a V2 bootrom or higher. Only enabled when the feature
|
||||
/// `rom-v2-intrinsics` is set.
|
||||
/// * `alias` - accepts a list of names to alias the intrinsic to.
|
||||
/// * `aeabi` - accepts a list of ARM EABI names to alias to.
|
||||
///
|
||||
macro_rules! intrinsics {
|
||||
() => {};
|
||||
|
||||
(
|
||||
#[slower_than_default]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[bootrom_v2]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(feature = "rom-v2-intrinsics"))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(feature = "rom-v2-intrinsics")]
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[aeabi = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
mod intrinsics;
|
||||
|
||||
pub mod dma;
|
||||
pub mod gpio;
|
||||
pub mod interrupt;
|
||||
pub mod rom_data;
|
||||
pub mod rtc;
|
||||
pub mod spi;
|
||||
#[cfg(feature = "time-driver")]
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -84,6 +89,8 @@ embassy_hal_common::peripherals! {
|
||||
DMA_CH11,
|
||||
|
||||
USB,
|
||||
|
||||
RTC,
|
||||
}
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
@ -108,6 +115,7 @@ pub fn init(_config: config::Config) -> Peripherals {
|
||||
|
||||
unsafe {
|
||||
clocks::init();
|
||||
#[cfg(feature = "time-driver")]
|
||||
timer::init();
|
||||
dma::init();
|
||||
}
|
||||
|
733
embassy-rp/src/rom_data.rs
Normal file
733
embassy-rp/src/rom_data.rs
Normal file
@ -0,0 +1,733 @@
|
||||
//! Functions and data from the RPI Bootrom.
|
||||
//!
|
||||
//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
|
||||
//!
|
||||
//! > The Bootrom contains a number of public functions that provide useful
|
||||
//! > RP2040 functionality that might be needed in the absence of any other code
|
||||
//! > 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];
|
||||
|
||||
/// This function searches for (table)
|
||||
type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
|
||||
|
||||
/// The following addresses are described at `2.8.2. Bootrom Contents`
|
||||
/// Pointer to the lookup table function supplied by the rom.
|
||||
const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
|
||||
|
||||
/// Pointer to helper functions lookup table.
|
||||
const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
|
||||
|
||||
/// Pointer to the public data lookup table.
|
||||
const DATA_TABLE: *const u16 = 0x0000_0016 as _;
|
||||
|
||||
/// Address of the version number of the ROM.
|
||||
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
|
||||
|
||||
/// Retrive rom content from a table using a code.
|
||||
fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
|
||||
unsafe {
|
||||
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
|
||||
let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
|
||||
rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// To save space, the ROM likes to store memory pointers (which are 32-bit on
|
||||
/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the
|
||||
/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped
|
||||
/// to address `0x0000_0000` and so 16-bits are always sufficient.
|
||||
///
|
||||
/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.
|
||||
unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
|
||||
let ptr: u16 = *rom_address;
|
||||
ptr as *const u32
|
||||
}
|
||||
|
||||
macro_rules! declare_rom_function {
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! rom_functions {
|
||||
() => {};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
rom_functions! {
|
||||
/// Return a count of the number of 1 bits in value.
|
||||
b"P3" fn popcount32(value: u32) -> u32;
|
||||
|
||||
/// Return the bits of value in the reverse order.
|
||||
b"R3" fn reverse32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
|
||||
b"L3" fn clz32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
|
||||
b"T3" fn ctz32(value: u32) -> u32;
|
||||
|
||||
/// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
|
||||
/// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
|
||||
/// for the USB Mass Storage Device:
|
||||
/// * 0 No pins are used as per cold boot.
|
||||
/// * Otherwise a single bit set indicating which GPIO pin should be set to output and
|
||||
/// raised whenever there is mass storage activity from the host.
|
||||
/// * disable_interface_mask may be used to control the exposed USB interfaces:
|
||||
/// * 0 To enable both interfaces (as per cold boot).
|
||||
/// * 1 To disable the USB Mass Storage Interface.
|
||||
/// * 2 to Disable the USB PICOBOOT Interface.
|
||||
b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr
|
||||
b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memset that may only
|
||||
/// be used if ptr is word aligned.
|
||||
// Note the datasheet does not match the actual ROM for the code here, see
|
||||
// https://github.com/raspberrypi/pico-feedback/issues/217
|
||||
b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memcpy that may only be
|
||||
/// used if dest and src are word aligned.
|
||||
b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
|
||||
|
||||
/// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
|
||||
b"IF" unsafe fn connect_internal_flash() -> ();
|
||||
|
||||
/// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
|
||||
///
|
||||
/// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
|
||||
/// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
|
||||
/// function configures the SSI with a fixed SCK clock divisor of /6.
|
||||
b"EX" unsafe fn flash_exit_xip() -> ();
|
||||
|
||||
/// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
|
||||
/// block erase command e.g. D8h block erase, and the size of the block erased by this
|
||||
/// command — this function will use the larger block erase where possible, for much higher
|
||||
/// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
|
||||
/// 4096 bytes.
|
||||
b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
|
||||
|
||||
/// Program data to a range of flash addresses starting at `addr` (and
|
||||
/// offset from the start of flash) and `count` bytes in size. The value
|
||||
/// `addr` must be aligned to a 256-byte boundary, and `count` must be a
|
||||
/// multiple of 256.
|
||||
b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
|
||||
|
||||
/// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
|
||||
/// drive the flashchip select as normal.
|
||||
b"FC" unsafe fn flash_flush_cache() -> ();
|
||||
|
||||
/// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
|
||||
/// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
|
||||
/// The debugger calls this function after performing a flash erase/programming operation, so
|
||||
/// that the freshly-programmed code and data is visible to the debug host, without having to
|
||||
/// know exactly what kind of flash device is connected.
|
||||
b"CX" unsafe fn flash_enter_cmd_xip() -> ();
|
||||
|
||||
/// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
|
||||
/// There are few cases where you should call this method (resetting core 1 is much better).
|
||||
/// This method does not return and should only ever be called on core 1.
|
||||
b"WV" unsafe fn wait_for_vector() -> !;
|
||||
}
|
||||
|
||||
// Various C intrinsics in the ROM
|
||||
intrinsics! {
|
||||
#[alias = __popcountdi2]
|
||||
extern "C" fn __popcountsi2(x: u32) -> u32 {
|
||||
popcount32(x)
|
||||
}
|
||||
|
||||
#[alias = __clzdi2]
|
||||
extern "C" fn __clzsi2(x: u32) -> u32 {
|
||||
clz32(x)
|
||||
}
|
||||
|
||||
#[alias = __ctzdi2]
|
||||
extern "C" fn __ctzsi2(x: u32) -> u32 {
|
||||
ctz32(x)
|
||||
}
|
||||
|
||||
// __rbit is only unofficial, but it show up in the ARM documentation,
|
||||
// so may as well hook it up.
|
||||
#[alias = __rbitl]
|
||||
extern "C" fn __rbit(x: u32) -> u32 {
|
||||
reverse32(x)
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset(dest, c as u8, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memset8]
|
||||
unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset4(dest as *mut u32, c as u8, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
|
||||
memset(dest, 0, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memclr8]
|
||||
unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
|
||||
memset4(dest as *mut u32, 0, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy(dest, src, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memcpy8]
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy44(dest as *mut u32, src as *const u32, n as u32);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn convert_str(s: *const u8) -> &'static str {
|
||||
let mut end = s;
|
||||
while *end != 0 {
|
||||
end = end.add(1);
|
||||
}
|
||||
let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
|
||||
core::str::from_utf8_unchecked(s)
|
||||
}
|
||||
|
||||
/// The version number of the rom.
|
||||
pub fn rom_version_number() -> u8 {
|
||||
unsafe { *VERSION_NUMBER }
|
||||
}
|
||||
|
||||
/// The Raspberry Pi Trading Ltd copyright string.
|
||||
pub fn copyright_string() -> &'static str {
|
||||
let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
|
||||
unsafe { convert_str(s) }
|
||||
}
|
||||
|
||||
/// The 8 most significant hex digits of the Bootrom git revision.
|
||||
pub fn git_revision() -> u32 {
|
||||
let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
|
||||
unsafe { *s }
|
||||
}
|
||||
|
||||
/// The start address of the floating point library code and data.
|
||||
///
|
||||
/// This and fplib_end along with the individual function pointers in
|
||||
/// soft_float_table can be used to copy the floating point implementation into
|
||||
/// RAM if desired.
|
||||
pub fn fplib_start() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FS")
|
||||
}
|
||||
|
||||
/// See Table 180 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_float_table() -> *const usize {
|
||||
rom_table_lookup(DATA_TABLE, *b"SF")
|
||||
}
|
||||
|
||||
/// The end address of the floating point library code and data.
|
||||
pub fn fplib_end() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FE")
|
||||
}
|
||||
|
||||
/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_double_table() -> *const usize {
|
||||
if rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Double precision operations require V2 bootrom (found: V{})",
|
||||
rom_version_number()
|
||||
);
|
||||
}
|
||||
rom_table_lookup(DATA_TABLE, *b"SD")
|
||||
}
|
||||
|
||||
/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)
|
||||
pub mod float_funcs {
|
||||
|
||||
macro_rules! make_functions {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_functions! {
|
||||
/// Calculates `a + b`
|
||||
0x00 fadd(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a - b`
|
||||
0x04 fsub(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a * b`
|
||||
0x08 fmul(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a / b`
|
||||
0x0c fdiv(a: f32, b: f32) -> f32;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 fsqrt(v: f32) -> f32;
|
||||
/// Converts an f32 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c float_to_int(v: f32) -> i32;
|
||||
/// Converts an f32 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 float_to_fix(v: f32, n: i32) -> i32;
|
||||
/// Converts an f32 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 float_to_uint(v: f32) -> u32;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 float_to_ufix(v: f32, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x2c int_to_float(v: i32) -> f32;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_float(v: i32, n: i32) -> f32;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x34 uint_to_float(v: u32) -> f32;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x38 ufix_to_float(v: u32, n: i32) -> f32;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c fcos(angle: f32) -> f32;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 fsin(angle: f32) -> f32;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 ftan(angle: f32) -> f32;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c fexp(v: f32) -> f32;
|
||||
/// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity
|
||||
0x50 fln(v: f32) -> f32;
|
||||
}
|
||||
|
||||
macro_rules! make_functions_v2 {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
if $crate::rom_data::rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Floating point function requires V2 bootrom (found: V{})",
|
||||
$crate::rom_data::rom_version_number()
|
||||
);
|
||||
}
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
make_functions_v2! {
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 fcmp(a: f32, b: f32) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 fatan2(y: f32, x: f32) -> f32;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x5c int64_to_float(v: i64) -> f32;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_float(v: i64, n: i32) -> f32;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x64 uint64_to_float(v: u64) -> f32;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest f32 value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_float(v: u64, n: i32) -> f32;
|
||||
/// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c float_to_int64(v: f32) -> i64;
|
||||
/// Converts an f32 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 float_to_fix64(v: f32, n: i32) -> f32;
|
||||
/// Converts an f32 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 float_to_uint64(v: f32) -> u64;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 float_to_ufix64(v: f32, n: i32) -> u64;
|
||||
/// Converts an f32 to an f64.
|
||||
0x7c float_to_double(v: f32) -> f64;
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)
|
||||
pub mod double_funcs {
|
||||
|
||||
macro_rules! make_double_funcs {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_double_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_double_funcs! {
|
||||
/// Calculates `a + b`
|
||||
0x00 dadd(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a - b`
|
||||
0x04 dsub(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a * b`
|
||||
0x08 dmul(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a / b`
|
||||
0x0c ddiv(a: f64, b: f64) -> f64;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 dsqrt(v: f64) -> f64;
|
||||
/// Converts an f64 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c double_to_int(v: f64) -> i32;
|
||||
/// Converts an f64 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 double_to_fix(v: f64, n: i32) -> i32;
|
||||
/// Converts an f64 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 double_to_uint(v: f64) -> u32;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 double_to_ufix(v: f64, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x2c int_to_double(v: i32) -> f64;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_double(v: i32, n: i32) -> f64;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x34 uint_to_double(v: u32) -> f64;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so f =
|
||||
/// nearest(v/(2^n))
|
||||
0x38 ufix_to_double(v: u32, n: i32) -> f64;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c dcos(angle: f64) -> f64;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 dsin(angle: f64) -> f64;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 dtan(angle: f64) -> f64;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c dexp(v: f64) -> f64;
|
||||
/// Calculates the natural logarithm of v. If v <= 0 return -Infinity
|
||||
0x50 dln(v: f64) -> f64;
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 dcmp(a: f64, b: f64) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 datan2(y: f64, x: f64) -> f64;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x5c int64_to_double(v: i64) -> f64;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x64 uint64_to_double(v: u64) -> f64;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest double value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_double(v: u64, n: i32) -> f64;
|
||||
/// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c double_to_int64(v: f64) -> i64;
|
||||
/// Converts an f64 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 double_to_fix64(v: f64, n: i32) -> i64;
|
||||
/// Converts an f64 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 double_to_uint64(v: f64) -> u64;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 double_to_ufix64(v: f64, n: i32) -> u64;
|
||||
/// Converts an f64 to an f32
|
||||
0x7c double_to_float(v: f64) -> f32;
|
||||
}
|
||||
}
|
62
embassy-rp/src/rtc/datetime_chrono.rs
Normal file
62
embassy-rp/src/rtc/datetime_chrono.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use chrono::{Datelike, Timelike};
|
||||
|
||||
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
|
||||
|
||||
/// Alias for [`chrono::NaiveDateTime`]
|
||||
pub type DateTime = chrono::NaiveDateTime;
|
||||
/// Alias for [`chrono::Weekday`]
|
||||
pub type DayOfWeek = chrono::Weekday;
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid date.
|
||||
InvalidDate,
|
||||
/// The [DateTime] contains an invalid time.
|
||||
InvalidTime,
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw.num_days_from_sunday() as u8
|
||||
}
|
||||
|
||||
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year() < 0 || dt.year() > 4095 {
|
||||
// rp2040 can't hold these years
|
||||
Err(Error::InvalidYear)
|
||||
} else {
|
||||
// The rest of the chrono date is assumed to be valid
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
|
||||
w.set_year(dt.year() as u16);
|
||||
w.set_month(dt.month() as u8);
|
||||
w.set_day(dt.day() as u8);
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
|
||||
w.set_dotw(dt.weekday().num_days_from_sunday() as u8);
|
||||
w.set_hour(dt.hour() as u8);
|
||||
w.set_min(dt.minute() as u8);
|
||||
w.set_sec(dt.second() as u8);
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year() as i32;
|
||||
let month = rtc_1.month() as u32;
|
||||
let day = rtc_1.day() as u32;
|
||||
|
||||
let hour = rtc_0.hour() as u32;
|
||||
let minute = rtc_0.min() as u32;
|
||||
let second = rtc_0.sec() as u32;
|
||||
|
||||
let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;
|
||||
let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;
|
||||
Ok(DateTime::new(date, time))
|
||||
}
|
127
embassy-rp/src/rtc/datetime_no_deps.rs
Normal file
127
embassy-rp/src/rtc/datetime_no_deps.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
|
||||
InvalidMonth,
|
||||
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
|
||||
InvalidDay,
|
||||
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
|
||||
InvalidDayOfWeek(
|
||||
/// The value of the DayOfWeek that was given.
|
||||
u8,
|
||||
),
|
||||
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
|
||||
InvalidHour,
|
||||
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
|
||||
InvalidMinute,
|
||||
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
|
||||
InvalidSecond,
|
||||
}
|
||||
|
||||
/// Structure containing date and time information
|
||||
pub struct DateTime {
|
||||
/// 0..4095
|
||||
pub year: u16,
|
||||
/// 1..12, 1 is January
|
||||
pub month: u8,
|
||||
/// 1..28,29,30,31 depending on month
|
||||
pub day: u8,
|
||||
///
|
||||
pub day_of_week: DayOfWeek,
|
||||
/// 0..23
|
||||
pub hour: u8,
|
||||
/// 0..59
|
||||
pub minute: u8,
|
||||
/// 0..59
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
/// A day of the week
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DayOfWeek {
|
||||
Sunday = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 3,
|
||||
Thursday = 4,
|
||||
Friday = 5,
|
||||
Saturday = 6,
|
||||
}
|
||||
|
||||
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
|
||||
Ok(match v {
|
||||
0 => DayOfWeek::Sunday,
|
||||
1 => DayOfWeek::Monday,
|
||||
2 => DayOfWeek::Tuesday,
|
||||
3 => DayOfWeek::Wednesday,
|
||||
4 => DayOfWeek::Thursday,
|
||||
5 => DayOfWeek::Friday,
|
||||
6 => DayOfWeek::Saturday,
|
||||
x => return Err(Error::InvalidDayOfWeek(x)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw as u8
|
||||
}
|
||||
|
||||
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year > 4095 {
|
||||
Err(Error::InvalidYear)
|
||||
} else if dt.month < 1 || dt.month > 12 {
|
||||
Err(Error::InvalidMonth)
|
||||
} else if dt.day < 1 || dt.day > 31 {
|
||||
Err(Error::InvalidDay)
|
||||
} else if dt.hour > 23 {
|
||||
Err(Error::InvalidHour)
|
||||
} else if dt.minute > 59 {
|
||||
Err(Error::InvalidMinute)
|
||||
} else if dt.second > 59 {
|
||||
Err(Error::InvalidSecond)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
|
||||
w.set_year(dt.year);
|
||||
w.set_month(dt.month);
|
||||
w.set_day(dt.day);
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
|
||||
w.set_dotw(dt.day_of_week as u8);
|
||||
w.set_hour(dt.hour);
|
||||
w.set_min(dt.minute);
|
||||
w.set_sec(dt.second);
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year();
|
||||
let month = rtc_1.month();
|
||||
let day = rtc_1.day();
|
||||
|
||||
let day_of_week = rtc_0.dotw();
|
||||
let hour = rtc_0.hour();
|
||||
let minute = rtc_0.min();
|
||||
let second = rtc_0.sec();
|
||||
|
||||
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||
Ok(DateTime {
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
day_of_week,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
})
|
||||
}
|
100
embassy-rp/src/rtc/filter.rs
Normal file
100
embassy-rp/src/rtc/filter.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use super::DayOfWeek;
|
||||
use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1};
|
||||
|
||||
/// A filter used for [`RealTimeClock::schedule_alarm`].
|
||||
///
|
||||
/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
|
||||
#[derive(Default)]
|
||||
pub struct DateTimeFilter {
|
||||
/// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
|
||||
pub year: Option<u16>,
|
||||
/// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.
|
||||
pub month: Option<u8>,
|
||||
/// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.
|
||||
pub day: Option<u8>,
|
||||
/// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.
|
||||
pub day_of_week: Option<DayOfWeek>,
|
||||
/// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.
|
||||
pub hour: Option<u8>,
|
||||
/// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.
|
||||
pub minute: Option<u8>,
|
||||
/// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.
|
||||
pub second: Option<u8>,
|
||||
}
|
||||
|
||||
impl DateTimeFilter {
|
||||
/// Set a filter on the given year
|
||||
pub fn year(mut self, year: u16) -> Self {
|
||||
self.year = Some(year);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given month
|
||||
pub fn month(mut self, month: u8) -> Self {
|
||||
self.month = Some(month);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day
|
||||
pub fn day(mut self, day: u8) -> Self {
|
||||
self.day = Some(day);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day of the week
|
||||
pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {
|
||||
self.day_of_week = Some(day_of_week);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given hour
|
||||
pub fn hour(mut self, hour: u8) -> Self {
|
||||
self.hour = Some(hour);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given minute
|
||||
pub fn minute(mut self, minute: u8) -> Self {
|
||||
self.minute = Some(minute);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given second
|
||||
pub fn second(mut self, second: u8) -> Self {
|
||||
self.second = Some(second);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// register helper functions
|
||||
impl DateTimeFilter {
|
||||
pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) {
|
||||
if let Some(year) = self.year {
|
||||
w.set_year_ena(true);
|
||||
|
||||
w.set_year(year);
|
||||
}
|
||||
if let Some(month) = self.month {
|
||||
w.set_month_ena(true);
|
||||
w.set_month(month);
|
||||
}
|
||||
if let Some(day) = self.day {
|
||||
w.set_day_ena(true);
|
||||
w.set_day(day);
|
||||
}
|
||||
}
|
||||
pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) {
|
||||
if let Some(day_of_week) = self.day_of_week {
|
||||
w.set_dotw_ena(true);
|
||||
let bits = super::datetime::day_of_week_to_u8(day_of_week);
|
||||
|
||||
w.set_dotw(bits);
|
||||
}
|
||||
if let Some(hour) = self.hour {
|
||||
w.set_hour_ena(true);
|
||||
w.set_hour(hour);
|
||||
}
|
||||
if let Some(minute) = self.minute {
|
||||
w.set_min_ena(true);
|
||||
w.set_min(minute);
|
||||
}
|
||||
if let Some(second) = self.second {
|
||||
w.set_sec_ena(true);
|
||||
w.set_sec(second);
|
||||
}
|
||||
}
|
||||
}
|
188
embassy-rp/src/rtc/mod.rs
Normal file
188
embassy-rp/src/rtc/mod.rs
Normal file
@ -0,0 +1,188 @@
|
||||
mod filter;
|
||||
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
|
||||
pub use self::filter::DateTimeFilter;
|
||||
|
||||
#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
|
||||
#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
|
||||
mod datetime;
|
||||
|
||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||
use crate::clocks::clk_rtc_freq;
|
||||
|
||||
/// A reference to the real time clock of the system
|
||||
pub struct RealTimeClock<'d, T: Instance> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
|
||||
into_ref!(inner);
|
||||
|
||||
// Set the RTC divider
|
||||
unsafe {
|
||||
inner
|
||||
.regs()
|
||||
.clkdiv_m1()
|
||||
.write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1))
|
||||
};
|
||||
|
||||
let mut result = Self { inner };
|
||||
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
||||
result.set_datetime(initial_date)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
|
||||
///
|
||||
/// Leap year checking is enabled by default.
|
||||
pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
|
||||
unsafe {
|
||||
self.inner
|
||||
.regs()
|
||||
.ctrl()
|
||||
.modify(|w| w.set_force_notleapyear(!leap_year_check_enabled))
|
||||
};
|
||||
}
|
||||
|
||||
/// Checks to see if this RealTimeClock is running
|
||||
pub fn is_running(&self) -> bool {
|
||||
unsafe { self.inner.regs().ctrl().read().rtc_active() }
|
||||
}
|
||||
|
||||
/// Set the datetime to a new value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
||||
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
||||
|
||||
// disable RTC while we configure it
|
||||
unsafe {
|
||||
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
|
||||
while self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.inner.regs().setup_0().write(|w| {
|
||||
self::datetime::write_setup_0(&t, w);
|
||||
});
|
||||
self.inner.regs().setup_1().write(|w| {
|
||||
self::datetime::write_setup_1(&t, w);
|
||||
});
|
||||
|
||||
// Load the new datetime and re-enable RTC
|
||||
self.inner.regs().ctrl().write(|w| w.set_load(true));
|
||||
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
|
||||
while !self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the current datetime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
|
||||
pub fn now(&self) -> Result<DateTime, RtcError> {
|
||||
if !self.is_running() {
|
||||
return Err(RtcError::NotRunning);
|
||||
}
|
||||
|
||||
let rtc_0 = unsafe { self.inner.regs().rtc_0().read() };
|
||||
let rtc_1 = unsafe { self.inner.regs().rtc_1().read() };
|
||||
|
||||
self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
|
||||
}
|
||||
|
||||
/// Disable the alarm that was scheduled with [`schedule_alarm`].
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn disable_alarm(&mut self) {
|
||||
unsafe {
|
||||
self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
|
||||
|
||||
while self.inner.regs().irq_setup_0().read().match_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
|
||||
///
|
||||
/// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:
|
||||
/// ```no_run
|
||||
/// # #[cfg(feature = "chrono")]
|
||||
/// # fn main() { }
|
||||
/// # #[cfg(not(feature = "chrono"))]
|
||||
/// # fn main() {
|
||||
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
|
||||
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
|
||||
/// let now = real_time_clock.now().unwrap();
|
||||
/// real_time_clock.schedule_alarm(
|
||||
/// DateTimeFilter::default()
|
||||
/// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
|
||||
self.disable_alarm();
|
||||
|
||||
unsafe {
|
||||
self.inner.regs().irq_setup_0().write(|w| {
|
||||
filter.write_setup_0(w);
|
||||
});
|
||||
self.inner.regs().irq_setup_1().write(|w| {
|
||||
filter.write_setup_1(w);
|
||||
});
|
||||
|
||||
// Set the enable bit and check if it is set
|
||||
self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
|
||||
while !self.inner.regs().irq_setup_0().read().match_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
|
||||
/// or the next [`schedule_alarm`] will never fire.
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
self.disable_alarm();
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur on methods on [RtcClock]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RtcError {
|
||||
/// An invalid DateTime was given or stored on the hardware.
|
||||
InvalidDateTime(DateTimeError),
|
||||
|
||||
/// The RTC clock is not running
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs(&self) -> crate::pac::rtc::Rtc;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
impl sealed::Instance for crate::peripherals::RTC {
|
||||
fn regs(&self) -> crate::pac::rtc::Rtc {
|
||||
crate::pac::RTC
|
||||
}
|
||||
}
|
||||
impl Instance for crate::peripherals::RTC {}
|
@ -1,9 +1,9 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Phase, Polarity};
|
||||
use futures::future::join;
|
||||
|
||||
use crate::dma::{AnyChannel, Channel};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
@ -325,30 +325,52 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let ch = self.tx_dma.as_mut().unwrap();
|
||||
let transfer = unsafe {
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
self.inner.regs().dmacr().modify(|reg| {
|
||||
reg.set_txdmae(true);
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
|
||||
crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
|
||||
};
|
||||
transfer.await;
|
||||
tx_transfer.await;
|
||||
|
||||
let p = self.inner.regs();
|
||||
unsafe {
|
||||
while p.sr().read().bsy() {}
|
||||
|
||||
// clear RX FIFO contents to prevent stale reads
|
||||
while p.sr().read().rne() {
|
||||
let _: u16 = p.dr().read().data();
|
||||
}
|
||||
// clear RX overrun interrupt
|
||||
p.icr().write(|w| w.set_roric(true));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
let ch = self.rx_dma.as_mut().unwrap();
|
||||
let transfer = unsafe {
|
||||
self.inner.regs().dmacr().modify(|reg| {
|
||||
unsafe {
|
||||
self.inner.regs().dmacr().write(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
});
|
||||
reg.set_txdmae(true);
|
||||
})
|
||||
};
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ)
|
||||
crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ)
|
||||
};
|
||||
transfer.await;
|
||||
let rx_ch = self.rx_dma.as_mut().unwrap();
|
||||
let rx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ)
|
||||
};
|
||||
join(tx_transfer, rx_transfer).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -364,20 +386,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
|
||||
let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr);
|
||||
let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr);
|
||||
assert_eq!(from_len, to_len);
|
||||
unsafe {
|
||||
self.inner.regs().dmacr().write(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_txdmae(true);
|
||||
})
|
||||
};
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
self.inner.regs().dmacr().modify(|reg| {
|
||||
reg.set_txdmae(true);
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
|
||||
};
|
||||
let rx_ch = self.rx_dma.as_mut().unwrap();
|
||||
let rx_transfer = unsafe {
|
||||
self.inner.regs().dmacr().modify(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ)
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::slice;
|
||||
use core::sync::atomic::Ordering;
|
||||
@ -6,10 +7,9 @@ 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 futures::future::poll_fn;
|
||||
use futures::Future;
|
||||
use embassy_usb::driver::{
|
||||
self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::{pac, peripherals, Peripheral, RegExt};
|
||||
@ -205,8 +205,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)| {
|
||||
@ -255,7 +255,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);
|
||||
@ -263,7 +263,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);
|
||||
@ -430,14 +430,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| {
|
||||
@ -475,14 +475,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]
|
||||
@ -493,8 +493,8 @@ impl Dir for In {
|
||||
|
||||
pub enum Out {}
|
||||
impl Dir for Out {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::Out
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -34,6 +34,7 @@ flavors = [
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
|
@ -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,8 +1,8 @@
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
@ -429,7 +429,7 @@ where
|
||||
}
|
||||
});
|
||||
|
||||
let (_, result) = futures::future::join(dma_read, result).await;
|
||||
let (_, result) = embassy_futures::join::join(dma_read, result).await;
|
||||
|
||||
unsafe { Self::toggle(false) };
|
||||
|
||||
@ -537,7 +537,7 @@ where
|
||||
|
||||
unsafe { Self::toggle(true) };
|
||||
|
||||
let (_, result) = futures::future::join(dma_result, result).await;
|
||||
let (_, result) = embassy_futures::join::join(dma_result, result).await;
|
||||
|
||||
unsafe { Self::toggle(false) };
|
||||
|
||||
|
@ -29,7 +29,7 @@ use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::cmp;
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{AtomicUsize, Ordering};
|
||||
@ -6,7 +7,6 @@ use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::dma::NoDma;
|
||||
use crate::gpio::sealed::AFType;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
// This must go FIRST so that all the other modules see its macros.
|
||||
pub mod fmt;
|
||||
|
@ -1,10 +1,10 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::{pac, peripherals, Peripheral};
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::default::Default;
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR};
|
||||
|
||||
use crate::dma::NoDma;
|
||||
|
@ -3,9 +3,9 @@
|
||||
use core::ptr;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
use futures::future::join;
|
||||
|
||||
use self::sealed::WordSize;
|
||||
use crate::dma::{slice_ptr_parts, NoDma, Transfer};
|
||||
|
@ -1,17 +1,16 @@
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::Poll;
|
||||
|
||||
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 futures::future::poll_fn;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State<'d, T: BasicInstance>(StateStorage<StateInner<'d, T>>);
|
||||
impl<'d, T: BasicInstance> State<'d, T> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::task::Poll;
|
||||
@ -8,10 +9,9 @@ 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 futures::future::poll_fn;
|
||||
use futures::Future;
|
||||
use embassy_usb::driver::{
|
||||
self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
use pac::common::{Reg, RW};
|
||||
use pac::usb::vals::{EpType, Stat};
|
||||
|
||||
@ -280,8 +280,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)
|
||||
});
|
||||
@ -294,7 +294,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;
|
||||
|
||||
@ -313,7 +313,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
assert!(!ep.used_in);
|
||||
ep.used_in = true;
|
||||
|
||||
@ -505,7 +505,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() {
|
||||
@ -524,7 +524,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() {
|
||||
@ -550,8 +550,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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +561,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,
|
||||
@ -577,7 +577,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,
|
||||
@ -617,14 +617,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]
|
||||
@ -635,8 +635,8 @@ impl Dir for In {
|
||||
|
||||
pub enum Out {}
|
||||
impl Dir for Out {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::Out
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
@ -2,11 +2,10 @@
|
||||
//!
|
||||
//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.
|
||||
use core::cell::{RefCell, UnsafeCell};
|
||||
use core::future::poll_fn;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::poll_fn;
|
||||
|
||||
use crate::blocking_mutex::raw::RawMutex;
|
||||
use crate::blocking_mutex::Mutex as BlockingMutex;
|
||||
use crate::waitqueue::WakerRegistration;
|
||||
@ -111,6 +110,22 @@ where
|
||||
|
||||
Ok(MutexGuard { mutex: self })
|
||||
}
|
||||
|
||||
/// Consumes this mutex, returning the underlying data.
|
||||
pub fn into_inner(self) -> T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
self.inner.into_inner()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the Mutex mutably, no actual locking needs to
|
||||
/// take place -- the mutable borrow statically guarantees no locks exist.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Async mutex guard.
|
||||
|
@ -1,9 +1,11 @@
|
||||
//! A synchronization primitive for passing the latest value to a task.
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::cell::Cell;
|
||||
use core::future::{poll_fn, Future};
|
||||
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,38 +72,46 @@ 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()) => Poll::Pending,
|
||||
State::Waiting(_) => panic!("waker overflow"),
|
||||
State::Signaled(_) => match mem::replace(state, State::None) {
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => {
|
||||
cell.set(State::Waiting(w));
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) => {
|
||||
cell.set(State::Waiting(cx.waker().clone()));
|
||||
w.wake();
|
||||
Poll::Pending
|
||||
}
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Future that completes when this Signal has been signaled.
|
||||
pub fn wait(&self) -> impl Future<Output = T> + '_ {
|
||||
futures_util::future::poll_fn(move |cx| self.poll_wait(cx))
|
||||
poll_fn(move |cx| self.poll_wait(cx))
|
||||
}
|
||||
|
||||
/// 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,5 +1,5 @@
|
||||
#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![warn(missing_docs)]
|
||||
|
@ -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 }
|
||||
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"
|
||||
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,19 +1,13 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![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;
|
||||
@ -269,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
|
||||
@ -293,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,
|
||||
@ -317,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,24 +1,16 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![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;
|
||||
@ -205,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,
|
||||
@ -438,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))]
|
||||
|
@ -1,27 +1,27 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
// 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.
|
||||
///
|
||||
@ -248,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))]
|
||||
|
@ -1,7 +1,6 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_boot_nrf::FirmwareUpdater;
|
||||
|
@ -1,7 +1,6 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
|
@ -21,7 +21,7 @@ fn main() -> ! {
|
||||
|
||||
let mut bl = BootLoader::default();
|
||||
let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new(
|
||||
&mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5),
|
||||
WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5),
|
||||
)));
|
||||
unsafe { bl.load(start) }
|
||||
}
|
||||
|
@ -20,10 +20,9 @@ fn main() -> ! {
|
||||
*/
|
||||
|
||||
let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default();
|
||||
let mut flash = Flash::unlock(p.FLASH);
|
||||
let start = bl.prepare(&mut SingleFlashConfig::new(
|
||||
&mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash),
|
||||
));
|
||||
let flash = Flash::unlock(p.FLASH);
|
||||
let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash);
|
||||
let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
|
||||
core::mem::drop(flash);
|
||||
unsafe { bl.load(start) }
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
@ -46,7 +47,7 @@ async fn run2() {
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run3() {
|
||||
futures::future::poll_fn(|cx| {
|
||||
poll_fn(|cx| {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::<()>::Pending
|
||||
})
|
||||
|
@ -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"
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use defmt::{info, unwrap};
|
||||
@ -26,7 +27,7 @@ async fn run2() {
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run3() {
|
||||
futures::future::poll_fn(|cx| {
|
||||
poll_fn(|cx| {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::<()>::Pending
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::mem;
|
||||
@ -16,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 _};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::mem;
|
||||
@ -8,15 +7,16 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
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 futures::future::join;
|
||||
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -67,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,
|
||||
@ -78,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 {
|
||||
|
@ -1,19 +1,18 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::mem;
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
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 futures::future::join;
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -60,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,
|
||||
|
@ -1,18 +1,17 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::mem;
|
||||
|
||||
use defmt::{info, panic};
|
||||
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 futures::future::join;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::mem;
|
||||
@ -9,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 _};
|
||||
|
||||
|
@ -8,11 +8,10 @@ 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", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] }
|
||||
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"
|
||||
defmt-rtt = "0.3"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
@ -14,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 _};
|
||||
|
@ -1,16 +1,15 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::{info, panic};
|
||||
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 futures::future::join;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -9,7 +9,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", "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"
|
||||
defmt-rtt = "0.3"
|
||||
|
@ -4,15 +4,15 @@
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
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 futures::future::join;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -9,8 +9,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", "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"
|
||||
defmt-rtt = "0.3"
|
||||
|
@ -4,15 +4,15 @@
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
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 futures::future::join;
|
||||
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() {
|
||||
|
@ -3,7 +3,6 @@
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![allow(dead_code)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
|
@ -11,10 +11,8 @@ 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"
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
@ -16,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;
|
||||
|
@ -1,18 +1,17 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
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 futures::future::join;
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
@ -56,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,
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
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 futures::future::join;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
|
@ -2,7 +2,6 @@
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![allow(dead_code)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
|
@ -2,7 +2,6 @@
|
||||
#![no_main]
|
||||
#![macro_use]
|
||||
#![allow(dead_code)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
@ -13,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 _};
|
||||
|
||||
@ -65,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(());
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Before upgrading check that everything is available on all tier1 targets here:
|
||||
# https://rust-lang.github.io/rustup-components-history
|
||||
[toolchain]
|
||||
channel = "nightly-2022-08-16"
|
||||
channel = "nightly-2022-09-22"
|
||||
components = [ "rust-src", "rustfmt" ]
|
||||
targets = [
|
||||
"thumbv7em-none-eabi",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user