From 9e67026345b39da3a2f08385cc5d30db7f7d258b Mon Sep 17 00:00:00 2001 From: sigil-03 Date: Wed, 5 Nov 2025 19:42:44 -0700 Subject: [PATCH] dynamic sequencer support --- ch32v-insert-coin/src/app.rs | 108 +++++++++++++++++++++++++-- ch32v-insert-coin/src/main.rs | 32 +++++--- ch32v-insert-coin/src/synthesizer.rs | 26 +++++-- 3 files changed, 144 insertions(+), 22 deletions(-) diff --git a/ch32v-insert-coin/src/app.rs b/ch32v-insert-coin/src/app.rs index 9a4e69d..17c2a77 100644 --- a/ch32v-insert-coin/src/app.rs +++ b/ch32v-insert-coin/src/app.rs @@ -93,6 +93,84 @@ pub mod sequencer { self.sequence[self.index] } } + + pub struct SequenceEntry { + pub frequency_hz: u16, + pub duration_ms: u16, + } + + pub struct DynamicSequence<'a> { + sequence: &'a [SequenceEntry], + index: usize, + + // timer stuff + system_tick_rate_hz: usize, + ticks_remaining: usize, + + // playback stuff + num_loops: usize, + loop_count: usize, + + // misc + enabled: bool, + } + + impl<'a> DynamicSequence<'a> { + pub fn new(sequence: &'a [SequenceEntry], system_tick_rate_hz: usize) -> Self { + Self { + sequence, + index: 0, + system_tick_rate_hz, + ticks_remaining: 0, + num_loops: 1, + loop_count: 0, + enabled: true, + } + } + + pub fn play_sequence(&mut self, sequence: &'a [SequenceEntry], num_loops: usize) { + self.sequence = sequence; + self.num_loops = num_loops; + self.loop_count = 0; + } + + pub fn enable(&mut self) { + self.enabled = true; + } + + pub fn disable(&mut self) { + self.enabled = false; + } + + pub fn tick(&mut self) { + if self.enabled { + self.ticks_remaining = self.ticks_remaining.saturating_sub(1); + } + } + + pub fn need_service(&self) -> bool { + self.ticks_remaining == 0 + } + + pub fn service(&mut self) -> Option { + let entry = &self.sequence[self.index]; + self.ticks_remaining = self.system_tick_rate_hz * (entry.duration_ms as usize) / 1000; + self.index = self.index.saturating_add(1); + + let out = if self.loop_count > self.num_loops { + None + } else { + Some(entry.frequency_hz) + }; + + if self.index > self.sequence.len() - 1 { + self.index = 0; + self.loop_count = self.loop_count.saturating_add(1); + } + + out + } + } } use crate::insert_coin::{ @@ -194,12 +272,14 @@ pub struct Services { pub led2: LedService, pub synth0: SynthesizerService, pub sample_player: DacService<'static>, + pub sequencer: sequencer::DynamicSequence<'static>, } impl Services { pub fn tick(&mut self) { self.synth0.tick(); self.sample_player.tick(); + self.sequencer.tick(); } } @@ -354,19 +434,31 @@ impl App { self.services.led2.service(); } + if self.services.sequencer.need_service() { + if let Some(out) = self.services.sequencer.service() { + self.services.synth0.set_freq(out.into()); + } else { + self.services.sequencer.disable(); + self.services.synth0.disable(); + } + } + if self.services.synth0.need_service() { - let out = self.services.synth0.service() / self.settings.volume.as_volume_divisor(); - // self.interfaces - // .pwm_core - // .write_amplitude(ch32_hal::timer::Channel::Ch4, out); + let out = match self.services.synth0.service() { + Some(value) => value / self.settings.volume.as_volume_divisor(), + None => 0, + }; + self.interfaces + .pwm_core + .write_amplitude(ch32_hal::timer::Channel::Ch4, out); } if self.services.sample_player.need_service() { self.services.sample_player.service(); - let out = self.services.sample_player.get_amplitude(); - self.interfaces - .pwm_core - .write_amplitude(ch32_hal::timer::Channel::Ch4, out as u8); + // let out = self.services.sample_player.get_amplitude(); + // self.interfaces + // .pwm_core + // .write_amplitude(ch32_hal::timer::Channel::Ch4, out as u8); } } } diff --git a/ch32v-insert-coin/src/main.rs b/ch32v-insert-coin/src/main.rs index e993c35..c7e2368 100644 --- a/ch32v-insert-coin/src/main.rs +++ b/ch32v-insert-coin/src/main.rs @@ -36,7 +36,19 @@ 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]; +static TEST_SEQ: [app::sequencer::SequenceEntry; 2] = [ + SequenceEntry { + frequency_hz: 440, + duration_ms: 1000, + }, + SequenceEntry { + frequency_hz: 220, + duration_ms: 1000, + }, +]; #[derive(Debug)] struct Flag { @@ -86,8 +98,8 @@ static mut INPUT_FLAGS: InputFlags = InputFlags { struct Test {} impl Handler for Test { unsafe fn on_interrupt() { - #[cfg(feature = "enable_print")] - println!("on_interrupt()"); + // #[cfg(feature = "enable_print")] + // println!("on_interrupt()"); critical_section::with(|_| unsafe { let flags = system::clear_interrupt(2, 6); if flags[0] { @@ -206,8 +218,8 @@ fn app_main(mut p: hal::Peripherals) -> ! { // println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel()); let adc_cal = adc.calibrate(); - #[cfg(feature = "enable_print")] - println!("ADC calibration value: {}", adc_cal); + // #[cfg(feature = "enable_print")] + // println!("ADC calibration value: {}", adc_cal); // definitions let sense_coin_pin = p.PC2; @@ -260,12 +272,15 @@ fn app_main(mut p: hal::Peripherals) -> ! { 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(&TEST_SEQ, 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 { @@ -303,9 +318,8 @@ fn app_main(mut p: hal::Peripherals) -> ! { // -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"); - + // #[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(); @@ -324,8 +338,8 @@ fn app_main(mut p: hal::Peripherals) -> ! { volume_btn_input.service(); if volume_btn_input.ready() { - #[cfg(feature = "enable_print")] - println!("volume btn value: {}", volume_btn_input.value()); + // #[cfg(feature = "enable_print")] + // println!("volume btn value: {}", volume_btn_input.value()); if !volume_btn_input.value() { app.volume_button(); } diff --git a/ch32v-insert-coin/src/synthesizer.rs b/ch32v-insert-coin/src/synthesizer.rs index b91da38..d3cb3a1 100644 --- a/ch32v-insert-coin/src/synthesizer.rs +++ b/ch32v-insert-coin/src/synthesizer.rs @@ -11,6 +11,8 @@ const SQUARE_WAVETABLE: [u8; 2] = [0, 100]; pub struct SynthesizerService { pub synth: SimpleWavetableSynthesizer>, + pub enabled: bool, + pub need_service: bool, } impl SynthesizerService { @@ -18,19 +20,29 @@ impl SynthesizerService { let square_wt = SimpleWavetable::new(&SQUARE_WAVETABLE); let synth = SimpleWavetableSynthesizer::new(square_wt, clock_freq_hz); - Self { synth } + Self { + synth, + enabled: true, + need_service: false, + } } pub fn tick(&mut self) { - self.synth.tick(); + if self.enabled { + self.synth.tick(); + } } pub fn need_service(&self) -> bool { - self.synth.has_new_output() + self.need_service || self.synth.has_new_output() } - pub fn service(&mut self) -> u8 { - self.synth.get_output() + pub fn service(&mut self) -> Option { + if self.enabled { + Some(self.synth.get_output()) + } else { + None + } } } @@ -40,10 +52,14 @@ impl SynthesizerService { } pub fn enable(&mut self) { + self.enabled = true; + self.need_service = true; // TODO: write the enable function } pub fn disable(&mut self) { + self.enabled = false; + self.need_service = true; // TODO: write the disable function } }