#![cfg_attr(test, allow(unused_imports))] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] #![feature(half_open_range_patterns)] #![feature(exclusive_range_pattern)] #![allow(dead_code)] // custom panic handler #[cfg(not(test))] use core::panic::PanicInfo; use core::{cell::RefCell, ops::DerefMut}; use cortex_m::{interrupt::free, interrupt::Mutex, peripheral::NVIC}; use cortex_m_rt::entry; use stm32l4xx_hal::{ delay::Delay, device::{I2C1, TIM2, TIM7}, gpio::{ Alternate, Edge, Floating, Input, OpenDrain, Output, PullUp, PushPull, AF4, PA3, PB5, PC15, }, gpio::{State, PA10, PA9}, i2c::I2c, interrupt, pac, prelude::*, rcc, stm32::Interrupt, timer::Timer, }; mod ds3231; mod nixie; mod pca9685; mod tusb322; use nixie::*; static RTC_INT: Mutex>>>> = Mutex::new(RefCell::new(None)); static FAULT_INT: Mutex>>>> = Mutex::new(RefCell::new(None)); static FAULT_LED: Mutex>>>> = Mutex::new(RefCell::new(None)); static I2C: Mutex< RefCell< Option< I2c< I2C1, ( PA9>>, PA10>>, ), >, >, >, > = Mutex::new(RefCell::new(None)); static FPS_TIMER: Mutex>>> = Mutex::new(RefCell::new(None)); static CYCLE_TIMER: Mutex>>> = Mutex::new(RefCell::new(None)); // unsafe fn any_as_u8_slice(p: &T) -> &[u8] { // core::slice::from_raw_parts( // (p as *const T) as *const u8, // core::mem::size_of::(), // ) // } // pub fn concat(a: &[T; A], b: &[T; B]) -> [T; A+B] { // let mut whole: [T; A+B] = [Default::default(); A+B]; // let (one, two) = whole.split_at_mut(A); // one.copy_from_slice(a); // two.copy_from_slice(b); // whole // } #[cfg(not(test))] #[entry] fn main() -> ! { // Acquire a singleton instance for the chip's peripherals let mut dp = pac::Peripherals::take().unwrap(); let cp = pac::CorePeripherals::take().unwrap(); // Consume the raw peripheral and return a new object that implements a higher level API let mut flash = dp.FLASH.constrain(); let mut rcc = dp.RCC.constrain(); let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1); // Configure clocks to run at maximum frequency off internal oscillator let clocks = rcc .cfgr .pll_source(rcc::PllSource::HSI16) .sysclk(64.mhz()) .hclk(64.mhz()) .pclk1(64.mhz()) .pclk2(64.mhz()) .hsi48(true) .freeze(&mut flash.acr, &mut pwr); // Configure delay timer that operates off systick timer let mut delay_timer = Delay::new(cp.SYST, clocks); // Split GPIO peripheral into independent pins and registers let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2); let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2); let mut gpioc = dp.GPIOC.split(&mut rcc.ahb2); // Configure high voltage PSU enable pin on PA2 let mut hv_enable = gpioa .pa2 .into_push_pull_output_with_state(&mut gpioa.moder, &mut gpioa.otyper, State::Low); // Configure serial port // let tx = gpiob.pb6.into_af7(&mut gpiob.moder, &mut gpiob.afrl); // let rx = gpiob.pb7.into_af7(&mut gpiob.moder, &mut gpiob.afrl); // let _serial = Serial::usart1( // dp.USART1, // (tx, rx), // Config::default().baudrate(115_200.bps()), // clocks, // &mut rcc.apb2, // ); // Configure fault LED output on PC15 let fault_led = gpioc.pc15.into_push_pull_output_with_state( &mut gpioc.moder, &mut gpioc.otyper, State::Low, ); // Store fault LED in static singleton so that interrupt has access to it free(|cs| { FAULT_LED.borrow(cs).replace(Some(fault_led)); }); // Configure fault input interrupt on PA3 let mut fault_int = gpioa .pa3 .into_pull_up_input(&mut gpioa.moder, &mut gpioa.pupdr); fault_int.make_interrupt_source(&mut dp.SYSCFG, &mut rcc.apb2); fault_int.enable_interrupt(&mut dp.EXTI); fault_int.trigger_on_edge(&mut dp.EXTI, Edge::FALLING); // Sanity check that fault pin isn't already set (active low) before enabling interrupt if fault_int.is_high().unwrap() { // Configure NVIC mask to enable interrupt source unsafe { NVIC::unmask(Interrupt::EXTI3); } // Store fault interrupt in static singleton so that interrupt has access to it free(|cs| { FAULT_INT.borrow(cs).replace(Some(fault_int)); }); } else { panic!(); } // Configure I2C SCL let scl = gpioa .pa9 .into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper); let scl = scl.into_af4(&mut gpioa.moder, &mut gpioa.afrh); // Configure I2C SDA let sda = gpioa .pa10 .into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper); let sda = sda.into_af4(&mut gpioa.moder, &mut gpioa.afrh); // Initialize I2C (configured for 1Mhz, but actually runs at 600kHz) let mut i2c = I2c::i2c1(dp.I2C1, (scl, sda), 1.mhz(), clocks, &mut rcc.apb1r1); // Initialize TUSB322 (USB Type-C configuration chip) tusb322::init(TUSB322_ADDR, &mut i2c); // Initialize DS3231 (RTC) ds3231::init(DS3231_ADDR, &mut i2c); // Configure input interrupt pin from DS3231 on PB5 // Interrupt is pulled high, with open drain on DS3231 to pull low let mut rtc_int = gpiob .pb5 .into_floating_input(&mut gpiob.moder, &mut gpiob.pupdr); rtc_int.make_interrupt_source(&mut dp.SYSCFG, &mut rcc.apb2); rtc_int.enable_interrupt(&mut dp.EXTI); rtc_int.trigger_on_edge(&mut dp.EXTI, Edge::FALLING); // Configure NVIC mask to enable interrupt source unsafe { NVIC::unmask(Interrupt::EXTI9_5); } // Store RTC interrupt in static singleton so that interrupt has access to it free(|cs| { RTC_INT.borrow(cs).replace(Some(rtc_int)); }); // Configure DAC AMP enable pin for AD8591 on PB1 let mut _dac_enable = gpiob.pb1.into_push_pull_output_with_state( &mut gpiob.moder, &mut gpiob.otyper, State::High, ); // Configure DAC VIN for AD8591 on PA5 // Note that this pin should actually be configured as analog output (for DAC) // but stm32l4xx_hal doesn't have support for the DAC as of now. We also currently // set the output to only the highest possible voltage, so the same functionality // can be achieved by configuring the pin as a digital output set to high. let mut _dac_output = gpioa.pa5.into_push_pull_output_with_state( &mut gpioa.moder, &mut gpioa.otyper, State::High, ); // Configure PWM enable pin (active low) for PCA9685 on PA7 let mut pwm_enable = gpioa.pa7.into_push_pull_output_with_state( &mut gpioa.moder, &mut gpioa.otyper, State::High, ); // Initialize the PCA9685 display refresh timer let fps_timer = Timer::tim2( dp.TIM2, nixie::DISPLAY_REFRESH_FPS.hz(), clocks, &mut rcc.apb1r1, ); // Configure NVIC mask to enable interrupt for TIM2 unsafe { NVIC::unmask(Interrupt::TIM2) }; // Save display refresh timer in static singleton so that interrupt has access to it free(|cs| { FPS_TIMER.borrow(cs).replace(Some(fps_timer)); }); // Small delay to ensure that PCA9685 is fully powered on before writing to it delay_timer.delay_us(10_u32); // Initialize PCA9685 (PWM driver) pca9685::init(PCA9685_ALL_CALL, &mut i2c); // Enable PWM output after PCA9685 has been initialized pwm_enable.set_low().unwrap(); // Store I2C peripheral in global static variable as it is used in interrupt free(|cs| { I2C.borrow(cs).replace(Some(i2c)); }); let cycle_timer = Timer::tim7( dp.TIM7, (1000 / nixie::CYCLE_FADE_DURATION_MS).hz(), clocks, &mut rcc.apb1r1, ); unsafe { NVIC::unmask(Interrupt::TIM7) }; // Save display refresh timer in static singleton so that interrupt has access to it free(|cs| { CYCLE_TIMER.borrow(cs).replace(Some(cycle_timer)); }); let rng = dp.RNG.enable(&mut rcc.ahb2, clocks); // Enable the high voltage power supply last hv_enable.set_high().unwrap(); nixie::cycle_start(0); nixie::cycle_start(1); nixie::cycle_start(2); nixie::cycle_start(3); loop { // let rng_delay = nixie::CYCLE_REFRESH_INTERVAL + (rng.get_random_data() % nixie::CYCLE_REFRESH_VARIANCE); let rng_delay = nixie::CYCLE_REFRESH_INTERVAL; delay_timer.delay_ms(rng_delay * 1000); let tube = (rng.get_random_data() % 4) as usize; nixie::cycle_start(tube); } } fn set_fault_led(state: State) { free(|cs| { let mut led_ref = FAULT_LED.borrow(cs).borrow_mut(); if let Some(ref mut led) = led_ref.deref_mut() { match state { State::High => led.set_high().unwrap(), State::Low => led.set_low().unwrap(), }; } }); } #[interrupt] fn EXTI9_5() { free(|cs| { let mut rtc_int_ref = RTC_INT.borrow(cs).borrow_mut(); let mut i2c_int_ref = I2C.borrow(cs).borrow_mut(); if let Some(ref mut rtc_int) = rtc_int_ref.deref_mut() { if let Some(ref mut i2c) = i2c_int_ref.deref_mut() { if rtc_int.check_interrupt() { nixie::rtc_interrupt(i2c); rtc_int.clear_interrupt_pending_bit(); } } } }); } #[interrupt] fn EXTI3() { free(|cs| { let mut nfault_ref = FAULT_INT.borrow(cs).borrow_mut(); if let Some(ref mut nfault) = nfault_ref.deref_mut() { if nfault.check_interrupt() { nfault.clear_interrupt_pending_bit(); panic!(); } } }); } #[interrupt] fn TIM2() { free(|cs| { let mut i2c_int_ref = I2C.borrow(cs).borrow_mut(); if let Some(ref mut i2c) = i2c_int_ref.deref_mut() { nixie::fps_interrupt(i2c); } }); } #[interrupt] fn TIM7() { nixie::cycle_interrupt(); } #[panic_handler] #[cfg(not(test))] /// Custom panic handler fn panic(_info: &PanicInfo) -> ! { set_fault_led(State::High); loop { continue; } }