start of plantsensor
This commit is contained in:
10
bootloader/.gitignore
vendored
Normal file
10
bootloader/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# ESP-IDF build artifacts
|
||||||
|
build/
|
||||||
|
.sdkconfig*
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake-build-*/
|
||||||
|
*.log
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.map
|
8
bootloader/.idea/.gitignore
generated
vendored
Normal file
8
bootloader/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
8
bootloader/.idea/bootloader.iml
generated
Normal file
8
bootloader/.idea/bootloader.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
bootloader/.idea/modules.xml
generated
Normal file
8
bootloader/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/bootloader.iml" filepath="$PROJECT_DIR$/.idea/bootloader.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
7
bootloader/.idea/vcs.xml
generated
Normal file
7
bootloader/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../website/themes/blowfish" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
2214
bootloader/sdkconfig.old
Normal file
2214
bootloader/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
2
rust/src/webserver/.gitignore
vendored
Normal file
2
rust/src/webserver/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
index.html.gz
|
||||||
|
bundle.js.gz
|
4
rust/src_webpack/.gitignore
vendored
Normal file
4
rust/src_webpack/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
index.html.gz
|
||||||
|
bundle.js.gz
|
||||||
|
index.html
|
||||||
|
bundle.js
|
14
rust_can_sensor/.cargo/config.toml
Normal file
14
rust_can_sensor/.cargo/config.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[build]
|
||||||
|
target = "riscv32imc-unknown-none-elf"
|
||||||
|
|
||||||
|
[target."riscv32imc-unknown-none-elf"]
|
||||||
|
rustflags = [
|
||||||
|
# "-C", "link-arg=-Tlink.x",
|
||||||
|
]
|
||||||
|
# runner = "riscv64-unknown-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 --erase"
|
||||||
|
# runner = "wlink -v flash"
|
32
rust_can_sensor/.doomrc
Normal file
32
rust_can_sensor/.doomrc
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
;;; .doomrc --- doom runtime config -*- mode: emacs-lisp; lexical-binding: t; -*-
|
||||||
|
;;; Commentary:
|
||||||
|
;;; Code:
|
||||||
|
(require 'doom) ; be silent, byte-compiler
|
||||||
|
|
||||||
|
(after! dape
|
||||||
|
(add-to-list
|
||||||
|
'dape-configs
|
||||||
|
`(gdb-dap-openocd
|
||||||
|
ensure (lambda (config)
|
||||||
|
(dape-ensure-command config)
|
||||||
|
(let* ((default-directory
|
||||||
|
(or (dape-config-get config 'command-cwd)
|
||||||
|
default-directory))
|
||||||
|
(command (dape-config-get config 'command))
|
||||||
|
(output (shell-command-to-string (format "%s --version" command)))
|
||||||
|
(version (save-match-data
|
||||||
|
(when (string-match "GNU gdb \\(?:(.*) \\)?\\([0-9.]+\\)" output)
|
||||||
|
(string-to-number (match-string 1 output))))))
|
||||||
|
(unless (>= version 14.1)
|
||||||
|
(user-error "Requires gdb version >= 14.1"))))
|
||||||
|
modes ()
|
||||||
|
command-cwd dape-command-cwd
|
||||||
|
command "gdb"
|
||||||
|
command-args ("--interpreter=dap")
|
||||||
|
:request nil
|
||||||
|
:program nil
|
||||||
|
:args []
|
||||||
|
:stopAtBeginningOfMainSubprogram nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
;;; .doomrc ends here
|
7
rust_can_sensor/.gdbinit
Normal file
7
rust_can_sensor/.gdbinit
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
target extended-remote :3333
|
||||||
|
set remotetimeout 2000
|
||||||
|
|
||||||
|
#symbol-file target/riscv32imc-unknown-none-elf/release/ch32v203-examples
|
||||||
|
file target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
|
||||||
|
monitor reset halt
|
2
rust_can_sensor/.gitignore
vendored
Normal file
2
rust_can_sensor/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
vendor
|
8
rust_can_sensor/.idea/.gitignore
generated
vendored
Normal file
8
rust_can_sensor/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
11
rust_can_sensor/.idea/ch32-sensor.iml
generated
Normal file
11
rust_can_sensor/.idea/ch32-sensor.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
rust_can_sensor/.idea/modules.xml
generated
Normal file
8
rust_can_sensor/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/ch32-sensor.iml" filepath="$PROJECT_DIR$/.idea/ch32-sensor.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
7
rust_can_sensor/.idea/vcs.xml
generated
Normal file
7
rust_can_sensor/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
47
rust_can_sensor/Cargo.toml
Normal file
47
rust_can_sensor/Cargo.toml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[package]
|
||||||
|
name = "bms"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ch32-hal = { path = "/home/empire/workspace/ch32-hal/", features = [
|
||||||
|
"ch32v203c8t6",
|
||||||
|
"memory-x",
|
||||||
|
"embassy",
|
||||||
|
"rt",
|
||||||
|
"time-driver-tim2",
|
||||||
|
|
||||||
|
], default-features = false }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
embassy-executor = { version = "0.7.0", features = [
|
||||||
|
# "arch-riscv32",
|
||||||
|
"arch-spin", # TODO: Required for USBD to connect properl
|
||||||
|
"executor-thread",
|
||||||
|
] }
|
||||||
|
|
||||||
|
#embassy-time = { version = "0.3.2" }
|
||||||
|
embassy-usb = { version = "0.3.0" }
|
||||||
|
embassy-futures = { version = "0.1.0" }
|
||||||
|
embassy-sync = { version = "0.6.0" }
|
||||||
|
|
||||||
|
# This is okay because we should automatically use whatever ch32-hal uses
|
||||||
|
qingke-rt = "*"
|
||||||
|
qingke = "*"
|
||||||
|
|
||||||
|
panic-halt = "1.0"
|
||||||
|
|
||||||
|
|
||||||
|
heapless = { version = "0.8.0", features = ["portable-atomic-critical-section"] }
|
||||||
|
embassy-time = { version = "0.4.0" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
#lto = true
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
|
||||||
|
lto = true
|
||||||
|
debug = false
|
||||||
|
opt-level = "z" # Optimize for size.
|
111
rust_can_sensor/README.md
Normal file
111
rust_can_sensor/README.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# ch32v203-bms
|
||||||
|
|
||||||
|
A simple battery management controller software.
|
||||||
|
|
||||||
|
## WeActStudio BluePill Plus CH32 Pin Labels
|
||||||
|
|
||||||
|
The BluePill Plus CH32 board from WeActStudio uses standard MCU port naming printed on the PCB silkscreen:
|
||||||
|
|
||||||
|
- PAx: GPIO Port A pins, labeled PA0 .. PA15
|
||||||
|
- PBx: GPIO Port B pins, labeled PB0 .. PB15
|
||||||
|
- PCx: GPIO Port C pins, commonly PC13 .. PC15 are broken out
|
||||||
|
- Other labels typically present: 3V3, 5V, G (GND), NRST (reset), BOOT (BOOT0), and SWD/RVSWD pads for programming/debug (SWCLK/SWDIO or similar).
|
||||||
|
|
||||||
|
For the exact header layout and picture of the silkscreen labels, please refer to the official WeActStudio documentation:
|
||||||
|
|
||||||
|
- https://github.com/WeActStudio/WeActStudio.BluePill-Plus-CH32
|
||||||
|
|
||||||
|
Pins used by this firmware (as referenced in `src/main.rs`):
|
||||||
|
|
||||||
|
- PA1: ADC analog input (combined Trigger/Threshold in the 555-timer example)
|
||||||
|
- PB0: Digital output (Q in the 555-timer example)
|
||||||
|
|
||||||
|
If you need to map a label to code, use the same letter+number as in the silkscreen. For example, `p.PA1` in code corresponds to pin labeled "PA1" on the PCB header, and `p.PB0` corresponds to "PB0".
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
## USB CDC Console (optional)
|
||||||
|
|
||||||
|
This project includes an optional software USB CDC-ACM device stack using embassy-usb. It runs on the CH32V203’s USB device peripheral but implements the protocol fully in software (no built-in USB class firmware is required).
|
||||||
|
|
||||||
|
How to enable:
|
||||||
|
- Build with the `usb-cdc` feature: `cargo build --release --features usb-cdc`
|
||||||
|
- Wire the MCU’s USB pins to a USB connector:
|
||||||
|
- D+ (PA12)
|
||||||
|
- D− (PA11)
|
||||||
|
- GND and 5V (as appropriate for your board; ensure you have a data-capable cable)
|
||||||
|
|
||||||
|
After flashing and powering via USB, your OS should enumerate a virtual serial port (e.g., /dev/ttyACM0 on Linux, COMx on Windows, /dev/tty.usbmodem* on macOS). Open it with any terminal program (baud setting is ignored by CDC but 115200 is fine).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- Linux: `screen /dev/ttyACM0 115200`
|
||||||
|
- macOS: `screen /dev/tty.usbmodemXXXX 115200`
|
||||||
|
- Windows: Use PuTTY on the shown COM port.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- The firmware currently implements an echo console: bytes you type are echoed back. You can extend it to print logs or interact with your application.
|
||||||
|
- If you don’t see a device, ensure D+ (PA12) and D− (PA11) are connected and the cable supports data.
|
||||||
|
|
||||||
|
## Flash
|
||||||
|
|
||||||
|
You can flash the built ELF using wchisp (WCH ISP tool):
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
# or, if using a wrapper on your system/container, the command may be:
|
||||||
|
# wchip wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unlock / Remove MCU Protection (fix "checksum error")
|
||||||
|
|
||||||
|
Some CH32 devices ship with flash protection enabled. When protected, tools may report a checksum error and refuse to program. You can clear the protection by performing a full chip erase with wchisp. This will erase all flash contents.
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
|
||||||
|
- Ensure WCH-Link/WCH-LinkE is connected to the target and your OS has permissions to access it.
|
||||||
|
- Verify connection and current status:
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
wchisp info
|
||||||
|
```
|
||||||
|
|
||||||
|
- Mass erase the chip (this also clears protection/lock bits):
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
* start the device (while pressing boot)
|
||||||
|
wchisp erase
|
||||||
|
* powercycle the device (while pressing boot)
|
||||||
|
wchisp config reset
|
||||||
|
* powercycle the device again (while pressing boot), flash should not work
|
||||||
|
```
|
||||||
|
|
||||||
|
- Flash your firmware again:
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- The target used in this project is CH32V203C8T6; wchisp detects it automatically with WCH-Link.
|
||||||
|
- If your wchisp version differs, run `wchisp --help`, `wchisp erase --help`, or consult the tool's README for the exact flag name.
|
||||||
|
- If the tool still reports protection, look for commands named `unprotect` or `protect --off` in `wchisp --help`. The mass/chip erase is the typical way to clear protection.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
For debugging purposes a container file is provided together with wrapper scripts to start the containerized `openocd` and `riscv-gdb` transparently. The wrapper scripts assume that `podman` is setup.
|
||||||
|
|
||||||
|
Starting Debug server
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/openocd
|
||||||
|
```
|
||||||
|
|
||||||
|
Connecting with gdb for interactive debugging
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/gdb -f target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
```
|
10
rust_can_sensor/bin/build-wch-tools-container.sh
Executable file
10
rust_can_sensor/bin/build-wch-tools-container.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
pushd "$CONTAINER_TOOLS_BASEDIR"
|
||||||
|
podman build -t "$CONTAINER_NAME" -f "../wch-tools.Containerfile" .
|
||||||
|
popd
|
29
rust_can_sensor/bin/gdb
Executable file
29
rust_can_sensor/bin/gdb
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_IMAGE="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
function _fatal {
|
||||||
|
echo -e "\e[31mERROR\e[0m $(</dev/stdin)$*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a PODMAN_ARGS=(
|
||||||
|
"--rm" "-i" "--log-driver=none"
|
||||||
|
"--network=host"
|
||||||
|
"--pid=host"
|
||||||
|
"-v" "$PWD:$PWD:rw"
|
||||||
|
"-w" "$PWD"
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ -t 1 ]] && PODMAN_ARGS+=("-t")
|
||||||
|
|
||||||
|
if ! podman image exists "$CONTAINER_IMAGE"; then
|
||||||
|
#attempt to build container
|
||||||
|
"$CONTAINER_TOOLS_BASEDIR/build-wch-tools-container.sh" 1>&2 ||
|
||||||
|
_fatal "faild to build local image, cannot continue! … please ensure you have an internet connection"
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman run "${PODMAN_ARGS[@]}" --entrypoint riscv-none-elf-gdb-py3 "$CONTAINER_IMAGE" "$@"
|
44
rust_can_sensor/bin/openocd
Executable file
44
rust_can_sensor/bin/openocd
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_IMAGE="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
function _fatal {
|
||||||
|
echo -e "\e[31mERROR\e[0m $(</dev/stdin)$*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a PODMAN_ARGS=(
|
||||||
|
"--rm" "-i" "--log-driver=none"
|
||||||
|
"--network=host"
|
||||||
|
"-v" "$PWD:$PWD:rw"
|
||||||
|
"-w" "$PWD"
|
||||||
|
)
|
||||||
|
|
||||||
|
for device in /dev/bus/usb/*/*; do
|
||||||
|
if udevadm info "$device" | grep -q "ID_VENDOR=wch.cn" && \
|
||||||
|
udevadm info "$device" | grep -q "ID_MODEL=WCH-Link"; then
|
||||||
|
DEBUGGER_DEV_PATH="$device"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "${DEBUGGER_DEV_PATH:-}" ]]; then
|
||||||
|
echo "Could not find hardware debugger … Exiting!" 1>&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
# add jlink to podman device
|
||||||
|
PODMAN_ARGS+=("--device=$DEBUGGER_DEV_PATH")
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -t 1 ]] && PODMAN_ARGS+=("-t")
|
||||||
|
|
||||||
|
if ! podman image exists "$CONTAINER_IMAGE"; then
|
||||||
|
#attempt to build container
|
||||||
|
"$CONTAINER_TOOLS_BASEDIR/build-wch-tools-container.sh" 1>&2 ||
|
||||||
|
_fatal "faild to build local image, cannot continue! … please ensure you have an internet connection"
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman run "${PODMAN_ARGS[@]}" --entrypoint openocd "$CONTAINER_IMAGE" "$@"
|
11
rust_can_sensor/build.rs
Normal file
11
rust_can_sensor/build.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
// println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
// println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
let out_dir = std::path::PathBuf::from(out_dir);
|
||||||
|
std::fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
}
|
125
rust_can_sensor/memory.x
Normal file
125
rust_can_sensor/memory.x
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/* CH32V203c8t6 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 64K /* BANK_1 */
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
|
REGION_ALIAS("REGION_TEXT", FLASH);
|
||||||
|
REGION_ALIAS("REGION_RODATA", FLASH);
|
||||||
|
REGION_ALIAS("REGION_DATA", RAM);
|
||||||
|
REGION_ALIAS("REGION_BSS", RAM);
|
||||||
|
REGION_ALIAS("REGION_HEAP", RAM);
|
||||||
|
REGION_ALIAS("REGION_STACK", RAM);
|
||||||
|
|
||||||
|
/* fault handlers */
|
||||||
|
|
||||||
|
PROVIDE(InstructionMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(InstructionFault = ExceptionHandler);
|
||||||
|
PROVIDE(IllegalInstruction = ExceptionHandler);
|
||||||
|
PROVIDE(Breakpoint = ExceptionHandler);
|
||||||
|
PROVIDE(LoadMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(LoadFault = ExceptionHandler);
|
||||||
|
PROVIDE(StoreMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(StoreFault = ExceptionHandler);;
|
||||||
|
PROVIDE(UserEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(SupervisorEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(MachineEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(InstructionPageFault = ExceptionHandler);
|
||||||
|
PROVIDE(LoadPageFault = ExceptionHandler);
|
||||||
|
PROVIDE(StorePageFault = ExceptionHandler);
|
||||||
|
|
||||||
|
/* core interrupt handlers */
|
||||||
|
|
||||||
|
PROVIDE(NonMaskableInt = DefaultHandler);
|
||||||
|
PROVIDE(Software = DefaultHandler);
|
||||||
|
|
||||||
|
/* external interrupt handlers */
|
||||||
|
|
||||||
|
PROVIDE(WWDG = DefaultHandler);
|
||||||
|
PROVIDE(PVD = DefaultHandler);
|
||||||
|
PROVIDE(TAMPER = DefaultHandler);
|
||||||
|
PROVIDE(RTC = DefaultHandler);
|
||||||
|
PROVIDE(FLASH = DefaultHandler);
|
||||||
|
PROVIDE(RCC = DefaultHandler);
|
||||||
|
PROVIDE(EXTI0 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI1 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI2 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI3 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL1 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL2 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL3 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL5 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL6 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL7 = DefaultHandler);
|
||||||
|
PROVIDE(ADC = DefaultHandler);
|
||||||
|
PROVIDE(USB_HP_CAN1_TX = DefaultHandler);
|
||||||
|
/*PROVIDE(USB_LP_CAN1_RX0 = DefaultHandler);*/
|
||||||
|
PROVIDE(CAN1_RX1 = DefaultHandler);
|
||||||
|
PROVIDE(CAN1_SCE = DefaultHandler);
|
||||||
|
PROVIDE(EXTI9_5 = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_CC = DefaultHandler);
|
||||||
|
PROVIDE(TIM2 = DefaultHandler);
|
||||||
|
PROVIDE(TIM3 = DefaultHandler);
|
||||||
|
PROVIDE(TIM4 = DefaultHandler);
|
||||||
|
PROVIDE(I2C1_EV = DefaultHandler);
|
||||||
|
PROVIDE(I2C1_ER = DefaultHandler);
|
||||||
|
PROVIDE(I2C2_EV = DefaultHandler);
|
||||||
|
PROVIDE(I2C2_ER = DefaultHandler);
|
||||||
|
PROVIDE(SPI1 = DefaultHandler);
|
||||||
|
PROVIDE(SPI2 = DefaultHandler);
|
||||||
|
PROVIDE(USART1 = DefaultHandler);
|
||||||
|
PROVIDE(USART2 = DefaultHandler);
|
||||||
|
PROVIDE(USART3 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI15_10 = DefaultHandler);
|
||||||
|
PROVIDE(RTCALARM = DefaultHandler);
|
||||||
|
PROVIDE(USBWAKE_UP = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_CC = DefaultHandler);
|
||||||
|
PROVIDE(RNG = DefaultHandler);
|
||||||
|
PROVIDE(FSMC = DefaultHandler);
|
||||||
|
PROVIDE(SDIO = DefaultHandler);
|
||||||
|
PROVIDE(TIM5 = DefaultHandler);
|
||||||
|
PROVIDE(SPI3 = DefaultHandler);
|
||||||
|
PROVIDE(UART4 = DefaultHandler);
|
||||||
|
PROVIDE(UART5 = DefaultHandler);
|
||||||
|
PROVIDE(TIM6 = DefaultHandler);
|
||||||
|
PROVIDE(TIM7 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL1 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL2 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL3 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL5 = DefaultHandler);
|
||||||
|
PROVIDE(ETH = DefaultHandler);
|
||||||
|
PROVIDE(ETH_WKUP = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_TX = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_RX0 = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_RX1 = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_SCE = DefaultHandler);
|
||||||
|
PROVIDE(OTG_FS = DefaultHandler);
|
||||||
|
PROVIDE(USBHSWAKEUP = DefaultHandler);
|
||||||
|
PROVIDE(USBHS = DefaultHandler);
|
||||||
|
PROVIDE(DVP = DefaultHandler);
|
||||||
|
PROVIDE(UART6 = DefaultHandler);
|
||||||
|
PROVIDE(UART7 = DefaultHandler);
|
||||||
|
PROVIDE(UART8 = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_CC = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_CC = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL6 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL7 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL8 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL9 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL10 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL11 = DefaultHandler);
|
17
rust_can_sensor/openocd.cfg
Normal file
17
rust_can_sensor/openocd.cfg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
set _CHIPNAME ch32v203
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
|
||||||
|
#bindto 0.0.0.0
|
||||||
|
|
||||||
|
adapter driver wlinke
|
||||||
|
adapter speed 6000
|
||||||
|
transport select sdi
|
||||||
|
|
||||||
|
sdi newtap $_CHIPNAME cpu -irlen 5 --expected-id 0x00001
|
||||||
|
target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
|
||||||
|
$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
|
||||||
|
set _FLASHNAME $_CHIPNAME.flash
|
||||||
|
|
||||||
|
flash bank $_FLASHNAME wch_rsicv 0x00000000 0 0 0 $_TARGETNAME.0
|
||||||
|
|
||||||
|
init
|
2
rust_can_sensor/rust-toolchain.toml
Normal file
2
rust_can_sensor/rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
207
rust_can_sensor/src/main.rs
Normal file
207
rust_can_sensor/src/main.rs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::fmt::Write as _;
|
||||||
|
use ch32_hal::gpio::{Level, Output, Speed};
|
||||||
|
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
||||||
|
use ch32_hal::peripherals::USBD;
|
||||||
|
// use ch32_hal::delay::Delay;
|
||||||
|
use embassy_executor::{Spawner, task};
|
||||||
|
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||||
|
use embassy_usb::{Builder, UsbDevice};
|
||||||
|
use embassy_futures::yield_now;
|
||||||
|
use hal::usbd::{Driver};
|
||||||
|
use hal::{bind_interrupts};
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::channel::{Channel, TrySendError};
|
||||||
|
use embassy_time::{Timer, Instant, Duration};
|
||||||
|
use heapless::String;
|
||||||
|
use {ch32_hal as hal, panic_halt as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 'static storage for USB descriptors and state so we can spawn tasks
|
||||||
|
static mut USB_CONFIG_DESCRIPTOR: [u8; 256] = [0; 256];
|
||||||
|
static mut USB_BOS_DESCRIPTOR: [u8; 256] = [0; 256];
|
||||||
|
static mut USB_CONTROL_BUF: [u8; 64] = [0; 64];
|
||||||
|
static mut CDC_STATE: core::mem::MaybeUninit<State<'static>> = core::mem::MaybeUninit::uninit();
|
||||||
|
static mut USB_DEVICE: core::mem::MaybeUninit<UsbDevice<'static, Driver<'static, hal::peripherals::USBD>>> = core::mem::MaybeUninit::uninit();
|
||||||
|
static mut CDC_CLASS: core::mem::MaybeUninit<CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>> = core::mem::MaybeUninit::uninit();
|
||||||
|
|
||||||
|
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
|
||||||
|
|
||||||
|
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = hal::init(hal::Config {
|
||||||
|
rcc: hal::rcc::Config::SYSCLK_FREQ_144MHZ_HSI,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Build driver and USB stack using 'static buffers
|
||||||
|
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
|
||||||
|
|
||||||
|
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-serial example");
|
||||||
|
config.serial_number = Some("12345678");
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = 64;
|
||||||
|
|
||||||
|
// Windows compatibility requires these; CDC-ACM
|
||||||
|
config.device_class = 0x02;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x00;
|
||||||
|
config.composite_with_iads = false;
|
||||||
|
|
||||||
|
let usb = unsafe {
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut USB_CONFIG_DESCRIPTOR,
|
||||||
|
&mut USB_BOS_DESCRIPTOR,
|
||||||
|
&mut [], // no msos descriptors
|
||||||
|
&mut USB_CONTROL_BUF,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize CDC state and create CDC-ACM class
|
||||||
|
CDC_STATE.write(State::new());
|
||||||
|
let class = {
|
||||||
|
let state_ref: &mut State<'static> = CDC_STATE.assume_init_mut();
|
||||||
|
CdcAcmClass::new(&mut builder, state_ref, 64)
|
||||||
|
};
|
||||||
|
CDC_CLASS.write(class);
|
||||||
|
|
||||||
|
// Build USB device
|
||||||
|
let dev = builder.build();
|
||||||
|
USB_DEVICE.write(dev);
|
||||||
|
|
||||||
|
USB_DEVICE.assume_init_mut()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create GPIO for 555 Q output (PB0)
|
||||||
|
let q_out = Output::new(p.PB0, Level::Low, Speed::Low);
|
||||||
|
// Built-in LED on PB2 mirrors Q state
|
||||||
|
let led = Output::new(p.PB2, Level::Low, Speed::Low);
|
||||||
|
|
||||||
|
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
|
||||||
|
let adc = Adc::new(p.ADC1, Default::default());
|
||||||
|
let ain = p.PA1;
|
||||||
|
|
||||||
|
// Spawn independent tasks using 'static references
|
||||||
|
unsafe {
|
||||||
|
let class = CDC_CLASS.assume_init_mut();
|
||||||
|
spawner.spawn(usb_task(usb)).unwrap();
|
||||||
|
spawner.spawn(usb_writer(class)).unwrap();
|
||||||
|
// move Q output, LED, ADC and analog input into worker task
|
||||||
|
spawner.spawn(worker(q_out, led, adc, ain)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent main from exiting
|
||||||
|
core::future::pending::<()>().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn worker(
|
||||||
|
mut q: Output<'static>,
|
||||||
|
mut led: Output<'static>,
|
||||||
|
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
||||||
|
mut ain: hal::peripherals::PA1,
|
||||||
|
) {
|
||||||
|
// 555 emulation state: Q initially Low
|
||||||
|
let mut q_high = false;
|
||||||
|
let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref
|
||||||
|
let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Count rising edges of Q in a 100 ms window
|
||||||
|
let start = Instant::now();
|
||||||
|
let mut pulses: u32 = 0;
|
||||||
|
let mut last_q = q_high;
|
||||||
|
|
||||||
|
while Instant::now().checked_duration_since(start).unwrap_or(Duration::from_millis(0))
|
||||||
|
< Duration::from_millis(1000)
|
||||||
|
{
|
||||||
|
// Sample the analog input (Threshold/Trigger on A1)
|
||||||
|
let val: u16 = adc.convert(&mut ain, SampleTime::CYCLES28_5);
|
||||||
|
|
||||||
|
// 555 core behavior:
|
||||||
|
// - If input <= 1/3 Vref => set Q high (trigger)
|
||||||
|
// - If input >= 2/3 Vref => set Q low (threshold)
|
||||||
|
// - Otherwise keep previous Q state (hysteresis)
|
||||||
|
if val <= low_th {
|
||||||
|
q_high = true;
|
||||||
|
} else if val >= high_th {
|
||||||
|
q_high = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drive output pin accordingly
|
||||||
|
if q_high {
|
||||||
|
q.set_high();
|
||||||
|
led.set_high();
|
||||||
|
} else {
|
||||||
|
q.set_low();
|
||||||
|
led.set_low();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count rising edges
|
||||||
|
if !last_q && q_high {
|
||||||
|
pulses = pulses.saturating_add(1);
|
||||||
|
}
|
||||||
|
last_q = q_high;
|
||||||
|
|
||||||
|
// Yield to allow USB and other tasks to run
|
||||||
|
yield_now().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute frequency from 100 ms window
|
||||||
|
let freq_hz = pulses; // pulses per 0.1s => Hz
|
||||||
|
|
||||||
|
let mut msg: heapless::String<128> = heapless::String::new();
|
||||||
|
let _ = write!(
|
||||||
|
&mut msg,
|
||||||
|
"555 window=100ms pulses={} freq={} Hz (A1->Q on PB0)\r\n",
|
||||||
|
pulses, freq_hz
|
||||||
|
);
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(message: heapless::String<128>) {
|
||||||
|
match LOG_CH.try_send(message) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn usb_task(usb: &'static mut UsbDevice<'static, Driver<'static, hal::peripherals::USBD>>) {
|
||||||
|
usb.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task]
|
||||||
|
async fn usb_writer(
|
||||||
|
class: &'static mut CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
|
||||||
|
class.wait_connection().await;
|
||||||
|
printer(class).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn printer(class: &mut CdcAcmClass<'static, Driver<'static, USBD>>) {
|
||||||
|
loop {
|
||||||
|
let msg = LOG_CH.receive().await;
|
||||||
|
match class.write_packet(msg.as_bytes()).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => {
|
||||||
|
// Disconnected or endpoint disabled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
rust_can_sensor/wch-tools.Containerfile
Normal file
27
rust_can_sensor/wch-tools.Containerfile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
RUN apt update -y && apt upgrade -y && apt install git libjaylink-dev libusb-1.0-0 unzip curl libhidapi-hidraw0 xz-utils -y
|
||||||
|
|
||||||
|
RUN cd /root && \
|
||||||
|
curl -L -o mrs-toolchain.tar.xz "https://github.com/ch32-riscv-ug/MounRiver_Studio_Community_miror/releases/download/1.92-toolchain/MRS_Toolchain_Linux_x64_V1.92.tar.xz" && \
|
||||||
|
mkdir mrs-toolchain && \
|
||||||
|
tar -xvf mrs-toolchain.tar.xz -C mrs-toolchain --strip-components=1 && \
|
||||||
|
mv mrs-toolchain/OpenOCD/bin/openocd /usr/local/bin && \
|
||||||
|
mv mrs-toolchain/OpenOCD/share/openocd /usr/local/share && \
|
||||||
|
# mv mrs-toolchain/RISC-V_Embedded_GCC12/bin/riscv-none-elf-gdb /usr/local/bin && \ # both toolchains in MRS are to old to work with emacs dape
|
||||||
|
# mv mrs-toolchain/RISC-V_Embedded_GCC12/libexec /usr/local && \ # both toolchains in MRS are to old to work with emacs dape
|
||||||
|
rm -rf mrs-toolchain mrs-toolchain.tar.xz && \
|
||||||
|
# Use up to date xpack toolchains for gdb
|
||||||
|
curl -L -o xpack-riscv-toolchain.tar.gz "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-x64.tar.gz" && \
|
||||||
|
mkdir xpack-toolchain && \
|
||||||
|
tar -xvf xpack-riscv-toolchain.tar.gz -C xpack-toolchain --strip-components=1 && \
|
||||||
|
mv xpack-toolchain/bin/* /usr/local/bin && \
|
||||||
|
mv xpack-toolchain/lib/ /usr/local && \
|
||||||
|
mv xpack-toolchain/lib64/ /usr/local && \
|
||||||
|
mv xpack-toolchain/libexec /usr/local && \
|
||||||
|
mv xpack-toolchain/riscv-none-elf /usr/local && \
|
||||||
|
rm -rf xpack-toolchain xpack-riscv-toolchain.tar.gz
|
||||||
|
|
||||||
|
RUN mkdir -p /root/.config/gdb && echo "set auto-load safe-path /" >> /root/.config/gdb/gdbinit
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/bin/bash" ]
|
Reference in New Issue
Block a user