Initial Setup for the microbit v1
This commit is contained in:
42
.cargo/config.toml
Normal file
42
.cargo/config.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
[target.thumbv7m-none-eabi]
|
||||
# uncomment this to make `cargo run` execute programs on QEMU
|
||||
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
# TODO(2) replace `$CHIP` with your chip's name (see `probe-rs chip list` output)
|
||||
runner = "probe-rs run --chip nRF51822_xxAA"
|
||||
# runner = ["probe-rs", "run", "--chip", "nRF51822_xxAA", "--log-format", "{L} {s}"]
|
||||
|
||||
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||
# which option to pick depends on your system
|
||||
#runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||
# runner = "gdb -q -x openocd.gdb"
|
||||
|
||||
rustflags = [
|
||||
# Previously, the linker arguments --nmagic and -Tlink.x were set here.
|
||||
# They are now set by build.rs instead. The linker argument can still
|
||||
# only be set here, if a custom linker is needed.
|
||||
"-C", "linker=flip-link",
|
||||
"-C", "link-arg=-Tdefmt.x",
|
||||
# By default, the LLD linker is used, which is shipped with the Rust
|
||||
# toolchain. If you run into problems with LLD, you can switch to the
|
||||
# GNU linker by uncommenting this line:
|
||||
# "-C", "linker=arm-none-eabi-ld",
|
||||
|
||||
# If you need to link to pre-compiled C libraries provided by a C toolchain
|
||||
# use GCC as the linker by uncommenting the three lines below:
|
||||
# "-C", "linker=arm-none-eabi-gcc",
|
||||
# "-C", "link-arg=-Wl,-Tlink.x",
|
||||
# "-C", "link-arg=-nostartfiles",
|
||||
]
|
||||
|
||||
[build]
|
||||
# Pick ONE of these default compilation targets
|
||||
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||
# target = "thumbv7m-none-eabi" # Cortex-M3
|
||||
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
||||
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||
# target = "thumbv8m.base-none-eabi" # Cortex-M23
|
||||
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
|
||||
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.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
|
||||
15
.idea/customTargets.xml
generated
Normal file
15
.idea/customTargets.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CLionExternalBuildManager">
|
||||
<target id="de437d0c-be58-4a32-a065-30cf5806e1cd" name="cargo-targets" defaultType="TOOL">
|
||||
<configuration id="b18548c4-f93a-4480-8d5f-1e7226ad38bf" name="cargo-targets">
|
||||
<build type="TOOL">
|
||||
<tool actionId="Tool_External Tools_cargo-build" />
|
||||
</build>
|
||||
<clean type="TOOL">
|
||||
<tool actionId="Tool_External Tools_cargo-clean" />
|
||||
</clean>
|
||||
</configuration>
|
||||
</target>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.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/test-microbit.iml" filepath="$PROJECT_DIR$/.idea/test-microbit.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
22
.idea/runConfigurations/Build.xml
generated
Normal file
22
.idea/runConfigurations/Build.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="buildProfileId" value="dev" />
|
||||
<option name="command" value="build --features v1" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs>
|
||||
<env name="DEFMT_LOG" value="trace" />
|
||||
</envs>
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
21
.idea/runConfigurations/Embed.xml
generated
Normal file
21
.idea/runConfigurations/Embed.xml
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Embed" type="CargoCommandRunConfiguration" factoryName="Cargo Command" nameIsGenerated="true">
|
||||
<option name="command" value="embed --features v1" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs>
|
||||
<env name="DEFMT_LOG" value="trace" />
|
||||
</envs>
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
21
.idea/runConfigurations/_template__of_Cargo_Command.xml
generated
Normal file
21
.idea/runConfigurations/_template__of_Cargo_Command.xml
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="embed --features v1" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs>
|
||||
<env name="DEFMT_LOG" value="trace" />
|
||||
</envs>
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
10
.idea/runConfigurations/_template__of_com_jetbrains_cidr_embedded_openocd_conf_factory.xml
generated
Normal file
10
.idea/runConfigurations/_template__of_com_jetbrains_cidr_embedded_openocd_conf_factory.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="true" type="com.jetbrains.cidr.embedded.openocd.conf.type" factoryName="com.jetbrains.cidr.embedded.openocd.conf.factory" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="template-microbit" TARGET_NAME="cargo-targets" CONFIG_NAME="cargo-targets" version="1" RUN_PATH="$PROJECT_DIR$/target/thumbv6m-none-eabi/debug/template-microbit">
|
||||
<openocd version="1" gdb-port="3333" telnet-port="4444" board-config="$PROJECT_DIR$/openocd.cfg" reset-type="INIT" download-type="UPDATED_ONLY">
|
||||
<debugger kind="GDB" isBundled="true" />
|
||||
</openocd>
|
||||
<method v="2">
|
||||
<option name="CLION.COMPOUND.BUILD" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
13
.idea/test-microbit.iml
generated
Normal file
13
.idea/test-microbit.iml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="CPP_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
23
.idea/tools/External Tools.xml
generated
Normal file
23
.idea/tools/External Tools.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
<toolSet name="External Tools">
|
||||
<tool name="cargo-build" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="cargo" />
|
||||
<option name="PARAMETERS" value="build" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
<tool name="cargo-build-release" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="cargo" />
|
||||
<option name="PARAMETERS" value="build --release" />
|
||||
<option name="WORKING_DIRECTORY" />
|
||||
</exec>
|
||||
</tool>
|
||||
<tool name="cargo-clean" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="cargo" />
|
||||
<option name="PARAMETERS" value="clean" />
|
||||
<option name="WORKING_DIRECTORY" />
|
||||
</exec>
|
||||
</tool>
|
||||
</toolSet>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
// override the default setting (`cargo check --all-targets`) which produces the following error
|
||||
// "can't find crate for `test`" when the default compilation target is a no_std target
|
||||
// with these changes RA will call `cargo check --bins` on save
|
||||
"rust-analyzer.checkOnSave.allTargets": false,
|
||||
"rust-analyzer.checkOnSave.extraArgs": [
|
||||
"--bins"
|
||||
]
|
||||
}
|
||||
84
Cargo.toml
Normal file
84
Cargo.toml
Normal file
@@ -0,0 +1,84 @@
|
||||
[package]
|
||||
authors = ["Lionel Sambuc <lionel.sambuc@gmail.com>"]
|
||||
name = "template-microbit"
|
||||
edition = "2021"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
harness = false
|
||||
|
||||
# needed for each integration test
|
||||
[[test]]
|
||||
name = "integration"
|
||||
harness = false
|
||||
|
||||
[dependencies.microbit-v2]
|
||||
version = "0.15.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.microbit]
|
||||
version = "0.15.1"
|
||||
optional = true
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core", "inline-asm"] }
|
||||
cortex-m-rt = "0.7"
|
||||
#cortex-m-semihosting = "0.5.0"
|
||||
#panic-halt = "0.2.0"
|
||||
panic-rtt-target = "0.1"
|
||||
|
||||
defmt = "0.3"
|
||||
# Either rtt-target or defmt-rtt
|
||||
#rtt-target = "0.5"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
|
||||
embedded-hal = "1.0.0"
|
||||
embedded-alloc = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
defmt-test = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["v1"]
|
||||
v2 = ["microbit-v2"]
|
||||
v1 = ["microbit"]
|
||||
|
||||
# cargo build/run
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 'z' # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo test
|
||||
[profile.test]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
# cargo build/run --release
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
||||
|
||||
# cargo test --release
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
||||
|
||||
12
Embed.toml
Normal file
12
Embed.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[default.general]
|
||||
# chip = "nrf52833_xxAA" # uncomment this line for micro:bit V2
|
||||
chip = "nrf51822_xxAA" # uncomment this line for micro:bit V1
|
||||
|
||||
[default.reset]
|
||||
halt_afterwards = true
|
||||
|
||||
[default.rtt]
|
||||
enabled = true
|
||||
|
||||
[default.gdb]
|
||||
enabled = false
|
||||
23
LICENSE-MIT
Normal file
23
LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Template for `BBC micro:bit`
|
||||
|
||||
This template as been setup for the BBC micro:bit.
|
||||
|
||||
## Run
|
||||
|
||||
You are now all set to `cargo-run` your first `defmt`-powered application!
|
||||
There are some examples in the `src/bin` directory.
|
||||
|
||||
Start by `cargo run`-ning `my-app/src/bin/hello.rs`:
|
||||
|
||||
``` console
|
||||
$ # `rb` is an alias for `run --bin`
|
||||
$ cargo rb hello
|
||||
Finished dev [optimized + debuginfo] target(s) in 0.03s
|
||||
flashing program ..
|
||||
DONE
|
||||
resetting device
|
||||
0.000000 INFO Hello, world!
|
||||
(..)
|
||||
|
||||
$ echo $?
|
||||
0
|
||||
```
|
||||
|
||||
If you're running out of memory (`flip-link` bails with an overflow error), you can decrease the size of the device memory buffer by setting the `DEFMT_RTT_BUFFER_SIZE` environment variable. The default value is 1024 bytes, and powers of two should be used for optimal performance:
|
||||
|
||||
``` console
|
||||
$ DEFMT_RTT_BUFFER_SIZE=64 cargo rb hello
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
The template comes configured for running unit tests and integration tests on the target.
|
||||
|
||||
Unit tests reside in the library crate and can test private API; the initial set of unit tests are in `src/lib.rs`.
|
||||
`cargo test --lib` will run those unit tests.
|
||||
|
||||
``` console
|
||||
$ cargo test --lib
|
||||
(1/1) running `it_works`...
|
||||
└─ app::unit_tests::__defmt_test_entry @ src/lib.rs:33
|
||||
all tests passed!
|
||||
└─ app::unit_tests::__defmt_test_entry @ src/lib.rs:28
|
||||
```
|
||||
|
||||
Integration tests reside in the `tests` directory; the initial set of integration tests are in `tests/integration.rs`.
|
||||
`cargo test --test integration` will run those integration tests.
|
||||
Note that the argument of the `--test` flag must match the name of the test file in the `tests` directory.
|
||||
|
||||
``` console
|
||||
$ cargo test --test integration
|
||||
(1/1) running `it_works`...
|
||||
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:13
|
||||
all tests passed!
|
||||
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:8
|
||||
```
|
||||
|
||||
Note that to add a new test file to the `tests` directory you also need to add a new `[[test]]` section to `Cargo.toml`.
|
||||
|
||||
## Generated using app-template
|
||||
|
||||
`app-template` is part of the [Knurling] project, [Ferrous Systems]' effort at
|
||||
improving tooling used to develop for embedded systems.
|
||||
|
||||
[Knurling]: https://knurling.ferrous-systems.com
|
||||
[Ferrous Systems]: https://ferrous-systems.com/
|
||||
29
SETUP.md
Normal file
29
SETUP.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Install tools
|
||||
|
||||
```shell
|
||||
cargo install cargo-binutils
|
||||
cargo install probe-rs-tools
|
||||
rustup component add llvm-tools
|
||||
```
|
||||
|
||||
## MacOS
|
||||
|
||||
```shell
|
||||
brew install arm-none-eabi-gdb picocom lsusb
|
||||
```
|
||||
|
||||
## Other OSes
|
||||
|
||||
Please refer to the instructions from:
|
||||
|
||||
* Linux : https://docs.rust-embedded.org/discovery/microbit/03-setup/linux.html
|
||||
* Windows : https://docs.rust-embedded.org/discovery/microbit/03-setup/windows.html
|
||||
|
||||
# Verifying installation
|
||||
|
||||
With the BBC micro:bit connected through USB:
|
||||
|
||||
```shell
|
||||
probe-rs list
|
||||
probe-rs info
|
||||
```
|
||||
43
build.rs
Normal file
43
build.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
//!
|
||||
//! The build script also sets the linker flags to tell it which link script to use.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
|
||||
// Specify linker arguments.
|
||||
|
||||
// `--nmagic` is required if memory section addresses are not aligned to 0x10000,
|
||||
// for example the FLASH and RAM sections in your `memory.x`.
|
||||
// See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||
println!("cargo:rustc-link-arg=--nmagic");
|
||||
|
||||
// Set the linker script to the one provided by cortex-m-rt.
|
||||
println!("cargo:rustc-link-arg=-Tlink.x");
|
||||
}
|
||||
13
examples/bitfield.rs
Normal file
13
examples/bitfield.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use template_microbit as _; // global logger + panicking-behavior + memory layout
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
// value of the FREQUENCY register (nRF52840 device; RADIO peripheral)
|
||||
let frequency: u32 = 276;
|
||||
defmt::info!("FREQUENCY: {0=0..7}, MAP: {0=8..9}", frequency);
|
||||
|
||||
template_microbit::exit()
|
||||
}
|
||||
29
examples/format.rs
Normal file
29
examples/format.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use template_microbit as _; // global logger + panicking-behavior + memory layout
|
||||
use defmt::Format; // <- derive attribute
|
||||
|
||||
#[derive(Format)]
|
||||
struct S1<T> {
|
||||
x: u8,
|
||||
y: T,
|
||||
}
|
||||
|
||||
#[derive(Format)]
|
||||
struct S2 {
|
||||
z: u8,
|
||||
}
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
let s = S1 {
|
||||
x: 42,
|
||||
y: S2 { z: 43 },
|
||||
};
|
||||
defmt::println!("s={:?}", s);
|
||||
let x = 42;
|
||||
defmt::println!("x={=u8}", x);
|
||||
|
||||
template_microbit::exit()
|
||||
}
|
||||
18
examples/hello.rs
Normal file
18
examples/hello.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
// global logger + panicking-behavior + memory layout
|
||||
|
||||
use microbit::board::Board;
|
||||
use microbit::hal::gpio::Level::High;
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
let board = Board::take().unwrap();
|
||||
board.display_pins.col1.into_pulldown_input();
|
||||
board.display_pins.row1.into_push_pull_output(High);
|
||||
|
||||
defmt::println!("Hello, world! freq ", );
|
||||
|
||||
template_microbit::exit()
|
||||
}
|
||||
20
examples/levels.rs
Normal file
20
examples/levels.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use template_microbit as _;
|
||||
use template_microbit::exit;
|
||||
// global logger + panicking-behavior + memory layout
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
// try setting the DEFMT_LOG environment variable
|
||||
// e.g. `export DEFMT_LOG=info` or `DEFMT_LOG=trace cargo rb levels`
|
||||
defmt::info!("info");
|
||||
defmt::trace!("trace");
|
||||
defmt::warn!("warn");
|
||||
defmt::debug!("debug");
|
||||
defmt::error!("error");
|
||||
exit();
|
||||
|
||||
template_microbit::exit()
|
||||
}
|
||||
28
examples/overflow.rs
Normal file
28
examples/overflow.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use template_microbit as _; // global logger + panicking-behavior + memory layout
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
ack(10, 10);
|
||||
template_microbit::exit()
|
||||
}
|
||||
|
||||
fn ack(m: u32, n: u32) -> u32 {
|
||||
// waste stack space to trigger a stack overflow
|
||||
let mut buffer = [0u8; 16 * 1024];
|
||||
// estimate of the Stack Pointer register
|
||||
let sp = buffer.as_mut_ptr();
|
||||
defmt::println!("ack(m={=u32}, n={=u32}, SP={:x})", m, n, sp);
|
||||
|
||||
if m == 0 {
|
||||
n + 1
|
||||
} else {
|
||||
if n == 0 {
|
||||
ack(m - 1, 1)
|
||||
} else {
|
||||
ack(m - 1, ack(m, n - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
11
examples/panic.rs
Normal file
11
examples/panic.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use template_microbit as _; // global logger + panicking-behavior + memory layout
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
defmt::println!("main");
|
||||
|
||||
defmt::panic!()
|
||||
}
|
||||
11
memory.x
Normal file
11
memory.x
Normal file
@@ -0,0 +1,11 @@
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE K = KiBi = 1024 bytes */
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* This is where the call stack will be allocated. */
|
||||
/* The stack is of the full descending type. */
|
||||
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||
2
openocd.cfg
Normal file
2
openocd.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
source [find interface/cmsis-dap.cfg]
|
||||
source [find target/nrf51.cfg]
|
||||
40
openocd.gdb
Normal file
40
openocd.gdb
Normal file
@@ -0,0 +1,40 @@
|
||||
target extended-remote :3333
|
||||
|
||||
# print demangled symbols
|
||||
set print asm-demangle on
|
||||
|
||||
# set backtrace limit to not have infinite backtrace loops
|
||||
set backtrace limit 32
|
||||
|
||||
# detect unhandled exceptions, hard faults and panics
|
||||
break DefaultHandler
|
||||
break HardFault
|
||||
break rust_begin_unwind
|
||||
# # run the next few lines so the panic message is printed immediately
|
||||
# # the number needs to be adjusted for your panic handler
|
||||
# commands $bpnum
|
||||
# next 4
|
||||
# end
|
||||
|
||||
# *try* to stop at the user entry point (it might be gone due to inlining)
|
||||
break main
|
||||
|
||||
monitor arm semihosting enable
|
||||
|
||||
# # send captured ITM to the file itm.fifo
|
||||
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
|
||||
# # 8000000 must match the core clock frequency
|
||||
# monitor tpiu config internal itm.txt uart off 8000000
|
||||
|
||||
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
|
||||
# # 8000000 must match the core clock frequency
|
||||
# # 2000000 is the frequency of the SWO pin
|
||||
# monitor tpiu config external uart off 8000000 2000000
|
||||
|
||||
# # enable ITM port 0
|
||||
# monitor itm port 0 on
|
||||
|
||||
load
|
||||
|
||||
# start the process but immediately halt the processor
|
||||
stepi
|
||||
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.82.0"
|
||||
components = ["rustfmt", "llvm-tools-preview"]
|
||||
7
src/bit_string/mod.rs
Normal file
7
src/bit_string/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod thumbv6m;
|
||||
|
||||
pub trait Sender {
|
||||
fn send(&self, value: u32);
|
||||
}
|
||||
|
||||
pub use thumbv6m::Msb2LsbSender;
|
||||
161
src/bit_string/thumbv6m.rs
Normal file
161
src/bit_string/thumbv6m.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
use super::Sender;
|
||||
use core::arch::asm;
|
||||
|
||||
pub struct Msb2LsbSender<
|
||||
const BITS: u32,
|
||||
const SHIFT: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_A_ADDRESS: u32,
|
||||
const PIN_LEVEL_B_ADDRESS: u32,
|
||||
const T0A: u32,
|
||||
const T0B: u32,
|
||||
const T1A: u32,
|
||||
const T1B: u32,
|
||||
> {}
|
||||
|
||||
impl<
|
||||
const BITS: u32,
|
||||
const SHIFT: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_A_ADDRESS: u32,
|
||||
const PIN_LEVEL_B_ADDRESS: u32,
|
||||
const T0A: u32,
|
||||
const T0B: u32,
|
||||
const T1A: u32,
|
||||
const T1B: u32,
|
||||
>
|
||||
Msb2LsbSender<
|
||||
BITS,
|
||||
SHIFT,
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_A_ADDRESS,
|
||||
PIN_LEVEL_B_ADDRESS,
|
||||
T0A,
|
||||
T0B,
|
||||
T1A,
|
||||
T1B,
|
||||
>
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Msb2LsbSender {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
const BITS: u32,
|
||||
const SHIFT: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_A_ADDRESS: u32,
|
||||
const PIN_LEVEL_B_ADDRESS: u32,
|
||||
const T0A: u32,
|
||||
const T0B: u32,
|
||||
const T1A: u32,
|
||||
const T1B: u32,
|
||||
> Sender
|
||||
for Msb2LsbSender<
|
||||
BITS,
|
||||
SHIFT,
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_A_ADDRESS,
|
||||
PIN_LEVEL_B_ADDRESS,
|
||||
T0A,
|
||||
T0B,
|
||||
T1A,
|
||||
T1B,
|
||||
>
|
||||
{
|
||||
// To send
|
||||
// Send a string of bit, stored in an u32. first shift by SHIFT to place the first bit in the MSB
|
||||
// position, then transmit BITS bits, from the most to the least significant one.
|
||||
// PIN_MASK is the mask to use to write in PIN_START_ADDRESS, and PIN_STOP_ADDRESS to toggle the pin.
|
||||
// T0A, T0B, T1A, T1B are the number of cycles to wait after writing to A START, and B STOP address.
|
||||
// At a minimum there is always a 3cycle-delay, and due to rounding there might be a +/- 1 cycle
|
||||
// difference for non-multiple of 3.
|
||||
fn send(&self, value: u32) {
|
||||
unsafe {
|
||||
let mut primask: u32;
|
||||
asm!(
|
||||
// Save interrupt mask and disable interrupts for each bit
|
||||
// We do this outside of the bit sending loop as it allows callers of the function to tune
|
||||
// how long the interrupts are disabled by setting the BITS type constant.
|
||||
" mrs {primask}, PRIMASK",
|
||||
" movs {tmp}, #1",
|
||||
" orrs {tmp}, {primask}",
|
||||
" msr PRIMASK, {tmp}",
|
||||
primask = out(reg) primask,
|
||||
tmp = out(reg) _,
|
||||
);
|
||||
|
||||
asm!(
|
||||
"3:",
|
||||
// reload pin mask
|
||||
" movs {tmp}, #{pin_mask}",
|
||||
// shift left and update carry flag
|
||||
" lsls {value}, {value}, #1",
|
||||
// send a 0 or a 1 based on the carry flag value
|
||||
" bcs 1f",
|
||||
" nop",
|
||||
" movs {delay1}, #{t0a}",
|
||||
" movs {delay2}, #{t0b}",
|
||||
" b 2f",
|
||||
"1: ",
|
||||
" movs {delay1}, #{t1a}",
|
||||
" movs {delay2}, #{t1b}",
|
||||
" b 2f",
|
||||
"2: ",
|
||||
|
||||
// set or clear pin
|
||||
" str {tmp}, [{pin_start_address}]",
|
||||
// wait first delay
|
||||
"1:",
|
||||
" subs {delay1}, #1",
|
||||
" bgt 1b",
|
||||
// toggle pin
|
||||
" str {tmp}, [{pin_stop_address}]",
|
||||
// wait second delay
|
||||
"1:",
|
||||
" subs {delay2}, #1",
|
||||
" bgt 1b",
|
||||
|
||||
// loop back to send next bit
|
||||
" subs {bit}, #1",
|
||||
" bgt 3b",
|
||||
|
||||
// A loop is 3 cycles, and the index is predecremented, so make sure it is at least 1.
|
||||
// We also add 1 so that the integer division is at most off by one.
|
||||
t0a = const match T0A {
|
||||
0..3 => 1,
|
||||
_ => (T0A + 1) / 3,
|
||||
},
|
||||
t0b = const match T0B {
|
||||
0..3 => 1,
|
||||
_ => (T0A + 1) / 3,
|
||||
},
|
||||
t1a = const match T1A {
|
||||
0..3 => 1,
|
||||
_ => (T1A + 1) / 3,
|
||||
},
|
||||
t1b = const match T1B {
|
||||
0..3 => 1,
|
||||
_ => (T1B + 1) / 3,
|
||||
},
|
||||
pin_mask = const PIN_MASK,
|
||||
pin_start_address = in(reg) PIN_LEVEL_A_ADDRESS,
|
||||
pin_stop_address = in(reg) PIN_LEVEL_B_ADDRESS,
|
||||
// Number of bits to transmit
|
||||
bit = inout(reg) BITS => _,
|
||||
// Place the value in the most significant bits of the register
|
||||
value = inout(reg) value << SHIFT => _,
|
||||
delay1 = out(reg) _,
|
||||
delay2 = out(reg) _,
|
||||
tmp = out(reg) _,
|
||||
);
|
||||
|
||||
asm!(
|
||||
// Restore primask to previous state.
|
||||
" msr PRIMASK, {primask}",
|
||||
primask = in(reg) primask,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/led_display/mod.rs
Normal file
22
src/led_display/mod.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
pub trait PixelDisplay<Position> {
|
||||
fn show(&self);
|
||||
|
||||
fn clear(&mut self);
|
||||
|
||||
fn fill(&mut self, value: u32);
|
||||
|
||||
fn point(&mut self, position: Position, value: u32);
|
||||
|
||||
fn fill_rectangle(&mut self, a: Position, b: Position, value: u32);
|
||||
|
||||
fn fill_circle(&mut self, origin: Position, radius: usize, value: u32);
|
||||
|
||||
fn rectangle(&mut self, bottom_left: Position, upper_right: Position, value: u32);
|
||||
|
||||
fn circle(&mut self, origin: Position, radius: usize, value: u32);
|
||||
|
||||
// fn segment(&mut self, origin: Position, end: Position, value: u32);
|
||||
//
|
||||
// fn copy(&mut self, origin: Position, data: &[[u32]]);
|
||||
|
||||
}
|
||||
52
src/lib.rs
Normal file
52
src/lib.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// #![no_main]
|
||||
#![no_std]
|
||||
//
|
||||
// use cortex_m_semihosting::debug;
|
||||
//
|
||||
// use defmt_rtt as _; // global logger
|
||||
//
|
||||
// // use some_hal as _; // memory layout
|
||||
// use microbit as _;
|
||||
//
|
||||
// use panic_probe as _;
|
||||
//
|
||||
// // same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||
// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||
// #[defmt::panic_handler]
|
||||
// fn panic() -> ! {
|
||||
// cortex_m::asm::udf()
|
||||
// }
|
||||
//
|
||||
// /// Terminates the application and makes a semihosting-capable debug tool exit
|
||||
// /// with status code 0.
|
||||
// pub fn exit() -> ! {
|
||||
// loop {
|
||||
// debug::exit(debug::EXIT_SUCCESS);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Hardfault handler.
|
||||
// ///
|
||||
// /// Terminates the application and makes a semihosting-capable debug tool exit
|
||||
// /// with an error. This seems better than the default, which is to spin in a
|
||||
// /// loop.
|
||||
// #[cortex_m_rt::exception]
|
||||
// unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||
// loop {
|
||||
// debug::exit(debug::EXIT_FAILURE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // defmt-test 0.3.0 has the limitation that this `#[tests]` attribute can only be used
|
||||
// // once within a crate. the module can be in any file but there can only be at most
|
||||
// // one `#[tests]` module in this library crate
|
||||
// #[cfg(test)]
|
||||
// #[defmt_test::tests]
|
||||
// mod unit_tests {
|
||||
// use defmt::assert;
|
||||
//
|
||||
// #[test]
|
||||
// fn it_works() {
|
||||
// assert!(true)
|
||||
// }
|
||||
// }
|
||||
93
src/main.rs
Normal file
93
src/main.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use crate::neo_pixel::CompositeNeoPixelDisplay;
|
||||
use cortex_m::asm;
|
||||
use cortex_m::asm::delay;
|
||||
use defmt_rtt as _;
|
||||
use led_display::PixelDisplay;
|
||||
use microbit::hal::gpio::Level::Low;
|
||||
use microbit::Board;
|
||||
use panic_probe as _;
|
||||
|
||||
mod bit_string;
|
||||
mod led_display;
|
||||
mod neo_pixel;
|
||||
|
||||
// Bit delay, starting always from set HIGH, wait, go to LOW, wait, repeat
|
||||
const T0H_NS: u32 = 250; // 350+/-150 [ns]
|
||||
const T1H_NS: u32 = 900; // 700+/-150 [ns]
|
||||
|
||||
// Latch reset delay
|
||||
const TLL_NS: u32 = 250_000; // 6_000 [ns] min, newer pixels require ~250_000 [ns]
|
||||
|
||||
const T0L_NS: u32 = 900; // 800+/-150 [ns]
|
||||
const T1L_NS: u32 = 600; // 600+/-150 [ns]
|
||||
|
||||
// Rounded down, from 20.8333
|
||||
const NS_PER_CYCLE: u32 = 114; // empirically measured to be about 114ns
|
||||
|
||||
const T0H: u32 = T0H_NS / NS_PER_CYCLE;
|
||||
const T0L: u32 = T0L_NS / NS_PER_CYCLE;
|
||||
const T1H: u32 = T1H_NS / NS_PER_CYCLE;
|
||||
const T1L: u32 = T1L_NS / NS_PER_CYCLE;
|
||||
const TLL: u32 = TLL_NS / NS_PER_CYCLE;
|
||||
|
||||
// TODO RETRIEVE THIS FROM PAC
|
||||
const EDGE_PAD0_MASK: u32 = 1 << 3;
|
||||
const EDGE_PAD0_SET_ADDRESS: u32 = 0x5000_0508; // Address of the SET register
|
||||
const EDGE_PAD0_CLEAR_ADDRESS: u32 = 0x5000_050c; // Address of the toggle register
|
||||
|
||||
fn setup() -> CompositeNeoPixelDisplay<8, 8, 4, 1, 2192, 8, 1342178568, 1342178572, 2, 7, 7, 5> {
|
||||
let board = Board::take().unwrap();
|
||||
let _pin = board.edge.e00.into_push_pull_output(Low);
|
||||
delay(TLL * 2);
|
||||
|
||||
CompositeNeoPixelDisplay::<
|
||||
8,
|
||||
8,
|
||||
4, /*NX*/
|
||||
1, /*NY*/
|
||||
TLL,
|
||||
EDGE_PAD0_MASK,
|
||||
EDGE_PAD0_SET_ADDRESS,
|
||||
EDGE_PAD0_CLEAR_ADDRESS,
|
||||
T0H,
|
||||
T0L,
|
||||
T1H,
|
||||
T1L,
|
||||
>::new()
|
||||
}
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
const SPACING: usize = 32;
|
||||
let mut display = setup();
|
||||
let mut frame = 0;
|
||||
|
||||
loop {
|
||||
display.clear();
|
||||
|
||||
display.vbar_shift_left(0x020000, frame, SPACING);
|
||||
display.vbar_shift_left(0x020000, frame + 1, SPACING);
|
||||
display.vbar_shift_left(0x020000, frame + 2, SPACING);
|
||||
display.vbar_shift_left(0x000200, frame + 3, SPACING);
|
||||
display.vbar_shift_left(0x000200, frame + 4, SPACING);
|
||||
display.vbar_shift_left(0x000002, frame + 5, SPACING);
|
||||
display.fill_circle(
|
||||
display.position((frame + 32 - 4) % { 4 * 8 }, 3),
|
||||
2,
|
||||
0x020000,
|
||||
);
|
||||
display.circle(
|
||||
display.position((frame + 32 - 4) % { 4 * 8 }, 3),
|
||||
3,
|
||||
0x040000,
|
||||
);
|
||||
|
||||
display.show();
|
||||
frame += 1;
|
||||
|
||||
asm::delay(200_000); // TODO: REPLACE WITH TIMER DELAY
|
||||
}
|
||||
}
|
||||
358
src/neo_pixel/mod.rs
Normal file
358
src/neo_pixel/mod.rs
Normal file
@@ -0,0 +1,358 @@
|
||||
use crate::bit_string;
|
||||
use crate::bit_string::Sender;
|
||||
use crate::led_display::PixelDisplay;
|
||||
use cortex_m::asm;
|
||||
use defmt::{warn, Format};
|
||||
|
||||
#[derive(Eq, Format, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Position {
|
||||
x: usize,
|
||||
y: usize,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
fn new(x: usize, y: usize) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
pub type NeoPixelSender<
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_HIGH_ADDRESS: u32,
|
||||
const PIN_LEVEL_LOW_ADDRESS: u32,
|
||||
const DELAY_ZERO_HIGH: u32,
|
||||
const DELAY_ZERO_LOW: u32,
|
||||
const DELAY_ONE_HIGH: u32,
|
||||
const DELAY_ONE_LOW: u32,
|
||||
> = bit_string::Msb2LsbSender<
|
||||
24,
|
||||
8,
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_HIGH_ADDRESS,
|
||||
PIN_LEVEL_LOW_ADDRESS,
|
||||
DELAY_ZERO_HIGH,
|
||||
DELAY_ZERO_LOW,
|
||||
DELAY_ONE_HIGH,
|
||||
DELAY_ONE_LOW,
|
||||
>;
|
||||
|
||||
struct BufferPosition<const X: usize, const Y: usize, const NX: usize, const NY: usize> {
|
||||
row: usize,
|
||||
col: usize,
|
||||
y: usize,
|
||||
x: usize,
|
||||
}
|
||||
|
||||
impl<const X: usize, const Y: usize, const NX: usize, const NY: usize> TryFrom<Option<Position>>
|
||||
for BufferPosition<X, Y, NX, NY>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(position: Option<Position>) -> Result<Self, Self::Error> {
|
||||
if let Some(position) = position {
|
||||
position.try_into()
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const X: usize, const Y: usize, const NX: usize, const NY: usize> TryFrom<Position>
|
||||
for BufferPosition<X, Y, NX, NY>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(position: Position) -> Result<Self, Self::Error> {
|
||||
let col = position.x / X;
|
||||
let row = position.y / Y;
|
||||
|
||||
if col > NX && row > NY {
|
||||
return Err(());
|
||||
}
|
||||
if col > NX {
|
||||
return Err(());
|
||||
}
|
||||
if row > NY {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let x = position.x % X;
|
||||
let y = position.y % Y;
|
||||
Ok(BufferPosition { row, col, y, x })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompositeNeoPixelDisplay<
|
||||
const X: usize,
|
||||
const Y: usize,
|
||||
const NX: usize,
|
||||
const NY: usize,
|
||||
const DELAY_RESET: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_HIGH_ADDRESS: u32,
|
||||
const PIN_LEVEL_LOW_ADDRESS: u32,
|
||||
const DELAY_ZERO_HIGH: u32,
|
||||
const DELAY_ZERO_LOW: u32,
|
||||
const DELAY_ONE_HIGH: u32,
|
||||
const DELAY_ONE_LOW: u32,
|
||||
> {
|
||||
buffer: [[[[u32; X]; Y]; NX]; NY],
|
||||
sender: NeoPixelSender<
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_HIGH_ADDRESS,
|
||||
PIN_LEVEL_LOW_ADDRESS,
|
||||
DELAY_ZERO_HIGH,
|
||||
DELAY_ZERO_LOW,
|
||||
DELAY_ONE_HIGH,
|
||||
DELAY_ONE_LOW,
|
||||
>,
|
||||
}
|
||||
|
||||
impl<
|
||||
const X: usize,
|
||||
const Y: usize,
|
||||
const NX: usize,
|
||||
const NY: usize,
|
||||
const DELAY_RESET: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_HIGH_ADDRESS: u32,
|
||||
const PIN_LEVEL_LOW_ADDRESS: u32,
|
||||
const DELAY_ZERO_HIGH: u32,
|
||||
const DELAY_ZERO_LOW: u32,
|
||||
const DELAY_ONE_HIGH: u32,
|
||||
const DELAY_ONE_LOW: u32,
|
||||
>
|
||||
CompositeNeoPixelDisplay<
|
||||
X,
|
||||
Y,
|
||||
NX,
|
||||
NY,
|
||||
DELAY_RESET,
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_HIGH_ADDRESS,
|
||||
PIN_LEVEL_LOW_ADDRESS,
|
||||
DELAY_ZERO_HIGH,
|
||||
DELAY_ZERO_LOW,
|
||||
DELAY_ONE_HIGH,
|
||||
DELAY_ONE_LOW,
|
||||
>
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
let display = Self {
|
||||
buffer: [[[[0x0; X]; Y]; NX]; NY],
|
||||
sender: NeoPixelSender::new(),
|
||||
};
|
||||
|
||||
display.show();
|
||||
display
|
||||
}
|
||||
|
||||
pub fn position(&self, x: usize, y: usize) -> Option<Position> {
|
||||
if (x < { X * NX }) && (y < { Y * NY }) {
|
||||
Some(Position::new(x, y))
|
||||
} else {
|
||||
warn!(
|
||||
"Position({},{}) is outside the display range [(0,0);({},{})[.",
|
||||
x,
|
||||
y,
|
||||
{ X * NX },
|
||||
{ Y * NY },
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// funzies
|
||||
pub fn vbar_shift_left(&mut self, color: u32, offset: usize, spacing: usize) {
|
||||
for i in 0..(X * Y * NX * NY) {
|
||||
if (i % spacing) == 0 {
|
||||
self.point(self.position((i + offset) % (X * NX), i / (X * NX)), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
const X: usize,
|
||||
const Y: usize,
|
||||
const NX: usize,
|
||||
const NY: usize,
|
||||
const DELAY_RESET: u32,
|
||||
const PIN_MASK: u32,
|
||||
const PIN_LEVEL_HIGH_ADDRESS: u32,
|
||||
const PIN_LEVEL_LOW_ADDRESS: u32,
|
||||
const DELAY_ZERO_HIGH: u32,
|
||||
const DELAY_ZERO_LOW: u32,
|
||||
const DELAY_ONE_HIGH: u32,
|
||||
const DELAY_ONE_LOW: u32,
|
||||
> PixelDisplay<Option<Position>>
|
||||
for CompositeNeoPixelDisplay<
|
||||
X,
|
||||
Y,
|
||||
NX,
|
||||
NY,
|
||||
DELAY_RESET,
|
||||
PIN_MASK,
|
||||
PIN_LEVEL_HIGH_ADDRESS,
|
||||
PIN_LEVEL_LOW_ADDRESS,
|
||||
DELAY_ZERO_HIGH,
|
||||
DELAY_ZERO_LOW,
|
||||
DELAY_ONE_HIGH,
|
||||
DELAY_ONE_LOW,
|
||||
>
|
||||
{
|
||||
fn show(&self) {
|
||||
for r in 0..NY {
|
||||
for c in 0..NX {
|
||||
for y in 0..Y {
|
||||
for x in 0..X {
|
||||
self.sender.send(self.buffer[r][c][y][x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asm::delay(DELAY_RESET);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.fill(0x0);
|
||||
}
|
||||
|
||||
fn fill(&mut self, value: u32) {
|
||||
self.fill_rectangle(
|
||||
self.position(0, 0),
|
||||
self.position(X * NX - 1, Y * NY - 1),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
fn point(&mut self, position: Option<Position>, value: u32) {
|
||||
if let Ok(BufferPosition::<X, Y, NX, NY> { row, col, y, x }) = position.try_into() {
|
||||
self.buffer[row][col][y][x] = value;
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_rectangle(&mut self, a: Option<Position>, b: Option<Position>, value: u32) {
|
||||
if let Some(a) = a {
|
||||
if let Some(b) = b {
|
||||
let bottom_left;
|
||||
let upper_right;
|
||||
if a < b {
|
||||
bottom_left = a;
|
||||
upper_right = b;
|
||||
} else {
|
||||
bottom_left = b;
|
||||
upper_right = a;
|
||||
}
|
||||
let Position { x: x0, y: y0 } = bottom_left;
|
||||
let Position { x: x1, y: y1 } = upper_right;
|
||||
|
||||
for y in y0..=y1 {
|
||||
for x in x0..=x1 {
|
||||
self.point(self.position(x, y), value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_circle(&mut self, origin: Option<Position>, radius: usize, value: u32) {
|
||||
if let Some(origin) = origin {
|
||||
let Position { x: cx, y: cy } = origin;
|
||||
let y0 = if cy < radius { 0 } else { cy - radius };
|
||||
let x0 = if cx < radius { 0 } else { cx - radius };
|
||||
|
||||
for y in y0..=(cy + radius) {
|
||||
for x in x0..=(cx + radius) {
|
||||
let dx = if x < cx { cx - x } else { x - cx };
|
||||
let dy = if y < cy { cy - y } else { y - cy };
|
||||
let h = dx * dx + dy * dy;
|
||||
let epsilon = (radius * 2) + 1 / radius + 1; // todo simplify or tune some more
|
||||
if h <= radius + epsilon {
|
||||
self.point(self.position(x, y), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fn segment(&mut self, a: Option<Position>, b: Option<Position>, value: u32) {
|
||||
// if let Some(a) = &a {
|
||||
// if let Some(b) = &b {
|
||||
// let start = if a < b { a } else { b };
|
||||
// let end = if a < b { b } else { a };
|
||||
// let dx = end.x - start.x;
|
||||
// let dy = end.y - start.y;
|
||||
// for x in start.x..=end.x {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn copy(&mut self, origin: Option<Position>, data: &[[u32]]) {
|
||||
// if let Some(origin) = origin {
|
||||
// let Position { x: x0, y: y0 } = origin;
|
||||
// let mut iy = 0;
|
||||
// let mut ix = 0;
|
||||
// for y in data.iter() {
|
||||
// ix = 0;
|
||||
// for x in y.iter() {
|
||||
// self.point(self.position(ix, iy), *x);
|
||||
// ix += 1;
|
||||
// }
|
||||
// iy += 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn rectangle(&mut self, a: Option<Position>, b: Option<Position>, value: u32) {
|
||||
if let Some(a) = a {
|
||||
if let Some(b) = b {
|
||||
let bottom_left;
|
||||
let upper_right;
|
||||
if a < b {
|
||||
bottom_left = a;
|
||||
upper_right = b;
|
||||
} else {
|
||||
bottom_left = b;
|
||||
upper_right = a;
|
||||
}
|
||||
let Position { x: x0, y: y0 } = bottom_left;
|
||||
let Position { x: x1, y: y1 } = upper_right;
|
||||
|
||||
for y in y0..=y1 {
|
||||
if (y != y0) && (y != y1) {
|
||||
continue;
|
||||
}
|
||||
for x in x0..=x1 {
|
||||
if (x != x0) && (x != x1) {
|
||||
continue;
|
||||
}
|
||||
self.point(self.position(x, y), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn circle(&mut self, origin: Option<Position>, radius: usize, value: u32) {
|
||||
if let Some(origin) = origin {
|
||||
let Position { x: cx, y: cy } = origin;
|
||||
let y0 = if cy < radius { 0 } else { cy - radius };
|
||||
let x0 = if cx < radius { 0 } else { cx - radius };
|
||||
|
||||
for y in y0..=(cy + radius) {
|
||||
for x in x0..=(cx + radius) {
|
||||
let dx = if x < cx { cx - x } else { x - cx };
|
||||
let dy = if y < cy { cy - y } else { y - cy };
|
||||
let r2 = radius * radius;
|
||||
let h = dx * dx + dy * dy;
|
||||
let d = if r2 < h { h - r2 } else { r2 - h };
|
||||
let epsilon = (radius * 2 + 1) / radius + 1;
|
||||
if d < epsilon {
|
||||
// we use a larger epsilon to have a full line
|
||||
self.point(self.position(x, y), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
tests/integration.rs
Normal file
16
tests/integration.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use template_microbit as _; // memory layout + panic handler
|
||||
|
||||
// See https://crates.io/crates/defmt-test/0.3.0 for more documentation (e.g. about the 'state'
|
||||
// feature)
|
||||
#[defmt_test::tests]
|
||||
mod tests {
|
||||
use defmt::assert;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert!(true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user