diff options
| author | klownfish <[email protected]> | 2024-09-24 20:46:04 +0200 |
|---|---|---|
| committer | klownfish <[email protected]> | 2024-09-24 20:46:04 +0200 |
| commit | 60347976b2e56eb8484e10d4aec5fa12534457f7 (patch) | |
| tree | fe1af22314ae008f2a93f34e1bb2b4129d4d85da | |
| parent | fe868fc1948472666b6d8386a3191a074468a34e (diff) | |
| parent | 0ede8479dc4c6a58cfab0a5d4df41c0592405971 (diff) | |
Merge remote-tracking branch 'origin/main' into u5_adc
36 files changed, 1602 insertions, 247 deletions
diff --git a/.gitignore b/.gitignore index 1c221e876..a0b5d6a70 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -5,3 +5,4 @@ Cargo.lock | |||
| 5 | third_party | 5 | third_party |
| 6 | /Cargo.toml | 6 | /Cargo.toml |
| 7 | out/ | 7 | out/ |
| 8 | .zed | ||
diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc index d8d50040b..53f85d995 100644 --- a/docs/pages/bootloader.adoc +++ b/docs/pages/bootloader.adoc | |||
| @@ -88,8 +88,7 @@ Then, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware | |||
| 88 | 88 | ||
| 89 | [source, bash] | 89 | [source, bash] |
| 90 | ---- | 90 | ---- |
| 91 | shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt | 91 | shasum -a 512 -b $FIRMWARE_DIR/myfirmware | head -c128 | xxd -p -r > $SECRETS_DIR/message.txt |
| 92 | cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt | ||
| 93 | signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig | 92 | signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig |
| 94 | cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed | 93 | cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed |
| 95 | tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed | 94 | tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed |
diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index 61d61b96e..5bffdc5ea 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs | |||
| @@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; | |||
| 5 | use embassy_sync::blocking_mutex::Mutex; | 5 | use embassy_sync::blocking_mutex::Mutex; |
| 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; | 6 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; |
| 7 | 7 | ||
| 8 | use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; | 8 | use crate::{State, DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; |
| 9 | 9 | ||
| 10 | /// Errors returned by bootloader | 10 | /// Errors returned by bootloader |
| 11 | #[derive(PartialEq, Eq, Debug)] | 11 | #[derive(PartialEq, Eq, Debug)] |
| @@ -276,7 +276,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 276 | self.state.erase(0, self.state.capacity() as u32)?; | 276 | self.state.erase(0, self.state.capacity() as u32)?; |
| 277 | 277 | ||
| 278 | // Set magic | 278 | // Set magic |
| 279 | state_word.fill(BOOT_MAGIC); | 279 | state_word.fill(REVERT_MAGIC); |
| 280 | self.state.write(0, state_word)?; | 280 | self.state.write(0, state_word)?; |
| 281 | } | 281 | } |
| 282 | } | 282 | } |
| @@ -411,6 +411,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S | |||
| 411 | Ok(State::Swap) | 411 | Ok(State::Swap) |
| 412 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { | 412 | } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) { |
| 413 | Ok(State::DfuDetach) | 413 | Ok(State::DfuDetach) |
| 414 | } else if !state_word.iter().any(|&b| b != REVERT_MAGIC) { | ||
| 415 | Ok(State::Revert) | ||
| 414 | } else { | 416 | } else { |
| 415 | Ok(State::Boot) | 417 | Ok(State::Boot) |
| 416 | } | 418 | } |
diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 86b441592..d9d15b004 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs | |||
| @@ -289,7 +289,8 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 289 | 289 | ||
| 290 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 290 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 291 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 291 | async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 292 | if self.get_state().await? == State::Boot { | 292 | let state = self.get_state().await?; |
| 293 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 293 | Ok(()) | 294 | Ok(()) |
| 294 | } else { | 295 | } else { |
| 295 | Err(FirmwareUpdaterError::BadState) | 296 | Err(FirmwareUpdaterError::BadState) |
| @@ -303,12 +304,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { | |||
| 303 | /// `mark_booted`. | 304 | /// `mark_booted`. |
| 304 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 305 | pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 305 | self.state.read(0, &mut self.aligned).await?; | 306 | self.state.read(0, &mut self.aligned).await?; |
| 306 | 307 | Ok(State::from(&self.aligned)) | |
| 307 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 308 | Ok(State::Swap) | ||
| 309 | } else { | ||
| 310 | Ok(State::Boot) | ||
| 311 | } | ||
| 312 | } | 308 | } |
| 313 | 309 | ||
| 314 | /// Mark to trigger firmware swap on next boot. | 310 | /// Mark to trigger firmware swap on next boot. |
diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index d3c723456..08062b0d0 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs | |||
| @@ -324,7 +324,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 324 | 324 | ||
| 325 | // Make sure we are running a booted firmware to avoid reverting to a bad state. | 325 | // Make sure we are running a booted firmware to avoid reverting to a bad state. |
| 326 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { | 326 | fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { |
| 327 | if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { | 327 | let state = self.get_state()?; |
| 328 | if state == State::Boot || state == State::DfuDetach || state == State::Revert { | ||
| 328 | Ok(()) | 329 | Ok(()) |
| 329 | } else { | 330 | } else { |
| 330 | Err(FirmwareUpdaterError::BadState) | 331 | Err(FirmwareUpdaterError::BadState) |
| @@ -338,14 +339,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { | |||
| 338 | /// `mark_booted`. | 339 | /// `mark_booted`. |
| 339 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { | 340 | pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> { |
| 340 | self.state.read(0, &mut self.aligned)?; | 341 | self.state.read(0, &mut self.aligned)?; |
| 341 | 342 | Ok(State::from(&self.aligned)) | |
| 342 | if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 343 | Ok(State::Swap) | ||
| 344 | } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 345 | Ok(State::DfuDetach) | ||
| 346 | } else { | ||
| 347 | Ok(State::Boot) | ||
| 348 | } | ||
| 349 | } | 343 | } |
| 350 | 344 | ||
| 351 | /// Mark to trigger firmware swap on next boot. | 345 | /// Mark to trigger firmware swap on next boot. |
diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 8849055e8..e2c4cf771 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs | |||
| @@ -25,6 +25,7 @@ pub use firmware_updater::{ | |||
| 25 | FirmwareUpdaterError, | 25 | FirmwareUpdaterError, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | pub(crate) const REVERT_MAGIC: u8 = 0xC0; | ||
| 28 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; | 29 | pub(crate) const BOOT_MAGIC: u8 = 0xD0; |
| 29 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; | 30 | pub(crate) const SWAP_MAGIC: u8 = 0xF0; |
| 30 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; | 31 | pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; |
| @@ -37,10 +38,30 @@ pub enum State { | |||
| 37 | Boot, | 38 | Boot, |
| 38 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. | 39 | /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. |
| 39 | Swap, | 40 | Swap, |
| 41 | /// Bootloader has reverted the active partition with the dfu partition and will attempt boot. | ||
| 42 | Revert, | ||
| 40 | /// Application has received a request to reboot into DFU mode to apply an update. | 43 | /// Application has received a request to reboot into DFU mode to apply an update. |
| 41 | DfuDetach, | 44 | DfuDetach, |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 47 | impl<T> From<T> for State | ||
| 48 | where | ||
| 49 | T: AsRef<[u8]>, | ||
| 50 | { | ||
| 51 | fn from(magic: T) -> State { | ||
| 52 | let magic = magic.as_ref(); | ||
| 53 | if !magic.iter().any(|&b| b != SWAP_MAGIC) { | ||
| 54 | State::Swap | ||
| 55 | } else if !magic.iter().any(|&b| b != REVERT_MAGIC) { | ||
| 56 | State::Revert | ||
| 57 | } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) { | ||
| 58 | State::DfuDetach | ||
| 59 | } else { | ||
| 60 | State::Boot | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 44 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. | 65 | /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. |
| 45 | #[repr(align(32))] | 66 | #[repr(align(32))] |
| 46 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); | 67 | pub struct AlignedBuffer<const N: usize>(pub [u8; N]); |
| @@ -157,6 +178,9 @@ mod tests { | |||
| 157 | // Running again should cause a revert | 178 | // Running again should cause a revert |
| 158 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); | 179 | assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); |
| 159 | 180 | ||
| 181 | // Next time we know it was reverted | ||
| 182 | assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap()); | ||
| 183 | |||
| 160 | let mut read_buf = [0; FIRMWARE_SIZE]; | 184 | let mut read_buf = [0; FIRMWARE_SIZE]; |
| 161 | flash.active().read(0, &mut read_buf).unwrap(); | 185 | flash.active().read(0, &mut read_buf).unwrap(); |
| 162 | assert_eq!(ORIGINAL, read_buf); | 186 | assert_eq!(ORIGINAL, read_buf); |
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index c8cea8503..b1838a425 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs | |||
| @@ -120,7 +120,7 @@ impl<'a> Control<'a> { | |||
| 120 | pwd: unwrap!(String::try_from(password)), | 120 | pwd: unwrap!(String::try_from(password)), |
| 121 | bssid: String::new(), | 121 | bssid: String::new(), |
| 122 | listen_interval: 3, | 122 | listen_interval: 3, |
| 123 | is_wpa3_supported: false, | 123 | is_wpa3_supported: true, |
| 124 | }; | 124 | }; |
| 125 | ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); | 125 | ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); |
| 126 | self.state_ch.set_link_state(LinkState::Up); | 126 | self.state_ch.set_link_state(LinkState::Up); |
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index c78578bf1..f05e2a70a 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs | |||
| @@ -137,7 +137,7 @@ where | |||
| 137 | let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); | 137 | let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); |
| 138 | let state_ch = ch_runner.state_runner(); | 138 | let state_ch = ch_runner.state_runner(); |
| 139 | 139 | ||
| 140 | let mut runner = Runner { | 140 | let runner = Runner { |
| 141 | ch: ch_runner, | 141 | ch: ch_runner, |
| 142 | state_ch, | 142 | state_ch, |
| 143 | shared: &state.shared, | 143 | shared: &state.shared, |
| @@ -148,7 +148,6 @@ where | |||
| 148 | spi, | 148 | spi, |
| 149 | heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, | 149 | heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, |
| 150 | }; | 150 | }; |
| 151 | runner.init().await; | ||
| 152 | 151 | ||
| 153 | (device, Control::new(state_ch, &state.shared), runner) | 152 | (device, Control::new(state_ch, &state.shared), runner) |
| 154 | } | 153 | } |
| @@ -174,8 +173,6 @@ where | |||
| 174 | IN: InputPin + Wait, | 173 | IN: InputPin + Wait, |
| 175 | OUT: OutputPin, | 174 | OUT: OutputPin, |
| 176 | { | 175 | { |
| 177 | async fn init(&mut self) {} | ||
| 178 | |||
| 179 | /// Run the packet processing. | 176 | /// Run the packet processing. |
| 180 | pub async fn run(mut self) -> ! { | 177 | pub async fn run(mut self) -> ! { |
| 181 | debug!("resetting..."); | 178 | debug!("resetting..."); |
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ef53fb905..a7b7efa87 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -260,7 +260,10 @@ pub struct Stack<'d> { | |||
| 260 | pub(crate) struct Inner { | 260 | pub(crate) struct Inner { |
| 261 | pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased. | 261 | pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased. |
| 262 | pub(crate) iface: Interface, | 262 | pub(crate) iface: Interface, |
| 263 | /// Waker used for triggering polls. | ||
| 263 | pub(crate) waker: WakerRegistration, | 264 | pub(crate) waker: WakerRegistration, |
| 265 | /// Waker used for waiting for link up or config up. | ||
| 266 | state_waker: WakerRegistration, | ||
| 264 | hardware_address: HardwareAddress, | 267 | hardware_address: HardwareAddress, |
| 265 | next_local_port: u16, | 268 | next_local_port: u16, |
| 266 | link_up: bool, | 269 | link_up: bool, |
| @@ -270,7 +273,6 @@ pub(crate) struct Inner { | |||
| 270 | static_v6: Option<StaticConfigV6>, | 273 | static_v6: Option<StaticConfigV6>, |
| 271 | #[cfg(feature = "dhcpv4")] | 274 | #[cfg(feature = "dhcpv4")] |
| 272 | dhcp_socket: Option<SocketHandle>, | 275 | dhcp_socket: Option<SocketHandle>, |
| 273 | config_waker: WakerRegistration, | ||
| 274 | #[cfg(feature = "dns")] | 276 | #[cfg(feature = "dns")] |
| 275 | dns_socket: SocketHandle, | 277 | dns_socket: SocketHandle, |
| 276 | #[cfg(feature = "dns")] | 278 | #[cfg(feature = "dns")] |
| @@ -326,6 +328,7 @@ pub fn new<'d, D: Driver, const SOCK: usize>( | |||
| 326 | sockets, | 328 | sockets, |
| 327 | iface, | 329 | iface, |
| 328 | waker: WakerRegistration::new(), | 330 | waker: WakerRegistration::new(), |
| 331 | state_waker: WakerRegistration::new(), | ||
| 329 | next_local_port, | 332 | next_local_port, |
| 330 | hardware_address, | 333 | hardware_address, |
| 331 | link_up: false, | 334 | link_up: false, |
| @@ -335,7 +338,6 @@ pub fn new<'d, D: Driver, const SOCK: usize>( | |||
| 335 | static_v6: None, | 338 | static_v6: None, |
| 336 | #[cfg(feature = "dhcpv4")] | 339 | #[cfg(feature = "dhcpv4")] |
| 337 | dhcp_socket: None, | 340 | dhcp_socket: None, |
| 338 | config_waker: WakerRegistration::new(), | ||
| 339 | #[cfg(feature = "dns")] | 341 | #[cfg(feature = "dns")] |
| 340 | dns_socket, | 342 | dns_socket, |
| 341 | #[cfg(feature = "dns")] | 343 | #[cfg(feature = "dns")] |
| @@ -421,10 +423,20 @@ impl<'d> Stack<'d> { | |||
| 421 | v4_up || v6_up | 423 | v4_up || v6_up |
| 422 | } | 424 | } |
| 423 | 425 | ||
| 426 | /// Wait for the network device to obtain a link signal. | ||
| 427 | pub async fn wait_link_up(&self) { | ||
| 428 | self.wait(|| self.is_link_up()).await | ||
| 429 | } | ||
| 430 | |||
| 431 | /// Wait for the network device to lose link signal. | ||
| 432 | pub async fn wait_link_down(&self) { | ||
| 433 | self.wait(|| !self.is_link_up()).await | ||
| 434 | } | ||
| 435 | |||
| 424 | /// Wait for the network stack to obtain a valid IP configuration. | 436 | /// Wait for the network stack to obtain a valid IP configuration. |
| 425 | /// | 437 | /// |
| 426 | /// ## Notes: | 438 | /// ## Notes: |
| 427 | /// - Ensure [`Stack::run`] has been called before using this function. | 439 | /// - Ensure [`Runner::run`] has been started before using this function. |
| 428 | /// | 440 | /// |
| 429 | /// - This function may never return (e.g. if no configuration is obtained through DHCP). | 441 | /// - This function may never return (e.g. if no configuration is obtained through DHCP). |
| 430 | /// The caller is supposed to handle a timeout for this case. | 442 | /// The caller is supposed to handle a timeout for this case. |
| @@ -451,13 +463,17 @@ impl<'d> Stack<'d> { | |||
| 451 | /// // ... | 463 | /// // ... |
| 452 | /// ``` | 464 | /// ``` |
| 453 | pub async fn wait_config_up(&self) { | 465 | pub async fn wait_config_up(&self) { |
| 454 | // If the config is up already, we can return immediately. | 466 | self.wait(|| self.is_config_up()).await |
| 455 | if self.is_config_up() { | 467 | } |
| 456 | return; | ||
| 457 | } | ||
| 458 | 468 | ||
| 459 | poll_fn(|cx| { | 469 | /// Wait for the network stack to lose a valid IP configuration. |
| 460 | if self.is_config_up() { | 470 | pub async fn wait_config_down(&self) { |
| 471 | self.wait(|| !self.is_config_up()).await | ||
| 472 | } | ||
| 473 | |||
| 474 | fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future<Output = ()> + 'a { | ||
| 475 | poll_fn(move |cx| { | ||
| 476 | if predicate() { | ||
| 461 | Poll::Ready(()) | 477 | Poll::Ready(()) |
| 462 | } else { | 478 | } else { |
| 463 | // If the config is not up, we register a waker that is woken up | 479 | // If the config is not up, we register a waker that is woken up |
| @@ -465,13 +481,12 @@ impl<'d> Stack<'d> { | |||
| 465 | trace!("Waiting for config up"); | 481 | trace!("Waiting for config up"); |
| 466 | 482 | ||
| 467 | self.with_mut(|i| { | 483 | self.with_mut(|i| { |
| 468 | i.config_waker.register(cx.waker()); | 484 | i.state_waker.register(cx.waker()); |
| 469 | }); | 485 | }); |
| 470 | 486 | ||
| 471 | Poll::Pending | 487 | Poll::Pending |
| 472 | } | 488 | } |
| 473 | }) | 489 | }) |
| 474 | .await; | ||
| 475 | } | 490 | } |
| 476 | 491 | ||
| 477 | /// Get the current IPv4 configuration. | 492 | /// Get the current IPv4 configuration. |
| @@ -775,7 +790,7 @@ impl Inner { | |||
| 775 | .update_servers(&dns_servers[..count]); | 790 | .update_servers(&dns_servers[..count]); |
| 776 | } | 791 | } |
| 777 | 792 | ||
| 778 | self.config_waker.wake(); | 793 | self.state_waker.wake(); |
| 779 | } | 794 | } |
| 780 | 795 | ||
| 781 | fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) { | 796 | fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) { |
| @@ -813,6 +828,7 @@ impl Inner { | |||
| 813 | // Print when changed | 828 | // Print when changed |
| 814 | if old_link_up != self.link_up { | 829 | if old_link_up != self.link_up { |
| 815 | info!("link_up = {:?}", self.link_up); | 830 | info!("link_up = {:?}", self.link_up); |
| 831 | self.state_waker.wake(); | ||
| 816 | } | 832 | } |
| 817 | 833 | ||
| 818 | #[allow(unused_mut)] | 834 | #[allow(unused_mut)] |
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ed146844c..f229a5acd 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -847,6 +847,10 @@ impl<'d, T: GpinPin> Gpin<'d, T> { | |||
| 847 | into_ref!(gpin); | 847 | into_ref!(gpin); |
| 848 | 848 | ||
| 849 | gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); | 849 | gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); |
| 850 | #[cfg(feature = "_rp235x")] | ||
| 851 | gpin.pad_ctrl().write(|w| { | ||
| 852 | w.set_iso(false); | ||
| 853 | }); | ||
| 850 | 854 | ||
| 851 | Gpin { | 855 | Gpin { |
| 852 | gpin: gpin.map_into(), | 856 | gpin: gpin.map_into(), |
| @@ -861,6 +865,7 @@ impl<'d, T: GpinPin> Gpin<'d, T> { | |||
| 861 | 865 | ||
| 862 | impl<'d, T: GpinPin> Drop for Gpin<'d, T> { | 866 | impl<'d, T: GpinPin> Drop for Gpin<'d, T> { |
| 863 | fn drop(&mut self) { | 867 | fn drop(&mut self) { |
| 868 | self.gpin.pad_ctrl().write(|_| {}); | ||
| 864 | self.gpin | 869 | self.gpin |
| 865 | .gpio() | 870 | .gpio() |
| 866 | .ctrl() | 871 | .ctrl() |
| @@ -921,11 +926,15 @@ pub struct Gpout<'d, T: GpoutPin> { | |||
| 921 | } | 926 | } |
| 922 | 927 | ||
| 923 | impl<'d, T: GpoutPin> Gpout<'d, T> { | 928 | impl<'d, T: GpoutPin> Gpout<'d, T> { |
| 924 | /// Create new general purpose cloud output. | 929 | /// Create new general purpose clock output. |
| 925 | pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self { | 930 | pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self { |
| 926 | into_ref!(gpout); | 931 | into_ref!(gpout); |
| 927 | 932 | ||
| 928 | gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08)); | 933 | gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08)); |
| 934 | #[cfg(feature = "_rp235x")] | ||
| 935 | gpout.pad_ctrl().write(|w| { | ||
| 936 | w.set_iso(false); | ||
| 937 | }); | ||
| 929 | 938 | ||
| 930 | Self { gpout } | 939 | Self { gpout } |
| 931 | } | 940 | } |
| @@ -1005,6 +1014,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { | |||
| 1005 | impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { | 1014 | impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { |
| 1006 | fn drop(&mut self) { | 1015 | fn drop(&mut self) { |
| 1007 | self.disable(); | 1016 | self.disable(); |
| 1017 | self.gpout.pad_ctrl().write(|_| {}); | ||
| 1008 | self.gpout | 1018 | self.gpout |
| 1009 | .gpio() | 1019 | .gpio() |
| 1010 | .ctrl() | 1020 | .ctrl() |
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 31397172c..cb54375e4 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -16,9 +16,9 @@ use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; | |||
| 16 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 16 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 17 | 17 | ||
| 18 | #[cfg(any(feature = "rp2040", feature = "rp235xa"))] | 18 | #[cfg(any(feature = "rp2040", feature = "rp235xa"))] |
| 19 | const BANK0_PIN_COUNT: usize = 30; | 19 | pub(crate) const BANK0_PIN_COUNT: usize = 30; |
| 20 | #[cfg(feature = "rp235xb")] | 20 | #[cfg(feature = "rp235xb")] |
| 21 | const BANK0_PIN_COUNT: usize = 48; | 21 | pub(crate) const BANK0_PIN_COUNT: usize = 48; |
| 22 | 22 | ||
| 23 | static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; | 23 | static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; |
| 24 | #[cfg(feature = "qspi-as-gpio")] | 24 | #[cfg(feature = "qspi-as-gpio")] |
| @@ -603,7 +603,7 @@ impl<'d> Flex<'d> { | |||
| 603 | 603 | ||
| 604 | #[inline] | 604 | #[inline] |
| 605 | fn bit(&self) -> u32 { | 605 | fn bit(&self) -> u32 { |
| 606 | 1 << self.pin.pin() | 606 | 1 << (self.pin.pin() % 32) |
| 607 | } | 607 | } |
| 608 | 608 | ||
| 609 | /// Set the pin's pull. | 609 | /// Set the pin's pull. |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c357c14c2..d402cf793 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -42,6 +42,8 @@ pub mod rtc; | |||
| 42 | pub mod spi; | 42 | pub mod spi; |
| 43 | #[cfg(feature = "time-driver")] | 43 | #[cfg(feature = "time-driver")] |
| 44 | pub mod time_driver; | 44 | pub mod time_driver; |
| 45 | #[cfg(feature = "_rp235x")] | ||
| 46 | pub mod trng; | ||
| 45 | pub mod uart; | 47 | pub mod uart; |
| 46 | pub mod usb; | 48 | pub mod usb; |
| 47 | pub mod watchdog; | 49 | pub mod watchdog; |
| @@ -402,6 +404,8 @@ embassy_hal_internal::peripherals! { | |||
| 402 | 404 | ||
| 403 | WATCHDOG, | 405 | WATCHDOG, |
| 404 | BOOTSEL, | 406 | BOOTSEL, |
| 407 | |||
| 408 | TRNG | ||
| 405 | } | 409 | } |
| 406 | 410 | ||
| 407 | #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] | 411 | #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] |
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 68b1d6849..72aa8f104 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs | |||
| @@ -5,7 +5,7 @@ use core::pin::Pin as FuturePin; | |||
| 5 | use core::sync::atomic::{compiler_fence, Ordering}; | 5 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use atomic_polyfill::{AtomicU32, AtomicU8}; | 8 | use atomic_polyfill::{AtomicU64, AtomicU8}; |
| 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | use fixed::types::extra::U8; | 11 | use fixed::types::extra::U8; |
| @@ -731,6 +731,8 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 731 | w.set_autopull(config.shift_out.auto_fill); | 731 | w.set_autopull(config.shift_out.auto_fill); |
| 732 | w.set_autopush(config.shift_in.auto_fill); | 732 | w.set_autopush(config.shift_in.auto_fill); |
| 733 | }); | 733 | }); |
| 734 | |||
| 735 | #[cfg(feature = "rp2040")] | ||
| 734 | sm.pinctrl().write(|w| { | 736 | sm.pinctrl().write(|w| { |
| 735 | w.set_sideset_count(config.pins.sideset_count); | 737 | w.set_sideset_count(config.pins.sideset_count); |
| 736 | w.set_set_count(config.pins.set_count); | 738 | w.set_set_count(config.pins.set_count); |
| @@ -740,6 +742,52 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { | |||
| 740 | w.set_set_base(config.pins.set_base); | 742 | w.set_set_base(config.pins.set_base); |
| 741 | w.set_out_base(config.pins.out_base); | 743 | w.set_out_base(config.pins.out_base); |
| 742 | }); | 744 | }); |
| 745 | |||
| 746 | #[cfg(feature = "_rp235x")] | ||
| 747 | { | ||
| 748 | let mut low_ok = true; | ||
| 749 | let mut high_ok = true; | ||
| 750 | |||
| 751 | let in_pins = config.pins.in_base..config.pins.in_base + config.in_count; | ||
| 752 | let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count; | ||
| 753 | let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count; | ||
| 754 | let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count; | ||
| 755 | |||
| 756 | for pin_range in [in_pins, side_pins, set_pins, out_pins] { | ||
| 757 | for pin in pin_range { | ||
| 758 | low_ok &= pin < 32; | ||
| 759 | high_ok &= pin >= 16; | ||
| 760 | } | ||
| 761 | } | ||
| 762 | |||
| 763 | if !low_ok && !high_ok { | ||
| 764 | panic!( | ||
| 765 | "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}", | ||
| 766 | config.pins.in_base, | ||
| 767 | config.pins.in_base + config.in_count - 1, | ||
| 768 | config.pins.sideset_base, | ||
| 769 | config.pins.sideset_base + config.pins.sideset_count - 1, | ||
| 770 | config.pins.set_base, | ||
| 771 | config.pins.set_base + config.pins.set_count - 1, | ||
| 772 | config.pins.out_base, | ||
| 773 | config.pins.out_base + config.pins.out_count - 1, | ||
| 774 | ) | ||
| 775 | } | ||
| 776 | let shift = if low_ok { 0 } else { 16 }; | ||
| 777 | |||
| 778 | sm.pinctrl().write(|w| { | ||
| 779 | w.set_sideset_count(config.pins.sideset_count); | ||
| 780 | w.set_set_count(config.pins.set_count); | ||
| 781 | w.set_out_count(config.pins.out_count); | ||
| 782 | w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default()); | ||
| 783 | w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default()); | ||
| 784 | w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default()); | ||
| 785 | w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default()); | ||
| 786 | }); | ||
| 787 | |||
| 788 | PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16)); | ||
| 789 | } | ||
| 790 | |||
| 743 | if let Some(origin) = config.origin { | 791 | if let Some(origin) = config.origin { |
| 744 | unsafe { instr::exec_jmp(self, origin) } | 792 | unsafe { instr::exec_jmp(self, origin) } |
| 745 | } | 793 | } |
| @@ -1006,6 +1054,10 @@ impl<'d, PIO: Instance> Common<'d, PIO> { | |||
| 1006 | pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { | 1054 | pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { |
| 1007 | into_ref!(pin); | 1055 | into_ref!(pin); |
| 1008 | pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); | 1056 | pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); |
| 1057 | #[cfg(feature = "_rp235x")] | ||
| 1058 | pin.pad_ctrl().modify(|w| { | ||
| 1059 | w.set_iso(false); | ||
| 1060 | }); | ||
| 1009 | // we can be relaxed about this because we're &mut here and nothing is cached | 1061 | // we can be relaxed about this because we're &mut here and nothing is cached |
| 1010 | PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); | 1062 | PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); |
| 1011 | Pin { | 1063 | Pin { |
| @@ -1187,7 +1239,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { | |||
| 1187 | // other way. | 1239 | // other way. |
| 1188 | pub struct State { | 1240 | pub struct State { |
| 1189 | users: AtomicU8, | 1241 | users: AtomicU8, |
| 1190 | used_pins: AtomicU32, | 1242 | used_pins: AtomicU64, |
| 1191 | } | 1243 | } |
| 1192 | 1244 | ||
| 1193 | fn on_pio_drop<PIO: Instance>() { | 1245 | fn on_pio_drop<PIO: Instance>() { |
| @@ -1195,8 +1247,7 @@ fn on_pio_drop<PIO: Instance>() { | |||
| 1195 | if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { | 1247 | if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { |
| 1196 | let used_pins = state.used_pins.load(Ordering::Relaxed); | 1248 | let used_pins = state.used_pins.load(Ordering::Relaxed); |
| 1197 | let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; | 1249 | let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; |
| 1198 | // we only have 30 pins. don't test the other two since gpio() asserts. | 1250 | for i in 0..crate::gpio::BANK0_PIN_COUNT { |
| 1199 | for i in 0..30 { | ||
| 1200 | if used_pins & (1 << i) != 0 { | 1251 | if used_pins & (1 << i) != 0 { |
| 1201 | pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); | 1252 | pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); |
| 1202 | } | 1253 | } |
| @@ -1221,7 +1272,7 @@ trait SealedInstance { | |||
| 1221 | fn state() -> &'static State { | 1272 | fn state() -> &'static State { |
| 1222 | static STATE: State = State { | 1273 | static STATE: State = State { |
| 1223 | users: AtomicU8::new(0), | 1274 | users: AtomicU8::new(0), |
| 1224 | used_pins: AtomicU32::new(0), | 1275 | used_pins: AtomicU64::new(0), |
| 1225 | }; | 1276 | }; |
| 1226 | 1277 | ||
| 1227 | &STATE | 1278 | &STATE |
diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs new file mode 100644 index 000000000..9f2f33c4b --- /dev/null +++ b/embassy-rp/src/trng.rs | |||
| @@ -0,0 +1,405 @@ | |||
| 1 | //! True Random Number Generator (TRNG) driver. | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::ops::Not; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | use embassy_hal_internal::Peripheral; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | use rand_core::Error; | ||
| 11 | |||
| 12 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 13 | use crate::peripherals::TRNG; | ||
| 14 | use crate::{interrupt, pac}; | ||
| 15 | |||
| 16 | trait SealedInstance { | ||
| 17 | fn regs() -> pac::trng::Trng; | ||
| 18 | fn waker() -> &'static AtomicWaker; | ||
| 19 | } | ||
| 20 | |||
| 21 | /// TRNG peripheral instance. | ||
| 22 | #[allow(private_bounds)] | ||
| 23 | pub trait Instance: SealedInstance { | ||
| 24 | /// Interrupt for this peripheral. | ||
| 25 | type Interrupt: Interrupt; | ||
| 26 | } | ||
| 27 | |||
| 28 | impl SealedInstance for TRNG { | ||
| 29 | fn regs() -> rp_pac::trng::Trng { | ||
| 30 | pac::TRNG | ||
| 31 | } | ||
| 32 | |||
| 33 | fn waker() -> &'static AtomicWaker { | ||
| 34 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 35 | &WAKER | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl Instance for TRNG { | ||
| 40 | type Interrupt = interrupt::typelevel::TRNG_IRQ; | ||
| 41 | } | ||
| 42 | |||
| 43 | #[derive(Copy, Clone, Debug)] | ||
| 44 | #[allow(missing_docs)] | ||
| 45 | /// TRNG ROSC Inverter chain length options. | ||
| 46 | pub enum InverterChainLength { | ||
| 47 | None = 0, | ||
| 48 | One, | ||
| 49 | Two, | ||
| 50 | Three, | ||
| 51 | Four, | ||
| 52 | } | ||
| 53 | |||
| 54 | impl From<InverterChainLength> for u8 { | ||
| 55 | fn from(value: InverterChainLength) -> Self { | ||
| 56 | value as u8 | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Configuration for the TRNG. | ||
| 61 | /// | ||
| 62 | /// - Three built in entropy checks | ||
| 63 | /// - ROSC frequency controlled by selecting one of ROSC chain lengths | ||
| 64 | /// - Sample period in terms of system clock ticks | ||
| 65 | /// | ||
| 66 | /// | ||
| 67 | /// Default configuration is based on the following from documentation: | ||
| 68 | /// | ||
| 69 | /// ---- | ||
| 70 | /// | ||
| 71 | /// RP2350 Datasheet 12.12.2 | ||
| 72 | /// | ||
| 73 | /// ... | ||
| 74 | /// | ||
| 75 | /// When configuring the TRNG block, consider the following principles: | ||
| 76 | /// • As average generation time increases, result quality increases and failed entropy checks decrease. | ||
| 77 | /// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and | ||
| 78 | /// failed entropy checks. | ||
| 79 | /// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or | ||
| 80 | /// 1 and sample count settings of 20-25. | ||
| 81 | /// | ||
| 82 | /// --- | ||
| 83 | /// | ||
| 84 | /// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly | ||
| 85 | /// by setting the sample period to 0. Random data collected this way is then passed through | ||
| 86 | /// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!). | ||
| 87 | #[non_exhaustive] | ||
| 88 | #[derive(Copy, Clone, Debug)] | ||
| 89 | pub struct Config { | ||
| 90 | /// Bypass TRNG autocorrelation test | ||
| 91 | pub disable_autocorrelation_test: bool, | ||
| 92 | /// Bypass CRNGT test | ||
| 93 | pub disable_crngt_test: bool, | ||
| 94 | /// When set, the Von-Neuman balancer is bypassed (including the | ||
| 95 | /// 32 consecutive bits test) | ||
| 96 | pub disable_von_neumann_balancer: bool, | ||
| 97 | /// Sets the number of rng_clk cycles between two consecutive | ||
| 98 | /// ring oscillator samples. | ||
| 99 | /// Note: If the von Neumann decorrelator is bypassed, the minimum value for | ||
| 100 | /// sample counter must not be less than seventeen | ||
| 101 | pub sample_count: u32, | ||
| 102 | /// Selects the number of inverters (out of four possible | ||
| 103 | /// selections) in the ring oscillator (the entropy source). Higher values select | ||
| 104 | /// longer inverter chain lengths. | ||
| 105 | pub inverter_chain_length: InverterChainLength, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl Default for Config { | ||
| 109 | fn default() -> Self { | ||
| 110 | Config { | ||
| 111 | disable_autocorrelation_test: true, | ||
| 112 | disable_crngt_test: true, | ||
| 113 | disable_von_neumann_balancer: true, | ||
| 114 | sample_count: 25, | ||
| 115 | inverter_chain_length: InverterChainLength::One, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// True Random Number Generator Driver for RP2350 | ||
| 121 | /// | ||
| 122 | /// This driver provides async and blocking options. | ||
| 123 | /// | ||
| 124 | /// See [Config] for configuration details. | ||
| 125 | /// | ||
| 126 | /// Usage example: | ||
| 127 | /// ```no_run | ||
| 128 | /// use embassy_executor::Spawner; | ||
| 129 | /// use embassy_rp::trng::Trng; | ||
| 130 | /// use embassy_rp::peripherals::TRNG; | ||
| 131 | /// use embassy_rp::bind_interrupts; | ||
| 132 | /// | ||
| 133 | /// bind_interrupts!(struct Irqs { | ||
| 134 | /// TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | ||
| 135 | /// }); | ||
| 136 | /// | ||
| 137 | /// #[embassy_executor::main] | ||
| 138 | /// async fn main(spawner: Spawner) { | ||
| 139 | /// let peripherals = embassy_rp::init(Default::default()); | ||
| 140 | /// let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); | ||
| 141 | /// | ||
| 142 | /// let mut randomness = [0u8; 58]; | ||
| 143 | /// loop { | ||
| 144 | /// trng.fill_bytes(&mut randomness).await; | ||
| 145 | /// assert_ne!(randomness, [0u8; 58]); | ||
| 146 | /// } | ||
| 147 | ///} | ||
| 148 | /// ``` | ||
| 149 | pub struct Trng<'d, T: Instance> { | ||
| 150 | phantom: PhantomData<&'d mut T>, | ||
| 151 | } | ||
| 152 | |||
| 153 | /// 12.12.1. Overview | ||
| 154 | /// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of | ||
| 155 | /// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC). | ||
| 156 | const TRNG_BLOCK_SIZE_BITS: usize = 192; | ||
| 157 | const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8; | ||
| 158 | |||
| 159 | impl<'d, T: Instance> Trng<'d, T> { | ||
| 160 | /// Create a new TRNG driver. | ||
| 161 | pub fn new( | ||
| 162 | _trng: impl Peripheral<P = T> + 'd, | ||
| 163 | _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 164 | config: Config, | ||
| 165 | ) -> Self { | ||
| 166 | let regs = T::regs(); | ||
| 167 | |||
| 168 | regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); | ||
| 169 | |||
| 170 | let trng_config_register = regs.trng_config(); | ||
| 171 | trng_config_register.write(|w| { | ||
| 172 | w.set_rnd_src_sel(config.inverter_chain_length.clone().into()); | ||
| 173 | }); | ||
| 174 | |||
| 175 | let sample_count_register = regs.sample_cnt1(); | ||
| 176 | sample_count_register.write(|w| { | ||
| 177 | *w = config.sample_count; | ||
| 178 | }); | ||
| 179 | |||
| 180 | let debug_control_register = regs.trng_debug_control(); | ||
| 181 | debug_control_register.write(|w| { | ||
| 182 | w.set_auto_correlate_bypass(config.disable_autocorrelation_test); | ||
| 183 | w.set_trng_crngt_bypass(config.disable_crngt_test); | ||
| 184 | w.set_vnc_bypass(config.disable_von_neumann_balancer) | ||
| 185 | }); | ||
| 186 | |||
| 187 | Trng { phantom: PhantomData } | ||
| 188 | } | ||
| 189 | |||
| 190 | fn start_rng(&self) { | ||
| 191 | let regs = T::regs(); | ||
| 192 | let source_enable_register = regs.rnd_source_enable(); | ||
| 193 | // Enable TRNG ROSC | ||
| 194 | source_enable_register.write(|w| w.set_rnd_src_en(true)); | ||
| 195 | } | ||
| 196 | |||
| 197 | fn stop_rng(&self) { | ||
| 198 | let regs = T::regs(); | ||
| 199 | let source_enable_register = regs.rnd_source_enable(); | ||
| 200 | source_enable_register.write(|w| w.set_rnd_src_en(false)); | ||
| 201 | let reset_bits_counter_register = regs.rst_bits_counter(); | ||
| 202 | reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); | ||
| 203 | } | ||
| 204 | |||
| 205 | fn enable_irq(&self) { | ||
| 206 | unsafe { T::Interrupt::enable() } | ||
| 207 | } | ||
| 208 | |||
| 209 | fn disable_irq(&self) { | ||
| 210 | T::Interrupt::disable(); | ||
| 211 | } | ||
| 212 | |||
| 213 | fn blocking_wait_for_successful_generation(&self) { | ||
| 214 | let regs = T::regs(); | ||
| 215 | |||
| 216 | let trng_busy_register = regs.trng_busy(); | ||
| 217 | let trng_valid_register = regs.trng_valid(); | ||
| 218 | |||
| 219 | let mut success = false; | ||
| 220 | while success.not() { | ||
| 221 | while trng_busy_register.read().trng_busy() {} | ||
| 222 | if trng_valid_register.read().ehr_valid().not() { | ||
| 223 | if regs.rng_isr().read().autocorr_err() { | ||
| 224 | regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); | ||
| 225 | } else { | ||
| 226 | panic!("RNG not busy, but ehr is not valid!") | ||
| 227 | } | ||
| 228 | } else { | ||
| 229 | success = true | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { | ||
| 235 | let regs = T::regs(); | ||
| 236 | let ehr_data_regs = [ | ||
| 237 | regs.ehr_data0(), | ||
| 238 | regs.ehr_data1(), | ||
| 239 | regs.ehr_data2(), | ||
| 240 | regs.ehr_data3(), | ||
| 241 | regs.ehr_data4(), | ||
| 242 | regs.ehr_data5(), | ||
| 243 | ]; | ||
| 244 | |||
| 245 | for (i, reg) in ehr_data_regs.iter().enumerate() { | ||
| 246 | buffer[i * 4..i * 4 + 4].copy_from_slice(®.read().to_ne_bytes()); | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { | ||
| 251 | self.blocking_wait_for_successful_generation(); | ||
| 252 | self.read_ehr_registers_into_array(buffer); | ||
| 253 | } | ||
| 254 | |||
| 255 | /// Fill the buffer with random bytes, async version. | ||
| 256 | pub async fn fill_bytes(&mut self, destination: &mut [u8]) { | ||
| 257 | if destination.is_empty() { | ||
| 258 | return; // Nothing to fill | ||
| 259 | } | ||
| 260 | |||
| 261 | self.start_rng(); | ||
| 262 | self.enable_irq(); | ||
| 263 | |||
| 264 | let mut bytes_transferred = 0usize; | ||
| 265 | let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; | ||
| 266 | |||
| 267 | let regs = T::regs(); | ||
| 268 | |||
| 269 | let trng_busy_register = regs.trng_busy(); | ||
| 270 | let trng_valid_register = regs.trng_valid(); | ||
| 271 | |||
| 272 | let waker = T::waker(); | ||
| 273 | |||
| 274 | let destination_length = destination.len(); | ||
| 275 | |||
| 276 | poll_fn(|context| { | ||
| 277 | waker.register(context.waker()); | ||
| 278 | if bytes_transferred == destination_length { | ||
| 279 | self.stop_rng(); | ||
| 280 | self.disable_irq(); | ||
| 281 | Poll::Ready(()) | ||
| 282 | } else { | ||
| 283 | if trng_busy_register.read().trng_busy() { | ||
| 284 | Poll::Pending | ||
| 285 | } else { | ||
| 286 | if trng_valid_register.read().ehr_valid().not() { | ||
| 287 | panic!("RNG not busy, but ehr is not valid!") | ||
| 288 | } | ||
| 289 | self.read_ehr_registers_into_array(&mut buffer); | ||
| 290 | let remaining = destination_length - bytes_transferred; | ||
| 291 | if remaining > TRNG_BLOCK_SIZE_BYTES { | ||
| 292 | destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES] | ||
| 293 | .copy_from_slice(&buffer); | ||
| 294 | bytes_transferred += TRNG_BLOCK_SIZE_BYTES | ||
| 295 | } else { | ||
| 296 | destination[bytes_transferred..bytes_transferred + remaining] | ||
| 297 | .copy_from_slice(&buffer[0..remaining]); | ||
| 298 | bytes_transferred += remaining | ||
| 299 | } | ||
| 300 | if bytes_transferred == destination_length { | ||
| 301 | self.stop_rng(); | ||
| 302 | self.disable_irq(); | ||
| 303 | Poll::Ready(()) | ||
| 304 | } else { | ||
| 305 | Poll::Pending | ||
| 306 | } | ||
| 307 | } | ||
| 308 | } | ||
| 309 | }) | ||
| 310 | .await | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Fill the buffer with random bytes, blocking version. | ||
| 314 | pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) { | ||
| 315 | if destination.is_empty() { | ||
| 316 | return; // Nothing to fill | ||
| 317 | } | ||
| 318 | self.start_rng(); | ||
| 319 | |||
| 320 | let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; | ||
| 321 | |||
| 322 | for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) { | ||
| 323 | self.blocking_wait_for_successful_generation(); | ||
| 324 | self.blocking_read_ehr_registers_into_array(&mut buffer); | ||
| 325 | chunk.copy_from_slice(&buffer[..chunk.len()]) | ||
| 326 | } | ||
| 327 | self.stop_rng() | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Return a random u32, blocking. | ||
| 331 | pub fn blocking_next_u32(&mut self) -> u32 { | ||
| 332 | let regs = T::regs(); | ||
| 333 | self.start_rng(); | ||
| 334 | self.blocking_wait_for_successful_generation(); | ||
| 335 | // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to | ||
| 336 | // clear all of the result registers. | ||
| 337 | let result = regs.ehr_data5().read(); | ||
| 338 | self.stop_rng(); | ||
| 339 | result | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Return a random u64, blocking. | ||
| 343 | pub fn blocking_next_u64(&mut self) -> u64 { | ||
| 344 | let regs = T::regs(); | ||
| 345 | self.start_rng(); | ||
| 346 | self.blocking_wait_for_successful_generation(); | ||
| 347 | |||
| 348 | let low = regs.ehr_data4().read() as u64; | ||
| 349 | // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to | ||
| 350 | // clear all of the result registers. | ||
| 351 | let result = (regs.ehr_data5().read() as u64) << 32 | low; | ||
| 352 | self.stop_rng(); | ||
| 353 | result | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { | ||
| 358 | fn next_u32(&mut self) -> u32 { | ||
| 359 | self.blocking_next_u32() | ||
| 360 | } | ||
| 361 | |||
| 362 | fn next_u64(&mut self) -> u64 { | ||
| 363 | self.blocking_next_u64() | ||
| 364 | } | ||
| 365 | |||
| 366 | fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 367 | self.blocking_fill_bytes(dest) | ||
| 368 | } | ||
| 369 | |||
| 370 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
| 371 | self.blocking_fill_bytes(dest); | ||
| 372 | Ok(()) | ||
| 373 | } | ||
| 374 | } | ||
| 375 | /// TRNG interrupt handler. | ||
| 376 | pub struct InterruptHandler<T: Instance> { | ||
| 377 | _trng: PhantomData<T>, | ||
| 378 | } | ||
| 379 | |||
| 380 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 381 | unsafe fn on_interrupt() { | ||
| 382 | let regs = T::regs(); | ||
| 383 | let isr = regs.rng_isr().read(); | ||
| 384 | // Clear ehr bit | ||
| 385 | regs.rng_icr().write(|w| { | ||
| 386 | w.set_ehr_valid(true); | ||
| 387 | }); | ||
| 388 | if isr.ehr_valid() { | ||
| 389 | T::waker().wake(); | ||
| 390 | } else { | ||
| 391 | // 12.12.5. List of Registers | ||
| 392 | // ... | ||
| 393 | // TRNG: RNG_ISR Register | ||
| 394 | // ... | ||
| 395 | // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. | ||
| 396 | // When set, RNG ceases functioning until next reset | ||
| 397 | if isr.autocorr_err() { | ||
| 398 | warn!("TRNG Autocorrect error! Resetting TRNG"); | ||
| 399 | regs.trng_sw_reset().write(|w| { | ||
| 400 | w.set_trng_sw_reset(true); | ||
| 401 | }); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3c6484c96..8fc8da006 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -47,7 +47,7 @@ embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } | |||
| 47 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } | 47 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } |
| 48 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 48 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 49 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | 49 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } |
| 50 | embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" } | 50 | embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } |
| 51 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } | 51 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } |
| 52 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } | 52 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } |
| 53 | embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } | 53 | embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } |
| @@ -72,7 +72,7 @@ rand_core = "0.6.3" | |||
| 72 | sdio-host = "0.5.0" | 72 | sdio-host = "0.5.0" |
| 73 | critical-section = "1.1" | 73 | critical-section = "1.1" |
| 74 | #stm32-metapac = { version = "15" } | 74 | #stm32-metapac = { version = "15" } |
| 75 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" } | 75 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f" } |
| 76 | 76 | ||
| 77 | vcell = "0.1.3" | 77 | vcell = "0.1.3" |
| 78 | nb = "1.0.0" | 78 | nb = "1.0.0" |
| @@ -99,7 +99,7 @@ proc-macro2 = "1.0.36" | |||
| 99 | quote = "1.0.15" | 99 | quote = "1.0.15" |
| 100 | 100 | ||
| 101 | #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} | 101 | #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} |
| 102 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] } | 102 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f", default-features = false, features = ["metadata"] } |
| 103 | 103 | ||
| 104 | [features] | 104 | [features] |
| 105 | default = ["rt"] | 105 | default = ["rt"] |
| @@ -129,7 +129,7 @@ unstable-pac = [] | |||
| 129 | #! ## Time | 129 | #! ## Time |
| 130 | 130 | ||
| 131 | ## Enables additional driver features that depend on embassy-time | 131 | ## Enables additional driver features that depend on embassy-time |
| 132 | time = ["dep:embassy-time"] | 132 | time = ["dep:embassy-time", "embassy-embedded-hal/time"] |
| 133 | 133 | ||
| 134 | # Features starting with `_` are for internal use only. They're not intended | 134 | # Features starting with `_` are for internal use only. They're not intended |
| 135 | # to be enabled by other crates, and are not covered by semver guarantees. | 135 | # to be enabled by other crates, and are not covered by semver guarantees. |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index e4cd001e6..fe04dd815 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -55,7 +55,7 @@ fn main() { | |||
| 55 | let mut singletons: Vec<String> = Vec::new(); | 55 | let mut singletons: Vec<String> = Vec::new(); |
| 56 | for p in METADATA.peripherals { | 56 | for p in METADATA.peripherals { |
| 57 | if let Some(r) = &p.registers { | 57 | if let Some(r) = &p.registers { |
| 58 | if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" { | 58 | if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" { |
| 59 | // TODO: should we emit this for all peripherals? if so, we will need a list of all | 59 | // TODO: should we emit this for all peripherals? if so, we will need a list of all |
| 60 | // possible peripherals across all chips, so that we can declare the configs | 60 | // possible peripherals across all chips, so that we can declare the configs |
| 61 | // (replacing the hard-coded list of `peri_*` cfgs below) | 61 | // (replacing the hard-coded list of `peri_*` cfgs below) |
| @@ -111,6 +111,8 @@ fn main() { | |||
| 111 | "peri_sai4", | 111 | "peri_sai4", |
| 112 | "peri_ucpd1", | 112 | "peri_ucpd1", |
| 113 | "peri_ucpd2", | 113 | "peri_ucpd2", |
| 114 | "peri_usb_otg_fs", | ||
| 115 | "peri_usb_otg_hs", | ||
| 114 | ]); | 116 | ]); |
| 115 | cfgs.declare_all(&["mco", "mco1", "mco2"]); | 117 | cfgs.declare_all(&["mco", "mco1", "mco2"]); |
| 116 | 118 | ||
diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 07e3dddad..1c7abfcb2 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs | |||
| @@ -200,7 +200,7 @@ impl Registers { | |||
| 200 | if header_reg.rtr().bit() { | 200 | if header_reg.rtr().bit() { |
| 201 | F::new_remote(id, len as usize) | 201 | F::new_remote(id, len as usize) |
| 202 | } else { | 202 | } else { |
| 203 | F::new(id, &data) | 203 | F::new(id, &data[0..(len as usize)]) |
| 204 | } | 204 | } |
| 205 | } else { | 205 | } else { |
| 206 | // Abort request failed because the frame was already sent (or being sent) on | 206 | // Abort request failed because the frame was already sent (or being sent) on |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index df041c4e9..d10b5554f 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -493,6 +493,26 @@ impl AnyChannel { | |||
| 493 | } | 493 | } |
| 494 | } | 494 | } |
| 495 | 495 | ||
| 496 | fn request_pause(&self) { | ||
| 497 | let info = self.info(); | ||
| 498 | match self.info().dma { | ||
| 499 | #[cfg(dma)] | ||
| 500 | DmaInfo::Dma(r) => { | ||
| 501 | // Disable the channel without overwriting the existing configuration | ||
| 502 | r.st(info.num).cr().modify(|w| { | ||
| 503 | w.set_en(false); | ||
| 504 | }); | ||
| 505 | } | ||
| 506 | #[cfg(bdma)] | ||
| 507 | DmaInfo::Bdma(r) => { | ||
| 508 | // Disable the channel without overwriting the existing configuration | ||
| 509 | r.ch(info.num).cr().modify(|w| { | ||
| 510 | w.set_en(false); | ||
| 511 | }); | ||
| 512 | } | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 496 | fn is_running(&self) -> bool { | 516 | fn is_running(&self) -> bool { |
| 497 | let info = self.info(); | 517 | let info = self.info(); |
| 498 | match self.info().dma { | 518 | match self.info().dma { |
| @@ -667,12 +687,22 @@ impl<'a> Transfer<'a> { | |||
| 667 | } | 687 | } |
| 668 | 688 | ||
| 669 | /// Request the transfer to stop. | 689 | /// Request the transfer to stop. |
| 690 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 691 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 670 | /// | 692 | /// |
| 671 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 693 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 672 | pub fn request_stop(&mut self) { | 694 | pub fn request_stop(&mut self) { |
| 673 | self.channel.request_stop() | 695 | self.channel.request_stop() |
| 674 | } | 696 | } |
| 675 | 697 | ||
| 698 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 699 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 700 | /// | ||
| 701 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 702 | pub fn request_pause(&mut self) { | ||
| 703 | self.channel.request_pause() | ||
| 704 | } | ||
| 705 | |||
| 676 | /// Return whether this transfer is still running. | 706 | /// Return whether this transfer is still running. |
| 677 | /// | 707 | /// |
| 678 | /// If this returns `false`, it can be because either the transfer finished, or | 708 | /// If this returns `false`, it can be because either the transfer finished, or |
| @@ -846,13 +876,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 846 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | 876 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); |
| 847 | } | 877 | } |
| 848 | 878 | ||
| 849 | /// Request DMA to stop. | 879 | /// Request the DMA to stop. |
| 880 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 881 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 850 | /// | 882 | /// |
| 851 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 883 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 852 | pub fn request_stop(&mut self) { | 884 | pub fn request_stop(&mut self) { |
| 853 | self.channel.request_stop() | 885 | self.channel.request_stop() |
| 854 | } | 886 | } |
| 855 | 887 | ||
| 888 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 889 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 890 | /// | ||
| 891 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 892 | pub fn request_pause(&mut self) { | ||
| 893 | self.channel.request_pause() | ||
| 894 | } | ||
| 895 | |||
| 856 | /// Return whether DMA is still running. | 896 | /// Return whether DMA is still running. |
| 857 | /// | 897 | /// |
| 858 | /// If this returns `false`, it can be because either the transfer finished, or | 898 | /// If this returns `false`, it can be because either the transfer finished, or |
| @@ -977,13 +1017,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 977 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | 1017 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); |
| 978 | } | 1018 | } |
| 979 | 1019 | ||
| 980 | /// Request DMA to stop. | 1020 | /// Request the DMA to stop. |
| 1021 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 1022 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 981 | /// | 1023 | /// |
| 982 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 1024 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 983 | pub fn request_stop(&mut self) { | 1025 | pub fn request_stop(&mut self) { |
| 984 | self.channel.request_stop() | 1026 | self.channel.request_stop() |
| 985 | } | 1027 | } |
| 986 | 1028 | ||
| 1029 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 1030 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 1031 | /// | ||
| 1032 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 1033 | pub fn request_pause(&mut self) { | ||
| 1034 | self.channel.request_pause() | ||
| 1035 | } | ||
| 1036 | |||
| 987 | /// Return whether DMA is still running. | 1037 | /// Return whether DMA is still running. |
| 988 | /// | 1038 | /// |
| 989 | /// If this returns `false`, it can be because either the transfer finished, or | 1039 | /// If this returns `false`, it can be because either the transfer finished, or |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index f9d66ca86..a877bb8d4 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs | |||
| @@ -216,7 +216,10 @@ impl<'a> Transfer<'a> { | |||
| 216 | data_size: WordSize, | 216 | data_size: WordSize, |
| 217 | _options: TransferOptions, | 217 | _options: TransferOptions, |
| 218 | ) -> Self { | 218 | ) -> Self { |
| 219 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | 219 | // BNDT is specified as bytes, not as number of transfers. |
| 220 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { | ||
| 221 | panic!("DMA transfers may not be larger than 65535 bytes."); | ||
| 222 | }; | ||
| 220 | 223 | ||
| 221 | let info = channel.info(); | 224 | let info = channel.info(); |
| 222 | let ch = info.dma.ch(info.num); | 225 | let ch = info.dma.ch(info.num); |
| @@ -226,9 +229,6 @@ impl<'a> Transfer<'a> { | |||
| 226 | 229 | ||
| 227 | let this = Self { channel }; | 230 | let this = Self { channel }; |
| 228 | 231 | ||
| 229 | #[cfg(dmamux)] | ||
| 230 | super::dmamux::configure_dmamux(&*this.channel, request); | ||
| 231 | |||
| 232 | ch.cr().write(|w| w.set_reset(true)); | 232 | ch.cr().write(|w| w.set_reset(true)); |
| 233 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs | 233 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs |
| 234 | ch.llr().write(|_| {}); // no linked list | 234 | ch.llr().write(|_| {}); // no linked list |
| @@ -245,10 +245,8 @@ impl<'a> Transfer<'a> { | |||
| 245 | }); | 245 | }); |
| 246 | w.set_reqsel(request); | 246 | w.set_reqsel(request); |
| 247 | }); | 247 | }); |
| 248 | ch.br1().write(|w| { | 248 | ch.tr3().write(|_| {}); // no address offsets. |
| 249 | // BNDT is specified as bytes, not as number of transfers. | 249 | ch.br1().write(|w| w.set_bndt(bndt)); |
| 250 | w.set_bndt((mem_len * data_size.bytes()) as u16) | ||
| 251 | }); | ||
| 252 | 250 | ||
| 253 | match dir { | 251 | match dir { |
| 254 | Dir::MemoryToPeripheral => { | 252 | Dir::MemoryToPeripheral => { |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index bfe8a60d6..6442176da 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -177,6 +177,20 @@ pub unsafe trait PHY { | |||
| 177 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; | 177 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | ||
| 181 | /// Directly expose the SMI interface used by the Ethernet driver. | ||
| 182 | /// | ||
| 183 | /// This can be used to for example configure special PHY registers for compliance testing. | ||
| 184 | /// | ||
| 185 | /// # Safety | ||
| 186 | /// | ||
| 187 | /// Revert any temporary PHY register changes such as to enable test modes before handing | ||
| 188 | /// the Ethernet device over to the networking stack otherwise things likely won't work. | ||
| 189 | pub unsafe fn station_management(&mut self) -> &mut impl StationManagement { | ||
| 190 | &mut self.station_management | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 180 | trait SealedInstance { | 194 | trait SealedInstance { |
| 181 | fn regs() -> crate::pac::eth::Eth; | 195 | fn regs() -> crate::pac::eth::Eth; |
| 182 | } | 196 | } |
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 376a0b454..55fe8ca9d 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -34,8 +34,10 @@ pub enum VoltageScale { | |||
| 34 | Scale2, | 34 | Scale2, |
| 35 | Scale3, | 35 | Scale3, |
| 36 | } | 36 | } |
| 37 | #[cfg(any(stm32h7rs))] | 37 | #[cfg(stm32h7rs)] |
| 38 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 38 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 39 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 40 | pub use crate::pac::rcc::vals::{Usbphycsel, Usbrefcksel}; | ||
| 39 | 41 | ||
| 40 | #[derive(Clone, Copy, Eq, PartialEq)] | 42 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 41 | pub enum HseMode { | 43 | pub enum HseMode { |
| @@ -557,6 +559,27 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 557 | 559 | ||
| 558 | let rtc = config.ls.init(); | 560 | let rtc = config.ls.init(); |
| 559 | 561 | ||
| 562 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 563 | let usb_refck = match config.mux.usbphycsel { | ||
| 564 | Usbphycsel::HSE => hse, | ||
| 565 | Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), | ||
| 566 | Usbphycsel::PLL3_Q => pll3.q, | ||
| 567 | _ => None, | ||
| 568 | }; | ||
| 569 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 570 | let usb_refck_sel = match usb_refck { | ||
| 571 | Some(clk_val) => match clk_val { | ||
| 572 | Hertz(16_000_000) => Usbrefcksel::MHZ16, | ||
| 573 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, | ||
| 574 | Hertz(20_000_000) => Usbrefcksel::MHZ20, | ||
| 575 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | ||
| 576 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | ||
| 577 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | ||
| 578 | _ => panic!("cannot select USBPHYC reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | ||
| 579 | }, | ||
| 580 | None => Usbrefcksel::MHZ24, | ||
| 581 | }; | ||
| 582 | |||
| 560 | #[cfg(stm32h7)] | 583 | #[cfg(stm32h7)] |
| 561 | { | 584 | { |
| 562 | RCC.d1cfgr().modify(|w| { | 585 | RCC.d1cfgr().modify(|w| { |
| @@ -593,6 +616,11 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 593 | w.set_ppre4(config.apb4_pre); | 616 | w.set_ppre4(config.apb4_pre); |
| 594 | w.set_ppre5(config.apb5_pre); | 617 | w.set_ppre5(config.apb5_pre); |
| 595 | }); | 618 | }); |
| 619 | |||
| 620 | #[cfg(peri_usb_otg_hs)] | ||
| 621 | RCC.ahbperckselr().modify(|w| { | ||
| 622 | w.set_usbrefcksel(usb_refck_sel); | ||
| 623 | }); | ||
| 596 | } | 624 | } |
| 597 | #[cfg(stm32h5)] | 625 | #[cfg(stm32h5)] |
| 598 | { | 626 | { |
| @@ -698,7 +726,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 698 | #[cfg(stm32h7rs)] | 726 | #[cfg(stm32h7rs)] |
| 699 | clk48mohci: None, // TODO | 727 | clk48mohci: None, // TODO |
| 700 | #[cfg(stm32h7rs)] | 728 | #[cfg(stm32h7rs)] |
| 701 | usb: None, // TODO | 729 | hse_div_2: hse.map(|clk| clk / 2u32), |
| 730 | #[cfg(stm32h7rs)] | ||
| 731 | usb: Some(Hertz(48_000_000)), | ||
| 702 | ); | 732 | ); |
| 703 | } | 733 | } |
| 704 | 734 | ||
| @@ -769,7 +799,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | |||
| 769 | if num == 0 { | 799 | if num == 0 { |
| 770 | // on PLL1, DIVP must be even for most series. | 800 | // on PLL1, DIVP must be even for most series. |
| 771 | // The enum value is 1 less than the divider, so check it's odd. | 801 | // The enum value is 1 less than the divider, so check it's odd. |
| 772 | #[cfg(not(pwr_h7rm0468))] | 802 | #[cfg(not(any(pwr_h7rm0468, stm32h7rs)))] |
| 773 | assert!(div.to_bits() % 2 == 1); | 803 | assert!(div.to_bits() % 2 == 1); |
| 774 | #[cfg(pwr_h7rm0468)] | 804 | #[cfg(pwr_h7rm0468)] |
| 775 | assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0); | 805 | assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0); |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 20718147a..d034c028e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -311,51 +311,29 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 311 | } | 311 | } |
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself. | ||
| 314 | fn set_word_size(&mut self, word_size: word_impl::Config) { | 315 | fn set_word_size(&mut self, word_size: word_impl::Config) { |
| 315 | if self.current_word_size == word_size { | 316 | if self.current_word_size == word_size { |
| 316 | return; | 317 | return; |
| 317 | } | 318 | } |
| 318 | 319 | ||
| 320 | self.info.regs.cr1().modify(|w| { | ||
| 321 | w.set_spe(false); | ||
| 322 | }); | ||
| 323 | |||
| 319 | #[cfg(any(spi_v1, spi_f1))] | 324 | #[cfg(any(spi_v1, spi_f1))] |
| 320 | { | 325 | self.info.regs.cr1().modify(|reg| { |
| 321 | self.info.regs.cr1().modify(|reg| { | 326 | reg.set_dff(word_size); |
| 322 | reg.set_spe(false); | 327 | }); |
| 323 | reg.set_dff(word_size) | ||
| 324 | }); | ||
| 325 | self.info.regs.cr1().modify(|reg| { | ||
| 326 | reg.set_spe(true); | ||
| 327 | }); | ||
| 328 | } | ||
| 329 | #[cfg(spi_v2)] | 328 | #[cfg(spi_v2)] |
| 330 | { | 329 | self.info.regs.cr2().modify(|w| { |
| 331 | self.info.regs.cr1().modify(|w| { | 330 | w.set_frxth(word_size.1); |
| 332 | w.set_spe(false); | 331 | w.set_ds(word_size.0); |
| 333 | }); | 332 | }); |
| 334 | self.info.regs.cr2().modify(|w| { | ||
| 335 | w.set_frxth(word_size.1); | ||
| 336 | w.set_ds(word_size.0); | ||
| 337 | }); | ||
| 338 | self.info.regs.cr1().modify(|w| { | ||
| 339 | w.set_spe(true); | ||
| 340 | }); | ||
| 341 | } | ||
| 342 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 333 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 343 | { | 334 | self.info.regs.cfg1().modify(|w| { |
| 344 | self.info.regs.cr1().modify(|w| { | 335 | w.set_dsize(word_size); |
| 345 | w.set_csusp(true); | 336 | }); |
| 346 | }); | ||
| 347 | while self.info.regs.sr().read().eot() {} | ||
| 348 | self.info.regs.cr1().modify(|w| { | ||
| 349 | w.set_spe(false); | ||
| 350 | }); | ||
| 351 | self.info.regs.cfg1().modify(|w| { | ||
| 352 | w.set_dsize(word_size); | ||
| 353 | }); | ||
| 354 | self.info.regs.cr1().modify(|w| { | ||
| 355 | w.set_csusp(false); | ||
| 356 | w.set_spe(true); | ||
| 357 | }); | ||
| 358 | } | ||
| 359 | 337 | ||
| 360 | self.current_word_size = word_size; | 338 | self.current_word_size = word_size; |
| 361 | } | 339 | } |
| @@ -365,9 +343,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 365 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 343 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 366 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 344 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 367 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 345 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 346 | self.set_word_size(W::CONFIG); | ||
| 368 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 347 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 369 | flush_rx_fifo(self.info.regs); | 348 | flush_rx_fifo(self.info.regs); |
| 370 | self.set_word_size(W::CONFIG); | ||
| 371 | for word in words.iter() { | 349 | for word in words.iter() { |
| 372 | // this cannot use `transfer_word` because on SPIv2 and higher, | 350 | // this cannot use `transfer_word` because on SPIv2 and higher, |
| 373 | // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. | 351 | // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. |
| @@ -402,9 +380,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 402 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 380 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 403 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 381 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 404 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 382 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 383 | self.set_word_size(W::CONFIG); | ||
| 405 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 384 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 406 | flush_rx_fifo(self.info.regs); | 385 | flush_rx_fifo(self.info.regs); |
| 407 | self.set_word_size(W::CONFIG); | ||
| 408 | for word in words.iter_mut() { | 386 | for word in words.iter_mut() { |
| 409 | *word = transfer_word(self.info.regs, W::default())?; | 387 | *word = transfer_word(self.info.regs, W::default())?; |
| 410 | } | 388 | } |
| @@ -418,9 +396,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 418 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 396 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 419 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 397 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 420 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 398 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 399 | self.set_word_size(W::CONFIG); | ||
| 421 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 400 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 422 | flush_rx_fifo(self.info.regs); | 401 | flush_rx_fifo(self.info.regs); |
| 423 | self.set_word_size(W::CONFIG); | ||
| 424 | for word in words.iter_mut() { | 402 | for word in words.iter_mut() { |
| 425 | *word = transfer_word(self.info.regs, *word)?; | 403 | *word = transfer_word(self.info.regs, *word)?; |
| 426 | } | 404 | } |
| @@ -437,9 +415,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 437 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 415 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 438 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 416 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 439 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 417 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 418 | self.set_word_size(W::CONFIG); | ||
| 440 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 419 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 441 | flush_rx_fifo(self.info.regs); | 420 | flush_rx_fifo(self.info.regs); |
| 442 | self.set_word_size(W::CONFIG); | ||
| 443 | let len = read.len().max(write.len()); | 421 | let len = read.len().max(write.len()); |
| 444 | for i in 0..len { | 422 | for i in 0..len { |
| 445 | let wb = write.get(i).copied().unwrap_or_default(); | 423 | let wb = write.get(i).copied().unwrap_or_default(); |
| @@ -648,10 +626,10 @@ impl<'d> Spi<'d, Async> { | |||
| 648 | return Ok(()); | 626 | return Ok(()); |
| 649 | } | 627 | } |
| 650 | 628 | ||
| 651 | self.set_word_size(W::CONFIG); | ||
| 652 | self.info.regs.cr1().modify(|w| { | 629 | self.info.regs.cr1().modify(|w| { |
| 653 | w.set_spe(false); | 630 | w.set_spe(false); |
| 654 | }); | 631 | }); |
| 632 | self.set_word_size(W::CONFIG); | ||
| 655 | 633 | ||
| 656 | let tx_dst = self.info.regs.tx_ptr(); | 634 | let tx_dst = self.info.regs.tx_ptr(); |
| 657 | let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; | 635 | let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; |
| @@ -685,6 +663,8 @@ impl<'d> Spi<'d, Async> { | |||
| 685 | w.set_spe(false); | 663 | w.set_spe(false); |
| 686 | }); | 664 | }); |
| 687 | 665 | ||
| 666 | self.set_word_size(W::CONFIG); | ||
| 667 | |||
| 688 | let comm = regs.cfg2().modify(|w| { | 668 | let comm = regs.cfg2().modify(|w| { |
| 689 | let prev = w.comm(); | 669 | let prev = w.comm(); |
| 690 | w.set_comm(vals::Comm::RECEIVER); | 670 | w.set_comm(vals::Comm::RECEIVER); |
| @@ -707,7 +687,6 @@ impl<'d> Spi<'d, Async> { | |||
| 707 | let rx_src = regs.rx_ptr(); | 687 | let rx_src = regs.rx_ptr(); |
| 708 | 688 | ||
| 709 | for mut chunk in data.chunks_mut(u16::max_value().into()) { | 689 | for mut chunk in data.chunks_mut(u16::max_value().into()) { |
| 710 | self.set_word_size(W::CONFIG); | ||
| 711 | set_rxdmaen(regs, true); | 690 | set_rxdmaen(regs, true); |
| 712 | 691 | ||
| 713 | let tsize = chunk.len(); | 692 | let tsize = chunk.len(); |
| @@ -765,12 +744,12 @@ impl<'d> Spi<'d, Async> { | |||
| 765 | return Ok(()); | 744 | return Ok(()); |
| 766 | } | 745 | } |
| 767 | 746 | ||
| 768 | self.set_word_size(W::CONFIG); | ||
| 769 | |||
| 770 | self.info.regs.cr1().modify(|w| { | 747 | self.info.regs.cr1().modify(|w| { |
| 771 | w.set_spe(false); | 748 | w.set_spe(false); |
| 772 | }); | 749 | }); |
| 773 | 750 | ||
| 751 | self.set_word_size(W::CONFIG); | ||
| 752 | |||
| 774 | // SPIv3 clears rxfifo on SPE=0 | 753 | // SPIv3 clears rxfifo on SPE=0 |
| 775 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 754 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 776 | flush_rx_fifo(self.info.regs); | 755 | flush_rx_fifo(self.info.regs); |
| @@ -783,7 +762,7 @@ impl<'d> Spi<'d, Async> { | |||
| 783 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; | 762 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; |
| 784 | 763 | ||
| 785 | let tx_dst = self.info.regs.tx_ptr(); | 764 | let tx_dst = self.info.regs.tx_ptr(); |
| 786 | let clock_byte = 0x00u8; | 765 | let clock_byte = W::default(); |
| 787 | let tx_f = unsafe { | 766 | let tx_f = unsafe { |
| 788 | self.tx_dma | 767 | self.tx_dma |
| 789 | .as_mut() | 768 | .as_mut() |
| @@ -813,11 +792,12 @@ impl<'d> Spi<'d, Async> { | |||
| 813 | return Ok(()); | 792 | return Ok(()); |
| 814 | } | 793 | } |
| 815 | 794 | ||
| 816 | self.set_word_size(W::CONFIG); | ||
| 817 | self.info.regs.cr1().modify(|w| { | 795 | self.info.regs.cr1().modify(|w| { |
| 818 | w.set_spe(false); | 796 | w.set_spe(false); |
| 819 | }); | 797 | }); |
| 820 | 798 | ||
| 799 | self.set_word_size(W::CONFIG); | ||
| 800 | |||
| 821 | // SPIv3 clears rxfifo on SPE=0 | 801 | // SPIv3 clears rxfifo on SPE=0 |
| 822 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 802 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 823 | flush_rx_fifo(self.info.regs); | 803 | flush_rx_fifo(self.info.regs); |
| @@ -1195,7 +1175,7 @@ trait SealedWord { | |||
| 1195 | 1175 | ||
| 1196 | /// Word sizes usable for SPI. | 1176 | /// Word sizes usable for SPI. |
| 1197 | #[allow(private_bounds)] | 1177 | #[allow(private_bounds)] |
| 1198 | pub trait Word: word::Word + SealedWord {} | 1178 | pub trait Word: word::Word + SealedWord + Default {} |
| 1199 | 1179 | ||
| 1200 | macro_rules! impl_word { | 1180 | macro_rules! impl_word { |
| 1201 | ($T:ty, $config:expr) => { | 1181 | ($T:ty, $config:expr) => { |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b0652046c..75834bf37 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -71,34 +71,19 @@ impl<'d> UartRx<'d, Async> { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | impl<'d> RingBufferedUartRx<'d> { | 73 | impl<'d> RingBufferedUartRx<'d> { |
| 74 | /// Clear the ring buffer and start receiving in the background | ||
| 75 | pub fn start(&mut self) -> Result<(), Error> { | ||
| 76 | // Clear the ring buffer so that it is ready to receive data | ||
| 77 | self.ring_buf.clear(); | ||
| 78 | |||
| 79 | self.setup_uart(); | ||
| 80 | |||
| 81 | Ok(()) | ||
| 82 | } | ||
| 83 | |||
| 84 | fn stop(&mut self, err: Error) -> Result<usize, Error> { | ||
| 85 | self.teardown_uart(); | ||
| 86 | |||
| 87 | Err(err) | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Reconfigure the driver | 74 | /// Reconfigure the driver |
| 91 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 75 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 92 | reconfigure(self.info, self.kernel_clock, config) | 76 | reconfigure(self.info, self.kernel_clock, config) |
| 93 | } | 77 | } |
| 94 | 78 | ||
| 95 | /// Start uart background receive | 79 | /// Configure and start the DMA backed UART receiver |
| 96 | fn setup_uart(&mut self) { | 80 | /// |
| 97 | // fence before starting DMA. | 81 | /// Note: This is also done automatically by [`read()`] if required. |
| 82 | pub fn start_uart(&mut self) { | ||
| 83 | // Clear the buffer so that it is ready to receive data | ||
| 98 | compiler_fence(Ordering::SeqCst); | 84 | compiler_fence(Ordering::SeqCst); |
| 99 | |||
| 100 | // start the dma controller | ||
| 101 | self.ring_buf.start(); | 85 | self.ring_buf.start(); |
| 86 | self.ring_buf.clear(); | ||
| 102 | 87 | ||
| 103 | let r = self.info.regs; | 88 | let r = self.info.regs; |
| 104 | // clear all interrupts and DMA Rx Request | 89 | // clear all interrupts and DMA Rx Request |
| @@ -118,9 +103,9 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 118 | }); | 103 | }); |
| 119 | } | 104 | } |
| 120 | 105 | ||
| 121 | /// Stop uart background receive | 106 | /// Stop DMA backed UART receiver |
| 122 | fn teardown_uart(&mut self) { | 107 | fn stop_uart(&mut self) { |
| 123 | self.ring_buf.request_stop(); | 108 | self.ring_buf.request_pause(); |
| 124 | 109 | ||
| 125 | let r = self.info.regs; | 110 | let r = self.info.regs; |
| 126 | // clear all interrupts and DMA Rx Request | 111 | // clear all interrupts and DMA Rx Request |
| @@ -153,13 +138,15 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 153 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | 138 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { |
| 154 | let r = self.info.regs; | 139 | let r = self.info.regs; |
| 155 | 140 | ||
| 156 | // Start background receive if it was not already started | 141 | // Start DMA and Uart if it was not already started, |
| 142 | // otherwise check for errors in status register. | ||
| 143 | let sr = clear_idle_flag(r); | ||
| 157 | if !r.cr3().read().dmar() { | 144 | if !r.cr3().read().dmar() { |
| 158 | self.start()?; | 145 | self.start_uart(); |
| 146 | } else { | ||
| 147 | check_for_errors(sr)?; | ||
| 159 | } | 148 | } |
| 160 | 149 | ||
| 161 | check_for_errors(clear_idle_flag(r))?; | ||
| 162 | |||
| 163 | loop { | 150 | loop { |
| 164 | match self.ring_buf.read(buf) { | 151 | match self.ring_buf.read(buf) { |
| 165 | Ok((0, _)) => {} | 152 | Ok((0, _)) => {} |
| @@ -167,14 +154,16 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 167 | return Ok(len); | 154 | return Ok(len); |
| 168 | } | 155 | } |
| 169 | Err(_) => { | 156 | Err(_) => { |
| 170 | return self.stop(Error::Overrun); | 157 | self.stop_uart(); |
| 158 | return Err(Error::Overrun); | ||
| 171 | } | 159 | } |
| 172 | } | 160 | } |
| 173 | 161 | ||
| 174 | match self.wait_for_data_or_idle().await { | 162 | match self.wait_for_data_or_idle().await { |
| 175 | Ok(_) => {} | 163 | Ok(_) => {} |
| 176 | Err(err) => { | 164 | Err(err) => { |
| 177 | return self.stop(err); | 165 | self.stop_uart(); |
| 166 | return Err(err); | ||
| 178 | } | 167 | } |
| 179 | } | 168 | } |
| 180 | } | 169 | } |
| @@ -228,7 +217,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 228 | 217 | ||
| 229 | impl Drop for RingBufferedUartRx<'_> { | 218 | impl Drop for RingBufferedUartRx<'_> { |
| 230 | fn drop(&mut self) { | 219 | fn drop(&mut self) { |
| 231 | self.teardown_uart(); | 220 | self.stop_uart(); |
| 232 | self.rx.as_ref().map(|x| x.set_as_disconnected()); | 221 | self.rx.as_ref().map(|x| x.set_as_disconnected()); |
| 233 | self.rts.as_ref().map(|x| x.set_as_disconnected()); | 222 | self.rts.as_ref().map(|x| x.set_as_disconnected()); |
| 234 | super::drop_tx_rx(self.info, self.state); | 223 | super::drop_tx_rx(self.info, self.state); |
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index ce9fe0a9b..a473285bf 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs | |||
| @@ -13,9 +13,19 @@ fn common_init<T: Instance>() { | |||
| 13 | // Check the USB clock is enabled and running at exactly 48 MHz. | 13 | // Check the USB clock is enabled and running at exactly 48 MHz. |
| 14 | // frequency() will panic if not enabled | 14 | // frequency() will panic if not enabled |
| 15 | let freq = T::frequency(); | 15 | let freq = T::frequency(); |
| 16 | |||
| 17 | // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally | ||
| 18 | #[cfg(stm32h7rs)] | ||
| 19 | if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { | ||
| 20 | panic!( | ||
| 21 | "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", | ||
| 22 | freq.0 | ||
| 23 | ) | ||
| 24 | } | ||
| 16 | // Check frequency is within the 0.25% tolerance allowed by the spec. | 25 | // Check frequency is within the 0.25% tolerance allowed by the spec. |
| 17 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user | 26 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user |
| 18 | // has tight clock restrictions due to something else (like audio). | 27 | // has tight clock restrictions due to something else (like audio). |
| 28 | #[cfg(not(stm32h7rs))] | ||
| 19 | if freq.0.abs_diff(48_000_000) > 120_000 { | 29 | if freq.0.abs_diff(48_000_000) > 120_000 { |
| 20 | panic!( | 30 | panic!( |
| 21 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", | 31 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", |
| @@ -48,6 +58,26 @@ fn common_init<T: Instance>() { | |||
| 48 | while !crate::pac::PWR.cr3().read().usb33rdy() {} | 58 | while !crate::pac::PWR.cr3().read().usb33rdy() {} |
| 49 | } | 59 | } |
| 50 | 60 | ||
| 61 | #[cfg(stm32h7rs)] | ||
| 62 | { | ||
| 63 | // If true, VDD33USB is generated by internal regulator from VDD50USB | ||
| 64 | // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) | ||
| 65 | // TODO: unhardcode | ||
| 66 | let internal_regulator = false; | ||
| 67 | |||
| 68 | // Enable USB power | ||
| 69 | critical_section::with(|_| { | ||
| 70 | crate::pac::PWR.csr2().modify(|w| { | ||
| 71 | w.set_usbregen(internal_regulator); | ||
| 72 | w.set_usb33den(true); | ||
| 73 | w.set_usbhsregen(true); | ||
| 74 | }) | ||
| 75 | }); | ||
| 76 | |||
| 77 | // Wait for USB power to stabilize | ||
| 78 | while !crate::pac::PWR.csr2().read().usb33rdy() {} | ||
| 79 | } | ||
| 80 | |||
| 51 | #[cfg(stm32u5)] | 81 | #[cfg(stm32u5)] |
| 52 | { | 82 | { |
| 53 | // Enable USB power | 83 | // Enable USB power |
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index e27b164e4..00cafe6e4 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -97,6 +97,45 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 97 | } | 97 | } |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | /// Initializes USB OTG peripheral with internal High-Speed PHY. | ||
| 101 | /// | ||
| 102 | /// # Arguments | ||
| 103 | /// | ||
| 104 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. | ||
| 105 | /// Must be large enough to fit all OUT endpoint max packet sizes. | ||
| 106 | /// Endpoint allocation will fail if it is too small. | ||
| 107 | pub fn new_hs( | ||
| 108 | _peri: impl Peripheral<P = T> + 'd, | ||
| 109 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 110 | dp: impl Peripheral<P = impl DpPin<T>> + 'd, | ||
| 111 | dm: impl Peripheral<P = impl DmPin<T>> + 'd, | ||
| 112 | ep_out_buffer: &'d mut [u8], | ||
| 113 | config: Config, | ||
| 114 | ) -> Self { | ||
| 115 | into_ref!(dp, dm); | ||
| 116 | |||
| 117 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 118 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 119 | |||
| 120 | let regs = T::regs(); | ||
| 121 | |||
| 122 | let instance = OtgInstance { | ||
| 123 | regs, | ||
| 124 | state: T::state(), | ||
| 125 | fifo_depth_words: T::FIFO_DEPTH_WORDS, | ||
| 126 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, | ||
| 127 | endpoint_count: T::ENDPOINT_COUNT, | ||
| 128 | phy_type: PhyType::InternalHighSpeed, | ||
| 129 | quirk_setup_late_cnak: quirk_setup_late_cnak(regs), | ||
| 130 | calculate_trdt_fn: calculate_trdt::<T>, | ||
| 131 | }; | ||
| 132 | |||
| 133 | Self { | ||
| 134 | inner: OtgDriver::new(ep_out_buffer, instance, config), | ||
| 135 | phantom: PhantomData, | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 100 | /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode). | 139 | /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode). |
| 101 | /// | 140 | /// |
| 102 | /// # Arguments | 141 | /// # Arguments |
| @@ -272,6 +311,19 @@ impl<'d, T: Instance> Bus<'d, T> { | |||
| 272 | } | 311 | } |
| 273 | }); | 312 | }); |
| 274 | 313 | ||
| 314 | #[cfg(stm32h7rs)] | ||
| 315 | critical_section::with(|_| { | ||
| 316 | let rcc = crate::pac::RCC; | ||
| 317 | rcc.ahb1enr().modify(|w| { | ||
| 318 | w.set_usbphycen(true); | ||
| 319 | w.set_usb_otg_hsen(true); | ||
| 320 | }); | ||
| 321 | rcc.ahb1lpenr().modify(|w| { | ||
| 322 | w.set_usbphyclpen(true); | ||
| 323 | w.set_usb_otg_hslpen(true); | ||
| 324 | }); | ||
| 325 | }); | ||
| 326 | |||
| 275 | let r = T::regs(); | 327 | let r = T::regs(); |
| 276 | let core_id = r.cid().read().0; | 328 | let core_id = r.cid().read().0; |
| 277 | trace!("Core id {:08x}", core_id); | 329 | trace!("Core id {:08x}", core_id); |
| @@ -286,6 +338,7 @@ impl<'d, T: Instance> Bus<'d, T> { | |||
| 286 | match core_id { | 338 | match core_id { |
| 287 | 0x0000_1200 | 0x0000_1100 => self.inner.config_v1(), | 339 | 0x0000_1200 | 0x0000_1100 => self.inner.config_v1(), |
| 288 | 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), | 340 | 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), |
| 341 | 0x0000_5000 => self.inner.config_v5(), | ||
| 289 | _ => unimplemented!("Unknown USB core id {:X}", core_id), | 342 | _ => unimplemented!("Unknown USB core id {:X}", core_id), |
| 290 | } | 343 | } |
| 291 | } | 344 | } |
| @@ -501,7 +554,7 @@ fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { | |||
| 501 | match speed { | 554 | match speed { |
| 502 | Dspd::HIGH_SPEED => { | 555 | Dspd::HIGH_SPEED => { |
| 503 | // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) | 556 | // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) |
| 504 | if ahb_freq >= 30_000_000 { | 557 | if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) { |
| 505 | 0x9 | 558 | 0x9 |
| 506 | } else { | 559 | } else { |
| 507 | panic!("AHB frequency is too low") | 560 | panic!("AHB frequency is too low") |
diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index a97eb7d5b..812302e2b 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs | |||
| @@ -194,6 +194,25 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 194 | } | 194 | } |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> crate::pubsub::PubSubBehavior<T> | ||
| 198 | for PubSubChannel<M, T, CAP, SUBS, PUBS> | ||
| 199 | { | ||
| 200 | fn publish_immediate(&self, message: T) { | ||
| 201 | self.inner.lock(|s| { | ||
| 202 | let mut s = s.borrow_mut(); | ||
| 203 | s.publish_immediate(message) | ||
| 204 | }) | ||
| 205 | } | ||
| 206 | |||
| 207 | fn capacity(&self) -> usize { | ||
| 208 | self.capacity() | ||
| 209 | } | ||
| 210 | |||
| 211 | fn is_full(&self) -> bool { | ||
| 212 | self.is_full() | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 197 | impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> SealedPubSubBehavior<T> | 216 | impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> SealedPubSubBehavior<T> |
| 198 | for PubSubChannel<M, T, CAP, SUBS, PUBS> | 217 | for PubSubChannel<M, T, CAP, SUBS, PUBS> |
| 199 | { | 218 | { |
| @@ -246,13 +265,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 246 | }) | 265 | }) |
| 247 | } | 266 | } |
| 248 | 267 | ||
| 249 | fn publish_immediate(&self, message: T) { | ||
| 250 | self.inner.lock(|s| { | ||
| 251 | let mut s = s.borrow_mut(); | ||
| 252 | s.publish_immediate(message) | ||
| 253 | }) | ||
| 254 | } | ||
| 255 | |||
| 256 | fn unregister_subscriber(&self, subscriber_next_message_id: u64) { | 268 | fn unregister_subscriber(&self, subscriber_next_message_id: u64) { |
| 257 | self.inner.lock(|s| { | 269 | self.inner.lock(|s| { |
| 258 | let mut s = s.borrow_mut(); | 270 | let mut s = s.borrow_mut(); |
| @@ -267,10 +279,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 267 | }) | 279 | }) |
| 268 | } | 280 | } |
| 269 | 281 | ||
| 270 | fn capacity(&self) -> usize { | ||
| 271 | self.capacity() | ||
| 272 | } | ||
| 273 | |||
| 274 | fn free_capacity(&self) -> usize { | 282 | fn free_capacity(&self) -> usize { |
| 275 | self.free_capacity() | 283 | self.free_capacity() |
| 276 | } | 284 | } |
| @@ -286,10 +294,6 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi | |||
| 286 | fn is_empty(&self) -> bool { | 294 | fn is_empty(&self) -> bool { |
| 287 | self.is_empty() | 295 | self.is_empty() |
| 288 | } | 296 | } |
| 289 | |||
| 290 | fn is_full(&self) -> bool { | ||
| 291 | self.is_full() | ||
| 292 | } | ||
| 293 | } | 297 | } |
| 294 | 298 | ||
| 295 | /// Internal state for the PubSub channel | 299 | /// Internal state for the PubSub channel |
| @@ -445,8 +449,6 @@ pub enum Error { | |||
| 445 | MaximumPublishersReached, | 449 | MaximumPublishersReached, |
| 446 | } | 450 | } |
| 447 | 451 | ||
| 448 | /// 'Middle level' behaviour of the pubsub channel. | ||
| 449 | /// This trait is used so that Sub and Pub can be generic over the channel. | ||
| 450 | trait SealedPubSubBehavior<T> { | 452 | trait SealedPubSubBehavior<T> { |
| 451 | /// Try to get a message from the queue with the given message id. | 453 | /// Try to get a message from the queue with the given message id. |
| 452 | /// | 454 | /// |
| @@ -462,12 +464,6 @@ trait SealedPubSubBehavior<T> { | |||
| 462 | /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. | 464 | /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. |
| 463 | fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>; | 465 | fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>; |
| 464 | 466 | ||
| 465 | /// Publish a message immediately | ||
| 466 | fn publish_immediate(&self, message: T); | ||
| 467 | |||
| 468 | /// Returns the maximum number of elements the channel can hold. | ||
| 469 | fn capacity(&self) -> usize; | ||
| 470 | |||
| 471 | /// Returns the free capacity of the channel. | 467 | /// Returns the free capacity of the channel. |
| 472 | /// | 468 | /// |
| 473 | /// This is equivalent to `capacity() - len()` | 469 | /// This is equivalent to `capacity() - len()` |
| @@ -482,9 +478,6 @@ trait SealedPubSubBehavior<T> { | |||
| 482 | /// Returns whether the channel is empty. | 478 | /// Returns whether the channel is empty. |
| 483 | fn is_empty(&self) -> bool; | 479 | fn is_empty(&self) -> bool; |
| 484 | 480 | ||
| 485 | /// Returns whether the channel is full. | ||
| 486 | fn is_full(&self) -> bool; | ||
| 487 | |||
| 488 | /// Let the channel know that a subscriber has dropped | 481 | /// Let the channel know that a subscriber has dropped |
| 489 | fn unregister_subscriber(&self, subscriber_next_message_id: u64); | 482 | fn unregister_subscriber(&self, subscriber_next_message_id: u64); |
| 490 | 483 | ||
| @@ -495,9 +488,16 @@ trait SealedPubSubBehavior<T> { | |||
| 495 | /// 'Middle level' behaviour of the pubsub channel. | 488 | /// 'Middle level' behaviour of the pubsub channel. |
| 496 | /// This trait is used so that Sub and Pub can be generic over the channel. | 489 | /// This trait is used so that Sub and Pub can be generic over the channel. |
| 497 | #[allow(private_bounds)] | 490 | #[allow(private_bounds)] |
| 498 | pub trait PubSubBehavior<T>: SealedPubSubBehavior<T> {} | 491 | pub trait PubSubBehavior<T>: SealedPubSubBehavior<T> { |
| 492 | /// Publish a message immediately | ||
| 493 | fn publish_immediate(&self, message: T); | ||
| 499 | 494 | ||
| 500 | impl<T, C: SealedPubSubBehavior<T>> PubSubBehavior<T> for C {} | 495 | /// Returns the maximum number of elements the channel can hold. |
| 496 | fn capacity(&self) -> usize; | ||
| 497 | |||
| 498 | /// Returns whether the channel is full. | ||
| 499 | fn is_full(&self) -> bool; | ||
| 500 | } | ||
| 501 | 501 | ||
| 502 | /// The result of the subscriber wait procedure | 502 | /// The result of the subscriber wait procedure |
| 503 | #[derive(Debug, Clone, PartialEq, Eq)] | 503 | #[derive(Debug, Clone, PartialEq, Eq)] |
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index b145f4aa8..f90403936 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs | |||
| @@ -584,6 +584,29 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { | |||
| 584 | }); | 584 | }); |
| 585 | } | 585 | } |
| 586 | 586 | ||
| 587 | /// Applies configuration specific to | ||
| 588 | /// Core ID 0x0000_5000 | ||
| 589 | pub fn config_v5(&mut self) { | ||
| 590 | let r = self.instance.regs; | ||
| 591 | let phy_type = self.instance.phy_type; | ||
| 592 | |||
| 593 | if phy_type == PhyType::InternalHighSpeed { | ||
| 594 | r.gccfg_v3().modify(|w| { | ||
| 595 | w.set_vbvaloven(!self.config.vbus_detection); | ||
| 596 | w.set_vbvaloval(!self.config.vbus_detection); | ||
| 597 | w.set_vbden(self.config.vbus_detection); | ||
| 598 | }); | ||
| 599 | } else { | ||
| 600 | r.gotgctl().modify(|w| { | ||
| 601 | w.set_bvaloen(!self.config.vbus_detection); | ||
| 602 | w.set_bvaloval(!self.config.vbus_detection); | ||
| 603 | }); | ||
| 604 | r.gccfg_v3().modify(|w| { | ||
| 605 | w.set_vbden(self.config.vbus_detection); | ||
| 606 | }); | ||
| 607 | } | ||
| 608 | } | ||
| 609 | |||
| 587 | fn init(&mut self) { | 610 | fn init(&mut self) { |
| 588 | let r = self.instance.regs; | 611 | let r = self.instance.regs; |
| 589 | let phy_type = self.instance.phy_type; | 612 | let phy_type = self.instance.phy_type; |
diff --git a/embassy-usb-synopsys-otg/src/otg_v1.rs b/embassy-usb-synopsys-otg/src/otg_v1.rs index d3abc328d..18e760fd1 100644 --- a/embassy-usb-synopsys-otg/src/otg_v1.rs +++ b/embassy-usb-synopsys-otg/src/otg_v1.rs | |||
| @@ -186,6 +186,11 @@ impl Otg { | |||
| 186 | pub const fn gccfg_v2(self) -> Reg<regs::GccfgV2, RW> { | 186 | pub const fn gccfg_v2(self) -> Reg<regs::GccfgV2, RW> { |
| 187 | unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } | 187 | unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } |
| 188 | } | 188 | } |
| 189 | #[doc = "General core configuration register, for core_id 0x0000_5xxx"] | ||
| 190 | #[inline(always)] | ||
| 191 | pub const fn gccfg_v3(self) -> Reg<regs::GccfgV3, RW> { | ||
| 192 | unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } | ||
| 193 | } | ||
| 189 | #[doc = "Core ID register"] | 194 | #[doc = "Core ID register"] |
| 190 | #[inline(always)] | 195 | #[inline(always)] |
| 191 | pub const fn cid(self) -> Reg<regs::Cid, RW> { | 196 | pub const fn cid(self) -> Reg<regs::Cid, RW> { |
| @@ -1831,6 +1836,172 @@ pub mod regs { | |||
| 1831 | GccfgV2(0) | 1836 | GccfgV2(0) |
| 1832 | } | 1837 | } |
| 1833 | } | 1838 | } |
| 1839 | #[doc = "OTG general core configuration register."] | ||
| 1840 | #[repr(transparent)] | ||
| 1841 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 1842 | pub struct GccfgV3(pub u32); | ||
| 1843 | impl GccfgV3 { | ||
| 1844 | #[doc = "Charger detection, result of the current mode (primary or secondary)."] | ||
| 1845 | #[inline(always)] | ||
| 1846 | pub const fn chgdet(&self) -> bool { | ||
| 1847 | let val = (self.0 >> 0usize) & 0x01; | ||
| 1848 | val != 0 | ||
| 1849 | } | ||
| 1850 | #[doc = "Charger detection, result of the current mode (primary or secondary)."] | ||
| 1851 | #[inline(always)] | ||
| 1852 | pub fn set_chgdet(&mut self, val: bool) { | ||
| 1853 | self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize); | ||
| 1854 | } | ||
| 1855 | #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."] | ||
| 1856 | #[inline(always)] | ||
| 1857 | pub const fn fsvplus(&self) -> bool { | ||
| 1858 | let val = (self.0 >> 1usize) & 0x01; | ||
| 1859 | val != 0 | ||
| 1860 | } | ||
| 1861 | #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."] | ||
| 1862 | #[inline(always)] | ||
| 1863 | pub fn set_fsvplus(&mut self, val: bool) { | ||
| 1864 | self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize); | ||
| 1865 | } | ||
| 1866 | #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."] | ||
| 1867 | #[inline(always)] | ||
| 1868 | pub const fn fsvminus(&self) -> bool { | ||
| 1869 | let val = (self.0 >> 2usize) & 0x01; | ||
| 1870 | val != 0 | ||
| 1871 | } | ||
| 1872 | #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard)."] | ||
| 1873 | #[inline(always)] | ||
| 1874 | pub fn set_fsvminus(&mut self, val: bool) { | ||
| 1875 | self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize); | ||
| 1876 | } | ||
| 1877 | #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."] | ||
| 1878 | #[inline(always)] | ||
| 1879 | pub const fn sessvld(&self) -> bool { | ||
| 1880 | let val = (self.0 >> 3usize) & 0x01; | ||
| 1881 | val != 0 | ||
| 1882 | } | ||
| 1883 | #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."] | ||
| 1884 | #[inline(always)] | ||
| 1885 | pub fn set_sessvld(&mut self, val: bool) { | ||
| 1886 | self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize); | ||
| 1887 | } | ||
| 1888 | #[doc = "Host CDP behavior enable."] | ||
| 1889 | #[inline(always)] | ||
| 1890 | pub const fn hcdpen(&self) -> bool { | ||
| 1891 | let val = (self.0 >> 16usize) & 0x01; | ||
| 1892 | val != 0 | ||
| 1893 | } | ||
| 1894 | #[doc = "Host CDP behavior enable."] | ||
| 1895 | #[inline(always)] | ||
| 1896 | pub fn set_hcdpen(&mut self, val: bool) { | ||
| 1897 | self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize); | ||
| 1898 | } | ||
| 1899 | #[doc = "Host CDP port voltage detector enable on DP."] | ||
| 1900 | #[inline(always)] | ||
| 1901 | pub const fn hcdpdeten(&self) -> bool { | ||
| 1902 | let val = (self.0 >> 17usize) & 0x01; | ||
| 1903 | val != 0 | ||
| 1904 | } | ||
| 1905 | #[doc = "Host CDP port voltage detector enable on DP."] | ||
| 1906 | #[inline(always)] | ||
| 1907 | pub fn set_hcdpdeten(&mut self, val: bool) { | ||
| 1908 | self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize); | ||
| 1909 | } | ||
| 1910 | #[doc = "Host CDP port Voltage source enable on DM."] | ||
| 1911 | #[inline(always)] | ||
| 1912 | pub const fn hvdmsrcen(&self) -> bool { | ||
| 1913 | let val = (self.0 >> 18usize) & 0x01; | ||
| 1914 | val != 0 | ||
| 1915 | } | ||
| 1916 | #[doc = "Host CDP port Voltage source enable on DM."] | ||
| 1917 | #[inline(always)] | ||
| 1918 | pub fn set_hvdmsrcen(&mut self, val: bool) { | ||
| 1919 | self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize); | ||
| 1920 | } | ||
| 1921 | #[doc = "Data Contact Detection enable."] | ||
| 1922 | #[inline(always)] | ||
| 1923 | pub const fn dcden(&self) -> bool { | ||
| 1924 | let val = (self.0 >> 19usize) & 0x01; | ||
| 1925 | val != 0 | ||
| 1926 | } | ||
| 1927 | #[doc = "Data Contact Detection enable."] | ||
| 1928 | #[inline(always)] | ||
| 1929 | pub fn set_dcden(&mut self, val: bool) { | ||
| 1930 | self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize); | ||
| 1931 | } | ||
| 1932 | #[doc = "Primary detection enable."] | ||
| 1933 | #[inline(always)] | ||
| 1934 | pub const fn pden(&self) -> bool { | ||
| 1935 | let val = (self.0 >> 20usize) & 0x01; | ||
| 1936 | val != 0 | ||
| 1937 | } | ||
| 1938 | #[doc = "Primary detection enable."] | ||
| 1939 | #[inline(always)] | ||
| 1940 | pub fn set_pden(&mut self, val: bool) { | ||
| 1941 | self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize); | ||
| 1942 | } | ||
| 1943 | #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."] | ||
| 1944 | #[inline(always)] | ||
| 1945 | pub const fn vbden(&self) -> bool { | ||
| 1946 | let val = (self.0 >> 21usize) & 0x01; | ||
| 1947 | val != 0 | ||
| 1948 | } | ||
| 1949 | #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."] | ||
| 1950 | #[inline(always)] | ||
| 1951 | pub fn set_vbden(&mut self, val: bool) { | ||
| 1952 | self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize); | ||
| 1953 | } | ||
| 1954 | #[doc = "Secondary detection enable."] | ||
| 1955 | #[inline(always)] | ||
| 1956 | pub const fn sden(&self) -> bool { | ||
| 1957 | let val = (self.0 >> 22usize) & 0x01; | ||
| 1958 | val != 0 | ||
| 1959 | } | ||
| 1960 | #[doc = "Secondary detection enable."] | ||
| 1961 | #[inline(always)] | ||
| 1962 | pub fn set_sden(&mut self, val: bool) { | ||
| 1963 | self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize); | ||
| 1964 | } | ||
| 1965 | #[doc = "Software override value of the VBUS B-session detection."] | ||
| 1966 | #[inline(always)] | ||
| 1967 | pub const fn vbvaloval(&self) -> bool { | ||
| 1968 | let val = (self.0 >> 23usize) & 0x01; | ||
| 1969 | val != 0 | ||
| 1970 | } | ||
| 1971 | #[doc = "Software override value of the VBUS B-session detection."] | ||
| 1972 | #[inline(always)] | ||
| 1973 | pub fn set_vbvaloval(&mut self, val: bool) { | ||
| 1974 | self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize); | ||
| 1975 | } | ||
| 1976 | #[doc = "Enables a software override of the VBUS B-session detection."] | ||
| 1977 | #[inline(always)] | ||
| 1978 | pub const fn vbvaloven(&self) -> bool { | ||
| 1979 | let val = (self.0 >> 24usize) & 0x01; | ||
| 1980 | val != 0 | ||
| 1981 | } | ||
| 1982 | #[doc = "Enables a software override of the VBUS B-session detection."] | ||
| 1983 | #[inline(always)] | ||
| 1984 | pub fn set_vbvaloven(&mut self, val: bool) { | ||
| 1985 | self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize); | ||
| 1986 | } | ||
| 1987 | #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."] | ||
| 1988 | #[inline(always)] | ||
| 1989 | pub const fn forcehostpd(&self) -> bool { | ||
| 1990 | let val = (self.0 >> 25usize) & 0x01; | ||
| 1991 | val != 0 | ||
| 1992 | } | ||
| 1993 | #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."] | ||
| 1994 | #[inline(always)] | ||
| 1995 | pub fn set_forcehostpd(&mut self, val: bool) { | ||
| 1996 | self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize); | ||
| 1997 | } | ||
| 1998 | } | ||
| 1999 | impl Default for GccfgV3 { | ||
| 2000 | #[inline(always)] | ||
| 2001 | fn default() -> GccfgV3 { | ||
| 2002 | GccfgV3(0) | ||
| 2003 | } | ||
| 2004 | } | ||
| 1834 | #[doc = "I2C access register"] | 2005 | #[doc = "I2C access register"] |
| 1835 | #[repr(transparent)] | 2006 | #[repr(transparent)] |
| 1836 | #[derive(Copy, Clone, Eq, PartialEq)] | 2007 | #[derive(Copy, Clone, Eq, PartialEq)] |
diff --git a/examples/boot/application/nrf/build.rs b/examples/boot/application/nrf/build.rs index cd1a264c4..e1da69328 100644 --- a/examples/boot/application/nrf/build.rs +++ b/examples/boot/application/nrf/build.rs | |||
| @@ -31,4 +31,7 @@ fn main() { | |||
| 31 | 31 | ||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); |
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); |
| 34 | if env::var("CARGO_FEATURE_DEFMT").is_ok() { | ||
| 35 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 36 | } | ||
| 34 | } | 37 | } |
diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 851a3d721..2c1d1a7bb 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![macro_use] | 3 | #![macro_use] |
| 4 | 4 | ||
| 5 | #[cfg(feature = "defmt")] | ||
| 6 | use defmt_rtt as _; | ||
| 7 | use embassy_boot::State; | ||
| 5 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; | 8 | use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; |
| 6 | use embassy_embedded_hal::adapter::BlockingAsync; | 9 | use embassy_embedded_hal::adapter::BlockingAsync; |
| 7 | use embassy_executor::Spawner; | 10 | use embassy_executor::Spawner; |
| @@ -22,6 +25,7 @@ async fn main(_spawner: Spawner) { | |||
| 22 | 25 | ||
| 23 | let mut button = Input::new(p.P0_11, Pull::Up); | 26 | let mut button = Input::new(p.P0_11, Pull::Up); |
| 24 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | 27 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); |
| 28 | let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard); | ||
| 25 | 29 | ||
| 26 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | 30 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); |
| 27 | //let mut button = Input::new(p.P1_02, Pull::Up); | 31 | //let mut button = Input::new(p.P1_02, Pull::Up); |
| @@ -53,6 +57,13 @@ async fn main(_spawner: Spawner) { | |||
| 53 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); | 57 | let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); |
| 54 | let mut magic = [0; 4]; | 58 | let mut magic = [0; 4]; |
| 55 | let mut updater = FirmwareUpdater::new(config, &mut magic); | 59 | let mut updater = FirmwareUpdater::new(config, &mut magic); |
| 60 | let state = updater.get_state().await.unwrap(); | ||
| 61 | if state == State::Revert { | ||
| 62 | led_reverted.set_low(); | ||
| 63 | } else { | ||
| 64 | led_reverted.set_high(); | ||
| 65 | } | ||
| 66 | |||
| 56 | loop { | 67 | loop { |
| 57 | led.set_low(); | 68 | led.set_low(); |
| 58 | button.wait_for_any_edge().await; | 69 | button.wait_for_any_edge().await; |
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs new file mode 100644 index 000000000..5076101ec --- /dev/null +++ b/examples/rp/src/bin/pio_onewire.rs | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | //! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::bind_interrupts; | ||
| 8 | use embassy_rp::peripherals::PIO0; | ||
| 9 | use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 21 | let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | sensor.start().await; // Start a new measurement | ||
| 25 | Timer::after_secs(1).await; // Allow 1s for the measurement to finish | ||
| 26 | match sensor.temperature().await { | ||
| 27 | Ok(temp) => info!("temp = {:?} deg C", temp), | ||
| 28 | _ => error!("sensor error"), | ||
| 29 | } | ||
| 30 | Timer::after_secs(1).await; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// DS18B20 temperature sensor driver | ||
| 35 | pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { | ||
| 36 | sm: StateMachine<'d, PIO, SM>, | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { | ||
| 40 | /// Create a new instance the driver | ||
| 41 | pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { | ||
| 42 | let prg = pio_proc::pio_asm!( | ||
| 43 | r#" | ||
| 44 | .wrap_target | ||
| 45 | again: | ||
| 46 | pull block | ||
| 47 | mov x, osr | ||
| 48 | jmp !x, read | ||
| 49 | write: | ||
| 50 | set pindirs, 1 | ||
| 51 | set pins, 0 | ||
| 52 | loop1: | ||
| 53 | jmp x--,loop1 | ||
| 54 | set pindirs, 0 [31] | ||
| 55 | wait 1 pin 0 [31] | ||
| 56 | pull block | ||
| 57 | mov x, osr | ||
| 58 | bytes1: | ||
| 59 | pull block | ||
| 60 | set y, 7 | ||
| 61 | set pindirs, 1 | ||
| 62 | bit1: | ||
| 63 | set pins, 0 [1] | ||
| 64 | out pins,1 [31] | ||
| 65 | set pins, 1 [20] | ||
| 66 | jmp y--,bit1 | ||
| 67 | jmp x--,bytes1 | ||
| 68 | set pindirs, 0 [31] | ||
| 69 | jmp again | ||
| 70 | read: | ||
| 71 | pull block | ||
| 72 | mov x, osr | ||
| 73 | bytes2: | ||
| 74 | set y, 7 | ||
| 75 | bit2: | ||
| 76 | set pindirs, 1 | ||
| 77 | set pins, 0 [1] | ||
| 78 | set pindirs, 0 [5] | ||
| 79 | in pins,1 [10] | ||
| 80 | jmp y--,bit2 | ||
| 81 | jmp x--,bytes2 | ||
| 82 | .wrap | ||
| 83 | "#, | ||
| 84 | ); | ||
| 85 | |||
| 86 | let pin = common.make_pio_pin(pin); | ||
| 87 | let mut cfg = Config::default(); | ||
| 88 | cfg.use_program(&common.load_program(&prg.program), &[]); | ||
| 89 | cfg.set_out_pins(&[&pin]); | ||
| 90 | cfg.set_in_pins(&[&pin]); | ||
| 91 | cfg.set_set_pins(&[&pin]); | ||
| 92 | cfg.shift_in = ShiftConfig { | ||
| 93 | auto_fill: true, | ||
| 94 | direction: ShiftDirection::Right, | ||
| 95 | threshold: 8, | ||
| 96 | }; | ||
| 97 | cfg.clock_divider = 255_u8.into(); | ||
| 98 | sm.set_config(&cfg); | ||
| 99 | sm.set_enable(true); | ||
| 100 | Self { sm } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Write bytes over the wire | ||
| 104 | async fn write_bytes(&mut self, bytes: &[u8]) { | ||
| 105 | self.sm.tx().wait_push(250).await; | ||
| 106 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 107 | for b in bytes { | ||
| 108 | self.sm.tx().wait_push(*b as u32).await; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Read bytes from the wire | ||
| 113 | async fn read_bytes(&mut self, bytes: &mut [u8]) { | ||
| 114 | self.sm.tx().wait_push(0).await; | ||
| 115 | self.sm.tx().wait_push(bytes.len() as u32 - 1).await; | ||
| 116 | for b in bytes.iter_mut() { | ||
| 117 | *b = (self.sm.rx().wait_pull().await >> 24) as u8; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /// Calculate CRC8 of the data | ||
| 122 | fn crc8(data: &[u8]) -> u8 { | ||
| 123 | let mut temp; | ||
| 124 | let mut data_byte; | ||
| 125 | let mut crc = 0; | ||
| 126 | for b in data { | ||
| 127 | data_byte = *b; | ||
| 128 | for _ in 0..8 { | ||
| 129 | temp = (crc ^ data_byte) & 0x01; | ||
| 130 | crc >>= 1; | ||
| 131 | if temp != 0 { | ||
| 132 | crc ^= 0x8C; | ||
| 133 | } | ||
| 134 | data_byte >>= 1; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | crc | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Start a new measurement. Allow at least 1000ms before getting `temperature`. | ||
| 141 | pub async fn start(&mut self) { | ||
| 142 | self.write_bytes(&[0xCC, 0x44]).await; | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. | ||
| 146 | pub async fn temperature(&mut self) -> Result<f32, ()> { | ||
| 147 | self.write_bytes(&[0xCC, 0xBE]).await; | ||
| 148 | let mut data = [0; 9]; | ||
| 149 | self.read_bytes(&mut data).await; | ||
| 150 | match Self::crc8(&data) == 0 { | ||
| 151 | true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), | ||
| 152 | false => Err(()), | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs new file mode 100644 index 000000000..e146baa2e --- /dev/null +++ b/examples/rp23/src/bin/trng.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example shows TRNG usage | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::block::ImageDef; | ||
| 10 | use embassy_rp::gpio::{Level, Output}; | ||
| 11 | use embassy_rp::peripherals::TRNG; | ||
| 12 | use embassy_rp::trng::Trng; | ||
| 13 | use embassy_time::Timer; | ||
| 14 | use rand::RngCore; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[link_section = ".start_block"] | ||
| 18 | #[used] | ||
| 19 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 20 | |||
| 21 | // Program metadata for `picotool info` | ||
| 22 | #[link_section = ".bi_entries"] | ||
| 23 | #[used] | ||
| 24 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | ||
| 25 | embassy_rp::binary_info::rp_program_name!(c"example"), | ||
| 26 | embassy_rp::binary_info::rp_cargo_version!(), | ||
| 27 | embassy_rp::binary_info::rp_program_description!(c"Blinky"), | ||
| 28 | embassy_rp::binary_info::rp_program_build_attribute!(), | ||
| 29 | ]; | ||
| 30 | |||
| 31 | bind_interrupts!(struct Irqs { | ||
| 32 | TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>; | ||
| 33 | }); | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async fn main(_spawner: Spawner) { | ||
| 37 | let peripherals = embassy_rp::init(Default::default()); | ||
| 38 | |||
| 39 | // Initialize the TRNG with default configuration | ||
| 40 | let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); | ||
| 41 | // A buffer to collect random bytes in. | ||
| 42 | let mut randomness = [0u8; 58]; | ||
| 43 | |||
| 44 | let mut led = Output::new(peripherals.PIN_25, Level::Low); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | trng.fill_bytes(&mut randomness).await; | ||
| 48 | info!("Random bytes async {}", &randomness); | ||
| 49 | trng.blocking_fill_bytes(&mut randomness); | ||
| 50 | info!("Random bytes blocking {}", &randomness); | ||
| 51 | let random_u32 = trng.next_u32(); | ||
| 52 | let random_u64 = trng.next_u64(); | ||
| 53 | info!("Random u32 {} u64 {}", random_u32, random_u64); | ||
| 54 | // Random number of blinks between 0 and 31 | ||
| 55 | let blinks = random_u32 % 32; | ||
| 56 | for _ in 0..blinks { | ||
| 57 | led.set_high(); | ||
| 58 | Timer::after_millis(20).await; | ||
| 59 | led.set_low(); | ||
| 60 | Timer::after_millis(20).await; | ||
| 61 | } | ||
| 62 | Timer::after_millis(1000).await; | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs new file mode 100644 index 000000000..5946fed79 --- /dev/null +++ b/examples/stm32f4/src/bin/eth_compliance_test.rs | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::eth::generic_smi::GenericSMI; | ||
| 7 | use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement}; | ||
| 8 | use embassy_stm32::time::Hertz; | ||
| 9 | use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | ETH => eth::InterruptHandler; | ||
| 16 | HASH_RNG => rng::InterruptHandler<peripherals::RNG>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) -> ! { | ||
| 21 | let mut config = Config::default(); | ||
| 22 | { | ||
| 23 | use embassy_stm32::rcc::*; | ||
| 24 | config.rcc.hse = Some(Hse { | ||
| 25 | freq: Hertz(8_000_000), | ||
| 26 | mode: HseMode::Bypass, | ||
| 27 | }); | ||
| 28 | config.rcc.pll_src = PllSource::HSE; | ||
| 29 | config.rcc.pll = Some(Pll { | ||
| 30 | prediv: PllPreDiv::DIV4, | ||
| 31 | mul: PllMul::MUL180, | ||
| 32 | divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. | ||
| 33 | divq: None, | ||
| 34 | divr: None, | ||
| 35 | }); | ||
| 36 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 37 | config.rcc.apb1_pre = APBPrescaler::DIV4; | ||
| 38 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 39 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 40 | } | ||
| 41 | let p = embassy_stm32::init(config); | ||
| 42 | |||
| 43 | info!("Hello Compliance World!"); | ||
| 44 | |||
| 45 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | ||
| 46 | |||
| 47 | const PHY_ADDR: u8 = 0; | ||
| 48 | static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new(); | ||
| 49 | let mut device = Ethernet::new( | ||
| 50 | PACKETS.init(PacketQueue::<4, 4>::new()), | ||
| 51 | p.ETH, | ||
| 52 | Irqs, | ||
| 53 | p.PA1, | ||
| 54 | p.PA2, | ||
| 55 | p.PC1, | ||
| 56 | p.PA7, | ||
| 57 | p.PC4, | ||
| 58 | p.PC5, | ||
| 59 | p.PG13, | ||
| 60 | p.PB13, | ||
| 61 | p.PG11, | ||
| 62 | GenericSMI::new(PHY_ADDR), | ||
| 63 | mac_addr, | ||
| 64 | ); | ||
| 65 | |||
| 66 | let sm = unsafe { device.station_management() }; | ||
| 67 | |||
| 68 | // Just an example. Exact register settings depend on the specific PHY and test. | ||
| 69 | sm.smi_write(PHY_ADDR, 0, 0x2100); | ||
| 70 | sm.smi_write(PHY_ADDR, 11, 0xA000); | ||
| 71 | |||
| 72 | // NB: Remember to reset the PHY after testing before starting the networking stack | ||
| 73 | |||
| 74 | loop { | ||
| 75 | Timer::after_secs(1).await; | ||
| 76 | } | ||
| 77 | } | ||
diff --git a/examples/stm32h7rs/src/bin/usb_serial.rs b/examples/stm32h7rs/src/bin/usb_serial.rs new file mode 100644 index 000000000..6773f7843 --- /dev/null +++ b/examples/stm32h7rs/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{panic, *}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::time::Hertz; | ||
| 8 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 9 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; | ||
| 10 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 11 | use embassy_usb::driver::EndpointError; | ||
| 12 | use embassy_usb::Builder; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | bind_interrupts!(struct Irqs { | ||
| 16 | OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; | ||
| 17 | }); | ||
| 18 | |||
| 19 | // If you are trying this and your USB device doesn't connect, the most | ||
| 20 | // common issues are the RCC config and vbus_detection | ||
| 21 | // | ||
| 22 | // See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure | ||
| 23 | // for more information. | ||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | info!("Hello World!"); | ||
| 27 | |||
| 28 | let mut config = Config::default(); | ||
| 29 | |||
| 30 | { | ||
| 31 | use embassy_stm32::rcc::*; | ||
| 32 | config.rcc.hse = Some(Hse { | ||
| 33 | freq: Hertz(24_000_000), | ||
| 34 | mode: HseMode::Oscillator, | ||
| 35 | }); | ||
| 36 | config.rcc.pll1 = Some(Pll { | ||
| 37 | source: PllSource::HSE, | ||
| 38 | prediv: PllPreDiv::DIV12, | ||
| 39 | mul: PllMul::MUL300, | ||
| 40 | divp: Some(PllDiv::DIV1), //600 MHz | ||
| 41 | divq: Some(PllDiv::DIV2), // 300 MHz | ||
| 42 | divr: Some(PllDiv::DIV2), // 300 MHz | ||
| 43 | }); | ||
| 44 | config.rcc.sys = Sysclk::PLL1_P; // 600 MHz | ||
| 45 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz | ||
| 46 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 47 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 48 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 49 | config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz | ||
| 50 | config.rcc.voltage_scale = VoltageScale::HIGH; | ||
| 51 | config.rcc.mux.usbphycsel = mux::Usbphycsel::HSE; | ||
| 52 | } | ||
| 53 | |||
| 54 | let p = embassy_stm32::init(config); | ||
| 55 | |||
| 56 | // Create the driver, from the HAL. | ||
| 57 | let mut ep_out_buffer = [0u8; 256]; | ||
| 58 | let mut config = embassy_stm32::usb::Config::default(); | ||
| 59 | |||
| 60 | // Do not enable vbus_detection. This is a safe default that works in all boards. | ||
| 61 | // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need | ||
| 62 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | ||
| 63 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | ||
| 64 | config.vbus_detection = false; | ||
| 65 | |||
| 66 | let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &mut ep_out_buffer, config); | ||
| 67 | |||
| 68 | // Create embassy-usb Config | ||
| 69 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 70 | config.manufacturer = Some("Embassy"); | ||
| 71 | config.product = Some("USB-serial example"); | ||
| 72 | config.serial_number = Some("12345678"); | ||
| 73 | // Required for windows compatibility. | ||
| 74 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 75 | config.device_class = 0xEF; | ||
| 76 | config.device_sub_class = 0x02; | ||
| 77 | config.device_protocol = 0x01; | ||
| 78 | config.composite_with_iads = true; | ||
| 79 | |||
| 80 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 81 | // It needs some buffers for building the descriptors. | ||
| 82 | let mut config_descriptor = [0; 256]; | ||
| 83 | let mut bos_descriptor = [0; 256]; | ||
| 84 | let mut control_buf = [0; 64]; | ||
| 85 | |||
| 86 | let mut state = State::new(); | ||
| 87 | |||
| 88 | let mut builder = Builder::new( | ||
| 89 | driver, | ||
| 90 | config, | ||
| 91 | &mut config_descriptor, | ||
| 92 | &mut bos_descriptor, | ||
| 93 | &mut [], // no msos descriptors | ||
| 94 | &mut control_buf, | ||
| 95 | ); | ||
| 96 | |||
| 97 | // Create classes on the builder. | ||
| 98 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 99 | |||
| 100 | // Build the builder. | ||
| 101 | let mut usb = builder.build(); | ||
| 102 | |||
| 103 | // Run the USB device. | ||
| 104 | let usb_fut = usb.run(); | ||
| 105 | |||
| 106 | // Do stuff with the class! | ||
| 107 | let echo_fut = async { | ||
| 108 | loop { | ||
| 109 | class.wait_connection().await; | ||
| 110 | info!("Connected"); | ||
| 111 | let _ = echo(&mut class).await; | ||
| 112 | info!("Disconnected"); | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | |||
| 116 | // Run everything concurrently. | ||
| 117 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 118 | join(usb_fut, echo_fut).await; | ||
| 119 | } | ||
| 120 | |||
| 121 | struct Disconnected {} | ||
| 122 | |||
| 123 | impl From<EndpointError> for Disconnected { | ||
| 124 | fn from(val: EndpointError) -> Self { | ||
| 125 | match val { | ||
| 126 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 127 | EndpointError::Disabled => Disconnected {}, | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 133 | let mut buf = [0; 64]; | ||
| 134 | loop { | ||
| 135 | let n = class.read_packet(&mut buf).await?; | ||
| 136 | let data = &buf[..n]; | ||
| 137 | info!("data: {:x}", data); | ||
| 138 | class.write_packet(data).await?; | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 53d44a94a..9712a8c5a 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs | |||
| @@ -7,7 +7,8 @@ use common::*; | |||
| 7 | use defmt::assert_eq; | 7 | use defmt::assert_eq; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::gpio::{Level, Output, Speed}; | 9 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 10 | use embassy_stm32::spi::{self, Spi}; | 10 | use embassy_stm32::mode::Blocking; |
| 11 | use embassy_stm32::spi::{self, Spi, Word}; | ||
| 11 | use embassy_stm32::time::Hertz; | 12 | use embassy_stm32::time::Hertz; |
| 12 | 13 | ||
| 13 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| @@ -31,11 +32,58 @@ async fn main(_spawner: Spawner) { | |||
| 31 | spi_config, | 32 | spi_config, |
| 32 | ); | 33 | ); |
| 33 | 34 | ||
| 34 | let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; | 35 | test_txrx::<u8>(&mut spi); |
| 36 | test_txrx::<u16>(&mut spi); | ||
| 37 | |||
| 38 | // Assert the RCC bit gets disabled on drop. | ||
| 39 | #[cfg(feature = "stm32f429zi")] | ||
| 40 | defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); | ||
| 41 | drop(spi); | ||
| 42 | #[cfg(feature = "stm32f429zi")] | ||
| 43 | defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); | ||
| 44 | |||
| 45 | // test rx-only configuration | ||
| 46 | let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); | ||
| 47 | let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); | ||
| 48 | |||
| 49 | test_rx::<u8>(&mut spi, &mut mosi_out); | ||
| 50 | test_rx::<u16>(&mut spi, &mut mosi_out); | ||
| 51 | drop(spi); | ||
| 52 | drop(mosi_out); | ||
| 53 | |||
| 54 | let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config); | ||
| 55 | test_tx::<u8>(&mut spi); | ||
| 56 | test_tx::<u16>(&mut spi); | ||
| 57 | drop(spi); | ||
| 58 | |||
| 59 | let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); | ||
| 60 | test_tx::<u8>(&mut spi); | ||
| 61 | test_tx::<u16>(&mut spi); | ||
| 62 | drop(spi); | ||
| 63 | |||
| 64 | info!("Test OK"); | ||
| 65 | cortex_m::asm::bkpt(); | ||
| 66 | } | ||
| 67 | |||
| 68 | fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) | ||
| 69 | where | ||
| 70 | W: core::ops::Not<Output = W>, | ||
| 71 | { | ||
| 72 | let data: [W; 9] = [ | ||
| 73 | 0x00u8.into(), | ||
| 74 | 0xFFu8.into(), | ||
| 75 | 0xAAu8.into(), | ||
| 76 | 0x55u8.into(), | ||
| 77 | 0xC0u8.into(), | ||
| 78 | 0xFFu8.into(), | ||
| 79 | 0xEEu8.into(), | ||
| 80 | 0xC0u8.into(), | ||
| 81 | 0xDEu8.into(), | ||
| 82 | ]; | ||
| 35 | 83 | ||
| 36 | // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. | 84 | // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. |
| 37 | // so we should get the data we sent back. | 85 | // so we should get the data we sent back. |
| 38 | let mut buf = [0; 9]; | 86 | let mut buf = [W::default(); 9]; |
| 39 | spi.blocking_transfer(&mut buf, &data).unwrap(); | 87 | spi.blocking_transfer(&mut buf, &data).unwrap(); |
| 40 | assert_eq!(buf, data); | 88 | assert_eq!(buf, data); |
| 41 | 89 | ||
| @@ -59,47 +107,33 @@ async fn main(_spawner: Spawner) { | |||
| 59 | spi.blocking_transfer_in_place::<u8>(&mut []).unwrap(); | 107 | spi.blocking_transfer_in_place::<u8>(&mut []).unwrap(); |
| 60 | spi.blocking_read::<u8>(&mut []).unwrap(); | 108 | spi.blocking_read::<u8>(&mut []).unwrap(); |
| 61 | spi.blocking_write::<u8>(&[]).unwrap(); | 109 | spi.blocking_write::<u8>(&[]).unwrap(); |
| 110 | } | ||
| 62 | 111 | ||
| 63 | // Assert the RCC bit gets disabled on drop. | 112 | fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>, mosi_out: &mut Output<'_>) |
| 64 | #[cfg(feature = "stm32f429zi")] | 113 | where |
| 65 | defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); | 114 | W: core::ops::Not<Output = W>, |
| 66 | drop(spi); | 115 | { |
| 67 | #[cfg(feature = "stm32f429zi")] | 116 | let mut buf = [W::default(); 9]; |
| 68 | defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); | ||
| 69 | 117 | ||
| 70 | // test rx-only configuration | ||
| 71 | let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); | ||
| 72 | let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); | ||
| 73 | mosi_out.set_high(); | 118 | mosi_out.set_high(); |
| 74 | spi.blocking_read(&mut buf).unwrap(); | 119 | spi.blocking_read(&mut buf).unwrap(); |
| 75 | assert_eq!(buf, [0xff; 9]); | 120 | assert_eq!(buf, [!W::default(); 9]); |
| 76 | mosi_out.set_low(); | 121 | mosi_out.set_low(); |
| 77 | spi.blocking_read(&mut buf).unwrap(); | 122 | spi.blocking_read(&mut buf).unwrap(); |
| 78 | assert_eq!(buf, [0x00; 9]); | 123 | assert_eq!(buf, [W::default(); 9]); |
| 79 | spi.blocking_read::<u8>(&mut []).unwrap(); | 124 | spi.blocking_read::<u8>(&mut []).unwrap(); |
| 80 | spi.blocking_read::<u8>(&mut []).unwrap(); | 125 | spi.blocking_read::<u8>(&mut []).unwrap(); |
| 81 | drop(mosi_out); | 126 | } |
| 82 | drop(spi); | 127 | |
| 128 | fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) | ||
| 129 | where | ||
| 130 | W: core::ops::Not<Output = W>, | ||
| 131 | { | ||
| 132 | let buf = [W::default(); 9]; | ||
| 83 | 133 | ||
| 84 | // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. | 134 | // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. |
| 85 | let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config); | ||
| 86 | spi.blocking_transfer(&mut buf, &data).unwrap(); | ||
| 87 | spi.blocking_transfer_in_place(&mut buf).unwrap(); | ||
| 88 | spi.blocking_write(&buf).unwrap(); | 135 | spi.blocking_write(&buf).unwrap(); |
| 89 | spi.blocking_read(&mut buf).unwrap(); | ||
| 90 | spi.blocking_transfer::<u8>(&mut [], &[]).unwrap(); | ||
| 91 | spi.blocking_transfer_in_place::<u8>(&mut []).unwrap(); | ||
| 92 | spi.blocking_read::<u8>(&mut []).unwrap(); | ||
| 93 | spi.blocking_write::<u8>(&[]).unwrap(); | 136 | spi.blocking_write::<u8>(&[]).unwrap(); |
| 94 | drop(spi); | ||
| 95 | |||
| 96 | // Test tx-only nosck. | ||
| 97 | let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); | ||
| 98 | spi.blocking_write(&buf).unwrap(); | 137 | spi.blocking_write(&buf).unwrap(); |
| 99 | spi.blocking_write::<u8>(&[]).unwrap(); | 138 | spi.blocking_write::<u8>(&[]).unwrap(); |
| 100 | spi.blocking_write(&buf).unwrap(); | ||
| 101 | drop(spi); | ||
| 102 | |||
| 103 | info!("Test OK"); | ||
| 104 | cortex_m::asm::bkpt(); | ||
| 105 | } | 139 | } |
diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index a1cbc0ed1..307409a16 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs | |||
| @@ -7,7 +7,8 @@ use common::*; | |||
| 7 | use defmt::assert_eq; | 7 | use defmt::assert_eq; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_stm32::gpio::{Level, Output, Speed}; | 9 | use embassy_stm32::gpio::{Level, Output, Speed}; |
| 10 | use embassy_stm32::spi::{self, Spi}; | 10 | use embassy_stm32::mode::Async; |
| 11 | use embassy_stm32::spi::{self, Spi, Word}; | ||
| 11 | use embassy_stm32::time::Hertz; | 12 | use embassy_stm32::time::Hertz; |
| 12 | 13 | ||
| 13 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| @@ -35,11 +36,61 @@ async fn main(_spawner: Spawner) { | |||
| 35 | spi_config, | 36 | spi_config, |
| 36 | ); | 37 | ); |
| 37 | 38 | ||
| 38 | let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; | 39 | test_txrx::<u8>(&mut spi).await; |
| 40 | test_txrx::<u16>(&mut spi).await; | ||
| 41 | drop(spi); | ||
| 42 | |||
| 43 | // test rx-only configuration | ||
| 44 | let mut spi = Spi::new_rxonly( | ||
| 45 | &mut spi_peri, | ||
| 46 | &mut sck, | ||
| 47 | &mut miso, | ||
| 48 | // SPIv1/f1 requires txdma even if rxonly. | ||
| 49 | #[cfg(not(feature = "spi-v345"))] | ||
| 50 | &mut tx_dma, | ||
| 51 | &mut rx_dma, | ||
| 52 | spi_config, | ||
| 53 | ); | ||
| 54 | let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); | ||
| 55 | |||
| 56 | test_rx::<u8>(&mut spi, &mut mosi_out).await; | ||
| 57 | test_rx::<u16>(&mut spi, &mut mosi_out).await; | ||
| 58 | drop(spi); | ||
| 59 | drop(mosi_out); | ||
| 60 | |||
| 61 | let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config); | ||
| 62 | test_tx::<u8>(&mut spi).await; | ||
| 63 | test_tx::<u16>(&mut spi).await; | ||
| 64 | drop(spi); | ||
| 65 | |||
| 66 | let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); | ||
| 67 | test_tx::<u8>(&mut spi).await; | ||
| 68 | test_tx::<u16>(&mut spi).await; | ||
| 69 | drop(spi); | ||
| 70 | |||
| 71 | info!("Test OK"); | ||
| 72 | cortex_m::asm::bkpt(); | ||
| 73 | } | ||
| 74 | |||
| 75 | async fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) | ||
| 76 | where | ||
| 77 | W: core::ops::Not<Output = W>, | ||
| 78 | { | ||
| 79 | let data: [W; 9] = [ | ||
| 80 | 0x00u8.into(), | ||
| 81 | 0xFFu8.into(), | ||
| 82 | 0xAAu8.into(), | ||
| 83 | 0x55u8.into(), | ||
| 84 | 0xC0u8.into(), | ||
| 85 | 0xFFu8.into(), | ||
| 86 | 0xEEu8.into(), | ||
| 87 | 0xC0u8.into(), | ||
| 88 | 0xDEu8.into(), | ||
| 89 | ]; | ||
| 39 | 90 | ||
| 40 | // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. | 91 | // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. |
| 41 | // so we should get the data we sent back. | 92 | // so we should get the data we sent back. |
| 42 | let mut buf = [0; 9]; | 93 | let mut buf = [W::default(); 9]; |
| 43 | spi.transfer(&mut buf, &data).await.unwrap(); | 94 | spi.transfer(&mut buf, &data).await.unwrap(); |
| 44 | assert_eq!(buf, data); | 95 | assert_eq!(buf, data); |
| 45 | 96 | ||
| @@ -83,56 +134,41 @@ async fn main(_spawner: Spawner) { | |||
| 83 | spi.blocking_write(&buf).unwrap(); | 134 | spi.blocking_write(&buf).unwrap(); |
| 84 | spi.blocking_read(&mut buf).unwrap(); | 135 | spi.blocking_read(&mut buf).unwrap(); |
| 85 | spi.write(&buf).await.unwrap(); | 136 | spi.write(&buf).await.unwrap(); |
| 137 | } | ||
| 86 | 138 | ||
| 87 | core::mem::drop(spi); | 139 | async fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>, mosi_out: &mut Output<'_>) |
| 140 | where | ||
| 141 | W: core::ops::Not<Output = W>, | ||
| 142 | { | ||
| 143 | let mut buf = [W::default(); 9]; | ||
| 88 | 144 | ||
| 89 | // test rx-only configuration | ||
| 90 | let mut spi = Spi::new_rxonly( | ||
| 91 | &mut spi_peri, | ||
| 92 | &mut sck, | ||
| 93 | &mut miso, | ||
| 94 | // SPIv1/f1 requires txdma even if rxonly. | ||
| 95 | #[cfg(not(feature = "spi-v345"))] | ||
| 96 | &mut tx_dma, | ||
| 97 | &mut rx_dma, | ||
| 98 | spi_config, | ||
| 99 | ); | ||
| 100 | let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); | ||
| 101 | mosi_out.set_high(); | 145 | mosi_out.set_high(); |
| 102 | spi.read(&mut buf).await.unwrap(); | 146 | spi.read(&mut buf).await.unwrap(); |
| 103 | assert_eq!(buf, [0xff; 9]); | 147 | assert_eq!(buf, [!W::default(); 9]); |
| 104 | spi.blocking_read(&mut buf).unwrap(); | 148 | spi.blocking_read(&mut buf).unwrap(); |
| 105 | assert_eq!(buf, [0xff; 9]); | 149 | assert_eq!(buf, [!W::default(); 9]); |
| 106 | spi.read(&mut buf).await.unwrap(); | 150 | spi.read(&mut buf).await.unwrap(); |
| 107 | assert_eq!(buf, [0xff; 9]); | 151 | assert_eq!(buf, [!W::default(); 9]); |
| 108 | spi.read(&mut buf).await.unwrap(); | 152 | spi.read(&mut buf).await.unwrap(); |
| 109 | assert_eq!(buf, [0xff; 9]); | 153 | assert_eq!(buf, [!W::default(); 9]); |
| 110 | spi.blocking_read(&mut buf).unwrap(); | 154 | spi.blocking_read(&mut buf).unwrap(); |
| 111 | assert_eq!(buf, [0xff; 9]); | 155 | assert_eq!(buf, [!W::default(); 9]); |
| 112 | spi.blocking_read(&mut buf).unwrap(); | 156 | spi.blocking_read(&mut buf).unwrap(); |
| 113 | assert_eq!(buf, [0xff; 9]); | 157 | assert_eq!(buf, [!W::default(); 9]); |
| 114 | mosi_out.set_low(); | 158 | mosi_out.set_low(); |
| 115 | spi.read(&mut buf).await.unwrap(); | 159 | spi.read(&mut buf).await.unwrap(); |
| 116 | assert_eq!(buf, [0x00; 9]); | 160 | assert_eq!(buf, [W::default(); 9]); |
| 117 | spi.read::<u8>(&mut []).await.unwrap(); | 161 | spi.read::<u8>(&mut []).await.unwrap(); |
| 118 | spi.blocking_read::<u8>(&mut []).unwrap(); | 162 | spi.blocking_read::<u8>(&mut []).unwrap(); |
| 119 | drop(mosi_out); | 163 | } |
| 120 | drop(spi); | ||
| 121 | 164 | ||
| 122 | // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. | 165 | async fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) |
| 123 | let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config); | 166 | where |
| 124 | spi.blocking_write(&buf).unwrap(); | 167 | W: core::ops::Not<Output = W>, |
| 125 | spi.write(&buf).await.unwrap(); | 168 | { |
| 126 | spi.blocking_write(&buf).unwrap(); | 169 | let buf = [W::default(); 9]; |
| 127 | spi.blocking_write(&buf).unwrap(); | ||
| 128 | spi.write(&buf).await.unwrap(); | ||
| 129 | spi.write(&buf).await.unwrap(); | ||
| 130 | spi.write::<u8>(&[]).await.unwrap(); | ||
| 131 | spi.blocking_write::<u8>(&[]).unwrap(); | ||
| 132 | drop(spi); | ||
| 133 | 170 | ||
| 134 | // Test tx-only nosck. | 171 | // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. |
| 135 | let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); | ||
| 136 | spi.blocking_write(&buf).unwrap(); | 172 | spi.blocking_write(&buf).unwrap(); |
| 137 | spi.write(&buf).await.unwrap(); | 173 | spi.write(&buf).await.unwrap(); |
| 138 | spi.blocking_write(&buf).unwrap(); | 174 | spi.blocking_write(&buf).unwrap(); |
| @@ -141,8 +177,4 @@ async fn main(_spawner: Spawner) { | |||
| 141 | spi.write(&buf).await.unwrap(); | 177 | spi.write(&buf).await.unwrap(); |
| 142 | spi.write::<u8>(&[]).await.unwrap(); | 178 | spi.write::<u8>(&[]).await.unwrap(); |
| 143 | spi.blocking_write::<u8>(&[]).unwrap(); | 179 | spi.blocking_write::<u8>(&[]).unwrap(); |
| 144 | drop(spi); | ||
| 145 | |||
| 146 | info!("Test OK"); | ||
| 147 | cortex_m::asm::bkpt(); | ||
| 148 | } | 180 | } |
