redboard_artemis_nano/
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//! Board file for SparkFun Redboard Artemis Nano
6//!
7//! - <https://www.sparkfun.com/products/15443>
8
9#![no_std]
10#![no_main]
11#![deny(missing_docs)]
12#![feature(custom_test_frameworks)]
13#![test_runner(test_runner)]
14#![reexport_test_harness_main = "test_main"]
15
16use core::ptr::addr_of;
17use core::ptr::addr_of_mut;
18
19use apollo3::chip::Apollo3DefaultPeripherals;
20use capsules_core::virtualizers::virtual_alarm::MuxAlarm;
21use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
22use components::bme280::Bme280Component;
23use components::ccs811::Ccs811Component;
24use kernel::capabilities;
25use kernel::component::Component;
26use kernel::hil::i2c::I2CMaster;
27use kernel::hil::led::LedHigh;
28use kernel::hil::time::Counter;
29use kernel::platform::{KernelResources, SyscallDriverLookup};
30use kernel::scheduler::round_robin::RoundRobinSched;
31use kernel::{create_capability, debug, static_init};
32
33/// Support routines for debugging I/O.
34pub mod io;
35
36#[cfg(test)]
37mod tests;
38
39// Number of concurrent processes this platform supports.
40const NUM_PROCS: usize = 4;
41
42// Actual memory for holding the active process structures.
43static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] = [None; 4];
44
45// Static reference to chip for panic dumps.
46static mut CHIP: Option<&'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> = None;
47// Static reference to process printer for panic dumps.
48static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
49    None;
50
51// How should the kernel respond when a process faults.
52const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
53    capsules_system::process_policies::PanicFaultPolicy {};
54
55// Test access to the peripherals
56#[cfg(test)]
57static mut PERIPHERALS: Option<&'static Apollo3DefaultPeripherals> = None;
58// Test access to board
59#[cfg(test)]
60static mut BOARD: Option<&'static kernel::Kernel> = None;
61// Test access to platform
62#[cfg(test)]
63static mut PLATFORM: Option<&'static RedboardArtemisNano> = None;
64// Test access to main loop capability
65#[cfg(test)]
66static mut MAIN_CAP: Option<&dyn kernel::capabilities::MainLoopCapability> = None;
67// Test access to alarm
68static mut ALARM: Option<&'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>> = None;
69// Test access to sensors
70static mut BME280: Option<
71    &'static capsules_extra::bme280::Bme280<
72        'static,
73        capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
74    >,
75> = None;
76static mut CCS811: Option<&'static capsules_extra::ccs811::Ccs811<'static>> = None;
77
78/// Dummy buffer that causes the linker to reserve enough space for the stack.
79#[no_mangle]
80#[link_section = ".stack_buffer"]
81pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
82
83type BME280Sensor = components::bme280::Bme280ComponentType<
84    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
85>;
86type TemperatureDriver = components::temperature::TemperatureComponentType<BME280Sensor>;
87type HumidityDriver = components::humidity::HumidityComponentType<BME280Sensor>;
88
89/// A structure representing this platform that holds references to all
90/// capsules for this platform.
91struct RedboardArtemisNano {
92    alarm: &'static capsules_core::alarm::AlarmDriver<
93        'static,
94        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
95    >,
96    led: &'static capsules_core::led::LedDriver<
97        'static,
98        LedHigh<'static, apollo3::gpio::GpioPin<'static>>,
99        1,
100    >,
101    gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
102    console: &'static capsules_core::console::Console<'static>,
103    i2c_master:
104        &'static capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
105    spi_controller: &'static capsules_core::spi_controller::Spi<
106        'static,
107        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
108            'static,
109            apollo3::iom::Iom<'static>,
110        >,
111    >,
112    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
113        'static,
114        apollo3::ble::Ble<'static>,
115        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
116    >,
117    temperature: &'static TemperatureDriver,
118    humidity: &'static HumidityDriver,
119    air_quality: &'static capsules_extra::air_quality::AirQualitySensor<'static>,
120    scheduler: &'static RoundRobinSched<'static>,
121    systick: cortexm4::systick::SysTick,
122}
123
124/// Mapping of integer syscalls to objects that implement syscalls.
125impl SyscallDriverLookup for RedboardArtemisNano {
126    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
127    where
128        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
129    {
130        match driver_num {
131            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
132            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
133            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
134            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
135            capsules_core::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
136            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.spi_controller)),
137            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
138            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temperature)),
139            capsules_extra::humidity::DRIVER_NUM => f(Some(self.humidity)),
140            capsules_extra::air_quality::DRIVER_NUM => f(Some(self.air_quality)),
141            _ => f(None),
142        }
143    }
144}
145
146impl KernelResources<apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> for RedboardArtemisNano {
147    type SyscallDriverLookup = Self;
148    type SyscallFilter = ();
149    type ProcessFault = ();
150    type Scheduler = RoundRobinSched<'static>;
151    type SchedulerTimer = cortexm4::systick::SysTick;
152    type WatchDog = ();
153    type ContextSwitchCallback = ();
154
155    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
156        self
157    }
158    fn syscall_filter(&self) -> &Self::SyscallFilter {
159        &()
160    }
161    fn process_fault(&self) -> &Self::ProcessFault {
162        &()
163    }
164    fn scheduler(&self) -> &Self::Scheduler {
165        self.scheduler
166    }
167    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
168        &self.systick
169    }
170    fn watchdog(&self) -> &Self::WatchDog {
171        &()
172    }
173    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
174        &()
175    }
176}
177
178// Ensure that `setup()` is never inlined
179// This helps reduce the stack frame, see https://github.com/tock/tock/issues/3518
180#[inline(never)]
181unsafe fn setup() -> (
182    &'static kernel::Kernel,
183    &'static RedboardArtemisNano,
184    &'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
185    &'static Apollo3DefaultPeripherals,
186) {
187    let peripherals = static_init!(Apollo3DefaultPeripherals, Apollo3DefaultPeripherals::new());
188
189    // No need to statically allocate mcu/pwr/clk_ctrl because they are only used in main!
190    let mcu_ctrl = apollo3::mcuctrl::McuCtrl::new();
191    let pwr_ctrl = apollo3::pwrctrl::PwrCtrl::new();
192    let clkgen = apollo3::clkgen::ClkGen::new();
193
194    clkgen.set_clock_frequency(apollo3::clkgen::ClockFrequency::Freq48MHz);
195
196    // initialize capabilities
197    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
198    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
199
200    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
201
202    // Power up components
203    pwr_ctrl.enable_uart0();
204    pwr_ctrl.enable_iom0();
205    pwr_ctrl.enable_iom2();
206    pwr_ctrl.enable_ios();
207
208    peripherals.init();
209
210    // Enable PinCfg
211    peripherals
212        .gpio_port
213        .enable_uart(&peripherals.gpio_port[48], &peripherals.gpio_port[49]);
214    // Enable SDA and SCL for I2C2 (exposed via Qwiic)
215    peripherals
216        .gpio_port
217        .enable_i2c(&peripherals.gpio_port[25], &peripherals.gpio_port[27]);
218    // Enable Main SPI
219    peripherals.gpio_port.enable_spi(
220        &peripherals.gpio_port[5],
221        &peripherals.gpio_port[7],
222        &peripherals.gpio_port[6],
223    );
224    // Enable I2C slave device
225    peripherals
226        .gpio_port
227        .enable_i2c_slave(&peripherals.gpio_port[1], &peripherals.gpio_port[0]);
228
229    // Configure kernel debug gpios as early as possible
230    kernel::debug::assign_gpios(
231        Some(&peripherals.gpio_port[19]), // Blue LED
232        None,
233        None,
234    );
235
236    // Create a shared UART channel for the console and for kernel debug.
237    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
238        .finalize(components::uart_mux_component_static!());
239
240    // Setup the console.
241    let console = components::console::ConsoleComponent::new(
242        board_kernel,
243        capsules_core::console::DRIVER_NUM,
244        uart_mux,
245    )
246    .finalize(components::console_component_static!());
247    // Create the debugger object that handles calls to `debug!()`.
248    components::debug_writer::DebugWriterComponent::new(
249        uart_mux,
250        create_capability!(capabilities::SetDebugWriterCapability),
251    )
252    .finalize(components::debug_writer_component_static!());
253
254    // LEDs
255    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
256        LedHigh<'static, apollo3::gpio::GpioPin>,
257        LedHigh::new(&peripherals.gpio_port[19]),
258    ));
259
260    // GPIOs
261    // These are also ADC channels, but let's expose them as GPIOs
262    let gpio = components::gpio::GpioComponent::new(
263        board_kernel,
264        capsules_core::gpio::DRIVER_NUM,
265        components::gpio_component_helper!(
266            apollo3::gpio::GpioPin,
267            0 => &peripherals.gpio_port[13],  // A0
268            1 => &peripherals.gpio_port[33],  // A1
269            2 => &peripherals.gpio_port[11],  // A2
270            3 => &peripherals.gpio_port[29],  // A3
271            5 => &peripherals.gpio_port[31]  // A5
272        ),
273    )
274    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
275
276    // Create a shared virtualisation mux layer on top of a single hardware
277    // alarm.
278    let _ = peripherals.stimer.start();
279    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.stimer).finalize(
280        components::alarm_mux_component_static!(apollo3::stimer::STimer),
281    );
282    let alarm = components::alarm::AlarmDriverComponent::new(
283        board_kernel,
284        capsules_core::alarm::DRIVER_NUM,
285        mux_alarm,
286    )
287    .finalize(components::alarm_component_static!(apollo3::stimer::STimer));
288    ALARM = Some(mux_alarm);
289
290    // Create a process printer for panic.
291    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
292        .finalize(components::process_printer_text_component_static!());
293    PROCESS_PRINTER = Some(process_printer);
294
295    // Init the I2C device attached via Qwiic
296    let i2c_master_buffer = static_init!(
297        [u8; capsules_core::i2c_master::BUFFER_LENGTH],
298        [0; capsules_core::i2c_master::BUFFER_LENGTH]
299    );
300    let i2c_master = static_init!(
301        capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
302        capsules_core::i2c_master::I2CMasterDriver::new(
303            &peripherals.iom2,
304            i2c_master_buffer,
305            board_kernel.create_grant(
306                capsules_core::i2c_master::DRIVER_NUM,
307                &memory_allocation_cap
308            )
309        )
310    );
311
312    peripherals.iom2.set_master_client(i2c_master);
313    peripherals.iom2.enable();
314
315    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.iom2, None)
316        .finalize(components::i2c_mux_component_static!(apollo3::iom::Iom));
317
318    let bme280 = Bme280Component::new(mux_i2c, 0x77)
319        .finalize(components::bme280_component_static!(apollo3::iom::Iom));
320    let temperature = components::temperature::TemperatureComponent::new(
321        board_kernel,
322        capsules_extra::temperature::DRIVER_NUM,
323        bme280,
324    )
325    .finalize(components::temperature_component_static!(BME280Sensor));
326    let humidity = components::humidity::HumidityComponent::new(
327        board_kernel,
328        capsules_extra::humidity::DRIVER_NUM,
329        bme280,
330    )
331    .finalize(components::humidity_component_static!(BME280Sensor));
332    BME280 = Some(bme280);
333
334    let ccs811 = Ccs811Component::new(mux_i2c, 0x5B)
335        .finalize(components::ccs811_component_static!(apollo3::iom::Iom));
336    let air_quality = components::air_quality::AirQualityComponent::new(
337        board_kernel,
338        capsules_extra::temperature::DRIVER_NUM,
339        ccs811,
340    )
341    .finalize(components::air_quality_component_static!());
342    CCS811 = Some(ccs811);
343
344    // Init the SPI controller
345    let mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom0).finalize(
346        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
347    );
348
349    // The IOM0 expects an auto chip select on pin D11 or D15, neither of
350    // which are broken out on the board. So the CS control must be manual
351    let spi_controller = components::spi::SpiSyscallComponent::new(
352        board_kernel,
353        mux_spi,
354        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
355            &peripherals.gpio_port[35], // A14
356        ),
357        capsules_core::spi_controller::DRIVER_NUM,
358    )
359    .finalize(components::spi_syscall_component_static!(
360        apollo3::iom::Iom<'static>
361    ));
362
363    // Setup BLE
364    mcu_ctrl.enable_ble();
365    clkgen.enable_ble();
366    pwr_ctrl.enable_ble();
367    peripherals.ble.setup_clocks();
368    mcu_ctrl.reset_ble();
369    peripherals.ble.power_up();
370    peripherals.ble.ble_initialise();
371
372    let ble_radio = components::ble::BLEComponent::new(
373        board_kernel,
374        capsules_extra::ble_advertising_driver::DRIVER_NUM,
375        &peripherals.ble,
376        mux_alarm,
377    )
378    .finalize(components::ble_component_static!(
379        apollo3::stimer::STimer,
380        apollo3::ble::Ble,
381    ));
382
383    mcu_ctrl.print_chip_revision();
384
385    debug!("Initialization complete. Entering main loop");
386
387    // These symbols are defined in the linker script.
388    extern "C" {
389        /// Beginning of the ROM region containing app images.
390        static _sapps: u8;
391        /// End of the ROM region containing app images.
392        static _eapps: u8;
393        /// Beginning of the RAM region for app memory.
394        static mut _sappmem: u8;
395        /// End of the RAM region for app memory.
396        static _eappmem: u8;
397    }
398
399    let scheduler = components::sched::round_robin::RoundRobinComponent::new(&*addr_of!(PROCESSES))
400        .finalize(components::round_robin_component_static!(NUM_PROCS));
401
402    let systick = cortexm4::systick::SysTick::new_with_calibration(48_000_000);
403
404    let artemis_nano = static_init!(
405        RedboardArtemisNano,
406        RedboardArtemisNano {
407            alarm,
408            led,
409            gpio,
410            console,
411            i2c_master,
412            spi_controller,
413            ble_radio,
414            temperature,
415            humidity,
416            air_quality,
417            scheduler,
418            systick,
419        }
420    );
421
422    let chip = static_init!(
423        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
424        apollo3::chip::Apollo3::new(peripherals)
425    );
426    CHIP = Some(chip);
427
428    kernel::process::load_processes(
429        board_kernel,
430        chip,
431        core::slice::from_raw_parts(
432            core::ptr::addr_of!(_sapps),
433            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
434        ),
435        core::slice::from_raw_parts_mut(
436            core::ptr::addr_of_mut!(_sappmem),
437            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
438        ),
439        &mut *addr_of_mut!(PROCESSES),
440        &FAULT_RESPONSE,
441        &process_mgmt_cap,
442    )
443    .unwrap_or_else(|err| {
444        debug!("Error loading processes!");
445        debug!("{:?}", err);
446    });
447
448    (board_kernel, artemis_nano, chip, peripherals)
449}
450
451/// Main function.
452///
453/// This function is called from the arch crate after some very basic RISC-V
454/// setup and RAM initialization.
455#[no_mangle]
456pub unsafe fn main() {
457    apollo3::init();
458
459    #[cfg(test)]
460    test_main();
461
462    #[cfg(not(test))]
463    {
464        let (board_kernel, esp32_c3_board, chip, _peripherals) = setup();
465
466        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
467
468        board_kernel.kernel_loop(
469            esp32_c3_board,
470            chip,
471            None::<&kernel::ipc::IPC<{ NUM_PROCS as u8 }>>,
472            &main_loop_cap,
473        );
474    }
475}
476
477#[cfg(test)]
478use kernel::platform::watchdog::WatchDog;
479
480#[cfg(test)]
481fn test_runner(tests: &[&dyn Fn()]) {
482    unsafe {
483        let (board_kernel, esp32_c3_board, _chip, peripherals) = setup();
484
485        BOARD = Some(board_kernel);
486        PLATFORM = Some(&esp32_c3_board);
487        PERIPHERALS = Some(peripherals);
488        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
489
490        PLATFORM.map(|p| {
491            p.watchdog().setup();
492        });
493
494        for test in tests {
495            test();
496        }
497    }
498
499    loop {}
500}