Compare commits
No commits in common. "main" and "cleanup" have entirely different histories.
30 changed files with 399 additions and 1766 deletions
9
.gitmodules
vendored
9
.gitmodules
vendored
|
|
@ -1,12 +1,9 @@
|
||||||
[submodule "ch32v-insert-coin/ext/ch32-hal"]
|
[submodule "ch32v-insert-coin/ext/ch32-hal"]
|
||||||
path = ch32v-insert-coin/ext/ch32-hal
|
path = ch32v-insert-coin/ext/ch32-hal
|
||||||
url = https://github.com/sigil-03/ch32-hal.git
|
url = git@github.com:sigil-03/ch32-hal.git
|
||||||
[submodule "ch32v-insert-coin/ext/qingke"]
|
[submodule "ch32v-insert-coin/ext/qingke"]
|
||||||
path = ch32v-insert-coin/ext/qingke
|
path = ch32v-insert-coin/ext/qingke
|
||||||
url = https://github.com/ch32-rs/qingke.git
|
url = git@github.com:ch32-rs/qingke.git
|
||||||
[submodule "ch32v-insert-coin/ext/adpcm-pwm-dac"]
|
[submodule "ch32v-insert-coin/ext/adpcm-pwm-dac"]
|
||||||
path = ch32v-insert-coin/ext/adpcm-pwm-dac
|
path = ch32v-insert-coin/ext/adpcm-pwm-dac
|
||||||
url = https://git.glyphs.tech/sigil-03/adpcm-pwm-dac.git
|
url = ssh://git@git.glyphs.tech:222/sigil-03/adpcm-pwm-dac.git
|
||||||
[submodule "ch32v-insert-coin/ext/wavetable-synth"]
|
|
||||||
path = ch32v-insert-coin/ext/wavetable-synth
|
|
||||||
url = https://git.glyphs.tech/sigil-03/wavetable-synth.git
|
|
||||||
|
|
|
||||||
5
ch32v-insert-coin/Cargo.lock
generated
5
ch32v-insert-coin/Cargo.lock
generated
|
|
@ -78,7 +78,6 @@ dependencies = [
|
||||||
"panic-halt",
|
"panic-halt",
|
||||||
"qingke 0.5.0",
|
"qingke 0.5.0",
|
||||||
"qingke-rt",
|
"qingke-rt",
|
||||||
"wavetable-synth",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -627,7 +626,3 @@ name = "void"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wavetable-synth"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ qingke = {path = "ext/qingke"}
|
||||||
|
|
||||||
adpcm-pwm-dac = { path = "ext/adpcm-pwm-dac/" }
|
adpcm-pwm-dac = { path = "ext/adpcm-pwm-dac/" }
|
||||||
|
|
||||||
wavetable-synth = { path = "ext/wavetable-synth" }
|
|
||||||
|
|
||||||
critical-section = { version = "1.2.0" }
|
critical-section = { version = "1.2.0" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,18 +0,0 @@
|
||||||
"wahwahwahwah" sound - let play a few times
|
|
||||||
note timing: 6hz
|
|
||||||
data: let freqs = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
||||||
|
|
||||||
"falling" sound
|
|
||||||
note timing: 6hz
|
|
||||||
[
|
|
||||||
1000,
|
|
||||||
990, 980, 970, 960, 950, 940, 930, 920, 910, 900,
|
|
||||||
890, 880, 870, 860, 850, 840, 830, 820, 810, 800,
|
|
||||||
790, 780, 770, 760, 750, 740, 730, 720, 710, 700,
|
|
||||||
690, 680, 670, 660, 650, 640, 630, 620, 610, 600,
|
|
||||||
590, 580, 570, 560, 550, 540, 530, 520, 510, 500,
|
|
||||||
490, 480, 470, 460, 450, 440, 430, 420, 410, 400,
|
|
||||||
390, 380, 370, 360, 350, 340, 330, 320, 310, 300,
|
|
||||||
290, 280, 270, 260, 250, 240, 230, 220, 210, 200,
|
|
||||||
190, 180, 170, 160, 150, 140, 130, 120, 110, 100,
|
|
||||||
]
|
|
||||||
BIN
ch32v-insert-coin/bins.zip
Normal file
BIN
ch32v-insert-coin/bins.zip
Normal file
Binary file not shown.
4
ch32v-insert-coin/bins/README.md
Normal file
4
ch32v-insert-coin/bins/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
TO FLASH / RUN:
|
||||||
|
|
||||||
|
`wlink -v flash --enable-sdi-print --watch-serial bins/coin_sound_8ksps.bin`
|
||||||
|
|
||||||
BIN
ch32v-insert-coin/bins/coin_sound_16ksps.bin
Executable file
BIN
ch32v-insert-coin/bins/coin_sound_16ksps.bin
Executable file
Binary file not shown.
BIN
ch32v-insert-coin/bins/coin_sound_6ksps.bin
Executable file
BIN
ch32v-insert-coin/bins/coin_sound_6ksps.bin
Executable file
Binary file not shown.
BIN
ch32v-insert-coin/bins/coin_sound_8ksps.bin
Executable file
BIN
ch32v-insert-coin/bins/coin_sound_8ksps.bin
Executable file
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
Subproject commit 99ce71e8d03e382b51732db7d0771349c51c7f48
|
Subproject commit ba25b7c89f4deb52426d97fd35eb13496f183775
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4f11d68e62dcb0e7098eecf357168724a8322d80
|
Subproject commit f41336744c4e2548c8f6ba9b323ae4aa39959f1d
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 30033e1438c25aed4ea506cdbd69cc4ceffa0b4e
|
|
||||||
|
|
@ -1,705 +0,0 @@
|
||||||
#[derive(Default, Clone, Copy)]
|
|
||||||
pub enum State {
|
|
||||||
// system is asleep, waiting for wake from coin insertion
|
|
||||||
DeepSleep,
|
|
||||||
// system is in low-power mode, dimmed lights, waiting for interaction
|
|
||||||
Idle,
|
|
||||||
// system is active. on entry: play coin sound. on button press: play different sound
|
|
||||||
#[default]
|
|
||||||
Active,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod settings {
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
|
||||||
pub enum Level {
|
|
||||||
Off,
|
|
||||||
Low,
|
|
||||||
#[default]
|
|
||||||
Medium,
|
|
||||||
High,
|
|
||||||
Maximum,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Level {
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
*self = match self {
|
|
||||||
Self::Off => Self::Low,
|
|
||||||
Self::Low => Self::Medium,
|
|
||||||
Self::Medium => Self::High,
|
|
||||||
Self::High => Self::Maximum,
|
|
||||||
Self::Maximum => Self::Off,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// volume control
|
|
||||||
impl Level {
|
|
||||||
pub fn as_volume_divisor(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Off => u8::MAX,
|
|
||||||
Self::Low => 4,
|
|
||||||
Self::Medium => 3,
|
|
||||||
Self::High => 2,
|
|
||||||
Self::Maximum => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_brightness_divisor(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Off => u8::MAX,
|
|
||||||
Self::Low => 8,
|
|
||||||
Self::Medium => 6,
|
|
||||||
Self::High => 4,
|
|
||||||
Self::Maximum => 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub brightness: Level,
|
|
||||||
pub volume: Level,
|
|
||||||
pub button_sound_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Settings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
brightness: Level::Medium,
|
|
||||||
volume: Level::Medium,
|
|
||||||
button_sound_index: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod sequencer {
|
|
||||||
pub struct BasicSequence<'a> {
|
|
||||||
sequence: &'a [u8],
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
impl<'a> BasicSequence<'a> {
|
|
||||||
pub fn new(sequence: &'a [u8]) -> Self {
|
|
||||||
Self { sequence, index: 0 }
|
|
||||||
}
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
self.index += 1;
|
|
||||||
if self.index > self.sequence.len() - 1 {
|
|
||||||
self.index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_value(&self) -> u8 {
|
|
||||||
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: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn play_sequence(&mut self, sequence: &'a [SequenceEntry], num_loops: usize) {
|
|
||||||
self.sequence = sequence;
|
|
||||||
self.num_loops = num_loops;
|
|
||||||
self.loop_count = 0;
|
|
||||||
self.enabled = true;
|
|
||||||
self.index = 0;
|
|
||||||
self.ticks_remaining = 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.enabled && self.ticks_remaining == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(&mut self) -> Option<u16> {
|
|
||||||
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::{
|
|
||||||
DacService, LedService, Service, SimplePwmCore, TickService, TickServiceData, TickTimerService,
|
|
||||||
};
|
|
||||||
use crate::synthesizer::SynthesizerService;
|
|
||||||
|
|
||||||
pub use settings::Settings;
|
|
||||||
|
|
||||||
// #[cfg(feature = "enable_print")]
|
|
||||||
use ch32_hal::println;
|
|
||||||
|
|
||||||
pub struct TimerConfig {
|
|
||||||
pub sp_timer_ms: usize,
|
|
||||||
pub lp_timer_ms: usize,
|
|
||||||
pub batt_adc_timer_ms: usize,
|
|
||||||
pub usb_adc_timer_ms: usize,
|
|
||||||
pub led0_timer_ms: usize,
|
|
||||||
pub led1_timer_ms: usize,
|
|
||||||
pub shutdown_timer_s: usize,
|
|
||||||
// pub led2_timer_ms: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Timers {
|
|
||||||
sp_timer: TickTimerService,
|
|
||||||
lp_timer: TickTimerService,
|
|
||||||
batt_adc_timer: TickTimerService,
|
|
||||||
usb_adc_timer: TickTimerService,
|
|
||||||
led0_timer: TickTimerService,
|
|
||||||
led1_timer: TickTimerService,
|
|
||||||
shutdown_timer: TickTimerService,
|
|
||||||
pps_timer: TickTimerService,
|
|
||||||
// led2_timer: TickTimerService,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timers {
|
|
||||||
pub fn new(config: TimerConfig, system_tick_rate_hz: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
sp_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.sp_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
lp_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.lp_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
batt_adc_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.batt_adc_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
usb_adc_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.usb_adc_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
led0_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.led0_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
led1_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.led1_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
shutdown_timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(config.shutdown_timer_s),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
pps_timer: TickTimerService::new(TickServiceData::new(system_tick_rate_hz), true),
|
|
||||||
// led2_timer: TickTimerService::new(
|
|
||||||
// TickServiceData::new(config.led2_timer_ms * system_tick_rate_hz / 1000),
|
|
||||||
// true,
|
|
||||||
// ),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
self.sp_timer.tick();
|
|
||||||
self.lp_timer.tick();
|
|
||||||
self.batt_adc_timer.tick();
|
|
||||||
self.usb_adc_timer.tick();
|
|
||||||
self.led0_timer.tick();
|
|
||||||
self.led1_timer.tick();
|
|
||||||
self.pps_timer.tick();
|
|
||||||
// self.led2_timer.tick();
|
|
||||||
}
|
|
||||||
pub fn need_service(&self) -> bool {
|
|
||||||
self.sp_timer.need_service()
|
|
||||||
| self.lp_timer.need_service()
|
|
||||||
| self.batt_adc_timer.need_service()
|
|
||||||
| self.usb_adc_timer.need_service()
|
|
||||||
| self.led0_timer.need_service()
|
|
||||||
| self.led1_timer.need_service()
|
|
||||||
| self.shutdown_timer.need_service()
|
|
||||||
| self.pps_timer.need_service()
|
|
||||||
// | self.led2_timer.need_service()
|
|
||||||
}
|
|
||||||
pub fn init(&mut self) {
|
|
||||||
self.led0_timer.reset();
|
|
||||||
self.led0_timer.enable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub struct ServiceConfigs {
|
|
||||||
|
|
||||||
// }
|
|
||||||
// things that sort of don't touch hardware but also do?
|
|
||||||
// TODO: make this merged with the interfaces maybe
|
|
||||||
// but also maybe not, since these are things that require servicing
|
|
||||||
pub struct Services {
|
|
||||||
pub led0: LedService,
|
|
||||||
pub led1: LedService,
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub system_tick_rate_hz: usize,
|
|
||||||
pub timers: TimerConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sequences {
|
|
||||||
pub led0: sequencer::BasicSequence<'static>,
|
|
||||||
pub led1: sequencer::BasicSequence<'static>,
|
|
||||||
// pub led2: sequencer::BasicSequence<'static>,
|
|
||||||
pub audio: &'static [(&'static [sequencer::SequenceEntry], usize)],
|
|
||||||
}
|
|
||||||
|
|
||||||
// things that touch hardware
|
|
||||||
pub struct Interfaces {
|
|
||||||
pub pwm_core: SimplePwmCore<'static, ch32_hal::peripherals::TIM1>,
|
|
||||||
pub adc_core: crate::AdcCore,
|
|
||||||
pub amp: crate::Amplifier,
|
|
||||||
pub usb: crate::Usb,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
state: State,
|
|
||||||
pub settings: Settings,
|
|
||||||
timers: Timers,
|
|
||||||
services: Services,
|
|
||||||
sequences: Sequences,
|
|
||||||
interfaces: Interfaces,
|
|
||||||
}
|
|
||||||
|
|
||||||
use settings::Level;
|
|
||||||
impl App {
|
|
||||||
pub fn new(
|
|
||||||
config: Config,
|
|
||||||
services: Services,
|
|
||||||
sequences: Sequences,
|
|
||||||
interfaces: Interfaces,
|
|
||||||
settings: Settings,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
state: State::default(),
|
|
||||||
settings,
|
|
||||||
timers: Timers::new(config.timers, config.system_tick_rate_hz),
|
|
||||||
services,
|
|
||||||
sequences,
|
|
||||||
interfaces,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(&mut self) {
|
|
||||||
// self.timers.init();
|
|
||||||
self.interfaces.amp.enable();
|
|
||||||
|
|
||||||
self.timers.batt_adc_timer.reset();
|
|
||||||
self.timers.batt_adc_timer.enable(true);
|
|
||||||
|
|
||||||
self.timers.usb_adc_timer.reset();
|
|
||||||
self.timers.usb_adc_timer.enable(true);
|
|
||||||
|
|
||||||
self.timers.led0_timer.reset();
|
|
||||||
self.timers.led0_timer.enable(true);
|
|
||||||
|
|
||||||
self.timers.led1_timer.reset();
|
|
||||||
self.timers.led1_timer.enable(true);
|
|
||||||
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
self.timers.shutdown_timer.enable(true);
|
|
||||||
|
|
||||||
self.timers.pps_timer.reset();
|
|
||||||
self.timers.pps_timer.enable(true);
|
|
||||||
|
|
||||||
// self.timers.led2_timer.reset();
|
|
||||||
// self.timers.led2_timer.enable(true);
|
|
||||||
|
|
||||||
// self.services.synth0.set_freq(1);
|
|
||||||
self.services.synth0.disable();
|
|
||||||
self.services.sequencer.disable();
|
|
||||||
|
|
||||||
crate::riscv::asm::delay(2_500_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_state(&mut self, state: State) {
|
|
||||||
self.state = state
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn state(&self) -> State {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn settings(&self) -> Settings {
|
|
||||||
self.settings
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
self.timers.tick();
|
|
||||||
self.services.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(&mut self) {
|
|
||||||
// timers
|
|
||||||
if self.timers.sp_timer.need_service() {
|
|
||||||
self.timers.sp_timer.service();
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("sp service");
|
|
||||||
self.timers.sp_timer.reset();
|
|
||||||
self.main_button_short_press();
|
|
||||||
}
|
|
||||||
if self.timers.lp_timer.need_service() {
|
|
||||||
self.timers.lp_timer.service();
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("lp service");
|
|
||||||
self.timers.lp_timer.reset();
|
|
||||||
self.main_button_long_press();
|
|
||||||
}
|
|
||||||
if self.timers.batt_adc_timer.need_service() {
|
|
||||||
self.timers.batt_adc_timer.service();
|
|
||||||
if !self.interfaces.usb.powered() {
|
|
||||||
let bv = self.interfaces.adc_core.get_battery_voltage();
|
|
||||||
let avg = self.interfaces.adc_core.get_average();
|
|
||||||
// #[cfg(feature = "enable_print")]
|
|
||||||
// println!("batt adc service: {bv}, {avg}");
|
|
||||||
// println!("none USB");
|
|
||||||
if avg < 421 {
|
|
||||||
self.set_state(State::DeepSleep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.timers.usb_adc_timer.need_service() {
|
|
||||||
self.timers.usb_adc_timer.service();
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("usb adc service");
|
|
||||||
}
|
|
||||||
if self.timers.led0_timer.need_service() {
|
|
||||||
let out = match self.settings.brightness {
|
|
||||||
Level::Off => 0,
|
|
||||||
Level::Low => 5,
|
|
||||||
Level::Medium => 25,
|
|
||||||
Level::High => 75,
|
|
||||||
Level::Maximum => {
|
|
||||||
self.sequences.led0.next();
|
|
||||||
self.sequences.led0.get_value() / 6
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.timers.led0_timer.service();
|
|
||||||
self.services.led0.set_amplitude(out);
|
|
||||||
// #[cfg(feature = "enable_print")]
|
|
||||||
// println!("led0 sevice {}", self.sequences.led0.get_value());
|
|
||||||
}
|
|
||||||
if self.timers.led1_timer.need_service() {
|
|
||||||
let out = match self.settings.brightness {
|
|
||||||
Level::Off => 0,
|
|
||||||
Level::Low => 5,
|
|
||||||
Level::Medium => 25,
|
|
||||||
Level::High => 75,
|
|
||||||
Level::Maximum => {
|
|
||||||
self.sequences.led1.next();
|
|
||||||
self.sequences.led1.get_value() / 6
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.timers.led1_timer.service();
|
|
||||||
self.services.led1.set_amplitude(out);
|
|
||||||
|
|
||||||
// #[cfg(feature = "enable_print")]
|
|
||||||
// println!("led1 service");
|
|
||||||
}
|
|
||||||
if self.timers.pps_timer.need_service() {
|
|
||||||
self.timers.pps_timer.service();
|
|
||||||
self.timers.shutdown_timer.tick();
|
|
||||||
}
|
|
||||||
if self.timers.shutdown_timer.need_service() {
|
|
||||||
self.timers.shutdown_timer.service();
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
// println!("eepy");
|
|
||||||
self.set_state(State::DeepSleep);
|
|
||||||
}
|
|
||||||
// if self.timers.led2_timer.need_service() {
|
|
||||||
// let out = match self.settings.brightness {
|
|
||||||
// Level::Off => 0,
|
|
||||||
// Level::Low => 5,
|
|
||||||
// Level::Medium => 25,
|
|
||||||
// Level::High => 75,
|
|
||||||
// Level::Maximum => {
|
|
||||||
// self.sequences.led2.next();
|
|
||||||
// self.sequences.led2.get_value() / 6
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// self.timers.led2_timer.service();
|
|
||||||
// self.services.led2.set_amplitude(out);
|
|
||||||
|
|
||||||
// // #[cfg(feature = "enable_print")]
|
|
||||||
// // println!("led2 service");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// services
|
|
||||||
if self.services.led0.need_service() {
|
|
||||||
self.interfaces
|
|
||||||
.pwm_core
|
|
||||||
.write_amplitude(self.services.led0.channel, self.services.led0.amplitude);
|
|
||||||
self.services.led0.service();
|
|
||||||
}
|
|
||||||
if self.services.led1.need_service() {
|
|
||||||
self.interfaces
|
|
||||||
.pwm_core
|
|
||||||
.write_amplitude(self.services.led1.channel, self.services.led1.amplitude);
|
|
||||||
self.services.led1.service();
|
|
||||||
}
|
|
||||||
// if self.services.led2.need_service() {
|
|
||||||
// self.interfaces
|
|
||||||
// .pwm_core
|
|
||||||
// .write_amplitude(self.services.led2.channel, self.services.led2.amplitude);
|
|
||||||
// self.services.led2.service();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: disable when you get to the end automatically
|
|
||||||
// in the sequencer, not here
|
|
||||||
if self.services.sequencer.need_service() {
|
|
||||||
if let Some(out) = self.services.sequencer.service() {
|
|
||||||
if out == 0 {
|
|
||||||
self.services.synth0.disable();
|
|
||||||
} else {
|
|
||||||
self.services.synth0.enable();
|
|
||||||
self.services.synth0.set_freq(out.into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.services.sequencer.disable();
|
|
||||||
self.services.synth0.disable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.services.synth0.need_service() {
|
|
||||||
let out = match self.services.synth0.service() {
|
|
||||||
Some(value) => value / 6 / 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() / 2;
|
|
||||||
self.interfaces
|
|
||||||
.pwm_core
|
|
||||||
.write_amplitude(ch32_hal::timer::Channel::Ch4, out as u8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// interfaces to the app (for buttons, etc.)
|
|
||||||
impl App {
|
|
||||||
pub fn shut_down(&mut self) {
|
|
||||||
self.interfaces
|
|
||||||
.pwm_core
|
|
||||||
.write_amplitude(self.services.led0.channel, 0);
|
|
||||||
self.interfaces
|
|
||||||
.pwm_core
|
|
||||||
.write_amplitude(self.services.led1.channel, 0);
|
|
||||||
crate::riscv::asm::delay(10_000_000);
|
|
||||||
|
|
||||||
self.interfaces.pwm_core.pwm.borrow().shutdown();
|
|
||||||
self.interfaces.adc_core.shutdown();
|
|
||||||
|
|
||||||
self.interfaces.amp.disable();
|
|
||||||
}
|
|
||||||
pub fn volume_button(&mut self) {
|
|
||||||
self.settings.volume.next();
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("new volume: {:?}", self.settings.volume);
|
|
||||||
self.services
|
|
||||||
.sequencer
|
|
||||||
.play_sequence(&crate::sequences::COIN_CHIRP, 0);
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
self.timers.shutdown_timer.enable(true);
|
|
||||||
}
|
|
||||||
pub fn brightness_button(&mut self) {
|
|
||||||
self.settings.brightness.next();
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("new brightness: {:?}", self.settings.brightness);
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
self.timers.shutdown_timer.enable(true);
|
|
||||||
}
|
|
||||||
pub fn main_button_press(&mut self) {
|
|
||||||
// TODO
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("main button press");
|
|
||||||
self.timers.sp_timer.reset();
|
|
||||||
self.timers.lp_timer.reset();
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
self.timers.sp_timer.enable(true);
|
|
||||||
self.timers.lp_timer.enable(true);
|
|
||||||
self.timers.shutdown_timer.enable(true);
|
|
||||||
self.main_button_click();
|
|
||||||
}
|
|
||||||
pub fn main_button_release(&mut self) {
|
|
||||||
// TODO
|
|
||||||
// #[cfg(feature = "enable_print")]
|
|
||||||
// println!("main button release");
|
|
||||||
// let timers = (
|
|
||||||
// self.timers.sp_timer.is_enabled(),
|
|
||||||
// self.timers.lp_timer.is_enabled(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
self.timers.sp_timer.reset();
|
|
||||||
self.timers.lp_timer.reset();
|
|
||||||
// match timers {
|
|
||||||
// // click
|
|
||||||
// (true, true) => self.main_button_click(),
|
|
||||||
// // short press
|
|
||||||
// (false, true) => self.main_button_short_press(),
|
|
||||||
// // long press
|
|
||||||
// (false, false) => self.main_button_long_press(),
|
|
||||||
// // anything else is not possible
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
pub fn coin_detect(&mut self) {
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("coin detect");
|
|
||||||
// self.services.sample_player.play_sample();
|
|
||||||
self.services
|
|
||||||
.sequencer
|
|
||||||
.play_sequence(&crate::sequences::COIN_CHIRP, 0);
|
|
||||||
self.timers.shutdown_timer.reset();
|
|
||||||
self.timers.shutdown_timer.enable(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events
|
|
||||||
impl App {
|
|
||||||
pub fn main_button_click(&mut self) {
|
|
||||||
// TODO
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("click");
|
|
||||||
let data = self.sequences.audio[self.settings.button_sound_index];
|
|
||||||
self.services.sequencer.play_sequence(data.0, data.1);
|
|
||||||
|
|
||||||
self.settings.button_sound_index += 1;
|
|
||||||
if self.settings.button_sound_index > self.sequences.audio.len() - 1 {
|
|
||||||
self.settings.button_sound_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.services.synth0.enable();
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// this is a hack to stop the coin thing from playing.
|
|
||||||
self.services.sample_player.disable();
|
|
||||||
}
|
|
||||||
fn main_button_short_press(&self) {
|
|
||||||
// TODO
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("short press");
|
|
||||||
}
|
|
||||||
fn main_button_long_press(&mut self) {
|
|
||||||
// TODO
|
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("long press");
|
|
||||||
self.set_state(State::DeepSleep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
impl App {
|
|
||||||
pub fn get_state(&self) -> State {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
pub fn get_settings(&self) -> Settings {
|
|
||||||
self.settings
|
|
||||||
}
|
|
||||||
pub fn should_wake(&self) -> bool {
|
|
||||||
if self.interfaces.usb.powered() {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if self.interfaces.adc_core.get_average() >= 421 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO LIST
|
|
||||||
// BROKEN:
|
|
||||||
// 1. audio scaling causes crash
|
|
||||||
// 2. popping on sample playback
|
|
||||||
// 3. actual app sequence (start at idle?)
|
|
||||||
//
|
|
||||||
// AUDIO:
|
|
||||||
// 3. amp_en control
|
|
||||||
//
|
|
||||||
// LED:
|
|
||||||
//
|
|
||||||
// INTERFACE:
|
|
||||||
// 1. short press handling
|
|
||||||
// 2. long press handling
|
|
||||||
//
|
|
||||||
// SYSTEM:
|
|
||||||
// 1. deep sleep
|
|
||||||
// 2. battery voltage monitoring
|
|
||||||
// 3. battery voltage cutoff
|
|
||||||
//
|
|
||||||
// STRETCH TODO LIST
|
|
||||||
// 1. clean up edge detector
|
|
||||||
// 2. better handling for pwm scaling (brightness / volume)
|
|
||||||
// 3. better interrupt handling structs
|
|
||||||
// 4. led DynamicSequence
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
use crate::insert_coin::{TickService, TickServiceData, TickTimerService};
|
|
||||||
use ch32_hal::gpio::{AnyPin, Input, Pull};
|
|
||||||
|
|
||||||
pub struct DebouncedGPIO<'a> {
|
|
||||||
input: Input<'a>,
|
|
||||||
// value of the GPIO
|
|
||||||
value: bool,
|
|
||||||
// GPIO is ready (debounced)
|
|
||||||
ready: bool,
|
|
||||||
// debouncer is active
|
|
||||||
active: bool,
|
|
||||||
// debounce timer
|
|
||||||
timer: TickTimerService,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DebouncedGPIO<'a> {
|
|
||||||
pub fn new(pin: AnyPin, system_tick_rate_hz: usize, debounce_time_ms: usize) -> Self {
|
|
||||||
// coin debounce timer (100ms)
|
|
||||||
Self {
|
|
||||||
input: Input::new(pin, Pull::None),
|
|
||||||
value: false,
|
|
||||||
ready: false,
|
|
||||||
active: false,
|
|
||||||
timer: TickTimerService::new(
|
|
||||||
TickServiceData::new(system_tick_rate_hz * debounce_time_ms / 1000),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ready(&self) -> bool {
|
|
||||||
self.ready
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active(&self) -> bool {
|
|
||||||
self.active
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value(&self) -> bool {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn begin(&mut self) {
|
|
||||||
self.reset();
|
|
||||||
self.timer.enable(true);
|
|
||||||
self.active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.timer.reset();
|
|
||||||
self.ready = false;
|
|
||||||
self.active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(&mut self) {
|
|
||||||
self.timer.tick();
|
|
||||||
if self.timer.need_service() {
|
|
||||||
self.timer.reset();
|
|
||||||
self.value = self.input.is_high();
|
|
||||||
self.ready = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn is_high_immediate(&self) -> bool {
|
|
||||||
self.input.is_high()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
use ch32_hal::timer::GeneralInstance16bit;
|
||||||
use ch32_hal::timer::simple_pwm::SimplePwm;
|
use ch32_hal::timer::simple_pwm::SimplePwm;
|
||||||
use ch32_hal::timer::Channel;
|
use ch32_hal::timer::Channel;
|
||||||
use ch32_hal::timer::GeneralInstance16bit;
|
|
||||||
|
|
||||||
use crate::insert_coin::services::{DacService, LedService, Service, TickService, TickServiceData};
|
use crate::insert_coin::services::{DacService, LedService, TickService, TickServiceData, Service};
|
||||||
|
|
||||||
|
|
||||||
// static mut led0_index: usize = 0;
|
// static mut led0_index: usize = 0;
|
||||||
// static LED0: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
// static LED0: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||||
|
|
@ -10,8 +11,8 @@ use crate::insert_coin::services::{DacService, LedService, Service, TickService,
|
||||||
// static mut LED1_INDEX: usize = 0;
|
// static mut LED1_INDEX: usize = 0;
|
||||||
// static LED1_DCS: [u8; 5] = [0u8, 25u8, 50u8, 75u8, 100u8];
|
// static LED1_DCS: [u8; 5] = [0u8, 25u8, 50u8, 75u8, 100u8];
|
||||||
|
|
||||||
pub struct SimplePwmCore<'d, T: GeneralInstance16bit> {
|
pub struct SimplePwmCore<'d, T: GeneralInstance16bit> {
|
||||||
pub pwm: core::cell::RefCell<SimplePwm<'d, T>>,
|
pwm: core::cell::RefCell<SimplePwm<'d, T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: GeneralInstance16bit> SimplePwmCore<'d, T> {
|
impl<'d, T: GeneralInstance16bit> SimplePwmCore<'d, T> {
|
||||||
|
|
@ -37,26 +38,22 @@ impl<'d, T: GeneralInstance16bit> SimplePwmCore<'d, T> {
|
||||||
self.pwm.borrow_mut().set_duty(ch, dc);
|
self.pwm.borrow_mut().set_duty(ch, dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable(&self, ch: Channel) {
|
// pub fn disable(&self, ch: Channel) {
|
||||||
self.pwm.borrow_mut().disable(ch);
|
// self.pwm.borrow_mut().disable(ch);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub struct SimplePwmHandle<T: GeneralInstance16Bit> {
|
|
||||||
// core: &'static SimplePwmCore<'_, T: GeneralInstance16Bit>,
|
|
||||||
// channel: Channel,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl<T: GeneralInstance16Bit> SimplePwmHandle<T> {
|
|
||||||
// pub fn set_amplitude(&self, amplitude: u8) {}
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub struct CoreConfig {
|
pub struct CoreConfig {
|
||||||
pub tick_rate_hz: usize,
|
pub tick_rate_hz: usize,
|
||||||
}
|
}
|
||||||
impl CoreConfig {
|
impl CoreConfig {
|
||||||
pub fn new(tick_rate_hz: usize) -> Self {
|
pub fn new(tick_rate_hz: usize) -> Self {
|
||||||
Self { tick_rate_hz }
|
Self {
|
||||||
|
tick_rate_hz,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,20 +70,25 @@ pub struct InsertCoin<'a, T: GeneralInstance16bit> {
|
||||||
pub led0: LedService,
|
pub led0: LedService,
|
||||||
pub led1: LedService,
|
pub led1: LedService,
|
||||||
// led2: LedService,
|
// led2: LedService,
|
||||||
|
|
||||||
pub dac: DacService<'a>,
|
pub dac: DacService<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
|
|
||||||
pub fn new(config: CoreConfig, pwm_core: SimplePwmCore<'a, T>) -> Self {
|
pub fn new(config: CoreConfig, pwm_core: SimplePwmCore<'a, T>) -> Self {
|
||||||
|
|
||||||
|
|
||||||
// LED0 servicer setup
|
// LED0 servicer setup
|
||||||
let led0 = LedService::new(ch32_hal::timer::Channel::Ch3);
|
let led0 = LedService::new(ch32_hal::timer::Channel::Ch3);
|
||||||
|
|
||||||
// LED1 servicer setup
|
// LED1 servicer setup
|
||||||
let led1 = LedService::new(ch32_hal::timer::Channel::Ch1);
|
let led1 = LedService::new(ch32_hal::timer::Channel::Ch1);
|
||||||
|
|
||||||
|
|
||||||
// DAC servicer setup
|
// DAC servicer setup
|
||||||
let dac_sample_rate_hz = 4000;
|
let dac_sample_rate_hz = 16000;
|
||||||
let dac_tick_per_service = config.tick_rate_hz / (dac_sample_rate_hz);
|
let dac_tick_per_service = config.tick_rate_hz/(dac_sample_rate_hz);
|
||||||
let dac_service_data = TickServiceData::new(dac_tick_per_service);
|
let dac_service_data = TickServiceData::new(dac_tick_per_service);
|
||||||
let dac = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
let dac = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
||||||
|
|
||||||
|
|
@ -103,28 +105,28 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
|
|
||||||
/// takes self reference and runs
|
/// takes self reference and runs
|
||||||
pub fn service(&mut self) {
|
pub fn service(&mut self) {
|
||||||
if self.is_active() {
|
|
||||||
|
if self.is_active() {
|
||||||
self.dac.tick();
|
self.dac.tick();
|
||||||
|
|
||||||
|
|
||||||
if self.led0.need_service() {
|
if self.led0.need_service() {
|
||||||
self.pwm_core
|
self.pwm_core.write_amplitude(self.led0.channel, self.led0.amplitude);
|
||||||
.write_amplitude(self.led0.channel, self.led0.amplitude);
|
|
||||||
self.led0.service();
|
self.led0.service();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.led1.need_service() {
|
if self.led1.need_service() {
|
||||||
self.pwm_core
|
self.pwm_core.write_amplitude(self.led1.channel, self.led1.amplitude);
|
||||||
.write_amplitude(self.led1.channel, self.led1.amplitude);
|
|
||||||
self.led1.service();
|
self.led1.service();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.dac.need_service() {
|
if self.dac.need_service() {
|
||||||
self.dac.service();
|
self.dac.service();
|
||||||
// TODO: adpcm-pwm-dac:e4c811653781e69e40b63fd27a8c1e20
|
// TODO: adpcm-pwm-dac:e4c811653781e69e40b63fd27a8c1e20
|
||||||
self.pwm_core
|
self.pwm_core.write_amplitude(self.dac.channel, self.dac.get_amplitude() as u8);
|
||||||
.write_amplitude(self.dac.channel, self.dac.get_amplitude() as u8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// consumes self and runs
|
// /// consumes self and runs
|
||||||
|
|
@ -132,6 +134,7 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
// let mut delay = Delay;
|
// let mut delay = Delay;
|
||||||
// let tick_interval_us = 1000000/self.config.tick_rate_hz;
|
// let tick_interval_us = 1000000/self.config.tick_rate_hz;
|
||||||
|
|
||||||
|
|
||||||
// let mut led0_index = 0;
|
// let mut led0_index = 0;
|
||||||
// let led0_dcs = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
// let led0_dcs = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||||
|
|
||||||
|
|
@ -140,11 +143,12 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
|
|
||||||
// loop {
|
// loop {
|
||||||
// self.dac.tick();
|
// self.dac.tick();
|
||||||
|
|
||||||
|
|
||||||
// if(self.led0.need_service()) {
|
// if(self.led0.need_service()) {
|
||||||
// self.led0.set_amplitude(led0_dcs[led0_index]);
|
// self.led0.set_amplitude(led0_dcs[led0_index]);
|
||||||
// self.pwm_core.write_amplitude(self.led0.channel, self.led0.amplitude);
|
// self.pwm_core.write_amplitude(self.led0.channel, self.led0.amplitude);
|
||||||
|
|
||||||
// led0_index += 1;
|
// led0_index += 1;
|
||||||
// if led0_index > led0_dcs.len() - 1 {
|
// if led0_index > led0_dcs.len() - 1 {
|
||||||
// led0_index = 0;
|
// led0_index = 0;
|
||||||
|
|
@ -155,7 +159,7 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
// if(self.led1.need_service()) {
|
// if(self.led1.need_service()) {
|
||||||
// self.led1.set_amplitude(led1_dcs[led1_index]);
|
// self.led1.set_amplitude(led1_dcs[led1_index]);
|
||||||
// self.pwm_core.write_amplitude(self.led1.channel, self.led1.amplitude);
|
// self.pwm_core.write_amplitude(self.led1.channel, self.led1.amplitude);
|
||||||
|
|
||||||
// led1_index += 1;
|
// led1_index += 1;
|
||||||
// if led1_index > led1_dcs.len() - 1 {
|
// if led1_index > led1_dcs.len() - 1 {
|
||||||
// led1_index = 0;
|
// led1_index = 0;
|
||||||
|
|
@ -174,7 +178,7 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn is_active(&self) -> bool {
|
pub fn is_active(&self) -> bool {
|
||||||
self.core.active
|
self.core.active
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_active(&mut self, active: bool) {
|
pub fn set_active(&mut self, active: bool) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
mod insert_coin;
|
mod insert_coin;
|
||||||
mod services;
|
mod services;
|
||||||
|
|
||||||
pub use services::{DacService, LedService, Service, TickTimerService};
|
pub use services::TickTimerService;
|
||||||
|
|
||||||
pub use insert_coin::{CoreConfig, InsertCoin, SimplePwmCore};
|
|
||||||
pub use services::{TickService, TickServiceData};
|
pub use services::{TickService, TickServiceData};
|
||||||
|
pub use insert_coin::{InsertCoin, CoreConfig, SimplePwmCore};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::insert_coin::services::{TickService, TickServiceData};
|
use crate::insert_coin::services::{TickServiceData, TickService};
|
||||||
|
|
||||||
|
|
||||||
use adpcm_pwm_dac::dac::DpcmDecoder;
|
use adpcm_pwm_dac::dac::DpcmDecoder;
|
||||||
use ch32_hal::timer::Channel;
|
use ch32_hal::timer::Channel;
|
||||||
|
|
@ -8,7 +9,6 @@ pub struct DacService<'a> {
|
||||||
dpcm_decoder: core::cell::RefCell<DpcmDecoder<'a>>,
|
dpcm_decoder: core::cell::RefCell<DpcmDecoder<'a>>,
|
||||||
amplitude: core::cell::RefCell<usize>,
|
amplitude: core::cell::RefCell<usize>,
|
||||||
pub channel: Channel,
|
pub channel: Channel,
|
||||||
enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DacService<'a> {
|
impl<'a> DacService<'a> {
|
||||||
|
|
@ -18,19 +18,9 @@ impl<'a> DacService<'a> {
|
||||||
dpcm_decoder: core::cell::RefCell::new(DpcmDecoder::new()),
|
dpcm_decoder: core::cell::RefCell::new(DpcmDecoder::new()),
|
||||||
amplitude: core::cell::RefCell::new(0),
|
amplitude: core::cell::RefCell::new(0),
|
||||||
channel,
|
channel,
|
||||||
enabled: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn play_sample(&mut self) {
|
|
||||||
self.dpcm_decoder.borrow_mut().seek_to_sample(0);
|
|
||||||
self.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable(&mut self) {
|
|
||||||
self.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_data(&self, data: &'a [u8]) {
|
pub fn load_data(&self, data: &'a [u8]) {
|
||||||
self.dpcm_decoder.borrow_mut().load_data(data);
|
self.dpcm_decoder.borrow_mut().load_data(data);
|
||||||
self.dpcm_decoder.borrow_mut().seek_to_sample(0);
|
self.dpcm_decoder.borrow_mut().seek_to_sample(0);
|
||||||
|
|
@ -46,23 +36,17 @@ impl<'a> DacService<'a> {
|
||||||
|
|
||||||
impl<'a> TickService for DacService<'a> {
|
impl<'a> TickService for DacService<'a> {
|
||||||
fn tick(&self) {
|
fn tick(&self) {
|
||||||
if self.enabled {
|
let mut tc = self.service_data.borrow_mut();
|
||||||
let mut tc = self.service_data.borrow_mut();
|
tc.ticks_remaining = tc.ticks_remaining.saturating_sub(1);
|
||||||
tc.ticks_remaining = tc.ticks_remaining.saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn need_service(&self) -> bool {
|
fn need_service(&self) -> bool {
|
||||||
self.enabled && self.service_data.borrow().ticks_remaining == 0
|
self.service_data.borrow().ticks_remaining == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service(&self) {
|
fn service(&self) {
|
||||||
let mut tc = self.service_data.borrow_mut();
|
let mut tc = self.service_data.borrow_mut();
|
||||||
tc.ticks_remaining = tc.ticks_per_service;
|
tc.ticks_remaining = tc.ticks_per_service;
|
||||||
if (self.dpcm_decoder.borrow().is_done()) {
|
self.set_amplitude(self.dpcm_decoder.borrow_mut().output_next());
|
||||||
self.set_amplitude(0);
|
|
||||||
} else {
|
|
||||||
self.set_amplitude(self.dpcm_decoder.borrow_mut().output_next());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ch32_hal::timer::Channel;
|
use ch32_hal::timer::Channel;
|
||||||
|
|
||||||
use crate::insert_coin::services::Service;
|
use crate::insert_coin::services::{Service};
|
||||||
|
|
||||||
pub struct LedService {
|
pub struct LedService {
|
||||||
// need_service: core::cell::RefCell<bool>,
|
// need_service: core::cell::RefCell<bool>,
|
||||||
|
|
@ -25,7 +25,9 @@ impl LedService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Service for LedService {
|
impl Service for LedService {
|
||||||
|
|
||||||
fn need_service(&self) -> bool {
|
fn need_service(&self) -> bool {
|
||||||
self.need_service
|
self.need_service
|
||||||
}
|
}
|
||||||
|
|
@ -34,3 +36,5 @@ impl Service for LedService {
|
||||||
self.need_service = false;
|
self.need_service = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::insert_coin::services::{TickService, TickServiceData};
|
use crate::insert_coin::services::{TickServiceData, TickService};
|
||||||
|
|
||||||
pub struct TickTimerService {
|
pub struct TickTimerService {
|
||||||
service_data: core::cell::RefCell<TickServiceData>,
|
service_data: core::cell::RefCell<TickServiceData>,
|
||||||
|
|
@ -15,21 +15,22 @@ impl TickTimerService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_enabled(&self) -> bool {
|
// pub fn is_enabled(&self) -> bool {
|
||||||
self.enabled
|
// self.enabled
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
let mut sd = self.service_data.borrow_mut();
|
let mut sd = self.service_data.borrow_mut();
|
||||||
sd.ticks_remaining = sd.ticks_per_service;
|
sd.ticks_remaining = sd.ticks_per_service;
|
||||||
self.enabled = false;
|
self.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable(&mut self, enable: bool) {
|
pub fn enable(&mut self, enable: bool) {
|
||||||
self.enabled = enable;
|
self.enabled = enable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl TickService for TickTimerService {
|
impl TickService for TickTimerService {
|
||||||
fn tick(&self) {
|
fn tick(&self) {
|
||||||
if self.enabled {
|
if self.enabled {
|
||||||
|
|
@ -47,3 +48,5 @@ impl TickService for TickTimerService {
|
||||||
tc.ticks_remaining = tc.ticks_per_service;
|
tc.ticks_remaining = tc.ticks_per_service;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,13 @@ mod insert_coin;
|
||||||
// system stuff
|
// system stuff
|
||||||
mod system;
|
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 ch32_hal::{adc::AdcChannel, interrupt::typelevel::Handler, timer::low_level::OutputPolarity};
|
||||||
use insert_coin::{CoreConfig, DacService, InsertCoin, LedService, SimplePwmCore};
|
use insert_coin::{CoreConfig, InsertCoin, SimplePwmCore};
|
||||||
|
|
||||||
use ch32_hal as hal;
|
use ch32_hal as hal;
|
||||||
use hal::bind_interrupts;
|
use hal::bind_interrupts;
|
||||||
use hal::delay::Delay;
|
use hal::delay::Delay;
|
||||||
use hal::gpio::{AnyPin, Input, Level, Output, OutputOpenDrain, Pin, Pull};
|
use hal::gpio::{AnyPin, Input, Pin, Pull};
|
||||||
use hal::time::Hertz;
|
use hal::time::Hertz;
|
||||||
use hal::timer::low_level::CountingMode;
|
use hal::timer::low_level::CountingMode;
|
||||||
use hal::timer::simple_pwm::{PwmPin, SimplePwm};
|
use hal::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||||
|
|
@ -40,249 +24,152 @@ use hal::println;
|
||||||
|
|
||||||
use qingke::riscv;
|
use qingke::riscv;
|
||||||
|
|
||||||
use crate::app::sequencer::{DynamicSequence, SequenceEntry};
|
struct DebouncedGPIO<'a> {
|
||||||
|
input: Input<'a>,
|
||||||
static LED0_SEQ: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
// value of the GPIO
|
||||||
|
|
||||||
pub struct Usb {
|
|
||||||
usb_pin: Input<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Usb {
|
|
||||||
pub fn new(usb_pin: Input<'static>) -> Self {
|
|
||||||
Self { usb_pin }
|
|
||||||
}
|
|
||||||
pub fn powered(&self) -> bool {
|
|
||||||
self.usb_pin.is_high()
|
|
||||||
}
|
|
||||||
// pub fn enable(&mut self) {
|
|
||||||
// self.usb_pin.set_as_input(Pull::Up);
|
|
||||||
// }
|
|
||||||
// pub fn disable(&mut self) {
|
|
||||||
// self.usb_pin.set_as_input(Pull::None);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Amplifier {
|
|
||||||
amp_en: OutputOpenDrain<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Amplifier {
|
|
||||||
pub fn new(amp_en: OutputOpenDrain<'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
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shutdown(&mut self) {
|
|
||||||
self.adc.shutdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Flag {
|
|
||||||
value: bool,
|
value: bool,
|
||||||
|
// GPIO is ready (debounced)
|
||||||
|
ready: bool,
|
||||||
|
// debounce timer
|
||||||
|
timer: TickTimerService,
|
||||||
}
|
}
|
||||||
impl Flag {
|
|
||||||
pub fn active(&self) -> bool {
|
impl<'a> DebouncedGPIO<'a> {
|
||||||
unsafe { core::ptr::read_volatile(&raw const self.value as *const bool) }
|
pub fn new(pin: AnyPin, system_tick_rate_hz: usize, debounce_time_ms: usize) -> Self {
|
||||||
|
// coin debounce timer (100ms)
|
||||||
|
Self {
|
||||||
|
input: Input::new(pin, Pull::Up),
|
||||||
|
value: false,
|
||||||
|
ready: false,
|
||||||
|
timer: TickTimerService::new(
|
||||||
|
TickServiceData::new(system_tick_rate_hz * debounce_time_ms / 1000),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn set(&mut self) {
|
|
||||||
unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, true) }
|
pub fn ready(&self) -> bool {
|
||||||
|
self.ready
|
||||||
}
|
}
|
||||||
pub fn clear(&mut self) {
|
|
||||||
unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, false) }
|
pub fn value(&self) -> bool {
|
||||||
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn begin(&mut self) {
|
||||||
|
self.reset();
|
||||||
|
self.timer.enable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.timer.reset();
|
||||||
|
self.ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service(&mut self) {
|
||||||
|
self.timer.tick();
|
||||||
|
if self.timer.need_service() {
|
||||||
|
self.timer.reset();
|
||||||
|
self.value = self.input.is_high();
|
||||||
|
self.ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepSleep --coin button irq--> Active
|
||||||
|
// Active --2s button--> Idle
|
||||||
|
// Idle/Active --5s button--> DeepSleep
|
||||||
|
|
||||||
|
pub enum SystemState {
|
||||||
|
// system is asleep, waiting for wake from coin insertion
|
||||||
|
DeepSleep,
|
||||||
|
// system is in low-power mode, dimmed lights, waiting for interaction
|
||||||
|
Idle,
|
||||||
|
// system is active. on entry: play coin sound. on button press: play different sound
|
||||||
|
Active,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct InputFlags {
|
struct InputFlags {
|
||||||
sense_coin_flag: Flag,
|
coin_flag: bool,
|
||||||
main_btn_flag: Flag,
|
button_flag: bool,
|
||||||
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 {
|
static mut INPUT_FLAGS: InputFlags = InputFlags {
|
||||||
sense_coin_flag: Flag { value: false },
|
coin_flag: false,
|
||||||
main_btn_flag: Flag { value: false },
|
button_flag: false,
|
||||||
volume_btn_flag: false,
|
|
||||||
light_ctrl_btn_flag: false,
|
|
||||||
systick_flag: Flag { value: false },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Test {}
|
struct Test {}
|
||||||
impl Handler<hal::interrupt::typelevel::EXTI7_0> for Test {
|
impl Handler<hal::interrupt::typelevel::EXTI7_0> for Test {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
// #[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
// println!("on_interrupt()");
|
println!("on_interrupt()");
|
||||||
critical_section::with(|_| unsafe {
|
critical_section::with(|_| unsafe {
|
||||||
let flags = system::clear_interrupt(2, 6);
|
INPUT_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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
fn systick_stop() {
|
|
||||||
let r = &ch32_hal::pac::SYSTICK;
|
|
||||||
// Reset SysTick
|
|
||||||
r.ctlr().write(|w| {
|
|
||||||
// Start with everything disabled
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
EXTI7_0 => Test;
|
EXTI7_0 => Test;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
use app::settings::Settings;
|
|
||||||
use insert_coin::TickTimerService;
|
use insert_coin::TickTimerService;
|
||||||
use insert_coin::{TickService, TickServiceData};
|
use insert_coin::{TickService, TickServiceData};
|
||||||
|
|
||||||
fn app_main(mut p: hal::Peripherals, app_settings: Settings) -> Settings {
|
fn debug_main(mut p: hal::Peripherals, mut delay: Delay) -> ! {
|
||||||
// initialize ADC core first, and exit if battery is too low
|
// LED0 output setup
|
||||||
let mut adc = hal::adc::Adc::new(p.ADC1, Default::default());
|
use hal::gpio::{Level, Output};
|
||||||
let mut batt_monitor_pin = p.PD4;
|
let mut led0_pin = Output::new(p.PC3, Level::High, Default::default());
|
||||||
let mut adc_core = AdcCore::new(adc, batt_monitor_pin);
|
|
||||||
|
|
||||||
let mut usb_detect_pin = p.PD5;
|
// button pin setup
|
||||||
let usb_detect_input = Input::new(usb_detect_pin, Pull::Up);
|
let button_pin = p.PD6;
|
||||||
let usb = Usb::new(usb_detect_input);
|
unsafe { system::init_gpio_irq(button_pin.pin(), button_pin.port(), false, true) };
|
||||||
|
let mut button_input = Input::new(button_pin, Pull::Up);
|
||||||
|
|
||||||
let bv = adc_core.get_battery_voltage();
|
delay.delay_ms(1000);
|
||||||
|
|
||||||
// if we don't have USB power, and the batt ADC reads under 421, don't wake
|
unsafe { system::enter_standby(4) };
|
||||||
if !usb.powered() && bv < 421 {
|
delay.delay_ms(1000);
|
||||||
adc_core.shutdown();
|
riscv::asm::wfi();
|
||||||
return app_settings;
|
|
||||||
|
// get the clocks re-initialized
|
||||||
|
let mut config = hal::Config::default();
|
||||||
|
config.rcc = hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI;
|
||||||
|
unsafe {
|
||||||
|
hal::rcc::init(config.rcc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("begin loop");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led0_pin.toggle();
|
||||||
|
delay.delay_ms(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_main(mut p: hal::Peripherals, mut delay: Delay) -> ! {
|
||||||
// === output setup ===
|
// === output setup ===
|
||||||
|
|
||||||
// LED0 output setup
|
// LED0 output setup
|
||||||
let led0_pin = PwmPin::new_ch3::<0>(p.PC3);
|
let led0_pin = PwmPin::new_ch3::<0>(p.PC3);
|
||||||
let led0_ch = hal::timer::Channel::Ch3;
|
let led0_ch = hal::timer::Channel::Ch3;
|
||||||
|
// let mut led0_pin = Output::new(p.PC3, Level::High, Default::default());
|
||||||
|
// unsafe {
|
||||||
|
// LED = Some(led0_pin);
|
||||||
|
// }
|
||||||
|
|
||||||
// LED1 output setup
|
// LED1 output setup
|
||||||
let led1_pin = PwmPin::new_ch1::<0>(p.PD2);
|
let led1_pin = PwmPin::new_ch1::<0>(p.PD2);
|
||||||
let led1_ch = hal::timer::Channel::Ch1;
|
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
|
// DAC output setup
|
||||||
let dac_pin = PwmPin::new_ch4::<0>(p.PC4);
|
let dac_pin = PwmPin::new_ch4::<0>(p.PC4);
|
||||||
// let dac_ch = hal::timer::Channel::Ch4;
|
// let dac_ch = hal::timer::Channel::Ch4;
|
||||||
|
|
@ -291,158 +178,129 @@ fn app_main(mut p: hal::Peripherals, app_settings: Settings) -> Settings {
|
||||||
let mut pwm = SimplePwm::new(
|
let mut pwm = SimplePwm::new(
|
||||||
p.TIM1,
|
p.TIM1,
|
||||||
Some(led1_pin),
|
Some(led1_pin),
|
||||||
// Some(led2_pin),
|
Some(led2_pin),
|
||||||
None,
|
|
||||||
Some(led0_pin),
|
Some(led0_pin),
|
||||||
Some(dac_pin),
|
Some(dac_pin),
|
||||||
Hertz::khz(200),
|
Hertz::khz(200),
|
||||||
CountingMode::default(),
|
CountingMode::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
pwm.set_polarity(led0_ch, OutputPolarity::ActiveHigh);
|
pwm.set_polarity(led0_ch, OutputPolarity::ActiveLow);
|
||||||
pwm.set_polarity(led1_ch, OutputPolarity::ActiveLow);
|
pwm.set_polarity(led1_ch, OutputPolarity::ActiveLow);
|
||||||
let mut pwm_core = SimplePwmCore::new(pwm);
|
|
||||||
pwm_core.write_amplitude(led0_ch, 0);
|
|
||||||
pwm_core.write_amplitude(led1_ch, 0);
|
|
||||||
|
|
||||||
// pwm.set_polarity(led2_ch, OutputPolarity::ActiveLow);
|
let sample_rate_hz = 16000;
|
||||||
|
let dac_tick_per_service = 5;
|
||||||
let tick_rate_hz = 50000;
|
let tick_rate_hz = sample_rate_hz * dac_tick_per_service;
|
||||||
|
|
||||||
let core_config = CoreConfig::new(tick_rate_hz);
|
let core_config = CoreConfig::new(tick_rate_hz);
|
||||||
|
|
||||||
// === input setup ===
|
// === input setup ===
|
||||||
|
|
||||||
// adc
|
// adc
|
||||||
// let mut adc = hal::adc::Adc::new(p.ADC1, Default::default());
|
let mut adc = hal::adc::Adc::new(p.ADC1, Default::default());
|
||||||
// let mut batt_monitor_pin = p.PD4;
|
let mut adc_pin = p.PD4;
|
||||||
// let adc_core = AdcCore::new(adc, batt_monitor_pin);
|
|
||||||
|
|
||||||
// adc2
|
// adc2
|
||||||
// let mut usb_detect_dc = hal::adc::Adc::new(p.ADC1, Default::default());
|
// let mut usb_detect_dc = hal::adc::Adc::new(p.ADC1, Default::default());
|
||||||
|
let mut usb_adc_pin = p.PD5;
|
||||||
|
|
||||||
// println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel());
|
// println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel());
|
||||||
|
delay.delay_ms(1000);
|
||||||
|
let adc_cal = adc.calibrate();
|
||||||
|
|
||||||
// #[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
// println!("ADC calibration value: {}", adc_cal);
|
println!("ADC calibration value: {}", adc_cal);
|
||||||
|
|
||||||
// definitions
|
// definitions
|
||||||
let sense_coin_pin = p.PC2;
|
let coin_pin = p.PC2;
|
||||||
let main_btn_pin = p.PD6;
|
let button_pin = p.PD6;
|
||||||
let volume_btn_pin = p.PC6;
|
// println!(
|
||||||
let light_ctrl_btn_pin = p.PC7;
|
// "coin pin: {} | coin port: {}",
|
||||||
let amp_en = p.PC5;
|
// coin_pin.pin(),
|
||||||
// let extra_io_1 = p.PD0;
|
// coin_pin.port()
|
||||||
// let extra_io_2 = p.PD3;
|
// );
|
||||||
|
// println!(
|
||||||
|
// "push pin: {} | push port: {}",
|
||||||
|
// button_pin.pin(),
|
||||||
|
// button_pin.port()
|
||||||
|
// );
|
||||||
|
|
||||||
let mut amp_en_output = OutputOpenDrain::new(amp_en, Level::Low, Default::default());
|
//2025-09-10 00:32:23.514: coin pin: 4 | coin port: 3
|
||||||
let amp = Amplifier::new(amp_en_output);
|
// 2025-09-10 00:32:23.515: push pin: 6 | push port: 3
|
||||||
|
|
||||||
// set up interrupts
|
// set up interrupts
|
||||||
unsafe { system::init_gpio_irq(sense_coin_pin.pin(), sense_coin_pin.port(), true, false) };
|
unsafe { system::init_gpio_irq(coin_pin.pin(), coin_pin.port(), false, true) };
|
||||||
unsafe { system::init_gpio_irq(main_btn_pin.pin(), main_btn_pin.port(), true, true) };
|
unsafe { system::init_gpio_irq(button_pin.pin(), button_pin.port(), true, true) };
|
||||||
|
|
||||||
// coin debouncer (100ms)
|
// coin debouncer (100ms)
|
||||||
let mut sense_coin_input =
|
let mut coin_input = DebouncedGPIO::new(coin_pin.degrade(), core_config.tick_rate_hz, 100);
|
||||||
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)
|
// button debouncer (100ms)
|
||||||
let mut volume_btn_input =
|
let mut button_input = DebouncedGPIO::new(button_pin.degrade(), core_config.tick_rate_hz, 100);
|
||||||
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 {
|
let pwm_core = SimplePwmCore::new(pwm);
|
||||||
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,
|
|
||||||
// 4 hours:
|
|
||||||
// shutdown_timer_s: 1,
|
|
||||||
shutdown_timer_s: 4 * 60 * 60 * 110 / 100,
|
|
||||||
// led2_timer_ms: 100,
|
|
||||||
};
|
|
||||||
|
|
||||||
let app_config = Config {
|
let mut interfaces = InsertCoin::new(core_config, pwm_core);
|
||||||
system_tick_rate_hz: tick_rate_hz,
|
interfaces.set_active(true);
|
||||||
timers: timer_config,
|
// insert_coin.init();
|
||||||
};
|
|
||||||
|
|
||||||
// DAC servicer setup
|
let mut led0_index = 0;
|
||||||
let dac_sample_rate_hz = 16000;
|
let led0_dcs = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||||
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 mut led1_index = 0;
|
||||||
// let coin_sound = include_bytes!("../audio/coin2.raw");
|
let led1_dcs = [0u8, 25u8, 50u8, 75u8, 100u8];
|
||||||
|
|
||||||
let sample_player = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
// tick timer 0
|
||||||
// sample_player.load_data(coin_sound);
|
let tt0_fire_rate_hz = 9;
|
||||||
|
let tt0_tick_per_service = interfaces.config.tick_rate_hz / (tt0_fire_rate_hz * 2);
|
||||||
|
let tt0_service_data = TickServiceData::new(tt0_tick_per_service);
|
||||||
|
let mut tt0 = TickTimerService::new(tt0_service_data, true);
|
||||||
|
|
||||||
let sequencer = app::sequencer::DynamicSequence::new(&SEQUENCE_LIST[0].0, tick_rate_hz);
|
// tick timer 1
|
||||||
|
let tt1_fire_rate_hz = 3;
|
||||||
|
let tt1_tick_per_service = interfaces.config.tick_rate_hz / (tt1_fire_rate_hz * 2);
|
||||||
|
let tt1_service_data = TickServiceData::new(tt1_tick_per_service);
|
||||||
|
let mut tt1 = TickTimerService::new(tt1_service_data, true);
|
||||||
|
|
||||||
let app_services = Services {
|
// short press timer
|
||||||
led0: LedService::new(led0_ch),
|
let sp_ticks = 2 * interfaces.config.tick_rate_hz;
|
||||||
led1: LedService::new(led1_ch),
|
let sp_timer_data = TickServiceData::new(sp_ticks);
|
||||||
// led2: LedService::new(led2_ch),
|
let mut sp_timer = TickTimerService::new(sp_timer_data, false);
|
||||||
synth0: SynthesizerService::new(tick_rate_hz),
|
sp_timer.reset();
|
||||||
sample_player,
|
|
||||||
sequencer,
|
|
||||||
};
|
|
||||||
|
|
||||||
let app_sequences = Sequences {
|
// long press timer
|
||||||
led0: BasicSequence::new(&LED0_SEQ),
|
let lp_ticks = 5 * interfaces.config.tick_rate_hz;
|
||||||
led1: BasicSequence::new(&LED0_SEQ),
|
let lp_timer_data = TickServiceData::new(lp_ticks);
|
||||||
// led2: BasicSequence::new(&LED0_SEQ),
|
let mut lp_timer = TickTimerService::new(lp_timer_data, false);
|
||||||
audio: &SEQUENCE_LIST,
|
lp_timer.reset();
|
||||||
};
|
|
||||||
|
|
||||||
let app_interfaces = Interfaces {
|
// battery read timer
|
||||||
pwm_core,
|
let adc1_ticks = 5 * interfaces.config.tick_rate_hz;
|
||||||
adc_core,
|
let adc1_timer_data = TickServiceData::new(adc1_ticks);
|
||||||
amp,
|
let mut adc1_timer = TickTimerService::new(adc1_timer_data, false);
|
||||||
usb,
|
adc1_timer.reset();
|
||||||
};
|
adc1_timer.enable(true);
|
||||||
|
|
||||||
let mut app = App::new(
|
let tick_interval_us = 1000000 / interfaces.config.tick_rate_hz - 10;
|
||||||
app_config,
|
|
||||||
app_services,
|
|
||||||
app_sequences,
|
|
||||||
app_interfaces,
|
|
||||||
app_settings,
|
|
||||||
);
|
|
||||||
|
|
||||||
let need_sound = unsafe {
|
// dac data
|
||||||
#[allow(static_mut_refs)]
|
// let coin_sound = include_bytes!("../audio/coin.raw");
|
||||||
if INPUT_FLAGS.main_btn_flag.active() {
|
let coin_sound = include_bytes!("../audio/sweep_dpcm_u4.raw");
|
||||||
#[allow(static_mut_refs)]
|
// let button_sound = include_bytes!("../audio/coinMixTest1_dpcm_u4.raw");
|
||||||
INPUT_FLAGS.main_btn_flag.clear();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// init systick
|
let mut system_state = SystemState::Active;
|
||||||
systick_init(tick_rate_hz);
|
|
||||||
|
interfaces.led0.set_amplitude(0);
|
||||||
|
interfaces.led1.set_amplitude(0);
|
||||||
|
interfaces.service();
|
||||||
|
|
||||||
// set up interrupts
|
|
||||||
unsafe {
|
unsafe {
|
||||||
use hal::pac::Interrupt;
|
use hal::pac::Interrupt;
|
||||||
use qingke::interrupt::Priority;
|
// use qingke_rt::CoreInterrupt;
|
||||||
use qingke_rt::CoreInterrupt;
|
|
||||||
|
|
||||||
|
// qingke::pfic::unpend_interrupt(Interrupt::EXTI7_0 as u8);
|
||||||
system::clear_interrupt(2, 6);
|
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(Interrupt::EXTI7_0 as u8);
|
||||||
qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8);
|
// qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MAIN APPLICATION
|
// MAIN APPLICATION
|
||||||
|
|
@ -453,159 +311,152 @@ fn app_main(mut p: hal::Peripherals, app_settings: Settings) -> Settings {
|
||||||
// -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 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
|
// -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw
|
||||||
|
|
||||||
// #[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
// println!("begin");
|
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();
|
|
||||||
if need_sound {
|
|
||||||
app.main_button_click();
|
|
||||||
}
|
|
||||||
loop {
|
loop {
|
||||||
// system servicing
|
{
|
||||||
|
// system input 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 {
|
unsafe {
|
||||||
#[allow(static_mut_refs)]
|
if INPUT_FLAGS.coin_flag {
|
||||||
INPUT_FLAGS.systick_flag.clear();
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("coin flag active");
|
||||||
|
INPUT_FLAGS.coin_flag = false;
|
||||||
|
coin_input.begin();
|
||||||
|
// enter the active state
|
||||||
|
system_state = SystemState::Active;
|
||||||
|
|
||||||
|
// todo: enter active
|
||||||
|
tt0.enable(true);
|
||||||
|
tt1.enable(true);
|
||||||
|
interfaces.dac.load_data(coin_sound);
|
||||||
|
}
|
||||||
|
if INPUT_FLAGS.button_flag {
|
||||||
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("button flag active");
|
||||||
|
INPUT_FLAGS.button_flag = false;
|
||||||
|
button_input.begin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// app tick
|
|
||||||
app.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.service();
|
// debouncer
|
||||||
|
coin_input.service();
|
||||||
|
button_input.service();
|
||||||
|
|
||||||
match app.get_state() {
|
if coin_input.ready() {
|
||||||
// enter standby
|
#[cfg(feature = "enable_print")]
|
||||||
app::State::DeepSleep => {
|
println!("debounced coin_input value: {}", coin_input.value());
|
||||||
app.shut_down();
|
coin_input.reset();
|
||||||
return app.get_settings();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
if button_input.ready() {
|
||||||
}
|
let value = button_input.value();
|
||||||
}
|
button_input.reset();
|
||||||
}
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("debounced button_input value: {}", value);
|
||||||
|
|
||||||
use ch32_hal::timer::low_level::{OutputCompareMode, Timer};
|
if !value {
|
||||||
use ch32_hal::timer::Channel;
|
// interfaces.dac.load_data(button_sound);
|
||||||
|
|
||||||
// fn shutdown_main(p: Peripherals) {
|
#[cfg(feature = "enable_print")]
|
||||||
fn shutdown_main(p: hal::Peripherals) {
|
println!("reset hold timers + enable");
|
||||||
systick_stop();
|
sp_timer.reset();
|
||||||
// LED0 output setup
|
sp_timer.enable(true);
|
||||||
let led0_pin = OutputOpenDrain::new(p.PC3, Level::Low, Default::default());
|
lp_timer.reset();
|
||||||
let led1_pin = OutputOpenDrain::new(p.PD2, Level::High, Default::default());
|
lp_timer.enable(true);
|
||||||
let led2_pin = OutputOpenDrain::new(p.PA1, Level::High, Default::default());
|
} else {
|
||||||
let dac_pin = OutputOpenDrain::new(p.PC4, Level::Low, Default::default());
|
sp_timer.reset();
|
||||||
let mut amp_pin = OutputOpenDrain::new(p.PC5, Level::Low, Default::default());
|
lp_timer.reset();
|
||||||
amp_pin.set_high();
|
}
|
||||||
let volume_btn_pin = OutputOpenDrain::new(p.PC6, Level::Low, Default::default());
|
}
|
||||||
let light_ctrl_btn_pin = OutputOpenDrain::new(p.PC7, Level::Low, Default::default());
|
|
||||||
let usb_detect_input = OutputOpenDrain::new(p.PD5, Level::Low, Default::default());
|
|
||||||
|
|
||||||
let sense_coin_pin = p.PC2;
|
// timers
|
||||||
let main_btn_pin = p.PD6;
|
sp_timer.tick();
|
||||||
|
lp_timer.tick();
|
||||||
|
adc1_timer.tick();
|
||||||
|
|
||||||
unsafe { system::init_gpio_irq(sense_coin_pin.pin(), sense_coin_pin.port(), true, false) };
|
if sp_timer.need_service() {
|
||||||
unsafe { system::init_gpio_irq(main_btn_pin.pin(), main_btn_pin.port(), true, false) };
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("sp detect!");
|
||||||
|
sp_timer.reset();
|
||||||
|
|
||||||
let sense_coin_pin = Input::new(sense_coin_pin, Pull::None);
|
// todo enter idle
|
||||||
let main_btn_pin = Input::new(main_btn_pin, Pull::None);
|
system_state = SystemState::Idle;
|
||||||
|
// TODO: fix polarity
|
||||||
|
interfaces.led0.set_amplitude(10);
|
||||||
|
interfaces.led1.set_amplitude(10);
|
||||||
|
interfaces.service();
|
||||||
|
}
|
||||||
|
|
||||||
riscv::asm::delay(1_000_000);
|
if lp_timer.need_service() {
|
||||||
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("lp detect!");
|
||||||
|
lp_timer.reset();
|
||||||
|
|
||||||
loop {
|
// todo enter deepsleep
|
||||||
unsafe { system::enter_standby() };
|
system_state = SystemState::DeepSleep;
|
||||||
riscv::asm::wfi();
|
// TODO: fix polarity
|
||||||
unsafe {
|
interfaces.led0.set_amplitude(0);
|
||||||
#[allow(static_mut_refs)]
|
interfaces.led1.set_amplitude(0);
|
||||||
if (INPUT_FLAGS.sense_coin_flag.active() || INPUT_FLAGS.main_btn_flag.active())
|
interfaces.service();
|
||||||
// && app.should_wake()
|
}
|
||||||
{
|
|
||||||
break;
|
if adc1_timer.need_service() {
|
||||||
|
let val = adc.convert(&mut adc_pin, hal::adc::SampleTime::CYCLES241);
|
||||||
|
let val = adc.convert(&mut usb_adc_pin, hal::adc::SampleTime::CYCLES241);
|
||||||
|
#[cfg(feature = "enable_print")]
|
||||||
|
println!("ADC value: {}", val);
|
||||||
|
|
||||||
|
adc1_timer.reset();
|
||||||
|
adc1_timer.enable(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match system_state {
|
||||||
|
SystemState::DeepSleep => {
|
||||||
|
// TODO: make this REALLY deep sleep
|
||||||
|
unsafe { system::enter_standby(4) };
|
||||||
|
loop {
|
||||||
|
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 {
|
||||||
|
if INPUT_FLAGS.coin_flag {
|
||||||
|
system_state = SystemState::Active;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SystemState::Idle => {}
|
||||||
|
SystemState::Active => {
|
||||||
|
tt0.tick();
|
||||||
|
tt1.tick();
|
||||||
|
|
||||||
|
if tt0.need_service() {
|
||||||
|
interfaces.led0.set_amplitude(led0_dcs[led0_index]);
|
||||||
|
led0_index += 1;
|
||||||
|
if led0_index > led0_dcs.len() - 1 {
|
||||||
|
led0_index = 0;
|
||||||
|
}
|
||||||
|
tt0.service();
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt1.need_service() {
|
||||||
|
interfaces.led1.set_amplitude(led1_dcs[led1_index]);
|
||||||
|
led1_index += 1;
|
||||||
|
if led1_index > led1_dcs.len() - 1 {
|
||||||
|
led1_index = 0;
|
||||||
|
}
|
||||||
|
tt1.service()
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces.service();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay.delay_us(tick_interval_us as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -619,30 +470,14 @@ fn main() -> ! {
|
||||||
let mut p = hal::init(config);
|
let mut p = hal::init(config);
|
||||||
|
|
||||||
// delay to let the debugger attach
|
// delay to let the debugger attach
|
||||||
// println!("pre");
|
let mut delay = Delay;
|
||||||
riscv::asm::delay(20_000_000);
|
delay.delay_ms(1000);
|
||||||
// println!("post");
|
|
||||||
// debug_main(p);
|
|
||||||
|
|
||||||
let mut app_settings = Settings::default();
|
// debug_main(p, delay);
|
||||||
|
app_main(p, delay);
|
||||||
loop {
|
|
||||||
unsafe {
|
|
||||||
hal::rcc::init(hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI);
|
|
||||||
}
|
|
||||||
let mut p = unsafe { hal::Peripherals::steal() };
|
|
||||||
app_settings = app_main(p, app_settings);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
hal::rcc::init(hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI);
|
|
||||||
}
|
|
||||||
let mut p = unsafe { hal::Peripherals::steal() };
|
|
||||||
shutdown_main(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
// println!("panic: {info:?}");
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,346 +0,0 @@
|
||||||
use crate::app::sequencer::SequenceEntry;
|
|
||||||
|
|
||||||
pub static TEST_SEQ: [SequenceEntry; 2] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 440,
|
|
||||||
duration_ms: 1000,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 220,
|
|
||||||
duration_ms: 1000,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static TEST_SEQ1: [SequenceEntry; 2] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 440,
|
|
||||||
duration_ms: 100,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 220,
|
|
||||||
duration_ms: 100,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// play twice
|
|
||||||
pub static WAHWAH: [SequenceEntry; 9] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 100,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 200,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 300,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 400,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 500,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 600,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 700,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 800,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 900,
|
|
||||||
duration_ms: 25,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static DECENDING_TONES: [SequenceEntry; 21] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1100,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1050,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1000,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 950,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 900,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 850,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 800,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 750,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 700,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 650,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 600,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 550,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 500,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 450,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 400,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 350,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 300,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 250,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 200,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 150,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 100,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// PLAY ONCE
|
|
||||||
pub static COIN_CHIRP: [SequenceEntry; 2] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1000,
|
|
||||||
duration_ms: 100,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1500,
|
|
||||||
duration_ms: 500,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// PLAY ONCE
|
|
||||||
pub static SOUND_8_BIT_GAME_4: [SequenceEntry; 4] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 220,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 440,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 660,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 880,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// PLAY TWICE
|
|
||||||
// TODO: this isn't working quite right with the 0hz "non note" for a rest...
|
|
||||||
pub static SOUND_8_BIT_GAME_1: [SequenceEntry; 3] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 440,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1500,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 0,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// PLAY 3 TIMES
|
|
||||||
pub static SOUND_8_BIT_GAME_3: [SequenceEntry; 3] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 440,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 660,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1000,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// PLAY 1 TIMES
|
|
||||||
pub static ASCENDING_TONES: [SequenceEntry; 29] = [
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 100,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 150,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 200,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 250,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 300,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 350,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 400,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 450,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 500,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 550,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 600,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 650,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 700,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 750,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 800,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 850,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 900,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 950,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1000,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1050,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1100,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1150,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1200,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1250,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1300,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1350,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1400,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1450,
|
|
||||||
duration_ms: 50,
|
|
||||||
},
|
|
||||||
SequenceEntry {
|
|
||||||
frequency_hz: 1500,
|
|
||||||
duration_ms: 1000,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static SEQUENCE_LIST: [(&'static [SequenceEntry], usize); 7] = [
|
|
||||||
(&SOUND_8_BIT_GAME_3, 2),
|
|
||||||
(&SOUND_8_BIT_GAME_4, 0),
|
|
||||||
(&ASCENDING_TONES, 0),
|
|
||||||
(&COIN_CHIRP, 0),
|
|
||||||
(&WAHWAH, 5),
|
|
||||||
(&DECENDING_TONES, 0),
|
|
||||||
(&SOUND_8_BIT_GAME_1, 1),
|
|
||||||
// &TEST_SEQ,
|
|
||||||
];
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
use crate::insert_coin::SimplePwmCore;
|
|
||||||
use ch32_hal::println;
|
|
||||||
use ch32_hal::timer::GeneralInstance16bit;
|
|
||||||
use wavetable_synth::{synthesizer::SimpleWavetableSynthesizer, wavetable::SimpleWavetable};
|
|
||||||
|
|
||||||
const SQUARE_WAVETABLE: [u8; 2] = [0, 100];
|
|
||||||
// const SQUARE_WAVETABLE: [u8; 32] = [
|
|
||||||
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100,
|
|
||||||
// 100, 100, 100, 100, 100, 100, 100,
|
|
||||||
// ];
|
|
||||||
|
|
||||||
pub struct SynthesizerService {
|
|
||||||
pub synth: SimpleWavetableSynthesizer<SimpleWavetable<'static, u8>>,
|
|
||||||
pub enabled: bool,
|
|
||||||
pub need_service: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SynthesizerService {
|
|
||||||
pub fn new(clock_freq_hz: usize) -> Self {
|
|
||||||
let square_wt = SimpleWavetable::new(&SQUARE_WAVETABLE);
|
|
||||||
let synth = SimpleWavetableSynthesizer::new(square_wt, clock_freq_hz);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
synth,
|
|
||||||
enabled: true,
|
|
||||||
need_service: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.synth.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn need_service(&self) -> bool {
|
|
||||||
self.need_service || (self.enabled && self.synth.has_new_output())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn service(&mut self) -> Option<u8> {
|
|
||||||
self.need_service = false;
|
|
||||||
if self.enabled {
|
|
||||||
Some(self.synth.get_output())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SynthesizerService {
|
|
||||||
pub fn set_freq(&mut self, freq_hz: usize) {
|
|
||||||
self.synth.set_freq(freq_hz);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,7 @@ use hal::println;
|
||||||
pub unsafe fn init_gpio_irq(pin: u8, port: u8, rising: bool, falling: bool) {
|
pub unsafe fn init_gpio_irq(pin: u8, port: u8, rising: bool, falling: bool) {
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
#[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
println!("init_gpio_irq {pin}:{port}");
|
println!("init_gpio_irq");
|
||||||
let exti = &hal::pac::EXTI;
|
let exti = &hal::pac::EXTI;
|
||||||
let afio = &hal::pac::AFIO;
|
let afio = &hal::pac::AFIO;
|
||||||
|
|
||||||
|
|
@ -20,8 +20,12 @@ pub unsafe fn init_gpio_irq(pin: u8, port: u8, rising: bool, falling: bool) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> [bool; 2] {
|
// FIXME: should return a vec of the interrupts
|
||||||
let mut output = [false, false];
|
pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> crate::InputFlags {
|
||||||
|
let mut input_flags = crate::InputFlags {
|
||||||
|
coin_flag: false,
|
||||||
|
button_flag: false,
|
||||||
|
};
|
||||||
|
|
||||||
let exti = &hal::pac::EXTI;
|
let exti = &hal::pac::EXTI;
|
||||||
|
|
||||||
|
|
@ -35,21 +39,25 @@ pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> [bool; 2] {
|
||||||
|
|
||||||
// We don't handle or change any EXTI lines above 24.
|
// We don't handle or change any EXTI lines above 24.
|
||||||
let bits = bits.0 & 0x00FFFFFF;
|
let bits = bits.0 & 0x00FFFFFF;
|
||||||
#[cfg(feature = "enable_print")]
|
|
||||||
println!("bits: {bits:08x}");
|
|
||||||
|
|
||||||
// coin_flag
|
// coin_flag
|
||||||
if (bits & (0x1 << coin_pin)) != 0x0 {
|
if (bits & (0x1 << coin_pin)) != 0x0 {
|
||||||
#[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
println!("coin irq!");
|
println!("coin irq!");
|
||||||
output[0] = true;
|
input_flags.coin_flag = true;
|
||||||
|
// unsafe {
|
||||||
|
// INPUT_FLAGS.coin_flag = true;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// button_flag
|
// button_flag
|
||||||
if (bits & (0x1 << button_pin)) != 0x0 {
|
if (bits & (0x1 << button_pin)) != 0x0 {
|
||||||
#[cfg(feature = "enable_print")]
|
#[cfg(feature = "enable_print")]
|
||||||
println!("main_btn irq!");
|
println!("button irq!");
|
||||||
output[1] = true;
|
input_flags.button_flag = true;
|
||||||
|
// unsafe {
|
||||||
|
// INPUT_FLAGS.button_flag = true;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear pending - Clears the EXTI's line pending bits.
|
// Clear pending - Clears the EXTI's line pending bits.
|
||||||
|
|
@ -64,7 +72,7 @@ pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> [bool; 2] {
|
||||||
exti.intenr().modify(|w| w.set_mr(coin_pin, true)); // enable interrupt
|
exti.intenr().modify(|w| w.set_mr(coin_pin, true)); // enable interrupt
|
||||||
exti.intenr().modify(|w| w.set_mr(button_pin, true)); // enable interrupt
|
exti.intenr().modify(|w| w.set_mr(button_pin, true)); // enable interrupt
|
||||||
|
|
||||||
output
|
input_flags
|
||||||
}
|
}
|
||||||
|
|
||||||
/// enter standby (SLEEPDEEP) mode, with WFE enabled.
|
/// enter standby (SLEEPDEEP) mode, with WFE enabled.
|
||||||
|
|
@ -76,7 +84,7 @@ pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> [bool; 2] {
|
||||||
/// * (probably WFI?)
|
/// * (probably WFI?)
|
||||||
/// exit:
|
/// exit:
|
||||||
/// 1. any interrupt/event (set in external interrupt register)
|
/// 1. any interrupt/event (set in external interrupt register)
|
||||||
pub unsafe fn enter_standby() {
|
pub unsafe fn enter_standby(pin: usize) {
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
use hal::pac::Interrupt;
|
use hal::pac::Interrupt;
|
||||||
use qingke_rt::CoreInterrupt;
|
use qingke_rt::CoreInterrupt;
|
||||||
|
|
@ -95,7 +103,10 @@ pub unsafe fn enter_standby() {
|
||||||
// set PDDS=1:
|
// set PDDS=1:
|
||||||
// get current value of PWR_CTLR
|
// get current value of PWR_CTLR
|
||||||
let mut reg: u32 = 0x4000_7000;
|
let mut reg: u32 = 0x4000_7000;
|
||||||
let mut val: u32 = unsafe { (reg as *mut u32).read_volatile() };
|
let mut val: u32 = 0;
|
||||||
|
unsafe {
|
||||||
|
val = (reg as *mut u32).read_volatile();
|
||||||
|
}
|
||||||
// modify PDDS
|
// modify PDDS
|
||||||
val |= 1 << 1; // PWR_CTLR[1] -> PDDS
|
val |= 1 << 1; // PWR_CTLR[1] -> PDDS
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue