From a71c7986e6e59bcb4eba0887b06783f03e264e70 Mon Sep 17 00:00:00 2001 From: sigil-03 Date: Thu, 21 Aug 2025 11:08:26 -0600 Subject: [PATCH] add interrupts for most handlers --- .gitmodules | 5 +- ch32v-insert-coin/.cargo/config.toml | 4 +- ch32v-insert-coin/Cargo.lock | 56 +- ch32v-insert-coin/Cargo.toml | 5 +- ch32v-insert-coin/audio/sweep_dpcm_u4.raw | Bin 0 -> 3992 bytes ch32v-insert-coin/ext/adpcm-pwm-dac | 2 +- ch32v-insert-coin/ext/ch32-hal | 2 +- .../ext/patches/optional-exti.patch | 652 ++++++++++++++++++ ch32v-insert-coin/ext/qingke | 1 + .../src/insert_coin/insert_coin.rs | 105 ++- ch32v-insert-coin/src/insert_coin/mod.rs | 3 +- .../src/insert_coin/services/dac.rs | 3 +- .../src/insert_coin/services/led.rs | 4 +- .../src/insert_coin/services/tick_timer.rs | 14 +- ch32v-insert-coin/src/main.rs | 452 +++++++----- 15 files changed, 1028 insertions(+), 280 deletions(-) create mode 100644 ch32v-insert-coin/audio/sweep_dpcm_u4.raw create mode 100644 ch32v-insert-coin/ext/patches/optional-exti.patch create mode 160000 ch32v-insert-coin/ext/qingke diff --git a/.gitmodules b/.gitmodules index c6e7965..b695a5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,7 @@ url = ssh://git@git.glyphs.tech:222/sigil-03/adpcm-pwm-dac.git [submodule "ch32v-insert-coin/ext/ch32-hal"] path = ch32v-insert-coin/ext/ch32-hal - url = git@github.com:ch32-rs/ch32-hal.git + url = git@github.com:sigil-03/ch32-hal.git +[submodule "ch32v-insert-coin/ext/qingke"] + path = ch32v-insert-coin/ext/qingke + url = git@github.com:ch32-rs/qingke.git diff --git a/ch32v-insert-coin/.cargo/config.toml b/ch32v-insert-coin/.cargo/config.toml index cf4d2cd..68ad23c 100644 --- a/ch32v-insert-coin/.cargo/config.toml +++ b/ch32v-insert-coin/.cargo/config.toml @@ -6,9 +6,9 @@ target = "riscv32ec-unknown-none-elf.json" # runner = "riscv64-elf-gdb -q -x openocd.gdb" # runner = "riscv-none-embed-gdb -q -x openocd.gdb" # runner = "gdb -q -x openocd.gdb" -runner = "wlink -v flash" +# runner = "wlink -v flash" -# runner = "wlink -v flash --enable-sdi-print --watch-serial" +runner = "wlink -v flash --enable-sdi-print --watch-serial" # Flash and debug chip with probe-rs. https://probe.rs/ # runner = "probe-rs run --chip ch32v003" diff --git a/ch32v-insert-coin/Cargo.lock b/ch32v-insert-coin/Cargo.lock index 348625b..dc8468c 100644 --- a/ch32v-insert-coin/Cargo.lock +++ b/ch32v-insert-coin/Cargo.lock @@ -18,27 +18,6 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" -[[package]] -name = "bitfields" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdbce6688e3ab66aff2ab413b762ccde9f37990e27bba0bb38a4b2ad1b5d877" -dependencies = [ - "bitfields-impl", -] - -[[package]] -name = "bitfields-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57413e4b276d883b77fb368b7b33ae6a5eb97692852d49a5394d4f72ba961827" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", - "thiserror", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -71,7 +50,7 @@ dependencies = [ "futures", "nb 1.1.0", "proc-macro2", - "qingke", + "qingke 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "qingke-rt", "quote", "rand_core", @@ -92,11 +71,12 @@ name = "ch32v-insert-coin" version = "0.1.0" dependencies = [ "adpcm-pwm-dac", - "bitfields", "ch32-hal", + "critical-section", "embassy-executor", "embedded-hal 1.0.0", "panic-halt", + "qingke 0.5.0", "qingke-rt", ] @@ -487,6 +467,14 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "qingke" +version = "0.5.0" +dependencies = [ + "bit_field", + "riscv 0.12.1", +] + [[package]] name = "qingke" version = "0.5.0" @@ -504,7 +492,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b955c60adac70c6d40205b1dbe9f57e1151d06aa842069cdbaef7bc07ad283fd" dependencies = [ - "qingke", + "qingke 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "qingke-rt-macros", ] @@ -615,26 +603,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/ch32v-insert-coin/Cargo.toml b/ch32v-insert-coin/Cargo.toml index b4fc9c7..1e46f37 100644 --- a/ch32v-insert-coin/Cargo.toml +++ b/ch32v-insert-coin/Cargo.toml @@ -24,8 +24,11 @@ embedded-hal = "1.0.0" qingke-rt = { version = "*", features = ["highcode"] } +qingke = {path = "ext/qingke"} + adpcm-pwm-dac = { path = "ext/adpcm-pwm-dac/" } -bitfields = "1.0.0" + +critical-section = { version = "1.2.0" } [profile.release] strip = false # symbols are not flashed to the microcontroller, so don't strip them. diff --git a/ch32v-insert-coin/audio/sweep_dpcm_u4.raw b/ch32v-insert-coin/audio/sweep_dpcm_u4.raw new file mode 100644 index 0000000000000000000000000000000000000000..1adc4941f1b07961bce9b2958be16ba9abcb8700 GIT binary patch literal 3992 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 L-Z1^B;5Pm@L7`ta literal 0 HcmV?d00001 diff --git a/ch32v-insert-coin/ext/adpcm-pwm-dac b/ch32v-insert-coin/ext/adpcm-pwm-dac index ba25b7c..e4bb93e 160000 --- a/ch32v-insert-coin/ext/adpcm-pwm-dac +++ b/ch32v-insert-coin/ext/adpcm-pwm-dac @@ -1 +1 @@ -Subproject commit ba25b7c89f4deb52426d97fd35eb13496f183775 +Subproject commit e4bb93e0399f27024434adf2558a893574fbfef3 diff --git a/ch32v-insert-coin/ext/ch32-hal b/ch32v-insert-coin/ext/ch32-hal index 2b8e1c8..412b9f5 160000 --- a/ch32v-insert-coin/ext/ch32-hal +++ b/ch32v-insert-coin/ext/ch32-hal @@ -1 +1 @@ -Subproject commit 2b8e1c864ba5545ee65b1c77dcb17c86a471b70c +Subproject commit 412b9f5ee3a3708de8602d6103ec83c6dd436b63 diff --git a/ch32v-insert-coin/ext/patches/optional-exti.patch b/ch32v-insert-coin/ext/patches/optional-exti.patch new file mode 100644 index 0000000..2ea58f3 --- /dev/null +++ b/ch32v-insert-coin/ext/patches/optional-exti.patch @@ -0,0 +1,652 @@ +From 7b086336e3820714c564aac13dc44fbaf7f5bc17 Mon Sep 17 00:00:00 2001 +From: mindshub +Date: Sat, 9 Aug 2025 10:16:33 +0200 +Subject: [PATCH] optional exti + +--- + Cargo.toml | 3 +- + src/exti.rs | 548 ++++++++++++++++++++++++++-------------------------- + src/lib.rs | 1 + + 3 files changed, 281 insertions(+), 271 deletions(-) + +diff --git a/Cargo.toml b/Cargo.toml +index ec451bf..55fa288 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -56,7 +56,7 @@ proc-macro2 = "1.0" + quote = "1.0" + + [features] +-default = ["embassy", "rt"] ++default = ["embassy", "rt", "exti"] + rt = ["dep:qingke-rt"] + highcode = ["qingke-rt/highcode"] + embassy = [ +@@ -66,6 +66,7 @@ embassy = [ + ] + defmt = ["dep:defmt"] + memory-x = ["ch32-metapac/memory-x"] ++exti = [] + + + # Features starting with `_` are for internal use only. They're not intended +diff --git a/src/exti.rs b/src/exti.rs +index a2458d1..3d613ea 100644 +--- a/src/exti.rs ++++ b/src/exti.rs +@@ -1,37 +1,11 @@ +-use core::future::Future; +-use core::marker::PhantomData; +-use core::pin::Pin; +-use core::task::{Context, Poll}; +- + use embassy_sync::waitqueue::AtomicWaker; +-use qingke_rt::interrupt; + +-use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; +-use crate::{impl_peripheral, into_ref, peripherals, Peripheral}; ++use crate::{impl_peripheral, peripherals}; + + const EXTI_COUNT: usize = 24; + const NEW_AW: AtomicWaker = AtomicWaker::new(); + static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT]; + +-pub unsafe fn on_irq() { +- let exti = &crate::pac::EXTI; +- +- let bits = exti.intfr().read(); +- +- // We don't handle or change any EXTI lines above 24. +- let bits = bits.0 & 0x00FFFFFF; +- +- // Clear pending - Clears the EXTI's line pending bits. +- exti.intfr().write(|w| w.0 = bits); +- +- exti.intenr().modify(|w| w.0 = w.0 & !bits); +- +- // Wake the tasks +- for pin in BitIter(bits) { +- EXTI_WAKERS[pin as usize].wake(); +- } +-} +- + struct BitIter(u32); + + impl Iterator for BitIter { +@@ -48,150 +22,6 @@ impl Iterator for BitIter { + } + } + +-/// EXTI input driver +-pub struct ExtiInput<'d> { +- pin: Input<'d>, +-} +- +-impl<'d> Unpin for ExtiInput<'d> {} +- +-impl<'d> ExtiInput<'d> { +- pub fn new( +- pin: impl Peripheral

+ 'd, +- ch: impl Peripheral

+ 'd, +- pull: Pull, +- ) -> Self { +- into_ref!(pin, ch); +- // Needed if using AnyPin+AnyChannel. +- assert_eq!(pin.pin(), ch.number()); +- +- Self { +- pin: Input::new(pin, pull), +- } +- } +- +- pub fn is_high(&self) -> bool { +- self.pin.is_high() +- } +- +- pub fn is_low(&self) -> bool { +- self.pin.is_low() +- } +- +- pub fn get_level(&self) -> Level { +- self.pin.get_level() +- } +- +- pub async fn wait_for_high<'a>(&'a mut self) { +- let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); +- if self.is_high() { +- return; +- } +- fut.await +- } +- +- pub async fn wait_for_low<'a>(&'a mut self) { +- let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); +- if self.is_low() { +- return; +- } +- fut.await +- } +- +- pub async fn wait_for_rising_edge<'a>(&'a mut self) { +- ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await +- } +- +- pub async fn wait_for_falling_edge<'a>(&'a mut self) { +- ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await +- } +- +- pub async fn wait_for_any_edge<'a>(&'a mut self) { +- ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await +- } +-} +- +-#[must_use = "futures do nothing unless you `.await` or poll them"] +-struct ExtiInputFuture<'a> { +- pin: u8, +- phantom: PhantomData<&'a mut AnyPin>, +-} +- +-// EXTI0-EXTI23 Px0-Px23(x=A/B/C) +-impl<'a> ExtiInputFuture<'a> { +- fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { +- critical_section::with(|_| { +- let exti = &crate::pac::EXTI; +- let afio = &crate::pac::AFIO; +- +- let port = port as u8; +- let pin = pin as usize; +- +- #[cfg(afio_v0)] +- { +- // AFIO_EXTICR +- // stride: 2, len: 15, 8 lines +- afio.exticr().modify(|w| w.set_exti(pin, port)); +- } +- // V1, V2, V3, L1 +- #[cfg(any(afio_v3, afio_l1))] +- { +- // AFIO_EXTICRx +- // stride: 4, len: 4, 16 lines +- afio.exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); +- } +- #[cfg(afio_x0)] +- { +- // stride: 2, len: 15, 24 lines +- afio.exticr(pin / 16).modify(|w| w.set_exti(pin % 16, port)); +- } +- #[cfg(afio_ch641)] +- { +- // single register +- afio.exticr().modify(|w| w.set_exti(pin, port != 0)); +- } +- +- // See-also: 7.4.3 +- exti.intenr().modify(|w| w.set_mr(pin, true)); // enable interrupt +- +- exti.rtenr().modify(|w| w.set_tr(pin, rising)); +- exti.ftenr().modify(|w| w.set_tr(pin, falling)); +- }); +- +- Self { +- pin, +- phantom: PhantomData, +- } +- } +-} +- +-impl<'a> Drop for ExtiInputFuture<'a> { +- fn drop(&mut self) { +- critical_section::with(|_| { +- let exti = &crate::pac::EXTI; +- let pin = self.pin; +- exti.intenr().modify(|w| w.0 = w.0 & !(1 << pin)); +- }); +- } +-} +- +-impl<'a> Future for ExtiInputFuture<'a> { +- type Output = (); +- +- fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { +- let exti = &crate::pac::EXTI; +- +- EXTI_WAKERS[self.pin as usize].register(cx.waker()); +- +- if exti.intenr().read().mr(self.pin as _) == false { +- // intenr cleared by on_irq, then we can assume it is triggered +- Poll::Ready(()) +- } else { +- Poll::Pending +- } +- } +-} +- + trait SealedChannel {} + + #[allow(private_bounds)] +@@ -207,6 +37,7 @@ pub trait Channel: SealedChannel + Sized { + pub struct AnyChannel { + number: u8, + } ++ + impl_peripheral!(AnyChannel); + impl SealedChannel for AnyChannel {} + impl Channel for AnyChannel { +@@ -267,128 +98,305 @@ mod _exti_24lines { + impl_exti!(EXTI23, 23); + } + +-/* +-EXTI0 +-EXTI1 +-EXTI2 +-EXTI3 +-EXTI4 +-EXTI9_5 +-EXTI15_10 +-EXTI7_0 +-EXTI15_8 +-EXTI25_16 +-*/ +- +-/// safety: must be called only once +-#[cfg(gpio_x0)] +-mod irq_impl { +- use super::*; ++#[cfg(feature = "exti")] ++pub use exti_inner::*; ++ ++#[cfg(feature = "exti")] ++mod exti_inner { ++ use super::{BitIter, Channel, EXTI_WAKERS}; ++ use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; ++ use crate::{into_ref, Peripheral}; ++ use core::future::Future; ++ use core::marker::PhantomData; ++ use core::pin::Pin; ++ use core::task::{Context, Poll}; ++ use qingke_rt::interrupt; ++ ++ /// EXTI input driver ++ pub struct ExtiInput<'d> { ++ pin: Input<'d>, ++ } ++ ++ impl<'d> Unpin for ExtiInput<'d> {} ++ ++ impl<'d> ExtiInput<'d> { ++ pub fn new( ++ pin: impl Peripheral

+ 'd, ++ ch: impl Peripheral

+ 'd, ++ pull: Pull, ++ ) -> Self { ++ into_ref!(pin, ch); ++ // Needed if using AnyPin+AnyChannel. ++ assert_eq!(pin.pin(), ch.number()); ++ ++ Self { ++ pin: Input::new(pin, pull), ++ } ++ } + +- #[interrupt] +- unsafe fn EXTI7_0() { +- on_irq(); +- } +- #[interrupt] +- unsafe fn EXTI15_8() { +- on_irq(); +- } +- #[interrupt] +- unsafe fn EXTI25_16() { +- on_irq(); +- } ++ pub fn is_high(&self) -> bool { ++ self.pin.is_high() ++ } + +- pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { +- use crate::pac::Interrupt; ++ pub fn is_low(&self) -> bool { ++ self.pin.is_low() ++ } + +- qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI25_16 as u8); +- } +-} ++ pub fn get_level(&self) -> Level { ++ self.pin.get_level() ++ } + +-#[cfg(all(gpio_v3, not(ch641)))] +-mod irq_impl { +- use super::*; ++ pub async fn wait_for_high<'a>(&'a mut self) { ++ let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); ++ if self.is_high() { ++ return; ++ } ++ fut.await ++ } + +- #[interrupt] +- unsafe fn EXTI0() { +- on_irq(); +- } +- #[interrupt] +- unsafe fn EXTI1() { +- on_irq(); +- } +- #[interrupt] +- unsafe fn EXTI2() { +- on_irq(); +- } +- #[interrupt] +- unsafe fn EXTI3() { +- on_irq(); ++ pub async fn wait_for_low<'a>(&'a mut self) { ++ let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); ++ if self.is_low() { ++ return; ++ } ++ fut.await ++ } ++ ++ pub async fn wait_for_rising_edge<'a>(&'a mut self) { ++ ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await ++ } ++ ++ pub async fn wait_for_falling_edge<'a>(&'a mut self) { ++ ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await ++ } ++ ++ pub async fn wait_for_any_edge<'a>(&'a mut self) { ++ ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await ++ } + } +- #[interrupt] +- unsafe fn EXTI4() { +- on_irq(); ++ ++ pub unsafe fn on_irq() { ++ let exti = &crate::pac::EXTI; ++ ++ let bits = exti.intfr().read(); ++ ++ // We don't handle or change any EXTI lines above 24. ++ let bits = bits.0 & 0x00FFFFFF; ++ ++ // Clear pending - Clears the EXTI's line pending bits. ++ exti.intfr().write(|w| w.0 = bits); ++ ++ exti.intenr().modify(|w| w.0 = w.0 & !bits); ++ ++ // Wake the tasks ++ for pin in BitIter(bits) { ++ EXTI_WAKERS[pin as usize].wake(); ++ } + } +- #[interrupt] +- unsafe fn EXTI9_5() { +- on_irq(); ++ ++ #[must_use = "futures do nothing unless you `.await` or poll them"] ++ struct ExtiInputFuture<'a> { ++ pin: u8, ++ phantom: PhantomData<&'a mut AnyPin>, ++ } ++ ++ // EXTI0-EXTI23 Px0-Px23(x=A/B/C) ++ impl<'a> ExtiInputFuture<'a> { ++ fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { ++ critical_section::with(|_| { ++ let exti = &crate::pac::EXTI; ++ let afio = &crate::pac::AFIO; ++ ++ let port = port as u8; ++ let pin = pin as usize; ++ ++ #[cfg(afio_v0)] ++ { ++ // AFIO_EXTICR ++ // stride: 2, len: 15, 8 lines ++ afio.exticr().modify(|w| w.set_exti(pin, port)); ++ } ++ // V1, V2, V3, L1 ++ #[cfg(any(afio_v3, afio_l1))] ++ { ++ // AFIO_EXTICRx ++ // stride: 4, len: 4, 16 lines ++ afio.exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); ++ } ++ #[cfg(afio_x0)] ++ { ++ // stride: 2, len: 15, 24 lines ++ afio.exticr(pin / 16).modify(|w| w.set_exti(pin % 16, port)); ++ } ++ #[cfg(afio_ch641)] ++ { ++ // single register ++ afio.exticr().modify(|w| w.set_exti(pin, port != 0)); ++ } ++ ++ // See-also: 7.4.3 ++ exti.intenr().modify(|w| w.set_mr(pin, true)); // enable interrupt ++ ++ exti.rtenr().modify(|w| w.set_tr(pin, rising)); ++ exti.ftenr().modify(|w| w.set_tr(pin, falling)); ++ }); ++ ++ Self { ++ pin, ++ phantom: PhantomData, ++ } ++ } + } +- #[interrupt] +- unsafe fn EXTI15_10() { +- on_irq(); ++ ++ impl<'a> Drop for ExtiInputFuture<'a> { ++ fn drop(&mut self) { ++ critical_section::with(|_| { ++ let exti = &crate::pac::EXTI; ++ let pin = self.pin; ++ exti.intenr().modify(|w| w.0 = w.0 & !(1 << pin)); ++ }); ++ } + } + +- pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { +- use crate::pac::Interrupt; ++ impl<'a> Future for ExtiInputFuture<'a> { ++ type Output = (); + +- qingke::pfic::enable_interrupt(Interrupt::EXTI0 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI1 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI2 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI3 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI4 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI9_5 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI15_10 as u8); +- } +-} ++ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ++ let exti = &crate::pac::EXTI; + +-#[cfg(gpio_v0)] +-mod irq_impl { +- use super::*; ++ EXTI_WAKERS[self.pin as usize].register(cx.waker()); + +- #[interrupt] +- unsafe fn EXTI7_0() { +- on_irq(); ++ if exti.intenr().read().mr(self.pin as _) == false { ++ // intenr cleared by on_irq, then we can assume it is triggered ++ Poll::Ready(()) ++ } else { ++ Poll::Pending ++ } ++ } + } + +- pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { +- use crate::pac::Interrupt; ++ /* ++ EXTI0 ++ EXTI1 ++ EXTI2 ++ EXTI3 ++ EXTI4 ++ EXTI9_5 ++ EXTI15_10 ++ EXTI7_0 ++ EXTI15_8 ++ EXTI25_16 ++ */ ++ ++ /// safety: must be called only once ++ #[cfg(gpio_x0)] ++ mod irq_impl { ++ use super::*; ++ ++ #[interrupt] ++ unsafe fn EXTI7_0() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI15_8() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI25_16() { ++ on_irq(); ++ } ++ ++ pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { ++ use crate::pac::Interrupt; + +- qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI25_16 as u8); ++ } + } +-} + +-#[cfg(all(gpio_v3, ch641))] +-mod irq_impl { +- use super::*; ++ #[cfg(all(gpio_v3, not(ch641)))] ++ mod irq_impl { ++ use super::*; ++ ++ #[interrupt] ++ unsafe fn EXTI0() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI1() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI2() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI3() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI4() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI9_5() { ++ on_irq(); ++ } ++ #[interrupt] ++ unsafe fn EXTI15_10() { ++ on_irq(); ++ } + +- #[interrupt] +- unsafe fn EXTI7_0() { +- on_irq(); ++ pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { ++ use crate::pac::Interrupt; ++ ++ qingke::pfic::enable_interrupt(Interrupt::EXTI0 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI1 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI2 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI3 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI4 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI9_5 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI15_10 as u8); ++ } + } + +- #[interrupt] +- unsafe fn EXTI15_8() { +- on_irq(); ++ #[cfg(gpio_v0)] ++ mod irq_impl { ++ use super::*; ++ ++ #[interrupt] ++ unsafe fn EXTI7_0() { ++ on_irq(); ++ } ++ ++ pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { ++ use crate::pac::Interrupt; ++ ++ qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); ++ } + } + +- pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { +- use crate::pac::Interrupt; ++ #[cfg(all(gpio_v3, ch641))] ++ mod irq_impl { ++ use super::*; ++ ++ #[interrupt] ++ unsafe fn EXTI7_0() { ++ on_irq(); ++ } ++ ++ #[interrupt] ++ unsafe fn EXTI15_8() { ++ on_irq(); ++ } ++ ++ pub(crate) unsafe fn init(_cs: critical_section::CriticalSection) { ++ use crate::pac::Interrupt; + +- qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); +- qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); ++ qingke::pfic::enable_interrupt(Interrupt::EXTI15_8 as u8); ++ } + } ++ pub(crate) use irq_impl::*; + } +- +-pub(crate) use irq_impl::*; +diff --git a/src/lib.rs b/src/lib.rs +index 997da34..b451a55 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -140,6 +140,7 @@ pub fn init(config: Config) -> Peripherals { + ::critical_section::with(|cs| unsafe { + gpio::init(cs); + dma::init(cs, config.dma_interrupt_priority); ++ #[cfg(feature = "exti")] + exti::init(cs); + }); + + diff --git a/ch32v-insert-coin/ext/qingke b/ch32v-insert-coin/ext/qingke new file mode 160000 index 0000000..2443891 --- /dev/null +++ b/ch32v-insert-coin/ext/qingke @@ -0,0 +1 @@ +Subproject commit 2443891811d7351d62e78085bcf60fa78c063c08 diff --git a/ch32v-insert-coin/src/insert_coin/insert_coin.rs b/ch32v-insert-coin/src/insert_coin/insert_coin.rs index fe69780..3d0dacb 100644 --- a/ch32v-insert-coin/src/insert_coin/insert_coin.rs +++ b/ch32v-insert-coin/src/insert_coin/insert_coin.rs @@ -1,8 +1,6 @@ -use adpcm_pwm_dac::dac; use ch32_hal::timer::GeneralInstance16bit; use ch32_hal::timer::simple_pwm::SimplePwm; use ch32_hal::timer::Channel; -use ch32_hal::delay::Delay; use crate::insert_coin::services::{DacService, LedService, TickService, TickServiceData, Service}; @@ -40,9 +38,9 @@ impl<'d, T: GeneralInstance16bit> SimplePwmCore<'d, T> { self.pwm.borrow_mut().set_duty(ch, dc); } - pub fn disable(&self, ch: Channel) { - self.pwm.borrow_mut().disable(ch); - } + // pub fn disable(&self, ch: Channel) { + // self.pwm.borrow_mut().disable(ch); + // } } @@ -61,7 +59,7 @@ impl CoreConfig { #[derive(Default)] struct Core { - tick: usize, + _tick: usize, active: bool, } @@ -73,7 +71,7 @@ pub struct InsertCoin<'a, T: GeneralInstance16bit> { pub led1: LedService, // led2: LedService, - dac: DacService<'a>, + pub dac: DacService<'a>, } impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> { @@ -82,25 +80,17 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> { // LED0 servicer setup - let led0_blink_rate_hz = 9; - let led0_tick_per_service = (config.tick_rate_hz/(led0_blink_rate_hz * 2)); - let led0_service_data = TickServiceData::new(led0_tick_per_service); - let led0 = LedService::new(ch32_hal::timer::Channel::Ch3, led0_service_data); + let led0 = LedService::new(ch32_hal::timer::Channel::Ch3); // LED1 servicer setup - let led1_blink_rate_hz = 3; - let led1_tick_per_service = (config.tick_rate_hz/(led1_blink_rate_hz * 2)); - let led1_service_data = TickServiceData::new(led1_tick_per_service); - let led1 = LedService::new(ch32_hal::timer::Channel::Ch1, led1_service_data); + let led1 = LedService::new(ch32_hal::timer::Channel::Ch1); // DAC servicer setup 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 = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data); - let data = include_bytes!("../../../../dpcm-encoder-decoder/sweep_dpcm_u4.raw"); - dac.load_data(data); Self { config, @@ -110,11 +100,6 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> { led1, // led2, dac, - // led1: Led { - // channel: hal::timer::Channel::Ch1, - // amplitude: 0, - // need_service: false, - // }, } } @@ -144,55 +129,53 @@ impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> { } - /// consumes self and runs - pub fn run(mut self) -> ! { - let mut delay = Delay; - let tick_interval_us = 1000000/self.config.tick_rate_hz; + // /// consumes self and runs + // pub fn run(mut self) -> ! { + // let mut delay = Delay; + // let tick_interval_us = 1000000/self.config.tick_rate_hz; - let mut led0_index = 0; - let led0_dcs = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8]; + // let mut led0_index = 0; + // let led0_dcs = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8]; - let mut led1_index = 0; - let led1_dcs = [0u8, 25u8, 50u8, 75u8, 100u8]; + // let mut led1_index = 0; + // let led1_dcs = [0u8, 25u8, 50u8, 75u8, 100u8]; - loop { - self.dac.tick(); + // loop { + // self.dac.tick(); - if(self.led0.need_service()) { - self.led0.set_amplitude(led0_dcs[led0_index]); - self.pwm_core.write_amplitude(self.led0.channel, self.led0.amplitude); + // if(self.led0.need_service()) { + // self.led0.set_amplitude(led0_dcs[led0_index]); + // self.pwm_core.write_amplitude(self.led0.channel, self.led0.amplitude); - led0_index += 1; - if led0_index > led0_dcs.len() - 1 { - led0_index = 0; - } - self.led0.service(); - } + // led0_index += 1; + // if led0_index > led0_dcs.len() - 1 { + // led0_index = 0; + // } + // self.led0.service(); + // } - if(self.led1.need_service()) { - self.led1.set_amplitude(led1_dcs[led1_index]); - self.pwm_core.write_amplitude(self.led1.channel, self.led1.amplitude); + // if(self.led1.need_service()) { + // self.led1.set_amplitude(led1_dcs[led1_index]); + // self.pwm_core.write_amplitude(self.led1.channel, self.led1.amplitude); - led1_index += 1; - if led1_index > led1_dcs.len() - 1 { - led1_index = 0; - } - self.led1.service(); - } + // led1_index += 1; + // if led1_index > led1_dcs.len() - 1 { + // led1_index = 0; + // } + // self.led1.service(); + // } - if(self.dac.need_service()) { - self.dac.service(); - // TODO: adpcm-pwm-dac:e4c811653781e69e40b63fd27a8c1e20 - self.pwm_core.write_amplitude(self.dac.channel, self.dac.get_amplitude() as u8); - } + // if(self.dac.need_service()) { + // self.dac.service(); + // // TODO: adpcm-pwm-dac:e4c811653781e69e40b63fd27a8c1e20 + // self.pwm_core.write_amplitude(self.dac.channel, self.dac.get_amplitude() as u8); + // } - - - delay.delay_us(tick_interval_us as u32); - } - } + // delay.delay_us(tick_interval_us as u32); + // } + // } pub fn is_active(&self) -> bool { self.core.active diff --git a/ch32v-insert-coin/src/insert_coin/mod.rs b/ch32v-insert-coin/src/insert_coin/mod.rs index e13b953..e7c42d8 100644 --- a/ch32v-insert-coin/src/insert_coin/mod.rs +++ b/ch32v-insert-coin/src/insert_coin/mod.rs @@ -1,8 +1,7 @@ mod insert_coin; mod services; -use services::LedService; pub use services::TickTimerService; -pub use services::{TickService, TickServiceData, Service}; +pub use services::{TickService, TickServiceData}; pub use insert_coin::{InsertCoin, CoreConfig, SimplePwmCore}; \ No newline at end of file diff --git a/ch32v-insert-coin/src/insert_coin/services/dac.rs b/ch32v-insert-coin/src/insert_coin/services/dac.rs index 22681de..4cdaec0 100644 --- a/ch32v-insert-coin/src/insert_coin/services/dac.rs +++ b/ch32v-insert-coin/src/insert_coin/services/dac.rs @@ -1,7 +1,7 @@ use crate::insert_coin::services::{TickServiceData, TickService}; -use adpcm_pwm_dac::{dac::{DpcmDac, DpcmDecoder}, interface::DacInterface}; +use adpcm_pwm_dac::dac::DpcmDecoder; use ch32_hal::timer::Channel; pub struct DacService<'a> { @@ -23,6 +23,7 @@ impl<'a> DacService<'a> { pub fn load_data(&self, data: &'a [u8]) { self.dpcm_decoder.borrow_mut().load_data(data); + self.dpcm_decoder.borrow_mut().seek_to_sample(0); } pub fn set_amplitude(&self, amplitude: usize) { diff --git a/ch32v-insert-coin/src/insert_coin/services/led.rs b/ch32v-insert-coin/src/insert_coin/services/led.rs index d248ab6..b7e2056 100644 --- a/ch32v-insert-coin/src/insert_coin/services/led.rs +++ b/ch32v-insert-coin/src/insert_coin/services/led.rs @@ -1,6 +1,6 @@ use ch32_hal::timer::Channel; -use crate::insert_coin::services::{Service, TickService, TickServiceData}; +use crate::insert_coin::services::{Service}; pub struct LedService { // need_service: core::cell::RefCell, @@ -10,7 +10,7 @@ pub struct LedService { } impl LedService { - pub fn new(channel: Channel, service_data: TickServiceData) -> Self { + pub fn new(channel: Channel) -> Self { Self { // service_data: core::cell::RefCell::new(service_data), need_service: false, diff --git a/ch32v-insert-coin/src/insert_coin/services/tick_timer.rs b/ch32v-insert-coin/src/insert_coin/services/tick_timer.rs index 90e0188..a10ab74 100644 --- a/ch32v-insert-coin/src/insert_coin/services/tick_timer.rs +++ b/ch32v-insert-coin/src/insert_coin/services/tick_timer.rs @@ -1,10 +1,8 @@ -use ch32_hal::timer::Channel; - use crate::insert_coin::services::{TickServiceData, TickService}; pub struct TickTimerService { service_data: core::cell::RefCell, - auto_reset: bool, + _auto_reset: bool, enabled: bool, } @@ -12,18 +10,18 @@ impl TickTimerService { pub fn new(service_data: TickServiceData, auto_reset: bool) -> Self { Self { service_data: core::cell::RefCell::new(service_data), - auto_reset, + _auto_reset: auto_reset, enabled: false, } } - pub fn is_enabled(&self) -> bool { - self.enabled - } + // pub fn is_enabled(&self) -> bool { + // self.enabled + // } pub fn reset(&mut self) { let mut sd = self.service_data.borrow_mut(); - sd.ticks_per_service = sd.ticks_remaining; + sd.ticks_remaining = sd.ticks_per_service; self.enabled = false; } diff --git a/ch32v-insert-coin/src/main.rs b/ch32v-insert-coin/src/main.rs index 59515b9..ac2877e 100644 --- a/ch32v-insert-coin/src/main.rs +++ b/ch32v-insert-coin/src/main.rs @@ -4,85 +4,167 @@ #![feature(impl_trait_in_assoc_type)] mod insert_coin; +use ch32_hal::{interrupt::typelevel::Handler, timer::low_level::OutputPolarity}; use insert_coin::{InsertCoin, SimplePwmCore, CoreConfig}; -use adpcm_pwm_dac::dac::DpcmDac; use {ch32_hal as hal}; -use hal::peripherals::EXTI4; -use hal::{bind_interrupts, interrupt}; +use hal::{bind_interrupts}; use hal::delay::Delay; -use hal::gpio::{AnyPin, Level, Input, Output, Pin, Pull}; +use hal::gpio::{AnyPin, Input, Pin, Pull}; use hal::time::Hertz; use hal::timer::low_level::CountingMode; use hal::timer::simple_pwm::{PwmPin, SimplePwm}; -use hal::timer::{Channel, GeneralInstance16bit}; -// #[qingke_rt::interrupt] -// fn EXTI4(){ -// unsafe{IRQ1_FLAG = true;}; -// } -// bind_interrupts!(struct Irqs { -// EXTI4 => button_press(); -// }); +use hal::println; + +use qingke::riscv; -// const DAC_DATA: [u8; 4] = [0x0, 0x80, 0xFF, 0x80]; -const DAC_DATA: [u8; 8] = [0, 25, 50, 75, 100, 75, 50, 25]; +struct DebouncedGPIO<'a> { + input: Input<'a>, + // value of the GPIO + value: bool, + // GPIO is ready (debounced) + ready: bool, + // debounce timer + timer: TickTimerService, +} -// DPCS DATA -// step size: 5 -// 0 -// + 25 -> [1, 0, 1, 1] -> 0xB -// + 25 -> 0xB -// + 25 -> 0xB -// + 25 -> 0xB -// - 25 -> 0xA -// - 25 -> 0xA -// - 25 -> 0xA -const DPCM_DAC_DATA: [u8; 4] = [0xBB, 0xBB, 0xAA, 0xAA]; +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::Up), + value: false, + ready: false, + timer: TickTimerService::new(TickServiceData::new(system_tick_rate_hz * debounce_time_ms / 1000), false), + } + } -static mut IRQ1_FLAG: bool = false; + pub fn ready(&self) -> bool { + self.ready + } + + 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; + } + } +} -// struct SimplePwmHandle<'a, 'c, T: GeneralInstance16bit>{ -// core: &'a SimplePwmCore<'c, T>, -// channel: Channel, -// } +// DeepSleep --coin button irq--> Active +// Active --2s button--> Idle +// Idle/Active --5s button--> DeepSleep -// impl<'a, 'c, T: GeneralInstance16bit> SimplePwmHandle<'a, 'c, T> { -// pub fn write_amplitude(&'a self, amplitude: u8) { -// self.core.write_amplitude(self.channel, amplitude); -// } +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, +} -// pub fn disable(&'a self) { -// self.core.disable(self.channel); -// } -// } +unsafe fn init_gpio_irq(pin: u8, port: u8, rising: bool, falling: bool) { + critical_section::with(|_| { + println!("init_gpio_irq"); + let exti = &hal::pac::EXTI; + let afio = &hal::pac::AFIO; -// struct SimplePwmDacHandle<'d, T: GeneralInstance16bit>{ -// pin: core::cell::RefCell>, -// ch: Channel, -// } + let port = port as u8; + let pin = pin as usize; + + // let b = afio.exticr().read(); + afio.exticr().modify(|w| w.set_exti(pin, port)); + + exti.intenr().modify(|w| w.set_mr(pin, true)); // enable interrupt + exti.rtenr().modify(|w| w.set_tr(pin, rising)); + exti.ftenr().modify(|w| w.set_tr(pin, falling)); + }); +} -// use adpcm_pwm_dac::{interface::DacInterface, dac::Dac}; +fn clear_interrupt (coin_pin: u8, button_pin: u8) { + let exti = &hal::pac::EXTI; + + let coin_pin = coin_pin as usize; + let button_pin = button_pin as usize; + + exti.intenr().modify(|w| w.set_mr(coin_pin, false)); // disable interrupt + exti.intenr().modify(|w| w.set_mr(button_pin, false)); // disable interrupt + + + let bits = exti.intfr().read(); + + // We don't handle or change any EXTI lines above 24. + let bits = bits.0 & 0x00FFFFFF; + + // coin_flag + if (bits & (0x1 << coin_pin)) != 0x0 { + println!("coin irq!"); + unsafe { INPUT_FLAGS.coin_flag = true; } + } + + + // button_flag + if (bits & (0x1 << button_pin)) != 0x0 { + println!("button irq!"); + unsafe { INPUT_FLAGS.button_flag = true; } + } + + + // Clear pending - Clears the EXTI's line pending bits. + exti.intfr().write(|w| w.0 = bits); + + exti.intenr().modify(|w| w.0 = w.0 & !bits); + exti.intenr().modify(|w| w.set_mr(coin_pin, true)); // enable interrupt + exti.intenr().modify(|w| w.set_mr(button_pin, true)); // enable interrupt +} + + +#[derive(Debug)] +struct InputFlags { + coin_flag: bool, + button_flag: bool, +} + +static mut INPUT_FLAGS: InputFlags = InputFlags{coin_flag: false, button_flag: false}; + +struct Test { +} +impl Handler for Test { + unsafe fn on_interrupt() { + println!("on_interrupt()"); + critical_section::with(|_| { + clear_interrupt(4, 6); + }); + } +} + +bind_interrupts!(struct Irqs { + EXTI7_0 => Test; +}); -// impl DacInterface for SimplePwmDacHandle<'_, T> -// where T: GeneralInstance16bit { -// fn write_amplitude(&mut self, amplitude: u8) { -// if !self.pin.borrow().is_enabled(self.ch) { -// self.pin.get_mut().enable(self.ch); -// } -// let max_duty = self.pin.borrow().get_max_duty(); -// let dc = amplitude as u32 * max_duty / 100; -// self.pin.get_mut().set_duty(self.ch, dc); -// } -// fn disable(&mut self) { -// self.pin.get_mut().disable(self.ch); -// } -// } // TODO: remove use insert_coin::{TickService, TickServiceData}; @@ -90,23 +172,28 @@ use insert_coin::TickTimerService; #[qingke_rt::entry] fn main() -> ! { + hal::debug::SDIPrint::enable(); let mut config = hal::Config::default(); config.rcc = hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSE; let p = hal::init(config); - // coin button input setup - let coin_button = Input::new(p.PD4, Pull::Up); + // delay to let the debugger attach + let mut delay = Delay; + delay.delay_ms(1000); - // p.EXTI4 - // let ei = hal::exti::ExtiInput::new(p.PD4, p.EXTI4, Pull::Up); - + // === output setup === + // LED0 output setup - let mut 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 mut led0_pin = Output::new(p.PC3, Level::High, Default::default()); + // unsafe { + // LED = Some(led0_pin); + // } // LED1 output setup - let mut led1_pin = PwmPin::new_ch1::<0>(p.PD2); + let led1_pin = PwmPin::new_ch1::<0>(p.PD2); let led1_ch = hal::timer::Channel::Ch1; @@ -114,7 +201,7 @@ fn main() -> ! { // DAC output setup let dac_pin = PwmPin::new_ch4::<0>(p.PC4); - let dac_ch = hal::timer::Channel::Ch4; + // let dac_ch = hal::timer::Channel::Ch4; @@ -129,15 +216,38 @@ fn main() -> ! { CountingMode::default(), ); + pwm.set_polarity(led0_ch, OutputPolarity::ActiveLow); + pwm.set_polarity(led1_ch, OutputPolarity::ActiveLow); + let sample_rate_hz = 16000; let dac_tick_per_service = 5; let tick_rate_hz = sample_rate_hz * dac_tick_per_service; - let config = CoreConfig::new(tick_rate_hz); + let core_config = CoreConfig::new(tick_rate_hz); + + + // === input setup === + + // definitions + let coin_pin = p.PD4; + let button_pin = p.PD6; + println!("coin pin: {} | coin port: {}", coin_pin.pin(), coin_pin.port()); + println!("push pin: {} | push port: {}", button_pin.pin(), button_pin.port()); + + // set up interrupts + unsafe {init_gpio_irq(coin_pin.pin(), coin_pin.port(), false, true)}; + unsafe {init_gpio_irq(button_pin.pin(), button_pin.port(), true, true)}; + + // coin debouncer (100ms) + let mut coin_input = DebouncedGPIO::new(coin_pin.degrade(), core_config.tick_rate_hz, 100); + // button debouncer (100ms) + let mut button_input = DebouncedGPIO::new(button_pin.degrade(), core_config.tick_rate_hz, 100); + + let pwm_core = SimplePwmCore::new(pwm); - let mut interfaces = InsertCoin::new(config, pwm_core); + let mut interfaces = InsertCoin::new(core_config, pwm_core); interfaces.set_active(true); // insert_coin.init(); @@ -149,22 +259,16 @@ fn main() -> ! { // tick timer 0 let tt0_fire_rate_hz = 9; - let tt0_tick_per_service = (interfaces.config.tick_rate_hz/(tt0_fire_rate_hz * 2)); + 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); // 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_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); - // debounce timer - // let db_ticks = interfaces.config.tick_rate_hz / 100; - let db_ticks = interfaces.config.tick_rate_hz / 100; - let db_timer_data = TickServiceData::new(db_ticks); - let mut db_timer = TickTimerService::new(db_timer_data, false); - db_timer.reset(); // short press timer let sp_ticks = 2 * interfaces.config.tick_rate_hz; @@ -179,49 +283,44 @@ fn main() -> ! { lp_timer.reset(); let mut delay = Delay; - let tick_interval_us = 1000000/interfaces.config.tick_rate_hz; - - interfaces.led0.set_amplitude(100); - interfaces.led1.set_amplitude(100); + let tick_interval_us = 1000000/interfaces.config.tick_rate_hz - 10; + + // dac data + let coin_sound = include_bytes!("../audio/sweep_dpcm_u4.raw"); + let button_sound = include_bytes!("../audio/sweep_dpcm_u4.raw"); + + let mut system_state = SystemState::DeepSleep; + + interfaces.led0.set_amplitude(0); + interfaces.led1.set_amplitude(0); + interfaces.service(); + + unsafe { + use hal::pac::Interrupt; + + qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8); + } + // MAIN APPLICATION + // process + // -depress big button (insert coin button) and it wakes up and turns on led one led at a fixed brightness + // -we will want one sound, the coin insert sound, to play when coin button is pressed. (This is when they insert a coin) + // -We will want a different sound or potentially multiple different sounds played in a rotating fashion when someone presses the button. Probably do some led blinking as well.(This is when they depress the big insert to play button) + // -depress the big button for approx 2s and it puts the led into low light mode. Just making it dimmer than usual. + // -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw + + println!("begin"); loop { - // wait for button to be released if it's pressed - if (coin_button.is_low()) { - loop { - if coin_button.is_high() && !db_timer.is_enabled() { - db_timer.enable(true); - } - if db_timer.need_service() { - db_timer.reset(); - if coin_button.is_high() { - break; - } - } - db_timer.tick(); - delay.delay_us(tick_interval_us as u32); - } - } - // wait for button input - loop { - if coin_button.is_low() && !db_timer.is_enabled() { - db_timer.enable(true); - } - if db_timer.need_service() { - db_timer.reset(); - if coin_button.is_low() { - break; - } - } - db_timer.tick(); - delay.delay_us(tick_interval_us as u32); - } + match system_state { + SystemState::DeepSleep => { + // TODO: make this REALLY deep sleep + riscv::asm::wfi(); + }, + SystemState::Idle => { - let mut active = true; - tt0.enable(true); - tt1.enable(true); - loop { - if active { + }, + SystemState::Active => { tt0.tick(); tt1.tick(); @@ -243,61 +342,102 @@ fn main() -> ! { tt1.service() } - } + interfaces.service(); + }, + } - // buttons - - // SP - if coin_button.is_low() && !sp_timer.is_enabled() { - sp_timer.reset(); - sp_timer.enable(true); - } - if sp_timer.need_service() { - sp_timer.reset(); - if coin_button.is_low() { - active = false; - // TODO: fix polarity - interfaces.led0.set_amplitude(90); - interfaces.led1.set_amplitude(90); + + { + // system input servicing + unsafe { + if INPUT_FLAGS.coin_flag { + 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 { + println!("button flag active"); + INPUT_FLAGS.button_flag = false; + button_input.begin(); } } - if coin_button.is_high() && sp_timer.is_enabled() { - sp_timer.reset(); + + // debouncer + coin_input.service(); + button_input.service(); + + + if coin_input.ready() { + println!("debounced coin_input value: {}", coin_input.value()); + coin_input.reset(); + } + if button_input.ready() { + + let value = button_input.value(); + button_input.reset(); + println!("debounced button_input value: {}", value); + + if !value { + interfaces.dac.load_data(button_sound); + + println!("reset hold timers + enable"); + sp_timer.reset(); + sp_timer.enable(true); + lp_timer.reset(); + lp_timer.enable(true); + } + else { + sp_timer.reset(); + lp_timer.reset(); + } + + } + + // timers sp_timer.tick(); - - - // LP - if coin_button.is_low() && !lp_timer.is_enabled() { - lp_timer.reset(); - lp_timer.enable(true); - } - if lp_timer.need_service() { - lp_timer.reset(); - if coin_button.is_low() { - interfaces.led0.set_amplitude(100); - interfaces.led1.set_amplitude(100); - // break; - } - } - if coin_button.is_high() && lp_timer.is_enabled() { - lp_timer.reset(); - } lp_timer.tick(); - interfaces.service(); - delay.delay_us(tick_interval_us as u32); + if sp_timer.need_service() { + println!("sp detect!"); + sp_timer.reset(); + + // todo enter idle + system_state = SystemState::Idle; + // TODO: fix polarity + interfaces.led0.set_amplitude(10); + interfaces.led1.set_amplitude(10); + interfaces.service(); + } + + if lp_timer.need_service() { + println!("lp detect!"); + lp_timer.reset(); + + // todo enter deepsleep + system_state = SystemState::DeepSleep; + // TODO: fix polarity + interfaces.led0.set_amplitude(0); + interfaces.led1.set_amplitude(0); + interfaces.service(); + } } + + delay.delay_us(tick_interval_us as u32); } - // // DAC servicer setup - // let mut pwm_dac_interface = SimplePwmDacHandle{pin: core::cell::RefCell::new(pwm), ch: dac_ch}; - // let mut dac = DpcmDac::new(pwm_dac_interface); - // let mut dac_active = true; } #[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { +fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }