RKTK API Docs RKTK Home Repo

rktk_drivers_nrf/
sdc.rs

1use embassy_nrf::interrupt::typelevel::{Binding, Interrupt};
2use embassy_nrf::mode::Mode;
3use embassy_nrf::peripherals::RNG;
4use embassy_nrf::rng::{InterruptHandler, Rng};
5use embassy_nrf::{interrupt, 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    PR: rng::Instance,
69    RM: Mode + Send,
70>(
71    spawner: embassy_executor::Spawner,
72    mpsl_peripherals: mpsl::Peripherals<'static>,
73    sdc_peripherals: sdc::Peripherals<'static>,
74    rng: &'static mut Rng<'static, PR, RM>,
75    l2cap_mtu: u16,
76    l2cap_txq: u8,
77    l2cap_rxq: u8,
78    irqs: I,
79) -> Result<nrf_sdc::SoftdeviceController<'static>, mpsl::Error> {
80    let lfclk_cfg = mpsl::raw::mpsl_clock_lfclk_cfg_t {
81        source: mpsl::raw::MPSL_CLOCK_LF_SRC_RC as u8,
82        rc_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_CTIV as u8,
83        rc_temp_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_TEMP_CTIV as u8,
84        accuracy_ppm: mpsl::raw::MPSL_DEFAULT_CLOCK_ACCURACY_PPM as u16,
85        skip_wait_lfclk_started: mpsl::raw::MPSL_DEFAULT_SKIP_WAIT_LFCLK_STARTED != 0,
86    };
87    static MPSL: StaticCell<MultiprotocolServiceLayer> = StaticCell::new();
88    let mpsl = MPSL.init(mpsl::MultiprotocolServiceLayer::new(
89        mpsl_peripherals,
90        irqs.clone(),
91        lfclk_cfg,
92    )?);
93
94    spawner.must_spawn(mpsl_task(&*mpsl));
95
96    let sdc_mem = singleton!(sdc::Mem::<3312>::new(), sdc::Mem::<3312>);
97
98    let sdc = sdc::Builder::new()?
99        .support_adv()?
100        .support_peripheral()?
101        .peripheral_count(1)?
102        .buffer_cfg(l2cap_mtu, l2cap_mtu, l2cap_txq, l2cap_rxq)?
103        .build(sdc_peripherals, rng, mpsl, sdc_mem)?;
104
105    Ok(sdc)
106}