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