aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-05-11 22:48:55 +0000
committerGitHub <[email protected]>2023-05-11 22:48:55 +0000
commit7f96359804b5f2bb3248cb7c0e1bab30e9135a41 (patch)
tree153a60a3813fce4335fada366ecea76d1735aec2
parente179e7cf85810f0aa7ef8027d8d48f6d21f64dac (diff)
parentbf45b1d83dba837dabc63361dc472902ab82cda4 (diff)
Merge #1424
1424: add TL maibox for stm32wb r=xoviat a=OueslatiGhaith Hello, This pull request is related to #1397 and #1401, inspired by #24, build upon the work done in #1405, and was tested on an stm32wb55rg. This pull request aims to add the transport layer mailbox for stm32wb microcontrollers. For now it's only capable of initializing it and getting the firmware information Co-authored-by: goueslati <[email protected]> Co-authored-by: Ghaith Oueslati <[email protected]> Co-authored-by: xoviat <[email protected]>
-rw-r--r--.vscode/.gitignore3
-rw-r--r--embassy-stm32/Cargo.toml3
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/tl_mbox/ble.rs26
-rw-r--r--embassy-stm32/src/tl_mbox/channels.rs104
-rw-r--r--embassy-stm32/src/tl_mbox/cmd.rs49
-rw-r--r--embassy-stm32/src/tl_mbox/evt.rs8
-rw-r--r--embassy-stm32/src/tl_mbox/mm.rs30
-rw-r--r--embassy-stm32/src/tl_mbox/mod.rs318
-rw-r--r--embassy-stm32/src/tl_mbox/sys.rs24
-rw-r--r--embassy-stm32/src/tl_mbox/unsafe_linked_list.rs123
-rw-r--r--examples/stm32wb/.cargo/config.toml3
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wb/memory.x32
-rw-r--r--examples/stm32wb/src/bin/tl_mbox.rs68
-rw-r--r--tests/stm32/Cargo.toml6
-rw-r--r--tests/stm32/src/bin/ble.rs51
17 files changed, 830 insertions, 22 deletions
diff --git a/.vscode/.gitignore b/.vscode/.gitignore
new file mode 100644
index 000000000..9fbb9ec95
--- /dev/null
+++ b/.vscode/.gitignore
@@ -0,0 +1,3 @@
1*.cortex-debug.*.json
2launch.json
3tasks.json \ No newline at end of file
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index b9887f9b3..21adb5ddf 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -67,6 +67,7 @@ seq-macro = "0.3.0"
67cfg-if = "1.0.0" 67cfg-if = "1.0.0"
68embedded-io = { version = "0.4.0", features = ["async"], optional = true } 68embedded-io = { version = "0.4.0", features = ["async"], optional = true }
69chrono = { version = "^0.4", default-features = false, optional = true} 69chrono = { version = "^0.4", default-features = false, optional = true}
70bit_field = "0.10.2"
70 71
71[dev-dependencies] 72[dev-dependencies]
72critical-section = { version = "1.1", features = ["std"] } 73critical-section = { version = "1.1", features = ["std"] }
@@ -1443,4 +1444,4 @@ stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ]
1443stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] 1444stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ]
1444stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] 1445stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ]
1445stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] 1446stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ]
1446stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] 1447stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 11820b7a0..b3dbe1e2f 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -57,6 +57,8 @@ pub mod rtc;
57pub mod sdmmc; 57pub mod sdmmc;
58#[cfg(spi)] 58#[cfg(spi)]
59pub mod spi; 59pub mod spi;
60#[cfg(stm32wb)]
61pub mod tl_mbox;
60#[cfg(usart)] 62#[cfg(usart)]
61pub mod usart; 63pub mod usart;
62#[cfg(all(usb, feature = "time"))] 64#[cfg(all(usb, feature = "time"))]
diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs
new file mode 100644
index 000000000..a2c0758d1
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/ble.rs
@@ -0,0 +1,26 @@
1use core::mem::MaybeUninit;
2
3use super::unsafe_linked_list::LST_init_head;
4use super::{channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
5use crate::ipcc::Ipcc;
6
7pub struct Ble;
8
9impl Ble {
10 pub fn new(ipcc: &mut Ipcc) -> Self {
11 unsafe {
12 LST_init_head(EVT_QUEUE.as_mut_ptr());
13
14 TL_BLE_TABLE = MaybeUninit::new(BleTable {
15 pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(),
16 pcs_buffer: CS_BUFFER.as_mut_ptr().cast(),
17 pevt_queue: EVT_QUEUE.as_ptr().cast(),
18 phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(),
19 });
20 }
21
22 ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true);
23
24 Ble
25 }
26}
diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs
new file mode 100644
index 000000000..1dde5d61c
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/channels.rs
@@ -0,0 +1,104 @@
1//! CPU1 CPU2
2//! | (SYSTEM) |
3//! |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->|
4//! | |
5//! |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------|
6//! | |
7//! | (ZIGBEE) |
8//! |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>|
9//! | |
10//! |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->|
11//! | |
12//! |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------|
13//! | |
14//! |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------|
15//! | |
16//! | (THREAD) |
17//! |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->|
18//! | |
19//! |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->|
20//! | |
21//! |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------|
22//! | |
23//! |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--|
24//! | |
25//! | (BLE) |
26//! |----HW_IPCC_BLE_CMD_CHANNEL--------------------->|
27//! | |
28//! |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->|
29//! | |
30//! |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------|
31//! | |
32//! | (BLE LLD) |
33//! |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->|
34//! | |
35//! |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------|
36//! | |
37//! |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------|
38//! | |
39//! | (MAC) |
40//! |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->|
41//! | |
42//! |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL|
43//! | |
44//! | (BUFFER) |
45//! |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>|
46//! | |
47//! | (TRACE) |
48//! |<----HW_IPCC_TRACES_CHANNEL----------------------|
49//! | |
50//!
51
52pub mod cpu1 {
53 use crate::ipcc::IpccChannel;
54
55 #[allow(dead_code)] // Not used currently but reserved
56 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1;
57 #[allow(dead_code)] // Not used currently but reserved
58 pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2;
59 #[allow(dead_code)] // Not used currently but reserved
60 pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;
61 #[allow(dead_code)] // Not used currently but reserved
62 pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3;
63 #[allow(dead_code)] // Not used currently but reserved
64 pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;
65 #[allow(dead_code)] // Not used currently but reserved
66 pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4;
67 #[allow(dead_code)] // Not used currently but reserved
68 pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
69 #[allow(dead_code)] // Not used currently but reserved
70 pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
71 #[allow(dead_code)] // Not used currently but reserved
72 pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
73 #[allow(dead_code)] // Not used currently but reserved
74 pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6;
75}
76
77pub mod cpu2 {
78 use crate::ipcc::IpccChannel;
79
80 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1;
81 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2;
82 #[allow(dead_code)] // Not used currently but reserved
83 pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
84 #[allow(dead_code)] // Not used currently but reserved
85 pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
86 #[allow(dead_code)] // Not used currently but reserved
87 pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
88 #[allow(dead_code)] // Not used currently but reserved
89 pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;
90 #[allow(dead_code)] // Not used currently but reserved
91 pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;
92 #[allow(dead_code)] // Not used currently but reserved
93 pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4;
94 #[allow(dead_code)] // Not used currently but reserved
95 pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5;
96 #[allow(dead_code)] // Not used currently but reserved
97 pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
98 #[allow(dead_code)] // Not used currently but reserved
99 pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
100 #[allow(dead_code)] // Not used currently but reserved
101 pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
102 #[allow(dead_code)] // Not used currently but reserved
103 pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5;
104}
diff --git a/embassy-stm32/src/tl_mbox/cmd.rs b/embassy-stm32/src/tl_mbox/cmd.rs
new file mode 100644
index 000000000..3507c3231
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/cmd.rs
@@ -0,0 +1,49 @@
1use super::PacketHeader;
2
3#[repr(C, packed)]
4#[derive(Copy, Clone)]
5pub struct Cmd {
6 pub cmd_code: u16,
7 pub payload_len: u8,
8 pub payload: [u8; 255],
9}
10
11impl Default for Cmd {
12 fn default() -> Self {
13 Self {
14 cmd_code: 0,
15 payload_len: 0,
16 payload: [0u8; 255],
17 }
18 }
19}
20
21#[repr(C, packed)]
22#[derive(Copy, Clone, Default)]
23pub struct CmdSerial {
24 pub ty: u8,
25 pub cmd: Cmd,
26}
27
28#[repr(C, packed)]
29#[derive(Copy, Clone, Default)]
30pub struct CmdPacket {
31 pub header: PacketHeader,
32 pub cmd_serial: CmdSerial,
33}
34
35#[repr(C, packed)]
36#[derive(Copy, Clone)]
37pub struct AclDataSerial {
38 pub ty: u8,
39 pub handle: u16,
40 pub length: u16,
41 pub acl_data: [u8; 1],
42}
43
44#[repr(C, packed)]
45#[derive(Copy, Clone)]
46pub struct AclDataPacket {
47 pub header: PacketHeader,
48 pub acl_data_serial: AclDataSerial,
49}
diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs
new file mode 100644
index 000000000..4244db810
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/evt.rs
@@ -0,0 +1,8 @@
1/// the payload of [`Evt`] for a command status event
2#[derive(Copy, Clone)]
3#[repr(C, packed)]
4pub struct CsEvt {
5 pub status: u8,
6 pub num_cmd: u8,
7 pub cmd_code: u16,
8}
diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs
new file mode 100644
index 000000000..cf4797305
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/mm.rs
@@ -0,0 +1,30 @@
1use core::mem::MaybeUninit;
2
3use super::unsafe_linked_list::LST_init_head;
4use super::{
5 MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF,
6 TL_MEM_MANAGER_TABLE,
7};
8
9pub struct MemoryManager;
10
11impl MemoryManager {
12 pub fn new() -> Self {
13 unsafe {
14 LST_init_head(FREE_BUFF_QUEUE.as_mut_ptr());
15 LST_init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
16
17 TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable {
18 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
19 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
20 ble_pool: EVT_POOL.as_ptr().cast(),
21 ble_pool_size: POOL_SIZE as u32,
22 pevt_free_buffer_queue: FREE_BUFF_QUEUE.as_mut_ptr(),
23 traces_evt_pool: core::ptr::null(),
24 traces_pool_size: 0,
25 });
26 }
27
28 MemoryManager
29 }
30}
diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs
new file mode 100644
index 000000000..73d2ca6d6
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/mod.rs
@@ -0,0 +1,318 @@
1use core::mem::MaybeUninit;
2
3use bit_field::BitField;
4
5use self::ble::Ble;
6use self::cmd::{AclDataPacket, CmdPacket};
7use self::evt::CsEvt;
8use self::mm::MemoryManager;
9use self::sys::Sys;
10use self::unsafe_linked_list::LinkedListNode;
11use crate::ipcc::Ipcc;
12
13mod ble;
14mod channels;
15mod cmd;
16mod evt;
17mod mm;
18mod sys;
19mod unsafe_linked_list;
20
21pub type PacketHeader = LinkedListNode;
22
23const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::<PacketHeader>();
24const TL_EVT_HEADER_SIZE: usize = 3;
25const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>();
26
27const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5;
28const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255;
29const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE;
30
31const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4);
32
33const fn divc(x: usize, y: usize) -> usize {
34 (x + y - 1) / y
35}
36
37#[repr(C, packed)]
38#[derive(Copy, Clone)]
39pub struct SafeBootInfoTable {
40 version: u32,
41}
42
43#[repr(C, packed)]
44#[derive(Copy, Clone)]
45pub struct RssInfoTable {
46 version: u32,
47 memory_size: u32,
48 rss_info: u32,
49}
50
51/// # Version
52/// - 0 -> 3 = Build - 0: Untracked - 15:Released - x: Tracked version
53/// - 4 -> 7 = branch - 0: Mass Market - x: ...
54/// - 8 -> 15 = Subversion
55/// - 16 -> 23 = Version minor
56/// - 24 -> 31 = Version major
57/// # Memory Size
58/// - 0 -> 7 = Flash ( Number of 4k sector)
59/// - 8 -> 15 = Reserved ( Shall be set to 0 - may be used as flash extension )
60/// - 16 -> 23 = SRAM2b ( Number of 1k sector)
61/// - 24 -> 31 = SRAM2a ( Number of 1k sector)
62#[repr(C, packed)]
63#[derive(Copy, Clone)]
64pub struct WirelessFwInfoTable {
65 version: u32,
66 memory_size: u32,
67 thread_info: u32,
68 ble_info: u32,
69}
70
71impl WirelessFwInfoTable {
72 pub fn version_major(&self) -> u8 {
73 let version = self.version;
74 (version.get_bits(24..31) & 0xff) as u8
75 }
76
77 pub fn version_minor(&self) -> u8 {
78 let version = self.version;
79 (version.get_bits(16..23) & 0xff) as u8
80 }
81
82 pub fn subversion(&self) -> u8 {
83 let version = self.version;
84 (version.get_bits(8..15) & 0xff) as u8
85 }
86
87 /// size of FLASH, expressed in number of 4K sectors
88 pub fn flash_size(&self) -> u8 {
89 let memory_size = self.memory_size;
90 (memory_size.get_bits(0..7) & 0xff) as u8
91 }
92
93 /// size for SRAM2a, expressed in number of 1K sectors
94 pub fn sram2a_size(&self) -> u8 {
95 let memory_size = self.memory_size;
96 (memory_size.get_bits(24..31) & 0xff) as u8
97 }
98
99 /// size of SRAM2b, expressed in number of 1K sectors
100 pub fn sram2b_size(&self) -> u8 {
101 let memory_size = self.memory_size;
102 (memory_size.get_bits(16..23) & 0xff) as u8
103 }
104}
105
106#[repr(C, packed)]
107#[derive(Copy, Clone)]
108pub struct DeviceInfoTable {
109 pub safe_boot_info_table: SafeBootInfoTable,
110 pub rss_info_table: RssInfoTable,
111 pub wireless_fw_info_table: WirelessFwInfoTable,
112}
113
114#[repr(C, packed)]
115struct BleTable {
116 pcmd_buffer: *mut CmdPacket,
117 pcs_buffer: *const u8,
118 pevt_queue: *const u8,
119 phci_acl_data_buffer: *mut AclDataPacket,
120}
121
122#[repr(C, packed)]
123struct ThreadTable {
124 no_stack_buffer: *const u8,
125 cli_cmd_rsp_buffer: *const u8,
126 ot_cmd_rsp_buffer: *const u8,
127}
128
129#[repr(C, packed)]
130struct SysTable {
131 pcmd_buffer: *mut CmdPacket,
132 sys_queue: *const LinkedListNode,
133}
134
135#[allow(dead_code)] // Not used currently but reserved
136#[repr(C, packed)]
137struct LldTestTable {
138 cli_cmd_rsp_buffer: *const u8,
139 m0_cmd_buffer: *const u8,
140}
141
142#[allow(dead_code)] // Not used currently but reserved
143#[repr(C, packed)]
144struct BleLldTable {
145 cmd_rsp_buffer: *const u8,
146 m0_cmd_buffer: *const u8,
147}
148
149#[allow(dead_code)] // Not used currently but reserved
150#[repr(C, packed)]
151struct ZigbeeTable {
152 notif_m0_to_m4_buffer: *const u8,
153 appli_cmd_m4_to_m0_buffer: *const u8,
154 request_m0_to_m4_buffer: *const u8,
155}
156
157#[repr(C, packed)]
158struct MemManagerTable {
159 spare_ble_buffer: *const u8,
160 spare_sys_buffer: *const u8,
161
162 ble_pool: *const u8,
163 ble_pool_size: u32,
164
165 pevt_free_buffer_queue: *mut LinkedListNode,
166
167 traces_evt_pool: *const u8,
168 traces_pool_size: u32,
169}
170
171#[repr(C, packed)]
172struct TracesTable {
173 traces_queue: *const u8,
174}
175
176#[repr(C, packed)]
177struct Mac802_15_4Table {
178 pcmd_rsp_buffer: *const u8,
179 pnotack_buffer: *const u8,
180 evt_queue: *const u8,
181}
182
183/// reference table. Contains pointers to all other tables
184#[repr(C, packed)]
185#[derive(Copy, Clone)]
186pub struct RefTable {
187 pub device_info_table: *const DeviceInfoTable,
188 ble_table: *const BleTable,
189 thread_table: *const ThreadTable,
190 sys_table: *const SysTable,
191 mem_manager_table: *const MemManagerTable,
192 traces_table: *const TracesTable,
193 mac_802_15_4_table: *const Mac802_15_4Table,
194}
195
196#[link_section = "TL_REF_TABLE"]
197pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
198
199#[link_section = "MB_MEM1"]
200static mut TL_DEVICE_INFO_TABLE: MaybeUninit<DeviceInfoTable> = MaybeUninit::uninit();
201
202#[link_section = "MB_MEM1"]
203static mut TL_BLE_TABLE: MaybeUninit<BleTable> = MaybeUninit::uninit();
204
205#[link_section = "MB_MEM1"]
206static mut TL_THREAD_TABLE: MaybeUninit<ThreadTable> = MaybeUninit::uninit();
207
208#[link_section = "MB_MEM1"]
209static mut TL_SYS_TABLE: MaybeUninit<SysTable> = MaybeUninit::uninit();
210
211#[link_section = "MB_MEM1"]
212static mut TL_MEM_MANAGER_TABLE: MaybeUninit<MemManagerTable> = MaybeUninit::uninit();
213
214#[link_section = "MB_MEM1"]
215static mut TL_TRACES_TABLE: MaybeUninit<TracesTable> = MaybeUninit::uninit();
216
217#[link_section = "MB_MEM1"]
218static mut TL_MAC_802_15_4_TABLE: MaybeUninit<Mac802_15_4Table> = MaybeUninit::uninit();
219
220#[allow(dead_code)] // Not used currently but reserved
221#[link_section = "MB_MEM2"]
222static mut FREE_BUFF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
223
224// not in shared RAM
225static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
226
227#[allow(dead_code)] // Not used currently but reserved
228#[link_section = "MB_MEM2"]
229static mut TRACES_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
230
231#[link_section = "MB_MEM2"]
232static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> =
233 MaybeUninit::uninit();
234
235#[link_section = "MB_MEM2"]
236static mut EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
237
238#[link_section = "MB_MEM2"]
239static mut SYSTEM_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
240
241#[link_section = "MB_MEM2"]
242static mut SYS_CMD_BUF: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
243
244#[link_section = "MB_MEM2"]
245static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit();
246
247#[link_section = "MB_MEM2"]
248static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> =
249 MaybeUninit::uninit();
250
251#[link_section = "MB_MEM2"]
252static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> =
253 MaybeUninit::uninit();
254
255#[link_section = "MB_MEM2"]
256static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
257
258#[link_section = "MB_MEM2"]
259// "magic" numbers from ST ---v---v
260static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit();
261
262pub struct TlMbox {
263 _sys: Sys,
264 _ble: Ble,
265 _mm: MemoryManager,
266}
267
268impl TlMbox {
269 /// initializes low-level transport between CPU1 and BLE stack on CPU2
270 pub fn init(ipcc: &mut Ipcc) -> TlMbox {
271 unsafe {
272 TL_REF_TABLE = MaybeUninit::new(RefTable {
273 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
274 ble_table: TL_BLE_TABLE.as_ptr(),
275 thread_table: TL_THREAD_TABLE.as_ptr(),
276 sys_table: TL_SYS_TABLE.as_ptr(),
277 mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
278 traces_table: TL_TRACES_TABLE.as_ptr(),
279 mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
280 });
281
282 TL_SYS_TABLE = MaybeUninit::zeroed();
283 TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed();
284 TL_BLE_TABLE = MaybeUninit::zeroed();
285 TL_THREAD_TABLE = MaybeUninit::zeroed();
286 TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed();
287 TL_TRACES_TABLE = MaybeUninit::zeroed();
288 TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed();
289
290 EVT_POOL = MaybeUninit::zeroed();
291 SYS_SPARE_EVT_BUF = MaybeUninit::zeroed();
292 BLE_SPARE_EVT_BUF = MaybeUninit::zeroed();
293
294 CS_BUFFER = MaybeUninit::zeroed();
295 BLE_CMD_BUFFER = MaybeUninit::zeroed();
296 HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed();
297 }
298
299 ipcc.init();
300
301 let _sys = Sys::new(ipcc);
302 let _ble = Ble::new(ipcc);
303 let _mm = MemoryManager::new();
304
305 TlMbox { _sys, _ble, _mm }
306 }
307
308 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
309 let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table };
310
311 // zero version indicates that CPU2 wasn't active and didn't fill the information table
312 if info.version != 0 {
313 Some(*info)
314 } else {
315 None
316 }
317 }
318}
diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs
new file mode 100644
index 000000000..13ae7f9f9
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/sys.rs
@@ -0,0 +1,24 @@
1use core::mem::MaybeUninit;
2
3use super::unsafe_linked_list::LST_init_head;
4use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE};
5use crate::ipcc::Ipcc;
6
7pub struct Sys;
8
9impl Sys {
10 pub fn new(ipcc: &mut Ipcc) -> Self {
11 unsafe {
12 LST_init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
13
14 TL_SYS_TABLE = MaybeUninit::new(SysTable {
15 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
16 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
17 });
18 }
19
20 ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true);
21
22 Sys
23 }
24}
diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs
new file mode 100644
index 000000000..9caf01d1d
--- /dev/null
+++ b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs
@@ -0,0 +1,123 @@
1//! Unsafe linked list.
2//! Translated from ST's C by `c2rust` tool.
3
4#![allow(
5 dead_code,
6 mutable_transmutes,
7 non_camel_case_types,
8 non_snake_case,
9 non_upper_case_globals,
10 unused_assignments,
11 unused_mut
12)]
13
14use cortex_m::interrupt;
15
16#[derive(Copy, Clone)]
17#[repr(C, packed(4))]
18pub struct LinkedListNode {
19 pub next: *mut LinkedListNode,
20 pub prev: *mut LinkedListNode,
21}
22
23impl Default for LinkedListNode {
24 fn default() -> Self {
25 LinkedListNode {
26 next: core::ptr::null_mut(),
27 prev: core::ptr::null_mut(),
28 }
29 }
30}
31
32pub unsafe fn LST_init_head(mut listHead: *mut LinkedListNode) {
33 (*listHead).next = listHead;
34 (*listHead).prev = listHead;
35}
36
37pub unsafe fn LST_is_empty(mut listHead: *mut LinkedListNode) -> bool {
38 interrupt::free(|_| ((*listHead).next) == listHead)
39}
40
41pub unsafe fn LST_insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) {
42 interrupt::free(|_| {
43 (*node).next = (*listHead).next;
44 (*node).prev = listHead;
45 (*listHead).next = node;
46 (*(*node).next).prev = node;
47 });
48}
49
50pub unsafe fn LST_insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) {
51 interrupt::free(|_| {
52 (*node).next = listHead;
53 (*node).prev = (*listHead).prev;
54 (*listHead).prev = node;
55 (*(*node).prev).next = node;
56 });
57}
58
59pub unsafe fn LST_remove_node(mut node: *mut LinkedListNode) {
60 interrupt::free(|_| {
61 (*(*node).prev).next = (*node).next;
62 (*(*node).next).prev = (*node).prev;
63 });
64}
65
66pub unsafe fn LST_remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) {
67 interrupt::free(|_| {
68 *node = (*listHead).next;
69 LST_remove_node((*listHead).next);
70 });
71}
72
73pub unsafe fn LST_remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) {
74 interrupt::free(|_| {
75 *node = (*listHead).prev;
76 LST_remove_node((*listHead).prev);
77 });
78}
79
80pub unsafe fn LST_insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) {
81 interrupt::free(|_| {
82 (*node).next = (*ref_node).next;
83 (*node).prev = ref_node;
84 (*ref_node).next = node;
85 (*(*node).next).prev = node;
86 });
87}
88
89pub unsafe fn LST_insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) {
90 interrupt::free(|_| {
91 (*node).next = ref_node;
92 (*node).prev = (*ref_node).prev;
93 (*ref_node).prev = node;
94 (*(*node).prev).next = node;
95 });
96}
97
98pub unsafe fn LST_get_size(mut listHead: *mut LinkedListNode) -> usize {
99 interrupt::free(|_| {
100 let mut size = 0;
101 let mut temp: *mut LinkedListNode = core::ptr::null_mut::<LinkedListNode>();
102
103 temp = (*listHead).next;
104 while temp != listHead {
105 size += 1;
106 temp = (*temp).next
107 }
108
109 size
110 })
111}
112
113pub unsafe fn LST_get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) {
114 interrupt::free(|_| {
115 *node = (*ref_node).next;
116 });
117}
118
119pub unsafe fn LST_get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) {
120 interrupt::free(|_| {
121 *node = (*ref_node).prev;
122 });
123}
diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml
index 5d78d79e5..d23fdc513 100644
--- a/examples/stm32wb/.cargo/config.toml
+++ b/examples/stm32wb/.cargo/config.toml
@@ -1,6 +1,7 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list`
3runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" 3# runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset"
4runner = "teleprobe local run --chip STM32WB55RG --elf"
4 5
5[build] 6[build]
6target = "thumbv7em-none-eabihf" 7target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index db1816da3..3c7e3e874 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "exti"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x
index 0e48c916d..5a07b7d19 100644
--- a/examples/stm32wb/memory.x
+++ b/examples/stm32wb/memory.x
@@ -10,6 +10,17 @@ MEMORY
10 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K 10 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
11} 11}
12 12
13/*
14 Memory size for STM32WB55xC with 512K FLASH
15
16 MEMORY
17 {
18 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
19 RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
20 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
21 }
22*/
23
13/* Place stack at the end of SRAM1 */ 24/* Place stack at the end of SRAM1 */
14_stack_start = ORIGIN(RAM) + LENGTH(RAM); 25_stack_start = ORIGIN(RAM) + LENGTH(RAM);
15 26
@@ -19,23 +30,6 @@ _stack_start = ORIGIN(RAM) + LENGTH(RAM);
19SECTIONS { 30SECTIONS {
20 TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED 31 TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED
21 32
22 TL_DEVICE_INFO_TABLE 0x2003001c (NOLOAD) : { *(TL_DEVICE_INFO_TABLE) } >RAM_SHARED 33 MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
23 TL_BLE_TABLE 0x2003003c (NOLOAD) : { *(TL_BLE_TABLE) } >RAM_SHARED 34 MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
24 TL_THREAD_TABLE 0x2003004c (NOLOAD) : { *(TL_THREAD_TABLE) } >RAM_SHARED
25 TL_SYS_TABLE 0x20030058 (NOLOAD) : { *(TL_SYS_TABLE) } >RAM_SHARED
26 TL_MEM_MANAGER_TABLE 0x20030060 (NOLOAD) : { *(TL_MEM_MANAGER_TABLE) } >RAM_SHARED
27 TL_TRACES_TABLE 0x2003007c (NOLOAD) : { *(TL_TRACES_TABLE) } >RAM_SHARED
28 TL_MAC_802_15_4_TABLE 0x20030080 (NOLOAD) : { *(TL_MAC_802_15_4_TABLE) } >RAM_SHARED
29
30 HCI_ACL_DATA_BUFFER 0x20030a08 (NOLOAD) : { *(HCI_ACL_DATA_BUFFER) } >RAM_SHARED
31 BLE_CMD_BUFFER 0x200308fc (NOLOAD) : { *(BLE_CMD_BUFFER) } >RAM_SHARED
32 BLE_SPARE_EVT_BUF 0x200301a8 (NOLOAD) : { *(BLE_SPARE_EVT_BUF) } >RAM_SHARED
33 SYS_SPARE_EVT_BUF 0x200302b4 (NOLOAD) : { *(SYS_SPARE_EVT_BUF) } >RAM_SHARED
34 EVT_POOL 0x200303c0 (NOLOAD) : { *(EVT_POOL) } >RAM_SHARED
35 SYS_CMD_BUF 0x2003009c (NOLOAD) : { *(SYS_CMD_BUF) } >RAM_SHARED
36 SYSTEM_EVT_QUEUE 0x20030b28 (NOLOAD) : { *(SYSTEM_EVT_QUEUE) } >RAM_SHARED
37 EVT_QUEUE 0x20030b10 (NOLOAD) : { *(EVT_QUEUE) } >RAM_SHARED
38 CS_BUFFER 0x20030b18 (NOLOAD) : { *(CS_BUFFER) } >RAM_SHARED
39 TRACES_EVT_QUEUE 0x20030094 (NOLOAD) : { *(TRACES_EVT_QUEUE) } >RAM_SHARED
40 FREE_BUF_QUEUE 0x2003008c (NOLOAD) : { *(FREE_BUF_QUEUE) } >RAM_SHARED
41} 35}
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs
new file mode 100644
index 000000000..6876526ae
--- /dev/null
+++ b/examples/stm32wb/src/bin/tl_mbox.rs
@@ -0,0 +1,68 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::ipcc::{Config, Ipcc};
8use embassy_stm32::tl_mbox::TlMbox;
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 /*
15 How to make this work:
16
17 - Obtain a NUCLEO-STM32WB55 from your preferred supplier.
18 - Download and Install STM32CubeProgrammer.
19 - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from
20 gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
21 - Open STM32CubeProgrammer
22 - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware.
23 - Once complete, click connect to connect to the device.
24 - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services".
25 - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file
26 - Select that file, the memory address, "verify download", and then "Firmware Upgrade".
27 - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the
28 stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address.
29 - Select that file, the memory address, "verify download", and then "Firmware Upgrade".
30 - Disconnect from the device.
31 - In the examples folder for stm32wb, modify the memory.x file to match your target device.
32 - Run this example.
33
34 Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name.
35 */
36
37 let p = embassy_stm32::init(Default::default());
38 info!("Hello World!");
39
40 let config = Config::default();
41 let mut ipcc = Ipcc::new(p.IPCC, config);
42
43 let mbox = TlMbox::init(&mut ipcc);
44
45 loop {
46 let wireless_fw_info = mbox.wireless_fw_info();
47 match wireless_fw_info {
48 None => error!("not yet initialized"),
49 Some(fw_info) => {
50 let version_major = fw_info.version_major();
51 let version_minor = fw_info.version_minor();
52 let subversion = fw_info.subversion();
53
54 let sram2a_size = fw_info.sram2a_size();
55 let sram2b_size = fw_info.sram2b_size();
56
57 info!(
58 "version {}.{}.{} - SRAM2a {} - SRAM2b {}",
59 version_major, version_minor, subversion, sram2a_size, sram2b_size
60 );
61
62 break;
63 }
64 }
65
66 Timer::after(Duration::from_millis(500)).await;
67 }
68}
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index 83bf1e9c9..bd8d90abe 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -18,6 +18,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
18 18
19sdmmc = [] 19sdmmc = []
20chrono = ["embassy-stm32/chrono", "dep:chrono"] 20chrono = ["embassy-stm32/chrono", "dep:chrono"]
21ble = []
21not-gpdma = [] 22not-gpdma = []
22 23
23[dependencies] 24[dependencies]
@@ -44,6 +45,11 @@ chrono = { version = "^0.4", default-features = false, optional = true}
44# BEGIN TESTS 45# BEGIN TESTS
45# Generated by gen_test.py. DO NOT EDIT. 46# Generated by gen_test.py. DO NOT EDIT.
46[[bin]] 47[[bin]]
48name = "ble"
49path = "src/bin/ble.rs"
50required-features = [ "ble",]
51
52[[bin]]
47name = "gpio" 53name = "gpio"
48path = "src/bin/gpio.rs" 54path = "src/bin/gpio.rs"
49required-features = [] 55required-features = []
diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs
new file mode 100644
index 000000000..f4c864c65
--- /dev/null
+++ b/tests/stm32/src/bin/ble.rs
@@ -0,0 +1,51 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5// required-features: ble
6
7#[path = "../example_common.rs"]
8mod example_common;
9use embassy_executor::Spawner;
10use embassy_stm32::ipcc::{Config, Ipcc};
11use embassy_stm32::tl_mbox::TlMbox;
12use embassy_time::{Duration, Timer};
13use example_common::*;
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let p = embassy_stm32::init(config());
18 info!("Hello World!");
19
20 let config = Config::default();
21 let mut ipcc = Ipcc::new(p.IPCC, config);
22
23 let mbox = TlMbox::init(&mut ipcc);
24
25 loop {
26 let wireless_fw_info = mbox.wireless_fw_info();
27 match wireless_fw_info {
28 None => error!("not yet initialized"),
29 Some(fw_info) => {
30 let version_major = fw_info.version_major();
31 let version_minor = fw_info.version_minor();
32 let subversion = fw_info.subversion();
33
34 let sram2a_size = fw_info.sram2a_size();
35 let sram2b_size = fw_info.sram2b_size();
36
37 info!(
38 "version {}.{}.{} - SRAM2a {} - SRAM2b {}",
39 version_major, version_minor, subversion, sram2a_size, sram2b_size
40 );
41
42 break;
43 }
44 }
45
46 Timer::after(Duration::from_millis(500)).await;
47 }
48
49 info!("Test OK");
50 cortex_m::asm::bkpt();
51}