aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-05-06 20:18:26 +0000
committerGitHub <[email protected]>2022-05-06 20:18:26 +0000
commit7e774ff8300cb4df6c561f99f1c33485256155e6 (patch)
treefa6000199428777b38df5a47862fd3629d939a63
parentf7af9a549f165419f4fa85c82b42a3e5b54d417e (diff)
parent8a80ae56854dec4cc052fcc999d16df9f3f73876 (diff)
Merge #755
755: Add support for flash and bootloader for F3, F7 and H7 r=matoushybl a=matoushybl Co-authored-by: Matous Hybl <[email protected]>
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc2
-rw-r--r--embassy-boot/stm32/Cargo.toml2
-rw-r--r--embassy-boot/stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/flash/f3.rs104
-rw-r--r--embassy-stm32/src/flash/f7.rs138
-rw-r--r--embassy-stm32/src/flash/h7.rs202
-rw-r--r--embassy-stm32/src/flash/l.rs185
-rw-r--r--embassy-stm32/src/flash/mod.rs193
-rw-r--r--embassy-stm32/src/lib.rs4
-rw-r--r--examples/boot/stm32f3/.cargo/config.toml6
-rw-r--r--examples/boot/stm32f3/Cargo.toml26
-rw-r--r--examples/boot/stm32f3/README.md29
-rw-r--r--examples/boot/stm32f3/build.rs37
-rw-r--r--examples/boot/stm32f3/memory.x15
-rw-r--r--examples/boot/stm32f3/src/bin/a.rs44
-rw-r--r--examples/boot/stm32f3/src/bin/b.rs25
-rw-r--r--examples/boot/stm32f7/.cargo/config.toml6
-rw-r--r--examples/boot/stm32f7/Cargo.toml26
-rw-r--r--examples/boot/stm32f7/README.md29
-rw-r--r--examples/boot/stm32f7/build.rs37
-rwxr-xr-xexamples/boot/stm32f7/flash-boot.sh8
-rw-r--r--examples/boot/stm32f7/memory-bl.x18
-rw-r--r--examples/boot/stm32f7/memory.x15
-rw-r--r--examples/boot/stm32f7/src/bin/a.rs44
-rw-r--r--examples/boot/stm32f7/src/bin/b.rs27
-rw-r--r--examples/boot/stm32h7/.cargo/config.toml6
-rw-r--r--examples/boot/stm32h7/Cargo.toml26
-rw-r--r--examples/boot/stm32h7/README.md29
-rw-r--r--examples/boot/stm32h7/build.rs37
-rwxr-xr-xexamples/boot/stm32h7/flash-boot.sh8
-rw-r--r--examples/boot/stm32h7/memory-bl.x18
-rw-r--r--examples/boot/stm32h7/memory.x15
-rw-r--r--examples/boot/stm32h7/src/bin/a.rs44
-rw-r--r--examples/boot/stm32h7/src/bin/b.rs27
-rw-r--r--examples/stm32f3/Cargo.toml1
-rw-r--r--examples/stm32f3/src/bin/blinky.rs2
-rw-r--r--examples/stm32f3/src/bin/flash.rs43
-rw-r--r--examples/stm32f7/Cargo.toml1
-rw-r--r--examples/stm32f7/src/bin/flash.rs59
-rw-r--r--examples/stm32h7/Cargo.toml1
-rw-r--r--examples/stm32h7/src/bin/flash.rs58
m---------stm32-data0
42 files changed, 1414 insertions, 185 deletions
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
index 7539774c4..3df2daf51 100644
--- a/docs/modules/ROOT/pages/bootloader.adoc
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -14,7 +14,7 @@ The bootloader supports both internal and external flash by relying on the `embe
14The bootloader supports 14The bootloader supports
15 15
16* nRF52 with and without softdevice 16* nRF52 with and without softdevice
17* STM32 L4, WB, WL, L1 and L0 17* STM32 L4, WB, WL, L1, L0, F3, F7 and H7
18 18
19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work. 19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work.
20 20
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml
index a706e4c06..78339b0a2 100644
--- a/embassy-boot/stm32/Cargo.toml
+++ b/embassy-boot/stm32/Cargo.toml
@@ -2,7 +2,7 @@
2authors = [ 2authors = [
3 "Ulf Lilleengen <[email protected]>", 3 "Ulf Lilleengen <[email protected]>",
4] 4]
5edition = "2018" 5edition = "2021"
6name = "embassy-boot-stm32" 6name = "embassy-boot-stm32"
7version = "0.1.0" 7version = "0.1.0"
8description = "Bootloader for STM32 chips" 8description = "Bootloader for STM32 chips"
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
index 82e32a97d..48512534b 100644
--- a/embassy-boot/stm32/src/lib.rs
+++ b/embassy-boot/stm32/src/lib.rs
@@ -67,7 +67,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
67 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, 67 [(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
68 { 68 {
69 match self.boot.prepare_boot(flash) { 69 match self.boot.prepare_boot(flash) {
70 Ok(_) => self.boot.boot_address(), 70 Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
71 Err(_) => panic!("boot prepare error!"), 71 Err(_) => panic!("boot prepare error!"),
72 } 72 }
73 } 73 }
diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs
new file mode 100644
index 000000000..a5dc8dd0a
--- /dev/null
+++ b/embassy-stm32/src/flash/f3.rs
@@ -0,0 +1,104 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7pub(crate) unsafe fn lock() {
8 pac::FLASH.cr().modify(|w| w.set_lock(true));
9}
10
11pub(crate) unsafe fn unlock() {
12 pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123));
13 pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB));
14}
15
16pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
17 pac::FLASH.cr().write(|w| w.set_pg(true));
18
19 let ret = {
20 let mut ret: Result<(), Error> = Ok(());
21 let mut offset = offset;
22 for chunk in buf.chunks(2) {
23 write_volatile(
24 offset as *mut u16,
25 u16::from_le_bytes(chunk[0..2].try_into().unwrap()),
26 );
27 offset += chunk.len() as u32;
28
29 ret = blocking_wait_ready();
30 if ret.is_err() {
31 break;
32 }
33 }
34 ret
35 };
36
37 pac::FLASH.cr().write(|w| w.set_pg(false));
38
39 ret
40}
41
42pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
43 for page in (from..to).step_by(super::ERASE_SIZE) {
44 pac::FLASH.cr().modify(|w| {
45 w.set_per(true);
46 });
47
48 pac::FLASH.ar().write(|w| w.set_far(page));
49
50 pac::FLASH.cr().modify(|w| {
51 w.set_strt(true);
52 });
53
54 let mut ret: Result<(), Error> = blocking_wait_ready();
55
56 if !pac::FLASH.sr().read().eop() {
57 trace!("FLASH: EOP not set");
58 ret = Err(Error::Prog);
59 } else {
60 pac::FLASH.sr().write(|w| w.set_eop(true));
61 }
62
63 pac::FLASH.cr().modify(|w| w.set_per(false));
64
65 clear_all_err();
66 if ret.is_err() {
67 return ret;
68 }
69 }
70
71 Ok(())
72}
73
74pub(crate) unsafe fn clear_all_err() {
75 pac::FLASH.sr().modify(|w| {
76 if w.pgerr() {
77 w.set_pgerr(true);
78 }
79 if w.wrprterr() {
80 w.set_wrprterr(true);
81 }
82 if w.eop() {
83 w.set_eop(true);
84 }
85 });
86}
87
88pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
89 loop {
90 let sr = pac::FLASH.sr().read();
91
92 if !sr.bsy() {
93 if sr.wrprterr() {
94 return Err(Error::Protected);
95 }
96
97 if sr.pgerr() {
98 return Err(Error::Seq);
99 }
100
101 return Ok(());
102 }
103 }
104}
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs
new file mode 100644
index 000000000..16316fd93
--- /dev/null
+++ b/embassy-stm32/src/flash/f7.rs
@@ -0,0 +1,138 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use atomic_polyfill::{fence, Ordering};
5
6use crate::flash::Error;
7use crate::pac;
8
9pub(crate) unsafe fn lock() {
10 pac::FLASH.cr().modify(|w| w.set_lock(true));
11}
12
13pub(crate) unsafe fn unlock() {
14 pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
15 pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
16}
17
18pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
19 pac::FLASH.cr().write(|w| {
20 w.set_pg(true);
21 w.set_psize(pac::flash::vals::Psize::PSIZE32);
22 });
23
24 let ret = {
25 let mut ret: Result<(), Error> = Ok(());
26 let mut offset = offset;
27 for chunk in buf.chunks(super::WRITE_SIZE) {
28 for val in chunk.chunks(4) {
29 write_volatile(
30 offset as *mut u32,
31 u32::from_le_bytes(val[0..4].try_into().unwrap()),
32 );
33 offset += val.len() as u32;
34
35 // prevents parallelism errors
36 fence(Ordering::SeqCst);
37 }
38
39 ret = blocking_wait_ready();
40 if ret.is_err() {
41 break;
42 }
43 }
44 ret
45 };
46
47 pac::FLASH.cr().write(|w| w.set_pg(false));
48
49 ret
50}
51
52pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
53 let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
54 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
55 } else {
56 (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
57 };
58
59 let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 {
60 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32
61 } else {
62 (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8)
63 };
64
65 for sector in start_sector..end_sector {
66 let ret = erase_sector(sector as u8);
67 if ret.is_err() {
68 return ret;
69 }
70 }
71
72 Ok(())
73}
74
75unsafe fn erase_sector(sector: u8) -> Result<(), Error> {
76 pac::FLASH.cr().modify(|w| {
77 w.set_ser(true);
78 w.set_snb(sector)
79 });
80
81 pac::FLASH.cr().modify(|w| {
82 w.set_strt(true);
83 });
84
85 let ret: Result<(), Error> = blocking_wait_ready();
86
87 pac::FLASH.cr().modify(|w| w.set_ser(false));
88
89 clear_all_err();
90
91 ret
92}
93
94pub(crate) unsafe fn clear_all_err() {
95 pac::FLASH.sr().modify(|w| {
96 if w.erserr() {
97 w.set_erserr(true);
98 }
99 if w.pgperr() {
100 w.set_pgperr(true);
101 }
102 if w.pgaerr() {
103 w.set_pgaerr(true);
104 }
105 if w.wrperr() {
106 w.set_wrperr(true);
107 }
108 if w.eop() {
109 w.set_eop(true);
110 }
111 });
112}
113
114pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
115 loop {
116 let sr = pac::FLASH.sr().read();
117
118 if !sr.bsy() {
119 if sr.erserr() {
120 return Err(Error::Seq);
121 }
122
123 if sr.pgperr() {
124 return Err(Error::Parallelism);
125 }
126
127 if sr.pgaerr() {
128 return Err(Error::Unaligned);
129 }
130
131 if sr.wrperr() {
132 return Err(Error::Protected);
133 }
134
135 return Ok(());
136 }
137 }
138}
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs
new file mode 100644
index 000000000..afccffc4a
--- /dev/null
+++ b/embassy-stm32/src/flash/h7.rs
@@ -0,0 +1,202 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7const SECOND_BANK_OFFSET: usize = 0x0010_0000;
8
9const fn is_dual_bank() -> bool {
10 super::FLASH_SIZE / 2 > super::ERASE_SIZE
11}
12
13pub(crate) unsafe fn lock() {
14 pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true));
15 if is_dual_bank() {
16 pac::FLASH.bank(1).cr().modify(|w| w.set_lock(true));
17 }
18}
19
20pub(crate) unsafe fn unlock() {
21 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123));
22 pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
23
24 if is_dual_bank() {
25 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123));
26 pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB));
27 }
28}
29
30pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
31 let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32
32 {
33 pac::FLASH.bank(0)
34 } else {
35 pac::FLASH.bank(1)
36 };
37
38 bank.cr().write(|w| {
39 w.set_pg(true);
40 w.set_psize(2); // 32 bits at once
41 });
42
43 let ret = {
44 let mut ret: Result<(), Error> = Ok(());
45 let mut offset = offset;
46 'outer: for chunk in buf.chunks(super::WRITE_SIZE) {
47 for val in chunk.chunks(4) {
48 trace!("Writing at {:x}", offset);
49 write_volatile(
50 offset as *mut u32,
51 u32::from_le_bytes(val[0..4].try_into().unwrap()),
52 );
53 offset += val.len() as u32;
54
55 ret = blocking_wait_ready(bank);
56 bank.sr().modify(|w| {
57 if w.eop() {
58 w.set_eop(true);
59 }
60 });
61 if ret.is_err() {
62 break 'outer;
63 }
64 }
65 }
66 ret
67 };
68
69 bank.cr().write(|w| w.set_pg(false));
70
71 ret
72}
73
74pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
75 let from = from - super::FLASH_BASE as u32;
76 let to = to - super::FLASH_BASE as u32;
77
78 let bank_size = (super::FLASH_SIZE / 2) as u32;
79
80 let (bank, start, end) = if to <= bank_size {
81 let start_sector = from / super::ERASE_SIZE as u32;
82 let end_sector = to / super::ERASE_SIZE as u32;
83 (0, start_sector, end_sector)
84 } else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) {
85 let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
86 let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32;
87 (1, start_sector, end_sector)
88 } else {
89 error!("Attempting to write outside of defined sectors");
90 return Err(Error::Unaligned);
91 };
92
93 trace!("Erasing bank {}, sectors from {} to {}", bank, start, end);
94 for sector in start..end {
95 let ret = erase_sector(pac::FLASH.bank(bank), sector as u8);
96 if ret.is_err() {
97 return ret;
98 }
99 }
100
101 Ok(())
102}
103
104unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> {
105 bank.cr().modify(|w| {
106 w.set_ser(true);
107 w.set_snb(sector)
108 });
109
110 bank.cr().modify(|w| {
111 w.set_start(true);
112 });
113
114 let ret: Result<(), Error> = blocking_wait_ready(bank);
115
116 bank.cr().modify(|w| w.set_ser(false));
117
118 bank_clear_all_err(bank);
119
120 ret
121}
122
123pub(crate) unsafe fn clear_all_err() {
124 bank_clear_all_err(pac::FLASH.bank(0));
125 bank_clear_all_err(pac::FLASH.bank(1));
126}
127
128unsafe fn bank_clear_all_err(bank: pac::flash::Bank) {
129 bank.sr().modify(|w| {
130 if w.wrperr() {
131 w.set_wrperr(true);
132 }
133 if w.pgserr() {
134 w.set_pgserr(true);
135 }
136 if w.strberr() {
137 // single address was written multiple times, can be ignored
138 w.set_strberr(true);
139 }
140 if w.incerr() {
141 // writing to a different address when programming 256 bit word was not finished
142 w.set_incerr(true);
143 }
144 if w.operr() {
145 w.set_operr(true);
146 }
147 if w.sneccerr1() {
148 // single ECC error
149 w.set_sneccerr1(true);
150 }
151 if w.dbeccerr() {
152 // double ECC error
153 w.set_dbeccerr(true);
154 }
155 if w.rdperr() {
156 w.set_rdperr(true);
157 }
158 if w.rdserr() {
159 w.set_rdserr(true);
160 }
161 });
162}
163
164pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
165 loop {
166 let sr = bank.sr().read();
167
168 if !sr.bsy() && !sr.qw() {
169 if sr.wrperr() {
170 return Err(Error::Protected);
171 }
172 if sr.pgserr() {
173 error!("pgserr");
174 return Err(Error::Seq);
175 }
176 if sr.incerr() {
177 // writing to a different address when programming 256 bit word was not finished
178 error!("incerr");
179 return Err(Error::Seq);
180 }
181 if sr.operr() {
182 return Err(Error::Prog);
183 }
184 if sr.sneccerr1() {
185 // single ECC error
186 return Err(Error::Prog);
187 }
188 if sr.dbeccerr() {
189 // double ECC error
190 return Err(Error::Prog);
191 }
192 if sr.rdperr() {
193 return Err(Error::Protected);
194 }
195 if sr.rdserr() {
196 return Err(Error::Protected);
197 }
198
199 return Ok(());
200 }
201 }
202}
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs
new file mode 100644
index 000000000..6ec4796a5
--- /dev/null
+++ b/embassy-stm32/src/flash/l.rs
@@ -0,0 +1,185 @@
1use core::convert::TryInto;
2use core::ptr::write_volatile;
3
4use crate::flash::Error;
5use crate::pac;
6
7pub(crate) unsafe fn lock() {
8 #[cfg(any(flash_wl, flash_wb, flash_l4))]
9 pac::FLASH.cr().modify(|w| w.set_lock(true));
10
11 #[cfg(any(flash_l0))]
12 pac::FLASH.pecr().modify(|w| {
13 w.set_optlock(true);
14 w.set_prglock(true);
15 w.set_pelock(true);
16 });
17}
18
19pub(crate) unsafe fn unlock() {
20 #[cfg(any(flash_wl, flash_wb, flash_l4))]
21 {
22 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
23 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
24 }
25
26 #[cfg(any(flash_l0, flash_l1))]
27 {
28 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
29 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
30
31 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
32 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
33 }
34}
35
36pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> {
37 #[cfg(any(flash_wl, flash_wb, flash_l4))]
38 pac::FLASH.cr().write(|w| w.set_pg(true));
39
40 let ret = {
41 let mut ret: Result<(), Error> = Ok(());
42 let mut offset = offset;
43 for chunk in buf.chunks(super::WRITE_SIZE) {
44 for val in chunk.chunks(4) {
45 write_volatile(
46 offset as *mut u32,
47 u32::from_le_bytes(val[0..4].try_into().unwrap()),
48 );
49 offset += val.len() as u32;
50 }
51
52 ret = blocking_wait_ready();
53 if ret.is_err() {
54 break;
55 }
56 }
57 ret
58 };
59
60 #[cfg(any(flash_wl, flash_wb, flash_l4))]
61 pac::FLASH.cr().write(|w| w.set_pg(false));
62
63 ret
64}
65
66pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> {
67 for page in (from..to).step_by(super::ERASE_SIZE) {
68 #[cfg(any(flash_l0, flash_l1))]
69 {
70 pac::FLASH.pecr().modify(|w| {
71 w.set_erase(true);
72 w.set_prog(true);
73 });
74
75 write_volatile(page as *mut u32, 0xFFFFFFFF);
76 }
77
78 #[cfg(any(flash_wl, flash_wb, flash_l4))]
79 {
80 let idx = page / super::ERASE_SIZE as u32;
81
82 pac::FLASH.cr().modify(|w| {
83 w.set_per(true);
84 w.set_pnb(idx as u8);
85 #[cfg(any(flash_wl, flash_wb))]
86 w.set_strt(true);
87 #[cfg(any(flash_l4))]
88 w.set_start(true);
89 });
90 }
91
92 let ret: Result<(), Error> = blocking_wait_ready();
93
94 #[cfg(any(flash_wl, flash_wb, flash_l4))]
95 pac::FLASH.cr().modify(|w| w.set_per(false));
96
97 #[cfg(any(flash_l0, flash_l1))]
98 pac::FLASH.pecr().modify(|w| {
99 w.set_erase(false);
100 w.set_prog(false);
101 });
102
103 clear_all_err();
104 if ret.is_err() {
105 return ret;
106 }
107 }
108
109 Ok(())
110}
111
112pub(crate) unsafe fn clear_all_err() {
113 pac::FLASH.sr().modify(|w| {
114 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
115 if w.rderr() {
116 w.set_rderr(true);
117 }
118 #[cfg(any(flash_wl, flash_wb, flash_l4))]
119 if w.fasterr() {
120 w.set_fasterr(true);
121 }
122 #[cfg(any(flash_wl, flash_wb, flash_l4))]
123 if w.miserr() {
124 w.set_miserr(true);
125 }
126 #[cfg(any(flash_wl, flash_wb, flash_l4))]
127 if w.pgserr() {
128 w.set_pgserr(true);
129 }
130 if w.sizerr() {
131 w.set_sizerr(true);
132 }
133 if w.pgaerr() {
134 w.set_pgaerr(true);
135 }
136 if w.wrperr() {
137 w.set_wrperr(true);
138 }
139 #[cfg(any(flash_wl, flash_wb, flash_l4))]
140 if w.progerr() {
141 w.set_progerr(true);
142 }
143 #[cfg(any(flash_wl, flash_wb, flash_l4))]
144 if w.operr() {
145 w.set_operr(true);
146 }
147 });
148}
149
150pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> {
151 loop {
152 let sr = pac::FLASH.sr().read();
153
154 if !sr.bsy() {
155 #[cfg(any(flash_wl, flash_wb, flash_l4))]
156 if sr.progerr() {
157 return Err(Error::Prog);
158 }
159
160 if sr.wrperr() {
161 return Err(Error::Protected);
162 }
163
164 if sr.pgaerr() {
165 return Err(Error::Unaligned);
166 }
167
168 if sr.sizerr() {
169 return Err(Error::Size);
170 }
171
172 #[cfg(any(flash_wl, flash_wb, flash_l4))]
173 if sr.miserr() {
174 return Err(Error::Miss);
175 }
176
177 #[cfg(any(flash_wl, flash_wb, flash_l4))]
178 if sr.pgserr() {
179 return Err(Error::Seq);
180 }
181
182 return Ok(());
183 }
184 }
185}
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index cff3119fd..4be611d2e 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -1,8 +1,5 @@
1use crate::pac;
2use crate::peripherals::FLASH; 1use crate::peripherals::FLASH;
3use core::convert::TryInto;
4use core::marker::PhantomData; 2use core::marker::PhantomData;
5use core::ptr::write_volatile;
6use embassy::util::Unborrow; 3use embassy::util::Unborrow;
7use embassy_hal_common::unborrow; 4use embassy_hal_common::unborrow;
8 5
@@ -17,6 +14,12 @@ pub use crate::pac::FLASH_SIZE;
17pub use crate::pac::WRITE_SIZE; 14pub use crate::pac::WRITE_SIZE;
18const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; 15const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
19 16
17#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")]
18#[cfg_attr(flash_f3, path = "f3.rs")]
19#[cfg_attr(flash_f7, path = "f7.rs")]
20#[cfg_attr(flash_h7, path = "h7.rs")]
21mod family;
22
20pub struct Flash<'d> { 23pub struct Flash<'d> {
21 _inner: FLASH, 24 _inner: FLASH,
22 _phantom: PhantomData<&'d mut FLASH>, 25 _phantom: PhantomData<&'d mut FLASH>,
@@ -33,37 +36,13 @@ impl<'d> Flash<'d> {
33 36
34 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self { 37 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
35 let flash = Self::new(p); 38 let flash = Self::new(p);
36 #[cfg(any(flash_wl, flash_wb, flash_l4))]
37 unsafe {
38 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
39 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
40 }
41 39
42 #[cfg(any(flash_l0))] 40 unsafe { family::unlock() };
43 unsafe {
44 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
45 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
46
47 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
48 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
49 }
50 flash 41 flash
51 } 42 }
52 43
53 pub fn lock(&mut self) { 44 pub fn lock(&mut self) {
54 #[cfg(any(flash_wl, flash_wb, flash_l4))] 45 unsafe { family::lock() };
55 unsafe {
56 pac::FLASH.cr().modify(|w| w.set_lock(true));
57 }
58
59 #[cfg(any(flash_l0))]
60 unsafe {
61 pac::FLASH.pecr().modify(|w| {
62 w.set_optlock(true);
63 w.set_prglock(true);
64 w.set_pelock(true);
65 });
66 }
67 } 46 }
68 47
69 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 48 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
@@ -89,36 +68,7 @@ impl<'d> Flash<'d> {
89 68
90 self.clear_all_err(); 69 self.clear_all_err();
91 70
92 #[cfg(any(flash_wl, flash_wb, flash_l4))] 71 unsafe { family::blocking_write(offset, buf) }
93 unsafe {
94 pac::FLASH.cr().write(|w| w.set_pg(true))
95 }
96
97 let mut ret: Result<(), Error> = Ok(());
98 let mut offset = offset;
99 for chunk in buf.chunks(WRITE_SIZE) {
100 for val in chunk.chunks(4) {
101 unsafe {
102 write_volatile(
103 offset as *mut u32,
104 u32::from_le_bytes(val[0..4].try_into().unwrap()),
105 );
106 }
107 offset += val.len() as u32;
108 }
109
110 ret = self.blocking_wait_ready();
111 if ret.is_err() {
112 break;
113 }
114 }
115
116 #[cfg(any(flash_wl, flash_wb, flash_l4))]
117 unsafe {
118 pac::FLASH.cr().write(|w| w.set_pg(false))
119 }
120
121 ret
122 } 72 }
123 73
124 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { 74 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
@@ -127,135 +77,17 @@ impl<'d> Flash<'d> {
127 if to < from || to as usize > FLASH_END { 77 if to < from || to as usize > FLASH_END {
128 return Err(Error::Size); 78 return Err(Error::Size);
129 } 79 }
130 if from as usize % ERASE_SIZE != 0 || to as usize % ERASE_SIZE != 0 { 80 if ((to - from) as usize % ERASE_SIZE) != 0 {
131 return Err(Error::Unaligned); 81 return Err(Error::Unaligned);
132 } 82 }
133 83
134 self.clear_all_err(); 84 self.clear_all_err();
135 85
136 for page in (from..to).step_by(ERASE_SIZE) { 86 unsafe { family::blocking_erase(from, to) }
137 #[cfg(any(flash_l0, flash_l1))]
138 unsafe {
139 pac::FLASH.pecr().modify(|w| {
140 w.set_erase(true);
141 w.set_prog(true);
142 });
143
144 write_volatile(page as *mut u32, 0xFFFFFFFF);
145 }
146
147 #[cfg(any(flash_wl, flash_wb, flash_l4))]
148 unsafe {
149 let idx = page / ERASE_SIZE as u32;
150
151 pac::FLASH.cr().modify(|w| {
152 w.set_per(true);
153 w.set_pnb(idx as u8);
154 #[cfg(any(flash_wl, flash_wb))]
155 w.set_strt(true);
156 #[cfg(any(flash_l4))]
157 w.set_start(true);
158 });
159 }
160
161 let ret: Result<(), Error> = self.blocking_wait_ready();
162
163 #[cfg(any(flash_wl, flash_wb, flash_l4))]
164 unsafe {
165 pac::FLASH.cr().modify(|w| w.set_per(false));
166 }
167
168 #[cfg(any(flash_l0, flash_l1))]
169 unsafe {
170 pac::FLASH.pecr().modify(|w| {
171 w.set_erase(false);
172 w.set_prog(false);
173 });
174 }
175
176 self.clear_all_err();
177 if ret.is_err() {
178 return ret;
179 }
180 }
181
182 Ok(())
183 }
184
185 fn blocking_wait_ready(&self) -> Result<(), Error> {
186 loop {
187 let sr = unsafe { pac::FLASH.sr().read() };
188
189 if !sr.bsy() {
190 #[cfg(any(flash_wl, flash_wb, flash_l4))]
191 if sr.progerr() {
192 return Err(Error::Prog);
193 }
194
195 if sr.wrperr() {
196 return Err(Error::Protected);
197 }
198
199 if sr.pgaerr() {
200 return Err(Error::Unaligned);
201 }
202
203 if sr.sizerr() {
204 return Err(Error::Size);
205 }
206
207 #[cfg(any(flash_wl, flash_wb, flash_l4))]
208 if sr.miserr() {
209 return Err(Error::Miss);
210 }
211
212 #[cfg(any(flash_wl, flash_wb, flash_l4))]
213 if sr.pgserr() {
214 return Err(Error::Seq);
215 }
216 return Ok(());
217 }
218 }
219 } 87 }
220 88
221 fn clear_all_err(&mut self) { 89 fn clear_all_err(&mut self) {
222 unsafe { 90 unsafe { family::clear_all_err() };
223 pac::FLASH.sr().modify(|w| {
224 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
225 if w.rderr() {
226 w.set_rderr(false);
227 }
228 #[cfg(any(flash_wl, flash_wb, flash_l4))]
229 if w.fasterr() {
230 w.set_fasterr(false);
231 }
232 #[cfg(any(flash_wl, flash_wb, flash_l4))]
233 if w.miserr() {
234 w.set_miserr(false);
235 }
236 #[cfg(any(flash_wl, flash_wb, flash_l4))]
237 if w.pgserr() {
238 w.set_pgserr(false);
239 }
240 if w.sizerr() {
241 w.set_sizerr(false);
242 }
243 if w.pgaerr() {
244 w.set_pgaerr(false);
245 }
246 if w.wrperr() {
247 w.set_wrperr(false);
248 }
249 #[cfg(any(flash_wl, flash_wb, flash_l4))]
250 if w.progerr() {
251 w.set_progerr(false);
252 }
253 #[cfg(any(flash_wl, flash_wb, flash_l4))]
254 if w.operr() {
255 w.set_operr(false);
256 }
257 });
258 }
259 } 91 }
260} 92}
261 93
@@ -274,6 +106,7 @@ pub enum Error {
274 Seq, 106 Seq,
275 Protected, 107 Protected,
276 Unaligned, 108 Unaligned,
109 Parallelism,
277} 110}
278 111
279impl<'d> ErrorType for Flash<'d> { 112impl<'d> ErrorType for Flash<'d> {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index ba426128f..1a46f8124 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -50,7 +50,9 @@ pub mod i2c;
50 50
51#[cfg(crc)] 51#[cfg(crc)]
52pub mod crc; 52pub mod crc;
53#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4))] 53#[cfg(any(
54 flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f7, flash_h7
55))]
54pub mod flash; 56pub mod flash;
55pub mod pwm; 57pub mod pwm;
56#[cfg(rng)] 58#[cfg(rng)]
diff --git a/examples/boot/stm32f3/.cargo/config.toml b/examples/boot/stm32f3/.cargo/config.toml
new file mode 100644
index 000000000..eb8a8b335
--- /dev/null
+++ b/examples/boot/stm32f3/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32F303VCTx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32f3/Cargo.toml b/examples/boot/stm32f3/Cargo.toml
new file mode 100644
index 000000000..d4ca600f8
--- /dev/null
+++ b/examples/boot/stm32f3/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f3-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32f3/README.md b/examples/boot/stm32f3/README.md
new file mode 100644
index 000000000..e92ffb692
--- /dev/null
+++ b/examples/boot/stm32f3/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32F3 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f303re --chip STM32F303RETx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32F303RETx
29```
diff --git a/examples/boot/stm32f3/build.rs b/examples/boot/stm32f3/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32f3/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32f3/memory.x b/examples/boot/stm32f3/memory.x
new file mode 100644
index 000000000..14b2a2c9f
--- /dev/null
+++ b/examples/boot/stm32f3/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32f3/src/bin/a.rs b/examples/boot/stm32f3/src/bin/a.rs
new file mode 100644
index 000000000..db9262f43
--- /dev/null
+++ b/examples/boot/stm32f3/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PA5, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_falling_edge().await;
31 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32f3/src/bin/b.rs b/examples/boot/stm32f3/src/bin/b.rs
new file mode 100644
index 000000000..814275988
--- /dev/null
+++ b/examples/boot/stm32f3/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32f7/.cargo/config.toml b/examples/boot/stm32f7/.cargo/config.toml
new file mode 100644
index 000000000..df5114520
--- /dev/null
+++ b/examples/boot/stm32f7/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32F767ZITx -v"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32f7/Cargo.toml b/examples/boot/stm32f7/Cargo.toml
new file mode 100644
index 000000000..857b287d5
--- /dev/null
+++ b/examples/boot/stm32f7/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f7-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32f7/README.md b/examples/boot/stm32f7/README.md
new file mode 100644
index 000000000..bf9142a1c
--- /dev/null
+++ b/examples/boot/stm32f7/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32F7 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18./flash-boot.sh
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32F767ZITx
29```
diff --git a/examples/boot/stm32f7/build.rs b/examples/boot/stm32f7/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32f7/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32f7/flash-boot.sh b/examples/boot/stm32f7/flash-boot.sh
new file mode 100755
index 000000000..86074ffa3
--- /dev/null
+++ b/examples/boot/stm32f7/flash-boot.sh
@@ -0,0 +1,8 @@
1#!/bin/bash
2mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
3cp memory-bl.x ../../../embassy-boot/stm32/memory.x
4
5cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf
6
7rm ../../../embassy-boot/stm32/memory.x
8mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
diff --git a/examples/boot/stm32f7/memory-bl.x b/examples/boot/stm32f7/memory-bl.x
new file mode 100644
index 000000000..47f3f4d9b
--- /dev/null
+++ b/examples/boot/stm32f7/memory-bl.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 256K
5 BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
6 ACTIVE : ORIGIN = 0x08080000, LENGTH = 256K
7 DFU : ORIGIN = 0x080c0000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 368K + 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
13
14__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
16
17__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
diff --git a/examples/boot/stm32f7/memory.x b/examples/boot/stm32f7/memory.x
new file mode 100644
index 000000000..1c5537d17
--- /dev/null
+++ b/examples/boot/stm32f7/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 256K
5 BOOTLOADER_STATE : ORIGIN = 0x08040000, LENGTH = 256K
6 FLASH : ORIGIN = 0x08080000, LENGTH = 256K
7 DFU : ORIGIN = 0x080c0000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 368K + 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32f7/src/bin/a.rs b/examples/boot/stm32f7/src/bin/a.rs
new file mode 100644
index 000000000..ca154f0af
--- /dev/null
+++ b/examples/boot/stm32f7/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Down);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB7, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_rising_edge().await;
31 let mut offset = 0;
32 let mut buf: [u8; 256 * 1024] = [0; 256 * 1024];
33 for chunk in APP_B.chunks(256 * 1024) {
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32f7/src/bin/b.rs b/examples/boot/stm32f7/src/bin/b.rs
new file mode 100644
index 000000000..ed37137f5
--- /dev/null
+++ b/examples/boot/stm32f7/src/bin/b.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 Timer::after(Duration::from_millis(300)).await;
17 let mut led = Output::new(p.PB7, Level::High, Speed::Low);
18 led.set_high();
19
20 loop {
21 led.set_high();
22 Timer::after(Duration::from_millis(500)).await;
23
24 led.set_low();
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
diff --git a/examples/boot/stm32h7/.cargo/config.toml b/examples/boot/stm32h7/.cargo/config.toml
new file mode 100644
index 000000000..8475e7f66
--- /dev/null
+++ b/examples/boot/stm32h7/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32H743ZITx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32h7/Cargo.toml b/examples/boot/stm32h7/Cargo.toml
new file mode 100644
index 000000000..1fd03906f
--- /dev/null
+++ b/examples/boot/stm32h7/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2021"
4name = "embassy-boot-stm32f7-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32h7/README.md b/examples/boot/stm32h7/README.md
new file mode 100644
index 000000000..1fdc305e6
--- /dev/null
+++ b/examples/boot/stm32h7/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32H7 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18./flash-boot.sh
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32H743ZITx
29```
diff --git a/examples/boot/stm32h7/build.rs b/examples/boot/stm32h7/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32h7/build.rs
@@ -0,0 +1,37 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32h7/flash-boot.sh b/examples/boot/stm32h7/flash-boot.sh
new file mode 100755
index 000000000..a910b7312
--- /dev/null
+++ b/examples/boot/stm32h7/flash-boot.sh
@@ -0,0 +1,8 @@
1#!/bin/bash
2mv ../../../embassy-boot/stm32/memory.x ../../../embassy-boot/stm32/memory-old.x
3cp memory-bl.x ../../../embassy-boot/stm32/memory.x
4
5cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32H743ZITx --target thumbv7em-none-eabihf
6
7rm ../../../embassy-boot/stm32/memory.x
8mv ../../../embassy-boot/stm32/memory-old.x ../../../embassy-boot/stm32/memory.x
diff --git a/examples/boot/stm32h7/memory-bl.x b/examples/boot/stm32h7/memory-bl.x
new file mode 100644
index 000000000..c6f447d8b
--- /dev/null
+++ b/examples/boot/stm32h7/memory-bl.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 128K
5 BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
6 ACTIVE : ORIGIN = 0x08040000, LENGTH = 128K
7 DFU : ORIGIN = 0x08100000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
13
14__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
16
17__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
diff --git a/examples/boot/stm32h7/memory.x b/examples/boot/stm32h7/memory.x
new file mode 100644
index 000000000..497a09e41
--- /dev/null
+++ b/examples/boot/stm32h7/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 128K
5 BOOTLOADER_STATE : ORIGIN = 0x08020000, LENGTH = 128K
6 FLASH : ORIGIN = 0x08040000, LENGTH = 256K
7 DFU : ORIGIN = 0x08100000, LENGTH = 512K
8 RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 368K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32h7/src/bin/a.rs b/examples/boot/stm32h7/src/bin/a.rs
new file mode 100644
index 000000000..1f23a8bc2
--- /dev/null
+++ b/examples/boot/stm32h7/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Down);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_rising_edge().await;
31 let mut offset = 0;
32 let mut buf: [u8; 128 * 1024] = [0; 128 * 1024];
33 for chunk in APP_B.chunks(128 * 1024) {
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32h7/src/bin/b.rs b/examples/boot/stm32h7/src/bin/b.rs
new file mode 100644
index 000000000..233b93e1a
--- /dev/null
+++ b/examples/boot/stm32h7/src/bin/b.rs
@@ -0,0 +1,27 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 Timer::after(Duration::from_millis(300)).await;
17 let mut led = Output::new(p.PB14, Level::High, Speed::Low);
18 led.set_high();
19
20 loop {
21 led.set_high();
22 Timer::after(Duration::from_millis(500)).await;
23
24 led.set_low();
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml
index 37f99fea2..e7a44c11e 100644
--- a/examples/stm32f3/Cargo.toml
+++ b/examples/stm32f3/Cargo.toml
@@ -19,3 +19,4 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
19futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 19futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
20heapless = { version = "0.7.5", default-features = false } 20heapless = { version = "0.7.5", default-features = false }
21nb = "1.0.0" 21nb = "1.0.0"
22embedded-storage = "0.3.0"
diff --git a/examples/stm32f3/src/bin/blinky.rs b/examples/stm32f3/src/bin/blinky.rs
index b3643a26f..4b181a784 100644
--- a/examples/stm32f3/src/bin/blinky.rs
+++ b/examples/stm32f3/src/bin/blinky.rs
@@ -15,7 +15,7 @@ use panic_probe as _;
15async fn main(_spawner: Spawner, p: Peripherals) { 15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello World!"); 16 info!("Hello World!");
17 17
18 let mut led = Output::new(p.PE12, Level::High, Speed::Low); 18 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
19 19
20 loop { 20 loop {
21 info!("high"); 21 info!("high");
diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs
new file mode 100644
index 000000000..3890f0514
--- /dev/null
+++ b/examples/stm32f3/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x26000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 2048));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index 09a06aa7f..ce0a6d48f 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -22,6 +22,7 @@ heapless = { version = "0.7.5", default-features = false }
22nb = "1.0.0" 22nb = "1.0.0"
23rand_core = "0.6.3" 23rand_core = "0.6.3"
24critical-section = "0.2.3" 24critical-section = "0.2.3"
25embedded-storage = "0.3.0"
25 26
26[dependencies.smoltcp] 27[dependencies.smoltcp]
27version = "0.8.0" 28version = "0.8.0"
diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs
new file mode 100644
index 000000000..9eb8e4b94
--- /dev/null
+++ b/examples/stm32f7/src/bin/flash.rs
@@ -0,0 +1,59 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::time::{Duration, Timer};
8use embassy_stm32::flash::Flash;
9use embassy_stm32::Peripherals;
10use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
11
12use defmt_rtt as _; // global logger
13use panic_probe as _;
14
15#[embassy::main]
16async fn main(_spawner: Spawner, p: Peripherals) {
17 info!("Hello Flash!");
18
19 const ADDR: u32 = 0x8_0000;
20
21 // wait a bit before accessing the flash
22 Timer::after(Duration::from_millis(300)).await;
23
24 let mut f = Flash::unlock(p.FLASH);
25
26 info!("Reading...");
27 let mut buf = [0u8; 32];
28 unwrap!(f.read(ADDR, &mut buf));
29 info!("Read: {=[u8]:x}", buf);
30
31 info!("Erasing...");
32 unwrap!(f.erase(ADDR, ADDR + 256 * 1024));
33
34 info!("Reading...");
35 let mut buf = [0u8; 32];
36 unwrap!(f.read(ADDR, &mut buf));
37 info!("Read after erase: {=[u8]:x}", buf);
38
39 info!("Writing...");
40 unwrap!(f.write(
41 ADDR,
42 &[
43 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
44 25, 26, 27, 28, 29, 30, 31, 32
45 ]
46 ));
47
48 info!("Reading...");
49 let mut buf = [0u8; 32];
50 unwrap!(f.read(ADDR, &mut buf));
51 info!("Read: {=[u8]:x}", buf);
52 assert_eq!(
53 &buf[..],
54 &[
55 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
56 25, 26, 27, 28, 29, 30, 31, 32
57 ]
58 );
59}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 419bbec31..8906a1d0b 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -27,6 +27,7 @@ rand_core = "0.6.3"
27critical-section = "0.2.5" 27critical-section = "0.2.5"
28micromath = "2.0.0" 28micromath = "2.0.0"
29stm32-fmc = "0.2.4" 29stm32-fmc = "0.2.4"
30embedded-storage = "0.3.0"
30 31
31[dependencies.smoltcp] 32[dependencies.smoltcp]
32version = "0.8.0" 33version = "0.8.0"
diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs
new file mode 100644
index 000000000..b008c0884
--- /dev/null
+++ b/examples/stm32h7/src/bin/flash.rs
@@ -0,0 +1,58 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use defmt_rtt as _; // global logger
7use embassy::executor::Spawner;
8use embassy::time::{Duration, Timer};
9use embassy_stm32::flash::Flash;
10use embassy_stm32::Peripherals;
11use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x08_0000;
19
20 // wait a bit before accessing the flash
21 Timer::after(Duration::from_millis(300)).await;
22
23 let mut f = Flash::unlock(p.FLASH);
24
25 info!("Reading...");
26 let mut buf = [0u8; 32];
27 unwrap!(f.read(ADDR, &mut buf));
28 info!("Read: {=[u8]:x}", buf);
29
30 info!("Erasing...");
31 unwrap!(f.erase(ADDR, ADDR + 128 * 1024));
32
33 info!("Reading...");
34 let mut buf = [0u8; 32];
35 unwrap!(f.read(ADDR, &mut buf));
36 info!("Read after erase: {=[u8]:x}", buf);
37
38 info!("Writing...");
39 unwrap!(f.write(
40 ADDR,
41 &[
42 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
43 25, 26, 27, 28, 29, 30, 31, 32
44 ]
45 ));
46
47 info!("Reading...");
48 let mut buf = [0u8; 32];
49 unwrap!(f.read(ADDR, &mut buf));
50 info!("Read: {=[u8]:x}", buf);
51 assert_eq!(
52 &buf[..],
53 &[
54 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
55 25, 26, 27, 28, 29, 30, 31, 32
56 ]
57 );
58}
diff --git a/stm32-data b/stm32-data
Subproject 9abfa9d2b51e6071fdc7e680b4a171e4fa20c2f Subproject b2d7a9f5de7dc3ae17c87c1ff94e13a822d18e7