#![no_main] #![no_std] mod display; mod information; mod pins; mod radio; mod sensors; // TODO: Enable warnings again. #[allow(unused)] mod assets { include!(concat!(env!("OUT_DIR"), "/assets.rs")); } use mkl25z4_hal::clocks::ClockConfiguration; use mkl25z4_hal::time::{CopyableMonoTimer, NonCopyableMonoTimer, U32Ext}; use panic_semihosting as _; use display::{Display, DisplayState}; use information::{Information, TemperatureHumidity}; use pins::{BME280Pins, DisplayBmeSpi}; use radio::Radio; #[rtfm::app(device = mkl25z4_hal::mkl25z4, peripherals = true)] const APP: () = { struct Resources { info: Information, display_state: DisplayState, display: Display, display_bme_spi: Option, bme_pins: Option, radio: Option, time: CopyableMonoTimer, } #[init(spawn = [fetch_update])] fn init(ctx: init::Context) -> init::LateResources { let mut sim = ctx.device.SIM; mkl25z4_hal::watchdog::disable(&mut sim); let clocks = ClockConfiguration::new() .use_irc() .core_clock(24.mhz()) .bus_clock(24.mhz()) .apply(&mut sim, ctx.device.OSC0, ctx.device.MCG); let mut pins = pins::Pins::configure( &mut sim, clocks, ctx.device.GPIOA, ctx.device.GPIOB, ctx.device.GPIOC, ctx.device.GPIOD, ctx.device.GPIOE, ctx.device.SPI0, ctx.device.SPI1, ); let time = CopyableMonoTimer::new(NonCopyableMonoTimer::new(ctx.device.PIT, clocks, &mut sim)); let display = Display::init(pins.display, &mut pins.display_bme_spi, time); let radio = Radio::init(pins.nrf, time); // Fetch the first update. ctx.spawn.fetch_update().ok(); init::LateResources { info: Information { time: 0, valid: false, inside: None, outside: None, bathroom: None, pressure: None, }, display_state: DisplayState{ // TODO }, display, display_bme_spi: Some(pins.display_bme_spi), bme_pins: Some(pins.bme), radio: Some(radio), time, } } #[task(binds = LPTMR0, priority = 3, spawn = [fetch_update])] fn lptmr0_interrupt(ctx: lptmr0_interrupt::Context) { // Restart the timer. // TODO // Trigger an update of the displayed values. ctx.spawn.fetch_update().ok(); } #[task(binds = LLWU, priority = 3)] fn llwu_interrupt(_: llwu_interrupt::Context) { // TODO } #[task(priority = 1, spawn = [update_display], resources = [info, bme_pins, display_bme_spi, &time, radio])] fn fetch_update(mut ctx: fetch_update::Context) { // Fetch local updates. let time = ctx.resources.time.clone(); let pins = ctx.resources.bme_pins.take().unwrap(); let (pins, values) = ctx.resources.display_bme_spi.lock(|display_bme_spi| { let spi = display_bme_spi.take().unwrap(); let (pins, spi, values) = sensors::bme280(pins, spi, time); *display_bme_spi = Some(spi); (pins, values) }); *ctx.resources.bme_pins = Some(pins); // Send local measurements to the base station. This needs to be done before fetching data // from the base station as the base station might return the data to us. let mut radio = ctx.resources.radio.as_mut().unwrap().enable(); radio.send_sensor_values(values); // TODO // Fetch updates via radio. // TODO ctx.resources.info.lock(|info| { info.inside = Some(TemperatureHumidity { temperature: values.temperature, humidity: values.humidity, }); info.pressure = Some(values.pressure); }); // If the update failed, simply increment the time and place a hint that // the information might be invalid. // TODO // Update the display to show the new values. ctx.spawn.update_display().ok(); } #[task(priority = 2, resources = [info, display_state, display, display_bme_spi, &time])] fn update_display(ctx: update_display::Context) { ctx.resources.display.update( &ctx.resources.display_state, &ctx.resources.info, ctx.resources.display_bme_spi.as_mut().unwrap(), ctx.resources.time.clone(), ); } #[idle] fn idle(_cx: idle::Context) -> ! { // Sleep loop { // wfi needs to be disabled for GDB /*// add some side effect to prevent this from turning into a UDF instruction // see rust-lang/rust#28728 for details atomic::compiler_fence(Ordering::SeqCst);*/ cortex_m::asm::wfi(); } } extern "C" { fn I2C0(); fn I2C1(); } };