#![no_main] #![no_std] extern crate alloc; use alloc::boxed::Box; use az::*; use core::cell::RefCell; use cortex_m::delay::Delay; use embedded_alloc::Heap; use embedded_hal::{blocking::spi::Write, digital::v2::InputPin, digital::v2::OutputPin}; use fixed::types::I16F16; use fugit::RateExtU32; use lights_core::{App, BodyPattern, DashboardPattern, Event, Instant, FPS, UI}; use panic_halt as _; use rp_pico::{ entry, hal::{ clocks::init_clocks_and_plls, gpio::{FunctionSio, Pin, PinId, PullUp, SioInput}, pac::{CorePeripherals, Peripherals}, spi::{Enabled, Spi, SpiDevice, ValidSpiPinout}, watchdog::Watchdog, Clock, Sio, }, Pins, }; #[global_allocator] static HEAP: Heap = Heap::empty(); const LIGHT_SCALE: I16F16 = I16F16::lit("256.0"); const DASHBOARD_BRIGHTESS: u8 = 1; const BODY_BRIGHTNESS: u8 = 8; struct DebouncedButton { debounce: Instant, pin: Pin, PullUp>, } impl DebouncedButton

{ fn new(pin: Pin, PullUp>) -> Self { Self { debounce: Instant((0 as u32).into()), pin, } } fn is_low(&self, time: Instant) -> bool { if time <= self.debounce { return false; } self.pin.is_low().unwrap_or(false) } fn set_debounce(&mut self, time: Instant) { self.debounce = time + Instant((250 as u32).into()); } } struct BikeUI< D: SpiDevice, P: ValidSpiPinout, LeftId: PinId, RightId: PinId, PreviousId: PinId, NextId: PinId, > { spi: RefCell>, left_blinker_button: DebouncedButton, right_blinker_button: DebouncedButton, previous_animation_button: DebouncedButton, next_animation_button: DebouncedButton, } impl< D: SpiDevice, P: ValidSpiPinout, LeftId: PinId, RightId: PinId, PreviousId: PinId, NextId: PinId, > BikeUI { fn new( spi: Spi, left_blinker_button: Pin, PullUp>, right_blinker_button: Pin, PullUp>, previous_animation_button: Pin, PullUp>, next_animation_button: Pin, PullUp>, ) -> Self { Self { spi: RefCell::new(spi), left_blinker_button: DebouncedButton::new(left_blinker_button), right_blinker_button: DebouncedButton::new(right_blinker_button), previous_animation_button: DebouncedButton::new(previous_animation_button), next_animation_button: DebouncedButton::new(next_animation_button), } } } impl< D: SpiDevice, P: ValidSpiPinout, LeftId: PinId, RightId: PinId, PreviousId: PinId, NextId: PinId, > UI for BikeUI { fn check_event(&mut self, current_time: Instant) -> Option { if self.left_blinker_button.is_low(current_time) { self.left_blinker_button.set_debounce(current_time); Some(Event::LeftBlinker) } else if self.right_blinker_button.is_low(current_time) { self.right_blinker_button.set_debounce(current_time); Some(Event::RightBlinker) } else if self.previous_animation_button.is_low(current_time) { self.previous_animation_button.set_debounce(current_time); Some(Event::PreviousPattern) } else if self.next_animation_button.is_low(current_time) { self.next_animation_button.set_debounce(current_time); Some(Event::NextPattern) } else { None } } fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern) { let mut lights: [u8; 260] = [0; 260]; lights[256] = 0xff; lights[257] = 0xff; lights[258] = 0xff; lights[259] = 0xff; for (idx, rgb) in dashboard_lights.iter().enumerate() { lights[(idx + 1) * 4 + 0] = 0xe0 + DASHBOARD_BRIGHTESS; lights[(idx + 1) * 4 + 1] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as(); lights[(idx + 1) * 4 + 2] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as(); lights[(idx + 1) * 4 + 3] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as(); } for (idx, rgb) in body_lights.iter().enumerate() { lights[(idx + 4) * 4 + 0] = 0xe0 + BODY_BRIGHTNESS; lights[(idx + 4) * 4 + 1] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as(); lights[(idx + 4) * 4 + 2] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as(); lights[(idx + 4) * 4 + 3] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as(); } let mut spi = self.spi.borrow_mut(); spi.write(lights.as_slice()); } } #[entry] fn main() -> ! { { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 8096; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } } let mut pac = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); let sio = Sio::new(pac.SIO); let mut watchdog = Watchdog::new(pac.WATCHDOG); let pins = Pins::new( pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS, ); let clocks = init_clocks_and_plls( 12_000_000u32, pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut watchdog, ) .ok() .unwrap(); let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); let mut spi_clk = pins.gpio10.into_function(); let mut spi_sdo = pins.gpio11.into_function(); let spi = Spi::<_, _, _, 8>::new(pac.SPI1, (spi_sdo, spi_clk)); let mut spi = spi.init( &mut pac.RESETS, clocks.peripheral_clock.freq(), 1_u32.MHz(), embedded_hal::spi::MODE_1, ); let left_blinker_button = pins.gpio18.into_pull_up_input(); let right_blinker_button = pins.gpio19.into_pull_up_input(); let previous_animation_button = pins.gpio20.into_pull_up_input(); let next_animation_button = pins.gpio21.into_pull_up_input(); let ui = BikeUI::new( spi, left_blinker_button, right_blinker_button, previous_animation_button, next_animation_button, ); let mut app = App::new(Box::new(ui)); let mut led_pin = pins.led.into_push_pull_output(); led_pin.set_high(); let mut time = Instant::default(); let delay_ms = 1000 / (FPS as u32); loop { app.tick(time); delay.delay_ms(delay_ms); time = time + Instant(delay_ms.into()); } }