nrf52dk/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Tock kernel for the Nordic Semiconductor nRF52 development kit (DK), a.k.a. the PCA10040.
6//!
7//! It is based on nRF52838 SoC (Cortex M4 core with a BLE transceiver) with many exported
8//! I/O and peripherals.
9//!
10//! nRF52838 has only one port and uses pins 0-31!
11//!
12//! Furthermore, there exist another a preview development kit for nRF52840 but it is not supported
13//! yet because unfortunately the pin configuration differ from nRF52-DK whereas nRF52840 uses two
14//! ports where port 0 has 32 pins and port 1 has 16 pins.
15//!
16//! Pin Configuration
17//! -------------------
18//!
19//! ### `GPIOs`
20//! * P0.27 -> (top left header)
21//! * P0.26 -> (top left header)
22//! * P0.02 -> (top left header)
23//! * P0.25 -> (top left header)
24//! * P0.24 -> (top left header)
25//! * P0.23 -> (top left header)
26//! * P0.22 -> (top left header)
27//! * P0.12 -> (top mid header)
28//! * P0.11 -> (top mid header)
29//! * P0.03 -> (bottom right header)
30//! * P0.04 -> (bottom right header)
31//! * P0.28 -> (bottom right header)
32//! * P0.29 -> (bottom right header)
33//! * P0.30 -> (bottom right header)
34//! * P0.31 -> (bottom right header)
35//!
36//! ### `LEDs`
37//! * P0.17 -> LED1
38//! * P0.18 -> LED2
39//! * P0.19 -> LED3
40//! * P0.20 -> LED4
41//!
42//! ### `Buttons`
43//! * P0.13 -> Button1
44//! * P0.14 -> Button2
45//! * P0.15 -> Button3
46//! * P0.16 -> Button4
47//! * P0.21 -> Reset Button
48//!
49//! ### `UART`
50//! * P0.05 -> RTS
51//! * P0.06 -> TXD
52//! * P0.07 -> CTS
53//! * P0.08 -> RXD
54//!
55//! ### `NFC`
56//! * P0.09 -> NFC1
57//! * P0.10 -> NFC2
58//!
59//! ### `LFXO`
60//! * P0.01 -> XL2
61//! * P0.00 -> XL1
62//!
63//! Author
64//! -------------------
65//! * Niklas Adolfsson <niklasadolfsson1@gmail.com>
66//! * July 16, 2017
67
68#![no_std]
69#![no_main]
70#![deny(missing_docs)]
71
72use core::ptr::addr_of;
73
74use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
75use kernel::component::Component;
76use kernel::hil::led::LedLow;
77use kernel::hil::time::Counter;
78use kernel::platform::{KernelResources, SyscallDriverLookup};
79use kernel::process::ProcessArray;
80use kernel::scheduler::round_robin::RoundRobinSched;
81#[allow(unused_imports)]
82use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
83use nrf52832::gpio::Pin;
84use nrf52832::interrupt_service::Nrf52832DefaultPeripherals;
85use nrf52832::rtc::Rtc;
86use nrf52_components::{UartChannel, UartPins};
87
88// The nRF52 DK LEDs (see back of board)
89const LED1_PIN: Pin = Pin::P0_17;
90const LED2_PIN: Pin = Pin::P0_18;
91const LED3_PIN: Pin = Pin::P0_19;
92const LED4_PIN: Pin = Pin::P0_20;
93
94// The nRF52 DK buttons (see back of board)
95const BUTTON1_PIN: Pin = Pin::P0_13;
96const BUTTON2_PIN: Pin = Pin::P0_14;
97const BUTTON3_PIN: Pin = Pin::P0_15;
98const BUTTON4_PIN: Pin = Pin::P0_16;
99const BUTTON_RST_PIN: Pin = Pin::P0_21;
100
101const UART_RTS: Option<Pin> = Some(Pin::P0_05);
102const UART_TXD: Pin = Pin::P0_06;
103const UART_CTS: Option<Pin> = Some(Pin::P0_07);
104const UART_RXD: Pin = Pin::P0_08;
105
106// SPI not used, but keep pins around
107const _SPI_MOSI: Pin = Pin::P0_22;
108const _SPI_MISO: Pin = Pin::P0_23;
109const _SPI_CLK: Pin = Pin::P0_24;
110
111/// UART Writer
112pub mod io;
113
114// FIXME: Ideally this should be replaced with Rust's builtin tests by conditional compilation
115//
116// Also read the instructions in `tests` how to run the tests
117#[allow(dead_code)]
118mod tests;
119
120// State for loading and holding applications.
121// How should the kernel respond when a process faults.
122const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
123    capsules_system::process_policies::PanicFaultPolicy {};
124
125// Number of concurrent processes this platform supports.
126const NUM_PROCS: usize = 4;
127
128/// Static variables used by io.rs.
129static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
130static mut CHIP: Option<&'static nrf52832::chip::NRF52<Nrf52832DefaultPeripherals>> = None;
131static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
132    None;
133
134kernel::stack_size! {0x1000}
135
136type TemperatureDriver =
137    components::temperature::TemperatureComponentType<nrf52832::temperature::Temp<'static>>;
138type RngDriver = components::rng::RngComponentType<nrf52832::trng::Trng<'static>>;
139
140/// Supported drivers by the platform
141pub struct Platform {
142    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
143        'static,
144        nrf52832::ble_radio::Radio<'static>,
145        VirtualMuxAlarm<'static, Rtc<'static>>,
146    >,
147    button: &'static capsules_core::button::Button<'static, nrf52832::gpio::GPIOPin<'static>>,
148    pconsole: &'static capsules_core::process_console::ProcessConsole<
149        'static,
150        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
151        VirtualMuxAlarm<'static, Rtc<'static>>,
152        components::process_console::Capability,
153    >,
154    console: &'static capsules_core::console::Console<'static>,
155    gpio: &'static capsules_core::gpio::GPIO<'static, nrf52832::gpio::GPIOPin<'static>>,
156    led: &'static capsules_core::led::LedDriver<
157        'static,
158        LedLow<'static, nrf52832::gpio::GPIOPin<'static>>,
159        4,
160    >,
161    rng: &'static RngDriver,
162    temp: &'static TemperatureDriver,
163    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
164    analog_comparator: &'static capsules_extra::analog_comparator::AnalogComparator<
165        'static,
166        nrf52832::acomp::Comparator<'static>,
167    >,
168    alarm: &'static capsules_core::alarm::AlarmDriver<
169        'static,
170        capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
171            'static,
172            nrf52832::rtc::Rtc<'static>,
173        >,
174    >,
175    scheduler: &'static RoundRobinSched<'static>,
176    systick: cortexm4::systick::SysTick,
177}
178
179impl SyscallDriverLookup for Platform {
180    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
181    where
182        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
183    {
184        match driver_num {
185            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
186            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
187            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
188            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
189            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
190            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
191            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
192            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temp)),
193            capsules_extra::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
194            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
195            _ => f(None),
196        }
197    }
198}
199
200impl KernelResources<nrf52832::chip::NRF52<'static, Nrf52832DefaultPeripherals<'static>>>
201    for Platform
202{
203    type SyscallDriverLookup = Self;
204    type SyscallFilter = ();
205    type ProcessFault = ();
206    type Scheduler = RoundRobinSched<'static>;
207    type SchedulerTimer = cortexm4::systick::SysTick;
208    type WatchDog = ();
209    type ContextSwitchCallback = ();
210
211    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
212        self
213    }
214    fn syscall_filter(&self) -> &Self::SyscallFilter {
215        &()
216    }
217    fn process_fault(&self) -> &Self::ProcessFault {
218        &()
219    }
220    fn scheduler(&self) -> &Self::Scheduler {
221        self.scheduler
222    }
223    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
224        &self.systick
225    }
226    fn watchdog(&self) -> &Self::WatchDog {
227        &()
228    }
229    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
230        &()
231    }
232}
233
234/// This is in a separate, inline(never) function so that its stack frame is
235/// removed when this function returns. Otherwise, the stack space used for
236/// these static_inits is wasted.
237#[inline(never)]
238pub unsafe fn start() -> (
239    &'static kernel::Kernel,
240    Platform,
241    &'static nrf52832::chip::NRF52<'static, Nrf52832DefaultPeripherals<'static>>,
242) {
243    nrf52832::init();
244
245    let nrf52832_peripherals = static_init!(
246        Nrf52832DefaultPeripherals,
247        Nrf52832DefaultPeripherals::new()
248    );
249
250    // set up circular peripheral dependencies
251    nrf52832_peripherals.init();
252    let base_peripherals = &nrf52832_peripherals.nrf52;
253
254    // Create an array to hold process references.
255    let processes = components::process_array::ProcessArrayComponent::new()
256        .finalize(components::process_array_component_static!(NUM_PROCS));
257    PROCESSES = Some(processes);
258
259    // Setup space to store the core kernel data structure.
260    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
261
262    let gpio = components::gpio::GpioComponent::new(
263        board_kernel,
264        capsules_core::gpio::DRIVER_NUM,
265        components::gpio_component_helper!(
266            nrf52832::gpio::GPIOPin,
267            // Bottom right header on DK board
268            0 => &nrf52832_peripherals.gpio_port[Pin::P0_03],
269            1 => &nrf52832_peripherals.gpio_port[Pin::P0_04],
270            2 => &nrf52832_peripherals.gpio_port[Pin::P0_28],
271            3 => &nrf52832_peripherals.gpio_port[Pin::P0_29],
272            4 => &nrf52832_peripherals.gpio_port[Pin::P0_30],
273            5 => &nrf52832_peripherals.gpio_port[Pin::P0_31],
274            // Top mid header on DK board
275            6 => &nrf52832_peripherals.gpio_port[Pin::P0_12],
276            7 => &nrf52832_peripherals.gpio_port[Pin::P0_11],
277            // Top left header on DK board
278            8 => &nrf52832_peripherals.gpio_port[Pin::P0_27],
279            9 => &nrf52832_peripherals.gpio_port[Pin::P0_26],
280            10 => &nrf52832_peripherals.gpio_port[Pin::P0_02],
281            11 => &nrf52832_peripherals.gpio_port[Pin::P0_25]
282        ),
283    )
284    .finalize(components::gpio_component_static!(nrf52832::gpio::GPIOPin));
285
286    let button = components::button::ButtonComponent::new(
287        board_kernel,
288        capsules_core::button::DRIVER_NUM,
289        components::button_component_helper!(
290            nrf52832::gpio::GPIOPin,
291            (
292                &nrf52832_peripherals.gpio_port[BUTTON1_PIN],
293                kernel::hil::gpio::ActivationMode::ActiveLow,
294                kernel::hil::gpio::FloatingState::PullUp
295            ), //13
296            (
297                &nrf52832_peripherals.gpio_port[BUTTON2_PIN],
298                kernel::hil::gpio::ActivationMode::ActiveLow,
299                kernel::hil::gpio::FloatingState::PullUp
300            ), //14
301            (
302                &nrf52832_peripherals.gpio_port[BUTTON3_PIN],
303                kernel::hil::gpio::ActivationMode::ActiveLow,
304                kernel::hil::gpio::FloatingState::PullUp
305            ), //15
306            (
307                &nrf52832_peripherals.gpio_port[BUTTON4_PIN],
308                kernel::hil::gpio::ActivationMode::ActiveLow,
309                kernel::hil::gpio::FloatingState::PullUp
310            ) //16
311        ),
312    )
313    .finalize(components::button_component_static!(
314        nrf52832::gpio::GPIOPin
315    ));
316
317    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
318        LedLow<'static, nrf52832::gpio::GPIOPin>,
319        LedLow::new(&nrf52832_peripherals.gpio_port[LED1_PIN]),
320        LedLow::new(&nrf52832_peripherals.gpio_port[LED2_PIN]),
321        LedLow::new(&nrf52832_peripherals.gpio_port[LED3_PIN]),
322        LedLow::new(&nrf52832_peripherals.gpio_port[LED4_PIN]),
323    ));
324
325    let chip = static_init!(
326        nrf52832::chip::NRF52<Nrf52832DefaultPeripherals>,
327        nrf52832::chip::NRF52::new(nrf52832_peripherals)
328    );
329    CHIP = Some(chip);
330
331    nrf52_components::startup::NrfStartupComponent::new(
332        false,
333        BUTTON_RST_PIN,
334        nrf52832::uicr::Regulator0Output::DEFAULT,
335        &base_peripherals.nvmc,
336    )
337    .finalize(());
338
339    // Create capabilities that the board needs to call certain protected kernel
340    // functions.
341    let process_management_capability =
342        create_capability!(capabilities::ProcessManagementCapability);
343    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
344
345    let gpio_port = &nrf52832_peripherals.gpio_port;
346    // Configure kernel debug gpios as early as possible
347    kernel::debug::assign_gpios(
348        Some(&gpio_port[LED1_PIN]),
349        Some(&gpio_port[LED2_PIN]),
350        Some(&gpio_port[LED3_PIN]),
351    );
352
353    let rtc = &base_peripherals.rtc;
354    let _ = rtc.start();
355    let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
356        .finalize(components::alarm_mux_component_static!(nrf52832::rtc::Rtc));
357    let alarm = components::alarm::AlarmDriverComponent::new(
358        board_kernel,
359        capsules_core::alarm::DRIVER_NUM,
360        mux_alarm,
361    )
362    .finalize(components::alarm_component_static!(nrf52832::rtc::Rtc));
363    let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
364    let channel = nrf52_components::UartChannelComponent::new(
365        uart_channel,
366        mux_alarm,
367        &base_peripherals.uarte0,
368    )
369    .finalize(nrf52_components::uart_channel_component_static!(
370        nrf52832::rtc::Rtc
371    ));
372
373    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
374        .finalize(components::process_printer_text_component_static!());
375    PROCESS_PRINTER = Some(process_printer);
376
377    // Create a shared UART channel for the console and for kernel debug.
378    let uart_mux = components::console::UartMuxComponent::new(channel, 115200)
379        .finalize(components::uart_mux_component_static!());
380
381    let pconsole = components::process_console::ProcessConsoleComponent::new(
382        board_kernel,
383        uart_mux,
384        mux_alarm,
385        process_printer,
386        Some(cortexm4::support::reset),
387    )
388    .finalize(components::process_console_component_static!(Rtc<'static>));
389
390    // Setup the console.
391    let console = components::console::ConsoleComponent::new(
392        board_kernel,
393        capsules_core::console::DRIVER_NUM,
394        uart_mux,
395    )
396    .finalize(components::console_component_static!());
397    // Create the debugger object that handles calls to `debug!()`.
398    components::debug_writer::DebugWriterComponent::new(
399        uart_mux,
400        create_capability!(capabilities::SetDebugWriterCapability),
401    )
402    .finalize(components::debug_writer_component_static!());
403
404    let ble_radio = components::ble::BLEComponent::new(
405        board_kernel,
406        capsules_extra::ble_advertising_driver::DRIVER_NUM,
407        &base_peripherals.ble_radio,
408        mux_alarm,
409    )
410    .finalize(components::ble_component_static!(
411        nrf52832::rtc::Rtc,
412        nrf52832::ble_radio::Radio
413    ));
414
415    let temp = components::temperature::TemperatureComponent::new(
416        board_kernel,
417        capsules_extra::temperature::DRIVER_NUM,
418        &base_peripherals.temp,
419    )
420    .finalize(components::temperature_component_static!(
421        nrf52832::temperature::Temp
422    ));
423
424    let rng = components::rng::RngComponent::new(
425        board_kernel,
426        capsules_core::rng::DRIVER_NUM,
427        &base_peripherals.trng,
428    )
429    .finalize(components::rng_component_static!(nrf52832::trng::Trng));
430
431    // Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
432    // These are hardcoded pin assignments specified in the driver
433    let analog_comparator = components::analog_comparator::AnalogComparatorComponent::new(
434        &base_peripherals.acomp,
435        components::analog_comparator_component_helper!(
436            nrf52832::acomp::Channel,
437            &*addr_of!(nrf52832::acomp::CHANNEL_AC0)
438        ),
439        board_kernel,
440        capsules_extra::analog_comparator::DRIVER_NUM,
441    )
442    .finalize(components::analog_comparator_component_static!(
443        nrf52832::acomp::Comparator
444    ));
445
446    nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
447
448    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
449        .finalize(components::round_robin_component_static!(NUM_PROCS));
450
451    let platform = Platform {
452        button,
453        ble_radio,
454        pconsole,
455        console,
456        led,
457        gpio,
458        rng,
459        temp,
460        alarm,
461        analog_comparator,
462        ipc: kernel::ipc::IPC::new(
463            board_kernel,
464            kernel::ipc::DRIVER_NUM,
465            &memory_allocation_capability,
466        ),
467        scheduler,
468        systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
469    };
470
471    let _ = platform.pconsole.start();
472    debug!("Initialization complete. Entering main loop\r");
473    debug!("{}", &*addr_of!(nrf52832::ficr::FICR_INSTANCE));
474
475    // These symbols are defined in the linker script.
476    extern "C" {
477        /// Beginning of the ROM region containing app images.
478        static _sapps: u8;
479        /// End of the ROM region containing app images.
480        static _eapps: u8;
481        /// Beginning of the RAM region for app memory.
482        static mut _sappmem: u8;
483        /// End of the RAM region for app memory.
484        static _eappmem: u8;
485    }
486
487    kernel::process::load_processes(
488        board_kernel,
489        chip,
490        core::slice::from_raw_parts(
491            core::ptr::addr_of!(_sapps),
492            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
493        ),
494        core::slice::from_raw_parts_mut(
495            core::ptr::addr_of_mut!(_sappmem),
496            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
497        ),
498        &FAULT_RESPONSE,
499        &process_management_capability,
500    )
501    .unwrap_or_else(|err| {
502        debug!("Error loading processes!");
503        debug!("{:?}", err);
504    });
505
506    (board_kernel, platform, chip)
507}
508
509/// Main function called after RAM initialized.
510#[no_mangle]
511pub unsafe fn main() {
512    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
513
514    let (board_kernel, platform, chip) = start();
515    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
516}