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