Merge pull request #3703 from loftyinclination/main
add documentation for the BLE feature of the embassy-stm32-wpan crate
This commit is contained in:
		
						commit
						e7c3e1b266
					
				| @ -13,10 +13,10 @@ documentation = "https://docs.embassy.dev/embassy-stm32-wpan" | |||||||
| src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" | ||||||
| src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" | ||||||
| target = "thumbv7em-none-eabihf" | target = "thumbv7em-none-eabihf" | ||||||
| features = ["stm32wb55rg"] | features = ["stm32wb55rg", "ble", "mac"] | ||||||
| 
 | 
 | ||||||
| [package.metadata.docs.rs] | [package.metadata.docs.rs] | ||||||
| features = ["stm32wb55rg"] | features = ["stm32wb55rg", "ble", "mac"] | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } | ||||||
|  | |||||||
| @ -69,7 +69,7 @@ pub const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>(); | |||||||
|  * enough to store all asynchronous events received in between. |  * enough to store all asynchronous events received in between. | ||||||
|  * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events |  * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events | ||||||
|  * between the HCI command and its event. |  * between the HCI command and its event. | ||||||
|  * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, |  * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is too small, | ||||||
|  * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting |  * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting | ||||||
|  * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate |  * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate | ||||||
|  * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). |  * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). | ||||||
|  | |||||||
| @ -1,3 +1,16 @@ | |||||||
|  | //! The embassy-stm32-wpan crate aims to provide safe use of the commands necessary to interface
 | ||||||
|  | //! with the Cortex C0 CPU2 coprocessor of STM32WB microcontrollers. It implements safe wrappers
 | ||||||
|  | //! around the Transport Layer, and in particular the system, memory, BLE and Mac channels.
 | ||||||
|  | //!
 | ||||||
|  | //! # Design
 | ||||||
|  | //!
 | ||||||
|  | //! This crate loosely follows the Application Note 5289 "How to build wireless applications with
 | ||||||
|  | //! STM32WB MCUs"; several of the startup procedures laid out in Annex 14.1 are implemented using
 | ||||||
|  | //! inline copies of the code contained within the `stm32wb_copro` C library.
 | ||||||
|  | //!
 | ||||||
|  | //! BLE commands are implemented via use of the [stm32wb_hci] crate, for which the
 | ||||||
|  | //! [stm32wb_hci::Controller] trait has been implemented.
 | ||||||
|  | 
 | ||||||
| #![no_std] | #![no_std] | ||||||
| #![allow(async_fn_in_trait)] | #![allow(async_fn_in_trait)] | ||||||
| #![doc = include_str!("../README.md")] | #![doc = include_str!("../README.md")] | ||||||
| @ -37,6 +50,7 @@ pub use crate::sub::ble::hci; | |||||||
| 
 | 
 | ||||||
| type PacketHeader = LinkedListNode; | type PacketHeader = LinkedListNode; | ||||||
| 
 | 
 | ||||||
|  | /// Transport Layer for the Mailbox interface
 | ||||||
| pub struct TlMbox<'d> { | pub struct TlMbox<'d> { | ||||||
|     _ipcc: PeripheralRef<'d, IPCC>, |     _ipcc: PeripheralRef<'d, IPCC>, | ||||||
| 
 | 
 | ||||||
| @ -49,6 +63,34 @@ pub struct TlMbox<'d> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'d> TlMbox<'d> { | impl<'d> TlMbox<'d> { | ||||||
|  |     /// Initialise the Transport Layer, and creates and returns a wrapper around it.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
 | ||||||
|  |     /// from the implementation documented in Figure 64, to avoid needing to reference any C
 | ||||||
|  |     /// function pointers.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Annex 14.1 lays out the following methods that should be called:
 | ||||||
|  |     ///     1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
 | ||||||
|  |     ///        and CPU2.
 | ||||||
|  |     ///     2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
 | ||||||
|  |     ///        calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
 | ||||||
|  |     ///     3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
 | ||||||
|  |     ///        manager commands.
 | ||||||
|  |     ///     4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
 | ||||||
|  |     /// This implementation initialises all of the shared refernce tables and all IPCC channel that
 | ||||||
|  |     /// would be initialised by this process. The developer should therefore treat this method as
 | ||||||
|  |     /// completing all steps in Figure 64.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Once this method has been called, no system commands may be sent until the CPU2 ready
 | ||||||
|  |     /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
 | ||||||
|  |     /// Figure 65.
 | ||||||
|  |     ///
 | ||||||
|  |     /// If the `ble` feature is enabled, at this point, the user should call
 | ||||||
|  |     /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
 | ||||||
|  |     /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
 | ||||||
|  |     /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
 | ||||||
|  |     /// Figure 66.
 | ||||||
|  |     // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
 | ||||||
|     pub fn init( |     pub fn init( | ||||||
|         ipcc: impl Peripheral<P = IPCC> + 'd, |         ipcc: impl Peripheral<P = IPCC> + 'd, | ||||||
|         _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> |         _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | ||||||
| @ -57,6 +99,9 @@ impl<'d> TlMbox<'d> { | |||||||
|     ) -> Self { |     ) -> Self { | ||||||
|         into_ref!(ipcc); |         into_ref!(ipcc); | ||||||
| 
 | 
 | ||||||
|  |         // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
 | ||||||
|  |         // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
 | ||||||
|  |         // implementation
 | ||||||
|         unsafe { |         unsafe { | ||||||
|             TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { |             TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { | ||||||
|                 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), |                 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), | ||||||
| @ -140,6 +185,7 @@ impl<'d> TlMbox<'d> { | |||||||
| 
 | 
 | ||||||
|         compiler_fence(Ordering::SeqCst); |         compiler_fence(Ordering::SeqCst); | ||||||
| 
 | 
 | ||||||
|  |         // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
 | ||||||
|         Ipcc::enable(config); |         Ipcc::enable(config); | ||||||
| 
 | 
 | ||||||
|         Self { |         Self { | ||||||
|  | |||||||
| @ -11,11 +11,39 @@ use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA | |||||||
| use crate::unsafe_linked_list::LinkedListNode; | use crate::unsafe_linked_list::LinkedListNode; | ||||||
| use crate::{channels, evt}; | use crate::{channels, evt}; | ||||||
| 
 | 
 | ||||||
|  | /// A guard that, once constructed, may be used to send BLE commands to CPU2.
 | ||||||
|  | ///
 | ||||||
|  | /// It is the responsibility of the caller to ensure that they have awaited an event via
 | ||||||
|  | /// [crate::sub::sys::Sys::read] before sending any of these commands, and to call
 | ||||||
|  | /// [crate::sub::sys::Sys::shci_c2_ble_init] and await the HCI_COMMAND_COMPLETE_EVENT before
 | ||||||
|  | /// sending any other commands.
 | ||||||
|  | ///
 | ||||||
|  | /// # Example
 | ||||||
|  | ///
 | ||||||
|  | /// ```
 | ||||||
|  | /// # embassy_stm32::bind_interrupts!(struct Irqs{
 | ||||||
|  | /// #     IPCC_C1_RX => ReceiveInterruptHandler;
 | ||||||
|  | /// #     IPCC_C1_TX => TransmitInterruptHandler;
 | ||||||
|  | /// # });
 | ||||||
|  | /// #
 | ||||||
|  | /// # let p = embassy_stm32::init(embassy_stm32::Config::default());
 | ||||||
|  | /// # let mut mbox = embassy_stm32_wpan::TlMbox::init(p.IPCC, Irqs, embassy_stm32::ipcc::Config::default());
 | ||||||
|  | /// #
 | ||||||
|  | /// # let sys_event = mbox.sys_subsystem.read().await;
 | ||||||
|  | /// # let _command_status = mbox.sys_subsystem.shci_c2_ble_init(Default::default());
 | ||||||
|  | /// # // BLE commands may now be sent
 | ||||||
|  | /// #
 | ||||||
|  | /// # mbox.ble_subsystem.reset().await;
 | ||||||
|  | /// # let _reset_response = mbox.ble_subsystem.read().await;
 | ||||||
|  | /// ```
 | ||||||
| pub struct Ble { | pub struct Ble { | ||||||
|     _private: (), |     _private: (), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Ble { | impl Ble { | ||||||
|  |     /// Constructs a guard that allows for BLE commands to be sent to CPU2.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.
 | ||||||
|     pub(crate) fn new() -> Self { |     pub(crate) fn new() -> Self { | ||||||
|         unsafe { |         unsafe { | ||||||
|             LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); |             LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); | ||||||
| @ -30,6 +58,7 @@ impl Ble { | |||||||
| 
 | 
 | ||||||
|         Self { _private: () } |         Self { _private: () } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     /// `HW_IPCC_BLE_EvtNot`
 |     /// `HW_IPCC_BLE_EvtNot`
 | ||||||
|     pub async fn tl_read(&self) -> EvtBox<Self> { |     pub async fn tl_read(&self) -> EvtBox<Self> { | ||||||
|         Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { |         Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ use crate::tables::{SysTable, WirelessFwInfoTable}; | |||||||
| use crate::unsafe_linked_list::LinkedListNode; | use crate::unsafe_linked_list::LinkedListNode; | ||||||
| use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; | use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; | ||||||
| 
 | 
 | ||||||
|  | /// A guard that, once constructed, allows for sys commands to be sent to CPU2.
 | ||||||
| pub struct Sys { | pub struct Sys { | ||||||
|     _private: (), |     _private: (), | ||||||
| } | } | ||||||
| @ -86,12 +87,22 @@ impl Sys { | |||||||
|         self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await |         self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Send a request to CPU2 to initialise the BLE stack.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This must be called before any BLE commands are sent via the BLE channel (according to
 | ||||||
|  |     /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via
 | ||||||
|  |     /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka
 | ||||||
|  |     /// [crate::sub::ble::hci::host::uart::UartHci::read].
 | ||||||
|     #[cfg(feature = "ble")] |     #[cfg(feature = "ble")] | ||||||
|     pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { |     pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { | ||||||
|         self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await |         self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// `HW_IPCC_SYS_EvtNot`
 |     /// `HW_IPCC_SYS_EvtNot`
 | ||||||
|  |     ///
 | ||||||
|  |     /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,
 | ||||||
|  |     /// as the embassy implementation avoids the need to call C public bindings, and instead
 | ||||||
|  |     /// handles the event channels directly.
 | ||||||
|     pub async fn read(&self) -> EvtBox<mm::MemoryManager> { |     pub async fn read(&self) -> EvtBox<mm::MemoryManager> { | ||||||
|         Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { |         Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { | ||||||
|             if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { |             if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { | ||||||
|  | |||||||
| @ -25,17 +25,17 @@ pub struct RssInfoTable { | |||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Version |  * Version | ||||||
|  * [0:3]   = Build - 0: Untracked - 15:Released - x: Tracked version |  * \[0:3\]   = Build - 0: Untracked - 15:Released - x: Tracked version | ||||||
|  * [4:7]   = branch - 0: Mass Market - x: ... |  * \[4:7\]   = branch - 0: Mass Market - x: ... | ||||||
|  * [8:15]  = Subversion |  * \[8:15\]  = Subversion | ||||||
|  * [16:23] = Version minor |  * \[16:23\] = Version minor | ||||||
|  * [24:31] = Version major |  * \[24:31\] = Version major | ||||||
|  * |  * | ||||||
|  * Memory Size |  * Memory Size | ||||||
|  * [0:7]   = Flash ( Number of 4k sector) |  * \[0:7\]   = Flash ( Number of 4k sector) | ||||||
|  * [8:15]  = Reserved ( Shall be set to 0 - may be used as flash extension ) |  * \[8:15\]  = Reserved ( Shall be set to 0 - may be used as flash extension ) | ||||||
|  * [16:23] = SRAM2b ( Number of 1k sector) |  * \[16:23\] = SRAM2b ( Number of 1k sector) | ||||||
|  * [24:31] = SRAM2a ( Number of 1k sector) |  * \[24:31\] = SRAM2a ( Number of 1k sector) | ||||||
|  */ |  */ | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug, Copy, Clone)] | ||||||
| #[repr(C, packed)] | #[repr(C, packed)] | ||||||
| @ -89,12 +89,19 @@ pub struct DeviceInfoTable { | |||||||
|     pub wireless_fw_info_table: WirelessFwInfoTable, |     pub wireless_fw_info_table: WirelessFwInfoTable, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// The bluetooth reference table, as defined in figure 67 of STM32WX AN5289.
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| #[repr(C)] | #[repr(C)] | ||||||
| pub struct BleTable { | pub struct BleTable { | ||||||
|  |     /// A pointer to the buffer that is used for sending BLE commands.
 | ||||||
|     pub pcmd_buffer: *mut CmdPacket, |     pub pcmd_buffer: *mut CmdPacket, | ||||||
|  |     /// A pointer to the buffer used for storing Command statuses.
 | ||||||
|     pub pcs_buffer: *const u8, |     pub pcs_buffer: *const u8, | ||||||
|  |     /// A pointer to the event queue, over which IPCC BLE events are sent. This may be accessed via
 | ||||||
|  |     /// [crate::sub::ble::Ble::tl_read].
 | ||||||
|     pub pevt_queue: *const u8, |     pub pevt_queue: *const u8, | ||||||
|  |     /// A pointer to the buffer that is used for sending HCI (Host-Controller Interface) ACL
 | ||||||
|  |     /// (Asynchronous Connection-oriented Logical transport) commands (unused).
 | ||||||
|     pub phci_acl_data_buffer: *mut AclDataPacket, |     pub phci_acl_data_buffer: *mut AclDataPacket, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -151,11 +151,6 @@ async fn main(_spawner: Spawner) { | |||||||
|     let response = mbox.ble_subsystem.read().await; |     let response = mbox.ble_subsystem.read().await; | ||||||
|     defmt::debug!("{}", response); |     defmt::debug!("{}", response); | ||||||
| 
 | 
 | ||||||
|     info!("set scan response data..."); |  | ||||||
|     mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); |  | ||||||
|     let response = mbox.ble_subsystem.read().await; |  | ||||||
|     defmt::debug!("{}", response); |  | ||||||
| 
 |  | ||||||
|     defmt::info!("initializing services and characteristics..."); |     defmt::info!("initializing services and characteristics..."); | ||||||
|     let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); |     let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); | ||||||
|     defmt::info!("{}", ble_context); |     defmt::info!("{}", ble_context); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user