aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-boot/boot/src/boot_loader.rs43
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs34
-rw-r--r--embassy-boot/boot/src/lib.rs159
-rw-r--r--embassy-boot/boot/src/mem_flash.rs156
-rw-r--r--embassy-boot/boot/src/partition.rs18
-rw-r--r--embassy-stm32/build.rs17
-rw-r--r--embassy-stm32/src/rcc/f4.rs166
-rw-r--r--embassy-stm32/src/rcc/l4.rs133
-rw-r--r--examples/stm32f4/src/bin/mco.rs30
-rw-r--r--examples/stm32l4/src/bin/mco.rs27
10 files changed, 618 insertions, 165 deletions
diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs
index e2e361e3c..9d047f778 100644
--- a/embassy-boot/boot/src/boot_loader.rs
+++ b/embassy-boot/boot/src/boot_loader.rs
@@ -31,7 +31,7 @@ where
31} 31}
32 32
33/// Extension of the embedded-storage flash type information with block size and erase value. 33/// Extension of the embedded-storage flash type information with block size and erase value.
34pub trait Flash: NorFlash + ReadNorFlash { 34pub trait Flash: NorFlash {
35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase 35 /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase
36 /// size of the flash, but for external QSPI flash modules, this can be lower. 36 /// size of the flash, but for external QSPI flash modules, this can be lower.
37 const BLOCK_SIZE: usize; 37 const BLOCK_SIZE: usize;
@@ -60,9 +60,11 @@ pub trait FlashConfig {
60/// different page sizes and flash write sizes. 60/// different page sizes and flash write sizes.
61pub struct BootLoader { 61pub struct BootLoader {
62 // Page with current state of bootloader. The state partition has the following format: 62 // Page with current state of bootloader. The state partition has the following format:
63 // | Range | Description | 63 // All ranges are in multiples of WRITE_SIZE bytes.
64 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 64 // | Range | Description |
65 // | WRITE_SIZE - N | Progress index used while swapping or reverting | 65 // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
66 // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. |
67 // | 2..2 + N | Progress index used while swapping or reverting |
66 state: Partition, 68 state: Partition,
67 // Location of the partition which will be booted from 69 // Location of the partition which will be booted from
68 active: Partition, 70 active: Partition,
@@ -192,12 +194,17 @@ impl BootLoader {
192 trace!("Reverting"); 194 trace!("Reverting");
193 self.revert(p, magic, page)?; 195 self.revert(p, magic, page)?;
194 196
195 // Overwrite magic and reset progress
196 let state_flash = p.state(); 197 let state_flash = p.state();
198
199 // Invalidate progress
197 magic.fill(!P::STATE::ERASE_VALUE); 200 magic.fill(!P::STATE::ERASE_VALUE);
198 self.state.write_blocking(state_flash, 0, magic)?; 201 self.state
202 .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?;
203
204 // Clear magic and progress
199 self.state.wipe_blocking(state_flash)?; 205 self.state.wipe_blocking(state_flash)?;
200 206
207 // Set magic
201 magic.fill(BOOT_MAGIC); 208 magic.fill(BOOT_MAGIC);
202 self.state.write_blocking(state_flash, 0, magic)?; 209 self.state.write_blocking(state_flash, 0, magic)?;
203 } 210 }
@@ -215,28 +222,34 @@ impl BootLoader {
215 222
216 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> { 223 fn current_progress<P: FlashConfig>(&mut self, config: &mut P, aligned: &mut [u8]) -> Result<usize, BootError> {
217 let write_size = aligned.len(); 224 let write_size = aligned.len();
218 let max_index = ((self.state.len() - write_size) / write_size) - 1; 225 let max_index = ((self.state.len() - write_size) / write_size) - 2;
219 aligned.fill(!P::STATE::ERASE_VALUE); 226 aligned.fill(!P::STATE::ERASE_VALUE);
220 227
221 let state_flash = config.state(); 228 let state_flash = config.state();
222 for i in 0..max_index { 229
230 self.state
231 .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?;
232 if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) {
233 // Progress is invalid
234 return Ok(max_index);
235 }
236
237 for index in 0..max_index {
223 self.state 238 self.state
224 .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; 239 .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
225 240
226 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { 241 if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) {
227 return Ok(i); 242 return Ok(index);
228 } 243 }
229 } 244 }
230 Ok(max_index) 245 Ok(max_index)
231 } 246 }
232 247
233 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { 248 fn update_progress<P: FlashConfig>(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> {
234 let write_size = magic.len();
235
236 let aligned = magic; 249 let aligned = magic;
237 aligned.fill(!P::STATE::ERASE_VALUE); 250 aligned.fill(!P::STATE::ERASE_VALUE);
238 self.state 251 self.state
239 .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; 252 .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?;
240 Ok(()) 253 Ok(())
241 } 254 }
242 255
@@ -360,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s
360 assert_eq!(active.len() % page_size, 0); 373 assert_eq!(active.len() % page_size, 0);
361 assert_eq!(dfu.len() % page_size, 0); 374 assert_eq!(dfu.len() % page_size, 0);
362 assert!(dfu.len() - active.len() >= page_size); 375 assert!(dfu.len() - active.len() >= page_size);
363 assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); 376 assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size);
364} 377}
365 378
366/// A flash wrapper implementing the Flash and embedded_storage traits. 379/// A flash wrapper implementing the Flash and embedded_storage traits.
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
index dd22b2fa7..2b5cc72fa 100644
--- a/embassy-boot/boot/src/firmware_updater.rs
+++ b/embassy-boot/boot/src/firmware_updater.rs
@@ -220,11 +220,24 @@ impl FirmwareUpdater {
220 self.state.read(state_flash, 0, aligned).await?; 220 self.state.read(state_flash, 0, aligned).await?;
221 221
222 if aligned.iter().any(|&b| b != magic) { 222 if aligned.iter().any(|&b| b != magic) {
223 aligned.fill(0); 223 // Read progress validity
224 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
225
226 // FIXME: Do not make this assumption.
227 const STATE_ERASE_VALUE: u8 = 0xFF;
228
229 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
230 // The current progress validity marker is invalid
231 } else {
232 // Invalidate progress
233 aligned.fill(!STATE_ERASE_VALUE);
234 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
235 }
224 236
225 self.state.write(state_flash, 0, aligned).await?; 237 // Clear magic and progress
226 self.state.wipe(state_flash).await?; 238 self.state.wipe(state_flash).await?;
227 239
240 // Set magic
228 aligned.fill(magic); 241 aligned.fill(magic);
229 self.state.write(state_flash, 0, aligned).await?; 242 self.state.write(state_flash, 0, aligned).await?;
230 } 243 }
@@ -417,11 +430,24 @@ impl FirmwareUpdater {
417 self.state.read_blocking(state_flash, 0, aligned)?; 430 self.state.read_blocking(state_flash, 0, aligned)?;
418 431
419 if aligned.iter().any(|&b| b != magic) { 432 if aligned.iter().any(|&b| b != magic) {
420 aligned.fill(0); 433 // Read progress validity
434 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
435
436 // FIXME: Do not make this assumption.
437 const STATE_ERASE_VALUE: u8 = 0xFF;
438
439 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
440 // The current progress validity marker is invalid
441 } else {
442 // Invalidate progress
443 aligned.fill(!STATE_ERASE_VALUE);
444 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
445 }
421 446
422 self.state.write_blocking(state_flash, 0, aligned)?; 447 // Clear magic and progress
423 self.state.wipe_blocking(state_flash)?; 448 self.state.wipe_blocking(state_flash)?;
424 449
450 // Set magic
425 aligned.fill(magic); 451 aligned.fill(magic);
426 self.state.write_blocking(state_flash, 0, aligned)?; 452 self.state.write_blocking(state_flash, 0, aligned)?;
427 } 453 }
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 428e7ca2b..d53c613a3 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -7,6 +7,7 @@ mod fmt;
7 7
8mod boot_loader; 8mod boot_loader;
9mod firmware_updater; 9mod firmware_updater;
10mod mem_flash;
10mod partition; 11mod partition;
11 12
12pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; 13pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
@@ -44,13 +45,10 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
44 45
45#[cfg(test)] 46#[cfg(test)]
46mod tests { 47mod tests {
47 use core::convert::Infallible;
48
49 use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
50 use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
51 use futures::executor::block_on; 48 use futures::executor::block_on;
52 49
53 use super::*; 50 use super::*;
51 use crate::mem_flash::MemFlash;
54 52
55 /* 53 /*
56 #[test] 54 #[test]
@@ -73,8 +71,8 @@ mod tests {
73 const ACTIVE: Partition = Partition::new(4096, 61440); 71 const ACTIVE: Partition = Partition::new(4096, 61440);
74 const DFU: Partition = Partition::new(61440, 122880); 72 const DFU: Partition = Partition::new(61440, 122880);
75 73
76 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 74 let mut flash = MemFlash::<131072, 4096, 4>::default();
77 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); 75 flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
78 let mut flash = SingleFlashConfig::new(&mut flash); 76 let mut flash = SingleFlashConfig::new(&mut flash);
79 77
80 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 78 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -93,14 +91,14 @@ mod tests {
93 const STATE: Partition = Partition::new(0, 4096); 91 const STATE: Partition = Partition::new(0, 4096);
94 const ACTIVE: Partition = Partition::new(4096, 61440); 92 const ACTIVE: Partition = Partition::new(4096, 61440);
95 const DFU: Partition = Partition::new(61440, 122880); 93 const DFU: Partition = Partition::new(61440, 122880);
96 let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); 94 let mut flash = MemFlash::<131072, 4096, 4>::random();
97 95
98 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 96 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
99 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 97 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
100 let mut aligned = [0; 4]; 98 let mut aligned = [0; 4];
101 99
102 for i in ACTIVE.from..ACTIVE.to { 100 for i in ACTIVE.from..ACTIVE.to {
103 flash.0[i] = original[i - ACTIVE.from]; 101 flash.mem[i] = original[i - ACTIVE.from];
104 } 102 }
105 103
106 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); 104 let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE);
@@ -122,12 +120,12 @@ mod tests {
122 ); 120 );
123 121
124 for i in ACTIVE.from..ACTIVE.to { 122 for i in ACTIVE.from..ACTIVE.to {
125 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 123 assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i);
126 } 124 }
127 125
128 // First DFU page is untouched 126 // First DFU page is untouched
129 for i in DFU.from + 4096..DFU.to { 127 for i in DFU.from + 4096..DFU.to {
130 assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); 128 assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i);
131 } 129 }
132 130
133 // Running again should cause a revert 131 // Running again should cause a revert
@@ -139,12 +137,12 @@ mod tests {
139 ); 137 );
140 138
141 for i in ACTIVE.from..ACTIVE.to { 139 for i in ACTIVE.from..ACTIVE.to {
142 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 140 assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i);
143 } 141 }
144 142
145 // Last page is untouched 143 // Last page is untouched
146 for i in DFU.from..DFU.to - 4096 { 144 for i in DFU.from..DFU.to - 4096 {
147 assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); 145 assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i);
148 } 146 }
149 147
150 // Mark as booted 148 // Mark as booted
@@ -164,16 +162,16 @@ mod tests {
164 const ACTIVE: Partition = Partition::new(4096, 16384); 162 const ACTIVE: Partition = Partition::new(4096, 16384);
165 const DFU: Partition = Partition::new(0, 16384); 163 const DFU: Partition = Partition::new(0, 16384);
166 164
167 let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); 165 let mut active = MemFlash::<16384, 4096, 8>::random();
168 let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); 166 let mut dfu = MemFlash::<16384, 2048, 8>::random();
169 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 167 let mut state = MemFlash::<4096, 128, 4>::random();
170 let mut aligned = [0; 4]; 168 let mut aligned = [0; 4];
171 169
172 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 170 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
173 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 171 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
174 172
175 for i in ACTIVE.from..ACTIVE.to { 173 for i in ACTIVE.from..ACTIVE.to {
176 active.0[i] = original[i - ACTIVE.from]; 174 active.mem[i] = original[i - ACTIVE.from];
177 } 175 }
178 176
179 let mut updater = FirmwareUpdater::new(DFU, STATE); 177 let mut updater = FirmwareUpdater::new(DFU, STATE);
@@ -201,12 +199,12 @@ mod tests {
201 ); 199 );
202 200
203 for i in ACTIVE.from..ACTIVE.to { 201 for i in ACTIVE.from..ACTIVE.to {
204 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 202 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
205 } 203 }
206 204
207 // First DFU page is untouched 205 // First DFU page is untouched
208 for i in DFU.from + 4096..DFU.to { 206 for i in DFU.from + 4096..DFU.to {
209 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 207 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
210 } 208 }
211 } 209 }
212 210
@@ -218,15 +216,15 @@ mod tests {
218 const DFU: Partition = Partition::new(0, 16384); 216 const DFU: Partition = Partition::new(0, 16384);
219 217
220 let mut aligned = [0; 4]; 218 let mut aligned = [0; 4];
221 let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); 219 let mut active = MemFlash::<16384, 2048, 4>::random();
222 let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); 220 let mut dfu = MemFlash::<16384, 4096, 8>::random();
223 let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); 221 let mut state = MemFlash::<4096, 128, 4>::random();
224 222
225 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()]; 223 let original: [u8; ACTIVE.len()] = [rand::random::<u8>(); ACTIVE.len()];
226 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()]; 224 let update: [u8; DFU.len()] = [rand::random::<u8>(); DFU.len()];
227 225
228 for i in ACTIVE.from..ACTIVE.to { 226 for i in ACTIVE.from..ACTIVE.to {
229 active.0[i] = original[i - ACTIVE.from]; 227 active.mem[i] = original[i - ACTIVE.from];
230 } 228 }
231 229
232 let mut updater = FirmwareUpdater::new(DFU, STATE); 230 let mut updater = FirmwareUpdater::new(DFU, STATE);
@@ -253,12 +251,12 @@ mod tests {
253 ); 251 );
254 252
255 for i in ACTIVE.from..ACTIVE.to { 253 for i in ACTIVE.from..ACTIVE.to {
256 assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); 254 assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i);
257 } 255 }
258 256
259 // First DFU page is untouched 257 // First DFU page is untouched
260 for i in DFU.from + 4096..DFU.to { 258 for i in DFU.from + 4096..DFU.to {
261 assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); 259 assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i);
262 } 260 }
263 } 261 }
264 262
@@ -288,13 +286,13 @@ mod tests {
288 286
289 const STATE: Partition = Partition::new(0, 4096); 287 const STATE: Partition = Partition::new(0, 4096);
290 const DFU: Partition = Partition::new(4096, 8192); 288 const DFU: Partition = Partition::new(4096, 8192);
291 let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); 289 let mut flash = MemFlash::<8192, 4096, 4>::default();
292 290
293 let firmware_len = firmware.len(); 291 let firmware_len = firmware.len();
294 292
295 let mut write_buf = [0; 4096]; 293 let mut write_buf = [0; 4096];
296 write_buf[0..firmware_len].copy_from_slice(firmware); 294 write_buf[0..firmware_len].copy_from_slice(firmware);
297 NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); 295 DFU.write_blocking(&mut flash, 0, &write_buf).unwrap();
298 296
299 // On with the test 297 // On with the test
300 298
@@ -311,113 +309,4 @@ mod tests {
311 )) 309 ))
312 .is_ok()); 310 .is_ok());
313 } 311 }
314
315 pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>(pub [u8; SIZE]);
316
317 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
318 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
319 {
320 const WRITE_SIZE: usize = WRITE_SIZE;
321 const ERASE_SIZE: usize = ERASE_SIZE;
322 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
323 let from = from as usize;
324 let to = to as usize;
325 assert!(from % ERASE_SIZE == 0);
326 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
327 for i in from..to {
328 self.0[i] = 0xFF;
329 }
330 Ok(())
331 }
332
333 fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
334 assert!(data.len() % WRITE_SIZE == 0);
335 assert!(offset as usize % WRITE_SIZE == 0);
336 assert!(offset as usize + data.len() <= SIZE);
337
338 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
339
340 Ok(())
341 }
342 }
343
344 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
345 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
346 {
347 type Error = Infallible;
348 }
349
350 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
351 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
352 {
353 const READ_SIZE: usize = 1;
354
355 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
356 let len = buf.len();
357 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
358 Ok(())
359 }
360
361 fn capacity(&self) -> usize {
362 SIZE
363 }
364 }
365
366 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> super::Flash
367 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
368 {
369 const BLOCK_SIZE: usize = ERASE_SIZE;
370 const ERASE_VALUE: u8 = 0xFF;
371 }
372
373 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
374 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
375 {
376 const READ_SIZE: usize = 1;
377
378 async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
379 let len = buf.len();
380 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
381 Ok(())
382 }
383
384 fn capacity(&self) -> usize {
385 SIZE
386 }
387 }
388
389 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
390 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
391 {
392 const WRITE_SIZE: usize = WRITE_SIZE;
393 const ERASE_SIZE: usize = ERASE_SIZE;
394
395 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
396 let from = from as usize;
397 let to = to as usize;
398 assert!(from % ERASE_SIZE == 0);
399 assert!(to % ERASE_SIZE == 0);
400 for i in from..to {
401 self.0[i] = 0xFF;
402 }
403 Ok(())
404 }
405
406 async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
407 info!("Writing {} bytes to 0x{:x}", data.len(), offset);
408 assert!(data.len() % WRITE_SIZE == 0);
409 assert!(offset as usize % WRITE_SIZE == 0);
410 assert!(
411 offset as usize + data.len() <= SIZE,
412 "OFFSET: {}, LEN: {}, FLASH SIZE: {}",
413 offset,
414 data.len(),
415 SIZE
416 );
417
418 self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data);
419
420 Ok(())
421 }
422 }
423} 312}
diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs
new file mode 100644
index 000000000..828aad9d9
--- /dev/null
+++ b/embassy-boot/boot/src/mem_flash.rs
@@ -0,0 +1,156 @@
1#![allow(unused)]
2
3use core::ops::{Bound, Range, RangeBounds};
4
5use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
6use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
7
8use crate::Flash;
9
10pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {
11 pub mem: [u8; SIZE],
12 pub pending_write_successes: Option<usize>,
13}
14
15#[derive(Debug)]
16pub struct MemFlashError;
17
18impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
19 pub const fn new(fill: u8) -> Self {
20 Self {
21 mem: [fill; SIZE],
22 pending_write_successes: None,
23 }
24 }
25
26 #[cfg(test)]
27 pub fn random() -> Self {
28 let mut mem = [0; SIZE];
29 for byte in mem.iter_mut() {
30 *byte = rand::random::<u8>();
31 }
32 Self {
33 mem,
34 pending_write_successes: None,
35 }
36 }
37}
38
39impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default
40 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
41{
42 fn default() -> Self {
43 Self::new(0xFF)
44 }
45}
46
47impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Flash
48 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
49{
50 const BLOCK_SIZE: usize = ERASE_SIZE;
51 const ERASE_VALUE: u8 = 0xFF;
52}
53
54impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
55 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
56{
57 type Error = MemFlashError;
58}
59
60impl NorFlashError for MemFlashError {
61 fn kind(&self) -> NorFlashErrorKind {
62 NorFlashErrorKind::Other
63 }
64}
65
66impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
67 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
68{
69 const READ_SIZE: usize = 1;
70
71 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
72 let len = bytes.len();
73 bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);
74 Ok(())
75 }
76
77 fn capacity(&self) -> usize {
78 SIZE
79 }
80}
81
82impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
83 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
84{
85 const WRITE_SIZE: usize = WRITE_SIZE;
86 const ERASE_SIZE: usize = ERASE_SIZE;
87
88 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
89 let from = from as usize;
90 let to = to as usize;
91 assert!(from % ERASE_SIZE == 0);
92 assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE);
93 for i in from..to {
94 self.mem[i] = 0xFF;
95 }
96 Ok(())
97 }
98
99 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
100 let offset = offset as usize;
101 assert!(bytes.len() % WRITE_SIZE == 0);
102 assert!(offset % WRITE_SIZE == 0);
103 assert!(offset + bytes.len() <= SIZE);
104
105 if let Some(pending_successes) = self.pending_write_successes {
106 if pending_successes > 0 {
107 self.pending_write_successes = Some(pending_successes - 1);
108 } else {
109 return Err(MemFlashError);
110 }
111 }
112
113 for ((offset, mem_byte), new_byte) in self
114 .mem
115 .iter_mut()
116 .enumerate()
117 .skip(offset)
118 .take(bytes.len())
119 .zip(bytes)
120 {
121 assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset);
122 *mem_byte = *new_byte;
123 }
124
125 Ok(())
126 }
127}
128
129impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash
130 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
131{
132 const READ_SIZE: usize = 1;
133
134 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
135 <Self as ReadNorFlash>::read(self, offset, bytes)
136 }
137
138 fn capacity(&self) -> usize {
139 <Self as ReadNorFlash>::capacity(self)
140 }
141}
142
143impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash
144 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
145{
146 const WRITE_SIZE: usize = WRITE_SIZE;
147 const ERASE_SIZE: usize = ERASE_SIZE;
148
149 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
150 <Self as NorFlash>::erase(self, from, to)
151 }
152
153 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
154 <Self as NorFlash>::write(self, offset, bytes)
155 }
156}
diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs
index 217c457fc..ac6b0ed0f 100644
--- a/embassy-boot/boot/src/partition.rs
+++ b/embassy-boot/boot/src/partition.rs
@@ -95,45 +95,45 @@ impl Partition {
95 95
96#[cfg(test)] 96#[cfg(test)]
97mod tests { 97mod tests {
98 use crate::tests::MemFlash; 98 use crate::mem_flash::MemFlash;
99 use crate::Partition; 99 use crate::Partition;
100 100
101 #[test] 101 #[test]
102 fn can_erase() { 102 fn can_erase() {
103 let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); 103 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
104 let partition = Partition::new(256, 512); 104 let partition = Partition::new(256, 512);
105 105
106 partition.erase_blocking(&mut flash, 64, 192).unwrap(); 106 partition.erase_blocking(&mut flash, 64, 192).unwrap();
107 107
108 for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { 108 for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) {
109 assert_eq!(0x00, byte, "Index {}", index); 109 assert_eq!(0x00, byte, "Index {}", index);
110 } 110 }
111 111
112 for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { 112 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) {
113 assert_eq!(0xFF, byte, "Index {}", index); 113 assert_eq!(0xFF, byte, "Index {}", index);
114 } 114 }
115 115
116 for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { 116 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) {
117 assert_eq!(0x00, byte, "Index {}", index); 117 assert_eq!(0x00, byte, "Index {}", index);
118 } 118 }
119 } 119 }
120 120
121 #[test] 121 #[test]
122 fn can_wipe() { 122 fn can_wipe() {
123 let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); 123 let mut flash = MemFlash::<1024, 64, 4>::new(0x00);
124 let partition = Partition::new(256, 512); 124 let partition = Partition::new(256, 512);
125 125
126 partition.wipe_blocking(&mut flash).unwrap(); 126 partition.wipe_blocking(&mut flash).unwrap();
127 127
128 for (index, byte) in flash.0.iter().copied().enumerate().take(256) { 128 for (index, byte) in flash.mem.iter().copied().enumerate().take(256) {
129 assert_eq!(0x00, byte, "Index {}", index); 129 assert_eq!(0x00, byte, "Index {}", index);
130 } 130 }
131 131
132 for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { 132 for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) {
133 assert_eq!(0xFF, byte, "Index {}", index); 133 assert_eq!(0xFF, byte, "Index {}", index);
134 } 134 }
135 135
136 for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { 136 for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) {
137 assert_eq!(0x00, byte, "Index {}", index); 137 assert_eq!(0x00, byte, "Index {}", index);
138 } 138 }
139 } 139 }
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 3780c5a40..481fec677 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -50,10 +50,13 @@ fn main() {
50 // We *shouldn't* have singletons for these, but the HAL currently requires 50 // We *shouldn't* have singletons for these, but the HAL currently requires
51 // singletons, for using with RccPeripheral to enable/disable clocks to them. 51 // singletons, for using with RccPeripheral to enable/disable clocks to them.
52 "rcc" => { 52 "rcc" => {
53 if r.version.starts_with("h7") { 53 if r.version.starts_with("h7") || r.version.starts_with("f4") {
54 singletons.push("MCO1".to_string()); 54 singletons.push("MCO1".to_string());
55 singletons.push("MCO2".to_string()); 55 singletons.push("MCO2".to_string());
56 } 56 }
57 if r.version.starts_with("l4") {
58 singletons.push("MCO".to_string());
59 }
57 singletons.push(p.name.to_string()); 60 singletons.push(p.name.to_string());
58 } 61 }
59 //"dbgmcu" => {} 62 //"dbgmcu" => {}
@@ -258,6 +261,7 @@ fn main() {
258 (("i2c", "SCL"), quote!(crate::i2c::SclPin)), 261 (("i2c", "SCL"), quote!(crate::i2c::SclPin)),
259 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), 262 (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)),
260 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)), 263 (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)),
264 (("rcc", "MCO"), quote!(crate::rcc::McoPin)),
261 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)), 265 (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)),
262 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)), 266 (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)),
263 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)), 267 (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)),
@@ -447,13 +451,22 @@ fn main() {
447 // MCO is special 451 // MCO is special
448 if pin.signal.starts_with("MCO_") { 452 if pin.signal.starts_with("MCO_") {
449 // Supported in H7 only for now 453 // Supported in H7 only for now
450 if regs.version.starts_with("h7") { 454 if regs.version.starts_with("h7") || regs.version.starts_with("f4") {
451 peri = format_ident!("{}", pin.signal.replace("_", "")); 455 peri = format_ident!("{}", pin.signal.replace("_", ""));
452 } else { 456 } else {
453 continue; 457 continue;
454 } 458 }
455 } 459 }
456 460
461 if pin.signal == "MCO" {
462 // Supported in H7 only for now
463 if regs.version.starts_with("l4") {
464 peri = format_ident!("MCO");
465 } else {
466 continue;
467 }
468 }
469
457 g.extend(quote! { 470 g.extend(quote! {
458 pin_trait_impl!(#tr, #peri, #pin_name, #af); 471 pin_trait_impl!(#tr, #peri, #pin_name, #af);
459 }) 472 })
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 200bcce9c..2a17eb9b0 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -1,8 +1,16 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
5
1use super::sealed::RccPeripheral; 6use super::sealed::RccPeripheral;
7use crate::gpio::sealed::AFType;
8use crate::gpio::Speed;
2use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; 9use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
3use crate::pac::{FLASH, PWR, RCC}; 10use crate::pac::{FLASH, PWR, RCC};
4use crate::rcc::{set_freqs, Clocks}; 11use crate::rcc::{set_freqs, Clocks};
5use crate::time::Hertz; 12use crate::time::Hertz;
13use crate::{peripherals, Peripheral};
6 14
7/// HSI speed 15/// HSI speed
8pub const HSI_FREQ: Hertz = Hertz(16_000_000); 16pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -96,6 +104,164 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
96 } 104 }
97} 105}
98 106
107pub enum McoClock {
108 DIV1,
109 DIV2,
110 DIV3,
111 DIV4,
112 DIV5,
113}
114
115impl McoClock {
116 fn into_raw(&self) -> Mcopre {
117 match self {
118 McoClock::DIV1 => Mcopre::DIV1,
119 McoClock::DIV2 => Mcopre::DIV2,
120 McoClock::DIV3 => Mcopre::DIV3,
121 McoClock::DIV4 => Mcopre::DIV4,
122 McoClock::DIV5 => Mcopre::DIV5,
123 }
124 }
125}
126
127#[derive(Copy, Clone)]
128pub enum Mco1Source {
129 Hsi,
130 Lse,
131 Hse,
132 Pll,
133}
134
135impl Default for Mco1Source {
136 fn default() -> Self {
137 Self::Hsi
138 }
139}
140
141pub trait McoSource {
142 type Raw;
143
144 fn into_raw(&self) -> Self::Raw;
145}
146
147impl McoSource for Mco1Source {
148 type Raw = Mco1;
149 fn into_raw(&self) -> Self::Raw {
150 match self {
151 Mco1Source::Hsi => Mco1::HSI,
152 Mco1Source::Lse => Mco1::LSE,
153 Mco1Source::Hse => Mco1::HSE,
154 Mco1Source::Pll => Mco1::PLL,
155 }
156 }
157}
158
159#[derive(Copy, Clone)]
160pub enum Mco2Source {
161 SysClk,
162 Plli2s,
163 Hse,
164 Pll,
165}
166
167impl Default for Mco2Source {
168 fn default() -> Self {
169 Self::SysClk
170 }
171}
172
173impl McoSource for Mco2Source {
174 type Raw = Mco2;
175 fn into_raw(&self) -> Self::Raw {
176 match self {
177 Mco2Source::SysClk => Mco2::SYSCLK,
178 Mco2Source::Plli2s => Mco2::PLLI2S,
179 Mco2Source::Hse => Mco2::HSE,
180 Mco2Source::Pll => Mco2::PLL,
181 }
182 }
183}
184
185pub(crate) mod sealed {
186 use stm32_metapac::rcc::vals::Mcopre;
187 pub trait McoInstance {
188 type Source;
189 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
190 }
191}
192
193pub trait McoInstance: sealed::McoInstance + 'static {}
194
195pin_trait!(McoPin, McoInstance);
196
197impl sealed::McoInstance for peripherals::MCO1 {
198 type Source = Mco1;
199 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
200 RCC.cfgr().modify(|w| {
201 w.set_mco1(source);
202 w.set_mco1pre(prescaler);
203 });
204 match source {
205 Mco1::PLL => {
206 RCC.cr().modify(|w| w.set_pllon(true));
207 while !RCC.cr().read().pllrdy() {}
208 }
209 Mco1::HSI => {
210 RCC.cr().modify(|w| w.set_hsion(true));
211 while !RCC.cr().read().hsirdy() {}
212 }
213 _ => {}
214 }
215 }
216}
217impl McoInstance for peripherals::MCO1 {}
218
219impl sealed::McoInstance for peripherals::MCO2 {
220 type Source = Mco2;
221 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
222 RCC.cfgr().modify(|w| {
223 w.set_mco2(source);
224 w.set_mco2pre(prescaler);
225 });
226 match source {
227 Mco2::PLL => {
228 RCC.cr().modify(|w| w.set_pllon(true));
229 while !RCC.cr().read().pllrdy() {}
230 }
231 #[cfg(not(stm32f410))]
232 Mco2::PLLI2S => {
233 RCC.cr().modify(|w| w.set_plli2son(true));
234 while !RCC.cr().read().plli2srdy() {}
235 }
236 _ => {}
237 }
238 }
239}
240impl McoInstance for peripherals::MCO2 {}
241
242pub struct Mco<'d, T: McoInstance> {
243 phantom: PhantomData<&'d mut T>,
244}
245
246impl<'d, T: McoInstance> Mco<'d, T> {
247 pub fn new(
248 _peri: impl Peripheral<P = T> + 'd,
249 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
250 source: impl McoSource<Raw = T::Source>,
251 prescaler: McoClock,
252 ) -> Self {
253 into_ref!(pin);
254
255 critical_section::with(|_| unsafe {
256 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
257 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
258 pin.set_speed(Speed::VeryHigh);
259 });
260
261 Self { phantom: PhantomData }
262 }
263}
264
99unsafe fn flash_setup(sysclk: u32) { 265unsafe fn flash_setup(sysclk: u32) {
100 use crate::pac::flash::vals::Latency; 266 use crate::pac::flash::vals::Latency;
101 267
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs
index e650490fe..c1bf7d0cd 100644
--- a/embassy-stm32/src/rcc/l4.rs
+++ b/embassy-stm32/src/rcc/l4.rs
@@ -1,7 +1,15 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
5
6use crate::gpio::sealed::AFType;
7use crate::gpio::Speed;
1use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; 8use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
2use crate::pac::{FLASH, RCC}; 9use crate::pac::{FLASH, RCC};
3use crate::rcc::{set_freqs, Clocks}; 10use crate::rcc::{set_freqs, Clocks};
4use crate::time::Hertz; 11use crate::time::Hertz;
12use crate::{peripherals, Peripheral};
5 13
6/// HSI speed 14/// HSI speed
7pub const HSI_FREQ: Hertz = Hertz(16_000_000); 15pub const HSI_FREQ: Hertz = Hertz(16_000_000);
@@ -298,6 +306,131 @@ impl Default for Config {
298 } 306 }
299} 307}
300 308
309pub enum McoClock {
310 DIV1,
311 DIV2,
312 DIV4,
313 DIV8,
314 DIV16,
315}
316
317impl McoClock {
318 fn into_raw(&self) -> Mcopre {
319 match self {
320 McoClock::DIV1 => Mcopre::DIV1,
321 McoClock::DIV2 => Mcopre::DIV2,
322 McoClock::DIV4 => Mcopre::DIV4,
323 McoClock::DIV8 => Mcopre::DIV8,
324 McoClock::DIV16 => Mcopre::DIV16,
325 }
326 }
327}
328
329#[derive(Copy, Clone)]
330pub enum Mco1Source {
331 Disabled,
332 Lse,
333 Lsi,
334 Hse,
335 Hsi16,
336 PllClk,
337 SysClk,
338 Msi,
339 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
340 Hsi48,
341}
342
343impl Default for Mco1Source {
344 fn default() -> Self {
345 Self::Hsi16
346 }
347}
348
349pub trait McoSource {
350 type Raw;
351
352 fn into_raw(&self) -> Self::Raw;
353}
354
355impl McoSource for Mco1Source {
356 type Raw = Mcosel;
357 fn into_raw(&self) -> Self::Raw {
358 match self {
359 Mco1Source::Disabled => Mcosel::NOCLOCK,
360 Mco1Source::Lse => Mcosel::LSE,
361 Mco1Source::Lsi => Mcosel::LSI,
362 Mco1Source::Hse => Mcosel::HSE,
363 Mco1Source::Hsi16 => Mcosel::HSI16,
364 Mco1Source::PllClk => Mcosel::PLL,
365 Mco1Source::SysClk => Mcosel::SYSCLK,
366 Mco1Source::Msi => Mcosel::MSI,
367 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
368 Mco1Source::Hsi48 => Mcosel::HSI48,
369 }
370 }
371}
372
373pub(crate) mod sealed {
374 use stm32_metapac::rcc::vals::Mcopre;
375 pub trait McoInstance {
376 type Source;
377 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre);
378 }
379}
380
381pub trait McoInstance: sealed::McoInstance + 'static {}
382
383pin_trait!(McoPin, McoInstance);
384
385impl sealed::McoInstance for peripherals::MCO {
386 type Source = Mcosel;
387
388 unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) {
389 RCC.cfgr().modify(|w| {
390 w.set_mcosel(source);
391 w.set_mcopre(prescaler);
392 });
393
394 match source {
395 Mcosel::HSI16 => {
396 RCC.cr().modify(|w| w.set_hsion(true));
397 while !RCC.cr().read().hsirdy() {}
398 }
399 #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
400 Mcosel::HSI48 => {
401 RCC.crrcr().modify(|w| w.set_hsi48on(true));
402 while !RCC.crrcr().read().hsi48rdy() {}
403 }
404 _ => {}
405 }
406 }
407}
408
409impl McoInstance for peripherals::MCO {}
410
411pub struct Mco<'d, T: McoInstance> {
412 phantom: PhantomData<&'d mut T>,
413}
414
415impl<'d, T: McoInstance> Mco<'d, T> {
416 pub fn new(
417 _peri: impl Peripheral<P = T> + 'd,
418 pin: impl Peripheral<P = impl McoPin<T>> + 'd,
419 source: impl McoSource<Raw = T::Source>,
420 prescaler: McoClock,
421 ) -> Self {
422 into_ref!(pin);
423
424 critical_section::with(|_| unsafe {
425 T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
426 pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
427 pin.set_speed(Speed::VeryHigh);
428 });
429
430 Self { phantom: PhantomData }
431 }
432}
433
301pub(crate) unsafe fn init(config: Config) { 434pub(crate) unsafe fn init(config: Config) {
302 let (sys_clk, sw) = match config.mux { 435 let (sys_clk, sw) = match config.mux {
303 ClockSrc::MSI(range) => { 436 ClockSrc::MSI(range) => {
diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs
new file mode 100644
index 000000000..2b9ceebc3
--- /dev/null
+++ b/examples/stm32f4/src/bin/mco.rs
@@ -0,0 +1,30 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1);
18 let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4);
19 let mut led = Output::new(p.PB7, Level::High, Speed::Low);
20
21 loop {
22 info!("high");
23 led.set_high();
24 Timer::after(Duration::from_millis(300)).await;
25
26 info!("low");
27 led.set_low();
28 Timer::after(Duration::from_millis(300)).await;
29 }
30}
diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs
new file mode 100644
index 000000000..dea0c66e0
--- /dev/null
+++ b/examples/stm32l4/src/bin/mco.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::rcc::{Mco, Mco1Source, McoClock};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_stm32::init(Default::default());
15 info!("Hello World!");
16
17 let _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1);
18
19 let mut led = Output::new(p.PB14, Level::High, Speed::Low);
20
21 loop {
22 led.set_high();
23 Timer::after(Duration::from_millis(300)).await;
24 led.set_low();
25 Timer::after(Duration::from_millis(300)).await;
26 }
27}