### Initialize and Run nRF SoftDevice Controller Source: https://github.com/alexmoon/nrf-sdc/blob/main/nrf-sdc/README.md This example demonstrates the complete setup and initialization process for the nRF SoftDevice Controller in an async Rust application. It includes necessary imports, interrupt binding, peripheral configuration for both MPSL and SDC, and spawning of their respective tasks. Ensure all required peripherals are correctly passed during initialization. ```rust #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; use embassy_nrf as _; use embassy_nrf::bind_interrupts; use embassy_nrf::rng::Rng; use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals as MpslPeripherals, raw as mpsl_raw}; use nrf_sdc::{self as sdc, raw as sdc_raw, SoftdeviceController, Peripherals as SdcPeripherals}; use static_cell::StaticCell; bind_interrupts!(struct Irqs { SWI0_EGU0 => nrf_mpsl::LowPrioInterruptHandler; POWER_CLOCK => nrf_mpsl::ClockInterruptHandler; RADIO => nrf_mpsl::HighPrioInterruptHandler; TIMER0 => nrf_mpsl::HighPrioInterruptHandler; RTC0 => nrf_mpsl::HighPrioInterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; }); #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let p = embassy_nrf::init(Default::default()); // Create the clock configuration let lfclk_cfg = mpsl_raw::mpsl_clock_lfclk_cfg_t { source: mpsl_raw::MPSL_CLOCK_LF_SRC_RC as u8, rc_ctiv: 16, rc_temp_ctiv: 2, accuracy_ppm: mpsl_raw::MPSL_CLOCK_LF_ACCURACY_500_PPM as u16, }; // On nrf52 chips, the peripherals needed by MPSL are: // RTC0, TIMER0, TEMP, PPI_CH19, PPI_CH30, PPI_CH31 // The list of peripherals is different for other chips. let mpsl_p = MpslPeripherals::new( p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31, ); // Initialize the MPSL static MPSL: StaticCell = StaticCell::new(); let mpsl = MPSL.init(MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg).unwrap()); // On nrf52 chips, the peripherals needed by SDC are: // PPI_CH17, PPI_CH18, PPI_CH20..=PPI_CH29 // The list of peripherals is different for other chips. let sdc_p = SdcPeripherals::new( p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, p.PPI_CH25, p.PPI_CH26, p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, ); static RNG: StaticCell> = StaticCell::new(); let mut rng = RNG.init(Rng::new(p.RNG, Irqs)); // The minimum memory required for the SoftDevice Controller to run. const SDC_MEM_SIZE: usize = sdc_raw::SDC_MEM_SIZE_MIN as usize; static SDC_MEM: StaticCell> = StaticCell::new(); // Initialize the SoftDevice Controller let sdc = sdc::Builder::new() .unwrap() .support_adv() .unwrap() .support_peripheral() .unwrap() .build(sdc_p, &mut rng, mpsl, SDC_MEM.init(sdc::Mem::new())) .unwrap(); // Spawn the MPSL and SDC tasks spawner.must_spawn(mpsl_task(mpsl)); spawner.must_spawn(sdc_task(sdc)); // Your application logic can go here. loop { // Do something } } #[embassy_executor::task] async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer) -> ! { mpsl.run().await } #[embassy_executor::task] async fn sdc_task(sdc: &'static SoftdeviceController) -> ! { loop { let mut evt_buf = [0; sdc_raw::HCI_MSG_BUFFER_MAX_SIZE as usize]; match sdc.hci_get(&mut evt_buf).await { Ok(_event) => { // Handle Bluetooth events } Err(e) => { // Handle errors core::panic!("sdc_task error: {:?}", e) } } } } #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } ``` -------------------------------- ### Configure Bluetooth with MPSL and SDC Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/configuration.md This snippet shows a complete configuration example for Bluetooth using MPSL and SDC. It includes MPSL clock configuration and SDC builder setup for central, peripheral, and advertising roles, along with buffer configurations. It also demonstrates how to calculate and print the required memory for the SDC configuration. ```rust use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals as MpslPeripherals, raw as mpsl_raw}; use nrf_sdc::{SoftdeviceController, Builder, Peripherals as SdcPeripherals}; fn configure_bluetooth() -> Result<(), Box> { // MPSL Configuration let lfclk_cfg = mpsl_raw::mpsl_clock_lfclk_cfg_t { source: mpsl_raw::MPSL_CLOCK_LF_SRC_XTAL as u8, rc_ctiv: 0, rc_temp_ctiv: 0, accuracy_ppm: mpsl_raw::MPSL_CLOCK_LF_ACCURACY_250_PPM as u16, }; // SDC Configuration let sdc_config = Builder::new()? .central_count(2)? .peripheral_count(2)? .buffer_cfg(251, 251, 2, 2)? .adv_count(1)? .adv_buffer_cfg(251)? .default_tx_power(4)?; // Calculate memory requirement let required_mem = sdc_config.required_memory()?; println!("SDC requires {} bytes", required_mem); Ok(()) } ``` -------------------------------- ### Mem Constructor: new() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Memory.md Example demonstrating how to create a new uninitialized memory buffer for the SoftDevice Controller. ```rust const SDC_MEM_SIZE: usize = 2048; let mem = Mem::::new(); ``` -------------------------------- ### From for RetVal Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ErrorHandling.md Demonstrates automatic conversion from an i32 to a RetVal. ```rust let ret: RetVal = unsafe { raw::sdc_init(None) }.into(); ``` -------------------------------- ### RetVal::new() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ErrorHandling.md Demonstrates creating a RetVal instance. ```rust let ret = RetVal::new(0); // Success ``` -------------------------------- ### Build Command with Feature Flags Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/UsagePatterns.md Example of how to build the project with specific Cargo features enabled. ```bash cargo build --features peripheral-only ``` -------------------------------- ### SessionMem Constructor: new() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Memory.md Example demonstrating the creation of a SessionMem buffer and its use in initializing the MultiprotocolServiceLayer with timeslot capabilities. ```rust const TIMESLOT_COUNT: usize = 1; static SESSION_MEM: StaticCell> = StaticCell::new(); let mem = SESSION_MEM.init(SessionMem::new()); let mpsl = MultiprotocolServiceLayer::with_timeslots( mpsl_p, Irqs, lfclk_cfg, &mut mem, )?; ``` -------------------------------- ### Complete Example: Requesting and Using Hfclk Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/HighFrequencyClock.md A full example showing how to request the high-frequency clock within an async task and perform operations that require it. The guard is automatically released when the task scope ends. ```rust use embassy_executor::Spawner; use nrf_mpsl::{MultiprotocolServiceLayer, Hfclk}; #[embassy_executor::task] async fn some_operation(mpsl: &'static MultiprotocolServiceLayer) -> Result<(), Box> { // Request and wait for high-frequency clock let _hfclk = mpsl.request_hfclk().await?; // Perform operation that requires HF clock // (e.g., high-speed communication, precise timing) // Guard is automatically dropped at end of scope // Clock may be released by MPSL if not needed Ok(()) } ``` -------------------------------- ### nRF52 Series Peripherals Initialization Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Peripherals.md Demonstrates how to initialize the Peripherals structure for the nRF52 series using provided peripheral instances. ```rust let mpsl_p = Peripherals::new( p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31, ); ``` -------------------------------- ### Get Temperature Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/types.md Demonstrates how to retrieve the current temperature using the Temperature type and print it in degrees Celsius. ```rust let temp: Temperature = mpsl.get_temperature(); println!("{}°C", temp.degrees()); ``` -------------------------------- ### Mem Constructor: default() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Memory.md Example showing how to initialize a Mem buffer using the Default trait. ```rust let mem: Mem<2048> = Default::default(); ``` -------------------------------- ### Minimal Imports for nrf-sdc and nrf-mpsl Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Shows the essential imports needed to use the SoftdeviceController and MultiprotocolServiceLayer. This is the most basic setup. ```rust use nrf_sdc::SoftdeviceController; use nrf_mpsl::MultiprotocolServiceLayer; ``` -------------------------------- ### Initialize and Run MPSL Source: https://github.com/alexmoon/nrf-sdc/blob/main/nrf-mpsl/README.md Example demonstrating how to initialize and run the MPSL in an application. This includes registering interrupt handlers, configuring the low-frequency clock, and initializing the MPSL with necessary peripherals. ```rust #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; use embassy_nrf::bind_interrupts; use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals, raw}; use static_cell::StaticCell; // This is where we register the interrupt handlers for the MPSL bind_interrupts!(struct Irqs { SWI0_EGU0 => nrf_mpsl::LowPrioInterruptHandler; POWER_CLOCK => nrf_mpsl::ClockInterruptHandler; RADIO => nrf_mpsl::HighPrioInterruptHandler; TIMER0 => nrf_mpsl::HighPrioInterruptHandler; RTC0 => nrf_mpsl::HighPrioInterruptHandler; }); #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let p = embassy_nrf::init(Default::default()); // Create the clock configuration let lfclk_cfg = raw::mpsl_clock_lfclk_cfg_t { source: raw::MPSL_CLOCK_LF_SRC_RC as u8, rc_ctiv: 16, rc_temp_ctiv: 2, accuracy_ppm: raw::MPSL_CLOCK_LF_ACCURACY_500_PPM as u16, }; // On nrf52 chips, the peripherals needed by MPSL are: // RTC0, TIMER0, TEMP, PPI_CH19, PPI_CH30, PPI_CH31 // The list of peripherals is different for other chips. let mpsl_p = Peripherals::new( p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31, ); // Initialize the MPSL static MPSL: StaticCell = StaticCell::new(); let mpsl = MPSL.init(MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg).unwrap()); // Spawn the MPSL task spawner.must_spawn(mpsl_task(mpsl)); // Your application logic can go here. loop { // Do something } } #[embassy_executor::task] async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer) -> ! { mpsl.run().await } #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } ``` -------------------------------- ### Acquiring Temperature Readings Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Demonstrates how to get a temperature reading from the MPSL instance. ```APIDOC ## Acquisition The temperature sensor is accessed through the MPSL instance: ```rust pub fn get_temperature(&self) -> Temperature ``` **Example:** ```rust let temp_sensor = mpsl.get_temperature(); println!("Integer: {}°C", temp_sensor.degrees()); println!("Fractional: {}m°C", temp_sensor.millidegrees()); ``` ``` -------------------------------- ### Execute ZephyrReadVersionInfo Command Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/VendorCommands.md Example of executing the `ZephyrReadVersionInfo` command and printing the retrieved hardware and firmware version information. Requires the `ControllerCmdSync` trait. ```rust use bt_hci::controller::ControllerCmdSync; let info = controller.exec(&ZephyrReadVersionInfo).await?; println!("Platform: {}, Variant: {}", info.hw_platform, info.hw_variant); println!("FW Version: {}.{}", info.fw_version, info.fw_revision); ``` -------------------------------- ### Create SoftDevice Controller Builder Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Creates a new builder for configuring the SoftDevice Controller. Use this to start setting up the SDC. ```rust let builder = Builder::new()?; ``` -------------------------------- ### Complete Setup Imports for nrf-sdc and nrf-mpsl Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Provides a comprehensive set of imports for configuring and using nrf-sdc and nrf-mpsl, including vendor commands and specific peripherals. ```rust use nrf_sdc::{SoftdeviceController, Builder, Peripherals as SdcPeripherals, Mem}; use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals as MpslPeripherals, Error, RetVal}; use nrf_sdc::vendor::*; ``` -------------------------------- ### Instantiate nRF52 Peripherals for SDC Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Peripherals.md Example of how to instantiate the Peripherals struct for the nRF52 series, passing the required PPI channels. This is used when setting up hardware resources for the SoftDevice Controller. ```rust let sdc_p = Peripherals::new( p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, p.PPI_CH25, p.PPI_CH26, p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, ); ``` -------------------------------- ### RetVal::to_result() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ErrorHandling.md Shows converting a raw C function return value to a Rust Result. ```rust let ret = unsafe { raw::sdc_init(None) }; let result = RetVal::from(ret).to_result()?; ``` -------------------------------- ### Standard Initialization Sequence Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/UsagePatterns.md Demonstrates the typical initialization sequence for nrf-sdc and nrf-mpsl, including setting up interrupts, embassy, MPSL clock, MPSL, RNG, and SDC. This is the primary pattern for starting the services. ```rust #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; use embassy_nrf::bind_interrupts; use embassy_nrf::rng::Rng; use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals as MpslPeripherals, raw as mpsl_raw}; use nrf_sdc::{SoftdeviceController, Builder, Peripherals as SdcPeripherals}; use static_cell::StaticCell; // 1. Define interrupt handlers bind_interrupts!(struct Irqs { SWI0_EGU0 => nrf_mpsl::LowPrioInterruptHandler; POWER_CLOCK => nrf_mpsl::ClockInterruptHandler; RADIO => nrf_mpsl::HighPrioInterruptHandler; TIMER0 => nrf_mpsl::HighPrioInterruptHandler; RTC0 => nrf_mpsl::HighPrioInterruptHandler; }); #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { // 2. Initialize embassy let p = embassy_nrf::init(Default::default()); // 3. Configure MPSL clock let lfclk_cfg = mpsl_raw::mpsl_clock_lfclk_cfg_t { source: mpsl_raw::MPSL_CLOCK_LF_SRC_XTAL as u8, rc_ctiv: 0, rc_temp_ctiv: 0, accuracy_ppm: mpsl_raw::MPSL_CLOCK_LF_ACCURACY_250_PPM as u16, }; // 4. Initialize MPSL let mpsl_p = MpslPeripherals::new( p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31, ); static MPSL: StaticCell = StaticCell::new(); let mpsl = MPSL.init(MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg).unwrap()); // 5. Initialize RNG static RNG: StaticCell> = StaticCell::new(); let mut rng = RNG.init(Rng::new(p.RNG, Irqs)); // 6. Configure and initialize SDC const SDC_MEM_SIZE: usize = 8192; // Adjust based on config static SDC_MEM: StaticCell> = StaticCell::new(); let sdc_p = SdcPeripherals::new( p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, p.PPI_CH25, p.PPI_CH26, p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, ); static SDC: StaticCell = StaticCell::new(); let sdc = SDC.init( Builder::new().unwrap() .support_adv().unwrap() .support_peripheral().unwrap() .build(sdc_p, &mut rng, mpsl, SDC_MEM.init(Mem::new())).unwrap() ); // 7. Spawn background tasks spawner.must_spawn(mpsl_task(mpsl)); spawner.must_spawn(sdc_task(sdc)); // 8. Your application runs here loop { // Application code } } #[embassy_executor::task] async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer) -> ! { mpsl.run().await } #[embassy_executor::task] async fn sdc_task(sdc: &'static SoftdeviceController) -> ! { loop { let mut evt_buf = [0; sdc_raw::HCI_MSG_BUFFER_MAX_SIZE as usize]; match sdc.hci_get(&mut evt_buf).await { Ok(PacketKind::Event) => { // Process BLE event } Ok(PacketKind::AclData) => { // Process ACL data } Err(e) => eprintln!("HCI error: {:?}", e), } } } ``` -------------------------------- ### Vendor Commands in nrf_sdc::vendor Submodule Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Lists example structures for Zephyr and Nordic-specific HCI commands, along with their associated support types. These are used for vendor-specific operations. ```rust pub struct ZephyrReadVersionInfo { ... } pub struct ZephyrReadSupportedCommands { ... } pub struct ZephyrWriteBdAddr { ... } pub struct ZephyrReadStaticAddrs { ... } pub struct ZephyrReadKeyHierarchyRoots { ... } pub struct ZephyrReadChipTemp { ... } pub struct ZephyrWriteTxPower { ... } pub struct ZephyrReadTxPower { ... } pub struct NordicLlpmModeSet { ... } pub struct NordicConnUpdate { ... } pub struct NordicConnEventExtend { ... } // ... 20+ more vendor commands pub struct ZephyrTxPower { ... } pub struct NordicConnUpdateParams { ... } // ... other parameter types ``` -------------------------------- ### Full MultiprotocolServiceLayer Initialization Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/MultiprotocolServiceLayer.md Initializes the Multiprotocol Service Layer with specified peripherals, interrupt bindings, and clock configuration. This is the primary setup for using MPSL in an application. ```rust use embassy_executor::Spawner; use embassy_nrf::bind_interrupts; use nrf_mpsl::{MultiprotocolServiceLayer, Peripherals, raw as mpsl_raw}; use static_cell::StaticCell; bind_interrupts!(struct Irqs { SWI0_EGU0 => nrf_mpsl::LowPrioInterruptHandler; POWER_CLOCK => nrf_mpsl::ClockInterruptHandler; RADIO => nrf_mpsl::HighPrioInterruptHandler; TIMER0 => nrf_mpsl::HighPrioInterruptHandler; RTC0 => nrf_mpsl::HighPrioInterruptHandler; }); #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { let p = embassy_nrf::init(Default::default()); let lfclk_cfg = mpsl_raw::mpsl_clock_lfclk_cfg_t { source: mpsl_raw::MPSL_CLOCK_LF_SRC_RC as u8, rc_ctiv: 16, rc_temp_ctiv: 2, accuracy_ppm: mpsl_raw::MPSL_CLOCK_LF_ACCURACY_500_PPM as u16, }; let mpsl_p = Peripherals::new( p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31, ); static MPSL: StaticCell = StaticCell::new(); let mpsl = MPSL.init(MultiprotocolServiceLayer::new(mpsl_p, Irqs, lfclk_cfg)?); spawner.must_spawn(mpsl_task(mpsl)); // ... rest of application } #[embassy_executor::task] async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer) -> ! { mpsl.run().await } ``` -------------------------------- ### Multi-role Configuration (Central + Peripheral) Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/UsagePatterns.md Configuration for devices acting as both central and peripheral. This setup supports multiple central and peripheral connections. ```rust Builder::new()? .central_count(2)? .peripheral_count(2)? .buffer_cfg(251, 251, 2, 2)? .support_scan()? .support_central()? .support_adv()? .support_peripheral()? .build(sdc_p, &mut rng, mpsl, mem)? ``` -------------------------------- ### Error Handling: Early Return for Initialization Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/UsagePatterns.md This pattern is used for handling initialization failures. If any step in the setup process returns an error, the function immediately returns the error, preventing further execution. ```rust fn setup() -> Result<(), Error> { let mpsl = MultiprotocolServiceLayer::new(p, Irqs, cfg)?; let sdc = Builder::new()?.build(sdc_p, &mut rng, mpsl, mem)?; Ok(()) } // In main: setup().expect("Bluetooth initialization failed"); ``` -------------------------------- ### Drift Compensation Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Shows how to obtain the raw temperature value in quarter-degree increments for potential use in frequency drift compensation calculations. The raw value is directly used to determine frequency offset. ```rust let temp = mpsl.get_temperature(); let temp_quarters = temp.raw(); // 0.25°C per unit // Frequency offset compensation might use: let freq_offset = calculate_drift(temp_quarters); apply_frequency_correction(freq_offset); ``` -------------------------------- ### run Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/MultiprotocolServiceLayer.md Starts the main processing loop for the Multiprotocol Service Layer (MPSL). This method should be spawned as an asynchronous task and will continuously handle internal MPSL processing and low-priority event callbacks. It never returns. ```APIDOC ## run() ### Description Runs the MPSL processing loop. This must be spawned as a task and will never return. Handles all MPSL internal processing and low-priority event handlers. ### Method Signature `pub async fn run(&self) -> !` ### Parameters - None ### Returns Never (the function is `!`). ### Example ```rust #[embassy_executor::task] async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer) -> ! { mpsl.run().await } ``` ``` -------------------------------- ### SessionMem Size Determination Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Memory.md Illustrates how the compiler automatically determines the size of SessionMem based on the generic const parameter. No manual size calculation is needed. ```rust let mem: SessionMem<4> = SessionMem::new(); // Size automatically determined by type system ``` -------------------------------- ### Use Case: Time-Sensitive Precision Operations Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/HighFrequencyClock.md Example demonstrating the use of the Hfclk guard for operations requiring accurate timing, such as precise measurements using cycle counters. ```rust async fn calibrate_timing(mpsl: &'static MultiprotocolServiceLayer) -> Result<(), Box> { // Request HF clock for accurate measurement let _hfclk = mpsl.request_hfclk().await?; // Measure timing with high precision let start = DWT::cycle_count(); operation(); let end = DWT::cycle_count(); let cycles = end - start; Ok(()) } ``` -------------------------------- ### Query Controller Information and Settings Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/VendorCommands.md This snippet demonstrates how to query the controller's firmware version, read the chip temperature, and set the TX power level using vendor commands. Ensure the `SoftdeviceController` is properly initialized. ```rust use bt_hci::controller::ControllerCmdSync; use nrf_sdc::vendor::*; async fn query_controller(controller: &SoftdeviceController) -> Result<(), Box> { // Get version info let version = controller.exec(&ZephyrReadVersionInfo).await?; println!("FW version: {}.{}", version.fw_version, version.fw_revision); // Read temperature let temp = controller.exec(&ZephyrReadChipTemp).await?; println!("Chip temperature: {}°C", temp); // Set TX power let tx_power = ZephyrWriteTxPowerParams { handle_type: 0, // Advertisement handle: 0, tx_power_level: 4, // 4 dBm }; controller.exec(&ZephyrWriteTxPower { tx_power }).await?; Ok(()) } ``` -------------------------------- ### Error::to_retval() Example Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ErrorHandling.md Demonstrates converting an Error enum variant back to a RetVal. ```rust let err = Error::EINVAL; let ret = err.to_retval(); ``` -------------------------------- ### NordicGetNextConnEventCounter Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/VendorCommands.md Gets the next connection event counter value. Used for precise timing synchronization. ```APIDOC ## NordicGetNextConnEventCounter ### Description Gets the next connection event counter value. Used for precise timing synchronization. ### Method Not applicable (Vendor Specific Command) ### Endpoint Not applicable (Vendor Specific Command) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **(ConnHandle)** - Required - Connection handle. ### Request Example ```json "conn_handle_value" ``` ### Response #### Success Response (200) - **next_conn_event_counter** (u16) - The next connection event counter value. #### Response Example ```json { "next_conn_event_counter": 1234 } ``` ``` -------------------------------- ### Initialization Chain for SoftdeviceController Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Illustrates the sequence of calls required to initialize the SoftdeviceController. It shows the dependencies on Peripherals, RNG, MultiprotocolServiceLayer, and Mem. ```rust Builder::new() ↓ (configure) ↓ Builder::build() ├─ Requires: Peripherals<'d> ├─ Requires: &'d mut RNG ├─ Requires: &'d mut MultiprotocolServiceLayer └─ Requires: &'d mut Mem ↓ SoftdeviceController<'d> (borrows all inputs, lifetime 'd) ``` -------------------------------- ### Get Current Temperature Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/MultiprotocolServiceLayer.md Reads the current temperature from the on-chip temperature sensor. Provides temperature in degrees and millidegrees. ```rust pub fn get_temperature(&self) -> Temperature ``` ```rust let temp = mpsl.get_temperature(); println!("Temperature: {}°C", temp.degrees()); println!("Millidegrees: {}m°C", temp.millidegrees()); ``` -------------------------------- ### Get MultiprotocolServiceLayer Build Revision Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/MultiprotocolServiceLayer.md Retrieves the build revision of the MPSL firmware. This is useful for identifying the specific firmware version. ```rust pub fn build_revision() -> Result<[u8; MPSL_BUILD_REVISION_SIZE], Error> ``` ```rust let revision = MultiprotocolServiceLayer::build_revision()?; println!("MPSL revision: {:?}", revision); ``` -------------------------------- ### Build SoftdeviceController Instance Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Finalizes SDC configuration and creates an active SoftdeviceController. Requires hardware peripherals, a random number generator, an initialized MPSL instance, and a memory buffer. ```rust const SDC_MEM_SIZE: usize = sdc_raw::SDC_MEM_SIZE_MIN as usize; static SDC_MEM: StaticCell> = StaticCell::new(); let sdc = Builder::new()? .support_adv()? .support_peripheral()? .build(sdc_p, &mut rng, mpsl, SDC_MEM.init(sdc::Mem::new()))?; ``` -------------------------------- ### Typical Mem Usage Pattern Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/Memory.md Illustrates the common pattern of creating a static, initialized memory buffer for the SoftDevice Controller. Ensures the buffer is available for the entire lifetime of the SDC. ```rust const SDC_MEM_SIZE: usize = nrf_sdc::raw::SDC_MEM_SIZE_MIN as usize; static SDC_MEM: StaticCell> = StaticCell::new(); fn main() { let mem = SDC_MEM.init(Mem::new()); let sdc = builder.build(sdc_p, &mut rng, mpsl, mem)?; } ``` -------------------------------- ### Get Integer Degrees Celsius Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Retrieves the integer part of the temperature in degrees Celsius. Use this for simple temperature display. ```rust let temp = mpsl.get_temperature(); let celsius = temp.degrees(); println!("Temperature: {}°C", celsius); ``` -------------------------------- ### Hfclk::wait() Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/HighFrequencyClock.md Asynchronously waits for the high-frequency clock to start running. This is typically called immediately after acquiring the HFCLK guard. ```APIDOC ## Hfclk::wait() ### Description Waits for the high-frequency clock to start running. This method is typically called immediately after obtaining the `Hfclk` guard. ### Method `async fn wait() -> Result<(), Error>` ### Parameters None ### Returns - `Ok(())` on success when the clock is running. - `Err(Error::EINVAL)` if the clock request failed. ### Example ```rust // Assuming 'mpsl' is an instance of MultiprotocolServiceLayer let hfclk_guard = mpsl.request_hfclk().await?; let result = hfclk_guard.wait().await; match result { Ok(_) => println!("HF clock is running."), Err(Error::EINVAL) => println!("Failed to start HF clock."), Err(e) => println!("An unexpected error occurred: {:?}", e), } // hfclk_guard is dropped automatically at the end of the scope ``` ``` -------------------------------- ### Use Case: Synchronized Timestamping Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/HighFrequencyClock.md An example of using the Hfclk guard to ensure accurate timestamping for coordinating activities and scheduling callbacks. ```rust async fn synchronized_operations(mpsl: &'static MultiprotocolServiceLayer) -> Result<(), Box> { let _hfclk = mpsl.request_hfclk().await?; let timestamp = get_timestamp(); schedule_callback_at(timestamp + Duration::from_millis(100)); Ok(()) } ``` -------------------------------- ### Builder::new() Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Creates a new builder for configuring the SoftDevice Controller. This is the entry point for setting up the SDC with specific parameters. ```APIDOC ## Builder::new() ### Description Creates a new builder for configuring the SoftDevice Controller. This is the entry point for setting up the SDC with specific parameters. ### Method `pub fn new() -> Result` ### Parameters No parameters. ### Returns `Result` - A new builder instance, or an error if initialization fails. ### Errors - `Error::EINVAL` if the SDC is already initialized ### Example ```rust let builder = Builder::new()?; ``` ``` -------------------------------- ### Configuration Methods Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Methods for configuring the SoftDevice Controller. These methods allow chaining to set various parameters for central links, peripheral links, advertising, buffers, and more. ```APIDOC ## central_count() ### Description Sets the number of simultaneous central links. ### Method `pub fn central_count(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of central links (0-254) ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## peripheral_count() ### Description Sets the number of simultaneous peripheral links. ### Method `pub fn peripheral_count(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of peripheral links (0-254) ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## adv_count() ### Description Sets the number of legacy advertising sets. ### Method `pub fn adv_count(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of advertising sets ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## buffer_cfg() ### Description Configures ACL buffer sizes and counts. ### Method `pub fn buffer_cfg( self, tx_packet_size: u16, rx_packet_size: u16, tx_packet_count: u8, rx_packet_count: u8, ) -> Result` ### Parameters #### Path Parameters - **tx_packet_size** (u16) - Required - TX packet size in bytes - **rx_packet_size** (u16) - Required - RX packet size in bytes - **tx_packet_count** (u8) - Required - Number of TX packets - **rx_packet_count** (u8) - Required - Number of RX packets ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## scan_buffer_cfg() ### Description Configures the number of scan buffers. ### Method `pub fn scan_buffer_cfg(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of scan buffers ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## adv_buffer_cfg() ### Description Configures the maximum advertising data size. ### Method `pub fn adv_buffer_cfg(self, max_adv_data: u16) -> Result` ### Parameters #### Path Parameters - **max_adv_data** (u16) - Required - Maximum advertising data size in bytes ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## periodic_adv_count() ### Description Sets the number of periodic advertising sets. ### Method `pub fn periodic_adv_count(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of periodic advertising sets ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## periodic_sync_count() ### Description Sets the number of periodic sync instances. ### Method `pub fn periodic_sync_count(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of periodic sync instances ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## periodic_sync_buffer_cfg() ### Description Configures periodic sync buffer count. ### Method `pub fn periodic_sync_buffer_cfg(self, count: u8) -> Result` ### Parameters #### Path Parameters - **count** (u8) - Required - Number of periodic sync buffers ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## periodic_adv_list_len() ### Description Sets the periodic advertising list size. ### Method `pub fn periodic_adv_list_len(self, len: u8) -> Result` ### Parameters #### Path Parameters - **len** (u8) - Required - List size ### Returns `Result` - The builder instance for chaining. ``` ```APIDOC ## default_tx_power() ### Description Sets the default TX power. ### Method `pub fn default_tx_power(self, dbm: i8) -> Result` ### Parameters #### Path Parameters - **dbm** (i8) - Required - TX power in dBm (typically -40 to +20) ### Returns `Result` - The builder instance for chaining. ``` -------------------------------- ### Get Millidegrees Celsius Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Retrieves the fractional part of the temperature as millidegrees Celsius. Combine with degrees() for full precision. The range is 0-750. ```rust let temp = mpsl.get_temperature(); let deg = temp.degrees(); let mdeg = temp.millidegrees(); println!("Temperature: {}.{:03}°C", deg, mdeg); ``` -------------------------------- ### MPSL Initialization Chain Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Details the initialization process for the MultiprotocolServiceLayer, including its requirements and the provision of Hfclk. ```rust MultiprotocolServiceLayer::new() ├─ Requires: Peripherals<'d> ├─ Requires: Interrupt binding └─ Requires: mpsl_clock_lfclk_cfg_t ↓ MultiprotocolServiceLayer<'d> (owns peripheral references) ↓ provides MultiprotocolServiceLayer::request_hfclk() ↓ Hfclk (exclusive, must be dropped to request again) ``` -------------------------------- ### Acquire Temperature Reading Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Demonstrates how to get a Temperature object from the MultiprotocolServiceLayer instance. This object can then be used to query degrees, millidegrees, or raw values. ```rust let temp_sensor = mpsl.get_temperature(); println!("Integer: {}°C", temp_sensor.degrees()); println!("Fractional: {}m°C", temp_sensor.millidegrees()); ``` -------------------------------- ### Async Wait for High-Frequency Clock Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/HighFrequencyClock.md Waits asynchronously for the high-frequency clock to start running. This is typically called immediately after acquiring the Hfclk guard. ```rust pub async fn wait() -> Result<(), Error> ``` -------------------------------- ### Initialize MultiprotocolServiceLayer with Timeslot Support Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/MultiprotocolServiceLayer.md Initializes the MPSL with timeslot support for flash operations and time-critical tasks. Requires hardware peripherals, interrupt bindings, clock configuration, and session memory. ```rust pub fn with_timeslots( p: Peripherals<'d>, _irq: I, clock_cfg: raw::mpsl_clock_lfclk_cfg_t, mem: &'d mut SessionMem, ) -> Result where T: Interrupt, I: Binding + Binding + Binding + Binding + Binding, ``` ```rust // Allocate memory for timeslot sessions static SESSION_MEM: StaticCell> = StaticCell::new(); let mpsl = MultiprotocolServiceLayer::with_timeslots( mpsl_p, Irqs, lfclk_cfg, SESSION_MEM.init(SessionMem::new()), )?; ``` -------------------------------- ### Get SoftDevice Controller Build Revision Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Retrieves the build revision of the SoftDevice Controller firmware. Returns a byte array representing the revision or an error. ```rust pub fn build_revision() -> Result<[u8; SDC_BUILD_REVISION_SIZE], Error> { // ... implementation details ... } ``` -------------------------------- ### Project Structure Overview Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/INDEX.md Illustrates the directory layout and module organization for the nrf-sdc and nrf-mpsl crates, including their respective system bindings. ```text nrf-sdc/ # Main Rust bindings ├── nrf-sdc/ # High-level SDC interface │ └── src/ │ ├── sdc.rs # SoftdeviceController (1922 lines) │ └── lib.rs # Module exports ├── nrf-mpsl/ # High-level MPSL interface │ └── src/ │ ├── mpsl.rs # MultiprotocolServiceLayer (400 lines) │ ├── error.rs # Error types (200 lines) │ ├── hfclk.rs # HF clock (67 lines) │ ├── temp.rs # Temperature (24 lines) │ ├── flash.rs # Flash operations (500+ lines) │ └── lib.rs # Module exports ├── nrf-sdc-sys/ # Low-level C bindings └── nrf-mpsl-sys/ # Low-level C bindings ``` -------------------------------- ### Unit Testing SDC Interface with Mocking Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/UsagePatterns.md Demonstrates unit testing of SDC functionality on a desktop environment by mocking the SDC interface using the `mockall` crate. This allows testing logic without a physical device. ```rust #[cfg(test)] mod tests { use mockall::predicate::*; #[test] fn test_packet_handling() { let mut mock_sdc = MockController::new(); mock_sdc.expect_read() .times(1) .returning(|_| Ok(PacketKind::Event)); // Test with mock } } ``` -------------------------------- ### Get Raw Temperature Value Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/TemperatureSensor.md Returns the raw temperature value in units of 0.25°C. Divide by 4 to convert to degrees Celsius for floating-point representation. ```rust let temp = mpsl.get_temperature(); let raw = temp.raw(); let celsius_f = raw as f32 * 0.25; println!("Temperature: {:.2}°C", celsius_f); ``` -------------------------------- ### Get Next Connection Event Counter Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/VendorCommands.md Retrieves the next connection event counter value for a specific connection handle. This is used for precise timing synchronization. ```rust cmd! { NordicGetNextConnEventCounter(VENDOR_SPECIFIC, 0x114) { Params = ConnHandle; NordicGetNextConnEventCounterReturn { next_conn_event_counter: u16, } } } ``` -------------------------------- ### Execute HCI Command Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/VendorCommands.md Demonstrates how to execute a vendor-specific HCI command using the `Controller` trait. Ensure the `Controller` trait from `bt_hci` is in scope. ```rust use bt_hci::controller::ControllerCmdSync; let result = controller.exec(&cmd).await; ``` -------------------------------- ### SoftdeviceController Build Method Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/SoftdeviceController.md Finalizes the SDC configuration and creates an active `SoftdeviceController`. This method requires hardware peripherals, a random number generator, an initialized MPSL instance, and a memory buffer. ```APIDOC ## build() ### Description Finalizes the SDC configuration and creates an active `SoftdeviceController`. ### Method `build` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **p** (`Peripherals<'d>`) - Required - Hardware peripherals required by SDC - **rng** (`&'d mut R: CryptoRng`) - Required - Random number generator for security operations - **mpsl** (`&'d MultiprotocolServiceLayer`) - Required - Initialized MPSL instance (must outlive the controller) - **mem** (`&'d mut Mem`) - Required - Memory buffer for SDC runtime state ### Response #### Success Response (Result, Error>) - `SoftdeviceController<'d>` - The active controller instance. - `Error` - An error if the build fails. #### Response Example ```rust let sdc = Builder::new()? \ .support_adv()? \ .support_peripheral()? \ .build(sdc_p, &mut rng, mpsl, SDC_MEM.init(sdc::Mem::new()))?; ``` **Errors:** - `Error::ENOMEM` if provided memory buffer is too small - Other errors if hardware initialization fails ``` -------------------------------- ### Logging Imports with defmt Source: https://github.com/alexmoon/nrf-sdc/blob/main/_autodocs/api-reference/ModuleStructure.md Shows how to import logging macros when the 'defmt' feature is enabled for logging. ```rust use defmt::{error, warn, info, debug}; ```