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