aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-embedded-hal/Cargo.toml10
-rw-r--r--embassy-embedded-hal/src/adapter/blocking_async.rs (renamed from embassy-embedded-hal/src/adapter.rs)2
-rw-r--r--embassy-embedded-hal/src/adapter/mod.rs7
-rw-r--r--embassy-embedded-hal/src/adapter/yielding_async.rs232
-rw-r--r--embassy-embedded-hal/src/flash.rs286
-rw-r--r--embassy-embedded-hal/src/lib.rs2
-rw-r--r--embassy-stm32/build.rs1
-rw-r--r--embassy-stm32/src/flash/common.rs19
-rw-r--r--embassy-stm32/src/flash/f0.rs2
-rw-r--r--embassy-stm32/src/flash/f3.rs2
-rw-r--r--embassy-stm32/src/flash/f4.rs84
-rw-r--r--embassy-stm32/src/flash/f7.rs2
-rw-r--r--embassy-stm32/src/flash/h7.rs2
-rw-r--r--embassy-stm32/src/flash/l.rs2
-rw-r--r--embassy-stm32/src/flash/mod.rs1
-rw-r--r--embassy-stm32/src/flash/other.rs2
16 files changed, 628 insertions, 28 deletions
diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml
index 19d512585..ad2f14568 100644
--- a/embassy-embedded-hal/Cargo.toml
+++ b/embassy-embedded-hal/Cargo.toml
@@ -14,11 +14,14 @@ target = "x86_64-unknown-linux-gnu"
14[features] 14[features]
15std = [] 15std = []
16# Enable nightly-only features 16# Enable nightly-only features
17nightly = ["embedded-hal-async", "embedded-storage-async"] 17nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
18 18
19[dependencies] 19[dependencies]
20embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
20embassy-sync = { version = "0.2.0", path = "../embassy-sync" } 21embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
21embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 22embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
23 "unproven",
24] }
22embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } 25embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
23embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } 26embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
24embedded-storage = "0.3.0" 27embedded-storage = "0.3.0"
@@ -26,3 +29,6 @@ embedded-storage-async = { version = "0.4.0", optional = true }
26nb = "1.0.0" 29nb = "1.0.0"
27 30
28defmt = { version = "0.3", optional = true } 31defmt = { version = "0.3", optional = true }
32
33[dev-dependencies]
34futures-test = "0.3.17"
diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs
index 171ff6c9f..b996d6a75 100644
--- a/embassy-embedded-hal/src/adapter.rs
+++ b/embassy-embedded-hal/src/adapter/blocking_async.rs
@@ -1,5 +1,3 @@
1//! Adapters between embedded-hal traits.
2
3use embedded_hal_02::{blocking, serial}; 1use embedded_hal_02::{blocking, serial};
4 2
5/// Wrapper that implements async traits using blocking implementations. 3/// Wrapper that implements async traits using blocking implementations.
diff --git a/embassy-embedded-hal/src/adapter/mod.rs b/embassy-embedded-hal/src/adapter/mod.rs
new file mode 100644
index 000000000..28da56137
--- /dev/null
+++ b/embassy-embedded-hal/src/adapter/mod.rs
@@ -0,0 +1,7 @@
1//! Adapters between embedded-hal traits.
2
3mod blocking_async;
4mod yielding_async;
5
6pub use blocking_async::BlockingAsync;
7pub use yielding_async::YieldingAsync;
diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs
new file mode 100644
index 000000000..96d5cca8e
--- /dev/null
+++ b/embassy-embedded-hal/src/adapter/yielding_async.rs
@@ -0,0 +1,232 @@
1use embassy_futures::yield_now;
2
3/// Wrapper that yields for each operation to the wrapped instance
4///
5/// This can be used in combination with BlockingAsync<T> to enforce yields
6/// between long running blocking operations.
7pub struct YieldingAsync<T> {
8 wrapped: T,
9}
10
11impl<T> YieldingAsync<T> {
12 /// Create a new instance of a wrapper that yields after each operation.
13 pub fn new(wrapped: T) -> Self {
14 Self { wrapped }
15 }
16}
17
18//
19// I2C implementations
20//
21impl<T> embedded_hal_1::i2c::ErrorType for YieldingAsync<T>
22where
23 T: embedded_hal_1::i2c::ErrorType,
24{
25 type Error = T::Error;
26}
27
28impl<T> embedded_hal_async::i2c::I2c for YieldingAsync<T>
29where
30 T: embedded_hal_async::i2c::I2c,
31{
32 async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
33 self.wrapped.read(address, read).await?;
34 yield_now().await;
35 Ok(())
36 }
37
38 async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
39 self.wrapped.write(address, write).await?;
40 yield_now().await;
41 Ok(())
42 }
43
44 async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
45 self.wrapped.write_read(address, write, read).await?;
46 yield_now().await;
47 Ok(())
48 }
49
50 async fn transaction(
51 &mut self,
52 address: u8,
53 operations: &mut [embedded_hal_1::i2c::Operation<'_>],
54 ) -> Result<(), Self::Error> {
55 self.wrapped.transaction(address, operations).await?;
56 yield_now().await;
57 Ok(())
58 }
59}
60
61//
62// SPI implementations
63//
64
65impl<T> embedded_hal_async::spi::ErrorType for YieldingAsync<T>
66where
67 T: embedded_hal_async::spi::ErrorType,
68{
69 type Error = T::Error;
70}
71
72impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
73where
74 T: embedded_hal_async::spi::SpiBus,
75{
76 async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
77 self.wrapped.transfer(read, write).await?;
78 yield_now().await;
79 Ok(())
80 }
81
82 async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
83 self.wrapped.transfer_in_place(words).await?;
84 yield_now().await;
85 Ok(())
86 }
87}
88
89impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
90where
91 T: embedded_hal_async::spi::SpiBusFlush,
92{
93 async fn flush(&mut self) -> Result<(), Self::Error> {
94 self.wrapped.flush().await?;
95 yield_now().await;
96 Ok(())
97 }
98}
99
100impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
101where
102 T: embedded_hal_async::spi::SpiBusWrite<u8>,
103{
104 async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
105 self.wrapped.write(data).await?;
106 yield_now().await;
107 Ok(())
108 }
109}
110
111impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
112where
113 T: embedded_hal_async::spi::SpiBusRead<u8>,
114{
115 async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
116 self.wrapped.read(data).await?;
117 yield_now().await;
118 Ok(())
119 }
120}
121
122///
123/// NOR flash implementations
124///
125impl<T: embedded_storage::nor_flash::ErrorType> embedded_storage::nor_flash::ErrorType for YieldingAsync<T> {
126 type Error = T::Error;
127}
128
129impl<T: embedded_storage_async::nor_flash::ReadNorFlash> embedded_storage_async::nor_flash::ReadNorFlash
130 for YieldingAsync<T>
131{
132 const READ_SIZE: usize = T::READ_SIZE;
133
134 async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
135 self.wrapped.read(offset, bytes).await?;
136 Ok(())
137 }
138
139 fn capacity(&self) -> usize {
140 self.wrapped.capacity()
141 }
142}
143
144impl<T: embedded_storage_async::nor_flash::NorFlash> embedded_storage_async::nor_flash::NorFlash for YieldingAsync<T> {
145 const WRITE_SIZE: usize = T::WRITE_SIZE;
146 const ERASE_SIZE: usize = T::ERASE_SIZE;
147
148 async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
149 self.wrapped.write(offset, bytes).await?;
150 yield_now().await;
151 Ok(())
152 }
153
154 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
155 // Yield between each actual erase
156 for from in (from..to).step_by(T::ERASE_SIZE) {
157 let to = core::cmp::min(from + T::ERASE_SIZE as u32, to);
158 self.wrapped.erase(from, to).await?;
159 yield_now().await;
160 }
161 Ok(())
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use embedded_storage_async::nor_flash::NorFlash;
168
169 use super::*;
170
171 extern crate std;
172
173 #[derive(Default)]
174 struct FakeFlash(Vec<(u32, u32)>);
175
176 impl embedded_storage::nor_flash::ErrorType for FakeFlash {
177 type Error = std::convert::Infallible;
178 }
179
180 impl embedded_storage_async::nor_flash::ReadNorFlash for FakeFlash {
181 const READ_SIZE: usize = 1;
182
183 async fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> {
184 unimplemented!()
185 }
186
187 fn capacity(&self) -> usize {
188 unimplemented!()
189 }
190 }
191
192 impl embedded_storage_async::nor_flash::NorFlash for FakeFlash {
193 const WRITE_SIZE: usize = 4;
194 const ERASE_SIZE: usize = 128;
195
196 async fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> {
197 unimplemented!()
198 }
199
200 async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
201 self.0.push((from, to));
202 Ok(())
203 }
204 }
205
206 #[futures_test::test]
207 async fn can_erase() {
208 let fake = FakeFlash::default();
209 let mut yielding = YieldingAsync::new(fake);
210
211 yielding.erase(0, 256).await.unwrap();
212
213 let fake = yielding.wrapped;
214 assert_eq!(2, fake.0.len());
215 assert_eq!((0, 128), fake.0[0]);
216 assert_eq!((128, 256), fake.0[1]);
217 }
218
219 #[futures_test::test]
220 async fn can_erase_wrong_erase_size() {
221 let fake = FakeFlash::default();
222 let mut yielding = YieldingAsync::new(fake);
223
224 yielding.erase(0, 257).await.unwrap();
225
226 let fake = yielding.wrapped;
227 assert_eq!(3, fake.0.len());
228 assert_eq!((0, 128), fake.0[0]);
229 assert_eq!((128, 256), fake.0[1]);
230 assert_eq!((256, 257), fake.0[2]);
231 }
232}
diff --git a/embassy-embedded-hal/src/flash.rs b/embassy-embedded-hal/src/flash.rs
new file mode 100644
index 000000000..9a6e4bd92
--- /dev/null
+++ b/embassy-embedded-hal/src/flash.rs
@@ -0,0 +1,286 @@
1//! Utilities related to flash.
2
3use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash};
4#[cfg(feature = "nightly")]
5use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
6
7/// Convenience helper for concatenating two consecutive flashes into one.
8/// This is especially useful if used with "flash regions", where one may
9/// want to concatenate multiple regions into one larger region.
10pub struct ConcatFlash<First, Second>(First, Second);
11
12impl<First, Second> ConcatFlash<First, Second> {
13 /// Create a new flash that concatenates two consecutive flashes.
14 pub fn new(first: First, second: Second) -> Self {
15 Self(first, second)
16 }
17}
18
19const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize {
20 if first_read_size != second_read_size {
21 panic!("The read size for the concatenated flashes must be the same");
22 }
23 first_read_size
24}
25
26const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize {
27 if first_write_size != second_write_size {
28 panic!("The write size for the concatenated flashes must be the same");
29 }
30 first_write_size
31}
32
33const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize {
34 let max_erase_size = if first_erase_size > second_erase_size {
35 first_erase_size
36 } else {
37 second_erase_size
38 };
39 if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 {
40 panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size");
41 }
42 max_erase_size
43}
44
45impl<First, Second, E> ErrorType for ConcatFlash<First, Second>
46where
47 First: ErrorType<Error = E>,
48 Second: ErrorType<Error = E>,
49 E: NorFlashError,
50{
51 type Error = E;
52}
53
54impl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second>
55where
56 First: ReadNorFlash<Error = E>,
57 Second: ReadNorFlash<Error = E>,
58 E: NorFlashError,
59{
60 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
61
62 fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
63 if offset < self.0.capacity() as u32 {
64 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
65 self.0.read(offset, &mut bytes[..len])?;
66 offset += len as u32;
67 bytes = &mut bytes[len..];
68 }
69
70 if !bytes.is_empty() {
71 self.1.read(offset - self.0.capacity() as u32, bytes)?;
72 }
73
74 Ok(())
75 }
76
77 fn capacity(&self) -> usize {
78 self.0.capacity() + self.1.capacity()
79 }
80}
81
82impl<First, Second, E> NorFlash for ConcatFlash<First, Second>
83where
84 First: NorFlash<Error = E>,
85 Second: NorFlash<Error = E>,
86 E: NorFlashError,
87{
88 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
89 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
90
91 fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
92 if offset < self.0.capacity() as u32 {
93 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
94 self.0.write(offset, &bytes[..len])?;
95 offset += len as u32;
96 bytes = &bytes[len..];
97 }
98
99 if !bytes.is_empty() {
100 self.1.write(offset - self.0.capacity() as u32, bytes)?;
101 }
102
103 Ok(())
104 }
105
106 fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
107 if from < self.0.capacity() as u32 {
108 let to = core::cmp::min(self.0.capacity() as u32, to);
109 self.0.erase(from, to)?;
110 from = self.0.capacity() as u32;
111 }
112
113 if from < to {
114 self.1
115 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?;
116 }
117
118 Ok(())
119 }
120}
121
122#[cfg(feature = "nightly")]
123impl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>
124where
125 First: AsyncReadNorFlash<Error = E>,
126 Second: AsyncReadNorFlash<Error = E>,
127 E: NorFlashError,
128{
129 const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);
130
131 async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {
132 if offset < self.0.capacity() as u32 {
133 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
134 self.0.read(offset, &mut bytes[..len]).await?;
135 offset += len as u32;
136 bytes = &mut bytes[len..];
137 }
138
139 if !bytes.is_empty() {
140 self.1.read(offset - self.0.capacity() as u32, bytes).await?;
141 }
142
143 Ok(())
144 }
145
146 fn capacity(&self) -> usize {
147 self.0.capacity() + self.1.capacity()
148 }
149}
150
151#[cfg(feature = "nightly")]
152impl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>
153where
154 First: AsyncNorFlash<Error = E>,
155 Second: AsyncNorFlash<Error = E>,
156 E: NorFlashError,
157{
158 const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);
159 const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);
160
161 async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {
162 if offset < self.0.capacity() as u32 {
163 let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());
164 self.0.write(offset, &bytes[..len]).await?;
165 offset += len as u32;
166 bytes = &bytes[len..];
167 }
168
169 if !bytes.is_empty() {
170 self.1.write(offset - self.0.capacity() as u32, bytes).await?;
171 }
172
173 Ok(())
174 }
175
176 async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {
177 if from < self.0.capacity() as u32 {
178 let to = core::cmp::min(self.0.capacity() as u32, to);
179 self.0.erase(from, to).await?;
180 from = self.0.capacity() as u32;
181 }
182
183 if from < to {
184 self.1
185 .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)
186 .await?;
187 }
188
189 Ok(())
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn can_write_and_read_across_flashes() {
199 let first = MemFlash::<64, 16, 4>::new();
200 let second = MemFlash::<64, 64, 4>::new();
201 let mut f = ConcatFlash::new(first, second);
202
203 f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap();
204
205 assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0 .0[60..]);
206 assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1 .0[0..4]);
207
208 let mut read_buf = [0; 8];
209 f.read(60, &mut read_buf).unwrap();
210
211 assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf);
212 }
213
214 #[test]
215 fn can_erase_across_flashes() {
216 let mut first = MemFlash::<128, 16, 4>::new();
217 let mut second = MemFlash::<128, 64, 4>::new();
218 first.0.fill(0x00);
219 second.0.fill(0x00);
220
221 let mut f = ConcatFlash::new(first, second);
222
223 f.erase(64, 192).unwrap();
224
225 assert_eq!(&[0x00; 64], &f.0 .0[0..64]);
226 assert_eq!(&[0xff; 64], &f.0 .0[64..128]);
227 assert_eq!(&[0xff; 64], &f.1 .0[0..64]);
228 assert_eq!(&[0x00; 64], &f.1 .0[64..128]);
229 }
230
231 pub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize>([u8; SIZE]);
232
233 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {
234 pub const fn new() -> Self {
235 Self([0xff; SIZE])
236 }
237 }
238
239 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType
240 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
241 {
242 type Error = core::convert::Infallible;
243 }
244
245 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash
246 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
247 {
248 const READ_SIZE: usize = 1;
249
250 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
251 let len = bytes.len();
252 bytes.copy_from_slice(&self.0[offset as usize..offset as usize + len]);
253 Ok(())
254 }
255
256 fn capacity(&self) -> usize {
257 SIZE
258 }
259 }
260
261 impl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash
262 for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>
263 {
264 const WRITE_SIZE: usize = WRITE_SIZE;
265 const ERASE_SIZE: usize = ERASE_SIZE;
266
267 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
268 let from = from as usize;
269 let to = to as usize;
270 assert_eq!(0, from % ERASE_SIZE);
271 assert_eq!(0, to % ERASE_SIZE);
272 self.0[from..to].fill(0xff);
273 Ok(())
274 }
275
276 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
277 let offset = offset as usize;
278 assert_eq!(0, bytes.len() % WRITE_SIZE);
279 assert_eq!(0, offset % WRITE_SIZE);
280 assert!(offset + bytes.len() <= SIZE);
281
282 self.0[offset..offset + bytes.len()].copy_from_slice(bytes);
283 Ok(())
284 }
285 }
286}
diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs
index 73c81b465..3aad838bd 100644
--- a/embassy-embedded-hal/src/lib.rs
+++ b/embassy-embedded-hal/src/lib.rs
@@ -7,6 +7,8 @@
7#[cfg(feature = "nightly")] 7#[cfg(feature = "nightly")]
8pub mod adapter; 8pub mod adapter;
9 9
10pub mod flash;
11
10pub mod shared_bus; 12pub mod shared_bus;
11 13
12/// Set the configuration of a peripheral driver. 14/// Set the configuration of a peripheral driver.
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index dcee535b5..540981727 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -206,6 +206,7 @@ fn main() {
206 erase_size: #erase_size, 206 erase_size: #erase_size,
207 write_size: #write_size, 207 write_size: #write_size,
208 erase_value: #erase_value, 208 erase_value: #erase_value,
209 _ensure_internal: (),
209 }; 210 };
210 }); 211 });
211 212
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs
index 6d7f55974..432c8a43b 100644
--- a/embassy-stm32/src/flash/common.rs
+++ b/embassy-stm32/src/flash/common.rs
@@ -17,6 +17,7 @@ impl<'d> Flash<'d> {
17 } 17 }
18 18
19 pub fn into_regions(self) -> FlashLayout<'d> { 19 pub fn into_regions(self) -> FlashLayout<'d> {
20 family::set_default_layout();
20 FlashLayout::new(self.release()) 21 FlashLayout::new(self.release())
21 } 22 }
22 23
@@ -25,11 +26,11 @@ impl<'d> Flash<'d> {
25 } 26 }
26 27
27 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { 28 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
28 unsafe { blocking_write(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } 29 unsafe { blocking_write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) }
29 } 30 }
30 31
31 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 32 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
32 unsafe { blocking_erase(FLASH_BASE as u32, from, to) } 33 unsafe { blocking_erase_sectored(FLASH_BASE as u32, from, to) }
33 } 34 }
34 35
35 pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { 36 pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> {
@@ -37,7 +38,7 @@ impl<'d> Flash<'d> {
37 } 38 }
38} 39}
39 40
40fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 41pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
41 if offset + bytes.len() as u32 > size { 42 if offset + bytes.len() as u32 > size {
42 return Err(Error::Size); 43 return Err(Error::Size);
43 } 44 }
@@ -48,7 +49,7 @@ fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<
48 Ok(()) 49 Ok(())
49} 50}
50 51
51unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { 52pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> {
52 if offset + bytes.len() as u32 > size { 53 if offset + bytes.len() as u32 > size {
53 return Err(Error::Size); 54 return Err(Error::Size);
54 } 55 }
@@ -81,7 +82,7 @@ unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Res
81 Ok(()) 82 Ok(())
82} 83}
83 84
84unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { 85pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> {
85 let start_address = base + from; 86 let start_address = base + from;
86 let end_address = base + to; 87 let end_address = base + to;
87 let regions = family::get_flash_regions(); 88 let regions = family::get_flash_regions();
@@ -154,11 +155,11 @@ impl FlashRegion {
154 } 155 }
155 156
156 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { 157 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
157 unsafe { blocking_write(self.base, self.size, offset, bytes) } 158 unsafe { blocking_write_chunked(self.base, self.size, offset, bytes) }
158 } 159 }
159 160
160 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 161 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
161 unsafe { blocking_erase(self.base, from, to) } 162 unsafe { blocking_erase_sectored(self.base, from, to) }
162 } 163 }
163} 164}
164 165
@@ -199,11 +200,11 @@ foreach_flash_region! {
199 } 200 }
200 201
201 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { 202 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
202 unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) } 203 unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) }
203 } 204 }
204 205
205 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 206 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
206 unsafe { blocking_erase(self.0.base, from, to) } 207 unsafe { blocking_erase_sectored(self.0.base, from, to) }
207 } 208 }
208 } 209 }
209 210
diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs
index 033afecd5..c6441ef16 100644
--- a/embassy-stm32/src/flash/f0.rs
+++ b/embassy-stm32/src/flash/f0.rs
@@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
7use crate::flash::Error; 7use crate::flash::Error;
8use crate::pac; 8use crate::pac;
9 9
10pub const fn set_default_layout() {}
11
10pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { 12pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
11 &FLASH_REGIONS 13 &FLASH_REGIONS
12} 14}
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs
index 10a09c42c..4c8172203 100644
--- a/embassy-stm32/src/flash/f3.rs
+++ b/embassy-stm32/src/flash/f3.rs
@@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
7use crate::flash::Error; 7use crate::flash::Error;
8use crate::pac; 8use crate::pac;
9 9
10pub const fn set_default_layout() {}
11
10pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { 12pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
11 &FLASH_REGIONS 13 &FLASH_REGIONS
12} 14}
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 60ac62c17..7f1c5f671 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -11,8 +11,11 @@ mod alt_regions {
11 use embassy_hal_common::PeripheralRef; 11 use embassy_hal_common::PeripheralRef;
12 use stm32_metapac::FLASH_SIZE; 12 use stm32_metapac::FLASH_SIZE;
13 13
14 use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; 14 use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION};
15 use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion}; 15 use crate::flash::{
16 blocking_erase_sectored, blocking_read, blocking_write_chunked, Bank1Region1, Bank1Region2, Error, Flash,
17 FlashBank, FlashRegion,
18 };
16 use crate::peripherals::FLASH; 19 use crate::peripherals::FLASH;
17 20
18 pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { 21 pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion {
@@ -45,20 +48,19 @@ mod alt_regions {
45 &ALT_BANK2_REGION3, 48 &ALT_BANK2_REGION3,
46 ]; 49 ];
47 50
48 pub type AltBank1Region1<'d> = Bank1Region1<'d>;
49 pub type AltBank1Region2<'d> = Bank1Region2<'d>;
50 pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); 51 pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
51 pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); 52 pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
52 pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); 53 pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
53 pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); 54 pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>);
54 55
55 pub struct AltFlashLayout<'d> { 56 pub struct AltFlashLayout<'d> {
56 pub bank1_region1: AltBank1Region1<'d>, 57 pub bank1_region1: Bank1Region1<'d>,
57 pub bank1_region2: AltBank1Region2<'d>, 58 pub bank1_region2: Bank1Region2<'d>,
58 pub bank1_region3: AltBank1Region3<'d>, 59 pub bank1_region3: AltBank1Region3<'d>,
59 pub bank2_region1: AltBank2Region1<'d>, 60 pub bank2_region1: AltBank2Region1<'d>,
60 pub bank2_region2: AltBank2Region2<'d>, 61 pub bank2_region2: AltBank2Region2<'d>,
61 pub bank2_region3: AltBank2Region3<'d>, 62 pub bank2_region3: AltBank2Region3<'d>,
63 pub otp_region: OTPRegion<'d>,
62 } 64 }
63 65
64 impl<'d> Flash<'d> { 66 impl<'d> Flash<'d> {
@@ -66,7 +68,7 @@ mod alt_regions {
66 unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; 68 unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) };
67 69
68 // SAFETY: We never expose the cloned peripheral references, and their instance is not public. 70 // SAFETY: We never expose the cloned peripheral references, and their instance is not public.
69 // Also, all flash region operations are protected with a cs. 71 // Also, all blocking flash region operations are protected with a cs.
70 let p = self.release(); 72 let p = self.release();
71 AltFlashLayout { 73 AltFlashLayout {
72 bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), 74 bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }),
@@ -75,22 +77,74 @@ mod alt_regions {
75 bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), 77 bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }),
76 bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), 78 bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }),
77 bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), 79 bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }),
80 otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }),
78 } 81 }
79 } 82 }
80 } 83 }
81 84
82 impl Drop for AltFlashLayout<'_> { 85 macro_rules! foreach_altflash_region {
83 fn drop(&mut self) { 86 ($type_name:ident, $region:ident) => {
84 unsafe { 87 impl $type_name<'_> {
85 super::lock(); 88 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
86 crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) 89 blocking_read(self.0.base, self.0.size, offset, bytes)
87 }; 90 }
88 } 91
92 pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
93 unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) }
94 }
95
96 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
97 unsafe { blocking_erase_sectored(self.0.base, from, to) }
98 }
99 }
100
101 impl embedded_storage::nor_flash::ErrorType for $type_name<'_> {
102 type Error = Error;
103 }
104
105 impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> {
106 const READ_SIZE: usize = 1;
107
108 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
109 self.blocking_read(offset, bytes)
110 }
111
112 fn capacity(&self) -> usize {
113 self.0.size as usize
114 }
115 }
116
117 impl embedded_storage::nor_flash::NorFlash for $type_name<'_> {
118 const WRITE_SIZE: usize = $region.write_size as usize;
119 const ERASE_SIZE: usize = $region.erase_size as usize;
120
121 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
122 self.blocking_write(offset, bytes)
123 }
124
125 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
126 self.blocking_erase(from, to)
127 }
128 }
129 };
89 } 130 }
131
132 foreach_altflash_region!(AltBank1Region3, ALT_BANK1_REGION3);
133 foreach_altflash_region!(AltBank2Region1, ALT_BANK2_REGION1);
134 foreach_altflash_region!(AltBank2Region2, ALT_BANK2_REGION2);
135 foreach_altflash_region!(AltBank2Region3, ALT_BANK2_REGION3);
90} 136}
91 137
92#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] 138#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
93pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; 139pub use alt_regions::*;
140
141#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
142pub fn set_default_layout() {
143 unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) };
144}
145
146#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
147pub const fn set_default_layout() {}
94 148
95#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] 149#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
96pub fn get_flash_regions() -> &'static [&'static FlashRegion] { 150pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs
index 6427d5a09..ac2834a84 100644
--- a/embassy-stm32/src/flash/f7.rs
+++ b/embassy-stm32/src/flash/f7.rs
@@ -6,6 +6,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
6use crate::flash::Error; 6use crate::flash::Error;
7use crate::pac; 7use crate::pac;
8 8
9pub const fn set_default_layout() {}
10
9pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { 11pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
10 &FLASH_REGIONS 12 &FLASH_REGIONS
11} 13}
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
index 4f38d50c0..1b1631068 100644
--- a/embassy-stm32/src/flash/h7.rs
+++ b/embassy-stm32/src/flash/h7.rs
@@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
7use crate::flash::Error; 7use crate::flash::Error;
8use crate::pac; 8use crate::pac;
9 9
10pub const fn set_default_layout() {}
11
10const fn is_dual_bank() -> bool { 12const fn is_dual_bank() -> bool {
11 FLASH_REGIONS.len() == 2 13 FLASH_REGIONS.len() == 2
12} 14}
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs
index 7d9cc6ea3..c94f61900 100644
--- a/embassy-stm32/src/flash/l.rs
+++ b/embassy-stm32/src/flash/l.rs
@@ -6,6 +6,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
6use crate::flash::Error; 6use crate::flash::Error;
7use crate::pac; 7use crate::pac;
8 8
9pub const fn set_default_layout() {}
10
9pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { 11pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
10 &FLASH_REGIONS 12 &FLASH_REGIONS
11} 13}
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index f6efa7753..b93270ae1 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -19,6 +19,7 @@ pub struct FlashRegion {
19 pub erase_size: u32, 19 pub erase_size: u32,
20 pub write_size: u32, 20 pub write_size: u32,
21 pub erase_value: u8, 21 pub erase_value: u8,
22 pub(crate) _ensure_internal: (),
22} 23}
23 24
24#[derive(Debug, PartialEq)] 25#[derive(Debug, PartialEq)]
diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs
index c151cb828..556034654 100644
--- a/embassy-stm32/src/flash/other.rs
+++ b/embassy-stm32/src/flash/other.rs
@@ -2,6 +2,8 @@
2 2
3use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; 3use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
4 4
5pub const fn set_default_layout() {}
6
5pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { 7pub const fn get_flash_regions() -> &'static [&'static FlashRegion] {
6 &FLASH_REGIONS 8 &FLASH_REGIONS
7} 9}