aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci.sh9
-rw-r--r--cyw43/src/runner.rs8
-rw-r--r--docs/modules/ROOT/pages/faq.adoc7
-rw-r--r--embassy-boot/src/firmware_updater/asynch.rs158
-rw-r--r--embassy-boot/src/firmware_updater/blocking.rs144
-rw-r--r--embassy-net/Cargo.toml4
-rw-r--r--embassy-nrf/src/pdm.rs55
-rw-r--r--embassy-nrf/src/spim.rs112
-rw-r--r--embassy-nrf/src/util.rs13
-rw-r--r--embassy-rp/src/gpio.rs11
-rw-r--r--embassy-rp/src/i2c_slave.rs235
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs122
-rw-r--r--embassy-stm32/src/gpio.rs2
-rw-r--r--embassy-stm32/src/hash/mod.rs592
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/f0.rs196
-rw-r--r--embassy-stm32/src/rcc/f013.rs448
-rw-r--r--embassy-stm32/src/rcc/f1.rs191
-rw-r--r--embassy-stm32/src/rcc/f247.rs (renamed from embassy-stm32/src/rcc/f.rs)0
-rw-r--r--embassy-stm32/src/rcc/f3.rs461
-rw-r--r--embassy-stm32/src/rcc/g4.rs354
-rw-r--r--embassy-stm32/src/rcc/h.rs27
-rw-r--r--embassy-stm32/src/rcc/mco.rs24
-rw-r--r--embassy-stm32/src/rcc/mod.rs16
-rw-r--r--embassy-stm32/src/rcc/wba.rs2
-rw-r--r--embassy-stm32/src/time_driver.rs66
-rw-r--r--embassy-usb-logger/src/lib.rs84
-rw-r--r--examples/rp/src/bin/i2c_slave.rs2
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs7
-rw-r--r--examples/rp/src/bin/usb_serial_with_logger.rs117
-rw-r--r--examples/stm32f0/Cargo.toml2
-rw-r--r--examples/stm32f1/src/bin/hello.rs4
-rw-r--r--examples/stm32f1/src/bin/usb_serial.rs20
-rw-r--r--examples/stm32f3/src/bin/hello.rs5
-rw-r--r--examples/stm32f3/src/bin/usb_serial.rs21
-rw-r--r--examples/stm32f334/src/bin/adc.rs24
-rw-r--r--examples/stm32f334/src/bin/hello.rs5
-rw-r--r--examples/stm32f334/src/bin/opamp.rs24
-rw-r--r--examples/stm32f334/src/bin/pwm.rs27
-rw-r--r--examples/stm32f7/.cargo/config.toml2
-rw-r--r--examples/stm32f7/Cargo.toml6
-rw-r--r--examples/stm32f7/src/bin/eth.rs2
-rw-r--r--examples/stm32f7/src/bin/hash.rs78
-rw-r--r--examples/stm32g4/src/bin/adc.rs16
-rw-r--r--examples/stm32g4/src/bin/pll.rs16
-rw-r--r--examples/stm32g4/src/bin/usb_serial.rs31
-rw-r--r--tests/stm32/.cargo/config.toml4
-rw-r--r--tests/stm32/Cargo.toml24
-rw-r--r--tests/stm32/build.rs2
-rw-r--r--tests/stm32/src/bin/hash.rs101
-rw-r--r--tests/stm32/src/common.rs92
52 files changed, 2554 insertions, 1425 deletions
diff --git a/ci.sh b/ci.sh
index 58a288441..ef706bdc5 100755
--- a/ci.sh
+++ b/ci.sh
@@ -88,6 +88,13 @@ cargo batch \
88 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \ 88 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
89 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \ 89 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
90 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \ 90 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
91 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
92 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
93 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \
94 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \
95 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \
96 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \
97 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \
91 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \ 98 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \
92 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \ 99 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \
93 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \ 100 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \
@@ -223,6 +230,8 @@ cargo batch \
223 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \ 230 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
224 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \ 231 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
225 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \ 232 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
233 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --out-dir out/tests/stm32f091rc \
234 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \
226 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ 235 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
227 --- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ 236 --- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
228 --- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \ 237 --- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \
diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs
index b2a9e3e80..c72cf0def 100644
--- a/cyw43/src/runner.rs
+++ b/cyw43/src/runner.rs
@@ -242,13 +242,12 @@ where
242 cmd, 242 cmd,
243 iface, 243 iface,
244 }) => { 244 }) => {
245 self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; 245 self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;
246 self.check_status(&mut buf).await; 246 self.check_status(&mut buf).await;
247 } 247 }
248 Either3::Second(packet) => { 248 Either3::Second(packet) => {
249 trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); 249 trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
250 250
251 let mut buf = [0; 512];
252 let buf8 = slice8_mut(&mut buf); 251 let buf8 = slice8_mut(&mut buf);
253 252
254 // There MUST be 2 bytes of padding between the SDPCM and BDC headers. 253 // There MUST be 2 bytes of padding between the SDPCM and BDC headers.
@@ -480,9 +479,8 @@ where
480 self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 479 self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
481 } 480 }
482 481
483 async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { 482 async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
484 let mut buf = [0; 512]; 483 let buf8 = slice8_mut(buf);
485 let buf8 = slice8_mut(&mut buf);
486 484
487 let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); 485 let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
488 486
diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc
index 7fb81e2ca..6b5e6d009 100644
--- a/docs/modules/ROOT/pages/faq.adoc
+++ b/docs/modules/ROOT/pages/faq.adoc
@@ -118,6 +118,13 @@ features = [
118] 118]
119---- 119----
120 120
121If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source:
122
123[source,rust]
124----
125use embassy_stm32 as _;
126----
127
121== Error: `Only one package in the dependency graph may specify the same links value.` 128== Error: `Only one package in the dependency graph may specify the same links value.`
122 129
123You have multiple versions of the same crate in your dependency tree. This means that some of your 130You have multiple versions of the same crate in your dependency tree. This means that some of your
diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs
index 668f16f16..26f65f295 100644
--- a/embassy-boot/src/firmware_updater/asynch.rs
+++ b/embassy-boot/src/firmware_updater/asynch.rs
@@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
13pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { 13pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
14 dfu: DFU, 14 dfu: DFU,
15 state: FirmwareState<'d, STATE>, 15 state: FirmwareState<'d, STATE>,
16 last_erased_dfu_sector_index: Option<usize>,
16} 17}
17 18
18#[cfg(target_os = "none")] 19#[cfg(target_os = "none")]
@@ -56,6 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
56 Self { 57 Self {
57 dfu: config.dfu, 58 dfu: config.dfu,
58 state: FirmwareState::new(config.state, aligned), 59 state: FirmwareState::new(config.state, aligned),
60 last_erased_dfu_sector_index: None,
59 } 61 }
60 } 62 }
61 63
@@ -72,7 +74,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
72 /// proceed with updating the firmware as it must be signed with a 74 /// proceed with updating the firmware as it must be signed with a
73 /// corresponding private key (otherwise it could be malicious firmware). 75 /// corresponding private key (otherwise it could be malicious firmware).
74 /// 76 ///
75 /// Mark to trigger firmware swap on next boot if verify suceeds. 77 /// Mark to trigger firmware swap on next boot if verify succeeds.
76 /// 78 ///
77 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have 79 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
78 /// been generated from a SHA-512 digest of the firmware bytes. 80 /// been generated from a SHA-512 digest of the firmware bytes.
@@ -172,21 +174,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
172 self.state.mark_booted().await 174 self.state.mark_booted().await
173 } 175 }
174 176
175 /// Write data to a flash page. 177 /// Writes firmware data to the device.
176 /// 178 ///
177 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. 179 /// This function writes the given data to the firmware area starting at the specified offset.
180 /// It handles sector erasures and data writes while verifying the device is in a proper state
181 /// for firmware updates. The function ensures that only unerased sectors are erased before
182 /// writing and efficiently handles the writing process across sector boundaries and in
183 /// various configurations (data size, sector size, etc.).
178 /// 184 ///
179 /// # Safety 185 /// # Arguments
186 ///
187 /// * `offset` - The starting offset within the firmware area where data writing should begin.
188 /// * `data` - A slice of bytes representing the firmware data to be written. It must be a
189 /// multiple of NorFlash WRITE_SIZE.
190 ///
191 /// # Returns
192 ///
193 /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
194 ///
195 /// # Errors
180 /// 196 ///
181 /// Failing to meet alignment and size requirements may result in a panic. 197 /// This function will return an error if:
198 ///
199 /// - The device is not in a proper state to receive firmware updates (e.g., not booted).
200 /// - There is a failure erasing a sector before writing.
201 /// - There is a failure writing data to the device.
182 pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { 202 pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
183 assert!(data.len() >= DFU::ERASE_SIZE); 203 // Make sure we are running a booted firmware to avoid reverting to a bad state.
184
185 self.state.verify_booted().await?; 204 self.state.verify_booted().await?;
186 205
187 self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; 206 // Initialize variables to keep track of the remaining data and the current offset.
207 let mut remaining_data = data;
208 let mut offset = offset;
209
210 // Continue writing as long as there is data left to write.
211 while !remaining_data.is_empty() {
212 // Compute the current sector and its boundaries.
213 let current_sector = offset / DFU::ERASE_SIZE;
214 let sector_start = current_sector * DFU::ERASE_SIZE;
215 let sector_end = sector_start + DFU::ERASE_SIZE;
216 // Determine if the current sector needs to be erased before writing.
217 let need_erase = self
218 .last_erased_dfu_sector_index
219 .map_or(true, |last_erased_sector| current_sector != last_erased_sector);
220
221 // If the sector needs to be erased, erase it and update the last erased sector index.
222 if need_erase {
223 self.dfu.erase(sector_start as u32, sector_end as u32).await?;
224 self.last_erased_dfu_sector_index = Some(current_sector);
225 }
226
227 // Calculate the size of the data chunk that can be written in the current iteration.
228 let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
229 // Split the data to get the current chunk to be written and the remaining data.
230 let (data_chunk, rest) = remaining_data.split_at(write_size);
188 231
189 self.dfu.write(offset as u32, data).await?; 232 // Write the current data chunk.
233 self.dfu.write(offset as u32, data_chunk).await?;
234
235 // Update the offset and remaining data for the next iteration.
236 remaining_data = rest;
237 offset += write_size;
238 }
190 239
191 Ok(()) 240 Ok(())
192 } 241 }
@@ -276,16 +325,25 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
276 async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { 325 async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
277 self.state.read(0, &mut self.aligned).await?; 326 self.state.read(0, &mut self.aligned).await?;
278 327
279 if self.aligned.iter().any(|&b| b != magic) { 328 if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) {
280 // Read progress validity 329 // Read progress validity
281 self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; 330 if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE {
331 self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
332 } else {
333 self.aligned.rotate_left(STATE::WRITE_SIZE);
334 }
282 335
283 if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { 336 if self.aligned[..STATE::WRITE_SIZE]
337 .iter()
338 .any(|&b| b != STATE_ERASE_VALUE)
339 {
284 // The current progress validity marker is invalid 340 // The current progress validity marker is invalid
285 } else { 341 } else {
286 // Invalidate progress 342 // Invalidate progress
287 self.aligned.fill(!STATE_ERASE_VALUE); 343 self.aligned.fill(!STATE_ERASE_VALUE);
288 self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; 344 self.state
345 .write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE])
346 .await?;
289 } 347 }
290 348
291 // Clear magic and progress 349 // Clear magic and progress
@@ -293,7 +351,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
293 351
294 // Set magic 352 // Set magic
295 self.aligned.fill(magic); 353 self.aligned.fill(magic);
296 self.state.write(0, &self.aligned).await?; 354 self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?;
297 } 355 }
298 Ok(()) 356 Ok(())
299 } 357 }
@@ -329,4 +387,76 @@ mod tests {
329 387
330 assert_eq!(Sha1::digest(update).as_slice(), hash); 388 assert_eq!(Sha1::digest(update).as_slice(), hash);
331 } 389 }
390
391 #[test]
392 fn can_verify_sha1_sector_bigger_than_chunk() {
393 let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
394 let state = Partition::new(&flash, 0, 4096);
395 let dfu = Partition::new(&flash, 65536, 65536);
396 let mut aligned = [0; 8];
397
398 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
399 let mut to_write = [0; 4096];
400 to_write[..7].copy_from_slice(update.as_slice());
401
402 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
403 let mut offset = 0;
404 for chunk in to_write.chunks(1024) {
405 block_on(updater.write_firmware(offset, chunk)).unwrap();
406 offset += chunk.len();
407 }
408 let mut chunk_buf = [0; 2];
409 let mut hash = [0; 20];
410 block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
411
412 assert_eq!(Sha1::digest(update).as_slice(), hash);
413 }
414
415 #[test]
416 fn can_verify_sha1_sector_smaller_than_chunk() {
417 let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
418 let state = Partition::new(&flash, 0, 4096);
419 let dfu = Partition::new(&flash, 65536, 65536);
420 let mut aligned = [0; 8];
421
422 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
423 let mut to_write = [0; 4096];
424 to_write[..7].copy_from_slice(update.as_slice());
425
426 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
427 let mut offset = 0;
428 for chunk in to_write.chunks(2048) {
429 block_on(updater.write_firmware(offset, chunk)).unwrap();
430 offset += chunk.len();
431 }
432 let mut chunk_buf = [0; 2];
433 let mut hash = [0; 20];
434 block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
435
436 assert_eq!(Sha1::digest(update).as_slice(), hash);
437 }
438
439 #[test]
440 fn can_verify_sha1_cross_sector_boundary() {
441 let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
442 let state = Partition::new(&flash, 0, 4096);
443 let dfu = Partition::new(&flash, 65536, 65536);
444 let mut aligned = [0; 8];
445
446 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
447 let mut to_write = [0; 4096];
448 to_write[..7].copy_from_slice(update.as_slice());
449
450 let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
451 let mut offset = 0;
452 for chunk in to_write.chunks(896) {
453 block_on(updater.write_firmware(offset, chunk)).unwrap();
454 offset += chunk.len();
455 }
456 let mut chunk_buf = [0; 2];
457 let mut hash = [0; 20];
458 block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
459
460 assert_eq!(Sha1::digest(update).as_slice(), hash);
461 }
332} 462}
diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs
index 4044871f0..35772a856 100644
--- a/embassy-boot/src/firmware_updater/blocking.rs
+++ b/embassy-boot/src/firmware_updater/blocking.rs
@@ -13,6 +13,7 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERA
13pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { 13pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
14 dfu: DFU, 14 dfu: DFU,
15 state: BlockingFirmwareState<'d, STATE>, 15 state: BlockingFirmwareState<'d, STATE>,
16 last_erased_dfu_sector_index: Option<usize>,
16} 17}
17 18
18#[cfg(target_os = "none")] 19#[cfg(target_os = "none")]
@@ -91,6 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
91 Self { 92 Self {
92 dfu: config.dfu, 93 dfu: config.dfu,
93 state: BlockingFirmwareState::new(config.state, aligned), 94 state: BlockingFirmwareState::new(config.state, aligned),
95 last_erased_dfu_sector_index: None,
94 } 96 }
95 } 97 }
96 98
@@ -107,7 +109,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
107 /// proceed with updating the firmware as it must be signed with a 109 /// proceed with updating the firmware as it must be signed with a
108 /// corresponding private key (otherwise it could be malicious firmware). 110 /// corresponding private key (otherwise it could be malicious firmware).
109 /// 111 ///
110 /// Mark to trigger firmware swap on next boot if verify suceeds. 112 /// Mark to trigger firmware swap on next boot if verify succeeds.
111 /// 113 ///
112 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have 114 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
113 /// been generated from a SHA-512 digest of the firmware bytes. 115 /// been generated from a SHA-512 digest of the firmware bytes.
@@ -207,20 +209,68 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
207 self.state.mark_booted() 209 self.state.mark_booted()
208 } 210 }
209 211
210 /// Write data to a flash page. 212 /// Writes firmware data to the device.
211 /// 213 ///
212 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. 214 /// This function writes the given data to the firmware area starting at the specified offset.
215 /// It handles sector erasures and data writes while verifying the device is in a proper state
216 /// for firmware updates. The function ensures that only unerased sectors are erased before
217 /// writing and efficiently handles the writing process across sector boundaries and in
218 /// various configurations (data size, sector size, etc.).
213 /// 219 ///
214 /// # Safety 220 /// # Arguments
221 ///
222 /// * `offset` - The starting offset within the firmware area where data writing should begin.
223 /// * `data` - A slice of bytes representing the firmware data to be written. It must be a
224 /// multiple of NorFlash WRITE_SIZE.
225 ///
226 /// # Returns
227 ///
228 /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
215 /// 229 ///
216 /// Failing to meet alignment and size requirements may result in a panic. 230 /// # Errors
231 ///
232 /// This function will return an error if:
233 ///
234 /// - The device is not in a proper state to receive firmware updates (e.g., not booted).
235 /// - There is a failure erasing a sector before writing.
236 /// - There is a failure writing data to the device.
217 pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { 237 pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
218 assert!(data.len() >= DFU::ERASE_SIZE); 238 // Make sure we are running a booted firmware to avoid reverting to a bad state.
219 self.state.verify_booted()?; 239 self.state.verify_booted()?;
220 240
221 self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; 241 // Initialize variables to keep track of the remaining data and the current offset.
242 let mut remaining_data = data;
243 let mut offset = offset;
244
245 // Continue writing as long as there is data left to write.
246 while !remaining_data.is_empty() {
247 // Compute the current sector and its boundaries.
248 let current_sector = offset / DFU::ERASE_SIZE;
249 let sector_start = current_sector * DFU::ERASE_SIZE;
250 let sector_end = sector_start + DFU::ERASE_SIZE;
251 // Determine if the current sector needs to be erased before writing.
252 let need_erase = self
253 .last_erased_dfu_sector_index
254 .map_or(true, |last_erased_sector| current_sector != last_erased_sector);
255
256 // If the sector needs to be erased, erase it and update the last erased sector index.
257 if need_erase {
258 self.dfu.erase(sector_start as u32, sector_end as u32)?;
259 self.last_erased_dfu_sector_index = Some(current_sector);
260 }
261
262 // Calculate the size of the data chunk that can be written in the current iteration.
263 let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
264 // Split the data to get the current chunk to be written and the remaining data.
265 let (data_chunk, rest) = remaining_data.split_at(write_size);
222 266
223 self.dfu.write(offset as u32, data)?; 267 // Write the current data chunk.
268 self.dfu.write(offset as u32, data_chunk)?;
269
270 // Update the offset and remaining data for the next iteration.
271 remaining_data = rest;
272 offset += write_size;
273 }
224 274
225 Ok(()) 275 Ok(())
226 } 276 }
@@ -368,4 +418,82 @@ mod tests {
368 418
369 assert_eq!(Sha1::digest(update).as_slice(), hash); 419 assert_eq!(Sha1::digest(update).as_slice(), hash);
370 } 420 }
421
422 #[test]
423 fn can_verify_sha1_sector_bigger_than_chunk() {
424 let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
425 let state = BlockingPartition::new(&flash, 0, 4096);
426 let dfu = BlockingPartition::new(&flash, 65536, 65536);
427 let mut aligned = [0; 8];
428
429 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
430 let mut to_write = [0; 4096];
431 to_write[..7].copy_from_slice(update.as_slice());
432
433 let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
434 let mut offset = 0;
435 for chunk in to_write.chunks(1024) {
436 updater.write_firmware(offset, chunk).unwrap();
437 offset += chunk.len();
438 }
439 let mut chunk_buf = [0; 2];
440 let mut hash = [0; 20];
441 updater
442 .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
443 .unwrap();
444
445 assert_eq!(Sha1::digest(update).as_slice(), hash);
446 }
447
448 #[test]
449 fn can_verify_sha1_sector_smaller_than_chunk() {
450 let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
451 let state = BlockingPartition::new(&flash, 0, 4096);
452 let dfu = BlockingPartition::new(&flash, 65536, 65536);
453 let mut aligned = [0; 8];
454
455 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
456 let mut to_write = [0; 4096];
457 to_write[..7].copy_from_slice(update.as_slice());
458
459 let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
460 let mut offset = 0;
461 for chunk in to_write.chunks(2048) {
462 updater.write_firmware(offset, chunk).unwrap();
463 offset += chunk.len();
464 }
465 let mut chunk_buf = [0; 2];
466 let mut hash = [0; 20];
467 updater
468 .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
469 .unwrap();
470
471 assert_eq!(Sha1::digest(update).as_slice(), hash);
472 }
473
474 #[test]
475 fn can_verify_sha1_cross_sector_boundary() {
476 let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
477 let state = BlockingPartition::new(&flash, 0, 4096);
478 let dfu = BlockingPartition::new(&flash, 65536, 65536);
479 let mut aligned = [0; 8];
480
481 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
482 let mut to_write = [0; 4096];
483 to_write[..7].copy_from_slice(update.as_slice());
484
485 let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
486 let mut offset = 0;
487 for chunk in to_write.chunks(896) {
488 updater.write_firmware(offset, chunk).unwrap();
489 offset += chunk.len();
490 }
491 let mut chunk_buf = [0; 2];
492 let mut hash = [0; 20];
493 updater
494 .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
495 .unwrap();
496
497 assert_eq!(Sha1::digest(update).as_slice(), hash);
498 }
371} 499}
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 44bd2e8f3..be9f1d784 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -16,11 +16,11 @@ categories = [
16[package.metadata.embassy_docs] 16[package.metadata.embassy_docs]
17src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" 17src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
18src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" 18src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
19features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] 19features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
20target = "thumbv7em-none-eabi" 20target = "thumbv7em-none-eabi"
21 21
22[package.metadata.docs.rs] 22[package.metadata.docs.rs]
23features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] 23features = ["defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp", "dhcpv4-hostname"]
24 24
25[features] 25[features]
26default = [] 26default = []
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
index 24fa29a4a..6ddc4dc0a 100644
--- a/embassy-nrf/src/pdm.rs
+++ b/embassy-nrf/src/pdm.rs
@@ -1,4 +1,4 @@
1//! Pulse Density Modulation (PDM) mirophone driver. 1//! Pulse Density Modulation (PDM) mirophone driver
2 2
3#![macro_use] 3#![macro_use]
4 4
@@ -26,7 +26,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
26pub use crate::pac::pdm::ratio::RATIO_A as Ratio; 26pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
27use crate::{interrupt, Peripheral}; 27use crate::{interrupt, Peripheral};
28 28
29/// Interrupt handler. 29/// Interrupt handler
30pub struct InterruptHandler<T: Instance> { 30pub struct InterruptHandler<T: Instance> {
31 _phantom: PhantomData<T>, 31 _phantom: PhantomData<T>,
32} 32}
@@ -56,12 +56,12 @@ pub struct Pdm<'d, T: Instance> {
56 _peri: PeripheralRef<'d, T>, 56 _peri: PeripheralRef<'d, T>,
57} 57}
58 58
59/// PDM error. 59/// PDM error
60#[derive(Debug, Clone, Copy, PartialEq, Eq)] 60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[cfg_attr(feature = "defmt", derive(defmt::Format))] 61#[cfg_attr(feature = "defmt", derive(defmt::Format))]
62#[non_exhaustive] 62#[non_exhaustive]
63pub enum Error { 63pub enum Error {
64 /// Buffer is too long. 64 /// Buffer is too long
65 BufferTooLong, 65 BufferTooLong,
66 /// Buffer is empty 66 /// Buffer is empty
67 BufferZeroLength, 67 BufferZeroLength,
@@ -75,13 +75,13 @@ static DUMMY_BUFFER: [i16; 1] = [0; 1];
75 75
76/// The state of a continuously running sampler. While it reflects 76/// The state of a continuously running sampler. While it reflects
77/// the progress of a sampler, it also signals what should be done 77/// the progress of a sampler, it also signals what should be done
78/// next. For example, if the sampler has stopped then the Pdm implementation 78/// next. For example, if the sampler has stopped then the PDM implementation
79/// can then tear down its infrastructure. 79/// can then tear down its infrastructure
80#[derive(PartialEq)] 80#[derive(PartialEq)]
81pub enum SamplerState { 81pub enum SamplerState {
82 /// The sampler processed the samples and is ready for more. 82 /// The sampler processed the samples and is ready for more
83 Sampled, 83 Sampled,
84 /// The sampler is done processing samples. 84 /// The sampler is done processing samples
85 Stopped, 85 Stopped,
86} 86}
87 87
@@ -145,15 +145,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
145 } 145 }
146 146
147 fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { 147 fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
148 let gain_left = gain_left 148 let gain_to_bits = |gain: I7F1| -> u8 {
149 .saturating_add(I7F1::from_bits(40)) 149 let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);
150 .saturating_to_num::<u8>() 150 unsafe { core::mem::transmute(gain) }
151 .clamp(0, 0x50); 151 };
152 let gain_right = gain_right 152 let gain_left = gain_to_bits(gain_left);
153 .saturating_add(I7F1::from_bits(40)) 153 let gain_right = gain_to_bits(gain_right);
154 .saturating_to_num::<u8>()
155 .clamp(0, 0x50);
156
157 r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); 154 r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
158 r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); 155 r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
159 } 156 }
@@ -163,12 +160,12 @@ impl<'d, T: Instance> Pdm<'d, T> {
163 Self::_set_gain(T::regs(), gain_left, gain_right) 160 Self::_set_gain(T::regs(), gain_left, gain_right)
164 } 161 }
165 162
166 /// Start sampling microphon data into a dummy buffer 163 /// Start sampling microphone data into a dummy buffer.
167 /// Usefull to start the microphon and keep it active between recording samples 164 /// Useful to start the microphone and keep it active between recording samples.
168 pub async fn start(&mut self) { 165 pub async fn start(&mut self) {
169 let r = T::regs(); 166 let r = T::regs();
170 167
171 // start dummy sampling because microphon needs some setup time 168 // start dummy sampling because microphone needs some setup time
172 r.sample 169 r.sample
173 .ptr 170 .ptr
174 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); 171 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
@@ -179,14 +176,14 @@ impl<'d, T: Instance> Pdm<'d, T> {
179 r.tasks_start.write(|w| unsafe { w.bits(1) }); 176 r.tasks_start.write(|w| unsafe { w.bits(1) });
180 } 177 }
181 178
182 /// Stop sampling microphon data inta a dummy buffer 179 /// Stop sampling microphone data inta a dummy buffer
183 pub async fn stop(&mut self) { 180 pub async fn stop(&mut self) {
184 let r = T::regs(); 181 let r = T::regs();
185 r.tasks_stop.write(|w| unsafe { w.bits(1) }); 182 r.tasks_stop.write(|w| unsafe { w.bits(1) });
186 r.events_started.reset(); 183 r.events_started.reset();
187 } 184 }
188 185
189 /// Sample data into the given buffer. 186 /// Sample data into the given buffer
190 pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { 187 pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
191 if buffer.len() == 0 { 188 if buffer.len() == 0 {
192 return Err(Error::BufferZeroLength); 189 return Err(Error::BufferZeroLength);
@@ -303,7 +300,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
303 }); 300 });
304 301
305 // Don't reorder the start event before the previous writes. Hopefully self 302 // Don't reorder the start event before the previous writes. Hopefully self
306 // wouldn't happen anyway. 303 // wouldn't happen anyway
307 compiler_fence(Ordering::SeqCst); 304 compiler_fence(Ordering::SeqCst);
308 305
309 r.tasks_start.write(|w| unsafe { w.bits(1) }); 306 r.tasks_start.write(|w| unsafe { w.bits(1) });
@@ -314,11 +311,11 @@ impl<'d, T: Instance> Pdm<'d, T> {
314 311
315 let drop = OnDrop::new(|| { 312 let drop = OnDrop::new(|| {
316 r.tasks_stop.write(|w| unsafe { w.bits(1) }); 313 r.tasks_stop.write(|w| unsafe { w.bits(1) });
317 // N.B. It would be better if this were async, but Drop only support sync code. 314 // N.B. It would be better if this were async, but Drop only support sync code
318 while r.events_stopped.read().bits() != 0 {} 315 while r.events_stopped.read().bits() != 0 {}
319 }); 316 });
320 317
321 // Wait for events and complete when the sampler indicates it has had enough. 318 // Wait for events and complete when the sampler indicates it has had enough
322 poll_fn(|cx| { 319 poll_fn(|cx| {
323 let r = T::regs(); 320 let r = T::regs();
324 321
@@ -331,7 +328,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
331 r.intenset.write(|w| w.end().set()); 328 r.intenset.write(|w| w.end().set());
332 329
333 if !done { 330 if !done {
334 // Discard the last buffer after the user requested a stop. 331 // Discard the last buffer after the user requested a stop
335 if sampler(&bufs[current_buffer]) == SamplerState::Sampled { 332 if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
336 let next_buffer = 1 - current_buffer; 333 let next_buffer = 1 - current_buffer;
337 current_buffer = next_buffer; 334 current_buffer = next_buffer;
@@ -405,7 +402,7 @@ impl Default for Config {
405 } 402 }
406} 403}
407 404
408/// PDM operation mode. 405/// PDM operation mode
409#[derive(PartialEq)] 406#[derive(PartialEq)]
410pub enum OperationMode { 407pub enum OperationMode {
411 /// Mono (1 channel) 408 /// Mono (1 channel)
@@ -476,9 +473,9 @@ pub(crate) mod sealed {
476 } 473 }
477} 474}
478 475
479/// PDM peripheral instance. 476/// PDM peripheral instance
480pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { 477pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
481 /// Interrupt for this peripheral. 478 /// Interrupt for this peripheral
482 type Interrupt: interrupt::typelevel::Interrupt; 479 type Interrupt: interrupt::typelevel::Interrupt;
483} 480}
484 481
diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs
index 8937159df..2d70732a8 100644
--- a/embassy-nrf/src/spim.rs
+++ b/embassy-nrf/src/spim.rs
@@ -17,7 +17,7 @@ use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
17use crate::gpio::sealed::Pin as _; 17use crate::gpio::sealed::Pin as _;
18use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; 18use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
19use crate::interrupt::typelevel::Interrupt; 19use crate::interrupt::typelevel::Interrupt;
20use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; 20use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
21use crate::{interrupt, pac, Peripheral}; 21use crate::{interrupt, pac, Peripheral};
22 22
23/// SPIM error 23/// SPIM error
@@ -25,10 +25,6 @@ use crate::{interrupt, pac, Peripheral};
25#[cfg_attr(feature = "defmt", derive(defmt::Format))] 25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[non_exhaustive] 26#[non_exhaustive]
27pub enum Error { 27pub enum Error {
28 /// Supplied TX buffer overflows EasyDMA transmit buffer
29 TxBufferTooLong,
30 /// Supplied RX buffer overflows EasyDMA receive buffer
31 RxBufferTooLong,
32 /// EasyDMA can only read from data memory, read only buffers in flash will fail. 28 /// EasyDMA can only read from data memory, read only buffers in flash will fail.
33 BufferNotInRAM, 29 BufferNotInRAM,
34} 30}
@@ -74,9 +70,13 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
74 let s = T::state(); 70 let s = T::state();
75 71
76 #[cfg(feature = "_nrf52832_anomaly_109")] 72 #[cfg(feature = "_nrf52832_anomaly_109")]
77 if r.events_started.read().bits() != 0 { 73 {
78 s.waker.wake(); 74 // Ideally we should call this only during the first chunk transfer,
79 r.intenclr.write(|w| w.started().clear()); 75 // but so far calling this every time doesn't seem to be causing any issues.
76 if r.events_started.read().bits() != 0 {
77 s.waker.wake();
78 r.intenclr.write(|w| w.started().clear());
79 }
80 } 80 }
81 81
82 if r.events_end.read().bits() != 0 { 82 if r.events_end.read().bits() != 0 {
@@ -209,35 +209,39 @@ impl<'d, T: Instance> Spim<'d, T> {
209 spim 209 spim
210 } 210 }
211 211
212 fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 212 fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
213 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
214 // NOTE: RAM slice check for rx is not necessary, as a mutable
215 // slice can only be built from data located in RAM.
216
217 compiler_fence(Ordering::SeqCst); 213 compiler_fence(Ordering::SeqCst);
218 214
219 let r = T::regs(); 215 let r = T::regs();
220 216
221 // Set up the DMA write. 217 fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) {
222 let (ptr, tx_len) = slice_ptr_parts(tx); 218 if total > offset {
223 if tx_len > EASY_DMA_SIZE { 219 (ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length))
224 return Err(Error::TxBufferTooLong); 220 } else {
221 (ptr, 0)
222 }
225 } 223 }
226 224
227 r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
228 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
229
230 // Set up the DMA read. 225 // Set up the DMA read.
231 let (ptr, rx_len) = slice_ptr_parts_mut(rx); 226 let (ptr, len) = slice_ptr_parts_mut(rx);
232 if rx_len > EASY_DMA_SIZE { 227 let (rx_ptr, rx_len) = xfer_params(ptr as _, len as _, offset, length);
233 return Err(Error::RxBufferTooLong); 228 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx_ptr) });
234 }
235
236 r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
237 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) }); 229 r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
238 230
231 // Set up the DMA write.
232 let (ptr, len) = slice_ptr_parts(tx);
233 let (tx_ptr, tx_len) = xfer_params(ptr as _, len as _, offset, length);
234 r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx_ptr) });
235 r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
236
237 /*
238 trace!("XFER: offset: {}, length: {}", offset, length);
239 trace!("RX(len: {}, ptr: {=u32:02x})", rx_len, rx_ptr as u32);
240 trace!("TX(len: {}, ptr: {=u32:02x})", tx_len, tx_ptr as u32);
241 */
242
239 #[cfg(feature = "_nrf52832_anomaly_109")] 243 #[cfg(feature = "_nrf52832_anomaly_109")]
240 { 244 if offset == 0 {
241 let s = T::state(); 245 let s = T::state();
242 246
243 r.events_started.reset(); 247 r.events_started.reset();
@@ -260,21 +264,32 @@ impl<'d, T: Instance> Spim<'d, T> {
260 264
261 // Start SPI transaction. 265 // Start SPI transaction.
262 r.tasks_start.write(|w| unsafe { w.bits(1) }); 266 r.tasks_start.write(|w| unsafe { w.bits(1) });
263
264 Ok(())
265 } 267 }
266 268
267 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 269 fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
268 self.prepare(rx, tx)?; 270 self.prepare_dma_transfer(rx, tx, offset, length);
269 271
270 #[cfg(feature = "_nrf52832_anomaly_109")] 272 #[cfg(feature = "_nrf52832_anomaly_109")]
271 while let Poll::Pending = self.nrf52832_dma_workaround_status() {} 273 if offset == 0 {
274 while self.nrf52832_dma_workaround_status().is_pending() {}
275 }
272 276
273 // Wait for 'end' event. 277 // Wait for 'end' event.
274 while T::regs().events_end.read().bits() == 0 {} 278 while T::regs().events_end.read().bits() == 0 {}
275 279
276 compiler_fence(Ordering::SeqCst); 280 compiler_fence(Ordering::SeqCst);
281 }
277 282
283 fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
284 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
285 // NOTE: RAM slice check for rx is not necessary, as a mutable
286 // slice can only be built from data located in RAM.
287
288 let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
289 for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
290 let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
291 self.blocking_inner_from_ram_chunk(rx, tx, offset, length);
292 }
278 Ok(()) 293 Ok(())
279 } 294 }
280 295
@@ -287,22 +302,23 @@ impl<'d, T: Instance> Spim<'d, T> {
287 tx_ram_buf.copy_from_slice(tx); 302 tx_ram_buf.copy_from_slice(tx);
288 self.blocking_inner_from_ram(rx, tx_ram_buf) 303 self.blocking_inner_from_ram(rx, tx_ram_buf)
289 } 304 }
290 Err(error) => Err(error),
291 } 305 }
292 } 306 }
293 307
294 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { 308 async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {
295 self.prepare(rx, tx)?; 309 self.prepare_dma_transfer(rx, tx, offset, length);
296 310
297 #[cfg(feature = "_nrf52832_anomaly_109")] 311 #[cfg(feature = "_nrf52832_anomaly_109")]
298 poll_fn(|cx| { 312 if offset == 0 {
299 let s = T::state(); 313 poll_fn(|cx| {
314 let s = T::state();
300 315
301 s.waker.register(cx.waker()); 316 s.waker.register(cx.waker());
302 317
303 self.nrf52832_dma_workaround_status() 318 self.nrf52832_dma_workaround_status()
304 }) 319 })
305 .await; 320 .await;
321 }
306 322
307 // Wait for 'end' event. 323 // Wait for 'end' event.
308 poll_fn(|cx| { 324 poll_fn(|cx| {
@@ -316,7 +332,18 @@ impl<'d, T: Instance> Spim<'d, T> {
316 .await; 332 .await;
317 333
318 compiler_fence(Ordering::SeqCst); 334 compiler_fence(Ordering::SeqCst);
335 }
336
337 async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
338 slice_in_ram_or(tx, Error::BufferNotInRAM)?;
339 // NOTE: RAM slice check for rx is not necessary, as a mutable
340 // slice can only be built from data located in RAM.
319 341
342 let xfer_len = core::cmp::max(slice_ptr_len(rx), slice_ptr_len(tx));
343 for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {
344 let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);
345 self.async_inner_from_ram_chunk(rx, tx, offset, length).await;
346 }
320 Ok(()) 347 Ok(())
321 } 348 }
322 349
@@ -329,7 +356,6 @@ impl<'d, T: Instance> Spim<'d, T> {
329 tx_ram_buf.copy_from_slice(tx); 356 tx_ram_buf.copy_from_slice(tx);
330 self.async_inner_from_ram(rx, tx_ram_buf).await 357 self.async_inner_from_ram(rx, tx_ram_buf).await
331 } 358 }
332 Err(error) => Err(error),
333 } 359 }
334 } 360 }
335 361
@@ -528,8 +554,6 @@ mod eh02 {
528impl embedded_hal_1::spi::Error for Error { 554impl embedded_hal_1::spi::Error for Error {
529 fn kind(&self) -> embedded_hal_1::spi::ErrorKind { 555 fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
530 match *self { 556 match *self {
531 Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
532 Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other,
533 Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, 557 Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,
534 } 558 }
535 } 559 }
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs
index b408c517b..6cdb97f08 100644
--- a/embassy-nrf/src/util.rs
+++ b/embassy-nrf/src/util.rs
@@ -4,6 +4,19 @@ use core::mem;
4const SRAM_LOWER: usize = 0x2000_0000; 4const SRAM_LOWER: usize = 0x2000_0000;
5const SRAM_UPPER: usize = 0x3000_0000; 5const SRAM_UPPER: usize = 0x3000_0000;
6 6
7// #![feature(const_slice_ptr_len)]
8// https://github.com/rust-lang/rust/issues/71146
9pub(crate) fn slice_ptr_len<T>(ptr: *const [T]) -> usize {
10 use core::ptr::NonNull;
11 let ptr = ptr.cast_mut();
12 if let Some(ptr) = NonNull::new(ptr) {
13 ptr.len()
14 } else {
15 // We know ptr is null, so we know ptr.wrapping_byte_add(1) is not null.
16 NonNull::new(ptr.wrapping_byte_add(1)).unwrap().len()
17 }
18}
19
7// TODO: replace transmutes with core::ptr::metadata once it's stable 20// TODO: replace transmutes with core::ptr::metadata once it's stable
8pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) { 21pub(crate) fn slice_ptr_parts<T>(slice: *const [T]) -> (*const T, usize) {
9 unsafe { mem::transmute(slice) } 22 unsafe { mem::transmute(slice) }
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index 93b29bbf9..a121a8036 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -889,6 +889,17 @@ pub struct AnyPin {
889 pin_bank: u8, 889 pin_bank: u8,
890} 890}
891 891
892impl AnyPin {
893 /// Unsafely create a new type-erased pin.
894 ///
895 /// # Safety
896 ///
897 /// You must ensure that you’re only using one instance of this type at a time.
898 pub unsafe fn steal(pin_bank: u8) -> Self {
899 Self { pin_bank }
900 }
901}
902
892impl_peripheral!(AnyPin); 903impl_peripheral!(AnyPin);
893 904
894impl Pin for AnyPin {} 905impl Pin for AnyPin {}
diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs
index 721b7a1f6..979686627 100644
--- a/embassy-rp/src/i2c_slave.rs
+++ b/embassy-rp/src/i2c_slave.rs
@@ -21,6 +21,16 @@ pub enum Error {
21 Abort(AbortReason), 21 Abort(AbortReason),
22 /// User passed in a response buffer that was 0 length 22 /// User passed in a response buffer that was 0 length
23 InvalidResponseBufferLength, 23 InvalidResponseBufferLength,
24 /// The response buffer length was too short to contain the message
25 ///
26 /// The length parameter will always be the length of the buffer, and is
27 /// provided as a convenience for matching alongside `Command::Write`.
28 PartialWrite(usize),
29 /// The response buffer length was too short to contain the message
30 ///
31 /// The length parameter will always be the length of the buffer, and is
32 /// provided as a convenience for matching alongside `Command::GeneralCall`.
33 PartialGeneralCall(usize),
24} 34}
25 35
26/// Received command 36/// Received command
@@ -56,17 +66,23 @@ pub enum ReadStatus {
56pub struct Config { 66pub struct Config {
57 /// Target Address 67 /// Target Address
58 pub addr: u16, 68 pub addr: u16,
69 /// Control if the peripheral should ack to and report general calls.
70 pub general_call: bool,
59} 71}
60 72
61impl Default for Config { 73impl Default for Config {
62 fn default() -> Self { 74 fn default() -> Self {
63 Self { addr: 0x55 } 75 Self {
76 addr: 0x55,
77 general_call: true,
78 }
64 } 79 }
65} 80}
66 81
67/// I2CSlave driver. 82/// I2CSlave driver.
68pub struct I2cSlave<'d, T: Instance> { 83pub struct I2cSlave<'d, T: Instance> {
69 phantom: PhantomData<&'d mut T>, 84 phantom: PhantomData<&'d mut T>,
85 pending_byte: Option<u8>,
70} 86}
71 87
72impl<'d, T: Instance> I2cSlave<'d, T> { 88impl<'d, T: Instance> I2cSlave<'d, T> {
@@ -96,7 +112,19 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
96 w.set_master_mode(false); 112 w.set_master_mode(false);
97 w.set_ic_slave_disable(false); 113 w.set_ic_slave_disable(false);
98 w.set_tx_empty_ctrl(true); 114 w.set_tx_empty_ctrl(true);
115 w.set_rx_fifo_full_hld_ctrl(true);
116
117 // This typically makes no sense for a slave, but it is used to
118 // tune spike suppression, according to the datasheet.
119 w.set_speed(pac::i2c::vals::Speed::FAST);
120
121 // Generate stop interrupts for general calls
122 // This also causes stop interrupts for other devices on the bus but those will not be
123 // propagated up to the application.
124 w.set_stop_det_ifaddressed(!config.general_call);
99 }); 125 });
126 p.ic_ack_general_call()
127 .write(|w| w.set_ack_gen_call(config.general_call));
100 128
101 // Set FIFO watermarks to 1 to make things simpler. This is encoded 129 // Set FIFO watermarks to 1 to make things simpler. This is encoded
102 // by a register value of 0. Rx watermark should never change, but Tx watermark will be 130 // by a register value of 0. Rx watermark should never change, but Tx watermark will be
@@ -119,7 +147,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
119 T::Interrupt::unpend(); 147 T::Interrupt::unpend();
120 unsafe { T::Interrupt::enable() }; 148 unsafe { T::Interrupt::enable() };
121 149
122 Self { phantom: PhantomData } 150 Self {
151 phantom: PhantomData,
152 pending_byte: None,
153 }
123 } 154 }
124 155
125 /// Calls `f` to check if we are ready or not. 156 /// Calls `f` to check if we are ready or not.
@@ -133,8 +164,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
133 future::poll_fn(|cx| { 164 future::poll_fn(|cx| {
134 let r = f(self); 165 let r = f(self);
135 166
136 trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
137
138 if r.is_pending() { 167 if r.is_pending() {
139 T::waker().register(cx.waker()); 168 T::waker().register(cx.waker());
140 g(self); 169 g(self);
@@ -146,14 +175,36 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
146 } 175 }
147 176
148 #[inline(always)] 177 #[inline(always)]
149 fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize { 178 fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
150 let p = T::regs(); 179 let p = T::regs();
151 let len = p.ic_rxflr().read().rxflr() as usize; 180
152 let end = offset + len; 181 for b in &mut buffer[*offset..] {
153 for i in offset..end { 182 if let Some(pending) = self.pending_byte.take() {
154 buffer[i] = p.ic_data_cmd().read().dat(); 183 *b = pending;
184 *offset += 1;
185 continue;
186 }
187
188 let status = p.ic_status().read();
189 if !status.rfne() {
190 break;
191 }
192
193 let dat = p.ic_data_cmd().read();
194 if *offset != 0 && dat.first_data_byte() {
195 // The RP2040 state machine will keep placing bytes into the
196 // FIFO, even if they are part of a subsequent write transaction.
197 //
198 // Unfortunately merely reading ic_data_cmd will consume that
199 // byte, the first byte of the next transaction, so we need
200 // to store it elsewhere
201 self.pending_byte = Some(dat.dat());
202 break;
203 }
204
205 *b = dat.dat();
206 *offset += 1;
155 } 207 }
156 end
157 } 208 }
158 209
159 #[inline(always)] 210 #[inline(always)]
@@ -165,52 +216,62 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
165 } 216 }
166 217
167 /// Wait asynchronously for commands from an I2C master. 218 /// Wait asynchronously for commands from an I2C master.
168 /// `buffer` is provided in case master does a 'write' and is unused for 'read'. 219 /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.
169 pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> { 220 pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
170 let p = T::regs(); 221 let p = T::regs();
171 222
172 p.ic_clr_intr().read();
173 // set rx fifo watermark to 1 byte 223 // set rx fifo watermark to 1 byte
174 p.ic_rx_tl().write(|w| w.set_rx_tl(0)); 224 p.ic_rx_tl().write(|w| w.set_rx_tl(0));
175 225
176 let mut len = 0; 226 let mut len = 0;
177 let ret = self 227 self.wait_on(
178 .wait_on( 228 |me| {
179 |me| { 229 let stat = p.ic_raw_intr_stat().read();
180 let stat = p.ic_raw_intr_stat().read(); 230
181 if p.ic_rxflr().read().rxflr() > 0 { 231 if p.ic_rxflr().read().rxflr() > 0 {
182 len = me.drain_fifo(buffer, len); 232 me.drain_fifo(buffer, &mut len);
183 // we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise 233 // we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
184 p.ic_rx_tl().write(|w| w.set_rx_tl(11)); 234 p.ic_rx_tl().write(|w| w.set_rx_tl(11));
185 } 235 }
186 236
187 if stat.restart_det() && stat.rd_req() { 237 if buffer.len() == len {
188 Poll::Ready(Ok(Command::WriteRead(len))) 238 if stat.gen_call() {
189 } else if stat.gen_call() && stat.stop_det() && len > 0 { 239 return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));
190 Poll::Ready(Ok(Command::GeneralCall(len)))
191 } else if stat.stop_det() {
192 Poll::Ready(Ok(Command::Write(len)))
193 } else if stat.rd_req() {
194 Poll::Ready(Ok(Command::Read))
195 } else { 240 } else {
196 Poll::Pending 241 return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
197 } 242 }
198 }, 243 }
199 |_me| { 244
200 p.ic_intr_mask().modify(|w| { 245 if stat.restart_det() && stat.rd_req() {
201 w.set_m_stop_det(true); 246 p.ic_clr_restart_det().read();
202 w.set_m_restart_det(true); 247 Poll::Ready(Ok(Command::WriteRead(len)))
203 w.set_m_gen_call(true); 248 } else if stat.gen_call() && stat.stop_det() && len > 0 {
204 w.set_m_rd_req(true); 249 p.ic_clr_gen_call().read();
205 w.set_m_rx_full(true); 250 p.ic_clr_stop_det().read();
206 }); 251 Poll::Ready(Ok(Command::GeneralCall(len)))
207 }, 252 } else if stat.stop_det() && len > 0 {
208 ) 253 p.ic_clr_stop_det().read();
209 .await; 254 Poll::Ready(Ok(Command::Write(len)))
210 255 } else if stat.rd_req() {
211 p.ic_clr_intr().read(); 256 p.ic_clr_stop_det().read();
212 257 p.ic_clr_restart_det().read();
213 ret 258 p.ic_clr_gen_call().read();
259 Poll::Ready(Ok(Command::Read))
260 } else {
261 Poll::Pending
262 }
263 },
264 |_me| {
265 p.ic_intr_mask().modify(|w| {
266 w.set_m_stop_det(true);
267 w.set_m_restart_det(true);
268 w.set_m_gen_call(true);
269 w.set_m_rd_req(true);
270 w.set_m_rx_full(true);
271 });
272 },
273 )
274 .await
214 } 275 }
215 276
216 /// Respond to an I2C master READ command, asynchronously. 277 /// Respond to an I2C master READ command, asynchronously.
@@ -223,47 +284,47 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
223 284
224 let mut chunks = buffer.chunks(FIFO_SIZE as usize); 285 let mut chunks = buffer.chunks(FIFO_SIZE as usize);
225 286
226 let ret = self 287 self.wait_on(
227 .wait_on( 288 |me| {
228 |me| { 289 if let Err(abort_reason) = me.read_and_clear_abort_reason() {
229 if let Err(abort_reason) = me.read_and_clear_abort_reason() { 290 if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
230 if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason { 291 p.ic_clr_intr().read();
231 return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes))); 292 return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
232 } else { 293 } else {
233 return Poll::Ready(Err(abort_reason)); 294 return Poll::Ready(Err(abort_reason));
234 }
235 } 295 }
296 }
236 297
237 if let Some(chunk) = chunks.next() { 298 if let Some(chunk) = chunks.next() {
238 me.write_to_fifo(chunk); 299 me.write_to_fifo(chunk);
239 300
240 Poll::Pending 301 p.ic_clr_rd_req().read();
241 } else {
242 let stat = p.ic_raw_intr_stat().read();
243
244 if stat.rx_done() && stat.stop_det() {
245 Poll::Ready(Ok(ReadStatus::Done))
246 } else if stat.rd_req() {
247 Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
248 } else {
249 Poll::Pending
250 }
251 }
252 },
253 |_me| {
254 p.ic_intr_mask().modify(|w| {
255 w.set_m_stop_det(true);
256 w.set_m_rx_done(true);
257 w.set_m_tx_empty(true);
258 w.set_m_tx_abrt(true);
259 })
260 },
261 )
262 .await;
263 302
264 p.ic_clr_intr().read(); 303 Poll::Pending
304 } else {
305 let stat = p.ic_raw_intr_stat().read();
265 306
266 ret 307 if stat.rx_done() && stat.stop_det() {
308 p.ic_clr_rx_done().read();
309 p.ic_clr_stop_det().read();
310 Poll::Ready(Ok(ReadStatus::Done))
311 } else if stat.rd_req() && stat.tx_empty() {
312 Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
313 } else {
314 Poll::Pending
315 }
316 }
317 },
318 |_me| {
319 p.ic_intr_mask().modify(|w| {
320 w.set_m_stop_det(true);
321 w.set_m_rx_done(true);
322 w.set_m_tx_empty(true);
323 w.set_m_tx_abrt(true);
324 })
325 },
326 )
327 .await
267 } 328 }
268 329
269 /// Respond to reads with the fill byte until the controller stops asking 330 /// Respond to reads with the fill byte until the controller stops asking
@@ -294,10 +355,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
294 let p = T::regs(); 355 let p = T::regs();
295 let mut abort_reason = p.ic_tx_abrt_source().read(); 356 let mut abort_reason = p.ic_tx_abrt_source().read();
296 357
297 // Mask off fifo flush count
298 let tx_flush_cnt = abort_reason.tx_flush_cnt();
299 abort_reason.set_tx_flush_cnt(0);
300
301 // Mask off master_dis 358 // Mask off master_dis
302 abort_reason.set_abrt_master_dis(false); 359 abort_reason.set_abrt_master_dis(false);
303 360
@@ -314,8 +371,8 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
314 AbortReason::NoAcknowledge 371 AbortReason::NoAcknowledge
315 } else if abort_reason.arb_lost() { 372 } else if abort_reason.arb_lost() {
316 AbortReason::ArbitrationLoss 373 AbortReason::ArbitrationLoss
317 } else if abort_reason.abrt_slvflush_txfifo() { 374 } else if abort_reason.tx_flush_cnt() > 0 {
318 AbortReason::TxNotEmpty(tx_flush_cnt) 375 AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt())
319 } else { 376 } else {
320 AbortReason::Other(abort_reason.0) 377 AbortReason::Other(abort_reason.0)
321 }; 378 };
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 3f5f12f06..389ed0041 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -68,7 +68,7 @@ rand_core = "0.6.3"
68sdio-host = "0.5.0" 68sdio-host = "0.5.0"
69critical-section = "1.1" 69critical-section = "1.1"
70#stm32-metapac = { version = "15" } 70#stm32-metapac = { version = "15" }
71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76" } 71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f" }
72vcell = "0.1.3" 72vcell = "0.1.3"
73bxcan = "0.7.0" 73bxcan = "0.7.0"
74nb = "1.0.0" 74nb = "1.0.0"
@@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] }
89proc-macro2 = "1.0.36" 89proc-macro2 = "1.0.36"
90quote = "1.0.15" 90quote = "1.0.15"
91#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 91#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
92stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5bf4bec597bdf0d85402789b40c3a37b0f5a8e76", default-features = false, features = ["metadata"]} 92stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-6097928f720646c73d6483a3245f922bd5faee2f", default-features = false, features = ["metadata"]}
93 93
94 94
95[features] 95[features]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 3885c5d18..068a74733 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -5,8 +5,7 @@ use std::{env, fs};
5 5
6use proc_macro2::{Ident, TokenStream}; 6use proc_macro2::{Ident, TokenStream};
7use quote::{format_ident, quote}; 7use quote::{format_ident, quote};
8use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet}; 8use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccKernelClock, StopMode, METADATA};
9use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
10 9
11fn main() { 10fn main() {
12 let target = env::var("TARGET").unwrap(); 11 let target = env::var("TARGET").unwrap();
@@ -183,40 +182,33 @@ fn main() {
183 182
184 let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { 183 let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
185 None => "", 184 None => "",
185 Some("tim1") => "TIM1",
186 Some("tim2") => "TIM2", 186 Some("tim2") => "TIM2",
187 Some("tim3") => "TIM3", 187 Some("tim3") => "TIM3",
188 Some("tim4") => "TIM4", 188 Some("tim4") => "TIM4",
189 Some("tim5") => "TIM5", 189 Some("tim5") => "TIM5",
190 Some("tim8") => "TIM8",
190 Some("tim9") => "TIM9", 191 Some("tim9") => "TIM9",
191 Some("tim11") => "TIM11",
192 Some("tim12") => "TIM12", 192 Some("tim12") => "TIM12",
193 Some("tim15") => "TIM15", 193 Some("tim15") => "TIM15",
194 Some("tim20") => "TIM20",
194 Some("tim21") => "TIM21", 195 Some("tim21") => "TIM21",
195 Some("tim22") => "TIM22", 196 Some("tim22") => "TIM22",
197 Some("tim23") => "TIM23",
198 Some("tim24") => "TIM24",
196 Some("any") => { 199 Some("any") => {
197 if singletons.contains(&"TIM2".to_string()) { 200 // Order of TIM candidators:
198 "TIM2" 201 // 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
199 } else if singletons.contains(&"TIM3".to_string()) { 202 // 2. In same catagory: larger TIM number first
200 "TIM3" 203 [
201 } else if singletons.contains(&"TIM4".to_string()) { 204 "TIM22", "TIM21", "TIM12", "TIM9", // 2CH
202 "TIM4" 205 "TIM15", // 2CH_CMP
203 } else if singletons.contains(&"TIM5".to_string()) { 206 "TIM19", "TIM4", "TIM3", // GP16
204 "TIM5" 207 "TIM24", "TIM23", "TIM5", "TIM2", // GP32
205 } else if singletons.contains(&"TIM9".to_string()) { 208 "TIM20", "TIM8", "TIM1", //ADV
206 "TIM9" 209 ]
207 } else if singletons.contains(&"TIM11".to_string()) { 210 .iter()
208 "TIM11" 211 .find(|tim| singletons.contains(&tim.to_string())).expect("time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.")
209 } else if singletons.contains(&"TIM12".to_string()) {
210 "TIM12"
211 } else if singletons.contains(&"TIM15".to_string()) {
212 "TIM15"
213 } else if singletons.contains(&"TIM21".to_string()) {
214 "TIM21"
215 } else if singletons.contains(&"TIM22".to_string()) {
216 "TIM22"
217 } else {
218 panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM9, TIM11, TIM12 or TIM15.")
219 }
220 } 212 }
221 _ => panic!("unknown time_driver {:?}", time_driver), 213 _ => panic!("unknown time_driver {:?}", time_driver),
222 }; 214 };
@@ -415,38 +407,6 @@ fn main() {
415 .unwrap(); 407 .unwrap();
416 408
417 // ======== 409 // ========
418 // Generate rcc fieldset and enum maps
419 let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
420 let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
421 let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
422 let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
423
424 rcc_blocks
425 .iter()
426 .filter_map(|b| match &b.inner {
427 BlockItemInner::Register(register) => register.fieldset.map(|f| (b.name, f)),
428 _ => None,
429 })
430 .filter_map(|(b, f)| {
431 rcc_fieldsets.get(f).map(|f| {
432 (
433 b,
434 f.fields
435 .iter()
436 .filter_map(|f| {
437 let enumm = f.enumm?;
438 let enumm = rcc_enums.get(enumm)?;
439
440 Some((f.name, *enumm))
441 })
442 .collect(),
443 )
444 })
445 })
446 .collect()
447 };
448
449 // ========
450 // Generate RccPeripheral impls 410 // Generate RccPeripheral impls
451 411
452 // count how many times each xxENR field is used, to enable refcounting if used more than once. 412 // count how many times each xxENR field is used, to enable refcounting if used more than once.
@@ -494,8 +454,8 @@ fn main() {
494 454
495 let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" }; 455 let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
496 let pname = format_ident!("{}", p.name); 456 let pname = format_ident!("{}", p.name);
497 let en_reg = format_ident!("{}", en.register); 457 let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
498 let set_en_field = format_ident!("set_{}", en.field); 458 let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
499 459
500 let refcount = 460 let refcount =
501 force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1; 461 force_refcount.contains(ptype) || *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1;
@@ -523,21 +483,25 @@ fn main() {
523 (TokenStream::new(), TokenStream::new()) 483 (TokenStream::new(), TokenStream::new())
524 }; 484 };
525 485
526 let mux_for = |mux: Option<&'static PeripheralRccRegister>| { 486 let clock_frequency = match &rcc.kernel_clock {
527 let mux = mux?; 487 PeripheralRccKernelClock::Mux(mux) => {
528 let fieldset = rcc_enum_map.get(mux.register)?; 488 let ir = &rcc_registers.ir;
529 let enumm = fieldset.get(mux.field)?; 489 let fieldset_name = mux.register.to_ascii_lowercase();
530 490 let fieldset = ir
531 Some((mux, *enumm)) 491 .fieldsets
532 }; 492 .iter()
533 493 .find(|i| i.name.eq_ignore_ascii_case(&fieldset_name))
534 let clock_frequency = match mux_for(rcc.mux.as_ref()) { 494 .unwrap();
535 Some((mux, rcc_enumm)) => { 495 let field_name = mux.field.to_ascii_lowercase();
536 let fieldset_name = format_ident!("{}", mux.register); 496 let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap();
537 let field_name = format_ident!("{}", mux.field); 497 let enum_name = field.enumm.unwrap();
538 let enum_name = format_ident!("{}", rcc_enumm.name); 498 let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap();
539 499
540 let match_arms: TokenStream = rcc_enumm 500 let fieldset_name = format_ident!("{}", fieldset_name);
501 let field_name = format_ident!("{}", field_name);
502 let enum_name = format_ident!("{}", enum_name);
503
504 let match_arms: TokenStream = enumm
541 .variants 505 .variants
542 .iter() 506 .iter()
543 .filter(|v| v.name != "DISABLE") 507 .filter(|v| v.name != "DISABLE")
@@ -561,9 +525,10 @@ fn main() {
561 } 525 }
562 } 526 }
563 } 527 }
564 None => { 528 PeripheralRccKernelClock::Clock(clock) => {
565 let clock_name = format_ident!("{}", rcc.clock); 529 let clock = clock.to_ascii_lowercase();
566 clock_names.insert(rcc.clock.to_string()); 530 let clock_name = format_ident!("{}", clock);
531 clock_names.insert(clock.to_string());
567 quote! { 532 quote! {
568 unsafe { crate::rcc::get_freqs().#clock_name.unwrap() } 533 unsafe { crate::rcc::get_freqs().#clock_name.unwrap() }
569 } 534 }
@@ -1056,6 +1021,7 @@ fn main() {
1056 (("dac", "CH1"), quote!(crate::dac::DacDma1)), 1021 (("dac", "CH1"), quote!(crate::dac::DacDma1)),
1057 (("dac", "CH2"), quote!(crate::dac::DacDma2)), 1022 (("dac", "CH2"), quote!(crate::dac::DacDma2)),
1058 (("timer", "UP"), quote!(crate::timer::UpDma)), 1023 (("timer", "UP"), quote!(crate::timer::UpDma)),
1024 (("hash", "IN"), quote!(crate::hash::Dma)),
1059 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)), 1025 (("timer", "CH1"), quote!(crate::timer::Ch1Dma)),
1060 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), 1026 (("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
1061 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), 1027 (("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index 1051a13c8..00e3e1727 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -249,7 +249,7 @@ impl From<Pull> for vals::Pupdr {
249 249
250/// Speed settings 250/// Speed settings
251/// 251///
252/// These vary dpeending on the chip, ceck the reference manual or datasheet for details. 252/// These vary depending on the chip, check the reference manual or datasheet for details.
253#[allow(missing_docs)] 253#[allow(missing_docs)]
254#[derive(Debug, Copy, Clone)] 254#[derive(Debug, Copy, Clone)]
255#[cfg_attr(feature = "defmt", derive(defmt::Format))] 255#[cfg_attr(feature = "defmt", derive(defmt::Format))]
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs
new file mode 100644
index 000000000..b47814f8b
--- /dev/null
+++ b/embassy-stm32/src/hash/mod.rs
@@ -0,0 +1,592 @@
1//! Hash generator (HASH)
2use core::cmp::min;
3#[cfg(hash_v2)]
4use core::future::poll_fn;
5use core::marker::PhantomData;
6#[cfg(hash_v2)]
7use core::ptr;
8#[cfg(hash_v2)]
9use core::task::Poll;
10
11use embassy_hal_internal::{into_ref, PeripheralRef};
12use embassy_sync::waitqueue::AtomicWaker;
13use stm32_metapac::hash::regs::*;
14
15use crate::dma::NoDma;
16#[cfg(hash_v2)]
17use crate::dma::Transfer;
18use crate::interrupt::typelevel::Interrupt;
19use crate::peripherals::HASH;
20use crate::rcc::sealed::RccPeripheral;
21use crate::{interrupt, pac, peripherals, Peripheral};
22
23#[cfg(hash_v1)]
24const NUM_CONTEXT_REGS: usize = 51;
25#[cfg(hash_v3)]
26const NUM_CONTEXT_REGS: usize = 103;
27#[cfg(any(hash_v2, hash_v4))]
28const NUM_CONTEXT_REGS: usize = 54;
29
30const HASH_BUFFER_LEN: usize = 132;
31const DIGEST_BLOCK_SIZE: usize = 128;
32
33static HASH_WAKER: AtomicWaker = AtomicWaker::new();
34
35/// HASH interrupt handler.
36pub struct InterruptHandler<T: Instance> {
37 _phantom: PhantomData<T>,
38}
39
40impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
41 unsafe fn on_interrupt() {
42 let bits = T::regs().sr().read();
43 if bits.dinis() {
44 T::regs().imr().modify(|reg| reg.set_dinie(false));
45 HASH_WAKER.wake();
46 }
47 if bits.dcis() {
48 T::regs().imr().modify(|reg| reg.set_dcie(false));
49 HASH_WAKER.wake();
50 }
51 }
52}
53
54///Hash algorithm selection
55#[derive(Clone, Copy, PartialEq)]
56pub enum Algorithm {
57 /// SHA-1 Algorithm
58 SHA1 = 0,
59
60 #[cfg(any(hash_v1, hash_v2, hash_v4))]
61 /// MD5 Algorithm
62 MD5 = 1,
63
64 /// SHA-224 Algorithm
65 SHA224 = 2,
66
67 /// SHA-256 Algorithm
68 SHA256 = 3,
69
70 #[cfg(hash_v3)]
71 /// SHA-384 Algorithm
72 SHA384 = 12,
73
74 #[cfg(hash_v3)]
75 /// SHA-512/224 Algorithm
76 SHA512_224 = 13,
77
78 #[cfg(hash_v3)]
79 /// SHA-512/256 Algorithm
80 SHA512_256 = 14,
81
82 #[cfg(hash_v3)]
83 /// SHA-256 Algorithm
84 SHA512 = 15,
85}
86
87/// Input data width selection
88#[repr(u8)]
89#[derive(Clone, Copy)]
90pub enum DataType {
91 ///32-bit data, no data is swapped.
92 Width32 = 0,
93 ///16-bit data, each half-word is swapped.
94 Width16 = 1,
95 ///8-bit data, all bytes are swapped.
96 Width8 = 2,
97 ///1-bit data, all bits are swapped.
98 Width1 = 3,
99}
100
101/// Stores the state of the HASH peripheral for suspending/resuming
102/// digest calculation.
103pub struct Context<'c> {
104 first_word_sent: bool,
105 key_sent: bool,
106 buffer: [u8; HASH_BUFFER_LEN],
107 buflen: usize,
108 algo: Algorithm,
109 format: DataType,
110 imr: u32,
111 str: u32,
112 cr: u32,
113 csr: [u32; NUM_CONTEXT_REGS],
114 key: HmacKey<'c>,
115}
116
117type HmacKey<'k> = Option<&'k [u8]>;
118
119/// HASH driver.
120pub struct Hash<'d, T: Instance, D = NoDma> {
121 _peripheral: PeripheralRef<'d, T>,
122 #[allow(dead_code)]
123 dma: PeripheralRef<'d, D>,
124}
125
126impl<'d, T: Instance, D> Hash<'d, T, D> {
127 /// Instantiates, resets, and enables the HASH peripheral.
128 pub fn new(
129 peripheral: impl Peripheral<P = T> + 'd,
130 dma: impl Peripheral<P = D> + 'd,
131 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
132 ) -> Self {
133 HASH::enable_and_reset();
134 into_ref!(peripheral, dma);
135 let instance = Self {
136 _peripheral: peripheral,
137 dma: dma,
138 };
139
140 T::Interrupt::unpend();
141 unsafe { T::Interrupt::enable() };
142
143 instance
144 }
145
146 /// Starts computation of a new hash and returns the saved peripheral state.
147 pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> {
148 // Define a context for this new computation.
149 let mut ctx = Context {
150 first_word_sent: false,
151 key_sent: false,
152 buffer: [0; HASH_BUFFER_LEN],
153 buflen: 0,
154 algo: algorithm,
155 format: format,
156 imr: 0,
157 str: 0,
158 cr: 0,
159 csr: [0; NUM_CONTEXT_REGS],
160 key,
161 };
162
163 // Set the data type in the peripheral.
164 T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
165
166 // Select the algorithm.
167 #[cfg(hash_v1)]
168 if ctx.algo == Algorithm::MD5 {
169 T::regs().cr().modify(|w| w.set_algo(true));
170 }
171
172 #[cfg(hash_v2)]
173 {
174 // Select the algorithm.
175 let mut algo0 = false;
176 let mut algo1 = false;
177 if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
178 algo0 = true;
179 }
180 if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
181 algo1 = true;
182 }
183 T::regs().cr().modify(|w| w.set_algo0(algo0));
184 T::regs().cr().modify(|w| w.set_algo1(algo1));
185 }
186
187 #[cfg(any(hash_v3, hash_v4))]
188 T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));
189
190 // Configure HMAC mode if a key is provided.
191 if let Some(key) = ctx.key {
192 T::regs().cr().modify(|w| w.set_mode(true));
193 if key.len() > 64 {
194 T::regs().cr().modify(|w| w.set_lkey(true));
195 }
196 }
197
198 T::regs().cr().modify(|w| w.set_init(true));
199
200 // Store and return the state of the peripheral.
201 self.store_context(&mut ctx);
202 ctx
203 }
204
205 /// Restores the peripheral state using the given context,
206 /// then updates the state with the provided data.
207 /// Peripheral state is saved upon return.
208 pub fn update_blocking<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) {
209 // Restore the peripheral state.
210 self.load_context(&ctx);
211
212 // Load the HMAC key if provided.
213 if !ctx.key_sent {
214 if let Some(key) = ctx.key {
215 self.accumulate_blocking(key);
216 T::regs().str().write(|w| w.set_dcal(true));
217 // Block waiting for digest.
218 while !T::regs().sr().read().dinis() {}
219 }
220 ctx.key_sent = true;
221 }
222
223 let mut data_waiting = input.len() + ctx.buflen;
224 if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
225 // There isn't enough data to digest a block, so append it to the buffer.
226 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
227 ctx.buflen += input.len();
228 self.store_context(ctx);
229 return;
230 }
231
232 let mut ilen_remaining = input.len();
233 let mut input_start = 0;
234
235 // Handle first block.
236 if !ctx.first_word_sent {
237 let empty_len = ctx.buffer.len() - ctx.buflen;
238 let copy_len = min(empty_len, ilen_remaining);
239 // Fill the buffer.
240 if copy_len > 0 {
241 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);
242 ctx.buflen += copy_len;
243 ilen_remaining -= copy_len;
244 input_start += copy_len;
245 }
246 self.accumulate_blocking(ctx.buffer.as_slice());
247 data_waiting -= ctx.buflen;
248 ctx.buflen = 0;
249 ctx.first_word_sent = true;
250 }
251
252 if data_waiting < DIGEST_BLOCK_SIZE {
253 // There isn't enough data remaining to process another block, so store it.
254 ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
255 ctx.buflen += ilen_remaining;
256 } else {
257 // First ingest the data in the buffer.
258 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
259 if empty_len > 0 {
260 let copy_len = min(empty_len, ilen_remaining);
261 ctx.buffer[ctx.buflen..ctx.buflen + copy_len]
262 .copy_from_slice(&input[input_start..input_start + copy_len]);
263 ctx.buflen += copy_len;
264 ilen_remaining -= copy_len;
265 input_start += copy_len;
266 }
267 self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]);
268 ctx.buflen = 0;
269
270 // Move any extra data to the now-empty buffer.
271 let leftovers = ilen_remaining % 64;
272 if leftovers > 0 {
273 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
274 ctx.buflen += leftovers;
275 ilen_remaining -= leftovers;
276 }
277
278 // Hash the remaining data.
279 self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]);
280 }
281
282 // Save the peripheral context.
283 self.store_context(ctx);
284 }
285
286 /// Restores the peripheral state using the given context,
287 /// then updates the state with the provided data.
288 /// Peripheral state is saved upon return.
289 #[cfg(hash_v2)]
290 pub async fn update<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8])
291 where
292 D: crate::hash::Dma<T>,
293 {
294 // Restore the peripheral state.
295 self.load_context(&ctx);
296
297 // Load the HMAC key if provided.
298 if !ctx.key_sent {
299 if let Some(key) = ctx.key {
300 self.accumulate(key).await;
301 }
302 ctx.key_sent = true;
303 }
304
305 let data_waiting = input.len() + ctx.buflen;
306 if data_waiting < DIGEST_BLOCK_SIZE {
307 // There isn't enough data to digest a block, so append it to the buffer.
308 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
309 ctx.buflen += input.len();
310 self.store_context(ctx);
311 return;
312 }
313
314 // Enable multiple DMA transfers.
315 T::regs().cr().modify(|w| w.set_mdmat(true));
316
317 let mut ilen_remaining = input.len();
318 let mut input_start = 0;
319
320 // First ingest the data in the buffer.
321 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
322 if empty_len > 0 {
323 let copy_len = min(empty_len, ilen_remaining);
324 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
325 ctx.buflen += copy_len;
326 ilen_remaining -= copy_len;
327 input_start += copy_len;
328 }
329 self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await;
330 ctx.buflen = 0;
331
332 // Move any extra data to the now-empty buffer.
333 let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
334 if leftovers > 0 {
335 assert!(ilen_remaining >= leftovers);
336 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
337 ctx.buflen += leftovers;
338 ilen_remaining -= leftovers;
339 } else {
340 ctx.buffer
341 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
342 ctx.buflen += DIGEST_BLOCK_SIZE;
343 ilen_remaining -= DIGEST_BLOCK_SIZE;
344 }
345
346 // Hash the remaining data.
347 self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
348
349 // Save the peripheral context.
350 self.store_context(ctx);
351 }
352
353 /// Computes a digest for the given context.
354 /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
355 /// The largest returned digest size is 128 bytes for SHA-512.
356 /// Panics if the supplied digest buffer is too short.
357 pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize {
358 // Restore the peripheral state.
359 self.load_context(&ctx);
360
361 // Hash the leftover bytes, if any.
362 self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]);
363 ctx.buflen = 0;
364
365 //Start the digest calculation.
366 T::regs().str().write(|w| w.set_dcal(true));
367
368 // Load the HMAC key if provided.
369 if let Some(key) = ctx.key {
370 while !T::regs().sr().read().dinis() {}
371 self.accumulate_blocking(key);
372 T::regs().str().write(|w| w.set_dcal(true));
373 }
374
375 // Block until digest computation is complete.
376 while !T::regs().sr().read().dcis() {}
377
378 // Return the digest.
379 let digest_words = match ctx.algo {
380 Algorithm::SHA1 => 5,
381 #[cfg(any(hash_v1, hash_v2, hash_v4))]
382 Algorithm::MD5 => 4,
383 Algorithm::SHA224 => 7,
384 Algorithm::SHA256 => 8,
385 #[cfg(hash_v3)]
386 Algorithm::SHA384 => 12,
387 #[cfg(hash_v3)]
388 Algorithm::SHA512_224 => 7,
389 #[cfg(hash_v3)]
390 Algorithm::SHA512_256 => 8,
391 #[cfg(hash_v3)]
392 Algorithm::SHA512 => 16,
393 };
394
395 let digest_len_bytes = digest_words * 4;
396 // Panics if the supplied digest buffer is too short.
397 if digest.len() < digest_len_bytes {
398 panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
399 }
400
401 let mut i = 0;
402 while i < digest_words {
403 let word = T::regs().hr(i).read();
404 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
405 i += 1;
406 }
407 digest_len_bytes
408 }
409
410 /// Computes a digest for the given context.
411 /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.
412 /// The largest returned digest size is 128 bytes for SHA-512.
413 /// Panics if the supplied digest buffer is too short.
414 #[cfg(hash_v2)]
415 pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize
416 where
417 D: crate::hash::Dma<T>,
418 {
419 // Restore the peripheral state.
420 self.load_context(&ctx);
421
422 // Must be cleared prior to the last DMA transfer.
423 T::regs().cr().modify(|w| w.set_mdmat(false));
424
425 // Hash the leftover bytes, if any.
426 self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
427 ctx.buflen = 0;
428
429 // Load the HMAC key if provided.
430 if let Some(key) = ctx.key {
431 self.accumulate(key).await;
432 }
433
434 // Wait for completion.
435 poll_fn(|cx| {
436 // Check if already done.
437 let bits = T::regs().sr().read();
438 if bits.dcis() {
439 return Poll::Ready(());
440 }
441 // Register waker, then enable interrupts.
442 HASH_WAKER.register(cx.waker());
443 T::regs().imr().modify(|reg| reg.set_dcie(true));
444 // Check for completion.
445 let bits = T::regs().sr().read();
446 if bits.dcis() {
447 Poll::Ready(())
448 } else {
449 Poll::Pending
450 }
451 })
452 .await;
453
454 // Return the digest.
455 let digest_words = match ctx.algo {
456 Algorithm::SHA1 => 5,
457 #[cfg(any(hash_v1, hash_v2, hash_v4))]
458 Algorithm::MD5 => 4,
459 Algorithm::SHA224 => 7,
460 Algorithm::SHA256 => 8,
461 #[cfg(hash_v3)]
462 Algorithm::SHA384 => 12,
463 #[cfg(hash_v3)]
464 Algorithm::SHA512_224 => 7,
465 #[cfg(hash_v3)]
466 Algorithm::SHA512_256 => 8,
467 #[cfg(hash_v3)]
468 Algorithm::SHA512 => 16,
469 };
470
471 let digest_len_bytes = digest_words * 4;
472 // Panics if the supplied digest buffer is too short.
473 if digest.len() < digest_len_bytes {
474 panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
475 }
476
477 let mut i = 0;
478 while i < digest_words {
479 let word = T::regs().hr(i).read();
480 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
481 i += 1;
482 }
483 digest_len_bytes
484 }
485
486 /// Push data into the hash core.
487 fn accumulate_blocking(&mut self, input: &[u8]) {
488 // Set the number of valid bits.
489 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
490 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
491
492 let mut i = 0;
493 while i < input.len() {
494 let mut word: [u8; 4] = [0; 4];
495 let copy_idx = min(i + 4, input.len());
496 word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]);
497 T::regs().din().write_value(u32::from_ne_bytes(word));
498 i += 4;
499 }
500 }
501
502 /// Push data into the hash core.
503 #[cfg(hash_v2)]
504 async fn accumulate(&mut self, input: &[u8])
505 where
506 D: crate::hash::Dma<T>,
507 {
508 // Ignore an input length of 0.
509 if input.len() == 0 {
510 return;
511 }
512
513 // Set the number of valid bits.
514 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
515 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
516
517 // Configure DMA to transfer input to hash core.
518 let dma_request = self.dma.request();
519 let dst_ptr = T::regs().din().as_ptr();
520 let mut num_words = input.len() / 4;
521 if input.len() % 4 > 0 {
522 num_words += 1;
523 }
524 let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
525 let dma_transfer =
526 unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
527 T::regs().cr().modify(|w| w.set_dmae(true));
528
529 // Wait for the transfer to complete.
530 dma_transfer.await;
531 }
532
533 /// Save the peripheral state to a context.
534 fn store_context<'c>(&mut self, ctx: &mut Context<'c>) {
535 // Block waiting for data in ready.
536 while !T::regs().sr().read().dinis() {}
537
538 // Store peripheral context.
539 ctx.imr = T::regs().imr().read().0;
540 ctx.str = T::regs().str().read().0;
541 ctx.cr = T::regs().cr().read().0;
542 let mut i = 0;
543 while i < NUM_CONTEXT_REGS {
544 ctx.csr[i] = T::regs().csr(i).read();
545 i += 1;
546 }
547 }
548
549 /// Restore the peripheral state from a context.
550 fn load_context(&mut self, ctx: &Context) {
551 // Restore the peripheral state from the context.
552 T::regs().imr().write_value(Imr { 0: ctx.imr });
553 T::regs().str().write_value(Str { 0: ctx.str });
554 T::regs().cr().write_value(Cr { 0: ctx.cr });
555 T::regs().cr().modify(|w| w.set_init(true));
556 let mut i = 0;
557 while i < NUM_CONTEXT_REGS {
558 T::regs().csr(i).write_value(ctx.csr[i]);
559 i += 1;
560 }
561 }
562}
563
564pub(crate) mod sealed {
565 use super::*;
566
567 pub trait Instance {
568 fn regs() -> pac::hash::Hash;
569 }
570}
571
572/// HASH instance trait.
573pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
574 /// Interrupt for this HASH instance.
575 type Interrupt: interrupt::typelevel::Interrupt;
576}
577
578foreach_interrupt!(
579 ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
580 impl Instance for peripherals::$inst {
581 type Interrupt = crate::interrupt::typelevel::$irq;
582 }
583
584 impl sealed::Instance for peripherals::$inst {
585 fn regs() -> crate::pac::hash::Hash {
586 crate::pac::$inst
587 }
588 }
589 };
590);
591
592dma_trait!(Dma, Instance);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index a465fccd8..cd1ede0fa 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -45,6 +45,8 @@ pub mod exti;
45pub mod flash; 45pub mod flash;
46#[cfg(fmc)] 46#[cfg(fmc)]
47pub mod fmc; 47pub mod fmc;
48#[cfg(hash)]
49pub mod hash;
48#[cfg(hrtim)] 50#[cfg(hrtim)]
49pub mod hrtim; 51pub mod hrtim;
50#[cfg(i2c)] 52#[cfg(i2c)]
diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs
deleted file mode 100644
index 1042a1cb2..000000000
--- a/embassy-stm32/src/rcc/f0.rs
+++ /dev/null
@@ -1,196 +0,0 @@
1use stm32_metapac::flash::vals::Latency;
2
3use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
4use crate::pac::{FLASH, RCC};
5use crate::time::Hertz;
6
7/// HSI speed
8pub const HSI_FREQ: Hertz = Hertz(8_000_000);
9
10/// Configuration of the clocks
11///
12/// hse takes precedence over hsi48 if both are enabled
13#[non_exhaustive]
14pub struct Config {
15 pub hse: Option<Hertz>,
16 pub bypass_hse: bool,
17 pub usb_pll: bool,
18
19 #[cfg(crs)]
20 pub hsi48: Option<super::Hsi48Config>,
21
22 pub sys_ck: Option<Hertz>,
23 pub hclk: Option<Hertz>,
24 pub pclk: Option<Hertz>,
25
26 pub ls: super::LsConfig,
27}
28
29impl Default for Config {
30 fn default() -> Self {
31 Self {
32 hse: Default::default(),
33 bypass_hse: Default::default(),
34 usb_pll: Default::default(),
35 #[cfg(crs)]
36 hsi48: Some(Default::default()),
37 sys_ck: Default::default(),
38 hclk: Default::default(),
39 pclk: Default::default(),
40 ls: Default::default(),
41 }
42 }
43}
44
45pub(crate) unsafe fn init(config: Config) {
46 let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI_FREQ.0);
47
48 #[cfg(crs)]
49 let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));
50 #[cfg(not(crs))]
51 let hsi48: Option<Hertz> = None;
52
53 let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| {
54 if hsi48.is_some() {
55 return (48_000_000, true);
56 }
57 (HSI_FREQ.0, false)
58 });
59
60 let (pllmul_bits, real_sysclk) = if sysclk == src_clk {
61 (None, sysclk)
62 } else {
63 let prediv = if config.hse.is_some() { 1 } else { 2 };
64 let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2;
65 let pllmul = pllmul.max(2).min(16);
66
67 let pllmul_bits = pllmul as u8 - 2;
68 let real_sysclk = pllmul * src_clk / prediv;
69 (Some(pllmul_bits), real_sysclk)
70 };
71
72 let hpre_bits = config
73 .hclk
74 .map(|hclk| match real_sysclk / hclk.0 {
75 0 => unreachable!(),
76 1 => 0b0111,
77 2 => 0b1000,
78 3..=5 => 0b1001,
79 6..=11 => 0b1010,
80 12..=39 => 0b1011,
81 40..=95 => 0b1100,
82 96..=191 => 0b1101,
83 192..=383 => 0b1110,
84 _ => 0b1111,
85 })
86 .unwrap_or(0b0111);
87 let hclk = real_sysclk / (1 << (hpre_bits - 0b0111));
88
89 let ppre_bits = config
90 .pclk
91 .map(|pclk| match hclk / pclk.0 {
92 0 => unreachable!(),
93 1 => 0b011,
94 2 => 0b100,
95 3..=5 => 0b101,
96 6..=11 => 0b110,
97 _ => 0b111,
98 })
99 .unwrap_or(0b011);
100
101 let ppre: u8 = 1 << (ppre_bits - 0b011);
102 let pclk = hclk / u32::from(ppre);
103
104 let timer_mul = if ppre == 1 { 1 } else { 2 };
105
106 FLASH.acr().write(|w| {
107 w.set_latency(if real_sysclk <= 24_000_000 {
108 Latency::WS0
109 } else {
110 Latency::WS1
111 });
112 });
113
114 match (config.hse.is_some(), use_hsi48) {
115 (true, _) => {
116 RCC.cr().modify(|w| {
117 w.set_csson(true);
118 w.set_hseon(true);
119 w.set_hsebyp(config.bypass_hse);
120 });
121 while !RCC.cr().read().hserdy() {}
122
123 if pllmul_bits.is_some() {
124 RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV))
125 }
126 }
127 // use_hsi48 will always be false for stm32f0x0
128 #[cfg(not(stm32f0x0))]
129 (false, true) => {
130 RCC.cr2().modify(|w| w.set_hsi48on(true));
131 while !RCC.cr2().read().hsi48rdy() {}
132
133 if pllmul_bits.is_some() {
134 RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV))
135 }
136 }
137 _ => {
138 RCC.cr().modify(|w| w.set_hsion(true));
139 while !RCC.cr().read().hsirdy() {}
140
141 if pllmul_bits.is_some() {
142 RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2))
143 }
144 }
145 }
146
147 if config.usb_pll {
148 RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P));
149 }
150 // TODO: Option to use CRS (Clock Recovery)
151
152 if let Some(pllmul_bits) = pllmul_bits {
153 RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits)));
154
155 RCC.cr().modify(|w| w.set_pllon(true));
156 while !RCC.cr().read().pllrdy() {}
157
158 RCC.cfgr().modify(|w| {
159 w.set_ppre(Ppre::from_bits(ppre_bits));
160 w.set_hpre(Hpre::from_bits(hpre_bits));
161 w.set_sw(Sw::PLL1_P)
162 });
163 } else {
164 RCC.cfgr().modify(|w| {
165 w.set_ppre(Ppre::from_bits(ppre_bits));
166 w.set_hpre(Hpre::from_bits(hpre_bits));
167
168 if config.hse.is_some() {
169 w.set_sw(Sw::HSE);
170 } else if use_hsi48 {
171 #[cfg(not(stm32f0x0))]
172 w.set_sw(Sw::HSI48);
173 } else {
174 w.set_sw(Sw::HSI)
175 }
176 })
177 }
178
179 let rtc = config.ls.init();
180
181 set_clocks!(
182 hsi: None,
183 lse: None,
184 sys: Some(Hertz(real_sysclk)),
185 pclk1: Some(Hertz(pclk)),
186 pclk2: Some(Hertz(pclk)),
187 pclk1_tim: Some(Hertz(pclk * timer_mul)),
188 pclk2_tim: Some(Hertz(pclk * timer_mul)),
189 hclk1: Some(Hertz(hclk)),
190 rtc: rtc,
191 hsi48: hsi48,
192
193 // TODO:
194 pll1_p: None,
195 );
196}
diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs
new file mode 100644
index 000000000..c2933186c
--- /dev/null
+++ b/embassy-stm32/src/rcc/f013.rs
@@ -0,0 +1,448 @@
1use crate::pac::flash::vals::Latency;
2#[cfg(stm32f1)]
3pub use crate::pac::rcc::vals::Adcpre as ADCPrescaler;
4#[cfg(stm32f3)]
5pub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler;
6use crate::pac::rcc::vals::Pllsrc;
7#[cfg(stm32f1)]
8pub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv;
9#[cfg(any(stm32f0, stm32f3))]
10pub use crate::pac::rcc::vals::Prediv as PllPreDiv;
11pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk};
12use crate::pac::{FLASH, RCC};
13use crate::time::Hertz;
14
15/// HSI speed
16pub const HSI_FREQ: Hertz = Hertz(8_000_000);
17
18#[derive(Clone, Copy, Eq, PartialEq)]
19pub enum HseMode {
20 /// crystal/ceramic oscillator (HSEBYP=0)
21 Oscillator,
22 /// external analog clock (low swing) (HSEBYP=1)
23 Bypass,
24}
25
26#[derive(Clone, Copy, Eq, PartialEq)]
27pub struct Hse {
28 /// HSE frequency.
29 pub freq: Hertz,
30 /// HSE mode.
31 pub mode: HseMode,
32}
33
34#[derive(Clone, Copy, Eq, PartialEq)]
35pub enum PllSource {
36 HSE,
37 HSI,
38 #[cfg(rcc_f0v4)]
39 HSI48,
40}
41
42#[derive(Clone, Copy)]
43pub struct Pll {
44 pub src: PllSource,
45
46 /// PLL pre-divider.
47 ///
48 /// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case.
49 pub prediv: PllPreDiv,
50
51 /// PLL multiplication factor.
52 pub mul: PllMul,
53}
54
55#[cfg(all(stm32f3, not(rcc_f37)))]
56#[derive(Clone, Copy)]
57pub enum AdcClockSource {
58 Pll(AdcPllPrescaler),
59 Hclk(AdcHclkPrescaler),
60}
61
62#[cfg(all(stm32f3, not(rcc_f37)))]
63#[derive(Clone, Copy, PartialEq, Eq)]
64pub enum AdcHclkPrescaler {
65 Div1,
66 Div2,
67 Div4,
68}
69
70#[cfg(stm32f334)]
71#[derive(Clone, Copy, PartialEq, Eq)]
72pub enum HrtimClockSource {
73 BusClk,
74 PllClk,
75}
76
77/// Clocks configutation
78#[non_exhaustive]
79pub struct Config {
80 pub hsi: bool,
81 pub hse: Option<Hse>,
82 #[cfg(crs)]
83 pub hsi48: Option<super::Hsi48Config>,
84 pub sys: Sysclk,
85
86 pub pll: Option<Pll>,
87
88 pub ahb_pre: AHBPrescaler,
89 pub apb1_pre: APBPrescaler,
90 #[cfg(not(stm32f0))]
91 pub apb2_pre: APBPrescaler,
92
93 #[cfg(stm32f1)]
94 pub adc_pre: ADCPrescaler,
95
96 #[cfg(all(stm32f3, not(rcc_f37)))]
97 pub adc: AdcClockSource,
98 #[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
99 pub adc34: AdcClockSource,
100 #[cfg(stm32f334)]
101 pub hrtim: HrtimClockSource,
102
103 pub ls: super::LsConfig,
104}
105
106impl Default for Config {
107 fn default() -> Self {
108 Self {
109 hsi: true,
110 hse: None,
111 #[cfg(crs)]
112 hsi48: Some(Default::default()),
113 sys: Sysclk::HSI,
114 pll: None,
115 ahb_pre: AHBPrescaler::DIV1,
116 apb1_pre: APBPrescaler::DIV1,
117 #[cfg(not(stm32f0))]
118 apb2_pre: APBPrescaler::DIV1,
119 ls: Default::default(),
120
121 #[cfg(stm32f1)]
122 // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz)
123 adc_pre: ADCPrescaler::DIV6,
124
125
126 #[cfg(all(stm32f3, not(rcc_f37)))]
127 adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),
128 #[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
129 adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),
130 #[cfg(stm32f334)]
131 hrtim: HrtimClockSource::BusClk,
132 }
133 }
134}
135
136/// Initialize and Set the clock frequencies
137pub(crate) unsafe fn init(config: Config) {
138 // Configure HSI
139 let hsi = match config.hsi {
140 false => {
141 RCC.cr().modify(|w| w.set_hsion(false));
142 None
143 }
144 true => {
145 RCC.cr().modify(|w| w.set_hsion(true));
146 while !RCC.cr().read().hsirdy() {}
147 Some(HSI_FREQ)
148 }
149 };
150
151 // Configure HSE
152 let hse = match config.hse {
153 None => {
154 RCC.cr().modify(|w| w.set_hseon(false));
155 None
156 }
157 Some(hse) => {
158 match hse.mode {
159 HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
160 HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
161 }
162
163 RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
164 RCC.cr().modify(|w| w.set_hseon(true));
165 while !RCC.cr().read().hserdy() {}
166 Some(hse.freq)
167 }
168 };
169
170 // configure HSI48
171 #[cfg(crs)]
172 let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));
173 #[cfg(not(crs))]
174 let hsi48: Option<Hertz> = None;
175
176 // Enable PLL
177 let pll = config.pll.map(|pll| {
178 let (src_val, src_freq) = match pll.src {
179 #[cfg(any(rcc_f0v3, rcc_f0v4, rcc_f3v3))]
180 PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)),
181 #[cfg(not(any(rcc_f0v3, rcc_f0v4, rcc_f3v3)))]
182 PllSource::HSI => {
183 if pll.prediv != PllPreDiv::DIV2 {
184 panic!("if PLL source is HSI, PLL prediv must be 2.");
185 }
186 (Pllsrc::HSI_DIV2, unwrap!(hsi))
187 }
188 PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)),
189 #[cfg(rcc_f0v4)]
190 PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)),
191 };
192 let in_freq = src_freq / pll.prediv;
193 assert!(max::PLL_IN.contains(&in_freq));
194 let out_freq = in_freq * pll.mul;
195 assert!(max::PLL_OUT.contains(&out_freq));
196
197 #[cfg(not(stm32f1))]
198 RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv));
199 RCC.cfgr().modify(|w| {
200 w.set_pllmul(pll.mul);
201 w.set_pllsrc(src_val);
202 #[cfg(stm32f1)]
203 w.set_pllxtpre(pll.prediv);
204 });
205 RCC.cr().modify(|w| w.set_pllon(true));
206 while !RCC.cr().read().pllrdy() {}
207
208 out_freq
209 });
210
211 #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))]
212 let usb = match pll {
213 Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5),
214 Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1),
215 _ => None,
216 }
217 .map(|usbpre| {
218 RCC.cfgr().modify(|w| w.set_usbpre(usbpre));
219 Hertz(48_000_000)
220 });
221
222 // Configure sysclk
223 let sys = match config.sys {
224 Sysclk::HSI => unwrap!(hsi),
225 Sysclk::HSE => unwrap!(hse),
226 Sysclk::PLL1_P => unwrap!(pll),
227 _ => unreachable!(),
228 };
229
230 let hclk = sys / config.ahb_pre;
231 let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
232 #[cfg(not(stm32f0))]
233 let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
234 #[cfg(stm32f0)]
235 let (pclk2, pclk2_tim) = (pclk1, pclk1_tim);
236
237 assert!(max::HCLK.contains(&hclk));
238 assert!(max::PCLK1.contains(&pclk1));
239 #[cfg(not(stm32f0))]
240 assert!(max::PCLK2.contains(&pclk2));
241
242 #[cfg(stm32f1)]
243 let adc = pclk2 / config.adc_pre;
244 #[cfg(stm32f1)]
245 assert!(max::ADC.contains(&adc));
246
247 // Set latency based on HCLK frquency
248 #[cfg(stm32f0)]
249 let latency = match hclk.0 {
250 ..=24_000_000 => Latency::WS0,
251 _ => Latency::WS1,
252 };
253 #[cfg(any(stm32f1, stm32f3))]
254 let latency = match hclk.0 {
255 ..=24_000_000 => Latency::WS0,
256 ..=48_000_000 => Latency::WS1,
257 _ => Latency::WS2,
258 };
259 FLASH.acr().modify(|w| {
260 w.set_latency(latency);
261 // RM0316: "The prefetch buffer must be kept on when using a prescaler
262 // different from 1 on the AHB clock.", "Half-cycle access cannot be
263 // used when there is a prescaler different from 1 on the AHB clock"
264 #[cfg(stm32f3)]
265 if config.ahb_pre != AHBPrescaler::DIV1 {
266 w.set_hlfcya(false);
267 w.set_prftbe(true);
268 }
269 #[cfg(not(stm32f3))]
270 w.set_prftbe(true);
271 });
272
273 // Set prescalers
274 // CFGR has been written before (PLL, PLL48) don't overwrite these settings
275 RCC.cfgr().modify(|w: &mut stm32_metapac::rcc::regs::Cfgr| {
276 #[cfg(not(stm32f0))]
277 {
278 w.set_ppre1(config.apb1_pre);
279 w.set_ppre2(config.apb2_pre);
280 }
281 #[cfg(stm32f0)]
282 w.set_ppre(config.apb1_pre);
283 w.set_hpre(config.ahb_pre);
284 #[cfg(stm32f1)]
285 w.set_adcpre(config.adc_pre);
286 });
287
288 // Wait for the new prescalers to kick in
289 // "The clocks are divided with the new prescaler factor from
290 // 1 to 16 AHB cycles after write"
291 cortex_m::asm::delay(16);
292
293 // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
294 RCC.cfgr().modify(|w| w.set_sw(config.sys));
295 while RCC.cfgr().read().sws() != config.sys {}
296
297 let rtc = config.ls.init();
298
299 #[cfg(all(stm32f3, not(rcc_f37)))]
300 use crate::pac::adccommon::vals::Ckmode;
301
302 #[cfg(all(stm32f3, not(rcc_f37)))]
303 let adc = match config.adc {
304 AdcClockSource::Pll(adcpres) => {
305 RCC.cfgr2().modify(|w| w.set_adc12pres(adcpres));
306 crate::pac::ADC_COMMON
307 .ccr()
308 .modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));
309
310 unwrap!(pll) / adcpres
311 }
312 AdcClockSource::Hclk(adcpres) => {
313 assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));
314
315 let (div, ckmode) = match adcpres {
316 AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1),
317 AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2),
318 AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4),
319 };
320 crate::pac::ADC_COMMON.ccr().modify(|w| w.set_ckmode(ckmode));
321
322 hclk / div
323 }
324 };
325
326 #[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
327 let adc34 = match config.adc34 {
328 AdcClockSource::Pll(adcpres) => {
329 RCC.cfgr2().modify(|w| w.set_adc34pres(adcpres));
330 crate::pac::ADC3_COMMON
331 .ccr()
332 .modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));
333
334 unwrap!(pll) / adcpres
335 }
336 AdcClockSource::Hclk(adcpres) => {
337 assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));
338
339 let (div, ckmode) = match adcpres {
340 AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1),
341 AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2),
342 AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4),
343 };
344 crate::pac::ADC3_COMMON.ccr().modify(|w| w.set_ckmode(ckmode));
345
346 hclk / div
347 }
348 };
349
350 #[cfg(stm32f334)]
351 let hrtim = match config.hrtim {
352 // Must be configured after the bus is ready, otherwise it won't work
353 HrtimClockSource::BusClk => None,
354 HrtimClockSource::PllClk => {
355 use crate::pac::rcc::vals::Timsw;
356
357 // Make sure that we're using the PLL
358 let pll = unwrap!(pll);
359 assert!((pclk2 == pll) || (pclk2 * 2u32 == pll));
360
361 RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
362
363 Some(pll * 2u32)
364 }
365 };
366
367 set_clocks!(
368 hsi: hsi,
369 hse: hse,
370 pll1_p: pll,
371 sys: Some(sys),
372 pclk1: Some(pclk1),
373 pclk2: Some(pclk2),
374 pclk1_tim: Some(pclk1_tim),
375 pclk2_tim: Some(pclk2_tim),
376 hclk1: Some(hclk),
377 #[cfg(all(stm32f3, not(rcc_f37)))]
378 adc: Some(adc),
379 #[cfg(all(stm32f3, not(rcc_f37), adc3_common))]
380 adc34: Some(adc34),
381 #[cfg(stm32f334)]
382 hrtim: hrtim,
383 rtc: rtc,
384 hsi48: hsi48,
385 #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))]
386 usb: usb,
387 lse: None,
388 );
389}
390
391#[cfg(stm32f0)]
392mod max {
393 use core::ops::RangeInclusive;
394
395 use crate::time::Hertz;
396
397 pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);
398 pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);
399
400 pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
401 pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
402
403 pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);
404 pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(48_000_000);
405}
406
407#[cfg(stm32f1)]
408mod max {
409 use core::ops::RangeInclusive;
410
411 use crate::time::Hertz;
412
413 #[cfg(not(rcc_f1cl))]
414 pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(16_000_000);
415 #[cfg(not(rcc_f1cl))]
416 pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);
417
418 #[cfg(rcc_f1cl)]
419 pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(3_000_000)..=Hertz(25_000_000);
420 #[cfg(rcc_f1cl)]
421 pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);
422
423 pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
424 pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);
425 pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
426
427 pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);
428 pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);
429
430 pub(crate) const ADC: RangeInclusive<Hertz> = Hertz(0)..=Hertz(14_000_000);
431}
432
433#[cfg(stm32f3)]
434mod max {
435 use core::ops::RangeInclusive;
436
437 use crate::time::Hertz;
438
439 pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);
440 pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);
441
442 pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
443 pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);
444 pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);
445
446 pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);
447 pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);
448}
diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs
deleted file mode 100644
index 7f0adab27..000000000
--- a/embassy-stm32/src/rcc/f1.rs
+++ /dev/null
@@ -1,191 +0,0 @@
1use core::convert::TryFrom;
2
3use crate::pac::flash::vals::Latency;
4use crate::pac::rcc::vals::*;
5use crate::pac::{FLASH, RCC};
6use crate::time::Hertz;
7
8/// HSI speed
9pub const HSI_FREQ: Hertz = Hertz(8_000_000);
10
11/// Configuration of the clocks
12///
13#[non_exhaustive]
14#[derive(Default)]
15pub struct Config {
16 pub hse: Option<Hertz>,
17
18 pub sys_ck: Option<Hertz>,
19 pub hclk: Option<Hertz>,
20 pub pclk1: Option<Hertz>,
21 pub pclk2: Option<Hertz>,
22 pub adcclk: Option<Hertz>,
23 pub pllxtpre: bool,
24
25 pub ls: super::LsConfig,
26}
27
28pub(crate) unsafe fn init(config: Config) {
29 let pllxtpre_div = if config.pllxtpre { 2 } else { 1 };
30 let pllsrcclk = config.hse.map(|hse| hse.0 / pllxtpre_div).unwrap_or(HSI_FREQ.0 / 2);
31
32 let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
33 let pllmul = sysclk / pllsrcclk;
34
35 let (pllmul_bits, real_sysclk) = if pllmul == 1 {
36 (None, config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0))
37 } else {
38 let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16);
39 (Some(pllmul as u8 - 2), pllsrcclk * pllmul)
40 };
41
42 assert!(real_sysclk <= 72_000_000);
43
44 let hpre_bits = config
45 .hclk
46 .map(|hclk| match real_sysclk / hclk.0 {
47 0 => unreachable!(),
48 1 => 0b0111,
49 2 => 0b1000,
50 3..=5 => 0b1001,
51 6..=11 => 0b1010,
52 12..=39 => 0b1011,
53 40..=95 => 0b1100,
54 96..=191 => 0b1101,
55 192..=383 => 0b1110,
56 _ => 0b1111,
57 })
58 .unwrap_or(0b0111);
59
60 let hclk = if hpre_bits >= 0b1100 {
61 real_sysclk / (1 << (hpre_bits - 0b0110))
62 } else {
63 real_sysclk / (1 << (hpre_bits - 0b0111))
64 };
65
66 assert!(hclk <= 72_000_000);
67
68 let ppre1_bits = config
69 .pclk1
70 .map(|pclk1| match hclk / pclk1.0 {
71 0 => unreachable!(),
72 1 => 0b011,
73 2 => 0b100,
74 3..=5 => 0b101,
75 6..=11 => 0b110,
76 _ => 0b111,
77 })
78 .unwrap_or(0b011);
79
80 let ppre1 = 1 << (ppre1_bits - 0b011);
81 let pclk1 = hclk / u32::try_from(ppre1).unwrap();
82 let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
83
84 assert!(pclk1 <= 36_000_000);
85
86 let ppre2_bits = config
87 .pclk2
88 .map(|pclk2| match hclk / pclk2.0 {
89 0 => unreachable!(),
90 1 => 0b011,
91 2 => 0b100,
92 3..=5 => 0b101,
93 6..=11 => 0b110,
94 _ => 0b111,
95 })
96 .unwrap_or(0b011);
97
98 let ppre2 = 1 << (ppre2_bits - 0b011);
99 let pclk2 = hclk / u32::try_from(ppre2).unwrap();
100 let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
101
102 assert!(pclk2 <= 72_000_000);
103
104 FLASH.acr().write(|w| {
105 w.set_latency(if real_sysclk <= 24_000_000 {
106 Latency::WS0
107 } else if real_sysclk <= 48_000_000 {
108 Latency::WS1
109 } else {
110 Latency::WS2
111 });
112 // the prefetch buffer is enabled by default, let's keep it enabled
113 w.set_prftbe(true);
114 });
115
116 // the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
117 // PLL output frequency is a supported one.
118 // usbpre == false: divide clock by 1.5, otherwise no division
119 #[cfg(not(rcc_f100))]
120 let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) {
121 (Some(_), Some(_), 72_000_000) => (false, true),
122 (Some(_), Some(_), 48_000_000) => (true, true),
123 _ => (true, false),
124 };
125
126 let apre_bits: u8 = config
127 .adcclk
128 .map(|adcclk| match pclk2 / adcclk.0 {
129 0..=2 => 0b00,
130 3..=4 => 0b01,
131 5..=7 => 0b10,
132 _ => 0b11,
133 })
134 .unwrap_or(0b11);
135
136 let apre = (apre_bits + 1) << 1;
137 let adcclk = pclk2 / unwrap!(u32::try_from(apre));
138
139 assert!(adcclk <= 14_000_000);
140
141 if config.hse.is_some() {
142 // enable HSE and wait for it to be ready
143 RCC.cr().modify(|w| w.set_hseon(true));
144 while !RCC.cr().read().hserdy() {}
145 }
146
147 if let Some(pllmul_bits) = pllmul_bits {
148 let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
149 RCC.cfgr()
150 .modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag)));
151
152 // enable PLL and wait for it to be ready
153 RCC.cfgr().modify(|w| {
154 w.set_pllmul(Pllmul::from_bits(pllmul_bits));
155 w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8));
156 });
157
158 RCC.cr().modify(|w| w.set_pllon(true));
159 while !RCC.cr().read().pllrdy() {}
160 }
161
162 // Only needed for stm32f103?
163 RCC.cfgr().modify(|w| {
164 w.set_adcpre(Adcpre::from_bits(apre_bits));
165 w.set_ppre2(Ppre::from_bits(ppre2_bits));
166 w.set_ppre1(Ppre::from_bits(ppre1_bits));
167 w.set_hpre(Hpre::from_bits(hpre_bits));
168 #[cfg(not(rcc_f100))]
169 w.set_usbpre(Usbpre::from_bits(usbpre as u8));
170 w.set_sw(if pllmul_bits.is_some() {
171 Sw::PLL1_P
172 } else if config.hse.is_some() {
173 Sw::HSE
174 } else {
175 Sw::HSI
176 });
177 });
178
179 let rtc = config.ls.init();
180
181 set_clocks!(
182 sys: Some(Hertz(real_sysclk)),
183 pclk1: Some(Hertz(pclk1)),
184 pclk2: Some(Hertz(pclk2)),
185 pclk1_tim: Some(Hertz(pclk1 * timer_mul1)),
186 pclk2_tim: Some(Hertz(pclk2 * timer_mul2)),
187 hclk1: Some(Hertz(hclk)),
188 adc: Some(Hertz(adcclk)),
189 rtc: rtc,
190 );
191}
diff --git a/embassy-stm32/src/rcc/f.rs b/embassy-stm32/src/rcc/f247.rs
index e306d478d..e306d478d 100644
--- a/embassy-stm32/src/rcc/f.rs
+++ b/embassy-stm32/src/rcc/f247.rs
diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs
deleted file mode 100644
index 25866e446..000000000
--- a/embassy-stm32/src/rcc/f3.rs
+++ /dev/null
@@ -1,461 +0,0 @@
1#[cfg(rcc_f3)]
2use crate::pac::adccommon::vals::Ckmode;
3use crate::pac::flash::vals::Latency;
4pub use crate::pac::rcc::vals::Adcpres;
5use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
6use crate::pac::{FLASH, RCC};
7use crate::time::Hertz;
8
9/// HSI speed
10pub const HSI_FREQ: Hertz = Hertz(8_000_000);
11
12#[cfg(rcc_f3)]
13impl From<AdcClockSource> for Ckmode {
14 fn from(value: AdcClockSource) -> Self {
15 match value {
16 AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1,
17 AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2,
18 AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4,
19 _ => unreachable!(),
20 }
21 }
22}
23
24#[derive(Clone, Copy)]
25pub enum AdcClockSource {
26 Pll(Adcpres),
27 BusDiv1,
28 BusDiv2,
29 BusDiv4,
30}
31
32impl AdcClockSource {
33 pub fn bus_div(&self) -> u32 {
34 match self {
35 Self::BusDiv1 => 1,
36 Self::BusDiv2 => 2,
37 Self::BusDiv4 => 4,
38 _ => unreachable!(),
39 }
40 }
41}
42
43#[derive(Default)]
44pub enum HrtimClockSource {
45 #[default]
46 BusClk,
47 PllClk,
48}
49
50/// Clocks configutation
51#[non_exhaustive]
52#[derive(Default)]
53pub struct Config {
54 /// Frequency of HSE oscillator
55 /// 4MHz to 32MHz
56 pub hse: Option<Hertz>,
57 /// Bypass HSE for an external clock
58 pub bypass_hse: bool,
59 /// Frequency of the System Clock
60 pub sysclk: Option<Hertz>,
61 /// Frequency of AHB bus
62 pub hclk: Option<Hertz>,
63 /// Frequency of APB1 bus
64 /// - Max frequency 36MHz
65 pub pclk1: Option<Hertz>,
66 /// Frequency of APB2 bus
67 /// - Max frequency with HSE is 72MHz
68 /// - Max frequency without HSE is 64MHz
69 pub pclk2: Option<Hertz>,
70 /// USB clock setup
71 /// It is valid only when,
72 /// - HSE is enabled,
73 /// - The System clock frequency is either 48MHz or 72MHz
74 /// - APB1 clock has a minimum frequency of 10MHz
75 pub pll48: bool,
76 #[cfg(rcc_f3)]
77 /// ADC clock setup
78 /// - For AHB, a psc of 4 or less must be used
79 pub adc: Option<AdcClockSource>,
80 #[cfg(rcc_f3)]
81 /// ADC clock setup
82 /// - For AHB, a psc of 4 or less must be used
83 pub adc34: Option<AdcClockSource>,
84 #[cfg(stm32f334)]
85 pub hrtim: HrtimClockSource,
86 pub ls: super::LsConfig,
87}
88
89// Information required to setup the PLL clock
90#[derive(Clone, Copy)]
91struct PllConfig {
92 pll_src: Pllsrc,
93 pll_mul: Pllmul,
94 pll_div: Option<Prediv>,
95}
96
97/// Initialize and Set the clock frequencies
98pub(crate) unsafe fn init(config: Config) {
99 // Calculate the real System clock, and PLL configuration if applicable
100 let (sysclk, pll_config) = get_sysclk(&config);
101 assert!(sysclk.0 <= 72_000_000);
102
103 // Calculate real AHB clock
104 let hclk = config.hclk.map(|h| h).unwrap_or(sysclk);
105 let hpre = match sysclk.0 / hclk.0 {
106 0 => unreachable!(),
107 1 => Hpre::DIV1,
108 2 => Hpre::DIV2,
109 3..=5 => Hpre::DIV4,
110 6..=11 => Hpre::DIV8,
111 12..=39 => Hpre::DIV16,
112 40..=95 => Hpre::DIV64,
113 96..=191 => Hpre::DIV128,
114 192..=383 => Hpre::DIV256,
115 _ => Hpre::DIV512,
116 };
117 let hclk = sysclk / hpre;
118 assert!(hclk <= Hertz(72_000_000));
119
120 // Calculate real APB1 clock
121 let pclk1 = config.pclk1.unwrap_or(hclk);
122 let ppre1 = match hclk / pclk1 {
123 0 => unreachable!(),
124 1 => Ppre::DIV1,
125 2 => Ppre::DIV2,
126 3..=5 => Ppre::DIV4,
127 6..=11 => Ppre::DIV8,
128 _ => Ppre::DIV16,
129 };
130 let timer_mul1 = if ppre1 == Ppre::DIV1 { 1u32 } else { 2 };
131 let pclk1 = hclk / ppre1;
132 assert!(pclk1 <= Hertz(36_000_000));
133
134 // Calculate real APB2 clock
135 let pclk2 = config.pclk2.unwrap_or(hclk);
136 let ppre2 = match hclk / pclk2 {
137 0 => unreachable!(),
138 1 => Ppre::DIV1,
139 2 => Ppre::DIV2,
140 3..=5 => Ppre::DIV4,
141 6..=11 => Ppre::DIV8,
142 _ => Ppre::DIV16,
143 };
144 let timer_mul2 = if ppre2 == Ppre::DIV1 { 1u32 } else { 2 };
145 let pclk2 = hclk / ppre2;
146 assert!(pclk2 <= Hertz(72_000_000));
147
148 // Set latency based on HCLK frquency
149 // RM0316: "The prefetch buffer must be kept on when using a prescaler
150 // different from 1 on the AHB clock.", "Half-cycle access cannot be
151 // used when there is a prescaler different from 1 on the AHB clock"
152 FLASH.acr().modify(|w| {
153 w.set_latency(if hclk <= Hertz(24_000_000) {
154 Latency::WS0
155 } else if hclk <= Hertz(48_000_000) {
156 Latency::WS1
157 } else {
158 Latency::WS2
159 });
160 if hpre != Hpre::DIV1 {
161 w.set_hlfcya(false);
162 w.set_prftbe(true);
163 }
164 });
165
166 // Enable HSE
167 // RM0316: "Bits 31:26 Reserved, must be kept at reset value."
168 if config.hse.is_some() {
169 RCC.cr().modify(|w| {
170 w.set_hsebyp(config.bypass_hse);
171 // We turn on clock security to switch to HSI when HSE fails
172 w.set_csson(true);
173 w.set_hseon(true);
174 });
175 while !RCC.cr().read().hserdy() {}
176 }
177
178 // Enable PLL
179 // RM0316: "Reserved, must be kept at reset value."
180 if let Some(ref pll_config) = pll_config {
181 RCC.cfgr().modify(|w| {
182 w.set_pllmul(pll_config.pll_mul);
183 w.set_pllsrc(pll_config.pll_src);
184 });
185 if let Some(pll_div) = pll_config.pll_div {
186 RCC.cfgr2().modify(|w| w.set_prediv(pll_div));
187 }
188 RCC.cr().modify(|w| w.set_pllon(true));
189 while !RCC.cr().read().pllrdy() {}
190 }
191
192 // CFGR has been written before (PLL) don't overwrite these settings
193 if config.pll48 {
194 let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config);
195 RCC.cfgr().modify(|w| {
196 w.set_usbpre(usb_pre);
197 });
198 }
199
200 // Set prescalers
201 // CFGR has been written before (PLL, PLL48) don't overwrite these settings
202 RCC.cfgr().modify(|w| {
203 w.set_ppre2(ppre2);
204 w.set_ppre1(ppre1);
205 w.set_hpre(hpre);
206 });
207
208 // Wait for the new prescalers to kick in
209 // "The clocks are divided with the new prescaler factor from
210 // 1 to 16 AHB cycles after write"
211 cortex_m::asm::delay(16);
212
213 // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
214 RCC.cfgr().modify(|w| {
215 w.set_sw(match (pll_config, config.hse) {
216 (Some(_), _) => Sw::PLL1_P,
217 (None, Some(_)) => Sw::HSE,
218 (None, None) => Sw::HSI,
219 })
220 });
221
222 #[cfg(rcc_f3)]
223 let adc = config.adc.map(|adc| match adc {
224 AdcClockSource::Pll(adcpres) => {
225 RCC.cfgr2().modify(|w| {
226 // Make sure that we're using the PLL
227 pll_config.unwrap();
228 w.set_adc12pres(adcpres);
229
230 sysclk / adcpres
231 })
232 }
233 _ => crate::pac::ADC_COMMON.ccr().modify(|w| {
234 assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
235
236 w.set_ckmode(adc.into());
237
238 sysclk / adc.bus_div()
239 }),
240 });
241
242 #[cfg(all(rcc_f3, adc3_common))]
243 let adc34 = config.adc34.map(|adc| match adc {
244 AdcClockSource::Pll(adcpres) => {
245 RCC.cfgr2().modify(|w| {
246 // Make sure that we're using the PLL
247 pll_config.unwrap();
248 w.set_adc34pres(adcpres);
249
250 sysclk / adcpres
251 })
252 }
253 _ => crate::pac::ADC_COMMON.ccr().modify(|w| {
254 assert!(!(adc.bus_div() == 1 && hpre != Hpre::DIV1));
255
256 w.set_ckmode(adc.into());
257
258 sysclk / adc.bus_div()
259 }),
260 });
261
262 #[cfg(stm32f334)]
263 let hrtim = match config.hrtim {
264 // Must be configured after the bus is ready, otherwise it won't work
265 HrtimClockSource::BusClk => None,
266 HrtimClockSource::PllClk => {
267 use crate::pac::rcc::vals::Timsw;
268
269 // Make sure that we're using the PLL
270 pll_config.unwrap();
271 assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
272
273 RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
274
275 Some(sysclk * 2u32)
276 }
277 };
278
279 let rtc = config.ls.init();
280
281 set_clocks!(
282 hsi: None,
283 lse: None,
284 pll1_p: None,
285 sys: Some(sysclk),
286 pclk1: Some(pclk1),
287 pclk2: Some(pclk2),
288 pclk1_tim: Some(pclk1 * timer_mul1),
289 pclk2_tim: Some(pclk2 * timer_mul2),
290 hclk1: Some(hclk),
291 #[cfg(rcc_f3)]
292 adc: adc,
293 #[cfg(all(rcc_f3, adc3_common))]
294 adc34: adc34,
295 #[cfg(all(rcc_f3, not(adc3_common)))]
296 adc34: None,
297 #[cfg(stm32f334)]
298 hrtim: hrtim,
299 rtc: rtc,
300 );
301}
302
303#[inline]
304fn get_sysclk(config: &Config) -> (Hertz, Option<PllConfig>) {
305 match (config.sysclk, config.hse) {
306 (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
307 (Some(sysclk), None) if sysclk == HSI_FREQ => (HSI_FREQ, None),
308 // If the user selected System clock is different from HSI or HSE
309 // we will have to setup PLL clock source
310 (Some(sysclk), _) => {
311 let (sysclk, pll_config) = calc_pll(config, sysclk);
312 (sysclk, Some(pll_config))
313 }
314 (None, Some(hse)) => (hse, None),
315 (None, None) => (HSI_FREQ, None),
316 }
317}
318
319#[inline]
320fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
321 // Calculates the Multiplier and the Divisor to arrive at
322 // the required System clock from PLL source frequency
323 let get_mul_div = |sysclk, pllsrcclk| {
324 let bus_div = gcd(sysclk, pllsrcclk);
325 let mut multiplier = sysclk / bus_div;
326 let mut divisor = pllsrcclk / bus_div;
327 // Minimum PLL multiplier is two
328 if multiplier == 1 {
329 multiplier *= 2;
330 divisor *= 2;
331 }
332 assert!(multiplier <= 16);
333 assert!(divisor <= 16);
334 (multiplier, divisor)
335 };
336 // Based on the source of Pll, we calculate the actual system clock
337 // frequency, PLL's source identifier, multiplier and divisor
338 let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse {
339 Some(Hertz(hse)) => {
340 let (multiplier, divisor) = get_mul_div(sysclk, hse);
341 (
342 Hertz((hse / divisor) * multiplier),
343 Pllsrc::HSE_DIV_PREDIV,
344 into_pll_mul(multiplier),
345 Some(into_pre_div(divisor)),
346 )
347 }
348 None => {
349 cfg_if::cfg_if! {
350 // For some chips PREDIV is always two, and cannot be changed
351 if #[cfg(any(flashsize_d, flashsize_e))] {
352 let (multiplier, divisor) = get_mul_div(sysclk, HSI_FREQ.0);
353 (
354 Hertz((HSI_FREQ.0 / divisor) * multiplier),
355 Pllsrc::HSI_DIV_PREDIV,
356 into_pll_mul(multiplier),
357 Some(into_pre_div(divisor)),
358 )
359 } else {
360 let pllsrcclk = HSI_FREQ.0 / 2;
361 let multiplier = sysclk / pllsrcclk;
362 assert!(multiplier <= 16);
363 (
364 Hertz(pllsrcclk * multiplier),
365 Pllsrc::HSI_DIV2,
366 into_pll_mul(multiplier),
367 None,
368 )
369 }
370 }
371 }
372 };
373 (
374 act_sysclk,
375 PllConfig {
376 pll_src,
377 pll_mul,
378 pll_div,
379 },
380 )
381}
382
383#[inline]
384#[allow(unused_variables)]
385fn get_usb_pre(config: &Config, sysclk: Hertz, pclk1: Hertz, pll_config: &Option<PllConfig>) -> Usbpre {
386 cfg_if::cfg_if! {
387 // Some chips do not have USB
388 if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
389 panic!("USB clock not supported by the chip");
390 } else {
391 let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= Hertz(10_000_000));
392 match (usb_ok, sysclk) {
393 (true, Hertz(72_000_000)) => Usbpre::DIV1_5,
394 (true, Hertz(48_000_000)) => Usbpre::DIV1,
395 _ => panic!(
396 "USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
397 ),
398 }
399 }
400 }
401}
402
403// This function assumes cases when multiplier is one and it
404// being greater than 16 is made impossible
405#[inline]
406fn into_pll_mul(multiplier: u32) -> Pllmul {
407 match multiplier {
408 2 => Pllmul::MUL2,
409 3 => Pllmul::MUL3,
410 4 => Pllmul::MUL4,
411 5 => Pllmul::MUL5,
412 6 => Pllmul::MUL6,
413 7 => Pllmul::MUL7,
414 8 => Pllmul::MUL8,
415 9 => Pllmul::MUL9,
416 10 => Pllmul::MUL10,
417 11 => Pllmul::MUL11,
418 12 => Pllmul::MUL12,
419 13 => Pllmul::MUL13,
420 14 => Pllmul::MUL14,
421 15 => Pllmul::MUL15,
422 16 => Pllmul::MUL16,
423 _ => unreachable!(),
424 }
425}
426
427// This function assumes the incoming divisor cannot be greater
428// than 16
429#[inline]
430fn into_pre_div(divisor: u32) -> Prediv {
431 match divisor {
432 1 => Prediv::DIV1,
433 2 => Prediv::DIV2,
434 3 => Prediv::DIV3,
435 4 => Prediv::DIV4,
436 5 => Prediv::DIV5,
437 6 => Prediv::DIV6,
438 7 => Prediv::DIV7,
439 8 => Prediv::DIV8,
440 9 => Prediv::DIV9,
441 10 => Prediv::DIV10,
442 11 => Prediv::DIV11,
443 12 => Prediv::DIV12,
444 13 => Prediv::DIV13,
445 14 => Prediv::DIV14,
446 15 => Prediv::DIV15,
447 16 => Prediv::DIV16,
448 _ => unreachable!(),
449 }
450}
451
452// Determine GCD using Euclidean algorithm
453#[inline]
454fn gcd(mut a: u32, mut b: u32) -> u32 {
455 while b != 0 {
456 let r = a % b;
457 a = b;
458 b = r;
459 }
460 a
461}
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index 3e20bf6af..382ffbead 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -1,10 +1,11 @@
1use stm32_metapac::flash::vals::Latency; 1use stm32_metapac::flash::vals::Latency;
2use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; 2use stm32_metapac::rcc::vals::{Adcsel, Sw};
3use stm32_metapac::FLASH; 3use stm32_metapac::FLASH;
4 4
5pub use crate::pac::rcc::vals::{ 5pub use crate::pac::rcc::vals::{
6 Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, 6 Adcsel as AdcClockSource, Clk48sel as Clk48Src, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler,
7 Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler, 7 Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler,
8 Sw as Sysclk,
8}; 9};
9use crate::pac::{PWR, RCC}; 10use crate::pac::{PWR, RCC};
10use crate::time::Hertz; 11use crate::time::Hertz;
@@ -12,28 +13,22 @@ use crate::time::Hertz;
12/// HSI speed 13/// HSI speed
13pub const HSI_FREQ: Hertz = Hertz(16_000_000); 14pub const HSI_FREQ: Hertz = Hertz(16_000_000);
14 15
15/// System clock mux source 16/// HSE Mode
16#[derive(Clone, Copy)] 17#[derive(Clone, Copy, Eq, PartialEq)]
17pub enum ClockSrc { 18pub enum HseMode {
18 HSE(Hertz), 19 /// crystal/ceramic oscillator (HSEBYP=0)
19 HSI, 20 Oscillator,
20 PLL, 21 /// external analog clock (low swing) (HSEBYP=1)
22 Bypass,
21} 23}
22 24
23/// PLL clock input source 25/// HSE Configuration
24#[derive(Clone, Copy, Debug)] 26#[derive(Clone, Copy, Eq, PartialEq)]
25pub enum PllSource { 27pub struct Hse {
26 HSI, 28 /// HSE frequency.
27 HSE(Hertz), 29 pub freq: Hertz,
28} 30 /// HSE mode.
29 31 pub mode: HseMode,
30impl Into<Pllsrc> for PllSource {
31 fn into(self) -> Pllsrc {
32 match self {
33 PllSource::HSE(..) => Pllsrc::HSE,
34 PllSource::HSI => Pllsrc::HSI,
35 }
36 }
37} 32}
38 33
39/// PLL Configuration 34/// PLL Configuration
@@ -43,69 +38,89 @@ impl Into<Pllsrc> for PllSource {
43/// frequency ranges for each of these settings. 38/// frequency ranges for each of these settings.
44pub struct Pll { 39pub struct Pll {
45 /// PLL Source clock selection. 40 /// PLL Source clock selection.
46 pub source: PllSource, 41 pub source: Pllsrc,
47 42
48 /// PLL pre-divider 43 /// PLL pre-divider
49 pub prediv_m: PllM, 44 pub prediv: PllPreDiv,
50 45
51 /// PLL multiplication factor for VCO 46 /// PLL multiplication factor for VCO
52 pub mul_n: PllN, 47 pub mul: PllMul,
53 48
54 /// PLL division factor for P clock (ADC Clock) 49 /// PLL division factor for P clock (ADC Clock)
55 pub div_p: Option<PllP>, 50 pub divp: Option<PllPDiv>,
56 51
57 /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) 52 /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI)
58 pub div_q: Option<PllQ>, 53 pub divq: Option<PllQDiv>,
59 54
60 /// PLL division factor for R clock (SYSCLK) 55 /// PLL division factor for R clock (SYSCLK)
61 pub div_r: Option<PllR>, 56 pub divr: Option<PllRDiv>,
62}
63
64/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
65pub enum Clock48MhzSrc {
66 /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
67 /// oscillator to comply with the USB specification for oscillator tolerance.
68 Hsi48(super::Hsi48Config),
69 /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
70 /// PLL needs to be using the HSE source to comply with the USB specification for oscillator
71 /// tolerance.
72 PllQ,
73} 57}
74 58
75/// Clocks configutation 59/// Clocks configutation
60#[non_exhaustive]
76pub struct Config { 61pub struct Config {
77 pub mux: ClockSrc, 62 /// HSI Enable
63 pub hsi: bool,
64
65 /// HSE Configuration
66 pub hse: Option<Hse>,
67
68 /// System Clock Configuration
69 pub sys: Sysclk,
70
71 /// HSI48 Configuration
72 pub hsi48: Option<super::Hsi48Config>,
73
74 /// PLL Configuration
75 pub pll: Option<Pll>,
76
77 /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
78 /// MUST turn on the PLLR output.
78 pub ahb_pre: AHBPrescaler, 79 pub ahb_pre: AHBPrescaler,
79 pub apb1_pre: APBPrescaler, 80 pub apb1_pre: APBPrescaler,
80 pub apb2_pre: APBPrescaler, 81 pub apb2_pre: APBPrescaler,
82
81 pub low_power_run: bool, 83 pub low_power_run: bool,
82 /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration 84
83 /// MUST turn on the PLLR output.
84 pub pll: Option<Pll>,
85 /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. 85 /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
86 pub clock_48mhz_src: Option<Clock48MhzSrc>, 86 pub clk48_src: Clk48Src,
87
88 /// Low-Speed Clock Configuration
89 pub ls: super::LsConfig,
90
91 /// Clock Source for ADCs 1 and 2
87 pub adc12_clock_source: AdcClockSource, 92 pub adc12_clock_source: AdcClockSource,
93
94 /// Clock Source for ADCs 3, 4 and 5
88 pub adc345_clock_source: AdcClockSource, 95 pub adc345_clock_source: AdcClockSource,
96
97 /// Clock Source for FDCAN
89 pub fdcan_clock_source: FdCanClockSource, 98 pub fdcan_clock_source: FdCanClockSource,
90 99
91 pub ls: super::LsConfig, 100 /// Enable range1 boost mode
101 /// Recommended when the SYSCLK frequency is greater than 150MHz.
102 pub boost: bool,
92} 103}
93 104
94impl Default for Config { 105impl Default for Config {
95 #[inline] 106 #[inline]
96 fn default() -> Config { 107 fn default() -> Config {
97 Config { 108 Config {
98 mux: ClockSrc::HSI, 109 hsi: true,
110 hse: None,
111 sys: Sysclk::HSI,
112 hsi48: Some(Default::default()),
113 pll: None,
99 ahb_pre: AHBPrescaler::DIV1, 114 ahb_pre: AHBPrescaler::DIV1,
100 apb1_pre: APBPrescaler::DIV1, 115 apb1_pre: APBPrescaler::DIV1,
101 apb2_pre: APBPrescaler::DIV1, 116 apb2_pre: APBPrescaler::DIV1,
102 low_power_run: false, 117 low_power_run: false,
103 pll: None, 118 clk48_src: Clk48Src::HSI48,
104 clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), 119 ls: Default::default(),
105 adc12_clock_source: Adcsel::DISABLE, 120 adc12_clock_source: Adcsel::DISABLE,
106 adc345_clock_source: Adcsel::DISABLE, 121 adc345_clock_source: Adcsel::DISABLE,
107 fdcan_clock_source: FdCanClockSource::PCLK1, 122 fdcan_clock_source: FdCanClockSource::PCLK1,
108 ls: Default::default(), 123 boost: false,
109 } 124 }
110 } 125 }
111} 126}
@@ -117,34 +132,65 @@ pub struct PllFreq {
117} 132}
118 133
119pub(crate) unsafe fn init(config: Config) { 134pub(crate) unsafe fn init(config: Config) {
120 let pll_freq = config.pll.map(|pll_config| { 135 // Configure HSI
121 let src_freq = match pll_config.source { 136 let hsi = match config.hsi {
122 PllSource::HSI => { 137 false => {
123 RCC.cr().write(|w| w.set_hsion(true)); 138 RCC.cr().modify(|w| w.set_hsion(false));
124 while !RCC.cr().read().hsirdy() {} 139 None
140 }
141 true => {
142 RCC.cr().modify(|w| w.set_hsion(true));
143 while !RCC.cr().read().hsirdy() {}
144 Some(HSI_FREQ)
145 }
146 };
125 147
126 HSI_FREQ 148 // Configure HSE
127 } 149 let hse = match config.hse {
128 PllSource::HSE(freq) => { 150 None => {
129 RCC.cr().write(|w| w.set_hseon(true)); 151 RCC.cr().modify(|w| w.set_hseon(false));
130 while !RCC.cr().read().hserdy() {} 152 None
131 freq 153 }
154 Some(hse) => {
155 match hse.mode {
156 HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
157 HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
132 } 158 }
159
160 RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
161 RCC.cr().modify(|w| w.set_hseon(true));
162 while !RCC.cr().read().hserdy() {}
163 Some(hse.freq)
164 }
165 };
166
167 // Configure HSI48 if required
168 if let Some(hsi48_config) = config.hsi48 {
169 super::init_hsi48(hsi48_config);
170 }
171
172 let pll_freq = config.pll.map(|pll_config| {
173 let src_freq = match pll_config.source {
174 Pllsrc::HSI => unwrap!(hsi),
175 Pllsrc::HSE => unwrap!(hse),
176 _ => unreachable!(),
133 }; 177 };
134 178
179 // TODO: check PLL input, internal and output frequencies for validity
180
135 // Disable PLL before configuration 181 // Disable PLL before configuration
136 RCC.cr().modify(|w| w.set_pllon(false)); 182 RCC.cr().modify(|w| w.set_pllon(false));
137 while RCC.cr().read().pllrdy() {} 183 while RCC.cr().read().pllrdy() {}
138 184
139 let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n; 185 let internal_freq = src_freq / pll_config.prediv * pll_config.mul;
140 186
141 RCC.pllcfgr().write(|w| { 187 RCC.pllcfgr().write(|w| {
142 w.set_plln(pll_config.mul_n); 188 w.set_plln(pll_config.mul);
143 w.set_pllm(pll_config.prediv_m); 189 w.set_pllm(pll_config.prediv);
144 w.set_pllsrc(pll_config.source.into()); 190 w.set_pllsrc(pll_config.source.into());
145 }); 191 });
146 192
147 let pll_p_freq = pll_config.div_p.map(|div_p| { 193 let pll_p_freq = pll_config.divp.map(|div_p| {
148 RCC.pllcfgr().modify(|w| { 194 RCC.pllcfgr().modify(|w| {
149 w.set_pllp(div_p); 195 w.set_pllp(div_p);
150 w.set_pllpen(true); 196 w.set_pllpen(true);
@@ -152,7 +198,7 @@ pub(crate) unsafe fn init(config: Config) {
152 internal_freq / div_p 198 internal_freq / div_p
153 }); 199 });
154 200
155 let pll_q_freq = pll_config.div_q.map(|div_q| { 201 let pll_q_freq = pll_config.divq.map(|div_q| {
156 RCC.pllcfgr().modify(|w| { 202 RCC.pllcfgr().modify(|w| {
157 w.set_pllq(div_q); 203 w.set_pllq(div_q);
158 w.set_pllqen(true); 204 w.set_pllqen(true);
@@ -160,7 +206,7 @@ pub(crate) unsafe fn init(config: Config) {
160 internal_freq / div_q 206 internal_freq / div_q
161 }); 207 });
162 208
163 let pll_r_freq = pll_config.div_r.map(|div_r| { 209 let pll_r_freq = pll_config.divr.map(|div_r| {
164 RCC.pllcfgr().modify(|w| { 210 RCC.pllcfgr().modify(|w| {
165 w.set_pllr(div_r); 211 w.set_pllr(div_r);
166 w.set_pllren(true); 212 w.set_pllren(true);
@@ -179,22 +225,10 @@ pub(crate) unsafe fn init(config: Config) {
179 } 225 }
180 }); 226 });
181 227
182 let (sys_clk, sw) = match config.mux { 228 let (sys_clk, sw) = match config.sys {
183 ClockSrc::HSI => { 229 Sysclk::HSI => (HSI_FREQ, Sw::HSI),
184 // Enable HSI 230 Sysclk::HSE => (unwrap!(hse), Sw::HSE),
185 RCC.cr().write(|w| w.set_hsion(true)); 231 Sysclk::PLL1_R => {
186 while !RCC.cr().read().hsirdy() {}
187
188 (HSI_FREQ, Sw::HSI)
189 }
190 ClockSrc::HSE(freq) => {
191 // Enable HSE
192 RCC.cr().write(|w| w.set_hseon(true));
193 while !RCC.cr().read().hserdy() {}
194
195 (freq, Sw::HSE)
196 }
197 ClockSrc::PLL => {
198 assert!(pll_freq.is_some()); 232 assert!(pll_freq.is_some());
199 assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); 233 assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
200 234
@@ -202,41 +236,51 @@ pub(crate) unsafe fn init(config: Config) {
202 236
203 assert!(freq <= 170_000_000); 237 assert!(freq <= 170_000_000);
204 238
205 if freq >= 150_000_000 {
206 // Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234)
207 PWR.cr5().modify(|w| w.set_r1mode(false));
208 // Set flash wait state in boost mode based on frequency ([RM0440] p191)
209 if freq <= 36_000_000 {
210 FLASH.acr().modify(|w| w.set_latency(Latency::WS0));
211 } else if freq <= 68_000_000 {
212 FLASH.acr().modify(|w| w.set_latency(Latency::WS1));
213 } else if freq <= 102_000_000 {
214 FLASH.acr().modify(|w| w.set_latency(Latency::WS2));
215 } else if freq <= 136_000_000 {
216 FLASH.acr().modify(|w| w.set_latency(Latency::WS3));
217 } else {
218 FLASH.acr().modify(|w| w.set_latency(Latency::WS4));
219 }
220 } else {
221 PWR.cr5().modify(|w| w.set_r1mode(true));
222 // Set flash wait state in normal mode based on frequency ([RM0440] p191)
223 if freq <= 30_000_000 {
224 FLASH.acr().modify(|w| w.set_latency(Latency::WS0));
225 } else if freq <= 60_000_000 {
226 FLASH.acr().modify(|w| w.set_latency(Latency::WS1));
227 } else if freq <= 80_000_000 {
228 FLASH.acr().modify(|w| w.set_latency(Latency::WS2));
229 } else if freq <= 120_000_000 {
230 FLASH.acr().modify(|w| w.set_latency(Latency::WS3));
231 } else {
232 FLASH.acr().modify(|w| w.set_latency(Latency::WS4));
233 }
234 }
235
236 (Hertz(freq), Sw::PLL1_R) 239 (Hertz(freq), Sw::PLL1_R)
237 } 240 }
241 _ => unreachable!(),
238 }; 242 };
239 243
244 // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.
245 let hclk = sys_clk / config.ahb_pre;
246
247 // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!)
248 if config.boost {
249 // RM0440 p235
250 // “The sequence to switch from Range1 normal mode to Range1 boost mode is:
251 // 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency.
252 RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2));
253 // 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode)
254 PWR.cr5().modify(|w| w.set_r1mode(false));
255
256 // Below:
257 // 3. Adjust wait states according to new freq target
258 // 4. Configure and switch to new frequency
259 }
260
261 // Configure flash read access latency based on boost mode and frequency (RM0440 p98)
262 FLASH.acr().modify(|w| {
263 w.set_latency(match (config.boost, hclk.0) {
264 (true, ..=34_000_000) => Latency::WS0,
265 (true, ..=68_000_000) => Latency::WS1,
266 (true, ..=102_000_000) => Latency::WS2,
267 (true, ..=136_000_000) => Latency::WS3,
268 (true, _) => Latency::WS4,
269
270 (false, ..=36_000_000) => Latency::WS0,
271 (false, ..=60_000_000) => Latency::WS1,
272 (false, ..=90_000_000) => Latency::WS2,
273 (false, ..=120_000_000) => Latency::WS3,
274 (false, _) => Latency::WS4,
275 })
276 });
277
278 if config.boost {
279 // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency.
280 cortex_m::asm::delay(16);
281 }
282
283 // Now that boost mode and flash read access latency are configured, set up SYSCLK
240 RCC.cfgr().modify(|w| { 284 RCC.cfgr().modify(|w| {
241 w.set_sw(sw); 285 w.set_sw(sw);
242 w.set_hpre(config.ahb_pre); 286 w.set_hpre(config.ahb_pre);
@@ -244,42 +288,26 @@ pub(crate) unsafe fn init(config: Config) {
244 w.set_ppre2(config.apb2_pre); 288 w.set_ppre2(config.apb2_pre);
245 }); 289 });
246 290
247 let ahb_freq = sys_clk / config.ahb_pre; 291 let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre);
248 292 let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre);
249 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
250 APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
251 pre => {
252 let freq = ahb_freq / pre;
253 (freq, freq * 2u32)
254 }
255 };
256
257 let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
258 APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
259 pre => {
260 let freq = ahb_freq / pre;
261 (freq, freq * 2u32)
262 }
263 };
264
265 // Setup the 48 MHz clock if needed
266 if let Some(clock_48mhz_src) = config.clock_48mhz_src {
267 let source = match clock_48mhz_src {
268 Clock48MhzSrc::PllQ => {
269 // Make sure the PLLQ is enabled and running at 48Mhz
270 let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
271 assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
272 293
294 // Configure the 48MHz clock source for USB and RNG peripherals.
295 RCC.ccipr().modify(|w| {
296 w.set_clk48sel(match config.clk48_src {
297 Clk48Src::PLL1_Q => {
298 // Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock.
299 // Peripherals which require one (USB, RNG) should check that they‘re driven by a valid 48MHz
300 // clock at init.
273 crate::pac::rcc::vals::Clk48sel::PLL1_Q 301 crate::pac::rcc::vals::Clk48sel::PLL1_Q
274 } 302 }
275 Clock48MhzSrc::Hsi48(config) => { 303 Clk48Src::HSI48 => {
276 super::init_hsi48(config); 304 // Make sure HSI48 is enabled
305 assert!(config.hsi48.is_some());
277 crate::pac::rcc::vals::Clk48sel::HSI48 306 crate::pac::rcc::vals::Clk48sel::HSI48
278 } 307 }
279 }; 308 _ => unreachable!(),
280 309 })
281 RCC.ccipr().modify(|w| w.set_clk48sel(source)); 310 });
282 }
283 311
284 RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); 312 RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source));
285 RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); 313 RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
@@ -308,18 +336,42 @@ pub(crate) unsafe fn init(config: Config) {
308 336
309 set_clocks!( 337 set_clocks!(
310 sys: Some(sys_clk), 338 sys: Some(sys_clk),
311 hclk1: Some(ahb_freq), 339 hclk1: Some(hclk),
312 hclk2: Some(ahb_freq), 340 hclk2: Some(hclk),
313 hclk3: Some(ahb_freq), 341 hclk3: Some(hclk),
314 pclk1: Some(apb1_freq), 342 pclk1: Some(apb1_freq),
315 pclk1_tim: Some(apb1_tim_freq), 343 pclk1_tim: Some(apb1_tim_freq),
316 pclk2: Some(apb2_freq), 344 pclk2: Some(apb2_freq),
317 pclk2_tim: Some(apb2_tim_freq), 345 pclk2_tim: Some(apb2_tim_freq),
318 adc: adc12_ck, 346 adc: adc12_ck,
319 adc34: adc345_ck, 347 adc34: adc345_ck,
320 pll1_p: None, 348 pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p),
321 pll1_q: None, // TODO 349 pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_p),
322 hse: None, // TODO 350 hse: hse,
323 rtc: rtc, 351 rtc: rtc,
324 ); 352 );
325} 353}
354
355// TODO: if necessary, make more of these, gated behind cfg attrs
356mod max {
357 use core::ops::RangeInclusive;
358
359 use crate::time::Hertz;
360
361 /// HSE 4-48MHz (RM0440 p280)
362 pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);
363
364 /// External Clock ?-48MHz (RM0440 p280)
365 pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);
366
367 // SYSCLK ?-170MHz (RM0440 p282)
368 //pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);
369
370 // PLL Output frequency ?-170MHz (RM0440 p281)
371 //pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);
372
373 // Left over from f.rs, remove if not necessary
374 //pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
375 //pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
376 //pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
377}
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs
index 474c44115..c2a71eaf1 100644
--- a/embassy-stm32/src/rcc/h.rs
+++ b/embassy-stm32/src/rcc/h.rs
@@ -170,22 +170,7 @@ pub enum SupplyConfig {
170/// This is only used in certain power supply configurations: 170/// This is only used in certain power supply configurations:
171/// SMPSLDO, SMPSExternalLDO, SMPSExternalLDOBypass. 171/// SMPSLDO, SMPSExternalLDO, SMPSExternalLDOBypass.
172#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))] 172#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
173#[derive(PartialEq)] 173pub use pac::pwr::vals::Sdlevel as SMPSSupplyVoltage;
174pub enum SMPSSupplyVoltage {
175 V1_8,
176 V2_5,
177}
178
179#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
180impl SMPSSupplyVoltage {
181 /// Convert SMPSSupplyVoltage to u8 representation.
182 fn to_u8(&self) -> u8 {
183 match self {
184 SMPSSupplyVoltage::V1_8 => 0b01,
185 SMPSSupplyVoltage::V2_5 => 0b10,
186 }
187 }
188}
189 174
190/// Configuration of the core clocks 175/// Configuration of the core clocks
191#[non_exhaustive] 176#[non_exhaustive]
@@ -279,7 +264,7 @@ pub(crate) unsafe fn init(config: Config) {
279 match config.supply_config { 264 match config.supply_config {
280 SupplyConfig::Default => { 265 SupplyConfig::Default => {
281 PWR.cr3().modify(|w| { 266 PWR.cr3().modify(|w| {
282 w.set_sdlevel(0b00); 267 w.set_sdlevel(SMPSSupplyVoltage::RESET);
283 w.set_sdexthp(false); 268 w.set_sdexthp(false);
284 w.set_sden(true); 269 w.set_sden(true);
285 w.set_ldoen(true); 270 w.set_ldoen(true);
@@ -301,11 +286,11 @@ pub(crate) unsafe fn init(config: Config) {
301 w.set_bypass(false); 286 w.set_bypass(false);
302 }); 287 });
303 } 288 }
304 SupplyConfig::SMPSLDO(ref smps_supply_voltage) 289 SupplyConfig::SMPSLDO(smps_supply_voltage)
305 | SupplyConfig::SMPSExternalLDO(ref smps_supply_voltage) 290 | SupplyConfig::SMPSExternalLDO(smps_supply_voltage)
306 | SupplyConfig::SMPSExternalLDOBypass(ref smps_supply_voltage) => { 291 | SupplyConfig::SMPSExternalLDOBypass(smps_supply_voltage) => {
307 PWR.cr3().modify(|w| { 292 PWR.cr3().modify(|w| {
308 w.set_sdlevel(smps_supply_voltage.to_u8()); 293 w.set_sdlevel(smps_supply_voltage);
309 w.set_sdexthp(matches!( 294 w.set_sdexthp(matches!(
310 config.supply_config, 295 config.supply_config,
311 SupplyConfig::SMPSExternalLDO(_) | SupplyConfig::SMPSExternalLDOBypass(_) 296 SupplyConfig::SMPSExternalLDO(_) | SupplyConfig::SMPSExternalLDOBypass(_)
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index eaaf8071c..654943bc1 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -4,7 +4,7 @@ use embassy_hal_internal::into_ref;
4 4
5use crate::gpio::sealed::AFType; 5use crate::gpio::sealed::AFType;
6use crate::gpio::Speed; 6use crate::gpio::Speed;
7#[cfg(not(stm32f1))] 7#[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]
8pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; 8pub use crate::pac::rcc::vals::Mcopre as McoPrescaler;
9#[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))] 9#[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))]
10pub use crate::pac::rcc::vals::Mcosel as McoSource; 10pub use crate::pac::rcc::vals::Mcosel as McoSource;
@@ -13,10 +13,16 @@ pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source};
13use crate::pac::RCC; 13use crate::pac::RCC;
14use crate::{peripherals, Peripheral}; 14use crate::{peripherals, Peripheral};
15 15
16#[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))]
17#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
18pub enum McoPrescaler {
19 DIV1,
20}
21
16pub(crate) mod sealed { 22pub(crate) mod sealed {
17 pub trait McoInstance { 23 pub trait McoInstance {
18 type Source; 24 type Source;
19 unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: super::McoPrescaler); 25 unsafe fn apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler);
20 } 26 }
21} 27}
22 28
@@ -29,7 +35,7 @@ macro_rules! impl_peri {
29 impl sealed::McoInstance for peripherals::$peri { 35 impl sealed::McoInstance for peripherals::$peri {
30 type Source = $source; 36 type Source = $source;
31 37
32 unsafe fn apply_clock_settings(source: Self::Source, #[cfg(not(stm32f1))] prescaler: McoPrescaler) { 38 unsafe fn apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) {
33 #[cfg(not(any(stm32u5, stm32wba)))] 39 #[cfg(not(any(stm32u5, stm32wba)))]
34 let r = RCC.cfgr(); 40 let r = RCC.cfgr();
35 #[cfg(any(stm32u5, stm32wba))] 41 #[cfg(any(stm32u5, stm32wba))]
@@ -37,8 +43,8 @@ macro_rules! impl_peri {
37 43
38 r.modify(|w| { 44 r.modify(|w| {
39 w.$set_source(source); 45 w.$set_source(source);
40 #[cfg(not(stm32f1))] 46 #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]
41 w.$set_prescaler(prescaler); 47 w.$set_prescaler(_prescaler);
42 }); 48 });
43 } 49 }
44 } 50 }
@@ -68,16 +74,12 @@ impl<'d, T: McoInstance> Mco<'d, T> {
68 _peri: impl Peripheral<P = T> + 'd, 74 _peri: impl Peripheral<P = T> + 'd,
69 pin: impl Peripheral<P = impl McoPin<T>> + 'd, 75 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
70 source: T::Source, 76 source: T::Source,
71 #[cfg(not(stm32f1))] prescaler: McoPrescaler, 77 prescaler: McoPrescaler,
72 ) -> Self { 78 ) -> Self {
73 into_ref!(pin); 79 into_ref!(pin);
74 80
75 critical_section::with(|_| unsafe { 81 critical_section::with(|_| unsafe {
76 T::apply_clock_settings( 82 T::apply_clock_settings(source, prescaler);
77 source,
78 #[cfg(not(stm32f1))]
79 prescaler,
80 );
81 pin.set_as_af(pin.af_num(), AFType::OutputPushPull); 83 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
82 pin.set_speed(Speed::VeryHigh); 84 pin.set_speed(Speed::VeryHigh);
83 }); 85 });
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 05937cc5d..0f3467151 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -18,17 +18,15 @@ mod hsi48;
18#[cfg(crs)] 18#[cfg(crs)]
19pub use hsi48::*; 19pub use hsi48::*;
20 20
21#[cfg_attr(rcc_f0, path = "f0.rs")] 21#[cfg_attr(any(stm32f0, stm32f1, stm32f3), path = "f013.rs")]
22#[cfg_attr(any(stm32f1), path = "f1.rs")] 22#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f247.rs")]
23#[cfg_attr(any(stm32f3), path = "f3.rs")] 23#[cfg_attr(stm32c0, path = "c0.rs")]
24#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = "f.rs")] 24#[cfg_attr(stm32g0, path = "g0.rs")]
25#[cfg_attr(rcc_c0, path = "c0.rs")] 25#[cfg_attr(stm32g4, path = "g4.rs")]
26#[cfg_attr(rcc_g0, path = "g0.rs")]
27#[cfg_attr(rcc_g4, path = "g4.rs")]
28#[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")] 26#[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")]
29#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")] 27#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
30#[cfg_attr(rcc_u5, path = "u5.rs")] 28#[cfg_attr(stm32u5, path = "u5.rs")]
31#[cfg_attr(rcc_wba, path = "wba.rs")] 29#[cfg_attr(stm32wba, path = "wba.rs")]
32mod _version; 30mod _version;
33 31
34pub use _version::*; 32pub use _version::*;
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs
index 47ce4783c..fbf2d1cf9 100644
--- a/embassy-stm32/src/rcc/wba.rs
+++ b/embassy-stm32/src/rcc/wba.rs
@@ -49,7 +49,7 @@ impl Default for Config {
49 apb2_pre: APBPrescaler::DIV1, 49 apb2_pre: APBPrescaler::DIV1,
50 apb7_pre: APBPrescaler::DIV1, 50 apb7_pre: APBPrescaler::DIV1,
51 ls: Default::default(), 51 ls: Default::default(),
52 adc_clock_source: AdcClockSource::HCLK1, 52 adc_clock_source: AdcClockSource::HCLK4,
53 voltage_scale: VoltageScale::RANGE2, 53 voltage_scale: VoltageScale::RANGE2,
54 } 54 }
55 } 55 }
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 29ff4a736..a1f54307d 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -1,3 +1,5 @@
1#![allow(non_snake_case)]
2
1use core::cell::Cell; 3use core::cell::Cell;
2use core::convert::TryInto; 4use core::convert::TryInto;
3use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; 5use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
@@ -22,18 +24,22 @@ use crate::{interrupt, peripherals};
22// As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any 24// As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any
23// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST 25// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST
24// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not 26// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not
25// candidates for use as an embassy-time driver provider. 27// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.)
26// 28//
27// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number 29// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number
28// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: 30// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers:
29// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. 31// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3.
30 32
31#[cfg(not(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22)))] 33cfg_if::cfg_if! {
32const ALARM_COUNT: usize = 3; 34 if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] {
33 35 const ALARM_COUNT: usize = 1;
34#[cfg(any(time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] 36 } else {
35const ALARM_COUNT: usize = 1; 37 const ALARM_COUNT: usize = 3;
38 }
39}
36 40
41#[cfg(time_drvier_tim1)]
42type T = peripherals::TIM1;
37#[cfg(time_driver_tim2)] 43#[cfg(time_driver_tim2)]
38type T = peripherals::TIM2; 44type T = peripherals::TIM2;
39#[cfg(time_driver_tim3)] 45#[cfg(time_driver_tim3)]
@@ -42,6 +48,8 @@ type T = peripherals::TIM3;
42type T = peripherals::TIM4; 48type T = peripherals::TIM4;
43#[cfg(time_driver_tim5)] 49#[cfg(time_driver_tim5)]
44type T = peripherals::TIM5; 50type T = peripherals::TIM5;
51#[cfg(time_driver_tim8)]
52type T = peripherals::TIM8;
45#[cfg(time_driver_tim9)] 53#[cfg(time_driver_tim9)]
46type T = peripherals::TIM9; 54type T = peripherals::TIM9;
47#[cfg(time_driver_tim11)] 55#[cfg(time_driver_tim11)]
@@ -50,12 +58,26 @@ type T = peripherals::TIM11;
50type T = peripherals::TIM12; 58type T = peripherals::TIM12;
51#[cfg(time_driver_tim15)] 59#[cfg(time_driver_tim15)]
52type T = peripherals::TIM15; 60type T = peripherals::TIM15;
61#[cfg(time_driver_tim20)]
62type T = peripherals::TIM20;
53#[cfg(time_driver_tim21)] 63#[cfg(time_driver_tim21)]
54type T = peripherals::TIM21; 64type T = peripherals::TIM21;
55#[cfg(time_driver_tim22)] 65#[cfg(time_driver_tim22)]
56type T = peripherals::TIM22; 66type T = peripherals::TIM22;
67#[cfg(time_driver_tim23)]
68type T = peripherals::TIM23;
69#[cfg(time_driver_tim24)]
70type T = peripherals::TIM24;
57 71
58foreach_interrupt! { 72foreach_interrupt! {
73 (TIM1, timer, $block:ident, UP, $irq:ident) => {
74 #[cfg(time_driver_tim1)]
75 #[cfg(feature = "rt")]
76 #[interrupt]
77 fn $irq() {
78 DRIVER.on_interrupt()
79 }
80 };
59 (TIM2, timer, $block:ident, UP, $irq:ident) => { 81 (TIM2, timer, $block:ident, UP, $irq:ident) => {
60 #[cfg(time_driver_tim2)] 82 #[cfg(time_driver_tim2)]
61 #[cfg(feature = "rt")] 83 #[cfg(feature = "rt")]
@@ -88,16 +110,16 @@ foreach_interrupt! {
88 DRIVER.on_interrupt() 110 DRIVER.on_interrupt()
89 } 111 }
90 }; 112 };
91 (TIM9, timer, $block:ident, UP, $irq:ident) => { 113 (TIM8, timer, $block:ident, UP, $irq:ident) => {
92 #[cfg(time_driver_tim9)] 114 #[cfg(time_driver_tim8)]
93 #[cfg(feature = "rt")] 115 #[cfg(feature = "rt")]
94 #[interrupt] 116 #[interrupt]
95 fn $irq() { 117 fn $irq() {
96 DRIVER.on_interrupt() 118 DRIVER.on_interrupt()
97 } 119 }
98 }; 120 };
99 (TIM11, timer, $block:ident, UP, $irq:ident) => { 121 (TIM9, timer, $block:ident, UP, $irq:ident) => {
100 #[cfg(time_driver_tim11)] 122 #[cfg(time_driver_tim9)]
101 #[cfg(feature = "rt")] 123 #[cfg(feature = "rt")]
102 #[interrupt] 124 #[interrupt]
103 fn $irq() { 125 fn $irq() {
@@ -120,6 +142,14 @@ foreach_interrupt! {
120 DRIVER.on_interrupt() 142 DRIVER.on_interrupt()
121 } 143 }
122 }; 144 };
145 (TIM20, timer, $block:ident, UP, $irq:ident) => {
146 #[cfg(time_driver_tim20)]
147 #[cfg(feature = "rt")]
148 #[interrupt]
149 fn $irq() {
150 DRIVER.on_interrupt()
151 }
152 };
123 (TIM21, timer, $block:ident, UP, $irq:ident) => { 153 (TIM21, timer, $block:ident, UP, $irq:ident) => {
124 #[cfg(time_driver_tim21)] 154 #[cfg(time_driver_tim21)]
125 #[cfg(feature = "rt")] 155 #[cfg(feature = "rt")]
@@ -136,6 +166,22 @@ foreach_interrupt! {
136 DRIVER.on_interrupt() 166 DRIVER.on_interrupt()
137 } 167 }
138 }; 168 };
169 (TIM23, timer, $block:ident, UP, $irq:ident) => {
170 #[cfg(time_driver_tim23)]
171 #[cfg(feature = "rt")]
172 #[interrupt]
173 fn $irq() {
174 DRIVER.on_interrupt()
175 }
176 };
177 (TIM24, timer, $block:ident, UP, $irq:ident) => {
178 #[cfg(time_driver_tim24)]
179 #[cfg(feature = "rt")]
180 #[interrupt]
181 fn $irq() {
182 DRIVER.on_interrupt()
183 }
184 };
139} 185}
140 186
141// Clock timekeeping works with something we call "periods", which are time intervals 187// Clock timekeeping works with something we call "periods", which are time intervals
diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs
index 45d780bf8..da5ff0f36 100644
--- a/embassy-usb-logger/src/lib.rs
+++ b/embassy-usb-logger/src/lib.rs
@@ -6,7 +6,7 @@ use core::fmt::Write as _;
6 6
7use embassy_futures::join::join; 7use embassy_futures::join::join;
8use embassy_sync::pipe::Pipe; 8use embassy_sync::pipe::Pipe;
9use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; 9use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
10use embassy_usb::driver::Driver; 10use embassy_usb::driver::Driver;
11use embassy_usb::{Builder, Config}; 11use embassy_usb::{Builder, Config};
12use log::{Metadata, Record}; 12use log::{Metadata, Record};
@@ -37,6 +37,9 @@ impl<'d> LoggerState<'d> {
37 } 37 }
38} 38}
39 39
40/// The packet size used in the usb logger, to be used with `create_future_from_class`
41pub const MAX_PACKET_SIZE: u8 = 64;
42
40/// The logger handle, which contains a pipe with configurable size for buffering log messages. 43/// The logger handle, which contains a pipe with configurable size for buffering log messages.
41pub struct UsbLogger<const N: usize> { 44pub struct UsbLogger<const N: usize> {
42 buffer: Pipe<CS, N>, 45 buffer: Pipe<CS, N>,
@@ -54,7 +57,6 @@ impl<const N: usize> UsbLogger<N> {
54 D: Driver<'d>, 57 D: Driver<'d>,
55 Self: 'd, 58 Self: 'd,
56 { 59 {
57 const MAX_PACKET_SIZE: u8 = 64;
58 let mut config = Config::new(0xc0de, 0xcafe); 60 let mut config = Config::new(0xc0de, 0xcafe);
59 config.manufacturer = Some("Embassy"); 61 config.manufacturer = Some("Embassy");
60 config.product = Some("USB-serial logger"); 62 config.product = Some("USB-serial logger");
@@ -87,22 +89,46 @@ impl<const N: usize> UsbLogger<N> {
87 let mut device = builder.build(); 89 let mut device = builder.build();
88 loop { 90 loop {
89 let run_fut = device.run(); 91 let run_fut = device.run();
90 let log_fut = async { 92 let class_fut = self.run_logger_class(&mut sender, &mut receiver);
91 let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; 93 join(run_fut, class_fut).await;
92 sender.wait_connection().await; 94 }
93 loop { 95 }
94 let len = self.buffer.read(&mut rx[..]).await; 96
95 let _ = sender.write_packet(&rx[..len]).await; 97 async fn run_logger_class<'d, D>(&self, sender: &mut Sender<'d, D>, receiver: &mut Receiver<'d, D>)
96 } 98 where
97 }; 99 D: Driver<'d>,
98 let discard_fut = async { 100 {
99 let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; 101 let log_fut = async {
100 receiver.wait_connection().await; 102 let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
101 loop { 103 sender.wait_connection().await;
102 let _ = receiver.read_packet(&mut discard_buf).await; 104 loop {
105 let len = self.buffer.read(&mut rx[..]).await;
106 let _ = sender.write_packet(&rx[..len]).await;
107 if len as u8 == MAX_PACKET_SIZE {
108 let _ = sender.write_packet(&[]).await;
103 } 109 }
104 }; 110 }
105 join(run_fut, join(log_fut, discard_fut)).await; 111 };
112 let discard_fut = async {
113 let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];
114 receiver.wait_connection().await;
115 loop {
116 let _ = receiver.read_packet(&mut discard_buf).await;
117 }
118 };
119
120 join(log_fut, discard_fut).await;
121 }
122
123 /// Creates the futures needed for the logger from a given class
124 /// This can be used in cases where the usb device is already in use for another connection
125 pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>)
126 where
127 D: Driver<'d>,
128 {
129 let (mut sender, mut receiver) = class.split();
130 loop {
131 self.run_logger_class(&mut sender, &mut receiver).await;
106 } 132 }
107 } 133 }
108} 134}
@@ -153,3 +179,27 @@ macro_rules! run {
153 let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; 179 let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;
154 }; 180 };
155} 181}
182
183/// Initialize the USB serial logger from a serial class and return the future to run it.
184///
185/// Arguments specify the buffer size, log level and the serial class, respectively.
186///
187/// # Usage
188///
189/// ```
190/// embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, class);
191/// ```
192///
193/// # Safety
194///
195/// This macro should only be invoked only once since it is setting the global logging state of the application.
196#[macro_export]
197macro_rules! with_class {
198 ( $x:expr, $l:expr, $p:ident ) => {{
199 static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new();
200 unsafe {
201 let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));
202 }
203 LOGGER.create_future_from_class($p)
204 }};
205}
diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs
index ac470d2be..9fffb4646 100644
--- a/examples/rp/src/bin/i2c_slave.rs
+++ b/examples/rp/src/bin/i2c_slave.rs
@@ -110,7 +110,7 @@ async fn main(spawner: Spawner) {
110 let c_sda = p.PIN_1; 110 let c_sda = p.PIN_1;
111 let c_scl = p.PIN_0; 111 let c_scl = p.PIN_0;
112 let mut config = i2c::Config::default(); 112 let mut config = i2c::Config::default();
113 config.frequency = 5_000; 113 config.frequency = 1_000_000;
114 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); 114 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
115 115
116 unwrap!(spawner.spawn(controller_task(controller))); 116 unwrap!(spawner.spawn(controller_task(controller)));
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index 9a97cb8a7..ac145933c 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -12,7 +12,7 @@ use embassy_rp::pio::{
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
13}; 13};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; 14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::Timer; 15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8; 16use fixed::types::U24F8;
17use fixed_macro::fixed; 17use fixed_macro::fixed;
18use smart_leds::RGB8; 18use smart_leds::RGB8;
@@ -107,6 +107,8 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
107 107
108 // DMA transfer 108 // DMA transfer
109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await; 109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
110
111 Timer::after_micros(55).await;
110 } 112 }
111} 113}
112 114
@@ -143,6 +145,7 @@ async fn main(_spawner: Spawner) {
143 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 145 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
144 146
145 // Loop forever making RGB values and pushing them out to the WS2812. 147 // Loop forever making RGB values and pushing them out to the WS2812.
148 let mut ticker = Ticker::every(Duration::from_millis(10));
146 loop { 149 loop {
147 for j in 0..(256 * 5) { 150 for j in 0..(256 * 5) {
148 debug!("New Colors:"); 151 debug!("New Colors:");
@@ -152,7 +155,7 @@ async fn main(_spawner: Spawner) {
152 } 155 }
153 ws2812.write(&data).await; 156 ws2812.write(&data).await;
154 157
155 Timer::after_millis(10).await; 158 ticker.next().await;
156 } 159 }
157 } 160 }
158} 161}
diff --git a/examples/rp/src/bin/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs
new file mode 100644
index 000000000..4ba4fc25c
--- /dev/null
+++ b/examples/rp/src/bin/usb_serial_with_logger.rs
@@ -0,0 +1,117 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip as well as how to create multiple usb classes for one device
2//!
3//! This creates a USB serial port that echos. It will also print out logging information on a separate serial device
4
5#![no_std]
6#![no_main]
7
8use defmt::{info, panic};
9use embassy_executor::Spawner;
10use embassy_futures::join::join;
11use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver, Instance, InterruptHandler};
14use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
15use embassy_usb::driver::EndpointError;
16use embassy_usb::{Builder, Config};
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 info!("Hello there!");
26
27 let p = embassy_rp::init(Default::default());
28
29 // Create the driver, from the HAL.
30 let driver = Driver::new(p.USB, Irqs);
31
32 // Create embassy-usb Config
33 let mut config = Config::new(0xc0de, 0xcafe);
34 config.manufacturer = Some("Embassy");
35 config.product = Some("USB-serial example");
36 config.serial_number = Some("12345678");
37 config.max_power = 100;
38 config.max_packet_size_0 = 64;
39
40 // Required for windows compatibility.
41 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
42 config.device_class = 0xEF;
43 config.device_sub_class = 0x02;
44 config.device_protocol = 0x01;
45 config.composite_with_iads = true;
46
47 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors.
49 let mut device_descriptor = [0; 256];
50 let mut config_descriptor = [0; 256];
51 let mut bos_descriptor = [0; 256];
52 let mut control_buf = [0; 64];
53
54 let mut state = State::new();
55 let mut logger_state = State::new();
56
57 let mut builder = Builder::new(
58 driver,
59 config,
60 &mut device_descriptor,
61 &mut config_descriptor,
62 &mut bos_descriptor,
63 &mut [], // no msos descriptors
64 &mut control_buf,
65 );
66
67 // Create classes on the builder.
68 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
69
70 // Create a class for the logger
71 let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64);
72
73 // Creates the logger and returns the logger future
74 // Note: You'll need to use log::info! afterwards instead of info! for this to work (this also applies to all the other log::* macros)
75 let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class);
76
77 // Build the builder.
78 let mut usb = builder.build();
79
80 // Run the USB device.
81 let usb_fut = usb.run();
82
83 // Do stuff with the class!
84 let echo_fut = async {
85 loop {
86 class.wait_connection().await;
87 log::info!("Connected");
88 let _ = echo(&mut class).await;
89 log::info!("Disconnected");
90 }
91 };
92
93 // Run everything concurrently.
94 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
95 join(usb_fut, join(echo_fut, log_fut)).await;
96}
97
98struct Disconnected {}
99
100impl From<EndpointError> for Disconnected {
101 fn from(val: EndpointError) -> Self {
102 match val {
103 EndpointError::BufferOverflow => panic!("Buffer overflow"),
104 EndpointError::Disabled => Disconnected {},
105 }
106 }
107}
108
109async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
110 let mut buf = [0; 64];
111 loop {
112 let n = class.read_packet(&mut buf).await?;
113 let data = &buf[..n];
114 info!("data: {:x}", data);
115 class.write_packet(data).await?;
116 }
117}
diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml
index 71b0eb683..c74980dc4 100644
--- a/examples/stm32f0/Cargo.toml
+++ b/examples/stm32f0/Cargo.toml
@@ -13,7 +13,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing
13cortex-m-rt = "0.7.0" 13cortex-m-rt = "0.7.0"
14defmt = "0.3" 14defmt = "0.3"
15defmt-rtt = "0.4" 15defmt-rtt = "0.4"
16panic-probe = "0.3" 16panic-probe = { version = "0.3", features = ["print-defmt"] }
17embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } 17embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
18embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 18embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
19embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 19embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
diff --git a/examples/stm32f1/src/bin/hello.rs b/examples/stm32f1/src/bin/hello.rs
index 7b761ecc1..3c295612c 100644
--- a/examples/stm32f1/src/bin/hello.rs
+++ b/examples/stm32f1/src/bin/hello.rs
@@ -3,15 +3,13 @@
3 3
4use defmt::info; 4use defmt::info;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::time::Hertz;
7use embassy_stm32::Config; 6use embassy_stm32::Config;
8use embassy_time::Timer; 7use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
10 9
11#[embassy_executor::main] 10#[embassy_executor::main]
12async fn main(_spawner: Spawner) -> ! { 11async fn main(_spawner: Spawner) -> ! {
13 let mut config = Config::default(); 12 let config = Config::default();
14 config.rcc.sys_ck = Some(Hertz(36_000_000));
15 let _p = embassy_stm32::init(config); 13 let _p = embassy_stm32::init(config);
16 14
17 loop { 15 loop {
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs
index e28381893..1ae6c1dee 100644
--- a/examples/stm32f1/src/bin/usb_serial.rs
+++ b/examples/stm32f1/src/bin/usb_serial.rs
@@ -21,9 +21,23 @@ bind_interrupts!(struct Irqs {
21#[embassy_executor::main] 21#[embassy_executor::main]
22async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
23 let mut config = Config::default(); 23 let mut config = Config::default();
24 config.rcc.hse = Some(Hertz(8_000_000)); 24 {
25 config.rcc.sys_ck = Some(Hertz(48_000_000)); 25 use embassy_stm32::rcc::*;
26 config.rcc.pclk1 = Some(Hertz(24_000_000)); 26 config.rcc.hse = Some(Hse {
27 freq: Hertz(8_000_000),
28 // Oscillator for bluepill, Bypass for nucleos.
29 mode: HseMode::Oscillator,
30 });
31 config.rcc.pll = Some(Pll {
32 src: PllSource::HSE,
33 prediv: PllPreDiv::DIV1,
34 mul: PllMul::MUL9,
35 });
36 config.rcc.sys = Sysclk::PLL1_P;
37 config.rcc.ahb_pre = AHBPrescaler::DIV1;
38 config.rcc.apb1_pre = APBPrescaler::DIV2;
39 config.rcc.apb2_pre = APBPrescaler::DIV1;
40 }
27 let mut p = embassy_stm32::init(config); 41 let mut p = embassy_stm32::init(config);
28 42
29 info!("Hello World!"); 43 info!("Hello World!");
diff --git a/examples/stm32f3/src/bin/hello.rs b/examples/stm32f3/src/bin/hello.rs
index fd54da53d..3c295612c 100644
--- a/examples/stm32f3/src/bin/hello.rs
+++ b/examples/stm32f3/src/bin/hello.rs
@@ -3,16 +3,13 @@
3 3
4use defmt::info; 4use defmt::info;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::time::Hertz;
7use embassy_stm32::Config; 6use embassy_stm32::Config;
8use embassy_time::Timer; 7use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
10 9
11#[embassy_executor::main] 10#[embassy_executor::main]
12async fn main(_spawner: Spawner) -> ! { 11async fn main(_spawner: Spawner) -> ! {
13 let mut config = Config::default(); 12 let config = Config::default();
14 config.rcc.hse = Some(Hertz(8_000_000));
15 config.rcc.sysclk = Some(Hertz(16_000_000));
16 let _p = embassy_stm32::init(config); 13 let _p = embassy_stm32::init(config);
17 14
18 loop { 15 loop {
diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs
index cf9ecedfa..ee1c43afd 100644
--- a/examples/stm32f3/src/bin/usb_serial.rs
+++ b/examples/stm32f3/src/bin/usb_serial.rs
@@ -21,11 +21,22 @@ bind_interrupts!(struct Irqs {
21#[embassy_executor::main] 21#[embassy_executor::main]
22async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
23 let mut config = Config::default(); 23 let mut config = Config::default();
24 config.rcc.hse = Some(mhz(8)); 24 {
25 config.rcc.sysclk = Some(mhz(48)); 25 use embassy_stm32::rcc::*;
26 config.rcc.pclk1 = Some(mhz(24)); 26 config.rcc.hse = Some(Hse {
27 config.rcc.pclk2 = Some(mhz(24)); 27 freq: mhz(8),
28 config.rcc.pll48 = true; 28 mode: HseMode::Bypass,
29 });
30 config.rcc.pll = Some(Pll {
31 src: PllSource::HSE,
32 prediv: PllPreDiv::DIV1,
33 mul: PllMul::MUL9,
34 });
35 config.rcc.sys = Sysclk::PLL1_P;
36 config.rcc.ahb_pre = AHBPrescaler::DIV1;
37 config.rcc.apb1_pre = APBPrescaler::DIV2;
38 config.rcc.apb2_pre = APBPrescaler::DIV1;
39 }
29 let p = embassy_stm32::init(config); 40 let p = embassy_stm32::init(config);
30 41
31 info!("Hello World!"); 42 info!("Hello World!");
diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs
index 063ee9dac..a9fb7f1a6 100644
--- a/examples/stm32f334/src/bin/adc.rs
+++ b/examples/stm32f334/src/bin/adc.rs
@@ -5,7 +5,6 @@ use defmt::info;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, SampleTime}; 6use embassy_stm32::adc::{Adc, SampleTime};
7use embassy_stm32::peripherals::ADC1; 7use embassy_stm32::peripherals::ADC1;
8use embassy_stm32::rcc::{AdcClockSource, Adcpres};
9use embassy_stm32::time::mhz; 8use embassy_stm32::time::mhz;
10use embassy_stm32::{adc, bind_interrupts, Config}; 9use embassy_stm32::{adc, bind_interrupts, Config};
11use embassy_time::{Delay, Timer}; 10use embassy_time::{Delay, Timer};
@@ -18,12 +17,23 @@ bind_interrupts!(struct Irqs {
18#[embassy_executor::main] 17#[embassy_executor::main]
19async fn main(_spawner: Spawner) -> ! { 18async fn main(_spawner: Spawner) -> ! {
20 let mut config = Config::default(); 19 let mut config = Config::default();
21 config.rcc.sysclk = Some(mhz(64)); 20 {
22 config.rcc.hclk = Some(mhz(64)); 21 use embassy_stm32::rcc::*;
23 config.rcc.pclk1 = Some(mhz(32)); 22 config.rcc.hse = Some(Hse {
24 config.rcc.pclk2 = Some(mhz(64)); 23 freq: mhz(8),
25 config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1)); 24 mode: HseMode::Bypass,
26 25 });
26 config.rcc.pll = Some(Pll {
27 src: PllSource::HSE,
28 prediv: PllPreDiv::DIV1,
29 mul: PllMul::MUL9,
30 });
31 config.rcc.sys = Sysclk::PLL1_P;
32 config.rcc.ahb_pre = AHBPrescaler::DIV1;
33 config.rcc.apb1_pre = APBPrescaler::DIV2;
34 config.rcc.apb2_pre = APBPrescaler::DIV1;
35 config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);
36 }
27 let mut p = embassy_stm32::init(config); 37 let mut p = embassy_stm32::init(config);
28 38
29 info!("create adc..."); 39 info!("create adc...");
diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs
index fd54da53d..3c295612c 100644
--- a/examples/stm32f334/src/bin/hello.rs
+++ b/examples/stm32f334/src/bin/hello.rs
@@ -3,16 +3,13 @@
3 3
4use defmt::info; 4use defmt::info;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::time::Hertz;
7use embassy_stm32::Config; 6use embassy_stm32::Config;
8use embassy_time::Timer; 7use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
10 9
11#[embassy_executor::main] 10#[embassy_executor::main]
12async fn main(_spawner: Spawner) -> ! { 11async fn main(_spawner: Spawner) -> ! {
13 let mut config = Config::default(); 12 let config = Config::default();
14 config.rcc.hse = Some(Hertz(8_000_000));
15 config.rcc.sysclk = Some(Hertz(16_000_000));
16 let _p = embassy_stm32::init(config); 13 let _p = embassy_stm32::init(config);
17 14
18 loop { 15 loop {
diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs
index 850a0e335..6f25191be 100644
--- a/examples/stm32f334/src/bin/opamp.rs
+++ b/examples/stm32f334/src/bin/opamp.rs
@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, SampleTime}; 6use embassy_stm32::adc::{Adc, SampleTime};
7use embassy_stm32::opamp::{OpAmp, OpAmpGain}; 7use embassy_stm32::opamp::{OpAmp, OpAmpGain};
8use embassy_stm32::peripherals::ADC2; 8use embassy_stm32::peripherals::ADC2;
9use embassy_stm32::rcc::{AdcClockSource, Adcpres};
10use embassy_stm32::time::mhz; 9use embassy_stm32::time::mhz;
11use embassy_stm32::{adc, bind_interrupts, Config}; 10use embassy_stm32::{adc, bind_interrupts, Config};
12use embassy_time::{Delay, Timer}; 11use embassy_time::{Delay, Timer};
@@ -19,12 +18,23 @@ bind_interrupts!(struct Irqs {
19#[embassy_executor::main] 18#[embassy_executor::main]
20async fn main(_spawner: Spawner) -> ! { 19async fn main(_spawner: Spawner) -> ! {
21 let mut config = Config::default(); 20 let mut config = Config::default();
22 config.rcc.sysclk = Some(mhz(64)); 21 {
23 config.rcc.hclk = Some(mhz(64)); 22 use embassy_stm32::rcc::*;
24 config.rcc.pclk1 = Some(mhz(32)); 23 config.rcc.hse = Some(Hse {
25 config.rcc.pclk2 = Some(mhz(64)); 24 freq: mhz(8),
26 config.rcc.adc = Some(AdcClockSource::Pll(Adcpres::DIV1)); 25 mode: HseMode::Bypass,
27 26 });
27 config.rcc.pll = Some(Pll {
28 src: PllSource::HSE,
29 prediv: PllPreDiv::DIV1,
30 mul: PllMul::MUL9,
31 });
32 config.rcc.sys = Sysclk::PLL1_P;
33 config.rcc.ahb_pre = AHBPrescaler::DIV1;
34 config.rcc.apb1_pre = APBPrescaler::DIV2;
35 config.rcc.apb2_pre = APBPrescaler::DIV1;
36 config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);
37 }
28 let mut p = embassy_stm32::init(config); 38 let mut p = embassy_stm32::init(config);
29 39
30 info!("create adc..."); 40 info!("create adc...");
diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs
index c149cad92..7fc1ea926 100644
--- a/examples/stm32f334/src/bin/pwm.rs
+++ b/examples/stm32f334/src/bin/pwm.rs
@@ -4,7 +4,6 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::hrtim::*; 6use embassy_stm32::hrtim::*;
7use embassy_stm32::rcc::HrtimClockSource;
8use embassy_stm32::time::{khz, mhz}; 7use embassy_stm32::time::{khz, mhz};
9use embassy_stm32::Config; 8use embassy_stm32::Config;
10use embassy_time::Timer; 9use embassy_time::Timer;
@@ -12,14 +11,26 @@ use {defmt_rtt as _, panic_probe as _};
12 11
13#[embassy_executor::main] 12#[embassy_executor::main]
14async fn main(_spawner: Spawner) { 13async fn main(_spawner: Spawner) {
15 let mut config: Config = Default::default(); 14 let mut config = Config::default();
16 config.rcc.sysclk = Some(mhz(64)); 15 {
17 config.rcc.hclk = Some(mhz(64)); 16 use embassy_stm32::rcc::*;
18 config.rcc.pclk1 = Some(mhz(32)); 17 config.rcc.hse = Some(Hse {
19 config.rcc.pclk2 = Some(mhz(64)); 18 freq: mhz(8),
20 config.rcc.hrtim = HrtimClockSource::PllClk; 19 mode: HseMode::Bypass,
21 20 });
21 config.rcc.pll = Some(Pll {
22 src: PllSource::HSE,
23 prediv: PllPreDiv::DIV1,
24 mul: PllMul::MUL9,
25 });
26 config.rcc.sys = Sysclk::PLL1_P;
27 config.rcc.ahb_pre = AHBPrescaler::DIV1;
28 config.rcc.apb1_pre = APBPrescaler::DIV2;
29 config.rcc.apb2_pre = APBPrescaler::DIV1;
30 config.rcc.hrtim = HrtimClockSource::PllClk;
31 }
22 let p = embassy_stm32::init(config); 32 let p = embassy_stm32::init(config);
33
23 info!("Hello World!"); 34 info!("Hello World!");
24 35
25 let ch1 = PwmPin::new_cha(p.PA8); 36 let ch1 = PwmPin::new_cha(p.PA8);
diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml
index 9088eea6e..086da2d78 100644
--- a/examples/stm32f7/.cargo/config.toml
+++ b/examples/stm32f7/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip STM32F767ZITx" 3runner = "probe-rs run --chip STM32F777ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 941ba38cd..736e81723 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -5,8 +5,8 @@ version = "0.1.0"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32f767zi to your chip name, if necessary. 8# Change stm32f777zi to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
@@ -28,6 +28,8 @@ rand_core = "0.6.3"
28critical-section = "1.1" 28critical-section = "1.1"
29embedded-storage = "0.3.1" 29embedded-storage = "0.3.1"
30static_cell = "2" 30static_cell = "2"
31sha2 = { version = "0.10.8", default-features = false }
32hmac = "0.12.1"
31 33
32[profile.release] 34[profile.release]
33debug = 2 35debug = 2
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index 5bff48197..9a608e909 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
19 19
20bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
21 ETH => eth::InterruptHandler; 21 ETH => eth::InterruptHandler;
22 RNG => rng::InterruptHandler<peripherals::RNG>; 22 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
23}); 23});
24 24
25type Device = Ethernet<'static, ETH, GenericSMI>; 25type Device = Ethernet<'static, ETH, GenericSMI>;
diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs
new file mode 100644
index 000000000..c2d1a7158
--- /dev/null
+++ b/examples/stm32f7/src/bin/hash.rs
@@ -0,0 +1,78 @@
1#![no_std]
2#![no_main]
3
4use defmt::info;
5use embassy_executor::Spawner;
6use embassy_stm32::hash::*;
7use embassy_stm32::{bind_interrupts, hash, peripherals, Config};
8use embassy_time::Instant;
9use hmac::{Hmac, Mac};
10use sha2::{Digest, Sha256};
11use {defmt_rtt as _, panic_probe as _};
12
13type HmacSha256 = Hmac<Sha256>;
14
15bind_interrupts!(struct Irqs {
16 HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) -> ! {
21 let config = Config::default();
22 let p = embassy_stm32::init(config);
23
24 let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
25 let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
26
27 let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs);
28
29 let hw_start_time = Instant::now();
30
31 // Compute a digest in hardware.
32 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);
33 hw_hasher.update(&mut context, test_1).await;
34 hw_hasher.update(&mut context, test_2).await;
35 let mut hw_digest: [u8; 32] = [0; 32];
36 hw_hasher.finish(context, &mut hw_digest).await;
37
38 let hw_end_time = Instant::now();
39 let hw_execution_time = hw_end_time - hw_start_time;
40
41 let sw_start_time = Instant::now();
42
43 // Compute a digest in software.
44 let mut sw_hasher = Sha256::new();
45 sw_hasher.update(test_1);
46 sw_hasher.update(test_2);
47 let sw_digest = sw_hasher.finalize();
48
49 let sw_end_time = Instant::now();
50 let sw_execution_time = sw_end_time - sw_start_time;
51
52 info!("Hardware Digest: {:?}", hw_digest);
53 info!("Software Digest: {:?}", sw_digest[..]);
54 info!("Hardware Execution Time: {:?}", hw_execution_time);
55 info!("Software Execution Time: {:?}", sw_execution_time);
56 assert_eq!(hw_digest, sw_digest[..]);
57
58 let hmac_key: [u8; 64] = [0x55; 64];
59
60 // Compute HMAC in hardware.
61 let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));
62 hw_hasher.update(&mut sha256hmac_context, test_1).await;
63 hw_hasher.update(&mut sha256hmac_context, test_2).await;
64 let mut hw_hmac: [u8; 32] = [0; 32];
65 hw_hasher.finish(sha256hmac_context, &mut hw_hmac).await;
66
67 // Compute HMAC in software.
68 let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();
69 sw_mac.update(test_1);
70 sw_mac.update(test_2);
71 let sw_hmac = sw_mac.finalize().into_bytes();
72
73 info!("Hardware HMAC: {:?}", hw_hmac);
74 info!("Software HMAC: {:?}", sw_hmac[..]);
75 assert_eq!(hw_hmac, sw_hmac[..]);
76
77 loop {}
78}
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs
index 35324d931..99e3ef63b 100644
--- a/examples/stm32g4/src/bin/adc.rs
+++ b/examples/stm32g4/src/bin/adc.rs
@@ -4,7 +4,7 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::adc::{Adc, SampleTime}; 6use embassy_stm32::adc::{Adc, SampleTime};
7use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSource}; 7use embassy_stm32::rcc::{AdcClockSource, Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk};
8use embassy_stm32::Config; 8use embassy_stm32::Config;
9use embassy_time::{Delay, Timer}; 9use embassy_time::{Delay, Timer};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
@@ -14,17 +14,17 @@ async fn main(_spawner: Spawner) {
14 let mut config = Config::default(); 14 let mut config = Config::default();
15 15
16 config.rcc.pll = Some(Pll { 16 config.rcc.pll = Some(Pll {
17 source: PllSource::HSI, 17 source: Pllsrc::HSI,
18 prediv_m: PllM::DIV4, 18 prediv: PllPreDiv::DIV4,
19 mul_n: PllN::MUL85, 19 mul: PllMul::MUL85,
20 div_p: None, 20 divp: None,
21 div_q: None, 21 divq: None,
22 // Main system clock at 170 MHz 22 // Main system clock at 170 MHz
23 div_r: Some(PllR::DIV2), 23 divr: Some(PllRDiv::DIV2),
24 }); 24 });
25 25
26 config.rcc.adc12_clock_source = AdcClockSource::SYS; 26 config.rcc.adc12_clock_source = AdcClockSource::SYS;
27 config.rcc.mux = ClockSrc::PLL; 27 config.rcc.sys = Sysclk::PLL1_R;
28 28
29 let mut p = embassy_stm32::init(config); 29 let mut p = embassy_stm32::init(config);
30 info!("Hello World!"); 30 info!("Hello World!");
diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs
index 46ebe0b0d..5274de79d 100644
--- a/examples/stm32g4/src/bin/pll.rs
+++ b/examples/stm32g4/src/bin/pll.rs
@@ -3,7 +3,7 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSource}; 6use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk};
7use embassy_stm32::Config; 7use embassy_stm32::Config;
8use embassy_time::Timer; 8use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
@@ -13,16 +13,16 @@ async fn main(_spawner: Spawner) {
13 let mut config = Config::default(); 13 let mut config = Config::default();
14 14
15 config.rcc.pll = Some(Pll { 15 config.rcc.pll = Some(Pll {
16 source: PllSource::HSI, 16 source: Pllsrc::HSI,
17 prediv_m: PllM::DIV4, 17 prediv: PllPreDiv::DIV4,
18 mul_n: PllN::MUL85, 18 mul: PllMul::MUL85,
19 div_p: None, 19 divp: None,
20 div_q: None, 20 divq: None,
21 // Main system clock at 170 MHz 21 // Main system clock at 170 MHz
22 div_r: Some(PllR::DIV2), 22 divr: Some(PllRDiv::DIV2),
23 }); 23 });
24 24
25 config.rcc.mux = ClockSrc::PLL; 25 config.rcc.sys = Sysclk::PLL1_R;
26 26
27 let _p = embassy_stm32::init(config); 27 let _p = embassy_stm32::init(config);
28 info!("Hello World!"); 28 info!("Hello World!");
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs
index c26fa76b7..989fef5b0 100644
--- a/examples/stm32g4/src/bin/usb_serial.rs
+++ b/examples/stm32g4/src/bin/usb_serial.rs
@@ -3,7 +3,9 @@
3 3
4use defmt::{panic, *}; 4use defmt::{panic, *};
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSource}; 6use embassy_stm32::rcc::{
7 Clk48Src, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk,
8};
7use embassy_stm32::time::Hertz; 9use embassy_stm32::time::Hertz;
8use embassy_stm32::usb::{self, Driver, Instance}; 10use embassy_stm32::usb::{self, Driver, Instance};
9use embassy_stm32::{bind_interrupts, peripherals, Config}; 11use embassy_stm32::{bind_interrupts, peripherals, Config};
@@ -24,25 +26,32 @@ async fn main(_spawner: Spawner) {
24 // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. 26 // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
25 const USE_HSI48: bool = true; 27 const USE_HSI48: bool = true;
26 28
27 let plldivq = if USE_HSI48 { None } else { Some(PllQ::DIV6) }; 29 let plldivq = if USE_HSI48 { None } else { Some(PllQDiv::DIV6) };
30
31 config.rcc.hse = Some(Hse {
32 freq: Hertz(8_000_000),
33 mode: HseMode::Oscillator,
34 });
28 35
29 config.rcc.pll = Some(Pll { 36 config.rcc.pll = Some(Pll {
30 source: PllSource::HSE(Hertz(8_000_000)), 37 source: Pllsrc::HSE,
31 prediv_m: PllM::DIV2, 38 prediv: PllPreDiv::DIV2,
32 mul_n: PllN::MUL72, 39 mul: PllMul::MUL72,
33 div_p: None, 40 divp: None,
34 div_q: plldivq, 41 divq: plldivq,
35 // Main system clock at 144 MHz 42 // Main system clock at 144 MHz
36 div_r: Some(PllR::DIV2), 43 divr: Some(PllRDiv::DIV2),
37 }); 44 });
38 45
39 config.rcc.mux = ClockSrc::PLL; 46 config.rcc.sys = Sysclk::PLL1_R;
47 config.rcc.boost = true; // BOOST!
40 48
41 if USE_HSI48 { 49 if USE_HSI48 {
42 // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. 50 // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
43 config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); 51 config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });
52 config.rcc.clk48_src = Clk48Src::HSI48;
44 } else { 53 } else {
45 config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); 54 config.rcc.clk48_src = Clk48Src::PLL1_Q;
46 } 55 }
47 56
48 let p = embassy_stm32::init(config); 57 let p = embassy_stm32::init(config);
diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml
index 8e32b4cee..528bd3451 100644
--- a/tests/stm32/.cargo/config.toml
+++ b/tests/stm32/.cargo/config.toml
@@ -14,9 +14,9 @@ rustflags = [
14] 14]
15 15
16[build] 16[build]
17target = "thumbv6m-none-eabi" 17#target = "thumbv6m-none-eabi"
18#target = "thumbv7m-none-eabi" 18#target = "thumbv7m-none-eabi"
19#target = "thumbv7em-none-eabi" 19target = "thumbv7em-none-eabi"
20#target = "thumbv8m.main-none-eabihf" 20#target = "thumbv8m.main-none-eabihf"
21 21
22[env] 22[env]
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index cb1bd9a50..8554682a4 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -15,22 +15,25 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma"
15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] 15stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] 16stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"]
17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] 17stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"]
18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] 18stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"]
19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"] 19stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash"]
20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"] 20stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash"]
21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] 21stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] 22stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"]
23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] 23stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"]
24stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] 24stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
25stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"] 25stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"]
26stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] 26stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
27stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"] 27stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
28stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"] 28stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"]
29stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] 29stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
30stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] 30stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
31stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"] 31stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
32stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] 32stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
33stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"]
34stm32h503rb = ["embassy-stm32/stm32h503rb", "rng"]
33 35
36hash = []
34eth = ["embassy-executor/task-arena-size-16384"] 37eth = ["embassy-executor/task-arena-size-16384"]
35rng = [] 38rng = []
36sdmmc = [] 39sdmmc = []
@@ -74,6 +77,8 @@ static_cell = "2"
74portable-atomic = { version = "1.5", features = [] } 77portable-atomic = { version = "1.5", features = [] }
75 78
76chrono = { version = "^0.4", default-features = false, optional = true} 79chrono = { version = "^0.4", default-features = false, optional = true}
80sha2 = { version = "0.10.8", default-features = false }
81hmac = "0.12.1"
77 82
78# BEGIN TESTS 83# BEGIN TESTS
79# Generated by gen_test.py. DO NOT EDIT. 84# Generated by gen_test.py. DO NOT EDIT.
@@ -108,6 +113,11 @@ path = "src/bin/gpio.rs"
108required-features = [] 113required-features = []
109 114
110[[bin]] 115[[bin]]
116name = "hash"
117path = "src/bin/hash.rs"
118required-features = [ "hash",]
119
120[[bin]]
111name = "rng" 121name = "rng"
112path = "src/bin/rng.rs" 122path = "src/bin/rng.rs"
113required-features = [ "rng",] 123required-features = [ "rng",]
diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs
index f32a7b2f8..bc5589164 100644
--- a/tests/stm32/build.rs
+++ b/tests/stm32/build.rs
@@ -16,6 +16,8 @@ fn main() -> Result<(), Box<dyn Error>> {
16 feature = "stm32l073rz", 16 feature = "stm32l073rz",
17 // wrong ram size in stm32-data 17 // wrong ram size in stm32-data
18 feature = "stm32wl55jc", 18 feature = "stm32wl55jc",
19 // no VTOR, so interrupts can't work when running from RAM
20 feature = "stm32f091rc",
19 )) { 21 )) {
20 println!("cargo:rustc-link-arg-bins=-Tlink.x"); 22 println!("cargo:rustc-link-arg-bins=-Tlink.x");
21 println!("cargo:rerun-if-changed=link.x"); 23 println!("cargo:rerun-if-changed=link.x");
diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs
new file mode 100644
index 000000000..8cc5d593f
--- /dev/null
+++ b/tests/stm32/src/bin/hash.rs
@@ -0,0 +1,101 @@
1// required-features: hash
2#![no_std]
3#![no_main]
4
5#[path = "../common.rs"]
6mod common;
7use common::*;
8use embassy_executor::Spawner;
9use embassy_stm32::dma::NoDma;
10use embassy_stm32::hash::*;
11use embassy_stm32::{bind_interrupts, hash, peripherals};
12use hmac::{Hmac, Mac};
13use sha2::{Digest, Sha224, Sha256};
14use {defmt_rtt as _, panic_probe as _};
15
16type HmacSha256 = Hmac<Sha256>;
17
18#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))]
19bind_interrupts!(struct Irqs {
20 HASH_RNG => hash::InterruptHandler<peripherals::HASH>;
21});
22
23#[cfg(any(
24 feature = "stm32wba52cg",
25 feature = "stm32l552ze",
26 feature = "stm32h563zi",
27 feature = "stm32h503rb",
28 feature = "stm32u5a5zj",
29 feature = "stm32u585ai"
30))]
31bind_interrupts!(struct Irqs {
32 HASH => hash::InterruptHandler<peripherals::HASH>;
33});
34
35#[embassy_executor::main]
36async fn main(_spawner: Spawner) {
37 let p: embassy_stm32::Peripherals = embassy_stm32::init(config());
38 let mut hw_hasher = Hash::new(p.HASH, NoDma, Irqs);
39
40 let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh";
41 let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr";
42 let test_3: &[u8] = b"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk";
43
44 // Start an SHA-256 digest.
45 let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);
46 hw_hasher.update_blocking(&mut sha256context, test_1);
47
48 // Interrupt the SHA-256 digest to compute an SHA-224 digest.
49 let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8, None);
50 hw_hasher.update_blocking(&mut sha224context, test_3);
51 let mut sha224_digest_buffer: [u8; 28] = [0; 28];
52 let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer);
53
54 // Finish the SHA-256 digest.
55 hw_hasher.update_blocking(&mut sha256context, test_2);
56 let mut sha256_digest_buffer: [u8; 32] = [0; 32];
57 let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer);
58
59 // Compute the SHA-256 digest in software.
60 let mut sw_sha256_hasher = Sha256::new();
61 sw_sha256_hasher.update(test_1);
62 sw_sha256_hasher.update(test_2);
63 let sw_sha256_digest = sw_sha256_hasher.finalize();
64
65 //Compute the SHA-224 digest in software.
66 let mut sw_sha224_hasher = Sha224::new();
67 sw_sha224_hasher.update(test_3);
68 let sw_sha224_digest = sw_sha224_hasher.finalize();
69
70 // Compare the SHA-256 digests.
71 info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer);
72 info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]);
73 defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]);
74
75 // Compare the SHA-224 digests.
76 info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer);
77 info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]);
78 defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]);
79
80 let hmac_key: [u8; 64] = [0x55; 64];
81
82 // Compute HMAC in hardware.
83 let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));
84 hw_hasher.update_blocking(&mut sha256hmac_context, test_1);
85 hw_hasher.update_blocking(&mut sha256hmac_context, test_2);
86 let mut hw_hmac: [u8; 32] = [0; 32];
87 hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac);
88
89 // Compute HMAC in software.
90 let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();
91 sw_mac.update(test_1);
92 sw_mac.update(test_2);
93 let sw_hmac = sw_mac.finalize().into_bytes();
94
95 info!("Hardware HMAC: {:?}", hw_hmac);
96 info!("Software HMAC: {:?}", sw_hmac[..]);
97 defmt::assert!(hw_hmac == sw_hmac[..]);
98
99 info!("Test OK");
100 cortex_m::asm::bkpt();
101}
diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs
index fefe72c86..50a7f9bae 100644
--- a/tests/stm32/src/common.rs
+++ b/tests/stm32/src/common.rs
@@ -54,6 +54,10 @@ teleprobe_meta::target!(b"nucleo-stm32l496zg");
54teleprobe_meta::target!(b"nucleo-stm32wl55jc"); 54teleprobe_meta::target!(b"nucleo-stm32wl55jc");
55#[cfg(feature = "stm32wba52cg")] 55#[cfg(feature = "stm32wba52cg")]
56teleprobe_meta::target!(b"nucleo-stm32wba52cg"); 56teleprobe_meta::target!(b"nucleo-stm32wba52cg");
57#[cfg(feature = "stm32f091rc")]
58teleprobe_meta::target!(b"nucleo-stm32f091rc");
59#[cfg(feature = "stm32h503rb")]
60teleprobe_meta::target!(b"nucleo-stm32h503rb");
57 61
58macro_rules! define_peris { 62macro_rules! define_peris {
59 ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => { 63 ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {
@@ -85,6 +89,12 @@ macro_rules! define_peris {
85 }; 89 };
86} 90}
87 91
92#[cfg(feature = "stm32f091rc")]
93define_peris!(
94 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
95 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,
96 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
97);
88#[cfg(feature = "stm32f103c8")] 98#[cfg(feature = "stm32f103c8")]
89define_peris!( 99define_peris!(
90 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, 100 UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,
@@ -157,6 +167,12 @@ define_peris!(
157 SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, 167 SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,
158 @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;}, 168 @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;},
159); 169);
170#[cfg(feature = "stm32h503rb")]
171define_peris!(
172 UART = USART1, UART_TX = PB14, UART_RX = PB15, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,
173 SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,
174 @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;},
175);
160#[cfg(feature = "stm32c031c6")] 176#[cfg(feature = "stm32c031c6")]
161define_peris!( 177define_peris!(
162 UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, 178 UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,
@@ -247,6 +263,39 @@ pub fn config() -> Config {
247 config.rcc = embassy_stm32::rcc::WPAN_DEFAULT; 263 config.rcc = embassy_stm32::rcc::WPAN_DEFAULT;
248 } 264 }
249 265
266 #[cfg(feature = "stm32f091rc")]
267 {
268 use embassy_stm32::rcc::*;
269 config.rcc.hse = Some(Hse {
270 freq: Hertz(8_000_000),
271 mode: HseMode::Bypass,
272 });
273 config.rcc.pll = Some(Pll {
274 src: PllSource::HSE,
275 prediv: PllPreDiv::DIV1,
276 mul: PllMul::MUL6,
277 });
278 config.rcc.sys = Sysclk::PLL1_P;
279 config.rcc.ahb_pre = AHBPrescaler::DIV1;
280 config.rcc.apb1_pre = APBPrescaler::DIV1;
281 }
282 #[cfg(feature = "stm32f103c8")]
283 {
284 use embassy_stm32::rcc::*;
285 config.rcc.hse = Some(Hse {
286 freq: Hertz(8_000_000),
287 mode: HseMode::Oscillator,
288 });
289 config.rcc.pll = Some(Pll {
290 src: PllSource::HSE,
291 prediv: PllPreDiv::DIV1,
292 mul: PllMul::MUL9,
293 });
294 config.rcc.sys = Sysclk::PLL1_P;
295 config.rcc.ahb_pre = AHBPrescaler::DIV1;
296 config.rcc.apb1_pre = APBPrescaler::DIV2;
297 config.rcc.apb2_pre = APBPrescaler::DIV1;
298 }
250 #[cfg(feature = "stm32f207zg")] 299 #[cfg(feature = "stm32f207zg")]
251 { 300 {
252 use embassy_stm32::rcc::*; 301 use embassy_stm32::rcc::*;
@@ -276,6 +325,24 @@ pub fn config() -> Config {
276 config.rcc.apb2_pre = APBPrescaler::DIV2; 325 config.rcc.apb2_pre = APBPrescaler::DIV2;
277 } 326 }
278 327
328 #[cfg(feature = "stm32f303ze")]
329 {
330 use embassy_stm32::rcc::*;
331 config.rcc.hse = Some(Hse {
332 freq: Hertz(8_000_000),
333 mode: HseMode::Bypass,
334 });
335 config.rcc.pll = Some(Pll {
336 src: PllSource::HSE,
337 prediv: PllPreDiv::DIV1,
338 mul: PllMul::MUL9,
339 });
340 config.rcc.sys = Sysclk::PLL1_P;
341 config.rcc.ahb_pre = AHBPrescaler::DIV1;
342 config.rcc.apb1_pre = APBPrescaler::DIV2;
343 config.rcc.apb2_pre = APBPrescaler::DIV1;
344 }
345
279 #[cfg(feature = "stm32f429zi")] 346 #[cfg(feature = "stm32f429zi")]
280 { 347 {
281 use embassy_stm32::rcc::*; 348 use embassy_stm32::rcc::*;
@@ -364,6 +431,31 @@ pub fn config() -> Config {
364 config.rcc.voltage_scale = VoltageScale::Scale0; 431 config.rcc.voltage_scale = VoltageScale::Scale0;
365 } 432 }
366 433
434 #[cfg(feature = "stm32h503rb")]
435 {
436 use embassy_stm32::rcc::*;
437 config.rcc.hsi = None;
438 config.rcc.hsi48 = Some(Default::default()); // needed for RNG
439 config.rcc.hse = Some(Hse {
440 freq: Hertz(24_000_000),
441 mode: HseMode::Oscillator,
442 });
443 config.rcc.pll1 = Some(Pll {
444 source: PllSource::HSE,
445 prediv: PllPreDiv::DIV6,
446 mul: PllMul::MUL125,
447 divp: Some(PllDiv::DIV2),
448 divq: Some(PllDiv::DIV2),
449 divr: None,
450 });
451 config.rcc.ahb_pre = AHBPrescaler::DIV1;
452 config.rcc.apb1_pre = APBPrescaler::DIV1;
453 config.rcc.apb2_pre = APBPrescaler::DIV1;
454 config.rcc.apb3_pre = APBPrescaler::DIV1;
455 config.rcc.sys = Sysclk::PLL1_P;
456 config.rcc.voltage_scale = VoltageScale::Scale0;
457 }
458
367 #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))] 459 #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))]
368 { 460 {
369 use embassy_stm32::rcc::*; 461 use embassy_stm32::rcc::*;