Compare commits
No commits in common. "main" and "entomologist-data" have entirely different histories.
main
...
entomologi
92 changed files with 147 additions and 3871 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
*.ignore
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
|
|
@ -1,12 +0,0 @@
|
|||
[submodule "ch32v-insert-coin/ext/ch32-hal"]
|
||||
path = ch32v-insert-coin/ext/ch32-hal
|
||||
url = https://github.com/sigil-03/ch32-hal.git
|
||||
[submodule "ch32v-insert-coin/ext/qingke"]
|
||||
path = ch32v-insert-coin/ext/qingke
|
||||
url = https://github.com/ch32-rs/qingke.git
|
||||
[submodule "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
|
||||
[submodule "ch32v-insert-coin/ext/wavetable-synth"]
|
||||
path = ch32v-insert-coin/ext/wavetable-synth
|
||||
url = https://git.glyphs.tech/sigil-03/wavetable-synth.git
|
||||
1
02175f27333ffe6b0c61218fae3142b6/description
Normal file
1
02175f27333ffe6b0c61218fae3142b6/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
enter deep sleep after long button press
|
||||
1
02175f27333ffe6b0c61218fae3142b6/tags
Normal file
1
02175f27333ffe6b0c61218fae3142b6/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
|
|
@ -0,0 +1 @@
|
|||
modified blinky.rs to have 3 heartbeat blinks, was able to flash and observe the update.
|
||||
1
0acf72abc9e4641d0683e1bb5f2cf829/description
Normal file
1
0acf72abc9e4641d0683e1bb5f2cf829/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
blink an LED
|
||||
1
0acf72abc9e4641d0683e1bb5f2cf829/state
Normal file
1
0acf72abc9e4641d0683e1bb5f2cf829/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
done
|
||||
1
0acf72abc9e4641d0683e1bb5f2cf829/tags
Normal file
1
0acf72abc9e4641d0683e1bb5f2cf829/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
phase-0
|
||||
|
|
@ -0,0 +1 @@
|
|||
flashing successful using wch linkE programmer
|
||||
1
1892880782c8c31990d591c7b05fa6c3/description
Normal file
1
1892880782c8c31990d591c7b05fa6c3/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
flash CH32V board
|
||||
1
1892880782c8c31990d591c7b05fa6c3/state
Normal file
1
1892880782c8c31990d591c7b05fa6c3/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
done
|
||||
1
1892880782c8c31990d591c7b05fa6c3/tags
Normal file
1
1892880782c8c31990d591c7b05fa6c3/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
phase-0
|
||||
4
2207e89d7201efbdceadd1c528d95341/description
Normal file
4
2207e89d7201efbdceadd1c528d95341/description
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
adpcm decoder
|
||||
|
||||
REQUIREMENTS:
|
||||
* write a module which takes as input adpcm data and outputs amplitudes
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
had some pretty good progress tonight. got the board, soldered headers on, got it breadboarded, and got started trying to compile for the riscv core, and get the device flashed.
|
||||
|
||||
there's good HAL support, actually, which is awesome:
|
||||
[ch32-hal](https://github.com/ch32-rs/ch32-hal)
|
||||
|
||||
i was able to eventually get it compiling, however flashing was a different story.
|
||||
|
||||
apparently these chips don't have a 'standard' SWD implementation, and instead have some proprietary stuff from WCH which is annoying.
|
||||
|
||||
there was this project, which has been inactive for 2 years:
|
||||
[picorvd](https://github.com/aappleby/picorvd)
|
||||
|
||||
it took a decent amount of work to get that project running - had to fix a bunch of broken dependencies, CMAKE version issues causing CMAKE to exit (protip: `export CMAKE_POLICY_VERSION_MINIMUM=3.5` from [stackoverflow](https://stackoverflow.com/questions/79534856/cannot-build-cmake-project-because-compatibility-with-cmake-3-5-has-been-remo).
|
||||
|
||||
once that was all fixed, and i had soldered headers on / flashed a pico - i then had to get the riscv64 version of GDB running, and talking to the pico. GDB did seem to have trouble with the probe sometimes, but i was able to (i think) flash a program to one of the boards.... and it bricked.
|
||||
|
||||
immediately i saw a bunch of activity on the SWD line, and the LED stopped blinking.
|
||||
|
||||
what concerned me is that the waveform did not go away after a long period of time. i did determine that the pico was the one driving the line (on the ch32v it's SWIO, so because there were debug prints in the code, i thought it might have been from that).
|
||||
|
||||
ultimately i think i'm going to just have to use the WCH Link(E?)... which is only $7 or so, but it does mean i'm going to have to wait for it...
|
||||
|
|
@ -0,0 +1 @@
|
|||
this is done - required getting a wch-linkE programmer, which was able to interface with probe-rs and program the ch32v003 chip.
|
||||
1
2ff2ce2cae3914fbb8382372498abdd5/description
Normal file
1
2ff2ce2cae3914fbb8382372498abdd5/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
set up toolchain for CH32V development
|
||||
1
2ff2ce2cae3914fbb8382372498abdd5/state
Normal file
1
2ff2ce2cae3914fbb8382372498abdd5/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
done
|
||||
1
2ff2ce2cae3914fbb8382372498abdd5/tags
Normal file
1
2ff2ce2cae3914fbb8382372498abdd5/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
phase-0
|
||||
1
3eb0b69b70c8853f9f533c6daa6acd1f/description
Normal file
1
3eb0b69b70c8853f9f533c6daa6acd1f/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
ADC battery voltage reading
|
||||
1
3eb0b69b70c8853f9f533c6daa6acd1f/tags
Normal file
1
3eb0b69b70c8853f9f533c6daa6acd1f/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
7
49260fa4a2467b55f7b4d197825683a2/description
Normal file
7
49260fa4a2467b55f7b4d197825683a2/description
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
gpio inputs
|
||||
|
||||
4x GPIO inputs
|
||||
|
||||
* 1-2 of these wake from sleep interrupt
|
||||
* 1 for button press
|
||||
* 1 for coin detect
|
||||
1
49260fa4a2467b55f7b4d197825683a2/tags
Normal file
1
49260fa4a2467b55f7b4d197825683a2/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
1
4b5cce76398043576a0f9a6e29492127/assignee
Normal file
1
4b5cce76398043576a0f9a6e29492127/assignee
Normal file
|
|
@ -0,0 +1 @@
|
|||
sigil-03
|
||||
6
4b5cce76398043576a0f9a6e29492127/description
Normal file
6
4b5cce76398043576a0f9a6e29492127/description
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
phase 0: bootstrap
|
||||
|
||||
REQUIREMENTS:
|
||||
* set up toolchain for CH32V development
|
||||
* flash CH32V board
|
||||
* blink an LED
|
||||
1
4b5cce76398043576a0f9a6e29492127/state
Normal file
1
4b5cce76398043576a0f9a6e29492127/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
done
|
||||
1
4b5cce76398043576a0f9a6e29492127/tags
Normal file
1
4b5cce76398043576a0f9a6e29492127/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
phase-0
|
||||
1
777077169fce2ffba439b0af82e0f574/assignee
Normal file
1
777077169fce2ffba439b0af82e0f574/assignee
Normal file
|
|
@ -0,0 +1 @@
|
|||
sigil-03
|
||||
41
777077169fce2ffba439b0af82e0f574/description
Normal file
41
777077169fce2ffba439b0af82e0f574/description
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
insert-coin
|
||||
|
||||
# HW
|
||||
CHV32V003 or STM32F0 variant
|
||||
|
||||
LM386/SC8002B amplifier
|
||||
* HW PWM filtering for audio
|
||||
|
||||
|
||||
|
||||
# FW FEATURES
|
||||
## DEEP SLEEP
|
||||
* wake from deep sleep on rising edge
|
||||
* deep sleep when battery voltage is low
|
||||
* deep sleep on long button press
|
||||
* deep sleep after some long period of time
|
||||
* does not need to be accurate
|
||||
* time domain approx. several hours
|
||||
|
||||
## BATTERY VOLTAGE MONITORING
|
||||
* ADC battery voltage reading
|
||||
|
||||
|
||||
|
||||
## GPIO INPUT
|
||||
* 4x switch input
|
||||
* one for button press
|
||||
* one for coin detect
|
||||
|
||||
## LED DRIVER
|
||||
* 3 channels of LEDs
|
||||
* need PWM-ish dimming but can be rough (bit-banged)
|
||||
|
||||
## AUDIO
|
||||
* store audio in MCU flash memory
|
||||
* 10 audio files with total of 3-5 seconds of audio time
|
||||
* output using PWM channels, will filter in HW
|
||||
|
||||
|
||||
# REFERENCE
|
||||
[spec](https://docs.google.com/document/d/1WP9aMegpzNuwF81Cxm263mibuAER9fZ7Ktj7NuHr9Rs)
|
||||
1
777077169fce2ffba439b0af82e0f574/state
Normal file
1
777077169fce2ffba439b0af82e0f574/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
inprogress
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
this is basically done, there's a PWM dac that can read DPCM data and output it via PWM.
|
||||
|
||||
additionally, there's an encoder which can take a raw u8 encoded PCM file and turn it into a 4-bit DPCM file
|
||||
5
8b98f887e6f027890908edf066debce1/description
Normal file
5
8b98f887e6f027890908edf066debce1/description
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
audio player
|
||||
|
||||
requirements:
|
||||
* DAC using PWM output
|
||||
* read DPCM data and export to DAC
|
||||
1
8b98f887e6f027890908edf066debce1/tags
Normal file
1
8b98f887e6f027890908edf066debce1/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
1
95dbcb73dd29dafe84621252289a4179/description
Normal file
1
95dbcb73dd29dafe84621252289a4179/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
wake from deep sleep on rising edge
|
||||
1
95dbcb73dd29dafe84621252289a4179/tags
Normal file
1
95dbcb73dd29dafe84621252289a4179/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
This branch is used by entomologist to track issues.
|
||||
1
a05a70c9994ab6c8e3f9c69c7d536952/description
Normal file
1
a05a70c9994ab6c8e3f9c69c7d536952/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
enter deep sleep after inactivity timeout
|
||||
1
a05a70c9994ab6c8e3f9c69c7d536952/tags
Normal file
1
a05a70c9994ab6c8e3f9c69c7d536952/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
5
b0682a96fb4d2cb7c6d06141b5d36849/description
Normal file
5
b0682a96fb4d2cb7c6d06141b5d36849/description
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
LED control
|
||||
|
||||
3 channels of LEDs
|
||||
|
||||
* need PWM-ish dimming, but can be bit-banged / slow
|
||||
1
b0682a96fb4d2cb7c6d06141b5d36849/tags
Normal file
1
b0682a96fb4d2cb7c6d06141b5d36849/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
4
c6c969915b74005bc0b7f0c4b182243e/description
Normal file
4
c6c969915b74005bc0b7f0c4b182243e/description
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pwm dac
|
||||
|
||||
REQUIREMENTS:
|
||||
* create an interface that accepts an input amplitude and outputs a PWM signal
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
[build]
|
||||
target = "riscv32ec-unknown-none-elf.json"
|
||||
#target = "riscv32i-unknown-none-elf"
|
||||
|
||||
[target.'cfg(all(target_arch = "riscv32", target_os = "none"))']
|
||||
# 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 --enable-sdi-print --watch-serial"
|
||||
|
||||
# Flash and debug chip with probe-rs. https://probe.rs/
|
||||
# runner = "probe-rs run --chip ch32v003"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core"]
|
||||
# build-std = ["core", "compiler_builtins"]
|
||||
# build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
[target."riscv32ec-unknown-none-elf"]
|
||||
rustflags = ["-C", "link-arg=-Tlink.x"]
|
||||
|
||||
#[target."riscv32i-unknown-none-elf"]
|
||||
#rustflags = [
|
||||
## "-C", "link-arg=-Tlink.x",
|
||||
#]
|
||||
1
ch32v-insert-coin/.gitignore
vendored
1
ch32v-insert-coin/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
target
|
||||
633
ch32v-insert-coin/Cargo.lock
generated
633
ch32v-insert-coin/Cargo.lock
generated
|
|
@ -1,633 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adpcm-pwm-dac"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "ch32-hal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ch32-metapac",
|
||||
"critical-section",
|
||||
"embassy-futures",
|
||||
"embassy-hal-internal",
|
||||
"embassy-sync",
|
||||
"embassy-time",
|
||||
"embassy-time-driver",
|
||||
"embassy-time-queue-utils",
|
||||
"embassy-usb-driver",
|
||||
"embedded-hal 0.2.7",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"embedded-hal-nb",
|
||||
"futures",
|
||||
"nb 1.1.0",
|
||||
"proc-macro2",
|
||||
"qingke 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qingke-rt",
|
||||
"quote",
|
||||
"rand_core",
|
||||
"sdio-host",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ch32-metapac"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ch32-rs/ch32-metapac?rev=b1cbc7a98e43af3fd3170821654784e2c01cb26b#b1cbc7a98e43af3fd3170821654784e2c01cb26b"
|
||||
dependencies = [
|
||||
"riscv 0.11.1",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ch32v-insert-coin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"adpcm-pwm-dac",
|
||||
"ch32-hal",
|
||||
"critical-section",
|
||||
"embassy-executor",
|
||||
"embedded-hal 1.0.0",
|
||||
"panic-halt",
|
||||
"qingke 0.5.0",
|
||||
"qingke-rt",
|
||||
"wavetable-synth",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-executor"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"document-features",
|
||||
"embassy-executor-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-executor-macros"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-futures"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067"
|
||||
|
||||
[[package]]
|
||||
name = "embassy-hal-internal"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef3bac31ec146321248a169e9c7b5799f1e0b3829c7a9b324cb4600a7438f59"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-sync"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
"embedded-io-async",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"heapless",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
"document-features",
|
||||
"embassy-time-driver",
|
||||
"embedded-hal 0.2.7",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time-driver"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time-queue-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83"
|
||||
dependencies = [
|
||||
"embassy-executor",
|
||||
"heapless",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-usb-driver"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76"
|
||||
dependencies = [
|
||||
"embedded-io-async",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal-async"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
||||
dependencies = [
|
||||
"embedded-hal 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal-nb"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
|
||||
dependencies = [
|
||||
"embedded-hal 1.0.0",
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io-async"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
|
||||
dependencies = [
|
||||
"embedded-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||
dependencies = [
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qingke"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"riscv 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qingke"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0230c5310b68c08a3cf8b59fbeec3e9d8e352bc6500f62cbaf9c677f42c8dfc"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"critical-section",
|
||||
"riscv 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qingke-rt"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b955c60adac70c6d40205b1dbe9f57e1151d06aa842069cdbaef7bc07ad283fd"
|
||||
dependencies = [
|
||||
"qingke 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qingke-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qingke-rt-macros"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f2ed46d18953ea5765ab26a07d1f092dffac2da1b4830c4397e02c3cec08501"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"embedded-hal 1.0.0",
|
||||
"paste",
|
||||
"riscv-macros",
|
||||
"riscv-pac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "riscv-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "riscv-pac"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436"
|
||||
|
||||
[[package]]
|
||||
name = "sdio-host"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93c025f9cfe4c388c328ece47d11a54a823da3b5ad0370b22d95ad47137f85a"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wavetable-synth"
|
||||
version = "0.1.0"
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
[package]
|
||||
name = "ch32v-insert-coin"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
enable_print = []
|
||||
|
||||
[dependencies]
|
||||
ch32-hal = { path = "ext/ch32-hal/", features = [
|
||||
"ch32v003f4u6",
|
||||
"memory-x",
|
||||
"embassy",
|
||||
"time-driver-tim2",
|
||||
"rt",
|
||||
] }
|
||||
|
||||
embassy-executor = { version = "0.7.0", features = [
|
||||
"arch-spin",
|
||||
"executor-thread",
|
||||
"task-arena-size-128", # or better use nightly, but fails on recent Rust versions
|
||||
] }
|
||||
|
||||
panic-halt = "1.0"
|
||||
|
||||
embedded-hal = "1.0.0"
|
||||
|
||||
qingke-rt = { version = "*", features = ["highcode"] }
|
||||
|
||||
qingke = {path = "ext/qingke"}
|
||||
|
||||
adpcm-pwm-dac = { path = "ext/adpcm-pwm-dac/" }
|
||||
|
||||
wavetable-synth = { path = "ext/wavetable-synth" }
|
||||
|
||||
critical-section = { version = "1.2.0" }
|
||||
|
||||
[profile.release]
|
||||
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
|
||||
lto = true
|
||||
opt-level = "s" # Optimize for size.
|
||||
|
||||
[profile.dev]
|
||||
overflow-checks = false
|
||||
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.
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,
|
||||
]
|
||||
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
cargo +nightly run --release
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 99ce71e8d03e382b51732db7d0771349c51c7f48
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 4f11d68e62dcb0e7098eecf357168724a8322d80
|
||||
|
|
@ -1,652 +0,0 @@
|
|||
From 7b086336e3820714c564aac13dc44fbaf7f5bc17 Mon Sep 17 00:00:00 2001
|
||||
From: mindshub <info@mindshub.com>
|
||||
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<T: GpioPin>(
|
||||
- pin: impl Peripheral<P = T> + 'd,
|
||||
- ch: impl Peripheral<P = T::ExtiChannel> + '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<Self::Output> {
|
||||
- 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<T: GpioPin>(
|
||||
+ pin: impl Peripheral<P = T> + 'd,
|
||||
+ ch: impl Peripheral<P = T::ExtiChannel> + '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<Self::Output> {
|
||||
+ 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);
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 2443891811d7351d62e78085bcf60fa78c063c08
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 30033e1438c25aed4ea506cdbd69cc4ceffa0b4e
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
DIR=${PWD}
|
||||
mkdir ${PWD}/tmp
|
||||
cd tmp
|
||||
git clone ssh://git@git.glyphs.tech:222/taproot-tech/docker-devtools.git
|
||||
cd docker-devtools/ch32
|
||||
ls
|
||||
./build.sh
|
||||
cd $DIR
|
||||
rm -rf tmp
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
podman run --privileged -it --rm -v "$PWD":/usr/src/app -w /usr/src/app ch32-rust:latest /bin/bash
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"arch": "riscv32",
|
||||
"atomic-cas": false,
|
||||
"cpu": "generic-rv32",
|
||||
"crt-objects-fallback": "false",
|
||||
"data-layout": "e-m:e-p:32:32-i64:64-n32-S32",
|
||||
"eh-frame-header": false,
|
||||
"emit-debug-gdb-scripts": false,
|
||||
"features": "+e,+c,+forced-atomics",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "gnu-lld",
|
||||
"llvm-target": "riscv32",
|
||||
"llvm-abiname": "ilp32e",
|
||||
"max-atomic-width": 32,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": 32
|
||||
}
|
||||
|
|
@ -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,183 +0,0 @@
|
|||
use ch32_hal::timer::simple_pwm::SimplePwm;
|
||||
use ch32_hal::timer::Channel;
|
||||
use ch32_hal::timer::GeneralInstance16bit;
|
||||
|
||||
use crate::insert_coin::services::{DacService, LedService, Service, TickService, TickServiceData};
|
||||
|
||||
// static mut led0_index: usize = 0;
|
||||
// static LED0: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||
|
||||
// static mut LED1_INDEX: usize = 0;
|
||||
// static LED1_DCS: [u8; 5] = [0u8, 25u8, 50u8, 75u8, 100u8];
|
||||
|
||||
pub struct SimplePwmCore<'d, T: GeneralInstance16bit> {
|
||||
pub pwm: core::cell::RefCell<SimplePwm<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: GeneralInstance16bit> SimplePwmCore<'d, T> {
|
||||
pub fn new(pwm: SimplePwm<'d, T>) -> Self {
|
||||
Self {
|
||||
pwm: core::cell::RefCell::new(pwm),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_handle(&'d self, ch: Channel) -> SimplePwmHandle<'d, '_, T> {
|
||||
// SimplePwmHandle {
|
||||
// core: &self,
|
||||
// channel: ch,
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn write_amplitude(&self, ch: Channel, amplitude: u8) {
|
||||
if !self.pwm.borrow().is_enabled(ch) {
|
||||
self.pwm.borrow_mut().enable(ch);
|
||||
}
|
||||
let max_duty = self.pwm.borrow().get_max_duty();
|
||||
let dc = amplitude as u32 * max_duty / 100;
|
||||
self.pwm.borrow_mut().set_duty(ch, dc);
|
||||
}
|
||||
|
||||
pub fn disable(&self, ch: Channel) {
|
||||
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 tick_rate_hz: usize,
|
||||
}
|
||||
impl CoreConfig {
|
||||
pub fn new(tick_rate_hz: usize) -> Self {
|
||||
Self { tick_rate_hz }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Core {
|
||||
_tick: usize,
|
||||
active: bool,
|
||||
}
|
||||
|
||||
pub struct InsertCoin<'a, T: GeneralInstance16bit> {
|
||||
core: Core,
|
||||
pub config: CoreConfig,
|
||||
pwm_core: SimplePwmCore<'a, T>,
|
||||
pub led0: LedService,
|
||||
pub led1: LedService,
|
||||
// led2: LedService,
|
||||
pub dac: DacService<'a>,
|
||||
}
|
||||
|
||||
impl<'a, T: GeneralInstance16bit> InsertCoin<'a, T> {
|
||||
pub fn new(config: CoreConfig, pwm_core: SimplePwmCore<'a, T>) -> Self {
|
||||
// LED0 servicer setup
|
||||
let led0 = LedService::new(ch32_hal::timer::Channel::Ch3);
|
||||
|
||||
// LED1 servicer setup
|
||||
let led1 = LedService::new(ch32_hal::timer::Channel::Ch1);
|
||||
|
||||
// DAC servicer setup
|
||||
let dac_sample_rate_hz = 4000;
|
||||
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);
|
||||
|
||||
Self {
|
||||
config,
|
||||
core: Default::default(),
|
||||
pwm_core,
|
||||
led0,
|
||||
led1,
|
||||
// led2,
|
||||
dac,
|
||||
}
|
||||
}
|
||||
|
||||
/// takes self reference and runs
|
||||
pub fn service(&mut self) {
|
||||
if self.is_active() {
|
||||
self.dac.tick();
|
||||
|
||||
if self.led0.need_service() {
|
||||
self.pwm_core
|
||||
.write_amplitude(self.led0.channel, self.led0.amplitude);
|
||||
self.led0.service();
|
||||
}
|
||||
|
||||
if self.led1.need_service() {
|
||||
self.pwm_core
|
||||
.write_amplitude(self.led1.channel, self.led1.amplitude);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /// 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 led1_index = 0;
|
||||
// let led1_dcs = [0u8, 25u8, 50u8, 75u8, 100u8];
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
// }
|
||||
|
||||
// delay.delay_us(tick_interval_us as u32);
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.core.active
|
||||
}
|
||||
|
||||
pub fn set_active(&mut self, active: bool) {
|
||||
self.core.active = active;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
mod insert_coin;
|
||||
mod services;
|
||||
|
||||
pub use services::{DacService, LedService, Service, TickTimerService};
|
||||
|
||||
pub use insert_coin::{CoreConfig, InsertCoin, SimplePwmCore};
|
||||
pub use services::{TickService, TickServiceData};
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
use crate::insert_coin::services::{TickService, TickServiceData};
|
||||
|
||||
use adpcm_pwm_dac::dac::DpcmDecoder;
|
||||
use ch32_hal::timer::Channel;
|
||||
|
||||
pub struct DacService<'a> {
|
||||
service_data: core::cell::RefCell<TickServiceData>,
|
||||
dpcm_decoder: core::cell::RefCell<DpcmDecoder<'a>>,
|
||||
amplitude: core::cell::RefCell<usize>,
|
||||
pub channel: Channel,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> DacService<'a> {
|
||||
pub fn new(channel: Channel, service_data: TickServiceData) -> Self {
|
||||
Self {
|
||||
service_data: core::cell::RefCell::new(service_data),
|
||||
dpcm_decoder: core::cell::RefCell::new(DpcmDecoder::new()),
|
||||
amplitude: core::cell::RefCell::new(0),
|
||||
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]) {
|
||||
self.dpcm_decoder.borrow_mut().load_data(data);
|
||||
self.dpcm_decoder.borrow_mut().seek_to_sample(0);
|
||||
}
|
||||
|
||||
pub fn set_amplitude(&self, amplitude: usize) {
|
||||
self.amplitude.replace(amplitude);
|
||||
}
|
||||
pub fn get_amplitude(&self) -> usize {
|
||||
*self.amplitude.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TickService for DacService<'a> {
|
||||
fn tick(&self) {
|
||||
if self.enabled {
|
||||
let mut tc = self.service_data.borrow_mut();
|
||||
tc.ticks_remaining = tc.ticks_remaining.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn need_service(&self) -> bool {
|
||||
self.enabled && self.service_data.borrow().ticks_remaining == 0
|
||||
}
|
||||
|
||||
fn service(&self) {
|
||||
let mut tc = self.service_data.borrow_mut();
|
||||
tc.ticks_remaining = tc.ticks_per_service;
|
||||
if (self.dpcm_decoder.borrow().is_done()) {
|
||||
self.set_amplitude(0);
|
||||
} else {
|
||||
self.set_amplitude(self.dpcm_decoder.borrow_mut().output_next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
use ch32_hal::timer::Channel;
|
||||
|
||||
use crate::insert_coin::services::Service;
|
||||
|
||||
pub struct LedService {
|
||||
// need_service: core::cell::RefCell<bool>,
|
||||
need_service: bool,
|
||||
pub channel: Channel,
|
||||
pub amplitude: u8,
|
||||
}
|
||||
|
||||
impl LedService {
|
||||
pub fn new(channel: Channel) -> Self {
|
||||
Self {
|
||||
// service_data: core::cell::RefCell::new(service_data),
|
||||
need_service: false,
|
||||
channel,
|
||||
amplitude: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_amplitude(&mut self, amplitude: u8) {
|
||||
self.amplitude = amplitude;
|
||||
self.need_service = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for LedService {
|
||||
fn need_service(&self) -> bool {
|
||||
self.need_service
|
||||
}
|
||||
|
||||
fn service(&mut self) {
|
||||
self.need_service = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
mod services;
|
||||
pub use services::{TickService, TickServiceData, Service};
|
||||
|
||||
mod led;
|
||||
pub use led::LedService;
|
||||
|
||||
mod dac;
|
||||
pub use dac::DacService;
|
||||
|
||||
mod tick_timer;
|
||||
pub use tick_timer::TickTimerService;
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
pub struct TickServiceData {
|
||||
pub ticks_per_service: usize,
|
||||
pub ticks_remaining: usize,
|
||||
}
|
||||
|
||||
impl TickServiceData {
|
||||
pub fn new(ticks_per_service: usize) -> Self {
|
||||
Self {
|
||||
ticks_per_service,
|
||||
ticks_remaining: ticks_per_service,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TickService {
|
||||
/// indicate to the service that a tick has occurred
|
||||
fn tick(&self);
|
||||
|
||||
/// return true if service needs the service() routine run
|
||||
fn need_service(&self) -> bool;
|
||||
|
||||
/// service routine - handle what needs to be done (non blocking) when
|
||||
/// the service needs to be serviced here
|
||||
fn service(&self);
|
||||
}
|
||||
|
||||
|
||||
pub trait Service {
|
||||
fn need_service(&self) -> bool;
|
||||
fn service(&mut self);
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use crate::insert_coin::services::{TickService, TickServiceData};
|
||||
|
||||
pub struct TickTimerService {
|
||||
service_data: core::cell::RefCell<TickServiceData>,
|
||||
_auto_reset: bool,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
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,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
let mut sd = self.service_data.borrow_mut();
|
||||
sd.ticks_remaining = sd.ticks_per_service;
|
||||
self.enabled = false;
|
||||
}
|
||||
|
||||
pub fn enable(&mut self, enable: bool) {
|
||||
self.enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
impl TickService for TickTimerService {
|
||||
fn tick(&self) {
|
||||
if self.enabled {
|
||||
let mut tc = self.service_data.borrow_mut();
|
||||
tc.ticks_remaining = tc.ticks_remaining.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn need_service(&self) -> bool {
|
||||
self.enabled && self.service_data.borrow().ticks_remaining == 0
|
||||
}
|
||||
|
||||
fn service(&self) {
|
||||
let mut tc = self.service_data.borrow_mut();
|
||||
tc.ticks_remaining = tc.ticks_per_service;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,648 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
// app stuff
|
||||
mod insert_coin;
|
||||
|
||||
// system stuff
|
||||
mod system;
|
||||
|
||||
mod debounced_gpio;
|
||||
use debounced_gpio::DebouncedGPIO;
|
||||
|
||||
// synthesizer :3
|
||||
mod synthesizer;
|
||||
use synthesizer::SynthesizerService;
|
||||
|
||||
// sequences
|
||||
mod sequences;
|
||||
use sequences::SEQUENCE_LIST;
|
||||
|
||||
mod app;
|
||||
use app::{
|
||||
sequencer::BasicSequence, App, Config, Interfaces, Sequences, Services, State, TimerConfig,
|
||||
};
|
||||
|
||||
use ch32_hal::{adc::AdcChannel, interrupt::typelevel::Handler, timer::low_level::OutputPolarity};
|
||||
use insert_coin::{CoreConfig, DacService, InsertCoin, LedService, SimplePwmCore};
|
||||
|
||||
use ch32_hal as hal;
|
||||
use hal::bind_interrupts;
|
||||
use hal::delay::Delay;
|
||||
use hal::gpio::{AnyPin, Input, Level, Output, OutputOpenDrain, Pin, Pull};
|
||||
use hal::time::Hertz;
|
||||
use hal::timer::low_level::CountingMode;
|
||||
use hal::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||
|
||||
use hal::println;
|
||||
|
||||
use qingke::riscv;
|
||||
|
||||
use crate::app::sequencer::{DynamicSequence, SequenceEntry};
|
||||
|
||||
static LED0_SEQ: [u8; 8] = [0u8, 25u8, 50u8, 75u8, 100u8, 75u8, 50u8, 25u8];
|
||||
|
||||
pub struct 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,
|
||||
}
|
||||
impl Flag {
|
||||
pub fn active(&self) -> bool {
|
||||
unsafe { core::ptr::read_volatile(&raw const self.value as *const bool) }
|
||||
}
|
||||
pub fn set(&mut self) {
|
||||
unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, true) }
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
unsafe { core::ptr::write_volatile(&raw mut self.value as *mut bool, false) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InputFlags {
|
||||
sense_coin_flag: Flag,
|
||||
main_btn_flag: Flag,
|
||||
volume_btn_flag: bool,
|
||||
light_ctrl_btn_flag: bool,
|
||||
systick_flag: Flag,
|
||||
}
|
||||
|
||||
impl Default for InputFlags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sense_coin_flag: Flag { value: false },
|
||||
main_btn_flag: Flag { value: false },
|
||||
volume_btn_flag: false,
|
||||
light_ctrl_btn_flag: false,
|
||||
systick_flag: Flag { value: false },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut INPUT_FLAGS: InputFlags = InputFlags {
|
||||
sense_coin_flag: Flag { value: false },
|
||||
main_btn_flag: Flag { value: false },
|
||||
volume_btn_flag: false,
|
||||
light_ctrl_btn_flag: false,
|
||||
systick_flag: Flag { value: false },
|
||||
};
|
||||
|
||||
struct Test {}
|
||||
impl Handler<hal::interrupt::typelevel::EXTI7_0> for Test {
|
||||
unsafe fn on_interrupt() {
|
||||
// #[cfg(feature = "enable_print")]
|
||||
// println!("on_interrupt()");
|
||||
critical_section::with(|_| unsafe {
|
||||
let flags = system::clear_interrupt(2, 6);
|
||||
if flags[0] {
|
||||
// safe because single-threaded
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.sense_coin_flag.set();
|
||||
}
|
||||
if flags[1] {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.main_btn_flag.set();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[qingke_rt::interrupt(core)]
|
||||
fn SysTick() {
|
||||
let r = &ch32_hal::pac::SYSTICK;
|
||||
|
||||
// Clear interrupt flag
|
||||
r.sr().write(|w| w.set_cntif(false));
|
||||
|
||||
unsafe {
|
||||
// safe because single-threaded
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.systick_flag.set();
|
||||
}
|
||||
}
|
||||
|
||||
fn systick_init(tick_freq_hz: usize) {
|
||||
let r = &ch32_hal::pac::SYSTICK;
|
||||
|
||||
// Calculate counts per millisecond using HCLK/8 as clock source
|
||||
// HCLK/8 = 48MHz/8 = 6MHz
|
||||
// For tick_freq_hz interrupt: 6MHz / tick_freq_hz
|
||||
let systick_per_tick = (48_000_000 / 8 / tick_freq_hz) as u32;
|
||||
|
||||
// Reset SysTick
|
||||
r.ctlr().write(|w| {
|
||||
// Start with everything disabled
|
||||
});
|
||||
|
||||
// Set compare register and reset counter
|
||||
r.cmp().write_value(systick_per_tick - 1);
|
||||
r.cnt().write_value(0);
|
||||
|
||||
// Clear interrupt flag
|
||||
r.sr().write(|w| w.set_cntif(false));
|
||||
|
||||
// Configure and start SysTick
|
||||
r.ctlr().write(|w| {
|
||||
w.set_ste(true); // Enable counter
|
||||
w.set_stie(true); // Enable interrupt
|
||||
w.set_stre(true); // Auto reload enable
|
||||
w.set_stclk(ch32_hal::pac::systick::vals::Stclk::HCLK_DIV8); // HCLK/8 clock source
|
||||
});
|
||||
}
|
||||
fn systick_stop() {
|
||||
let r = &ch32_hal::pac::SYSTICK;
|
||||
// Reset SysTick
|
||||
r.ctlr().write(|w| {
|
||||
// Start with everything disabled
|
||||
});
|
||||
}
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
EXTI7_0 => Test;
|
||||
});
|
||||
|
||||
// TODO: remove
|
||||
use app::settings::Settings;
|
||||
use insert_coin::TickTimerService;
|
||||
use insert_coin::{TickService, TickServiceData};
|
||||
|
||||
fn app_main(mut p: hal::Peripherals, app_settings: Settings) -> Settings {
|
||||
// initialize ADC core first, and exit if battery is too low
|
||||
let mut adc = hal::adc::Adc::new(p.ADC1, Default::default());
|
||||
let mut batt_monitor_pin = p.PD4;
|
||||
let mut adc_core = AdcCore::new(adc, batt_monitor_pin);
|
||||
|
||||
let mut usb_detect_pin = p.PD5;
|
||||
let usb_detect_input = Input::new(usb_detect_pin, Pull::Up);
|
||||
let usb = Usb::new(usb_detect_input);
|
||||
|
||||
let bv = adc_core.get_battery_voltage();
|
||||
|
||||
// if we don't have USB power, and the batt ADC reads under 421, don't wake
|
||||
if !usb.powered() && bv < 421 {
|
||||
adc_core.shutdown();
|
||||
return app_settings;
|
||||
}
|
||||
// === output setup ===
|
||||
|
||||
// LED0 output setup
|
||||
let led0_pin = PwmPin::new_ch3::<0>(p.PC3);
|
||||
let led0_ch = hal::timer::Channel::Ch3;
|
||||
|
||||
// LED1 output setup
|
||||
let led1_pin = PwmPin::new_ch1::<0>(p.PD2);
|
||||
let led1_ch = hal::timer::Channel::Ch1;
|
||||
|
||||
// DAC output setup
|
||||
let dac_pin = PwmPin::new_ch4::<0>(p.PC4);
|
||||
// let dac_ch = hal::timer::Channel::Ch4;
|
||||
|
||||
// PWM timer setup
|
||||
let mut pwm = SimplePwm::new(
|
||||
p.TIM1,
|
||||
Some(led1_pin),
|
||||
// Some(led2_pin),
|
||||
None,
|
||||
Some(led0_pin),
|
||||
Some(dac_pin),
|
||||
Hertz::khz(200),
|
||||
CountingMode::default(),
|
||||
);
|
||||
|
||||
pwm.set_polarity(led0_ch, OutputPolarity::ActiveHigh);
|
||||
pwm.set_polarity(led1_ch, OutputPolarity::ActiveLow);
|
||||
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 tick_rate_hz = 50000;
|
||||
|
||||
let core_config = CoreConfig::new(tick_rate_hz);
|
||||
|
||||
// === input setup ===
|
||||
|
||||
// adc
|
||||
// let mut adc = hal::adc::Adc::new(p.ADC1, Default::default());
|
||||
// let mut batt_monitor_pin = p.PD4;
|
||||
// let adc_core = AdcCore::new(adc, batt_monitor_pin);
|
||||
|
||||
// adc2
|
||||
// let mut usb_detect_dc = hal::adc::Adc::new(p.ADC1, Default::default());
|
||||
|
||||
// println!("ADC_PIN CHANNEL: {}", adc_pin.channel().channel());
|
||||
|
||||
// #[cfg(feature = "enable_print")]
|
||||
// println!("ADC calibration value: {}", adc_cal);
|
||||
|
||||
// definitions
|
||||
let sense_coin_pin = p.PC2;
|
||||
let main_btn_pin = p.PD6;
|
||||
let volume_btn_pin = p.PC6;
|
||||
let light_ctrl_btn_pin = p.PC7;
|
||||
let amp_en = p.PC5;
|
||||
// let extra_io_1 = p.PD0;
|
||||
// let extra_io_2 = p.PD3;
|
||||
|
||||
let mut amp_en_output = OutputOpenDrain::new(amp_en, Level::Low, Default::default());
|
||||
let amp = Amplifier::new(amp_en_output);
|
||||
|
||||
// set up interrupts
|
||||
unsafe { system::init_gpio_irq(sense_coin_pin.pin(), sense_coin_pin.port(), true, false) };
|
||||
unsafe { system::init_gpio_irq(main_btn_pin.pin(), main_btn_pin.port(), true, true) };
|
||||
|
||||
// coin debouncer (100ms)
|
||||
let mut sense_coin_input =
|
||||
DebouncedGPIO::new(sense_coin_pin.degrade(), core_config.tick_rate_hz, 20);
|
||||
// main button debouncer (100ms)
|
||||
let mut main_btn_input =
|
||||
DebouncedGPIO::new(main_btn_pin.degrade(), core_config.tick_rate_hz, 20);
|
||||
// button debouncer (100ms)
|
||||
let mut volume_btn_input =
|
||||
DebouncedGPIO::new(volume_btn_pin.degrade(), core_config.tick_rate_hz, 100);
|
||||
// button debouncer (100ms)
|
||||
let mut light_ctrl_btn_input =
|
||||
DebouncedGPIO::new(light_ctrl_btn_pin.degrade(), core_config.tick_rate_hz, 100);
|
||||
|
||||
let timer_config = TimerConfig {
|
||||
sp_timer_ms: 1000,
|
||||
lp_timer_ms: 3000,
|
||||
batt_adc_timer_ms: 1000,
|
||||
usb_adc_timer_ms: 10000,
|
||||
led0_timer_ms: 100,
|
||||
led1_timer_ms: 100,
|
||||
// 4 hours:
|
||||
// shutdown_timer_s: 1,
|
||||
shutdown_timer_s: 4 * 60 * 60 * 110 / 100,
|
||||
// led2_timer_ms: 100,
|
||||
};
|
||||
|
||||
let app_config = Config {
|
||||
system_tick_rate_hz: tick_rate_hz,
|
||||
timers: timer_config,
|
||||
};
|
||||
|
||||
// DAC servicer setup
|
||||
let dac_sample_rate_hz = 16000;
|
||||
let dac_tick_per_service = tick_rate_hz / dac_sample_rate_hz;
|
||||
let dac_service_data = TickServiceData::new(dac_tick_per_service);
|
||||
|
||||
// let coin_sound = include_bytes!("../audio/coin5.raw");
|
||||
// let coin_sound = include_bytes!("../audio/coin2.raw");
|
||||
|
||||
let sample_player = DacService::new(ch32_hal::timer::Channel::Ch4, dac_service_data);
|
||||
// sample_player.load_data(coin_sound);
|
||||
|
||||
let sequencer = app::sequencer::DynamicSequence::new(&SEQUENCE_LIST[0].0, tick_rate_hz);
|
||||
|
||||
let app_services = Services {
|
||||
led0: LedService::new(led0_ch),
|
||||
led1: LedService::new(led1_ch),
|
||||
// led2: LedService::new(led2_ch),
|
||||
synth0: SynthesizerService::new(tick_rate_hz),
|
||||
sample_player,
|
||||
sequencer,
|
||||
};
|
||||
|
||||
let app_sequences = Sequences {
|
||||
led0: BasicSequence::new(&LED0_SEQ),
|
||||
led1: BasicSequence::new(&LED0_SEQ),
|
||||
// led2: BasicSequence::new(&LED0_SEQ),
|
||||
audio: &SEQUENCE_LIST,
|
||||
};
|
||||
|
||||
let app_interfaces = Interfaces {
|
||||
pwm_core,
|
||||
adc_core,
|
||||
amp,
|
||||
usb,
|
||||
};
|
||||
|
||||
let mut app = App::new(
|
||||
app_config,
|
||||
app_services,
|
||||
app_sequences,
|
||||
app_interfaces,
|
||||
app_settings,
|
||||
);
|
||||
|
||||
let need_sound = unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
if INPUT_FLAGS.main_btn_flag.active() {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.main_btn_flag.clear();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
// init systick
|
||||
systick_init(tick_rate_hz);
|
||||
|
||||
// set up interrupts
|
||||
unsafe {
|
||||
use hal::pac::Interrupt;
|
||||
use qingke::interrupt::Priority;
|
||||
use qingke_rt::CoreInterrupt;
|
||||
|
||||
system::clear_interrupt(2, 6);
|
||||
|
||||
qingke::pfic::set_priority(CoreInterrupt::SysTick as u8, Priority::P15 as u8);
|
||||
|
||||
qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8);
|
||||
qingke::pfic::enable_interrupt(CoreInterrupt::SysTick as u8);
|
||||
}
|
||||
|
||||
// MAIN APPLICATION
|
||||
// process
|
||||
// -depress big button (insert coin button) and it wakes up and turns on led one led at a fixed brightness
|
||||
// -we will want one sound, the coin insert sound, to play when coin button is pressed. (This is when they insert a coin)
|
||||
// -We will want a different sound or potentially multiple different sounds played in a rotating fashion when someone presses the button. Probably do some led blinking as well.(This is when they depress the big insert to play button)
|
||||
// -depress the big button for approx 2s and it puts the led into low light mode. Just making it dimmer than usual.
|
||||
// -depress the big button for 5s and it goes into deep sleep, everything off with the low sleep current draw
|
||||
|
||||
// #[cfg(feature = "enable_print")]
|
||||
// println!("begin");
|
||||
let mut volume_btn_prev = volume_btn_input.is_high_immediate();
|
||||
let mut light_ctrl_btn_prev = light_ctrl_btn_input.is_high_immediate();
|
||||
|
||||
app.init();
|
||||
if need_sound {
|
||||
app.main_button_click();
|
||||
}
|
||||
loop {
|
||||
// system servicing
|
||||
|
||||
// volume edge detector
|
||||
if !volume_btn_input.active() {
|
||||
let volume_btn_curr = volume_btn_input.is_high_immediate();
|
||||
if volume_btn_prev != volume_btn_curr {
|
||||
volume_btn_input.begin();
|
||||
volume_btn_prev = volume_btn_curr;
|
||||
}
|
||||
}
|
||||
|
||||
volume_btn_input.service();
|
||||
if volume_btn_input.ready() {
|
||||
// #[cfg(feature = "enable_print")]
|
||||
// println!("volume btn value: {}", volume_btn_input.value());
|
||||
if !volume_btn_input.value() {
|
||||
app.volume_button();
|
||||
}
|
||||
volume_btn_input.reset();
|
||||
}
|
||||
|
||||
// brightness edge detector
|
||||
if !light_ctrl_btn_input.active() {
|
||||
let light_ctrl_btn_curr = light_ctrl_btn_input.is_high_immediate();
|
||||
if light_ctrl_btn_prev != light_ctrl_btn_curr {
|
||||
light_ctrl_btn_input.begin();
|
||||
light_ctrl_btn_prev = light_ctrl_btn_curr;
|
||||
}
|
||||
}
|
||||
|
||||
light_ctrl_btn_input.service();
|
||||
if light_ctrl_btn_input.ready() {
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("brightness btn value: {}", light_ctrl_btn_input.value());
|
||||
if !light_ctrl_btn_input.value() {
|
||||
app.brightness_button();
|
||||
}
|
||||
light_ctrl_btn_input.reset();
|
||||
}
|
||||
|
||||
// coin_detect interrupt
|
||||
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
if INPUT_FLAGS.sense_coin_flag.active() {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.sense_coin_flag.clear();
|
||||
sense_coin_input.begin();
|
||||
}
|
||||
sense_coin_input.service();
|
||||
if sense_coin_input.ready() {
|
||||
sense_coin_input.reset();
|
||||
app.coin_detect();
|
||||
}
|
||||
}
|
||||
|
||||
// main button handling
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
if INPUT_FLAGS.main_btn_flag.active() {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.main_btn_flag.clear();
|
||||
main_btn_input.begin();
|
||||
}
|
||||
}
|
||||
main_btn_input.service();
|
||||
if main_btn_input.ready() {
|
||||
let value = main_btn_input.value();
|
||||
main_btn_input.reset();
|
||||
// #[cfg(feature = "enable_print")]
|
||||
// println!("main button", value);
|
||||
|
||||
match value {
|
||||
true => app.main_button_press(),
|
||||
false => app.main_button_release(),
|
||||
}
|
||||
}
|
||||
|
||||
// systick tick
|
||||
if unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.systick_flag.active()
|
||||
} {
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
INPUT_FLAGS.systick_flag.clear();
|
||||
}
|
||||
// app tick
|
||||
app.tick();
|
||||
}
|
||||
|
||||
app.service();
|
||||
|
||||
match app.get_state() {
|
||||
// enter standby
|
||||
app::State::DeepSleep => {
|
||||
app.shut_down();
|
||||
return app.get_settings();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use ch32_hal::timer::low_level::{OutputCompareMode, Timer};
|
||||
use ch32_hal::timer::Channel;
|
||||
|
||||
// fn shutdown_main(p: Peripherals) {
|
||||
fn shutdown_main(p: hal::Peripherals) {
|
||||
systick_stop();
|
||||
// LED0 output setup
|
||||
let led0_pin = OutputOpenDrain::new(p.PC3, Level::Low, Default::default());
|
||||
let led1_pin = OutputOpenDrain::new(p.PD2, Level::High, Default::default());
|
||||
let led2_pin = OutputOpenDrain::new(p.PA1, Level::High, Default::default());
|
||||
let dac_pin = OutputOpenDrain::new(p.PC4, Level::Low, Default::default());
|
||||
let mut amp_pin = OutputOpenDrain::new(p.PC5, Level::Low, Default::default());
|
||||
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;
|
||||
let main_btn_pin = p.PD6;
|
||||
|
||||
unsafe { system::init_gpio_irq(sense_coin_pin.pin(), sense_coin_pin.port(), true, false) };
|
||||
unsafe { system::init_gpio_irq(main_btn_pin.pin(), main_btn_pin.port(), true, false) };
|
||||
|
||||
let sense_coin_pin = Input::new(sense_coin_pin, Pull::None);
|
||||
let main_btn_pin = Input::new(main_btn_pin, Pull::None);
|
||||
|
||||
riscv::asm::delay(1_000_000);
|
||||
|
||||
loop {
|
||||
unsafe { system::enter_standby() };
|
||||
riscv::asm::wfi();
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
if (INPUT_FLAGS.sense_coin_flag.active() || INPUT_FLAGS.main_btn_flag.active())
|
||||
// && app.should_wake()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[qingke_rt::entry]
|
||||
fn main() -> ! {
|
||||
#[cfg(feature = "enable_print")]
|
||||
hal::debug::SDIPrint::enable();
|
||||
|
||||
let mut config = hal::Config::default();
|
||||
config.rcc = hal::rcc::Config::SYSCLK_FREQ_48MHZ_HSI;
|
||||
let mut p = hal::init(config);
|
||||
|
||||
// delay to let the debugger attach
|
||||
// println!("pre");
|
||||
riscv::asm::delay(20_000_000);
|
||||
// println!("post");
|
||||
// debug_main(p);
|
||||
|
||||
let mut app_settings = Settings::default();
|
||||
|
||||
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]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
// println!("panic: {info:?}");
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
use ch32_hal as hal;
|
||||
use hal::println;
|
||||
|
||||
pub unsafe fn init_gpio_irq(pin: u8, port: u8, rising: bool, falling: bool) {
|
||||
critical_section::with(|_| {
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("init_gpio_irq {pin}:{port}");
|
||||
let exti = &hal::pac::EXTI;
|
||||
let afio = &hal::pac::AFIO;
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn clear_interrupt(coin_pin: u8, button_pin: u8) -> [bool; 2] {
|
||||
let mut output = [false, false];
|
||||
|
||||
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;
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("bits: {bits:08x}");
|
||||
|
||||
// coin_flag
|
||||
if (bits & (0x1 << coin_pin)) != 0x0 {
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("coin irq!");
|
||||
output[0] = true;
|
||||
}
|
||||
|
||||
// button_flag
|
||||
if (bits & (0x1 << button_pin)) != 0x0 {
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("main_btn irq!");
|
||||
output[1] = true;
|
||||
}
|
||||
|
||||
// Clear pending - Clears the EXTI's line pending bits.
|
||||
exti.intfr().write(|w| w.0 = bits);
|
||||
|
||||
use hal::pac::Interrupt;
|
||||
unsafe {
|
||||
qingke::pfic::unpend_interrupt(Interrupt::EXTI7_0 as u8);
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
/// enter standby (SLEEPDEEP) mode, with WFE enabled.
|
||||
/// from CH32V003 reference manual 2.3.1:
|
||||
/// entry:
|
||||
/// 1. configure core register control bit: SLEEPDEEP=1 | PDDS = 1
|
||||
/// 2. configure external interrupt register to mask all but EXTI7_0
|
||||
/// 3. execute WFI or WFE (optionally SEVONPEND) and SLEEPONEXIT
|
||||
/// * (probably WFI?)
|
||||
/// exit:
|
||||
/// 1. any interrupt/event (set in external interrupt register)
|
||||
pub unsafe fn enter_standby() {
|
||||
critical_section::with(|_| {
|
||||
use hal::pac::Interrupt;
|
||||
use qingke_rt::CoreInterrupt;
|
||||
|
||||
// configure core register control bit (SLEEPDEEP=1 | PDDS=1)
|
||||
let pfic = &hal::pac::PFIC;
|
||||
pfic.sctlr().modify(|w| {
|
||||
// we only want to wake on enabled interrupts
|
||||
w.set_sevonpend(false);
|
||||
// we want to enable deep sleep
|
||||
w.set_sleepdeep(true);
|
||||
w.set_wfitowfe(false);
|
||||
w.set_sleeponexit(false);
|
||||
});
|
||||
|
||||
// set PDDS=1:
|
||||
// get current value of PWR_CTLR
|
||||
let mut reg: u32 = 0x4000_7000;
|
||||
let mut val: u32 = unsafe { (reg as *mut u32).read_volatile() };
|
||||
// modify PDDS
|
||||
val |= 1 << 1; // PWR_CTLR[1] -> PDDS
|
||||
unsafe {
|
||||
(reg as *mut u32).write_volatile(val);
|
||||
}
|
||||
|
||||
// // disable all exti interrupts
|
||||
let exti = &hal::pac::EXTI;
|
||||
// exti.intenr().write(| w| {
|
||||
// w.0 = 0x00000000;
|
||||
// // w.set_mr(pin, true);
|
||||
// // w.set_mr(pin, true);
|
||||
// });
|
||||
|
||||
// clear all pending exti interrupts
|
||||
let bits = 0xFFFFFFFF;
|
||||
exti.intfr().write(|w| w.0 = bits);
|
||||
|
||||
// enable all exti interrupts
|
||||
let exti = &hal::pac::EXTI;
|
||||
exti.intenr().write(|w| {
|
||||
w.0 = 0x00FFFFFF;
|
||||
// w.set_mr(pin, true);
|
||||
// w.set_mr(pin, true);
|
||||
});
|
||||
|
||||
unsafe {
|
||||
qingke::pfic::disable_interrupt(CoreInterrupt::SysTick as u8);
|
||||
qingke::pfic::enable_interrupt(Interrupt::EXTI7_0 as u8);
|
||||
}
|
||||
|
||||
// execute WFI
|
||||
#[cfg(feature = "enable_print")]
|
||||
println!("WFI CONFIGURED HOPEFULLY");
|
||||
});
|
||||
}
|
||||
5
d5a78ca0e0e0591dbd312378abcc82a1/description
Normal file
5
d5a78ca0e0e0591dbd312378abcc82a1/description
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
embassy hangs
|
||||
|
||||
noted that embassy hangs on the ch32v003 - even on the templated example.
|
||||
|
||||
for now, we will not use embassy.
|
||||
1
d5a78ca0e0e0591dbd312378abcc82a1/tags
Normal file
1
d5a78ca0e0e0591dbd312378abcc82a1/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
bug
|
||||
9
e9e2d173359f7a1ef441f8d5cc9af0c1/description
Normal file
9
e9e2d173359f7a1ef441f8d5cc9af0c1/description
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
phase 1: init
|
||||
|
||||
REQUIREMENTS
|
||||
* PWM DAC
|
||||
* GPIO input
|
||||
* interrupts
|
||||
* PWM outputs
|
||||
* bit-banged
|
||||
* hardware
|
||||
1
e9e2d173359f7a1ef441f8d5cc9af0c1/state
Normal file
1
e9e2d173359f7a1ef441f8d5cc9af0c1/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
wontdo
|
||||
|
|
@ -0,0 +1 @@
|
|||
was able to compile the blinky.rs example out of the ch32v-hal repository
|
||||
1
f00aa7a5559d50ec2b47b76369adde1b/description
Normal file
1
f00aa7a5559d50ec2b47b76369adde1b/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
compile code
|
||||
1
f00aa7a5559d50ec2b47b76369adde1b/state
Normal file
1
f00aa7a5559d50ec2b47b76369adde1b/state
Normal file
|
|
@ -0,0 +1 @@
|
|||
done
|
||||
1
f00aa7a5559d50ec2b47b76369adde1b/tags
Normal file
1
f00aa7a5559d50ec2b47b76369adde1b/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
phase-0
|
||||
1
f311060330813a8833ab4bc82835aefa/description
Normal file
1
f311060330813a8833ab4bc82835aefa/description
Normal file
|
|
@ -0,0 +1 @@
|
|||
enter deep sleep when adc battery voltage reading is low
|
||||
1
f311060330813a8833ab4bc82835aefa/tags
Normal file
1
f311060330813a8833ab4bc82835aefa/tags
Normal file
|
|
@ -0,0 +1 @@
|
|||
feature
|
||||
97
notes.md
97
notes.md
|
|
@ -1,97 +0,0 @@
|
|||
# ENVIRONMENT
|
||||
there is a docker image that contains the entire toolchain + flashing utility. first, you need to build the base docker image with the following script located in the ch32v-insert-coin directory:
|
||||
```shell
|
||||
$ ./init.sh
|
||||
```
|
||||
once built, you won't need to build this again. for the remainder of your development, you can use the following script to launch the environment shell:
|
||||
```shell
|
||||
$ ./launch.sh
|
||||
```
|
||||
|
||||
this will launch the docker image and give you a shell which has all of the toolchains and flashing utilities installed. to build the firmware image and flash it to the ch32 (assuming you have a wch-linke attached) run the following from inside the env shell:
|
||||
|
||||
```shell
|
||||
$ ./build-run.sh
|
||||
```
|
||||
this will build the firmware image, and attempt to upload it to the board using `wlink`. once uploaded, it will attach a serial debugger.
|
||||
|
||||
to exit the serial debugger simply use `ctrl-c`. to exit the environment shell use:
|
||||
|
||||
```shell
|
||||
$ exit
|
||||
```
|
||||
|
||||
# FLASHING
|
||||
flashing is done using the [`wlink`](https://github.com/ch32-rs/wlink?tab=readme-ov-file#install) utility. `probe-rs` also works, but can be flaky, and does not support SDI prints very well.
|
||||
|
||||
the `wlink` utility will automatically detect the correct chip, but if it doesn't you can specify it with an additional argument.
|
||||
|
||||
`wlink --help` lists all the options to the `wlink` utility.
|
||||
|
||||
## DEVELOPMENT
|
||||
when building using the rust toolchain, you can simply add the following to your `.cargo/config.toml`:
|
||||
`runner = "wlink -v flash`
|
||||
|
||||
if you want to monitor prints via SDI, you can use the following instead:
|
||||
`runner = "wlink -v flash --enable-sdi-print --watch-serial"`
|
||||
|
||||
## STANDALONE
|
||||
when flashing standalone with the `wlink` utility, you can simply run the following:
|
||||
`wlink -v flash <PATH_TO_BINARY>`
|
||||
|
||||
|
||||
# CH32V MISC NOTES
|
||||
## SLEEP / DEEP SLEEP
|
||||
it turns out you can not put the chip into deep sleep and have it wake up correctly until you power cycle it. WHAT THE FUCK. see page 36 of the [qingkev3 processor manual](https://www.wch-ic.com/download/file?id=368) at the bottom of section 6.1 Enter Sleep
|
||||
|
||||
|
||||
# EVAL BOARD NOTES
|
||||
## PINOUT
|
||||
WCHLinkE SWDIO -> PD1
|
||||
|
||||
|
||||
|
||||
# AUDIO NOTES
|
||||
## LIMITS
|
||||
we have ~9-10kB of space remaining on the chip.
|
||||
i can likely squeeze out _maybe_ another kB by dropping every print, but
|
||||
that appears to cause some lockup, i think because the SDI peripheral is
|
||||
trying to attach, but unable to.
|
||||
|
||||
for how things sit currently, we have the following (conservative) limits
|
||||
to fit within 9kB:
|
||||
|
||||
16ksps, 4b depth -> 64kbps -> 1.125s
|
||||
8ksps, 4b depth -> 32kbps -> 2.25s
|
||||
6ksps, 4b depth -> 24kbps -> 3.0s
|
||||
4ksps, 4b depth -> 16kbps -> 4.5s
|
||||
|
||||
in reality, the numbers i have seen are a little smaller than this, likely due
|
||||
to compiler optimizations when the dac is unloaded / there's no audio.
|
||||
|
||||
a 16ksps file has an _experimental_ max of 0.75s. applying that offset
|
||||
everywhere, we get empirical limits of:
|
||||
|
||||
16ksps, 4b depth: 0.75s
|
||||
8ksps, 4b depth: 1.875s
|
||||
6ksps, 4b depth: 2.625s
|
||||
4ksps, 4b depth: 4.225s
|
||||
|
||||
## FFMPEG
|
||||
use the following command to convert an input .wav to a raw mono pcm_u8 with 8ksps:
|
||||
```shell
|
||||
ffmpeg -i input.wav -f u8 -c:a pcm_u8 -ar 8000 -ac 1 output.raw
|
||||
```
|
||||
|
||||
|
||||
# PINOUTS
|
||||
LED0: PC3
|
||||
LED1: PD2
|
||||
DAC: PC4
|
||||
COIN SWITCH: PC2
|
||||
BUTTON SWITCH: PD6
|
||||
ADC: PD4
|
||||
GPIO: any remaining pin
|
||||
|
||||
## FORBIDDEN:
|
||||
PD1
|
||||
Loading…
Add table
Add a link
Reference in a new issue