#![no_std] #![no_main] mod dotstar; mod logger; mod macros; mod rtc; use clint::HandlerArray; use core::mem; use core::panic::PanicInfo; use cortex_m::asm::wfi; use cortex_m_rt::{entry, exception, ExceptionFrame}; use embedded_hal::{blocking::i2c::Write, digital::v2::OutputPin}; use log::{info, LevelFilter}; use smart_leds::colors; use smart_leds_trait::SmartLedsWrite; use trinket_m0::{ self as hal, clock::GenericClockController, gpio::{OpenDrain, Output, Pa10, Pa6, Pa7, PfD}, sercom, target_device::{interrupt, Interrupt}, time::*, CorePeripherals, Peripherals, }; // I²C on trinket is on SERCOM2 static mut LED: usize = 0; static HANDLERS: HandlerArray = HandlerArray::new(); const NRF_WIREADDR: u8 = 10; #[entry] fn main() -> ! { let mut cp = CorePeripherals::take().expect("taking core peripherals"); let mut dp = Peripherals::take().expect("taking device peripherals"); let mut clocks = GenericClockController::with_internal_32kosc( dp.GCLK, &mut dp.PM, &mut dp.SYSCTRL, &mut dp.NVMCTRL, ); let mut pins = hal::Pins::new(dp.PORT); let uart = hal::uart( &mut clocks, 115_200.hz(), dp.SERCOM0, &mut cp.NVIC, &mut dp.PM, pins.d3, pins.d4, &mut pins.port, ); let mut i2c_master = hal::i2c_master( &mut clocks, 400_000.hz(), dp.SERCOM2, &mut dp.PM, pins.d0, pins.d2, &mut pins.port, ); let mut red_led = pins.d13.into_open_drain_output(&mut pins.port); red_led.set_low().expect("turning off red LED"); unsafe { LED = mem::transmute(&red_led) } let mut dotstar = dotstar::new( dp.SERCOM1, pins.swdio, pins.dotstar_di, pins.dotstar_ci, &mut pins.port, &mut dp.PM, &mut clocks, ); let black = [colors::BLACK]; dotstar .write(black.iter().cloned()) .expect("turning off dotstar"); // We do the transmute because, while all the underlying data is // static, we're unable to get a referecence to the UART or LED // until run-time. Another option would be to use Option in the // SerialLogger definition, but that requires a check every time // they might be used. let uart_wrapped = logger::WriteWrapper::new(uart); let logger = logger::SerialLogger::new(uart_wrapped, red_led); // Wow, would I love to not be annotating this type. let logger_ref: &'static logger::SerialLogger< sercom::UART0>, sercom::Sercom0Pad2>, (), ()>, Pa10>, > = unsafe { mem::transmute(&logger) }; unsafe { log::set_logger_racy(logger_ref).expect("couldn't set logger") }; log::set_max_level(LevelFilter::Trace); let mut rtc_handler = rtc::setup(dp.RTC, &mut clocks); HANDLERS.with_overrides(|hs| { hs.register(0, &mut rtc_handler); cp.NVIC.enable(Interrupt::RTC); info!("Bootstrap complete."); let mut last_tick = 0; loop { let tick = rtc::millis(); if tick > last_tick + 1024 { last_tick = tick; i2c_master.write(NRF_WIREADDR, b"!").expect("writing i2c"); } wfi() } }); unreachable!(); } #[panic_handler] fn panic_handler(pi: &PanicInfo) -> ! { let red_led: &mut Pa10> = unsafe { mem::transmute(LED) }; red_led.set_high().ok(); logln_now!("~~~ PANIC ~~~"); logln_now!("{}", pi); logln_now!("flushing log"); loop { log::logger().flush(); wfi() } } #[exception] fn HardFault(ef: &ExceptionFrame) -> ! { let red_led: &mut Pa10> = unsafe { mem::transmute(LED) }; red_led.set_high().ok(); log::logger().flush(); logln_now!("!!! Hard Fault - ef: {:?} !!!", ef); logln_now!("flushing log"); loop { log::logger().flush(); wfi() } } #[interrupt] fn RTC() { HANDLERS.call(0); } #[interrupt] fn USB() { HANDLERS.call(1); }