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