RKTK API Docs RKTK Home Repo

rktk_drivers_nrf/
sdc.rs

1use embassy_nrf::interrupt;
2use embassy_nrf::interrupt::typelevel::{Binding, Interrupt};
3use embassy_nrf::mode::Mode;
4use embassy_nrf::peripherals::RNG;
5use embassy_nrf::rng::{InterruptHandler, Rng};
6use nrf_sdc::mpsl::{
7    ClockInterruptHandler, HighPrioInterruptHandler, LowPrioInterruptHandler,
8    MultiprotocolServiceLayer,
9};
10use nrf_sdc::{self as sdc, mpsl};
11use rktk::singleton;
12use static_cell::StaticCell;
13
14pub use mpsl::Peripherals as MpslPeripherals;
15pub use sdc::Peripherals as SdcPeripherals;
16
17#[derive(Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum SdcInitError {
20    Mpsl(mpsl::Error),
21    Sdc(sdc::Error),
22}
23
24#[macro_export]
25macro_rules! init_sdc {
26    (
27        $spawner:expr,
28        $sdc_name:ident,
29        $irqs:expr,
30        $rng:expr,
31        mpsl: ($rtc0:expr, $timer0:expr, $temp:expr, $ppi1:expr, $ppi2:expr, $ppi3:expr),
32        sdc: ($ppis1:expr,$ppis2:expr,$ppis3:expr,$ppis4:expr,$ppis5:expr,$ppis6:expr,$ppis7:expr,$ppis8:expr,$ppis9:expr,$ppis10:expr,$ppis11:expr,$ppis12:expr),
33        mtu: $l2cap_mtu:expr,
34        txq: $l2cap_txq:expr,
35        rxq: $l2cap_rxq:expr
36    ) => {
37        let __mpsl_p =
38            $crate::sdc::MpslPeripherals::new($rtc0, $timer0, $temp, $ppi1, $ppi2, $ppi3);
39        let __sdc_p = $crate::sdc::SdcPeripherals::new(
40            $ppis1, $ppis2, $ppis3, $ppis4, $ppis5, $ppis6, $ppis7, $ppis8, $ppis9, $ppis10,
41            $ppis11, $ppis12,
42        );
43        let $sdc_name = $crate::sdc::init_sdc(
44            $spawner, __mpsl_p, __sdc_p, $rng, $l2cap_mtu, $l2cap_txq, $l2cap_rxq, $irqs,
45        );
46    };
47}
48
49#[embassy_executor::task]
50async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
51    mpsl.run().await
52}
53
54/// Initialize softdevice controller(sdc) and starts mpsl task.
55///
56/// This function must be called only once.
57#[allow(clippy::too_many_arguments)]
58pub fn init_sdc<
59    T: Interrupt,
60    I: Binding<T, LowPrioInterruptHandler>
61        + Binding<interrupt::typelevel::RADIO, HighPrioInterruptHandler>
62        + Binding<interrupt::typelevel::TIMER0, HighPrioInterruptHandler>
63        + Binding<interrupt::typelevel::RTC0, HighPrioInterruptHandler>
64        + Binding<interrupt::typelevel::CLOCK_POWER, ClockInterruptHandler>
65        + Binding<interrupt::typelevel::RNG, InterruptHandler<RNG>>
66        + 'static
67        + Clone,
68    RM: Mode + Send,
69>(
70    spawner: embassy_executor::Spawner,
71    mpsl_peripherals: mpsl::Peripherals<'static>,
72    sdc_peripherals: sdc::Peripherals<'static>,
73    rng: &'static mut Rng<'static, RM>,
74    l2cap_mtu: u16,
75    l2cap_txq: u8,
76    l2cap_rxq: u8,
77    irqs: I,
78) -> Result<nrf_sdc::SoftdeviceController<'static>, mpsl::Error> {
79    let lfclk_cfg = mpsl::raw::mpsl_clock_lfclk_cfg_t {
80        source: mpsl::raw::MPSL_CLOCK_LF_SRC_RC as u8,
81        rc_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_CTIV as u8,
82        rc_temp_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_TEMP_CTIV as u8,
83        accuracy_ppm: mpsl::raw::MPSL_DEFAULT_CLOCK_ACCURACY_PPM as u16,
84        skip_wait_lfclk_started: mpsl::raw::MPSL_DEFAULT_SKIP_WAIT_LFCLK_STARTED != 0,
85    };
86    static MPSL: StaticCell<MultiprotocolServiceLayer> = StaticCell::new();
87    let mpsl = MPSL.init(mpsl::MultiprotocolServiceLayer::new(
88        mpsl_peripherals,
89        irqs.clone(),
90        lfclk_cfg,
91    )?);
92
93    spawner.must_spawn(mpsl_task(&*mpsl));
94
95    let sdc_mem = singleton!(sdc::Mem::<3312>::new(), sdc::Mem::<3312>);
96
97    let sdc = sdc::Builder::new()?
98        .support_adv()?
99        .support_peripheral()?
100        .peripheral_count(1)?
101        .buffer_cfg(l2cap_mtu, l2cap_mtu, l2cap_txq, l2cap_rxq)?
102        .build(sdc_peripherals, rng, mpsl, sdc_mem)?;
103
104    Ok(sdc)
105}