#![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] // app stuff mod insert_coin; // system stuff mod system; mod debounced_gpio; use debounced_gpio::DebouncedGPIO; // synthesizer :3 mod synthesizer; use synthesizer::SynthesizerService; // sequences mod sequences; use sequences::SEQUENCE_LIST; mod app; use app::{ sequencer::BasicSequence, App, Config, Interfaces, Sequences, Services, State, TimerConfig, }; use ch32_hal::{adc::AdcChannel, interrupt::typelevel::Handler, timer::low_level::OutputPolarity}; use insert_coin::{CoreConfig, DacService, InsertCoin, LedService, SimplePwmCore}; use ch32_hal as hal; use hal::bind_interrupts; use hal::delay::Delay; use hal::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; use hal::time::Hertz; use hal::timer::low_level::CountingMode; use hal::timer::simple_pwm::{PwmPin, SimplePwm}; use hal::println; use qingke::riscv; use crate::app::sequencer::{DynamicSequence, SequenceEntry}; static LED0_SEQ: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8]; pub struct Amplifier { amp_en: Output<'static>, } impl Amplifier { pub fn new(amp_en: Output<'static>) -> Self { let mut amp = Self { amp_en }; amp.disable(); amp } pub fn enable(&mut self) { self.amp_en.set_low(); } pub fn disable(&mut self) { self.amp_en.set_high(); } pub fn enabled(&self) -> bool { !self.amp_en.is_set_high() } } use hal::adc::Adc; use hal::peripherals::{ADC1, PD4}; pub struct AdcCore { adc: Adc<'static, ADC1>, battery_pin: PD4, batt_values: [u16; 10], index: usize, } impl AdcCore { pub fn new(mut adc: Adc<'static, ADC1>, pin: PD4) -> Self { riscv::asm::delay(20_000); adc.calibrate(); Self { adc, battery_pin: pin, batt_values: [1024; 10], index: 0, } } // TODO make this a float or something pub fn get_battery_voltage(&mut self) -> u16 { let val = self .adc .convert(&mut self.battery_pin, hal::adc::SampleTime::CYCLES241); self.batt_values[self.index] = val; self.index += 1; if self.index > &self.batt_values.len() - 1 { self.index = 0; } val } pub fn get_average(&self) -> u16 { let mut sum = 0; for value in &self.batt_values { sum += value; } sum / self.batt_values.len() as u16 } } #[derive(Debug)] struct Flag { value: bool, } impl Flag { pub fn active(&self) -> bool { unsafe { core::ptr::read_volatile(&raw const self.value as *const bool) } } pub fn set(&mut self) { unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, true) } } pub fn clear(&mut self) { unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, false) } } } #[derive(Debug)] struct InputFlags { sense_coin_flag: Flag, main_btn_flag: Flag, volume_btn_flag: bool, light_ctrl_btn_flag: bool, systick_flag: Flag, } impl Default for InputFlags { fn default() -> Self { Self { sense_coin_flag: Flag { value: false }, main_btn_flag: Flag { value: false }, volume_btn_flag: false, light_ctrl_btn_flag: false, systick_flag: Flag { value: false }, } } } static mut INPUT_FLAGS: InputFlags = InputFlags { sense_coin_flag: Flag { value: false }, main_btn_flag: Flag { value: false }, volume_btn_flag: false, light_ctrl_btn_flag: false, systick_flag: Flag { value: false }, }; struct Test {} impl Handler for Test { unsafe fn on_interrupt() { // #[cfg(feature = "enable_print")] // println!("on_interrupt()"); critical_section::with(|_| unsafe { let flags = system::clear_interrupt(2, 6); if flags[0] { // safe because single-threaded #[allow(static_mut_refs)] INPUT_FLAGS.sense_coin_flag.set(); } if flags[1] { #[allow(static_mut_refs)] INPUT_FLAGS.main_btn_flag.set(); } }); } } #[qingke_rt::interrupt(core)] fn SysTick() { let r = &ch32_hal::pac::SYSTICK; // Clear interrupt flag r.sr().write(|w| w.set_cntif(false)); unsafe { // safe because single-threaded #[allow(static_mut_refs)] INPUT_FLAGS.systick_flag.set(); } } fn systick_init(tick_freq_hz: usize) { let r = &ch32_hal::pac::SYSTICK; // Calculate counts per millisecond using HCLK/8 as clock source // HCLK/8 = 48MHz/8 = 6MHz // For tick_freq_hz interrupt: 6MHz / tick_freq_hz let systick_per_tick = (48_000_000 / 8 / tick_freq_hz) as u32; // Reset SysTick r.ctlr().write(|w| { // Start with everything disabled }); // Set compare register and reset counter r.cmp().write_value(systick_per_tick - 1); r.cnt().write_value(0); // Clear interrupt flag r.sr().write(|w| w.set_cntif(false)); // Configure and start SysTick r.ctlr().write(|w| { w.set_ste(true); // Enable counter w.set_stie(true); // Enable interrupt w.set_stre(true); // Auto reload enable w.set_stclk(ch32_hal::pac::systick::vals::Stclk::HCLK_DIV8); // HCLK/8 clock source }); } bind_interrupts!(struct Irqs { EXTI7_0 => Test; }); // TODO: remove use insert_coin::TickTimerService; use insert_coin::{TickService, TickServiceData}; fn app_main(mut p: hal::Peripherals) -> ! { // === output setup === // LED0 output setup let led0_pin = PwmPin::new_ch3::<0>(p.PC3); let led0_ch = hal::timer::Channel::Ch3; // LED1 output setup let led1_pin = PwmPin::new_ch1::<0>(p.PD2); let led1_ch = hal::timer::Channel::Ch1; // // LED2 output setup // let led2_pin = PwmPin::new_ch2::<0>(p.PA1); // let led2_ch = hal::timer::Channel::Ch2; // DAC output setup let dac_pin = PwmPin::new_ch4::<0>(p.PC4); // let dac_ch = hal::timer::Channel::Ch4; // PWM timer setup let mut pwm = SimplePwm::new( p.TIM1, Some(led1_pin), // Some(led2_pin), None, Some(led0_pin), Some(dac_pin), Hertz::khz(200), CountingMode::default(), ); pwm.set_polarity(led0_ch, OutputPolarity::ActiveHigh); pwm.set_polarity(led1_ch, OutputPolarity::ActiveLow); // pwm.set_polarity(led2_ch, OutputPolarity::ActiveLow); let tick_rate_hz = 50000; let core_config = CoreConfig::new(tick_rate_hz); let pwm_core = SimplePwmCore::new(pwm); // === input setup === // adc let mut adc = hal::adc::Adc::new(p.ADC1, Default::default()); let mut batt_monitor_pin = p.PD4; let adc_core = AdcCore::new(adc, batt_monitor_pin); // adc2 // let mut usb_detect_dc = hal::adc::Adc::new(p.ADC1, Default::default()); let mut usb_detect_pin = p.PD5; // println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel()); // #[cfg(feature = "enable_print")] // println!("ADC calibration value: {}", adc_cal); // definitions let sense_coin_pin = p.PC2; let main_btn_pin = p.PD6; let volume_btn_pin = p.PC6; let light_ctrl_btn_pin = p.PC7; let amp_en = p.PC5; let extra_io_1 = p.PD0; let extra_io_2 = p.PD3; let mut amp_en_output = Output::new(amp_en, Level::Low, Default::default()); let amp = Amplifier::new(amp_en_output); // set up interrupts unsafe { system::init_gpio_irq(sense_coin_pin.pin(), sense_coin_pin.port(), true, false) }; unsafe { system::init_gpio_irq(main_btn_pin.pin(), main_btn_pin.port(), true, true) }; // coin debouncer (100ms) let mut sense_coin_input = DebouncedGPIO::new(sense_coin_pin.degrade(), core_config.tick_rate_hz, 20); // main button debouncer (100ms) let mut main_btn_input = DebouncedGPIO::new(main_btn_pin.degrade(), core_config.tick_rate_hz, 20); // button debouncer (100ms) let mut volume_btn_input = DebouncedGPIO::new(volume_btn_pin.degrade(), core_config.tick_rate_hz, 100); // button debouncer (100ms) let mut light_ctrl_btn_input = DebouncedGPIO::new(light_ctrl_btn_pin.degrade(), core_config.tick_rate_hz, 100); let timer_config = TimerConfig { sp_timer_ms: 1000, lp_timer_ms: 3000, batt_adc_timer_ms: 1000, usb_adc_timer_ms: 10000, led0_timer_ms: 100, led1_timer_ms: 100, shutdown_timer_ms: 4 * 60 * 60 * 1000, // led2_timer_ms: 100, }; let app_config = Config { system_tick_rate_hz: tick_rate_hz, timers: timer_config, }; // DAC servicer setup let dac_sample_rate_hz = 16000; let dac_tick_per_service = tick_rate_hz / dac_sample_rate_hz; let dac_service_data = TickServiceData::new(dac_tick_per_service); // let coin_sound = include_bytes!("../audio/coin5.raw"); // let coin_sound = include_bytes!("../audio/coin2.raw"); let sample_player = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data); // sample_player.load_data(coin_sound); let sequencer = app::sequencer::DynamicSequence::new(&SEQUENCE_LIST[0].0, tick_rate_hz); let app_services = Services { led0: LedService::new(led0_ch), led1: LedService::new(led1_ch), // led2: LedService::new(led2_ch), synth0: SynthesizerService::new(tick_rate_hz), sample_player, sequencer, }; let app_sequences = Sequences { led0: BasicSequence::new(&LED0_SEQ), led1: BasicSequence::new(&LED0_SEQ), // led2: BasicSequence::new(&LED0_SEQ), audio: &SEQUENCE_LIST, }; let app_interfaces = Interfaces { pwm_core, adc_core, amp, }; let mut app = App::new(app_config, app_services, app_sequences, app_interfaces); // init systick systick_init(tick_rate_hz); // set up interrupts unsafe { use hal::pac::Interrupt; use qingke::interrupt::Priority; use qingke_rt::CoreInterrupt; system::clear_interrupt(2, 6); qingke::pfic::set_priority(CoreInterrupt::SysTick as u8, Priority::P15 as u8); qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8); } // MAIN APPLICATION // process // -depress big button (insert coin button) and it wakes up and turns on led one led at a fixed brightness // -we will want one sound, the coin insert sound, to play when coin button is pressed. (This is when they insert a coin) // -We will want a different sound or potentially multiple different sounds played in a rotating fashion when someone presses the button. Probably do some led blinking as well.(This is when they depress the big insert to play button) // -depress the big button for approx 2s and it puts the led into low light mode. Just making it dimmer than usual. // -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw // #[cfg(feature = "enable_print")] // println!("begin"); let mut volume_btn_prev = volume_btn_input.is_high_immediate(); let mut light_ctrl_btn_prev = light_ctrl_btn_input.is_high_immediate(); app.init(); loop { // system servicing // volume edge detector if !volume_btn_input.active() { let volume_btn_curr = volume_btn_input.is_high_immediate(); if volume_btn_prev != volume_btn_curr { volume_btn_input.begin(); volume_btn_prev = volume_btn_curr; } } volume_btn_input.service(); if volume_btn_input.ready() { // #[cfg(feature = "enable_print")] // println!("volume btn value: {}", volume_btn_input.value()); if !volume_btn_input.value() { app.volume_button(); } volume_btn_input.reset(); } // brightness edge detector if !light_ctrl_btn_input.active() { let light_ctrl_btn_curr = light_ctrl_btn_input.is_high_immediate(); if light_ctrl_btn_prev != light_ctrl_btn_curr { light_ctrl_btn_input.begin(); light_ctrl_btn_prev = light_ctrl_btn_curr; } } light_ctrl_btn_input.service(); if light_ctrl_btn_input.ready() { #[cfg(feature = "enable_print")] println!("brightness btn value: {}", light_ctrl_btn_input.value()); if !light_ctrl_btn_input.value() { app.brightness_button(); } light_ctrl_btn_input.reset(); } // coin_detect interrupt unsafe { #[allow(static_mut_refs)] if INPUT_FLAGS.sense_coin_flag.active() { #[allow(static_mut_refs)] INPUT_FLAGS.sense_coin_flag.clear(); sense_coin_input.begin(); } sense_coin_input.service(); if sense_coin_input.ready() { sense_coin_input.reset(); app.coin_detect(); } } // main button handling unsafe { #[allow(static_mut_refs)] if INPUT_FLAGS.main_btn_flag.active() { #[allow(static_mut_refs)] INPUT_FLAGS.main_btn_flag.clear(); main_btn_input.begin(); } } main_btn_input.service(); if main_btn_input.ready() { let value = main_btn_input.value(); main_btn_input.reset(); // #[cfg(feature = "enable_print")] // println!("main button", value); match value { true => app.main_button_press(), false => app.main_button_release(), } } // systick tick if unsafe { #[allow(static_mut_refs)] INPUT_FLAGS.systick_flag.active() } { unsafe { #[allow(static_mut_refs)] INPUT_FLAGS.systick_flag.clear(); } // app tick app.tick(); } app.service(); match app.get_state() { // enter standby app::State::DeepSleep => { app.shut_down(); loop { unsafe { system::enter_standby() }; unsafe { #[allow(static_mut_refs)] INPUT_FLAGS.sense_coin_flag.clear(); #[allow(static_mut_refs)] INPUT_FLAGS.main_btn_flag.clear(); } riscv::asm::wfi(); let mut config = hal::Config::default(); config.rcc = hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI; unsafe { hal::rcc::init(config.rcc); } unsafe { #[allow(static_mut_refs)] if INPUT_FLAGS.sense_coin_flag.active() || (INPUT_FLAGS.main_btn_flag.active() && main_btn_input.is_high_immediate()) { unsafe { use hal::pac::Interrupt; use qingke::interrupt::Priority; use qingke_rt::CoreInterrupt; system::clear_interrupt(2, 6); qingke::pfic::set_priority( CoreInterrupt::SysTick as u8, Priority::P15 as u8, ); qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8); } app.set_state(State::Active); break; } } } } // for everything else, don't do anything _ => {} } } } // // if adc1_timer.need_service() { // // let val = adc.convert(&mut batt_monitor_pin, hal::adc::SampleTime::CYCLES241); // // let val = adc.convert(&mut usb_detect_pin, hal::adc::SampleTime::CYCLES241); // // #[cfg(feature = "enable_print")] // // println!("ADC value: {}", val); // // adc1_timer.reset(); // // adc1_timer.enable(true); // // } // } #[qingke_rt::entry] fn main() -> ! { #[cfg(feature = "enable_print")] hal::debug::SDIPrint::enable(); let mut config = hal::Config::default(); config.rcc = hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI; let mut p = hal::init(config); // delay to let the debugger attach // println!("pre"); riscv::asm::delay(20_000_000); // println!("post"); // debug_main(p); app_main(p); } #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { // println!("panic: {info:?}"); loop {} }