Merge pull request 'New VA108xx Rust workspace structure + dependency updates' (#1) from update-va108xx into main
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
Some checks failed
Rust/va108xx-rs/pipeline/head There was a failure building this commit
Reviewed-on: #1
This commit is contained in:
commit
42c7f0d3a2
1
.cargo/.gitignore
vendored
Normal file
1
.cargo/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
config.toml
|
48
.cargo/def-config.toml
Normal file
48
.cargo/def-config.toml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# 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"
|
||||||
|
runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
|
||||||
|
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
|
||||||
|
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
|
||||||
|
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
|
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
||||||
|
# "-C", "linker=flip-link",
|
||||||
|
# Unfortunately, defmt is clunky to use without probe-rs..
|
||||||
|
# "-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# Can be useful for debugging.
|
||||||
|
"-Clink-args=-Map=app.map"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Pick ONE of these 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)
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
re = "run --example"
|
||||||
|
rb = "run --bin"
|
||||||
|
rrb = "run --release --bin"
|
||||||
|
ut = "test --target x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
49
.github/workflows/ci.yml
vendored
Normal file
49
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: ci
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv6m-none-eabi"
|
||||||
|
- run: cargo check --release
|
||||||
|
- run: cargo check --examples --release
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install nextest
|
||||||
|
uses: taiki-e/install-action@nextest
|
||||||
|
- run: cargo nextest run --all-features
|
||||||
|
- run: cargo test --doc
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo clippy -- -D warnings
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -6,11 +6,12 @@
|
|||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
||||||
|
/app.map
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
/.vscode/.cortex-debug.*
|
/.vscode
|
||||||
/.vscode/settings.json
|
|
||||||
|
|
||||||
# JetBrains IDEs
|
# JetBrains IDEs
|
||||||
/.idea
|
/.idea
|
||||||
|
15
.gitmodules
vendored
15
.gitmodules
vendored
@ -1,15 +0,0 @@
|
|||||||
[submodule "va108xx-rs"]
|
|
||||||
path = va108xx
|
|
||||||
url = https://egit.irs.uni-stuttgart.de/rust/va108xx
|
|
||||||
[submodule "vorago-reb1-rs"]
|
|
||||||
path = vorago-reb1
|
|
||||||
url = https://egit.irs.uni-stuttgart.de/rust/vorago-reb1
|
|
||||||
[submodule "va108xx-hal-rs"]
|
|
||||||
path = va108xx-hal
|
|
||||||
url = https://egit.irs.uni-stuttgart.de/rust/va108xx-hal.git
|
|
||||||
[submodule "adt75-rs"]
|
|
||||||
path = adt75-rs
|
|
||||||
url = https://egit.irs.uni-stuttgart.de/rust/adt75-rs.git
|
|
||||||
[submodule "max116xx-10bit"]
|
|
||||||
path = max116xx-10bit
|
|
||||||
url = https://egit.irs.uni-stuttgart.de/rust/max116xx-10bit.git
|
|
33
Cargo.toml
33
Cargo.toml
@ -1,22 +1,31 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"vorago-reb1",
|
"vorago-reb1",
|
||||||
"va108xx",
|
"va108xx",
|
||||||
"va108xx-hal",
|
"va108xx-hal",
|
||||||
"max116xx-10bit",
|
"examples/simple",
|
||||||
|
"board-tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
"defmt-testapp",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
lto = false
|
codegen-units = 1
|
||||||
debug = true
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# Can be problematic for debugging and is definitely problematic with RTT
|
codegen-units = 1
|
||||||
lto = false
|
debug = 2
|
||||||
debug = true
|
debug-assertions = false # <-
|
||||||
opt-level = 's'
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
[profile.release-lto]
|
opt-level = 3 # <-
|
||||||
inherits = "release"
|
overflow-checks = false # <-
|
||||||
lto = true
|
|
||||||
|
99
README.md
99
README.md
@ -1,37 +1,102 @@
|
|||||||
Vorago Rust Workspace
|
Vorago VA108xx Rust Support
|
||||||
========
|
=========
|
||||||
|
|
||||||
Workspace for developing Rust code for the Vorago devices
|
This crate collection provided support to write Rust applications for the VA108XX family
|
||||||
|
of devices.
|
||||||
|
|
||||||
After cloning, run
|
## List of crates
|
||||||
|
|
||||||
|
This workspace contains the following released crates:
|
||||||
|
|
||||||
|
- The [`va108xx`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx) PAC
|
||||||
|
crate containing basic low-level register definition.
|
||||||
|
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
|
||||||
|
HAL crate containing higher-level abstractions on top of the PAC register crate.
|
||||||
|
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
|
||||||
|
BSP crate containing support for the REB1 development board.
|
||||||
|
|
||||||
|
It also contains the following helper crates:
|
||||||
|
|
||||||
|
- The `board-tests` contains an application which can be used to test the libraries on the
|
||||||
|
board.
|
||||||
|
- The `examples` crates contains various example applications for the HAL and the PAC.
|
||||||
|
|
||||||
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
|
Use the following command to have a starting `config.toml` file
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git submodule update --init
|
cp .cargo/def-config.toml .cargo/config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
# Preparing the Rust installation
|
You then can adapt the `config.toml` to your needs. For example, you can configure runners
|
||||||
|
to conveniently flash with `cargo run`.
|
||||||
|
|
||||||
|
## Using the sample VS Code files
|
||||||
|
|
||||||
Building an application for the VA108XX family requires the `thumbv6m-none-eabi`
|
Use the following command to have a starting configuration for VS Code:
|
||||||
cross-compiler toolchain. If you have not installed it yet, you can do so with
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rustup target add thumbv6m-none-eabi
|
cp -rT vscode .vscode
|
||||||
```
|
```
|
||||||
|
|
||||||
# Debugging with VS Code
|
You can then adapt the files in `.vscode` to your needs.
|
||||||
|
|
||||||
The REB1 board features an on-board JTAG, so all that is required to flash the board is a
|
## Flashing, running and debugging the software
|
||||||
Micro-USB cable and an
|
|
||||||
You can debug applications on the REB1 board with a graphical user interface using VS Code with
|
You can use CLI or VS Code for flashing, running and debugging. In any case, take
|
||||||
|
care of installing the pre-requisites first.
|
||||||
|
|
||||||
|
### Pre-Requisites
|
||||||
|
|
||||||
|
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||||
|
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||||
|
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
||||||
|
|
||||||
|
### Using CLI
|
||||||
|
|
||||||
|
You can build the blinky example application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the GDB server first. The server needs to be started with a certain configuration and with
|
||||||
|
a JLink script to disable ROM protection.
|
||||||
|
For example, on Debian based system the following command can be used to do this (this command
|
||||||
|
is also run when running the `jlink-gdb.sh` script)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG-speed auto \
|
||||||
|
-LocalhostOnly
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, you can flash and debug the application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that you can automate all steps except starting the GDB server by using a cargo
|
||||||
|
runner configuration, for example with the following lines in your `.cargo/config.toml` file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "gdb-multiarch -q -x jlink/jlink.gdb"
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can simply use `cargo run --example blinky` to flash the blinky
|
||||||
|
example.
|
||||||
|
|
||||||
|
### Using VS Code
|
||||||
|
|
||||||
|
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||||
|
|
||||||
Some sample configuration files for VS code were provided as well. You can simply use `Run and Debug`
|
Some sample configuration files for VS code were provided and can be used by running
|
||||||
|
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||||
to automatically rebuild and flash your application.
|
to automatically rebuild and flash your application.
|
||||||
|
|
||||||
The `tasks.json` and the `launch.json` files are generic and you can use them immediately by
|
|
||||||
opening the folder in VS code or adding it to a workspace.
|
|
||||||
|
|
||||||
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
||||||
configuration variables in your `settings.json`:
|
configuration variables in your `settings.json`:
|
||||||
|
|
||||||
|
1
adt75-rs
1
adt75-rs
@ -1 +0,0 @@
|
|||||||
Subproject commit 8b7121eb4771c2537c6123f632d20fbfb2ba3657
|
|
13
automation/Dockerfile
Normal file
13
automation/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Run the following commands from root directory to build and run locally
|
||||||
|
# docker build -f automation/Dockerfile -t <NAME> .
|
||||||
|
# docker run -it <NAME>
|
||||||
|
FROM rust:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get --yes upgrade
|
||||||
|
# tzdata is a dependency, won't install otherwise
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN rustup install nightly && \
|
||||||
|
rustup target add thumbv6m-none-eabi && \
|
||||||
|
rustup +nightly target add thumbv6m-none-eabi && \
|
||||||
|
rustup component add rustfmt clippy
|
41
automation/Jenkinsfile
vendored
Normal file
41
automation/Jenkinsfile
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Rust Toolchain Info') {
|
||||||
|
steps {
|
||||||
|
sh 'rustc --version'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Clippy') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo clippy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Rustfmt') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo fmt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
sh: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Examples') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi --examples'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
board-tests/Cargo.toml
Normal file
20
board-tests/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "board-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m-rtic = "1"
|
||||||
|
panic-halt = "0.2"
|
||||||
|
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
rtt-target = "0.5"
|
||||||
|
panic-rtt-target = "0.1.3"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.6"
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
features = ["rt"]
|
207
board-tests/src/main.rs
Normal file
207
board-tests/src/main.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
//! Test image
|
||||||
|
//!
|
||||||
|
//! It would be nice to use a test framework like defmt-test, but I have issues
|
||||||
|
//! with probe run and it would be better to make the RTT work first
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{InputPin, OutputPin, StatefulOutputPin},
|
||||||
|
};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{PinState, PinsA, PinsB},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer, IrqCfg},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TestCase {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
TestBasic,
|
||||||
|
TestPullup,
|
||||||
|
TestPulldown,
|
||||||
|
TestMask,
|
||||||
|
// Tie PORTB[22] to PORTB[23] for this test
|
||||||
|
PortB,
|
||||||
|
Perid,
|
||||||
|
// Tie PA0 to an oscilloscope and configure pulse detection
|
||||||
|
Pulse,
|
||||||
|
// Tie PA0, PA1 and PA3 to an oscilloscope
|
||||||
|
DelayGpio,
|
||||||
|
DelayMs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Test Application --");
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||||
|
let mut led1 = pinsa.pa10.into_readable_push_pull_output();
|
||||||
|
let test_case = TestCase::DelayMs;
|
||||||
|
|
||||||
|
match test_case {
|
||||||
|
TestCase::TestBasic
|
||||||
|
| TestCase::TestPulldown
|
||||||
|
| TestCase::TestPullup
|
||||||
|
| TestCase::TestMask => {
|
||||||
|
rprintln!(
|
||||||
|
"Test case {:?}. Make sure to tie PORTA[0] to PORTA[1]",
|
||||||
|
test_case
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
rprintln!("Test case {:?}", test_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match test_case {
|
||||||
|
TestCase::TestBasic => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
|
let mut input = pinsa.pa1.into_floating_input();
|
||||||
|
out.set_high().unwrap();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
out.set_low().unwrap();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
}
|
||||||
|
TestCase::TestPullup => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let mut input = pinsa.pa1.into_pull_up_input();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
|
out.set_low().unwrap();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
out.set_high().unwrap();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
out.into_floating_input();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
}
|
||||||
|
TestCase::TestPulldown => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let mut input = pinsa.pa1.into_pull_down_input();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
|
out.set_low().unwrap();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
out.set_high().unwrap();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
out.into_floating_input();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
}
|
||||||
|
TestCase::TestMask => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let input = pinsa.pa1.into_pull_down_input().clear_datamask();
|
||||||
|
assert!(!input.datamask());
|
||||||
|
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
||||||
|
assert!(input.is_low_masked().is_err());
|
||||||
|
assert!(out.set_high_masked().is_err());
|
||||||
|
}
|
||||||
|
TestCase::PortB => {
|
||||||
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
|
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||||
|
let mut input = pinsb.pb23.into_floating_input();
|
||||||
|
out.set_high().unwrap();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
out.set_low().unwrap();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
|
}
|
||||||
|
TestCase::Perid => {
|
||||||
|
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||||
|
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||||
|
}
|
||||||
|
TestCase::Pulse => {
|
||||||
|
let mut output_pulsed = pinsa
|
||||||
|
.pa0
|
||||||
|
.into_push_pull_output()
|
||||||
|
.pulse_mode(true, PinState::Low);
|
||||||
|
rprintln!("Pulsing high 10 times..");
|
||||||
|
output_pulsed.set_low().unwrap();
|
||||||
|
for _ in 0..10 {
|
||||||
|
output_pulsed.set_high().unwrap();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High);
|
||||||
|
rprintln!("Pulsing low 10 times..");
|
||||||
|
for _ in 0..10 {
|
||||||
|
output_pulsed.set_low().unwrap();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestCase::DelayGpio => {
|
||||||
|
let mut out_0 = pinsa
|
||||||
|
.pa0
|
||||||
|
.into_readable_push_pull_output()
|
||||||
|
.delay(true, false);
|
||||||
|
let mut out_1 = pinsa
|
||||||
|
.pa1
|
||||||
|
.into_readable_push_pull_output()
|
||||||
|
.delay(false, true);
|
||||||
|
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
|
||||||
|
for _ in 0..20 {
|
||||||
|
out_0.toggle().unwrap();
|
||||||
|
out_1.toggle().unwrap();
|
||||||
|
out_2.toggle().unwrap();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestCase::DelayMs => {
|
||||||
|
let mut ms_timer = set_up_ms_tick(
|
||||||
|
IrqCfg::new(pac::Interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle().ok();
|
||||||
|
ms_timer.delay_ms(500);
|
||||||
|
led1.toggle().ok();
|
||||||
|
ms_timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut delay_timer = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
|
let mut pa0 = pinsa.pa0.into_readable_push_pull_output();
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay_timer.delay_ms(500);
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay_timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
let ahb_freq: Hertz = 50.MHz();
|
||||||
|
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.raw());
|
||||||
|
// Test usecond delay using both TIM peripheral and SYST. Use the release image if you
|
||||||
|
// want to verify the timings!
|
||||||
|
loop {
|
||||||
|
pa0.toggle().ok();
|
||||||
|
delay_timer.delay_us(50);
|
||||||
|
pa0.toggle().ok();
|
||||||
|
delay_timer.delay_us(50);
|
||||||
|
pa0.toggle_with_toggle_reg();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
pa0.toggle_with_toggle_reg();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rprintln!("Test success");
|
||||||
|
loop {
|
||||||
|
led1.toggle().ok();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
48
defmt-testapp/.cargo/config.toml
Normal file
48
defmt-testapp/.cargo/config.toml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# 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"
|
||||||
|
runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
|
||||||
|
# Probe-rs is currently problematic: https://github.com/probe-rs/probe-rs/issues/2567
|
||||||
|
# runner = "probe-rs run --chip VA108xx --chip-description-path ./scripts/VA108xx_Series.yaml"
|
||||||
|
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
|
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
# Unfortunately, defmt is clunky to use without probe-rs..
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# Can be useful for debugging.
|
||||||
|
"-Clink-args=-Map=app.map"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Pick ONE of these 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)
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
re = "run --example"
|
||||||
|
rb = "run --bin"
|
||||||
|
rrb = "run --release --bin"
|
||||||
|
ut = "test --target x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
1
defmt-testapp/.gitignore
vendored
Normal file
1
defmt-testapp/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
36
defmt-testapp/Cargo.toml
Normal file
36
defmt-testapp/Cargo.toml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
[package]
|
||||||
|
name = "defmt-testapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
panic-rtt-target = "0.1"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
rtt-target = "0.5"
|
||||||
|
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
# Tricky without probe-rs.
|
||||||
|
defmt = "0.3"
|
||||||
|
defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||||
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
|
||||||
|
[dependencies.rtic]
|
||||||
|
version = "2"
|
||||||
|
features = ["thumbv6-backend"]
|
||||||
|
|
||||||
|
[dependencies.rtic-monotonics]
|
||||||
|
version = "1"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.6"
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
|
[dependencies.va108xx]
|
||||||
|
version = "0.3"
|
||||||
|
path = "../va108xx"
|
9
defmt-testapp/README.md
Normal file
9
defmt-testapp/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
defmt Testapp
|
||||||
|
======
|
||||||
|
|
||||||
|
`defmt` is clunky to use without probe-rs and requires special configuration inside the
|
||||||
|
`.cargo/config.toml` file.
|
||||||
|
|
||||||
|
`probe-rs` is currently problematic for usage with the VA108xx , so it is not the default tool
|
||||||
|
recommended and used for the whole workspace. This project contains an isolated, `defmt` compatible
|
||||||
|
configuration for testing with `defmt` (and `probe-rs`).
|
53
defmt-testapp/src/lib.rs
Normal file
53
defmt-testapp/src/lib.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_semihosting::debug;
|
||||||
|
|
||||||
|
use defmt_brtt as _; // global logger
|
||||||
|
|
||||||
|
use va108xx_hal as _; // memory layout
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
29
defmt-testapp/src/main.rs
Normal file
29
defmt-testapp/src/main.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//! Empty RTIC project template
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use defmt_testapp as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac)]
|
||||||
|
mod app {
|
||||||
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago RTIC template --");
|
||||||
|
(Shared {}, Local {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
37
examples/simple/Cargo.toml
Normal file
37
examples/simple/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[package]
|
||||||
|
name = "simple-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
panic-halt = "0.2"
|
||||||
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
panic-rtt-target = "0.1"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
rtt-target = "0.5"
|
||||||
|
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
cortex-m-semihosting = "0.5.0"
|
||||||
|
# I'd really like to use those, but it is tricky without probe-rs..
|
||||||
|
# defmt = "0.3"
|
||||||
|
# defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] }
|
||||||
|
# panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
|
|
||||||
|
[dependencies.rtic]
|
||||||
|
version = "2"
|
||||||
|
features = ["thumbv6-backend"]
|
||||||
|
|
||||||
|
[dependencies.rtic-monotonics]
|
||||||
|
version = "1"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.6"
|
||||||
|
path = "../../va108xx-hal"
|
||||||
|
features = ["rt", "defmt"]
|
||||||
|
|
||||||
|
[dependencies.va108xx]
|
||||||
|
version = "0.3"
|
||||||
|
path = "../../va108xx"
|
47
examples/simple/examples/blinky-pac.rs
Normal file
47
examples/simple/examples/blinky-pac.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//! Blinky examples using only the PAC
|
||||||
|
//!
|
||||||
|
//! Additional note on LEDs:
|
||||||
|
//! Pulling the GPIOs low makes the LEDs blink. See REB1
|
||||||
|
//! schematic for more details.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_halt as _;
|
||||||
|
use va108xx as pac;
|
||||||
|
|
||||||
|
// REB LED pin definitions. All on port A
|
||||||
|
const LED_D2: u32 = 1 << 10;
|
||||||
|
const LED_D3: u32 = 1 << 7;
|
||||||
|
const LED_D4: u32 = 1 << 6;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
// Enable all peripheral clocks
|
||||||
|
dp.sysconfig
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
||||||
|
dp.porta
|
||||||
|
.dir()
|
||||||
|
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
dp.porta
|
||||||
|
.datamask()
|
||||||
|
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
for _ in 0..10 {
|
||||||
|
dp.porta
|
||||||
|
.clrout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(5_000_000);
|
||||||
|
dp.porta
|
||||||
|
.setout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(5_000_000);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
dp.porta
|
||||||
|
.togout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
64
examples/simple/examples/blinky.rs
Normal file
64
examples/simple/examples/blinky.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//! Simple blinky example
|
||||||
|
//!
|
||||||
|
//! Additional note on LEDs when using the REB1 development board:
|
||||||
|
//! Be not afraid: Pulling the GPIOs low makes the LEDs blink. See REB1
|
||||||
|
//! schematic for more details.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
digital::{OutputPin, StatefulOutputPin},
|
||||||
|
};
|
||||||
|
use panic_halt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
pwm::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer},
|
||||||
|
timer::DelayMs,
|
||||||
|
IrqCfg,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay_ms = DelayMs::new(set_up_ms_tick(
|
||||||
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
let mut delay_tim1 = CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
|
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
let mut led3 = porta.pa6.into_readable_push_pull_output();
|
||||||
|
for _ in 0..10 {
|
||||||
|
led1.set_low().ok();
|
||||||
|
led2.set_low().ok();
|
||||||
|
led3.set_low().ok();
|
||||||
|
delay_ms.delay_ms(200);
|
||||||
|
led1.set_high().ok();
|
||||||
|
led2.set_high().ok();
|
||||||
|
led3.set_high().ok();
|
||||||
|
delay_tim1.delay_ms(200);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
led1.toggle().ok();
|
||||||
|
delay_ms.delay_ms(200);
|
||||||
|
led2.toggle().ok();
|
||||||
|
delay_tim1.delay_ms(200);
|
||||||
|
led3.toggle().ok();
|
||||||
|
delay_ms.delay_ms(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
142
examples/simple/examples/cascade.rs
Normal file
142
examples/simple/examples/cascade.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//! Simple Cascade example
|
||||||
|
//!
|
||||||
|
//! A timer will be periodically started which starts another timer via the cascade feature.
|
||||||
|
//! This timer will then start another timer with the cascade feature as well.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
timer::{
|
||||||
|
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
|
||||||
|
CountDownTimer, Event, IrqCfg,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static CSD_TGT_1: Mutex<RefCell<Option<CountDownTimer<pac::Tim4>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
static CSD_TGT_2: Mutex<RefCell<Option<CountDownTimer<pac::Tim5>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Cascade example application--");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
|
||||||
|
// Will be started periodically to trigger a cascade
|
||||||
|
let mut cascade_triggerer =
|
||||||
|
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
|
||||||
|
cascade_triggerer.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
IrqCfg::new(va108xx::Interrupt::OC1, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
|
|
||||||
|
// First target for cascade
|
||||||
|
let mut cascade_target_1 =
|
||||||
|
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim4).auto_deactivate(true);
|
||||||
|
cascade_target_1
|
||||||
|
.cascade_0_source(CascadeSource::TimBase, Some(3))
|
||||||
|
.expect("Configuring cascade source for TIM4 failed");
|
||||||
|
let mut csd_cfg = CascadeCtrl {
|
||||||
|
enb_start_src_csd0: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
// Use trigger mode here
|
||||||
|
csd_cfg.trg_csd0 = true;
|
||||||
|
cascade_target_1.cascade_control(csd_cfg);
|
||||||
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
|
// register but a full interrupt is use here to display print output when
|
||||||
|
// the timer expires
|
||||||
|
cascade_target_1.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
IrqCfg::new(va108xx::Interrupt::OC2, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
|
// it is okay to call start here to set the reset value
|
||||||
|
cascade_target_1.start(1.Hz());
|
||||||
|
|
||||||
|
// Activated by first cascade target
|
||||||
|
let mut cascade_target_2 =
|
||||||
|
CountDownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim5).auto_deactivate(true);
|
||||||
|
// Set TIM4 as cascade source
|
||||||
|
cascade_target_2
|
||||||
|
.cascade_1_source(CascadeSource::TimBase, Some(4))
|
||||||
|
.expect("Configuring cascade source for TIM5 failed");
|
||||||
|
|
||||||
|
csd_cfg = CascadeCtrl::default();
|
||||||
|
csd_cfg.enb_start_src_csd1 = true;
|
||||||
|
// Use trigger mode here
|
||||||
|
csd_cfg.trg_csd1 = true;
|
||||||
|
cascade_target_2.cascade_control(csd_cfg);
|
||||||
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
|
// register but a full interrupt is use here to display print output when
|
||||||
|
// the timer expires
|
||||||
|
cascade_target_2.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
IrqCfg::new(va108xx::Interrupt::OC3, true, false),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
|
// it is okay to call start here to set the reset value
|
||||||
|
cascade_target_2.start(1.Hz());
|
||||||
|
|
||||||
|
// Unpend all IRQs
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3);
|
||||||
|
}
|
||||||
|
// Make both cascade targets accessible from the IRQ handler with the Mutex dance
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
CSD_TGT_1.borrow(cs).replace(Some(cascade_target_1));
|
||||||
|
CSD_TGT_2.borrow(cs).replace(Some(cascade_target_2));
|
||||||
|
});
|
||||||
|
loop {
|
||||||
|
rprintln!("-- Triggering cascade in 0.5 seconds --");
|
||||||
|
cascade_triggerer.start(2.Hz());
|
||||||
|
delay.delay_ms(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC1() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
rprintln!("{}: Cascade triggered timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC2() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
rprintln!("{}: First cascade target timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC3() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
rprintln!("{}: Second cascade target timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
72
examples/simple/examples/pwm.rs
Normal file
72
examples/simple/examples/pwm.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//! Simple PWM example
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::PinsA,
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
pwm::{self, get_duty_from_percent, PwmA, PwmB, ReducedPwmPin},
|
||||||
|
timer::set_up_ms_delay_provider,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx PWM example application--");
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
|
let mut pwm = pwm::PwmPin::new(
|
||||||
|
(pinsa.pa3.into_funsel_1(), dp.tim3),
|
||||||
|
50.MHz(),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
10.Hz(),
|
||||||
|
);
|
||||||
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
let mut current_duty_cycle = 0.0;
|
||||||
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
pwm.enable();
|
||||||
|
|
||||||
|
// Delete type information, increased code readibility for the rest of the code
|
||||||
|
let mut reduced_pin = ReducedPwmPin::from(pwm);
|
||||||
|
loop {
|
||||||
|
let mut counter = 0;
|
||||||
|
// Increase duty cycle continuously
|
||||||
|
while current_duty_cycle < 1.0 {
|
||||||
|
delay.delay_ms(400);
|
||||||
|
current_duty_cycle += 0.02;
|
||||||
|
counter += 1;
|
||||||
|
if counter % 10 == 0 {
|
||||||
|
rprintln!("current duty cycle: {}", current_duty_cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
reduced_pin
|
||||||
|
.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to PWMB and decrease the window with a high signal from 100 % to 0 %
|
||||||
|
// continously
|
||||||
|
current_duty_cycle = 0.0;
|
||||||
|
let mut upper_limit = 1.0;
|
||||||
|
let mut lower_limit = 0.0;
|
||||||
|
let mut pwmb: ReducedPwmPin<PwmB> = ReducedPwmPin::from(reduced_pin);
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
while lower_limit < 0.5 {
|
||||||
|
delay.delay_ms(400);
|
||||||
|
lower_limit += 0.01;
|
||||||
|
upper_limit -= 0.01;
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
rprintln!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
|
rprintln!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
|
}
|
||||||
|
reduced_pin = ReducedPwmPin::<PwmA>::from(pwmb);
|
||||||
|
}
|
||||||
|
}
|
30
examples/simple/examples/rtic-empty.rs
Normal file
30
examples/simple/examples/rtic-empty.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Empty RTIC project template
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac)]
|
||||||
|
mod app {
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_default};
|
||||||
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||||
|
rtt_init_default!();
|
||||||
|
rprintln!("-- Vorago RTIC template --");
|
||||||
|
(Shared {}, Local {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
19
examples/simple/examples/rtt-log.rs
Normal file
19
examples/simple/examples/rtt-log.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//! Code to test RTT logger functionality
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_halt as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx as _;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
let mut counter = 0;
|
||||||
|
loop {
|
||||||
|
rprintln!("{}: Hello, world!", counter);
|
||||||
|
counter += 1;
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
244
examples/simple/examples/spi.rs
Normal file
244
examples/simple/examples/spi.rs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
//! SPI example application
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
spi::{Mode, SpiBus, MODE_0},
|
||||||
|
};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{PinsA, PinsB},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
pwm::{default_ms_irq_handler, set_up_ms_tick},
|
||||||
|
spi::{self, Spi, SpiBase, TransferConfig},
|
||||||
|
IrqCfg,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum ExampleSelect {
|
||||||
|
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
||||||
|
Loopback,
|
||||||
|
// Send a test buffer and print everything received
|
||||||
|
TestBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum SpiBusSelect {
|
||||||
|
SpiAPortA,
|
||||||
|
SpiAPortB,
|
||||||
|
SpiBPortB,
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback;
|
||||||
|
const SPI_BUS_SEL: SpiBusSelect = SpiBusSelect::SpiBPortB;
|
||||||
|
const SPI_SPEED_KHZ: u32 = 1000;
|
||||||
|
const SPI_MODE: Mode = MODE_0;
|
||||||
|
const BLOCKMODE: bool = true;
|
||||||
|
const FILL_WORD: u8 = 0x0f;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx SPI example application--");
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = set_up_ms_tick(
|
||||||
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
|
||||||
|
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
|
||||||
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
|
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||||
|
|
||||||
|
let mut spi_cfg = spi::SpiConfig::default();
|
||||||
|
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||||
|
spi_cfg = spi_cfg.loopback(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the SPI peripheral
|
||||||
|
match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA => {
|
||||||
|
let (sck, mosi, miso) = (
|
||||||
|
pinsa.pa31.into_funsel_1(),
|
||||||
|
pinsa.pa30.into_funsel_1(),
|
||||||
|
pinsa.pa29.into_funsel_1(),
|
||||||
|
);
|
||||||
|
let mut spia = Spi::spia(
|
||||||
|
dp.spia,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
50.MHz(),
|
||||||
|
spi_cfg,
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
spia.set_fill_word(FILL_WORD);
|
||||||
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiAPortB => {
|
||||||
|
let (sck, mosi, miso) = (
|
||||||
|
pinsb.pb9.into_funsel_2(),
|
||||||
|
pinsb.pb8.into_funsel_2(),
|
||||||
|
pinsb.pb7.into_funsel_2(),
|
||||||
|
);
|
||||||
|
let mut spia = Spi::spia(
|
||||||
|
dp.spia,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
50.MHz(),
|
||||||
|
spi_cfg,
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
spia.set_fill_word(FILL_WORD);
|
||||||
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
let (sck, mosi, miso) = (
|
||||||
|
pinsb.pb5.into_funsel_1(),
|
||||||
|
pinsb.pb4.into_funsel_1(),
|
||||||
|
pinsb.pb3.into_funsel_1(),
|
||||||
|
);
|
||||||
|
let mut spib = Spi::spib(
|
||||||
|
dp.spib,
|
||||||
|
(sck, miso, mosi),
|
||||||
|
50.MHz(),
|
||||||
|
spi_cfg,
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
spib.set_fill_word(FILL_WORD);
|
||||||
|
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Configure transfer specific properties here
|
||||||
|
match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
|
let transfer_cfg =
|
||||||
|
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
|
||||||
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
|
let hw_cs_pin = pinsb.pb2.into_funsel_1();
|
||||||
|
let transfer_cfg = TransferConfig::new(
|
||||||
|
SPI_SPEED_KHZ.kHz(),
|
||||||
|
SPI_MODE,
|
||||||
|
Some(hw_cs_pin),
|
||||||
|
BLOCKMODE,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application logic
|
||||||
|
loop {
|
||||||
|
let mut reply_buf: [u8; 8] = [0; 8];
|
||||||
|
match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
|
if let Some(ref mut spi) = *spia_ref.borrow_mut() {
|
||||||
|
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||||
|
// Can't really verify correct reply here.
|
||||||
|
spi.write(&[0x42]).expect("write failed");
|
||||||
|
// Because of the loopback mode, we should get back the fill word here.
|
||||||
|
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||||
|
assert_eq!(reply_buf[0], FILL_WORD);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
tx_rx_buf[0],
|
||||||
|
tx_rx_buf[1],
|
||||||
|
tx_rx_buf[2]
|
||||||
|
);
|
||||||
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
|
} else {
|
||||||
|
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(1000_u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
if let Some(ref mut spi) = *spib_ref.borrow_mut() {
|
||||||
|
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||||
|
// Can't really verify correct reply here.
|
||||||
|
spi.write(&[0x42]).expect("write failed");
|
||||||
|
// Because of the loopback mode, we should get back the fill word here.
|
||||||
|
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||||
|
assert_eq!(reply_buf[0], FILL_WORD);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
tx_rx_buf[0],
|
||||||
|
tx_rx_buf[1],
|
||||||
|
tx_rx_buf[2]
|
||||||
|
);
|
||||||
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
|
} else {
|
||||||
|
let send_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &send_buf).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(1000_u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
118
examples/simple/examples/timer-ticks.rs
Normal file
118
examples/simple/examples/timer-ticks.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
clock::{get_sys_clock, set_sys_clock},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountDownTimer, Event, IrqCfg, MS_COUNTER},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum LibType {
|
||||||
|
Pac,
|
||||||
|
Hal,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SEC_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut last_ms = 0;
|
||||||
|
rprintln!("-- Vorago system ticks using timers --");
|
||||||
|
set_sys_clock(50.MHz());
|
||||||
|
let lib_type = LibType::Hal;
|
||||||
|
match lib_type {
|
||||||
|
LibType::Pac => {
|
||||||
|
unsafe {
|
||||||
|
dp.sysconfig
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|_, w| w.irqsel().set_bit());
|
||||||
|
dp.sysconfig
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | (1 << 0) | (1 << 1)));
|
||||||
|
dp.irqsel.tim0(0).write(|w| w.bits(0x00));
|
||||||
|
dp.irqsel.tim0(1).write(|w| w.bits(0x01));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sys_clk: Hertz = 50.MHz();
|
||||||
|
let cnt_ms = sys_clk.raw() / 1000 - 1;
|
||||||
|
let cnt_sec = sys_clk.raw() - 1;
|
||||||
|
unsafe {
|
||||||
|
dp.tim0.cnt_value().write(|w| w.bits(cnt_ms));
|
||||||
|
dp.tim0.rst_value().write(|w| w.bits(cnt_ms));
|
||||||
|
dp.tim0.ctrl().write(|w| {
|
||||||
|
w.enable().set_bit();
|
||||||
|
w.irq_enb().set_bit()
|
||||||
|
});
|
||||||
|
dp.tim1.cnt_value().write(|w| w.bits(cnt_sec));
|
||||||
|
dp.tim1.rst_value().write(|w| w.bits(cnt_sec));
|
||||||
|
dp.tim1.ctrl().write(|w| {
|
||||||
|
w.enable().set_bit();
|
||||||
|
w.irq_enb().set_bit()
|
||||||
|
});
|
||||||
|
unmask_irqs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibType::Hal => {
|
||||||
|
set_up_ms_tick(
|
||||||
|
IrqCfg::new(interrupt::OC0, true, true),
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
50.MHz(),
|
||||||
|
dp.tim0,
|
||||||
|
);
|
||||||
|
let mut second_timer =
|
||||||
|
CountDownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
|
||||||
|
second_timer.listen(
|
||||||
|
Event::TimeOut,
|
||||||
|
IrqCfg::new(interrupt::OC1, true, true),
|
||||||
|
Some(&mut dp.irqsel),
|
||||||
|
Some(&mut dp.sysconfig),
|
||||||
|
);
|
||||||
|
second_timer.start(1.Hz());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
||||||
|
if current_ms - last_ms >= 1000 {
|
||||||
|
last_ms = current_ms;
|
||||||
|
rprintln!("MS counter: {}", current_ms);
|
||||||
|
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
||||||
|
rprintln!("Second counter: {}", second);
|
||||||
|
}
|
||||||
|
cortex_m::asm::delay(10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmask_irqs() {
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
default_ms_irq_handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC1() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||||
|
sec += 1;
|
||||||
|
SEC_COUNTER.borrow(cs).set(sec);
|
||||||
|
});
|
||||||
|
}
|
168
examples/simple/examples/uart-irq-rtic.rs
Normal file
168
examples/simple/examples/uart-irq-rtic.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//! More complex UART application
|
||||||
|
//!
|
||||||
|
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
|
||||||
|
//! a non-blocking way. You can send variably sized strings to the VA10820 which will be echoed
|
||||||
|
//! back to the sender.
|
||||||
|
//!
|
||||||
|
//! This script was tested with an Arduino Due. You can find the test script in the
|
||||||
|
//! [`/test/DueSerialTest`](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/test/DueSerialTest)
|
||||||
|
//! folder.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC4])]
|
||||||
|
mod app {
|
||||||
|
use embedded_io::Write;
|
||||||
|
use rtic_monotonics::systick::Systick;
|
||||||
|
use rtic_sync::make_channel;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{
|
||||||
|
time::Hertz,
|
||||||
|
gpio::PinsB,
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
uart::{self, IrqCfg, IrqResult, UartWithIrqBase},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
rx_info_tx: rtic_sync::channel::Sender<'static, RxInfo, 3>,
|
||||||
|
rx_info_rx: rtic_sync::channel::Receiver<'static, RxInfo, 3>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
irq_uart: UartWithIrqBase<pac::Uartb>,
|
||||||
|
rx_buf: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct RxInfo {
|
||||||
|
pub bytes_read: usize,
|
||||||
|
pub end_idx: usize,
|
||||||
|
pub timeout: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
rtt_init_print!();
|
||||||
|
//set_print_channel(channels.up.0);
|
||||||
|
rprintln!("-- VA108xx UART IRQ example application--");
|
||||||
|
|
||||||
|
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||||
|
let systick_mono_token = rtic_monotonics::create_systick_token!();
|
||||||
|
Systick::start(cx.core.SYST, Hertz::from(50.MHz()).raw(), systick_mono_token);
|
||||||
|
|
||||||
|
let mut dp = cx.device;
|
||||||
|
let gpiob = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
|
||||||
|
let tx = gpiob.pb21.into_funsel_1();
|
||||||
|
let rx = gpiob.pb20.into_funsel_1();
|
||||||
|
|
||||||
|
let irq_cfg = IrqCfg::new(pac::interrupt::OC3, true, true);
|
||||||
|
let (mut irq_uart, _) =
|
||||||
|
uart::Uart::uartb(dp.uartb, (tx, rx), 115200.Hz(), &mut dp.sysconfig, 50.MHz())
|
||||||
|
.into_uart_with_irq(irq_cfg, Some(&mut dp.sysconfig), Some(&mut dp.irqsel))
|
||||||
|
.downgrade();
|
||||||
|
irq_uart
|
||||||
|
.read_fixed_len_using_irq(64, true)
|
||||||
|
.expect("Read initialization failed");
|
||||||
|
|
||||||
|
let (rx_info_tx, rx_info_rx) = make_channel!(RxInfo, 3);
|
||||||
|
let rx_buf: [u8; 64] = [0; 64];
|
||||||
|
//reply_handler::spawn().expect("spawning reply handler failed");
|
||||||
|
(
|
||||||
|
Shared { irq_uart, rx_buf },
|
||||||
|
Local {
|
||||||
|
rx_info_tx,
|
||||||
|
rx_info_rx,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
binds = OC3,
|
||||||
|
shared = [irq_uart, rx_buf],
|
||||||
|
local = [cnt: u32 = 0, result: IrqResult = IrqResult::new(), rx_info_tx],
|
||||||
|
)]
|
||||||
|
fn reception_task(cx: reception_task::Context) {
|
||||||
|
let result = cx.local.result;
|
||||||
|
let cnt: &mut u32 = cx.local.cnt;
|
||||||
|
let irq_uart = cx.shared.irq_uart;
|
||||||
|
let rx_buf = cx.shared.rx_buf;
|
||||||
|
let (completed, end_idx) = (irq_uart, rx_buf).lock(|irq_uart, rx_buf| {
|
||||||
|
match irq_uart.irq_handler(result, rx_buf) {
|
||||||
|
Ok(_) => {
|
||||||
|
if result.complete() {
|
||||||
|
// Initiate next transfer immediately
|
||||||
|
irq_uart
|
||||||
|
.read_fixed_len_using_irq(64, true)
|
||||||
|
.expect("Read operation init failed");
|
||||||
|
|
||||||
|
let mut end_idx = 0;
|
||||||
|
for idx in 0..rx_buf.len() {
|
||||||
|
if (rx_buf[idx] as char) == '\n' {
|
||||||
|
end_idx = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(true, end_idx)
|
||||||
|
} else {
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
rprintln!("reception error {:?}", e);
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if completed {
|
||||||
|
rprintln!("counter: {}", cnt);
|
||||||
|
cx.local
|
||||||
|
.rx_info_tx
|
||||||
|
.try_send(RxInfo {
|
||||||
|
bytes_read: result.bytes_read,
|
||||||
|
end_idx,
|
||||||
|
timeout: result.timeout(),
|
||||||
|
})
|
||||||
|
.expect("RX queue full");
|
||||||
|
}
|
||||||
|
*cnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [irq_uart, rx_buf], local = [rx_info_rx], priority=1)]
|
||||||
|
async fn reply_handler(cx: reply_handler::Context) {
|
||||||
|
let mut irq_uart = cx.shared.irq_uart;
|
||||||
|
let mut rx_buf = cx.shared.rx_buf;
|
||||||
|
loop {
|
||||||
|
match cx.local.rx_info_rx.recv().await {
|
||||||
|
Ok(rx_info) => {
|
||||||
|
rprintln!("reception success, {} bytes read", rx_info.bytes_read);
|
||||||
|
if rx_info.timeout {
|
||||||
|
rprintln!("timeout occurred");
|
||||||
|
}
|
||||||
|
rx_buf.lock(|rx_buf| {
|
||||||
|
let string = core::str::from_utf8(&rx_buf[0..rx_info.end_idx])
|
||||||
|
.expect("Invalid string format");
|
||||||
|
rprintln!("read string: {}", string);
|
||||||
|
irq_uart.lock(|uart| {
|
||||||
|
writeln!(uart.uart, "{}", string).expect("Sending reply failed");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
rprintln!("error receiving RX info: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
examples/simple/examples/uart.rs
Normal file
47
examples/simple/examples/uart.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//! UART example application. Sends a test string over a UART and then enters
|
||||||
|
//! echo mode.
|
||||||
|
//!
|
||||||
|
//! Instructions:
|
||||||
|
//!
|
||||||
|
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8.
|
||||||
|
//! 2. Connect to the serial interface by using an application like Putty or picocom.
|
||||||
|
//! You should set a "Hello World" print when the application starts. After that, everything
|
||||||
|
//! typed on the console should be printed back by the echo application.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal_nb::{nb, serial::Read};
|
||||||
|
use embedded_io::Write as _;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, uart};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx UART example application--");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
|
let tx = gpioa.pa9.into_funsel_2();
|
||||||
|
let rx = gpioa.pa8.into_funsel_2();
|
||||||
|
|
||||||
|
let uarta = uart::Uart::uarta(dp.uarta, (tx, rx), 115200.Hz(), &mut dp.sysconfig, 50.MHz());
|
||||||
|
let (mut tx, mut rx) = uarta.split();
|
||||||
|
writeln!(tx, "Hello World\r").unwrap();
|
||||||
|
loop {
|
||||||
|
// Echo what is received on the serial link.
|
||||||
|
match rx.read() {
|
||||||
|
Ok(recv) => {
|
||||||
|
nb::block!(embedded_hal_nb::serial::Write::write(&mut tx, recv))
|
||||||
|
.expect("TX send error");
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => (),
|
||||||
|
Err(nb::Error::Other(uart_error)) => {
|
||||||
|
rprintln!("UART receive error {:?}", uart_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
examples/simple/src/main.rs
Normal file
13
examples/simple/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! Dummy app which does not do anything.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
Subproject commit c9b5f6a4e919f8924db457e14ce9a504dece983c
|
|
144
scripts/VA108xx_Series.yaml
Normal file
144
scripts/VA108xx_Series.yaml
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
name: VA108xx Series
|
||||||
|
generated_from_pack: true
|
||||||
|
pack_file_release: 1.4.0
|
||||||
|
variants:
|
||||||
|
- name: VA108xx
|
||||||
|
cores:
|
||||||
|
- name: main
|
||||||
|
type: armv6m
|
||||||
|
core_access_options: !Arm
|
||||||
|
ap: 0
|
||||||
|
psel: 0x0
|
||||||
|
memory_map:
|
||||||
|
- !Ram
|
||||||
|
name: IRAM1
|
||||||
|
range:
|
||||||
|
start: 0x10000000
|
||||||
|
end: 0x10008000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
- !Nvm
|
||||||
|
name: IROM1
|
||||||
|
range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
is_boot_memory: true
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
flash_algorithms:
|
||||||
|
- va108xx_fm25v20a_fram_128kb_prog
|
||||||
|
- va108xx_m95m01_128kb_prog
|
||||||
|
- va108xx_mr25h10_1mb_prog
|
||||||
|
- va108xx_ttflash_prog
|
||||||
|
flash_algorithms:
|
||||||
|
- name: va108xx_fm25v20a_fram_128kb_prog
|
||||||
|
description: VA108_FM25V20A_FRAM_128KB
|
||||||
|
instructions: QLpwR8C6cEdkSMFoyQf80MFoyQb81AMhwWJwR3BHALVgSV9IyGNdSgIgEGFeSBBgXkhQYAMg0GL/9+b/XEiQYNBowAf80AEgkGDAB5Bg//fb/wAgAL0Atf/31v9PSFRJgWDBaMkH/NABIYFg/SGBYMkHgWD/98j/ACAAvXC1ACJLTUZLAiYURp1g//e9/xACnmABAgkOmWAABAAOmGCcYAAg2WiJB/zVnGBAHP8o+NPYaIAH/NUBIMAHmGD/96T/ASBAAlIcgkLe0wAgcL0AIHBHPLUFRgAgC0YAkP/3lP8uTDNIoGD/94//AiCgYCgCAA6gYCgEAA6gYOiyoGAL4OBogAf81RB4oGCgaAGQAJhSHEAcWx4AkAEr8dHgaIAH/NUQeAEhyQdAGKBg//ds/wAgPL0AIHBH8LUORgVG//dj/xZIAyGBYCkCCQ6BYCkECQ6BYOmygWAAIxlGgWDEaGQH/NWEaFscBCv32wAjDOCBYMRoZAf81YRoF3jksqdCAdDoGPC9UhxbHLNC8NMBIckHgWD/9zj/qBnwvQAgBUD///8AQAAAQAcEAACCAgAABgAAgAAAAAA=
|
||||||
|
pc_init: 0x1f
|
||||||
|
pc_uninit: 0x57
|
||||||
|
pc_program_page: 0xd3
|
||||||
|
pc_erase_sector: 0xcf
|
||||||
|
pc_erase_all: 0x7d
|
||||||
|
data_section_offset: 0x1b4
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 3000
|
||||||
|
erase_sector_timeout: 3000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_m95m01_128kb_prog
|
||||||
|
description: VA108XX_M95M01_128KB
|
||||||
|
default: true
|
||||||
|
instructions: QLpwR8C6cEd6SMFoyQf80MFoyQb81AMhwWJwR3i1//fz/wUk5QcAJnJIc0sDIoRghWDBaMkH/NDBaEkH/NWBaACRwWhJB/zVgWgAkckHwmIH0ACWAL8AmUkcAJGZQvnb5ed4vQC1ZklkSMhjYUoCIBBhZEgQYGRIUGADINBi//fD/2JIkGD/97//ASCQYMAHkGD/97n/ACAAvQC1//e+///3sv9TSllIkGD/963/ASCQYFZI9zCQYP/3pv8AIAC98LUAJFFPS00mRq9g//ec/yACAiGpYAECCQ6pYAAEAA6oYK5gACDpaIkH/NWuYEAc/yj40+hogAf81QEgwAeoYP/3gv//94r/ASBAAmQchELb0wAg8L0AIHBHfLUGRgAgFEYNRgCQ//d5///3bf8xSjZIkGD/92j/AiCQYDACAA6QYDAEAA6QYPCykGAL4NBogAf81SB4kGCQaAGQAJhkHEAcbR4AkAEt8dHQaIAH/NUgeAEhyQdAGJBg//dF///3Tf8AIHy9ACBwR/C1FEYORgVG//dD///3N/8WSQMgiGAoAgAOiGAoBAAOiGDosohgACMaRopgyGhAB/zViGhbHAQr99sAIAzgimDLaFsH/NWLaCd427KfQgHQKBjwvUAcZBywQvDTASDAB4hg//cM/6gZ8L0AIAVAUMMAAP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x65
|
||||||
|
pc_uninit: 0x9b
|
||||||
|
pc_program_page: 0x11b
|
||||||
|
pc_erase_sector: 0x117
|
||||||
|
pc_erase_all: 0xc1
|
||||||
|
data_section_offset: 0x210
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 3000
|
||||||
|
erase_sector_timeout: 3000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_mr25h10_1mb_prog
|
||||||
|
description: VA108_MR25H10_1Mb
|
||||||
|
instructions: QLpwR8C6cEdjSMFoyQf80MFoyQb81AMhwWJwR3BHALVfSV5IyGNcSgIgEGFdSBBgXUhQYAMg0GL/9+b/W0iQYP/34v8BIJBgwAeQYP/33P8AIAC9ALX/99f/T0pTSJBg//fS/wEgkGBQSPcwkGD/98v/ACAAvXC1ACJMTUZLAiYURp1g//fA/xACnmABAgkOmWAABAAOmGCcYAAg2WiJB/zVnGBAHP8o+NPYaIAH/NUBIMAHmGD/96f/ASBAAlIcgkLe0wAgcL0AIHBHPLUFRgAgC0YAkP/3l/8vTDNIoGD/95L/AiCgYCgCAA6gYCgEAA6gYOiyoGAL4OBogAf81RB4oGCgaAGQAJhSHEAcWx4AkAEr8dHgaIAH/NUQeAEhyQdAGKBg//dv/wAgPL0AIHBH8LUORgVG//dm/xZIAyGBYCkCCQ6BYCkECQ6BYOmygWAAIxlGgWDEaGQH/NWEaFscBCv32wAjDOCBYMRoZAf81YRoF3jksqdCAdDoGPC9UhxbHLNC8NMBIckHgWD/9zv/qBnwvQAAACAFQP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x1f
|
||||||
|
pc_uninit: 0x55
|
||||||
|
pc_program_page: 0xcd
|
||||||
|
pc_erase_sector: 0xc9
|
||||||
|
pc_erase_all: 0x77
|
||||||
|
data_section_offset: 0x1b0
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 10000
|
||||||
|
erase_sector_timeout: 10000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_ttflash_prog
|
||||||
|
description: VA108_TT_Flash_8MBx
|
||||||
|
instructions: QLpwR8C6cEd9SMFoyQf80MFoyQb81AMhwWJwR3i1//fz/wUk5QcAJnVIdksDIoRghWDBaMkH/NDBaEkH/NWBaACRwWhJB/zVgWgAkckHwmIH0ACWAL8AmUkcAJGZQvnb5ed4vQC1aUlnSMhjZEoCIBBhZ0gQYGdIUGADINBi//fD/2VIkGD/97//ASCQYAAgkGABIMAHkGD/97b/ACAAvQC1//e7///3r/9VSlpIkGD/96r/ASCQYP0gkGDAB5Bg//ei/wAgAL0Atf/3p///95v/S0hQSYFgT0laMYFg//eT///3m/8AIAC9ELUERv/3lf//94n/QkpHSJBg//eE/yAgkGAgAgAOkGAgBAAOkGABIeCyyQdAGJBg//d////3c/8AIBC9fLUGRgAgFEYNRgCQ//dz///3Z/8xSjZIkGD/92L/AiCQYDACAA6QYDAEAA6QYPCykGAL4NBogAf81SB4kGCQaAGQAJhkHEAcbR4AkAEt8dHQaIAH/NUgeAEhyQdAGJBg//c////3R/8AIHy9ACBwR/C1FEYORgVG//c9///3Mf8WSQMgiGAoAgAOiGAoBAAOiGDosohgACMaRopgyGhAB/zViGhbHAQr99sAIAzgimDLaFsH/NWLaCd427KfQgHQKBjwvUAcZBywQvDTASDAB4hg//cG/6gZ8L0AIAVAUMMAAP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x65
|
||||||
|
pc_uninit: 0xa1
|
||||||
|
pc_program_page: 0x127
|
||||||
|
pc_erase_sector: 0xeb
|
||||||
|
pc_erase_all: 0xc9
|
||||||
|
data_section_offset: 0x21c
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0xff
|
||||||
|
program_page_timeout: 10000
|
||||||
|
erase_sector_timeout: 50000
|
||||||
|
sectors:
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x0
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x1000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x2000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x3000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x4000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x5000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x6000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x7000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x8000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x9000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xa000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xb000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xc000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xd000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xe000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xf000
|
1
va108xx
1
va108xx
@ -1 +0,0 @@
|
|||||||
Subproject commit f626e33e72d4863b68da89099cf60ce14de3e114
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit e9f12945725cf8814fb224a30ac513074de624cc
|
|
@ -1,10 +1,9 @@
|
|||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||||
# which option to pick depends on your system
|
# which option to pick depends on your system
|
||||||
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
# runner = "arm-none-eabi-gdb -q -x jlink.gdb"
|
||||||
# runner = "gdb-multiarch -q -x openocd.gdb"
|
# runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
# runner = "gdb -q -x openocd.gdb"
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
runner = "gdb-multiarch -q -x jlink.gdb"
|
|
||||||
|
|
||||||
rustflags = [
|
rustflags = [
|
||||||
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
@ -16,12 +15,12 @@ rustflags = [
|
|||||||
|
|
||||||
# if you run into problems with LLD switch to the GNU linker by commenting out
|
# if you run into problems with LLD switch to the GNU linker by commenting out
|
||||||
# this line
|
# this line
|
||||||
# "-C", "linker=/home/rmueller/.local/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/10.2.1-1.1.2/.content/bin/arm-none-eabi-ld",
|
# "-C", "linker=arm-none-eabi-ld",
|
||||||
|
|
||||||
# if you need to link to pre-compiled C libraries provided by a C toolchain
|
# if you need to link to pre-compiled C libraries provided by a C toolchain
|
||||||
# use GCC as the linker by commenting out both lines above and then
|
# use GCC as the linker by commenting out both lines above and then
|
||||||
# uncommenting the three lines below
|
# uncommenting the three lines below
|
||||||
# "-C", "linker=/home/rmueller/.local/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/10.2.1-1.1.2/.content/bin/arm-none-eabi-gcc",
|
# "-C", "linker=arm-none-eabi-gcc",
|
||||||
# "-C", "link-arg=-Wl,-Tlink.x",
|
# "-C", "link-arg=-Wl,-Tlink.x",
|
||||||
# "-C", "link-arg=-nostartfiles",
|
# "-C", "link-arg=-nostartfiles",
|
||||||
]
|
]
|
2
va108xx-hal/.github/bors.toml
vendored
Normal file
2
va108xx-hal/.github/bors.toml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
status = ["ci"]
|
||||||
|
delete_merged_branches = true
|
20
va108xx-hal/.github/workflows/changelog.yml
vendored
Normal file
20
va108xx-hal/.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
|
||||||
|
name: Changelog check
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changelog:
|
||||||
|
name: Changelog check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Changelog updated
|
||||||
|
uses: Zomzog/changelog-checker@v1.2.0
|
||||||
|
with:
|
||||||
|
fileName: CHANGELOG.md
|
||||||
|
noChangelogLabel: no changelog
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
65
va108xx-hal/.github/workflows/ci.yml
vendored
Normal file
65
va108xx-hal/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
on: [push]
|
||||||
|
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --examples
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- run: rustup component add rustfmt
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- run: rustup component add clippy
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: -- -D warnings
|
||||||
|
|
||||||
|
ci:
|
||||||
|
if: ${{ success() }}
|
||||||
|
# all new jobs must be added to this list
|
||||||
|
needs: [check, fmt, clippy]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: CI succeeded
|
||||||
|
run: exit 0
|
9
va108xx-hal/.gitignore
vendored
Normal file
9
va108xx-hal/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
145
va108xx-hal/CHANGELOG.md
Normal file
145
va108xx-hal/CHANGELOG.md
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
Change Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [v0.5.2] 2024-06-16
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
|
||||||
|
compilers.
|
||||||
|
|
||||||
|
## [v0.5.1]
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
- Updated dependencies:
|
||||||
|
- `cortex-m-rtic` (dev-depencency) to 1.1.2
|
||||||
|
- `once_cell` to 1.12.0
|
||||||
|
- Other dependencies: Only revision has changed
|
||||||
|
|
||||||
|
## [v0.5.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Reactored IRQ handling, so that `unmask` operations can be moved to HAL
|
||||||
|
- Added UART IRQ handler. Right now, can only perform reception, TX still needs to be done in
|
||||||
|
a blocking manner
|
||||||
|
- Added RTIC template and RTIC UART IRQ application
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Bugfix in UART code where RX and TX could not be enabled or disabled independently
|
||||||
|
|
||||||
|
## [v0.4.3]
|
||||||
|
|
||||||
|
- Various smaller fixes for READMEs, update of links in documentation
|
||||||
|
- Simplified CI for github, do not use `cross`
|
||||||
|
- New `blinky-pac` example
|
||||||
|
- Use HAL delay in `blinky` example
|
||||||
|
|
||||||
|
## [v0.4.2]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `port_mux` function to set pin function select manually
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Clear TX and RX FIFO in SPI transfer function
|
||||||
|
|
||||||
|
## [v0.4.1]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Initial blockmode setting was not set in SPI constructor
|
||||||
|
|
||||||
|
## [v0.4.0]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed
|
||||||
|
`+ Copy` where not necessary
|
||||||
|
|
||||||
|
## [v0.3.1]
|
||||||
|
|
||||||
|
- Updated all links to point to new repository
|
||||||
|
|
||||||
|
## [v0.3.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- TIM Cascade example
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `CountDownTimer` new function now expects an `impl Into<Hertz>` instead of `Hertz`
|
||||||
|
- Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
|
||||||
|
- Relicensed as Apache-2.0
|
||||||
|
|
||||||
|
## [0.2.3]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Basic API for EDAC functionality
|
||||||
|
- PWM implementation and example
|
||||||
|
- API to perform peripheral resets
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved Timer API. It is now possible to simply use `new` on `CountDownTimer`
|
||||||
|
|
||||||
|
## [0.2.2]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- DelayUs and DelayMs trait implementations for timer
|
||||||
|
- SPI implementation for blocking API, supports blockmode as well
|
||||||
|
- Basic I2C implementation for blocking API
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
|
||||||
|
|
||||||
|
## [0.2.1]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Adds the IRQ interface to configure interrupts on output and input pins
|
||||||
|
- Utility function to set up millisecond timer with `TIM0`
|
||||||
|
- Function to set clock divisor registers in `clock` module
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Minor optimizations and tweaks for GPIO module
|
||||||
|
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
|
||||||
|
- Clearing output state at initialization of Output pins
|
||||||
|
|
||||||
|
## [0.2.0]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- New GPIO implementation which uses type-level programming. Implementation heavily based on the
|
||||||
|
ATSAMD GPIO HAL: https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html
|
||||||
|
- Changes to API, therefore minor version bump
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- UART implementation
|
||||||
|
- UART example
|
||||||
|
- Some bugfixes for GPIO implementation
|
||||||
|
- Rust edition updated to 2021
|
||||||
|
|
||||||
|
## [0.1.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- First version of the HAL which adds the GPIO implementation and timer implementation.
|
||||||
|
- Also adds some examples and helper files to set up new binary crates
|
||||||
|
- RTT example application
|
||||||
|
- Added basic test binary in form of an example
|
||||||
|
- README with basic instructions how to set up own binary crate
|
45
va108xx-hal/Cargo.toml
Normal file
45
va108xx-hal/Cargo.toml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
[package]
|
||||||
|
name = "va108xx-hal"
|
||||||
|
version = "0.6.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2021"
|
||||||
|
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-hal"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||||
|
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
nb = "1"
|
||||||
|
paste = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
libm = "0.2"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
fugit = "0.3"
|
||||||
|
typenum = "1"
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
delegate = "0.12"
|
||||||
|
|
||||||
|
[dependencies.va108xx]
|
||||||
|
version = "0.3.0"
|
||||||
|
path = "../va108xx"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
|
[dependencies.embedded-hal]
|
||||||
|
version = "1"
|
||||||
|
|
||||||
|
[dependencies.void]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1.14"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["rt"]
|
||||||
|
rt = ["va108xx/rt"]
|
201
va108xx-hal/LICENSE-APACHE
Normal file
201
va108xx-hal/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
3
va108xx-hal/NOTICE
Normal file
3
va108xx-hal/NOTICE
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Rust Hardware Abstraction Layer (HAL) crate for the Vorago VA108xx family of MCUs
|
||||||
|
|
||||||
|
This software contains code developed at the University of Stuttgart.
|
88
va108xx-hal/README.md
Normal file
88
va108xx-hal/README.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
[![Crates.io](https://img.shields.io/crates/v/va108xx-hal)](https://crates.io/crates/va108xx-hal)
|
||||||
|
[![ci](https://github.com/us-irs/va108xx-hal-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/us-irs/va108xx-hal-rs/actions/workflows/ci.yml)
|
||||||
|
[![docs.rs](https://img.shields.io/docsrs/va108xx-hal)](https://docs.rs/va108xx-hal)
|
||||||
|
|
||||||
|
# HAL for the Vorago VA108xx MCU family
|
||||||
|
|
||||||
|
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
|
||||||
|
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va108xx).
|
||||||
|
|
||||||
|
It is the result of reading the datasheet for the device and encoding a type-safe layer over the
|
||||||
|
raw PAC. This crate also implements traits specified by the
|
||||||
|
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||||
|
various drivers in the embedded rust ecosystem.
|
||||||
|
|
||||||
|
In contrats to other HAL implementations, there is only one chip variant available here so there
|
||||||
|
is no need to pass the chip variant as a feature.
|
||||||
|
|
||||||
|
## Supported Boards
|
||||||
|
|
||||||
|
The first way to use this HAL will probably be with the
|
||||||
|
[REB1 development board](https://www.voragotech.com/products/reb1-va108x0-development-board-0).
|
||||||
|
The BSP provided for this board also contains instructions how to flash the board.
|
||||||
|
|
||||||
|
| Crate | Version |
|
||||||
|
|:------|:--------|
|
||||||
|
[vorago-reb1](https://crates.io/crates/vorago-reb1) | [![Crates.io](https://img.shields.io/crates/v/vorago-reb1)](https://crates.io/crates/vorago-reb1) |
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Building an application requires the `thumbv6m-none-eabi` cross-compiler toolchain.
|
||||||
|
If you have not installed it yet, you can do so with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rustup target add thumbv6m-none-eabi
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can use `cargo build` to build the development version of the crate.
|
||||||
|
|
||||||
|
If you have not done this yet, it is recommended to read some of the excellent resources
|
||||||
|
available to learn Rust:
|
||||||
|
|
||||||
|
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
|
||||||
|
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Some examples, which are not specific to a particular board were provided as well.
|
||||||
|
You can build the timer example with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --example timer-ticks
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setting up your own binary crate
|
||||||
|
|
||||||
|
If you have a custom board, you might be interested in setting up a new binary crate for your
|
||||||
|
project. These steps aim to provide a complete list to get a binary crate working to flash
|
||||||
|
your custom board.
|
||||||
|
|
||||||
|
The hello world of embedded development is usually to blinky a LED. This example
|
||||||
|
is contained within the
|
||||||
|
[examples folder](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs).
|
||||||
|
|
||||||
|
1. Set up your Rust cross-compiler if you have not done so yet. See more in the [build chapter](#Building)
|
||||||
|
2. Create a new binary crate with `cargo init`
|
||||||
|
3. To ensure that `cargo build` cross-compiles, it is recommended to create a `.cargo/config.toml`
|
||||||
|
file. A sample `.cargo/config.toml` file is provided in this repository as well
|
||||||
|
4. Copy the `memory.x` file into your project. This file contains information required by the linker.
|
||||||
|
5. Copy the `blinky.rs` file to the `src/main.rs` file in your binary crate
|
||||||
|
6. You need to add some dependencies to your `Cargo.toml` file
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "<Compatible Version>"
|
||||||
|
cortex-m-rt = "<Compatible Version>"
|
||||||
|
panic-halt = "<Compatible Version>"
|
||||||
|
embedded-hal = "<Compatible Version>"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "<Most Recent Version>"
|
||||||
|
features = ["rt"]
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Build the application with `cargo build`
|
||||||
|
|
||||||
|
7. Flashing the board might work differently for different boards and there is usually
|
||||||
|
more than one way. You can find example instructions for the REB1 development board
|
||||||
|
[here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1).
|
13
va108xx-hal/automation/Dockerfile
Normal file
13
va108xx-hal/automation/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Run the following commands from root directory to build and run locally
|
||||||
|
# docker build -f automation/Dockerfile -t <NAME> .
|
||||||
|
# docker run -it <NAME>
|
||||||
|
FROM rust:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get --yes upgrade
|
||||||
|
# tzdata is a dependency, won't install otherwise
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN rustup install nightly && \
|
||||||
|
rustup target add thumbv6m-none-eabi && \
|
||||||
|
rustup +nightly target add thumbv6m-none-eabi && \
|
||||||
|
rustup component add rustfmt clippy
|
37
va108xx-hal/automation/Jenkinsfile
vendored
Normal file
37
va108xx-hal/automation/Jenkinsfile
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Clippy') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo clippy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Rustfmt') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo fmt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo +nightly doc'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Examples') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi --examples'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
va108xx-hal/jlink.gdb
Normal file
10
va108xx-hal/jlink.gdb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
target remote localhost:2331
|
||||||
|
|
||||||
|
monitor reset
|
||||||
|
|
||||||
|
# *try* to stop at the user entry point (it might be gone due to inlining)
|
||||||
|
break main
|
||||||
|
|
||||||
|
load
|
||||||
|
|
||||||
|
continue
|
10
va108xx-hal/memory.x
Normal file
10
va108xx-hal/memory.x
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128K */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
64
va108xx-hal/src/clock.rs
Normal file
64
va108xx-hal/src/clock.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//! # API for clock related functionality
|
||||||
|
//!
|
||||||
|
//! This also includes functionality to enable the peripheral clocks
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::PeripheralSelect;
|
||||||
|
use cortex_m::interrupt::{self, Mutex};
|
||||||
|
use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
|
static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
|
||||||
|
|
||||||
|
pub type PeripheralClocks = PeripheralSelect;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilterClkSel {
|
||||||
|
SysClk = 0,
|
||||||
|
Clk1 = 1,
|
||||||
|
Clk2 = 2,
|
||||||
|
Clk3 = 3,
|
||||||
|
Clk4 = 4,
|
||||||
|
Clk5 = 5,
|
||||||
|
Clk6 = 6,
|
||||||
|
Clk7 = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Vorago in powered by an external clock which might have different frequencies.
|
||||||
|
/// The clock can be set here so it can be used by other software components as well.
|
||||||
|
/// The clock can be set exactly once
|
||||||
|
pub fn set_sys_clock(freq: impl Into<Hertz>) {
|
||||||
|
interrupt::free(|cs| {
|
||||||
|
SYS_CLOCK.borrow(cs).set(freq.into()).ok();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the configured system clock
|
||||||
|
pub fn get_sys_clock() -> Option<Hertz> {
|
||||||
|
interrupt::free(|cs| SYS_CLOCK.borrow(cs).get().copied())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
|
||||||
|
match clk_sel {
|
||||||
|
FilterClkSel::SysClk => (),
|
||||||
|
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }),
|
||||||
|
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_peripheral_clock(syscfg: &mut va108xx::Sysconfig, clock: PeripheralClocks) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
|
||||||
|
}
|
517
va108xx-hal/src/gpio/dynpins.rs
Normal file
517
va108xx-hal/src/gpio/dynpins.rs
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
//! # Type-erased, value-level module for GPIO pins
|
||||||
|
//!
|
||||||
|
//! Although the type-level API is generally preferred, it is not suitable in
|
||||||
|
//! all cases. Because each pin is represented by a distinct type, it is not
|
||||||
|
//! possible to store multiple pins in a homogeneous data structure. The
|
||||||
|
//! value-level API solves this problem by erasing the type information and
|
||||||
|
//! tracking the pin at run-time.
|
||||||
|
//!
|
||||||
|
//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
|
||||||
|
//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
|
||||||
|
//! respectively. The implementation of these types closely mirrors the
|
||||||
|
//! type-level API.
|
||||||
|
//!
|
||||||
|
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
|
||||||
|
//! created from their type-level equivalents using [`From`]/[`Into`].
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Move a pin out of the Pins struct and convert to a DynPin
|
||||||
|
//! let pa0: DynPin = pins.pa0.into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Conversions between pin modes use a value-level version of the type-level
|
||||||
|
//! API.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Use one of the literal function names
|
||||||
|
//! pa0.into_floating_input();
|
||||||
|
//! // Use a method and a DynPinMode variant
|
||||||
|
//! pa0.into_mode(DYN_FLOATING_INPUT);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
|
||||||
|
//! operations become fallible. Run-time checks are inserted to ensure that
|
||||||
|
//! users don't try to, for example, set the output level of an input pin.
|
||||||
|
//!
|
||||||
|
//! Users may try to convert value-level pins back to their type-level
|
||||||
|
//! equivalents. However, this option is fallible, because the compiler cannot
|
||||||
|
//! guarantee the pin has the correct ID or is in the correct mode at
|
||||||
|
//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/
|
||||||
|
//! [`TryInto`](core::convert::TryInto) for this conversion.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Convert to a `DynPin`
|
||||||
|
//! let pa0: DynPin = pins.pa0.into();
|
||||||
|
//! // Change pin mode
|
||||||
|
//! pa0.into_floating_input();
|
||||||
|
//! // Convert back to a `Pin`
|
||||||
|
//! let pa0: Pin<PA0, FloatingInput> = pa0.try_into().unwrap();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Embedded HAL traits
|
||||||
|
//!
|
||||||
|
//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
|
||||||
|
//! However, whereas the type-level API uses
|
||||||
|
//! `Error = core::convert::Infallible`, the value-level API can return a real
|
||||||
|
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
|
||||||
|
//! operation, the trait functions will return
|
||||||
|
//! [`InvalidPinType`](PinError::InvalidPinType).
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pins::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||||
|
reg::RegisterInterface,
|
||||||
|
};
|
||||||
|
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` for disabled configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynDisabled {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for input configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynInput {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for output configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynOutput {
|
||||||
|
PushPull,
|
||||||
|
OpenDrain,
|
||||||
|
ReadablePushPull,
|
||||||
|
ReadableOpenDrain,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DynAlternate = FunSel;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Error
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// GPIO error type
|
||||||
|
///
|
||||||
|
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||||
|
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InvalidPinTypeError;
|
||||||
|
|
||||||
|
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
||||||
|
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||||
|
embedded_hal::digital::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` representing pin modes
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynPinMode {
|
||||||
|
Input(DynInput),
|
||||||
|
Output(DynOutput),
|
||||||
|
Alternate(DynAlternate),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for floating input mode
|
||||||
|
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-down input mode
|
||||||
|
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-up input mode
|
||||||
|
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for push-pull output mode
|
||||||
|
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for open-drain output mode
|
||||||
|
pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
|
||||||
|
pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable opendrain output mode
|
||||||
|
pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 1
|
||||||
|
pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 2
|
||||||
|
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 3
|
||||||
|
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynGroup & DynPinId
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` for pin groups
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynGroup {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `struct` representing pin IDs
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct DynPinId {
|
||||||
|
pub group: DynGroup,
|
||||||
|
pub num: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynRegisters
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`DynPin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
|
/// access the corresponding regsiters.
|
||||||
|
struct DynRegisters {
|
||||||
|
id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||||
|
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl RegisterInterface for DynRegisters {
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> DynPinId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynRegisters {
|
||||||
|
/// Create a new instance of [`DynRegisters`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`DynPinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
|
DynRegisters { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
|
||||||
|
///
|
||||||
|
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||||
|
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||||
|
pub struct DynPin {
|
||||||
|
regs: DynRegisters,
|
||||||
|
mode: DynPinMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynPin {
|
||||||
|
/// Create a new [`DynPin`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
||||||
|
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||||
|
/// time. Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
|
DynPin {
|
||||||
|
regs: DynRegisters::new(id),
|
||||||
|
mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin ID
|
||||||
|
#[inline]
|
||||||
|
pub fn id(&self) -> DynPinId {
|
||||||
|
self.regs.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin mode
|
||||||
|
#[inline]
|
||||||
|
pub fn mode(&self) -> DynPinMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`DynPinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||||
|
// Only modify registers if we are actually changing pin mode
|
||||||
|
if mode != self.mode {
|
||||||
|
self.regs.change_mode(mode);
|
||||||
|
self.mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(&mut self) {
|
||||||
|
self.into_mode(DYN_FLOATING_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_DOWN_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_UP_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_reg_if_functions!();
|
||||||
|
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
pub fn pulse_mode(
|
||||||
|
self,
|
||||||
|
enable: bool,
|
||||||
|
default_state: PinState,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.pulse_mode(enable, default_state);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn filter_type(
|
||||||
|
self,
|
||||||
|
filter: FilterType,
|
||||||
|
clksel: FilterClkSel,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) => {
|
||||||
|
self.regs.filter_type(filter, clksel);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_edge(
|
||||||
|
mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut pac::Sysconfig>,
|
||||||
|
irqsel: Option<&mut pac::Irqsel>,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.toggle();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
|
Ok(self.regs.read_pin())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.write_pin(bit);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self._read().map(|v| !v)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self._read()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self._write(false)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self._write(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Convert between Pin and DynPin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
|
||||||
|
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||||
|
/// [`DynPin`]
|
||||||
|
#[inline]
|
||||||
|
fn from(_pin: Pin<I, M>) -> Self {
|
||||||
|
// The `Pin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `DynPin`
|
||||||
|
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
|
||||||
|
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||||
|
///
|
||||||
|
/// There is no way for the compiler to know if the conversion will be
|
||||||
|
/// successful at compile-time. We must verify the conversion at run-time
|
||||||
|
/// or refuse to perform it.
|
||||||
|
#[inline]
|
||||||
|
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||||
|
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
Ok(unsafe { Self::new() })
|
||||||
|
} else {
|
||||||
|
Err(InvalidPinTypeError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for DynPin {
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::OutputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::InputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_low()
|
||||||
|
}
|
||||||
|
}
|
111
va108xx-hal/src/gpio/mod.rs
Normal file
111
va108xx-hal/src/gpio/mod.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
//! # API for the GPIO peripheral
|
||||||
|
//!
|
||||||
|
//! The implementation of this GPIO module is heavily based on the
|
||||||
|
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
|
||||||
|
//!
|
||||||
|
//! This API provides two different submodules, [`mod@pins`] and [`dynpins`],
|
||||||
|
//! representing two different ways to handle GPIO pins. The default, [`mod@pins`],
|
||||||
|
//! is a type-level API that tracks the state of each pin at compile-time. The
|
||||||
|
//! alternative, [`dynpins`] is a type-erased, value-level API that tracks the
|
||||||
|
//! state of each pin at run-time.
|
||||||
|
//!
|
||||||
|
//! The type-level API is strongly preferred. By representing the state of each
|
||||||
|
//! pin within the type system, the compiler can detect logic errors at
|
||||||
|
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
|
||||||
|
//! cost.
|
||||||
|
//!
|
||||||
|
//! If needed, [`dynpins`] can be used to erase the type-level differences
|
||||||
|
//! between pins. However, by doing so, pins must now be tracked at run-time,
|
||||||
|
//! and each pin has a non-zero memory footprint.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs)
|
||||||
|
//!
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
|
macro_rules! common_reg_if_functions {
|
||||||
|
() => {
|
||||||
|
paste::paste!(
|
||||||
|
#[inline]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
self.regs.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_datamask(self) -> Self {
|
||||||
|
self.regs.clear_datamask();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_datamask(self) -> Self {
|
||||||
|
self.regs.set_datamask();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn irq_enb(
|
||||||
|
&mut self,
|
||||||
|
irq_cfg: crate::IrqCfg,
|
||||||
|
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
irqsel: Option<&mut va108xx::Irqsel>,
|
||||||
|
) {
|
||||||
|
if syscfg.is_some() {
|
||||||
|
crate::clock::enable_peripheral_clock(
|
||||||
|
syscfg.unwrap(),
|
||||||
|
crate::clock::PeripheralClocks::Irqsel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.regs.enable_irq();
|
||||||
|
if let Some(irqsel) = irqsel {
|
||||||
|
if irq_cfg.route {
|
||||||
|
match self.regs.id().group {
|
||||||
|
// Set the correct interrupt number in the IRQSEL register
|
||||||
|
DynGroup::A => {
|
||||||
|
irqsel
|
||||||
|
.porta0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
|
}
|
||||||
|
DynGroup::B => {
|
||||||
|
irqsel
|
||||||
|
.portb0(self.regs.id().num as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod dynpins;
|
||||||
|
pub use dynpins::*;
|
||||||
|
|
||||||
|
pub mod pins;
|
||||||
|
pub use pins::*;
|
||||||
|
|
||||||
|
mod reg;
|
890
va108xx-hal/src/gpio/pins.rs
Normal file
890
va108xx-hal/src/gpio/pins.rs
Normal file
@ -0,0 +1,890 @@
|
|||||||
|
//! # Type-level module for GPIO pins
|
||||||
|
//!
|
||||||
|
//! This documentation is strongly based on the
|
||||||
|
//! [atsamd documentation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/pin/index.html).
|
||||||
|
//!
|
||||||
|
//! This module provides a type-level API for GPIO pins. It uses the type system
|
||||||
|
//! to track the state of pins at compile-time. Representing GPIO pins in this
|
||||||
|
//! manner incurs no run-time overhead. Each [`Pin`] struct is zero-sized, so
|
||||||
|
//! there is no data to copy around. Instead, real code is generated as a side
|
||||||
|
//! effect of type transformations, and the resulting assembly is nearly
|
||||||
|
//! identical to the equivalent, hand-written C.
|
||||||
|
//!
|
||||||
|
//! To track the state of pins at compile-time, this module uses traits to
|
||||||
|
//! represent [type classes] and types as instances of those type classes. For
|
||||||
|
//! example, the trait [`InputConfig`] acts as a [type-level enum] of the
|
||||||
|
//! available input configurations, and the types [`Floating`], [`PullDown`] and
|
||||||
|
//! [`PullUp`] are its type-level variants.
|
||||||
|
//!
|
||||||
|
//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and
|
||||||
|
//! [`PinMode`].
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! pub struct Pin<I, M>
|
||||||
|
//! where
|
||||||
|
//! I: PinId,
|
||||||
|
//! M: PinMode,
|
||||||
|
//! {
|
||||||
|
//! // ...
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! A `PinId` identifies a pin by it's group (A, B, C or D) and pin number. Each
|
||||||
|
//! `PinId` instance is named according to its datasheet identifier, e.g.
|
||||||
|
//! [`PA02`].
|
||||||
|
//!
|
||||||
|
//! A `PinMode` represents the various pin modes. The available `PinMode`
|
||||||
|
//! variants are [`Disabled`], [`Input`], [`Interrupt`], [`Output`] and
|
||||||
|
//! [`Alternate`], each with its own corresponding configurations.
|
||||||
|
//!
|
||||||
|
//! It is not possible for users to create new instances of a [`Pin`]. Singleton
|
||||||
|
//! instances of each pin are made available to users through the [`Pins`]
|
||||||
|
//! struct.
|
||||||
|
//!
|
||||||
|
//! To create the [`Pins`] struct, users must supply the PAC
|
||||||
|
//! [`PORT`](crate::pac::PORT) peripheral. The [`Pins`] struct takes
|
||||||
|
//! ownership of the [`PORT`] and provides the corresponding pins. Each [`Pin`]
|
||||||
|
//! within the [`Pins`] struct can be moved out and used individually.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
//! let pins = Pins::new(peripherals.PORT);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Pins can be converted between modes using several different methods.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Use one of the literal function names
|
||||||
|
//! let pa27 = pins.pa27.into_floating_input();
|
||||||
|
//! // Use a generic method and one of the `PinMode` variant types
|
||||||
|
//! let pa27 = pins.pa27.into_mode::<FloatingInput>();
|
||||||
|
//! // Specify the target type and use `From`/`Into`
|
||||||
|
//! let pa27: Pin<PA27, FloatingInput> = pins.pa27.into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Embedded HAL traits
|
||||||
|
//!
|
||||||
|
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||||
|
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||||
|
//! [`ToggleableOutputPin`] and [`StatefulOutputPin`].
|
||||||
|
//!
|
||||||
|
//! For example, you can control the logic level of an `OutputPin` like so
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use atsamd_hal::pac::Peripherals;
|
||||||
|
//! use atsamd_hal::gpio::Pins;
|
||||||
|
//! use crate::ehal_02::digital::v2::OutputPin;
|
||||||
|
//!
|
||||||
|
//! let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
//! let mut pins = Pins::new(peripherals.PORT);
|
||||||
|
//! pins.pa27.set_high();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Type-level features
|
||||||
|
//!
|
||||||
|
//! This module also provides additional, type-level tools to work with GPIO
|
||||||
|
//! pins.
|
||||||
|
//!
|
||||||
|
//! The [`OptionalPinId`] and [`OptionalPin`] traits use the [`OptionalKind`]
|
||||||
|
//! pattern to act as type-level versions of [`Option`] for `PinId` and `Pin`
|
||||||
|
//! respectively. And the [`AnyPin`] trait defines an [`AnyKind`] type class
|
||||||
|
//! for all `Pin` types.
|
||||||
|
//!
|
||||||
|
//! [type classes]: crate::typelevel#type-classes
|
||||||
|
//! [type-level enum]: crate::typelevel#type-level-enum
|
||||||
|
//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
|
||||||
|
//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
|
use super::reg::RegisterInterface;
|
||||||
|
use crate::{
|
||||||
|
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||||
|
typelevel::Sealed,
|
||||||
|
IrqCfg,
|
||||||
|
};
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::mem::transmute;
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors and Definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Input configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for input configurations
|
||||||
|
///
|
||||||
|
/// The valid options are [`Floating`], [`PullDown`] and [`PullUp`].
|
||||||
|
pub trait InputConfig: Sealed {
|
||||||
|
/// Corresponding [`DynInput`](super::DynInput)
|
||||||
|
const DYN: DynInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Floating {}
|
||||||
|
pub enum PullDown {}
|
||||||
|
pub enum PullUp {}
|
||||||
|
|
||||||
|
impl InputConfig for Floating {
|
||||||
|
const DYN: DynInput = DynInput::Floating;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullDown {
|
||||||
|
const DYN: DynInput = DynInput::PullDown;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullUp {
|
||||||
|
const DYN: DynInput = DynInput::PullUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Floating {}
|
||||||
|
impl Sealed for PullDown {}
|
||||||
|
impl Sealed for PullUp {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for floating input mode
|
||||||
|
pub type InputFloating = Input<Floating>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-down input mode
|
||||||
|
pub type InputPullDown = Input<PullDown>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-up input mode
|
||||||
|
pub type InputPullUp = Input<PullUp>;
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for input modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
||||||
|
/// [`PullUp`]
|
||||||
|
pub struct Input<C: InputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> Sealed for Input<C> {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilterType {
|
||||||
|
SystemClock = 0,
|
||||||
|
DirectInputWithSynchronization = 1,
|
||||||
|
FilterOneClockCycle = 2,
|
||||||
|
FilterTwoClockCycles = 3,
|
||||||
|
FilterThreeClockCycles = 4,
|
||||||
|
FilterFourClockCycles = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use crate::clock::FilterClkSel;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Output configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait OutputConfig: Sealed {
|
||||||
|
const DYN: DynOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ReadableOutput: Sealed {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||||
|
pub enum PushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||||
|
pub enum OpenDrain {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||||
|
pub enum ReadablePushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||||
|
pub enum ReadableOpenDrain {}
|
||||||
|
|
||||||
|
impl Sealed for PushPull {}
|
||||||
|
impl Sealed for OpenDrain {}
|
||||||
|
impl Sealed for ReadableOpenDrain {}
|
||||||
|
impl Sealed for ReadablePushPull {}
|
||||||
|
impl ReadableOutput for ReadableOpenDrain {}
|
||||||
|
impl ReadableOutput for ReadablePushPull {}
|
||||||
|
|
||||||
|
impl OutputConfig for PushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::PushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for OpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::OpenDrain;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadablePushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadablePushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadableOpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadableOpenDrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for output modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
||||||
|
/// their respective readable versions
|
||||||
|
pub struct Output<C: OutputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: OutputConfig> Sealed for Output<C> {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for push-pull output mode
|
||||||
|
pub type PushPullOutput = Output<PushPull>;
|
||||||
|
/// Type-level variant of [`PinMode`] for open drain output mode
|
||||||
|
pub type OutputOpenDrain = Output<OpenDrain>;
|
||||||
|
|
||||||
|
pub type OutputReadablePushPull = Output<ReadablePushPull>;
|
||||||
|
pub type OutputReadableOpenDrain = Output<ReadableOpenDrain>;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Alternate configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for alternate peripheral function configurations
|
||||||
|
pub trait AlternateConfig: Sealed {
|
||||||
|
const DYN: DynAlternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Funsel1 {}
|
||||||
|
pub enum Funsel2 {}
|
||||||
|
pub enum Funsel3 {}
|
||||||
|
|
||||||
|
impl AlternateConfig for Funsel1 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel1;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel2 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel2;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel3 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel3;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Funsel1 {}
|
||||||
|
impl Sealed for Funsel2 {}
|
||||||
|
impl Sealed for Funsel3 {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for alternate peripheral functions
|
||||||
|
///
|
||||||
|
/// Type `C` is an [`AlternateConfig`]
|
||||||
|
pub struct Alternate<C: AlternateConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: AlternateConfig> Sealed for Alternate<C> {}
|
||||||
|
|
||||||
|
pub type AltFunc1 = Alternate<Funsel1>;
|
||||||
|
pub type AltFunc2 = Alternate<Funsel2>;
|
||||||
|
pub type AltFunc3 = Alternate<Funsel3>;
|
||||||
|
|
||||||
|
/// Type alias for the [`PinMode`] at reset
|
||||||
|
pub type Reset = InputFloating;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin modes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum representing pin modes
|
||||||
|
///
|
||||||
|
/// The valid options are [`Input`], [`Output`] and [`Alternate`].
|
||||||
|
pub trait PinMode: Sealed {
|
||||||
|
/// Corresponding [`DynPinMode`](super::DynPinMode)
|
||||||
|
const DYN: DynPinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> PinMode for Input<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: OutputConfig> PinMode for Output<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: AlternateConfig> PinMode for Alternate<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Alternate(C::DYN);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin IDs
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for pin IDs
|
||||||
|
pub trait PinId: Sealed {
|
||||||
|
/// Corresponding [`DynPinId`](super::DynPinId)
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pin_id {
|
||||||
|
($Group:ident, $Id:ident, $NUM:literal) => {
|
||||||
|
// Need paste macro to use ident in doc attribute
|
||||||
|
paste! {
|
||||||
|
#[doc = "Pin ID representing pin " $Id]
|
||||||
|
pub enum $Id {}
|
||||||
|
impl Sealed for $Id {}
|
||||||
|
impl PinId for $Id {
|
||||||
|
const DYN: DynPinId = DynPinId {
|
||||||
|
group: DynGroup::$Group,
|
||||||
|
num: $NUM,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
|
||||||
|
|
||||||
|
pub struct Pin<I: PinId, M: PinMode> {
|
||||||
|
pub(in crate::gpio) regs: Registers<I>,
|
||||||
|
mode: PhantomData<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||||
|
/// Create a new [`Pin`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be
|
||||||
|
/// at most one corresponding [`Pin`] in existence at any given time.
|
||||||
|
/// Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||||
|
Pin {
|
||||||
|
regs: Registers::new(),
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
|
// Only modify registers if we are actually changing pin mode
|
||||||
|
// This check should compile away
|
||||||
|
if N::DYN != M::DYN {
|
||||||
|
self.regs.change_mode::<N>();
|
||||||
|
}
|
||||||
|
// Safe because we drop the existing Pin
|
||||||
|
unsafe { Pin::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(self) -> Pin<I, InputFloating> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(self) -> Pin<I, InputPullDown> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(self) -> Pin<I, InputPullUp> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(self) -> Pin<I, OutputReadablePushPull> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable open-drain output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(self) -> Pin<I, OutputReadableOpenDrain> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
common_reg_if_functions!();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_high(&mut self) {
|
||||||
|
self.regs.write_pin(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_low(&mut self) {
|
||||||
|
self.regs.write_pin(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
||||||
|
self.regs.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
|
!self.regs.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
|
self.regs.read_pin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// AnyPin
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Type class for [`Pin`] types
|
||||||
|
///
|
||||||
|
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
|
||||||
|
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
|
||||||
|
/// pattern.
|
||||||
|
///
|
||||||
|
/// ## `v1` Compatibility
|
||||||
|
///
|
||||||
|
/// Normally, this trait would use `Is<Type = SpecificPin<Self>>` as a super
|
||||||
|
/// trait. But doing so would restrict implementations to only the `v2` `Pin`
|
||||||
|
/// type in this module. To aid in backwards compatibility, we want to implement
|
||||||
|
/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few
|
||||||
|
/// reasons. First, both structs are zero-sized, so there is no meaningful
|
||||||
|
/// memory layout to begin with. And even if there were, the `v1` `Pin` type is
|
||||||
|
/// a newtype wrapper around a `v2` `Pin`, and single-field structs are
|
||||||
|
/// guaranteed to have the same layout as the field, even for `repr(Rust)`.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
/// [type class]: crate::typelevel#type-classes
|
||||||
|
pub trait AnyPin
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<SpecificPin<Self>>,
|
||||||
|
Self: Into<SpecificPin<Self>>,
|
||||||
|
Self: AsRef<SpecificPin<Self>>,
|
||||||
|
Self: AsMut<SpecificPin<Self>>,
|
||||||
|
{
|
||||||
|
/// [`PinId`] of the corresponding [`Pin`]
|
||||||
|
type Id: PinId;
|
||||||
|
/// [`PinMode`] of the corresponding [`Pin`]
|
||||||
|
type Mode: PinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> Sealed for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> AnyPin for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Id = I;
|
||||||
|
type Mode = M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias to recover the specific [`Pin`] type from an implementation of
|
||||||
|
/// [`AnyPin`]
|
||||||
|
///
|
||||||
|
/// See the [`AnyKind`] documentation for more details on the pattern.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> &mut P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Additional functionality
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
pub fn interrupt_edge(
|
||||||
|
mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||||
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn toggle_with_toggle_reg(&mut self) {
|
||||||
|
self._toggle_with_toggle_reg()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self {
|
||||||
|
self.regs.pulse_mode(enable, default_state);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_edge(
|
||||||
|
mut self,
|
||||||
|
edge_type: InterruptEdge,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
syscfg: Option<&mut Sysconfig>,
|
||||||
|
irqsel: Option<&mut Irqsel>,
|
||||||
|
) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||||
|
self.regs.filter_type(filter, clksel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_high();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_low();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> InputPin for Pin<I, Input<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: InputConfig,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> InputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Registers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`Pin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(in crate::gpio) struct Registers<I: PinId> {
|
||||||
|
id: PhantomData<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
|
||||||
|
// each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> DynPinId {
|
||||||
|
I::DYN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> Registers<I> {
|
||||||
|
/// Create a new instance of [`Registers`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`PinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new() -> Self {
|
||||||
|
Registers { id: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a type-level equivalent for the
|
||||||
|
/// [`RegisterInterface::change_mode`] method.
|
||||||
|
#[inline]
|
||||||
|
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
|
||||||
|
RegisterInterface::change_mode(self, M::DYN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pins {
|
||||||
|
(
|
||||||
|
$Port:ident, $PinsName:ident, $($Id:ident,)+,
|
||||||
|
) => {
|
||||||
|
paste!(
|
||||||
|
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||||
|
pub struct $PinsName {
|
||||||
|
iocfg: Option<va108xx::Ioconfig>,
|
||||||
|
port: $Port,
|
||||||
|
$(
|
||||||
|
#[doc = "Pin " $Id]
|
||||||
|
pub [<$Id:lower>]: Pin<$Id, Reset>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $PinsName {
|
||||||
|
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
|
||||||
|
/// is optional because it might be required to create pin definitions for both
|
||||||
|
/// ports.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut va108xx::Sysconfig,
|
||||||
|
iocfg: Option<va108xx::Ioconfig>,
|
||||||
|
port: $Port
|
||||||
|
) -> $PinsName {
|
||||||
|
syscfg.peripheral_clk_enable().modify(|_, w| {
|
||||||
|
w.[<$Port:lower>]().set_bit();
|
||||||
|
w.gpio().set_bit();
|
||||||
|
w.ioconfig().set_bit()
|
||||||
|
});
|
||||||
|
$PinsName {
|
||||||
|
iocfg,
|
||||||
|
port,
|
||||||
|
// Safe because we only create one `Pin` per `PinId`
|
||||||
|
$(
|
||||||
|
[<$Id:lower>]: unsafe { Pin::new() },
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the peripheral ID
|
||||||
|
/// Safety: Read-only register
|
||||||
|
pub fn get_perid() -> u32 {
|
||||||
|
let port = unsafe { &(*$Port::ptr()) };
|
||||||
|
port.perid().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the Pins struct and returns the port definitions
|
||||||
|
pub fn release(self) -> (Option<va108xx::Ioconfig>, $Port) {
|
||||||
|
(self.iocfg, self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_pins {
|
||||||
|
(
|
||||||
|
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+]
|
||||||
|
) => {
|
||||||
|
pins!($Port, $PinsName, $($Id,)+,);
|
||||||
|
$(
|
||||||
|
pin_id!($Group, $Id, $NUM);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
A,
|
||||||
|
PinsA,
|
||||||
|
Porta,
|
||||||
|
[
|
||||||
|
(PA0, 0),
|
||||||
|
(PA1, 1),
|
||||||
|
(PA2, 2),
|
||||||
|
(PA3, 3),
|
||||||
|
(PA4, 4),
|
||||||
|
(PA5, 5),
|
||||||
|
(PA6, 6),
|
||||||
|
(PA7, 7),
|
||||||
|
(PA8, 8),
|
||||||
|
(PA9, 9),
|
||||||
|
(PA10, 10),
|
||||||
|
(PA11, 11),
|
||||||
|
(PA12, 12),
|
||||||
|
(PA13, 13),
|
||||||
|
(PA14, 14),
|
||||||
|
(PA15, 15),
|
||||||
|
(PA16, 16),
|
||||||
|
(PA17, 17),
|
||||||
|
(PA18, 18),
|
||||||
|
(PA19, 19),
|
||||||
|
(PA20, 20),
|
||||||
|
(PA21, 21),
|
||||||
|
(PA22, 22),
|
||||||
|
(PA23, 23),
|
||||||
|
(PA24, 24),
|
||||||
|
(PA25, 25),
|
||||||
|
(PA26, 26),
|
||||||
|
(PA27, 27),
|
||||||
|
(PA28, 28),
|
||||||
|
(PA29, 29),
|
||||||
|
(PA30, 30),
|
||||||
|
(PA31, 31),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
B,
|
||||||
|
PinsB,
|
||||||
|
Portb,
|
||||||
|
[
|
||||||
|
(PB0, 0),
|
||||||
|
(PB1, 1),
|
||||||
|
(PB2, 2),
|
||||||
|
(PB3, 3),
|
||||||
|
(PB4, 4),
|
||||||
|
(PB5, 5),
|
||||||
|
(PB6, 6),
|
||||||
|
(PB7, 7),
|
||||||
|
(PB8, 8),
|
||||||
|
(PB9, 9),
|
||||||
|
(PB10, 10),
|
||||||
|
(PB11, 11),
|
||||||
|
(PB12, 12),
|
||||||
|
(PB13, 13),
|
||||||
|
(PB14, 14),
|
||||||
|
(PB15, 15),
|
||||||
|
(PB16, 16),
|
||||||
|
(PB17, 17),
|
||||||
|
(PB18, 18),
|
||||||
|
(PB19, 19),
|
||||||
|
(PB20, 20),
|
||||||
|
(PB21, 21),
|
||||||
|
(PB22, 22),
|
||||||
|
(PB23, 23),
|
||||||
|
]
|
||||||
|
);
|
382
va108xx-hal/src/gpio/reg.rs
Normal file
382
va108xx-hal/src/gpio/reg.rs
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode};
|
||||||
|
use super::pins::{FilterType, InterruptEdge, InterruptLevel, PinState};
|
||||||
|
use super::IsMaskedError;
|
||||||
|
use crate::clock::FilterClkSel;
|
||||||
|
use va108xx::{ioconfig, porta};
|
||||||
|
|
||||||
|
/// Type definition to avoid confusion: These register blocks are identical
|
||||||
|
type PortRegisterBlock = porta::RegisterBlock;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// ModeFields
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ModeFields {
|
||||||
|
dir: bool,
|
||||||
|
opendrn: bool,
|
||||||
|
pull_en: bool,
|
||||||
|
/// true for pullup, false for pulldown
|
||||||
|
pull_dir: bool,
|
||||||
|
funsel: u8,
|
||||||
|
enb_input: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DynPinMode> for ModeFields {
|
||||||
|
#[inline]
|
||||||
|
fn from(mode: DynPinMode) -> Self {
|
||||||
|
let mut fields = Self::default();
|
||||||
|
use DynPinMode::*;
|
||||||
|
match mode {
|
||||||
|
Input(config) => {
|
||||||
|
use dynpins::DynInput::*;
|
||||||
|
fields.dir = false;
|
||||||
|
match config {
|
||||||
|
Floating => (),
|
||||||
|
PullUp => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = true;
|
||||||
|
}
|
||||||
|
PullDown => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Output(config) => {
|
||||||
|
use dynpins::DynOutput::*;
|
||||||
|
fields.dir = true;
|
||||||
|
match config {
|
||||||
|
PushPull => (),
|
||||||
|
OpenDrain => {
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadableOpenDrain => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadablePushPull => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Alternate(config) => {
|
||||||
|
fields.funsel = config as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type PortReg = ioconfig::Porta;
|
||||||
|
/*
|
||||||
|
pub type IocfgPort = ioconfig::Porta;
|
||||||
|
#[repr(C)]
|
||||||
|
pub(super) struct IocfgPortGroup {
|
||||||
|
port: [IocfgPort; 32],
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Provide a safe register interface for pin objects
|
||||||
|
///
|
||||||
|
/// [`PORTA`] and [`PORTB`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
|
||||||
|
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
|
||||||
|
/// interface is quite restrictive. Instead, it would be ideal if we could split
|
||||||
|
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
|
||||||
|
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
|
||||||
|
/// `struct`s for every pin, where each pin is only allowed to control its own
|
||||||
|
/// registers. Furthermore, each pin `struct` should be a singleton, so that
|
||||||
|
/// exclusive access to the `struct` also guarantees exclusive access to the
|
||||||
|
/// corresponding registers. Finally, the pin `struct`s should not have any
|
||||||
|
/// interior mutability. Together, these requirements would allow the pin
|
||||||
|
/// `struct`s to be both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// This trait creates a safe API for accomplishing these goals. Implementers
|
||||||
|
/// supply a pin ID through the [`id`] function. The remaining functions provide
|
||||||
|
/// a safe API for accessing the registers associated with that pin ID. Any
|
||||||
|
/// modification of the registers requires `&mut self`, which destroys interior
|
||||||
|
/// mutability.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
///
|
||||||
|
/// [`id`]: Self::id
|
||||||
|
pub(super) unsafe trait RegisterInterface {
|
||||||
|
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
||||||
|
/// this type.
|
||||||
|
fn id(&self) -> DynPinId;
|
||||||
|
|
||||||
|
const PORTA: *const PortRegisterBlock = va108xx::Porta::ptr();
|
||||||
|
const PORTB: *const PortRegisterBlock = va108xx::Portb::ptr();
|
||||||
|
|
||||||
|
/// Change the pin mode
|
||||||
|
#[inline]
|
||||||
|
fn change_mode(&mut self, mode: DynPinMode) {
|
||||||
|
let ModeFields {
|
||||||
|
dir,
|
||||||
|
funsel,
|
||||||
|
opendrn,
|
||||||
|
pull_dir,
|
||||||
|
pull_en,
|
||||||
|
enb_input,
|
||||||
|
} = mode.into();
|
||||||
|
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||||
|
iocfg.write(|w| {
|
||||||
|
w.opendrn().bit(opendrn);
|
||||||
|
w.pen().bit(pull_en);
|
||||||
|
w.plevel().bit(pull_dir);
|
||||||
|
w.iewo().bit(enb_input);
|
||||||
|
unsafe { w.funsel().bits(funsel) }
|
||||||
|
});
|
||||||
|
let mask = self.mask_32();
|
||||||
|
unsafe {
|
||||||
|
if dir {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||||
|
// Clear output
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => unsafe { &(*Self::PORTA) },
|
||||||
|
DynGroup::B => unsafe { &(*Self::PORTB) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn iocfg_port(&self) -> &PortReg {
|
||||||
|
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => ioconfig.porta(self.id().num as usize),
|
||||||
|
DynGroup::B => ioconfig.portb0(self.id().num as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_irq(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Read the logic level of an output pin
|
||||||
|
fn read_pin(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DATAMASK bit for this particular pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn datamask(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
(portreg.datamask().read().bits() >> self.id().num) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_pin(&mut self, bit: bool) {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline]
|
||||||
|
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError)
|
||||||
|
} else {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn toggle(&mut self) {
|
||||||
|
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||||
|
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||||
|
#[inline]
|
||||||
|
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
match edge_type {
|
||||||
|
InterruptEdge::HighToLow => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::LowToHigh => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::BothEdges => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_edge()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure which edge or level type triggers an interrupt
|
||||||
|
#[inline]
|
||||||
|
fn interrupt_level(&mut self, level: InterruptLevel) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
if level == InterruptLevel::Low {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for input pins
|
||||||
|
#[inline]
|
||||||
|
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
self.iocfg_port().modify(|_, w| {
|
||||||
|
// Safety: Only write to register for this Pin ID
|
||||||
|
unsafe {
|
||||||
|
w.flttype().bits(filter as u8);
|
||||||
|
w.fltclk().bits(clksel as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||||
|
/// state of the bit and allows access of the corresponding bit
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||||
|
/// of the corresponding bit for output and input operations
|
||||||
|
#[inline(always)]
|
||||||
|
fn clear_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
fn pulse_mode(&self, enable: bool, default_state: PinState) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if default_state == PinState::Low {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
fn delay(&self, delay_1: bool, delay_2: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if delay_1 {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if delay_2 {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
878
va108xx-hal/src/i2c.rs
Normal file
878
va108xx-hal/src/i2c.rs
Normal file
@ -0,0 +1,878 @@
|
|||||||
|
//! API for the I2C peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/adt75-temp-sensor.rs)
|
||||||
|
use crate::{
|
||||||
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
|
pac,
|
||||||
|
time::Hertz,
|
||||||
|
typelevel::Sealed,
|
||||||
|
};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Defintions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum FifoEmptyMode {
|
||||||
|
Stall = 0,
|
||||||
|
EndTransaction = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Error {
|
||||||
|
InvalidTimingParams,
|
||||||
|
ArbitrationLost,
|
||||||
|
NackAddr,
|
||||||
|
/// Data not acknowledged in write operation
|
||||||
|
NackData,
|
||||||
|
/// Not enough data received in read operation
|
||||||
|
InsufficientDataReceived,
|
||||||
|
/// Number of bytes in transfer too large (larger than 0x7fe)
|
||||||
|
DataTooLarge,
|
||||||
|
WrongAddrMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::Error for Error {
|
||||||
|
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||||
|
match self {
|
||||||
|
Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
|
||||||
|
Error::NackAddr => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address)
|
||||||
|
}
|
||||||
|
Error::NackData => {
|
||||||
|
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
||||||
|
}
|
||||||
|
Error::DataTooLarge
|
||||||
|
| Error::WrongAddrMode
|
||||||
|
| Error::InsufficientDataReceived
|
||||||
|
| Error::InvalidTimingParams => embedded_hal::i2c::ErrorKind::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
enum I2cCmd {
|
||||||
|
Start = 0b00,
|
||||||
|
Stop = 0b10,
|
||||||
|
StartWithStop = 0b11,
|
||||||
|
Cancel = 0b100,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum I2cSpeed {
|
||||||
|
Regular100khz = 0,
|
||||||
|
Fast400khz = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum I2cDirection {
|
||||||
|
Send = 0,
|
||||||
|
Read = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum I2cAddress {
|
||||||
|
Regular(u8),
|
||||||
|
TenBit(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Config
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct TrTfThighTlow(u8, u8, u8, u8);
|
||||||
|
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
|
||||||
|
|
||||||
|
pub struct TimingCfg {
|
||||||
|
// 4 bit max width
|
||||||
|
tr: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tf: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thigh: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tlow: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sto: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tsu_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
thd_sta: u8,
|
||||||
|
// 4 bit max width
|
||||||
|
tbuf: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimingCfg {
|
||||||
|
pub fn new(
|
||||||
|
first_16_bits: TrTfThighTlow,
|
||||||
|
second_16_bits: TsuStoTsuStaThdStaTBuf,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
if first_16_bits.0 > 0xf
|
||||||
|
|| first_16_bits.1 > 0xf
|
||||||
|
|| first_16_bits.2 > 0xf
|
||||||
|
|| first_16_bits.3 > 0xf
|
||||||
|
|| second_16_bits.0 > 0xf
|
||||||
|
|| second_16_bits.1 > 0xf
|
||||||
|
|| second_16_bits.2 > 0xf
|
||||||
|
|| second_16_bits.3 > 0xf
|
||||||
|
{
|
||||||
|
return Err(Error::InvalidTimingParams);
|
||||||
|
}
|
||||||
|
Ok(TimingCfg {
|
||||||
|
tr: first_16_bits.0,
|
||||||
|
tf: first_16_bits.1,
|
||||||
|
thigh: first_16_bits.2,
|
||||||
|
tlow: first_16_bits.3,
|
||||||
|
tsu_sto: second_16_bits.0,
|
||||||
|
tsu_sta: second_16_bits.1,
|
||||||
|
thd_sta: second_16_bits.2,
|
||||||
|
tbuf: second_16_bits.3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reg(&self) -> u32 {
|
||||||
|
(self.tbuf as u32) << 28
|
||||||
|
| (self.thd_sta as u32) << 24
|
||||||
|
| (self.tsu_sta as u32) << 20
|
||||||
|
| (self.tsu_sto as u32) << 16
|
||||||
|
| (self.tlow as u32) << 12
|
||||||
|
| (self.thigh as u32) << 8
|
||||||
|
| (self.tf as u32) << 4
|
||||||
|
| (self.tr as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TimingCfg {
|
||||||
|
fn default() -> Self {
|
||||||
|
TimingCfg {
|
||||||
|
tr: 0x02,
|
||||||
|
tf: 0x01,
|
||||||
|
thigh: 0x08,
|
||||||
|
tlow: 0x09,
|
||||||
|
tsu_sto: 0x8,
|
||||||
|
tsu_sta: 0x0a,
|
||||||
|
thd_sta: 0x8,
|
||||||
|
tbuf: 0xa,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MasterConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Enable the analog delay glitch filter
|
||||||
|
pub alg_filt: bool,
|
||||||
|
/// Enable the digital glitch filter
|
||||||
|
pub dlg_filt: bool,
|
||||||
|
pub tm_cfg: Option<TimingCfg>,
|
||||||
|
// Loopback mode
|
||||||
|
// lbm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MasterConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
MasterConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
alg_filt: false,
|
||||||
|
dlg_filt: false,
|
||||||
|
tm_cfg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for MasterConfig {}
|
||||||
|
|
||||||
|
pub struct SlaveConfig {
|
||||||
|
pub tx_fe_mode: FifoEmptyMode,
|
||||||
|
pub rx_fe_mode: FifoEmptyMode,
|
||||||
|
/// Maximum number of words before issuing a negative acknowledge.
|
||||||
|
/// Range should be 0 to 0x7fe. Setting the value to 0x7ff has the same effect as not setting
|
||||||
|
/// the enable bit since RXCOUNT stops counting at 0x7fe.
|
||||||
|
pub max_words: Option<usize>,
|
||||||
|
/// A received address is compared to the ADDRESS register (addr) using the address mask
|
||||||
|
/// (addr_mask). Those bits with a 1 in the address mask must match for there to be an address
|
||||||
|
/// match
|
||||||
|
pub addr: I2cAddress,
|
||||||
|
/// The default address mask will be 0x3ff to only allow full matches
|
||||||
|
pub addr_mask: Option<u16>,
|
||||||
|
/// Optionally specify a second I2C address the slave interface responds to
|
||||||
|
pub addr_b: Option<I2cAddress>,
|
||||||
|
pub addr_b_mask: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SlaveConfig {
|
||||||
|
/// Build a default slave config given a specified slave address to respond to
|
||||||
|
pub fn new(addr: I2cAddress) -> Self {
|
||||||
|
SlaveConfig {
|
||||||
|
tx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
rx_fe_mode: FifoEmptyMode::Stall,
|
||||||
|
max_words: None,
|
||||||
|
addr,
|
||||||
|
addr_mask: None,
|
||||||
|
addr_b: None,
|
||||||
|
addr_b_mask: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for SlaveConfig {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Base
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cBase<I2C> {
|
||||||
|
i2c: I2C,
|
||||||
|
sys_clk: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I2C> I2cBase<I2C> {
|
||||||
|
#[inline]
|
||||||
|
fn unwrap_addr(addr: I2cAddress) -> (u16, u32) {
|
||||||
|
match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! i2c_base {
|
||||||
|
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
|
||||||
|
$(
|
||||||
|
impl I2cBase<$I2CX> {
|
||||||
|
pub fn $i2cx(
|
||||||
|
i2c: $I2CX,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
ms_cfg: Option<&MasterConfig>,
|
||||||
|
sl_cfg: Option<&SlaveConfig>,
|
||||||
|
sys_cfg: Option<&mut va108xx::Sysconfig>,
|
||||||
|
) -> Self {
|
||||||
|
if let Some(sys_cfg) = sys_cfg {
|
||||||
|
enable_peripheral_clock(sys_cfg, $clk_enb);
|
||||||
|
}
|
||||||
|
let mut i2c_base = I2cBase {
|
||||||
|
i2c,
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
};
|
||||||
|
if let Some(ms_cfg) = ms_cfg {
|
||||||
|
i2c_base.cfg_master(ms_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sl_cfg) = sl_cfg {
|
||||||
|
i2c_base.cfg_slave(sl_cfg);
|
||||||
|
}
|
||||||
|
i2c_base.cfg_clk_scale(speed_mode);
|
||||||
|
i2c_base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_master(&mut self, ms_cfg: &MasterConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd);
|
||||||
|
w.dlgfilter().bit(ms_cfg.dlg_filt);
|
||||||
|
w.algfilter().bit(ms_cfg.alg_filt)
|
||||||
|
});
|
||||||
|
if let Some(ref tm_cfg) = ms_cfg.tm_cfg {
|
||||||
|
self.i2c.tmconfig().write(|w| unsafe { w.bits(tm_cfg.reg()) });
|
||||||
|
}
|
||||||
|
self.i2c.fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cfg_slave(&mut self, sl_cfg: &SlaveConfig) {
|
||||||
|
let (txfemd, rxfemd) = match (sl_cfg.tx_fe_mode, sl_cfg.rx_fe_mode) {
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false),
|
||||||
|
(FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false),
|
||||||
|
(FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true),
|
||||||
|
};
|
||||||
|
self.i2c.s0_ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(txfemd);
|
||||||
|
w.rxffmd().bit(rxfemd)
|
||||||
|
});
|
||||||
|
self.i2c.s0_fifo_clr().write(|w| {
|
||||||
|
w.rxfifo().set_bit();
|
||||||
|
w.txfifo().set_bit()
|
||||||
|
});
|
||||||
|
let max_words = sl_cfg.max_words;
|
||||||
|
if let Some(max_words) = max_words {
|
||||||
|
self.i2c
|
||||||
|
.s0_maxwords()
|
||||||
|
.write(|w| unsafe { w.bits(1 << 31 | max_words as u32) });
|
||||||
|
}
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr);
|
||||||
|
// The first bit is the read/write value. Normally, both read and write are matched
|
||||||
|
// using the RWMASK bit of the address mask register
|
||||||
|
self.i2c
|
||||||
|
.s0_address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
|
||||||
|
if let Some(addr_mask) = sl_cfg.addr_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmask()
|
||||||
|
.write(|w| unsafe { w.bits((addr_mask << 1) as u32) });
|
||||||
|
}
|
||||||
|
if let Some(addr_b) = sl_cfg.addr_b {
|
||||||
|
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
|
||||||
|
self.i2c
|
||||||
|
.s0_addressb()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) })
|
||||||
|
}
|
||||||
|
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
|
||||||
|
self.i2c
|
||||||
|
.s0_addressmaskb()
|
||||||
|
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.dlgfilter().bit(digital_filt);
|
||||||
|
w.algfilter().bit(analog_filt)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) {
|
||||||
|
self.i2c.ctrl().modify(|_, w| {
|
||||||
|
w.txfemd().bit(tx as u8 != 0);
|
||||||
|
w.rxffmd().bit(rx as u8 != 0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> u8 {
|
||||||
|
if speed_mode == I2cSpeed::Regular100khz {
|
||||||
|
((self.sys_clk.raw() / (u32::pow(10, 5) * 20)) - 1) as u8
|
||||||
|
} else {
|
||||||
|
(((10 * self.sys_clk.raw()) / u32::pow(10, 8)) - 1) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the clock scale for a given speed mode setting
|
||||||
|
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) {
|
||||||
|
self.i2c.clkscale().write(|w| unsafe {
|
||||||
|
w.bits((speed_mode as u32) << 31 | self.calc_clk_div(speed_mode) as u32)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_address(&mut self, addr: u16) {
|
||||||
|
// Load address
|
||||||
|
self.i2c
|
||||||
|
.address()
|
||||||
|
.write(|w| unsafe { w.bits((addr << 1) as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn stop_cmd(&mut self) {
|
||||||
|
self.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Stop as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique mode to use the loopback functionality
|
||||||
|
// pub struct I2cLoopback<I2C> {
|
||||||
|
// i2c_base: I2cBase<I2C>,
|
||||||
|
// master_cfg: MasterConfig,
|
||||||
|
// slave_cfg: SlaveConfig,
|
||||||
|
// }
|
||||||
|
|
||||||
|
i2c_base!(
|
||||||
|
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
|
||||||
|
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
|
||||||
|
);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Master
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cMaster<I2C, ADDR = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2C>,
|
||||||
|
_addr: PhantomData<ADDR>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! i2c_master {
|
||||||
|
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
|
||||||
|
$(
|
||||||
|
impl<ADDR> I2cMaster<$I2CX, ADDR> {
|
||||||
|
pub fn $i2cx(
|
||||||
|
i2c: $I2CX,
|
||||||
|
cfg: MasterConfig,
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) -> Self {
|
||||||
|
I2cMaster {
|
||||||
|
i2c_base: I2cBase::$i2cx(
|
||||||
|
i2c,
|
||||||
|
sys_clk,
|
||||||
|
speed_mode,
|
||||||
|
Some(&cfg),
|
||||||
|
None,
|
||||||
|
sys_cfg
|
||||||
|
),
|
||||||
|
_addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_master()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cancel_transfer(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_master(self) -> Self {
|
||||||
|
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
if *init_cmd == I2cCmd::Start {
|
||||||
|
self.i2c_base.stop_cmd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_base(
|
||||||
|
&mut self,
|
||||||
|
addr: I2cAddress,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
bytes: impl IntoIterator<Item = u8>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut iter = bytes.into_iter();
|
||||||
|
// Load address
|
||||||
|
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(init_cmd as u32) });
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = iter.next() {
|
||||||
|
self.load_fifo(next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.error_handler_write(&init_cmd);
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_buffer(
|
||||||
|
&mut self,
|
||||||
|
init_cmd: I2cCmd,
|
||||||
|
addr: I2cAddress,
|
||||||
|
output: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.write_base(addr, init_cmd, output.iter().cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
// Load number of words
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.words()
|
||||||
|
.write(|w| unsafe { w.bits(len as u32) });
|
||||||
|
let (addr, addr_mode_bit) = match addr {
|
||||||
|
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
|
||||||
|
I2cAddress::TenBit(addr) => (addr, 1 << 15),
|
||||||
|
};
|
||||||
|
// Load address
|
||||||
|
self.i2c_base.i2c.address().write(|w| unsafe {
|
||||||
|
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
// Start receive transfer
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.cmd()
|
||||||
|
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.status().read();
|
||||||
|
if status_reader.arblost().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::ArbitrationLost);
|
||||||
|
} else if status_reader.nackaddr().bit_is_set() {
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
return Err(Error::NackAddr);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_if_next_available();
|
||||||
|
read_bytes += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================================
|
||||||
|
// Embedded HAL I2C implementations
|
||||||
|
//======================================================================================
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: SevenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::Regular(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
|
||||||
|
type Error = Error;
|
||||||
|
}
|
||||||
|
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: TenBitAddress,
|
||||||
|
operations: &mut [Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for operation in operations {
|
||||||
|
match operation {
|
||||||
|
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
|
||||||
|
Operation::Write(buf) => self.write_from_buffer(
|
||||||
|
I2cCmd::StartWithStop,
|
||||||
|
I2cAddress::TenBit(address),
|
||||||
|
buf,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_master!(
|
||||||
|
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
|
||||||
|
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
|
||||||
|
);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// I2C Slave
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct I2cSlave<I2C, ADDR = SevenBitAddress> {
|
||||||
|
i2c_base: I2cBase<I2C>,
|
||||||
|
_addr: PhantomData<ADDR>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! i2c_slave {
|
||||||
|
($($I2CX:path: ($i2cx:ident, $i2cx_slave:ident),)+) => {
|
||||||
|
$(
|
||||||
|
impl<ADDR> I2cSlave<$I2CX, ADDR> {
|
||||||
|
fn $i2cx_slave(
|
||||||
|
i2c: $I2CX,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) -> Self {
|
||||||
|
I2cSlave {
|
||||||
|
i2c_base: I2cBase::$i2cx(
|
||||||
|
i2c,
|
||||||
|
sys_clk,
|
||||||
|
speed_mode,
|
||||||
|
None,
|
||||||
|
Some(&cfg),
|
||||||
|
sys_cfg
|
||||||
|
),
|
||||||
|
_addr: PhantomData,
|
||||||
|
}
|
||||||
|
.enable_slave()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_slave(self) -> Self {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_ctrl()
|
||||||
|
.modify(|_, w| w.enable().clear_bit());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_fifo(&self, word: u8) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_data()
|
||||||
|
.write(|w| unsafe { w.bits(word as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo(&self) -> u8 {
|
||||||
|
self.i2c_base.i2c.s0_data().read().bits() as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_tx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.txfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_rx_fifo(&self) {
|
||||||
|
self.i2c_base
|
||||||
|
.i2c
|
||||||
|
.s0_fifo_clr()
|
||||||
|
.write(|w| w.rxfifo().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last address that was matched by the slave control and the corresponding
|
||||||
|
/// master direction
|
||||||
|
pub fn last_address(&self) -> (I2cDirection, u32) {
|
||||||
|
let bits = self.i2c_base.i2c.s0_lastaddress().read().bits();
|
||||||
|
match bits & 0x01 {
|
||||||
|
0 => (I2cDirection::Send, bits >> 1),
|
||||||
|
1 => (I2cDirection::Read, bits >> 1),
|
||||||
|
_ => (I2cDirection::Send, bits >> 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, output: &[u8]) -> Result<(), Error> {
|
||||||
|
let len = output.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
let mut bytes = output.iter();
|
||||||
|
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||||
|
// but not all of it because the transaction might fail immediately
|
||||||
|
const FILL_DEPTH: usize = 12;
|
||||||
|
|
||||||
|
// load the FIFO
|
||||||
|
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
|
||||||
|
self.load_fifo(*bytes.next().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
let mut load_if_next_available = || {
|
||||||
|
if let Some(next_byte) = bytes.next() {
|
||||||
|
self.load_fifo(*next_byte);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
if status_reader.nackdata().bit_is_set() {
|
||||||
|
self.clear_tx_fifo();
|
||||||
|
return Err(Error::NackData);
|
||||||
|
} else if status_reader.idle().bit_is_set() {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
while !status_reader.txnfull().bit_is_set() {
|
||||||
|
load_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let len = buffer.len();
|
||||||
|
// It should theoretically possible to transfer larger data sizes by tracking
|
||||||
|
// the number of sent words and setting it to 0x7fe as soon as only that many
|
||||||
|
// bytes are remaining. However, large transfer like this are not common. This
|
||||||
|
// feature will therefore not be supported for now.
|
||||||
|
if len > 0x7fe {
|
||||||
|
return Err(Error::DataTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the receive FIFO
|
||||||
|
self.clear_rx_fifo();
|
||||||
|
|
||||||
|
let mut buf_iter = buffer.iter_mut();
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
let mut read_if_next_available = || {
|
||||||
|
if let Some(next_byte) = buf_iter.next() {
|
||||||
|
*next_byte = self.read_fifo();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let status_reader = self.i2c_base.i2c.s0_status().read();
|
||||||
|
if status_reader.idle().bit_is_set() {
|
||||||
|
if read_bytes != len {
|
||||||
|
return Err(Error::InsufficientDataReceived);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else if status_reader.rxnempty().bit_is_set() {
|
||||||
|
read_bytes += 1;
|
||||||
|
read_if_next_available();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl I2cSlave<$I2CX, SevenBitAddress> {
|
||||||
|
/// Create a new I2C slave for seven bit addresses
|
||||||
|
///
|
||||||
|
/// Returns a [`Error::WrongAddrMode`] error if a ten bit address is passed
|
||||||
|
pub fn i2ca(
|
||||||
|
i2c: $I2CX,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
if let I2cAddress::TenBit(_) = cfg.addr {
|
||||||
|
return Err(Error::WrongAddrMode);
|
||||||
|
}
|
||||||
|
Ok(Self::$i2cx_slave(i2c, cfg, sys_clk, speed_mode, sys_cfg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2cSlave<$I2CX, TenBitAddress> {
|
||||||
|
pub fn $i2cx(
|
||||||
|
i2c: $I2CX,
|
||||||
|
cfg: SlaveConfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
speed_mode: I2cSpeed,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) -> Self {
|
||||||
|
Self::$i2cx_slave(i2c, cfg, sys_clk, speed_mode, sys_cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_slave!(pac::I2ca: (i2ca, i2ca_slave), pac::I2cb: (i2cb, i2cb_slave),);
|
99
va108xx-hal/src/lib.rs
Normal file
99
va108xx-hal/src/lib.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
pub use va108xx;
|
||||||
|
pub use va108xx as pac;
|
||||||
|
|
||||||
|
pub mod clock;
|
||||||
|
pub mod gpio;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod prelude;
|
||||||
|
pub mod pwm;
|
||||||
|
pub mod spi;
|
||||||
|
pub mod sysconfig;
|
||||||
|
pub mod time;
|
||||||
|
pub mod timer;
|
||||||
|
pub mod typelevel;
|
||||||
|
pub mod uart;
|
||||||
|
pub mod utility;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
|
pub enum FunSel {
|
||||||
|
Sel1 = 0b01,
|
||||||
|
Sel2 = 0b10,
|
||||||
|
Sel3 = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PortSel {
|
||||||
|
PortA,
|
||||||
|
PortB,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PeripheralSelect {
|
||||||
|
PortA = 0,
|
||||||
|
PortB = 1,
|
||||||
|
Spi0 = 4,
|
||||||
|
Spi1 = 5,
|
||||||
|
Spi2 = 6,
|
||||||
|
Uart0 = 8,
|
||||||
|
Uart1 = 9,
|
||||||
|
I2c0 = 16,
|
||||||
|
I2c1 = 17,
|
||||||
|
Irqsel = 21,
|
||||||
|
Ioconfig = 22,
|
||||||
|
Utility = 23,
|
||||||
|
Gpio = 24,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
||||||
|
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||||
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
||||||
|
/// this steps themselves
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct IrqCfg {
|
||||||
|
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||||
|
pub irq: pac::Interrupt,
|
||||||
|
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
|
||||||
|
pub route: bool,
|
||||||
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrqCfg {
|
||||||
|
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
||||||
|
IrqCfg { irq, route, enable }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct InvalidPin(pub(crate) ());
|
||||||
|
|
||||||
|
/// Can be used to manually manipulate the function select of port pins
|
||||||
|
pub fn port_mux(
|
||||||
|
ioconfig: &mut pac::Ioconfig,
|
||||||
|
port: PortSel,
|
||||||
|
pin: u8,
|
||||||
|
funsel: FunSel,
|
||||||
|
) -> Result<(), InvalidPin> {
|
||||||
|
match port {
|
||||||
|
PortSel::PortA => {
|
||||||
|
if pin > 31 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.porta(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PortSel::PortB => {
|
||||||
|
if pin > 23 {
|
||||||
|
return Err(InvalidPin(()));
|
||||||
|
}
|
||||||
|
ioconfig
|
||||||
|
.portb0(pin as usize)
|
||||||
|
.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
va108xx-hal/src/prelude.rs
Normal file
3
va108xx-hal/src/prelude.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Prelude
|
||||||
|
pub use fugit::ExtU32 as _;
|
||||||
|
pub use fugit::RateExtU32 as _;
|
387
va108xx-hal/src/pwm.rs
Normal file
387
va108xx-hal/src/pwm.rs
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
|
//!
|
||||||
|
//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/pwm.rs)
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::pac;
|
||||||
|
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||||
|
pub use crate::{gpio::PinId, time::Hertz, timer::*};
|
||||||
|
|
||||||
|
const DUTY_MAX: u16 = u16::MAX;
|
||||||
|
|
||||||
|
pub struct PwmBase {
|
||||||
|
sys_clk: Hertz,
|
||||||
|
/// For PWMB, this is the upper limit
|
||||||
|
current_duty: u16,
|
||||||
|
/// For PWMA, this value will not be used
|
||||||
|
current_lower_limit: u16,
|
||||||
|
current_period: Hertz,
|
||||||
|
current_rst_val: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StatusSelPwm {
|
||||||
|
PwmA = 3,
|
||||||
|
PwmB = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PwmA {}
|
||||||
|
pub struct PwmB {}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Common
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pwm_common_func {
|
||||||
|
() => {
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_a(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_pwm_b(&mut self) {
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||||
|
self.pwm_base.current_period = period.into();
|
||||||
|
// Avoid division by 0
|
||||||
|
if self.pwm_base.current_period.raw() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.pwm_base.current_rst_val =
|
||||||
|
self.pwm_base.sys_clk.raw() / self.pwm_base.current_period.raw();
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.reg.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn period(&self) -> Hertz {
|
||||||
|
self.pwm_base.current_period
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn duty(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pwmb_func {
|
||||||
|
() => {
|
||||||
|
pub fn pwmb_lower_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_lower_limit
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwmb_upper_limit(&self) -> u16 {
|
||||||
|
self.pwm_base.current_duty
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the lower limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is larger than
|
||||||
|
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_lower_limit = duty;
|
||||||
|
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_lower_limit as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwmb_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwmb_val as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the higher limit for PWMB
|
||||||
|
///
|
||||||
|
/// The PWM signal will be 1 as long as the current RST counter is smaller than
|
||||||
|
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
|
||||||
|
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
|
||||||
|
/// state
|
||||||
|
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* self.pwm_base.current_duty as u64)
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Strongly typed PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
||||||
|
reg: TimAndPinRegister<Pin, Tim>,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim, Mode> PwmPin<Pin, Tim, Mode>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
/// Create a new stronlgy typed PWM pin
|
||||||
|
pub fn new(
|
||||||
|
vtp: (Pin, Tim),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin = PwmPin {
|
||||||
|
pwm_base: PwmBase {
|
||||||
|
current_duty: 0,
|
||||||
|
current_lower_limit: 0,
|
||||||
|
current_period: initial_period.into(),
|
||||||
|
current_rst_val: 0,
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
},
|
||||||
|
reg: unsafe { TimAndPinRegister::new(vtp.0, vtp.1) },
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||||
|
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||||
|
sys_cfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) });
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin.set_period(initial_period);
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
pub fn release(self) -> (Pin, Tim) {
|
||||||
|
self.reg.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM, PwmB>> for PwmPin<PIN, TIM, PwmA>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmA>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwma(
|
||||||
|
vtp: (Pin, Tim),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmA> = Self::new(vtp, sys_clk, sys_cfg, initial_period);
|
||||||
|
pin.enable_pwm_a();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||||
|
where
|
||||||
|
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
|
||||||
|
{
|
||||||
|
pub fn pwmb(
|
||||||
|
vtp: (Pin, Tim),
|
||||||
|
sys_clk: impl Into<Hertz> + Copy,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
initial_period: impl Into<Hertz> + Copy,
|
||||||
|
) -> Self {
|
||||||
|
let mut pin: PwmPin<Pin, Tim, PwmB> = Self::new(vtp, sys_clk, sys_cfg, initial_period);
|
||||||
|
pin.enable_pwm_b();
|
||||||
|
pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Reduced PWM pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Reduced version where type information is deleted
|
||||||
|
pub struct ReducedPwmPin<Mode = PwmA> {
|
||||||
|
reg: TimDynRegister,
|
||||||
|
pwm_base: PwmBase,
|
||||||
|
pin_id: DynPinId,
|
||||||
|
mode: PhantomData<Mode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> {
|
||||||
|
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
||||||
|
ReducedPwmPin {
|
||||||
|
reg: TimDynRegister::from(pwm_pin.reg),
|
||||||
|
pwm_base: pwm_pin.pwm_base,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<MODE> ReducedPwmPin<MODE> {
|
||||||
|
pwm_common_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_b();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||||
|
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
||||||
|
let mut pwmb = Self {
|
||||||
|
reg: other.reg,
|
||||||
|
pwm_base: other.pwm_base,
|
||||||
|
pin_id: other.pin_id,
|
||||||
|
mode: PhantomData,
|
||||||
|
};
|
||||||
|
pwmb.enable_pwm_a();
|
||||||
|
pwmb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// PWMB implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReducedPwmPin<PwmB> {
|
||||||
|
pwmb_func!();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL implementation: PWMA only
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::ErrorType for PwmPin<Pin, Tim> {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::ErrorType for ReducedPwmPin {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin, Tim> {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
DUTY_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.pwm_base.current_duty = duty;
|
||||||
|
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||||
|
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||||
|
/ DUTY_MAX as u64;
|
||||||
|
self.reg
|
||||||
|
.reg()
|
||||||
|
.pwma_value()
|
||||||
|
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding u16 duty cycle from a percent value ranging between 0.0 and 1.0.
|
||||||
|
///
|
||||||
|
/// Please note that this might load a lot of floating point code because this processor does not
|
||||||
|
/// have a FPU
|
||||||
|
pub fn get_duty_from_percent(percent: f32) -> u16 {
|
||||||
|
if percent > 1.0 {
|
||||||
|
DUTY_MAX
|
||||||
|
} else if percent <= 0.0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(percent * DUTY_MAX as f32) as u16
|
||||||
|
}
|
||||||
|
}
|
1214
va108xx-hal/src/spi.rs
Normal file
1214
va108xx-hal/src/spi.rs
Normal file
File diff suppressed because it is too large
Load Diff
56
va108xx-hal/src/sysconfig.rs
Normal file
56
va108xx-hal/src/sysconfig.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use crate::{pac, PeripheralSelect};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct InvalidounterResetVal(pub(crate) ());
|
||||||
|
|
||||||
|
/// Enable scrubbing for the ROM
|
||||||
|
///
|
||||||
|
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0
|
||||||
|
/// (equivalent to disabling) or larger than 24 bits
|
||||||
|
pub fn enable_rom_scrubbing(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
scrub_rate: u32,
|
||||||
|
) -> Result<(), InvalidounterResetVal> {
|
||||||
|
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
||||||
|
return Err(InvalidounterResetVal(()));
|
||||||
|
}
|
||||||
|
syscfg.rom_scrub().write(|w| unsafe { w.bits(scrub_rate) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
|
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable scrubbing for the RAM
|
||||||
|
///
|
||||||
|
/// Returns [`UtilityError::InvalidCounterResetVal`] if the scrub rate is 0
|
||||||
|
/// (equivalent to disabling) or larger than 24 bits
|
||||||
|
pub fn enable_ram_scrubbing(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
scrub_rate: u32,
|
||||||
|
) -> Result<(), InvalidounterResetVal> {
|
||||||
|
if scrub_rate == 0 || scrub_rate > u32::pow(2, 24) {
|
||||||
|
return Err(InvalidounterResetVal(()));
|
||||||
|
}
|
||||||
|
syscfg.ram_scrub().write(|w| unsafe { w.bits(scrub_rate) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
|
||||||
|
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral
|
||||||
|
/// in a reset state
|
||||||
|
pub fn clear_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph_sel as u8)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_reset_bit(syscfg: &mut pac::Sysconfig, periph_sel: PeripheralSelect) {
|
||||||
|
syscfg
|
||||||
|
.peripheral_reset()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) });
|
||||||
|
}
|
26
va108xx-hal/src/time.rs
Normal file
26
va108xx-hal/src/time.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//! Time units
|
||||||
|
|
||||||
|
// Frequency based
|
||||||
|
|
||||||
|
/// Hertz
|
||||||
|
pub type Hertz = fugit::HertzU32;
|
||||||
|
|
||||||
|
/// KiloHertz
|
||||||
|
pub type KiloHertz = fugit::KilohertzU32;
|
||||||
|
|
||||||
|
/// MegaHertz
|
||||||
|
pub type MegaHertz = fugit::MegahertzU32;
|
||||||
|
|
||||||
|
// Period based
|
||||||
|
|
||||||
|
/// Seconds
|
||||||
|
pub type Seconds = fugit::SecsDurationU32;
|
||||||
|
|
||||||
|
/// Milliseconds
|
||||||
|
pub type Milliseconds = fugit::MillisDurationU32;
|
||||||
|
|
||||||
|
/// Microseconds
|
||||||
|
pub type Microseconds = fugit::MicrosDurationU32;
|
||||||
|
|
||||||
|
/// Nanoseconds
|
||||||
|
pub type Nanoseconds = fugit::NanosDurationU32;
|
790
va108xx-hal/src/timer.rs
Normal file
790
va108xx-hal/src/timer.rs
Normal file
@ -0,0 +1,790 @@
|
|||||||
|
//! API for the TIM peripherals
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/timer-ticks.rs)
|
||||||
|
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/cascade.rs)
|
||||||
|
pub use crate::IrqCfg;
|
||||||
|
use crate::{
|
||||||
|
clock::{enable_peripheral_clock, PeripheralClocks},
|
||||||
|
gpio::{
|
||||||
|
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
|
||||||
|
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
|
||||||
|
PA9, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB16, PB17, PB18, PB19, PB2, PB20, PB21,
|
||||||
|
PB22, PB23, PB3, PB4, PB5, PB6,
|
||||||
|
},
|
||||||
|
pac::{self, tim0},
|
||||||
|
time::Hertz,
|
||||||
|
timer,
|
||||||
|
typelevel::Sealed,
|
||||||
|
utility::unmask_irq,
|
||||||
|
};
|
||||||
|
use core::cell::Cell;
|
||||||
|
use cortex_m::interrupt::Mutex;
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
|
||||||
|
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
|
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Defintions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Interrupt events
|
||||||
|
pub enum Event {
|
||||||
|
/// Timer timed out / count down ended
|
||||||
|
TimeOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct CascadeCtrl {
|
||||||
|
/// Enable Cascade 0 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd0: bool,
|
||||||
|
/// Invert Cascade 0, making it active low
|
||||||
|
pub inv_csd0: bool,
|
||||||
|
/// Enable Cascade 1 signal active as a requirement for counting
|
||||||
|
pub enb_start_src_csd1: bool,
|
||||||
|
/// Invert Cascade 1, making it active low
|
||||||
|
pub inv_csd1: bool,
|
||||||
|
/// Specify required operation if both Cascade 0 and Cascade 1 are active.
|
||||||
|
/// 0 is a logical AND of both cascade signals, 1 is a logical OR
|
||||||
|
pub dual_csd_op: bool,
|
||||||
|
/// Enable trigger mode for Cascade 0. In trigger mode, couting will start with the selected
|
||||||
|
/// cascade signal active, but once the counter is active, cascade control will be ignored
|
||||||
|
pub trg_csd0: bool,
|
||||||
|
/// Trigger mode, identical to [`trg_csd0`](CascadeCtrl) but for Cascade 1
|
||||||
|
pub trg_csd1: bool,
|
||||||
|
/// Enable Cascade 2 signal active as a requirement to stop counting. This mode is similar
|
||||||
|
/// to the REQ_STOP control bit, but signalled by a Cascade source
|
||||||
|
pub enb_stop_src_csd2: bool,
|
||||||
|
/// Invert Cascade 2, making it active low
|
||||||
|
pub inv_csd2: bool,
|
||||||
|
/// The counter is automatically disabled if the corresponding Cascade 2 level-sensitive input
|
||||||
|
/// souce is active when the count reaches 0. If the counter is not 0, the cascade control is
|
||||||
|
/// ignored
|
||||||
|
pub trg_csd2: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum CascadeSel {
|
||||||
|
Csd0 = 0,
|
||||||
|
Csd1 = 1,
|
||||||
|
Csd2 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum CascadeSource {
|
||||||
|
PortABase = 0,
|
||||||
|
PortBBase = 32,
|
||||||
|
TimBase = 64,
|
||||||
|
RamSbe = 96,
|
||||||
|
RamMbe = 97,
|
||||||
|
RomSbe = 98,
|
||||||
|
RomMbe = 99,
|
||||||
|
Txev = 100,
|
||||||
|
ClockDividerBase = 120,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum TimerErrors {
|
||||||
|
Canceled,
|
||||||
|
/// Invalid input for Cascade source
|
||||||
|
InvalidCsdSourceInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Valid TIM and PIN combinations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait TimPin {
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ValidTim {
|
||||||
|
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
|
||||||
|
const TIM_ID: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! tim_marker {
|
||||||
|
($TIMX:path, $ID:expr) => {
|
||||||
|
impl ValidTim for $TIMX {
|
||||||
|
const TIM_ID: u8 = $ID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tim_marker!(pac::Tim0, 0);
|
||||||
|
tim_marker!(pac::Tim1, 1);
|
||||||
|
tim_marker!(pac::Tim2, 2);
|
||||||
|
tim_marker!(pac::Tim3, 3);
|
||||||
|
tim_marker!(pac::Tim4, 4);
|
||||||
|
tim_marker!(pac::Tim5, 5);
|
||||||
|
tim_marker!(pac::Tim6, 6);
|
||||||
|
tim_marker!(pac::Tim7, 7);
|
||||||
|
tim_marker!(pac::Tim8, 8);
|
||||||
|
tim_marker!(pac::Tim9, 9);
|
||||||
|
tim_marker!(pac::Tim10, 10);
|
||||||
|
tim_marker!(pac::Tim11, 11);
|
||||||
|
tim_marker!(pac::Tim12, 12);
|
||||||
|
tim_marker!(pac::Tim13, 13);
|
||||||
|
tim_marker!(pac::Tim14, 14);
|
||||||
|
tim_marker!(pac::Tim15, 15);
|
||||||
|
tim_marker!(pac::Tim16, 16);
|
||||||
|
tim_marker!(pac::Tim17, 17);
|
||||||
|
tim_marker!(pac::Tim18, 18);
|
||||||
|
tim_marker!(pac::Tim19, 19);
|
||||||
|
tim_marker!(pac::Tim20, 20);
|
||||||
|
tim_marker!(pac::Tim21, 21);
|
||||||
|
tim_marker!(pac::Tim22, 22);
|
||||||
|
tim_marker!(pac::Tim23, 23);
|
||||||
|
|
||||||
|
pub trait ValidTimAndPin<PIN: TimPin, TIM: ValidTim>: Sealed {}
|
||||||
|
|
||||||
|
macro_rules! pin_and_tim {
|
||||||
|
($PAX:ident, $ALTFUNC:ident, $ID:expr, $TIMX:path) => {
|
||||||
|
impl TimPin for Pin<$PAX, $ALTFUNC>
|
||||||
|
where
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
const DYN: DynPinId = $PAX::DYN;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> ValidTimAndPin<PIN, TIM> for (Pin<$PAX, $ALTFUNC>, $TIMX)
|
||||||
|
where
|
||||||
|
Pin<$PAX, $ALTFUNC>: TimPin,
|
||||||
|
$PAX: PinId,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for (Pin<$PAX, $ALTFUNC>, $TIMX) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_and_tim!(PA31, AltFunc2, 23, pac::Tim23);
|
||||||
|
pin_and_tim!(PA30, AltFunc2, 22, pac::Tim22);
|
||||||
|
pin_and_tim!(PA29, AltFunc2, 21, pac::Tim21);
|
||||||
|
pin_and_tim!(PA28, AltFunc2, 20, pac::Tim20);
|
||||||
|
pin_and_tim!(PA27, AltFunc2, 19, pac::Tim19);
|
||||||
|
pin_and_tim!(PA26, AltFunc2, 18, pac::Tim18);
|
||||||
|
pin_and_tim!(PA25, AltFunc2, 17, pac::Tim17);
|
||||||
|
pin_and_tim!(PA24, AltFunc2, 16, pac::Tim16);
|
||||||
|
|
||||||
|
pin_and_tim!(PA15, AltFunc1, 15, pac::Tim15);
|
||||||
|
pin_and_tim!(PA14, AltFunc1, 14, pac::Tim14);
|
||||||
|
pin_and_tim!(PA13, AltFunc1, 13, pac::Tim13);
|
||||||
|
pin_and_tim!(PA12, AltFunc1, 12, pac::Tim12);
|
||||||
|
pin_and_tim!(PA11, AltFunc1, 11, pac::Tim11);
|
||||||
|
pin_and_tim!(PA10, AltFunc1, 10, pac::Tim10);
|
||||||
|
pin_and_tim!(PA9, AltFunc1, 9, pac::Tim9);
|
||||||
|
pin_and_tim!(PA8, AltFunc1, 8, pac::Tim8);
|
||||||
|
pin_and_tim!(PA7, AltFunc1, 7, pac::Tim7);
|
||||||
|
pin_and_tim!(PA6, AltFunc1, 6, pac::Tim6);
|
||||||
|
pin_and_tim!(PA5, AltFunc1, 5, pac::Tim5);
|
||||||
|
pin_and_tim!(PA4, AltFunc1, 4, pac::Tim4);
|
||||||
|
pin_and_tim!(PA3, AltFunc1, 3, pac::Tim3);
|
||||||
|
pin_and_tim!(PA2, AltFunc1, 2, pac::Tim2);
|
||||||
|
pin_and_tim!(PA1, AltFunc1, 1, pac::Tim1);
|
||||||
|
pin_and_tim!(PA0, AltFunc1, 0, pac::Tim0);
|
||||||
|
|
||||||
|
pin_and_tim!(PB23, AltFunc3, 23, pac::Tim23);
|
||||||
|
pin_and_tim!(PB22, AltFunc3, 22, pac::Tim22);
|
||||||
|
pin_and_tim!(PB21, AltFunc3, 21, pac::Tim21);
|
||||||
|
pin_and_tim!(PB20, AltFunc3, 20, pac::Tim20);
|
||||||
|
pin_and_tim!(PB19, AltFunc3, 19, pac::Tim19);
|
||||||
|
pin_and_tim!(PB18, AltFunc3, 18, pac::Tim18);
|
||||||
|
pin_and_tim!(PB17, AltFunc3, 17, pac::Tim17);
|
||||||
|
pin_and_tim!(PB16, AltFunc3, 16, pac::Tim16);
|
||||||
|
pin_and_tim!(PB15, AltFunc3, 15, pac::Tim15);
|
||||||
|
pin_and_tim!(PB14, AltFunc3, 14, pac::Tim14);
|
||||||
|
pin_and_tim!(PB13, AltFunc3, 13, pac::Tim13);
|
||||||
|
pin_and_tim!(PB12, AltFunc3, 12, pac::Tim12);
|
||||||
|
pin_and_tim!(PB11, AltFunc3, 11, pac::Tim11);
|
||||||
|
pin_and_tim!(PB10, AltFunc3, 10, pac::Tim10);
|
||||||
|
|
||||||
|
pin_and_tim!(PB6, AltFunc3, 6, pac::Tim6);
|
||||||
|
pin_and_tim!(PB5, AltFunc3, 5, pac::Tim5);
|
||||||
|
pin_and_tim!(PB4, AltFunc3, 4, pac::Tim4);
|
||||||
|
pin_and_tim!(PB3, AltFunc3, 3, pac::Tim3);
|
||||||
|
pin_and_tim!(PB2, AltFunc3, 2, pac::Tim2);
|
||||||
|
pin_and_tim!(PB1, AltFunc3, 1, pac::Tim1);
|
||||||
|
pin_and_tim!(PB0, AltFunc3, 0, pac::Tim0);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface for TIM registers and TIM pins
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type TimRegBlock = tim0::RegisterBlock;
|
||||||
|
|
||||||
|
/// Register interface.
|
||||||
|
///
|
||||||
|
/// This interface provides valid TIM pins a way to access their corresponding TIM
|
||||||
|
/// registers
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`tim_id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
pub(super) unsafe trait TimRegInterface {
|
||||||
|
fn tim_id(&self) -> u8;
|
||||||
|
|
||||||
|
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
|
||||||
|
|
||||||
|
/// All 24 TIM blocks are identical. This helper functions returns the correct
|
||||||
|
/// memory mapped peripheral depending on the TIM ID.
|
||||||
|
#[inline(always)]
|
||||||
|
fn reg(&self) -> &TimRegBlock {
|
||||||
|
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.tim_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the reset bit of the TIM, holding it in reset
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn clear_tim_reset_bit(&self) {
|
||||||
|
unsafe {
|
||||||
|
va108xx::Peripherals::steal()
|
||||||
|
.sysconfig
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn set_tim_reset_bit(&self) {
|
||||||
|
unsafe {
|
||||||
|
va108xx::Peripherals::steal()
|
||||||
|
.sysconfig
|
||||||
|
.tim_reset()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`ValidTimAndPin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(super) struct TimAndPinRegister<Pin: TimPin, Tim: ValidTim> {
|
||||||
|
pin: Pin,
|
||||||
|
tim: Tim,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimRegister<TIM: ValidTim> {
|
||||||
|
tim: TIM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> TimRegister<TIM> {
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(tim: TIM) -> Self {
|
||||||
|
TimRegister { tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> TIM {
|
||||||
|
self.tim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<TIM: ValidTim> TimRegInterface for TimRegister<TIM> {
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM>
|
||||||
|
where
|
||||||
|
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self {
|
||||||
|
TimAndPinRegister { pin, tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release(self) -> (PIN, TIM) {
|
||||||
|
(self.pin, self.tim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimAndPinRegister<PIN, TIM> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TimDynRegister {
|
||||||
|
tim_id: u8,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pin_id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PIN: TimPin, TIM: ValidTim> From<TimAndPinRegister<PIN, TIM>> for TimDynRegister {
|
||||||
|
fn from(_reg: TimAndPinRegister<PIN, TIM>) -> Self {
|
||||||
|
Self {
|
||||||
|
tim_id: TIM::TIM_ID,
|
||||||
|
pin_id: PIN::DYN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl TimRegInterface for TimDynRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
self.tim_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Timers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Hardware timers
|
||||||
|
pub struct CountDownTimer<TIM: ValidTim> {
|
||||||
|
tim: TimRegister<TIM>,
|
||||||
|
curr_freq: Hertz,
|
||||||
|
irq_cfg: Option<IrqCfg>,
|
||||||
|
sys_clk: Hertz,
|
||||||
|
rst_val: u32,
|
||||||
|
last_cnt: u32,
|
||||||
|
listening: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<TIM: ValidTim> TimRegInterface for CountDownTimer<TIM> {
|
||||||
|
fn tim_id(&self) -> u8 {
|
||||||
|
TIM::TIM_ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! csd_sel {
|
||||||
|
($func_name:ident, $csd_reg:ident) => {
|
||||||
|
/// Configure the Cascade sources
|
||||||
|
pub fn $func_name(
|
||||||
|
&mut self,
|
||||||
|
src: CascadeSource,
|
||||||
|
id: Option<u8>,
|
||||||
|
) -> Result<(), TimerErrors> {
|
||||||
|
let mut id_num = 0;
|
||||||
|
if let CascadeSource::PortABase
|
||||||
|
| CascadeSource::PortBBase
|
||||||
|
| CascadeSource::ClockDividerBase
|
||||||
|
| CascadeSource::TimBase = src
|
||||||
|
{
|
||||||
|
if id.is_none() {
|
||||||
|
return Err(TimerErrors::InvalidCsdSourceInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id.is_some() {
|
||||||
|
id_num = id.unwrap();
|
||||||
|
}
|
||||||
|
match src {
|
||||||
|
CascadeSource::PortABase => {
|
||||||
|
if id_num > 55 {
|
||||||
|
return Err(TimerErrors::InvalidCsdSourceInput);
|
||||||
|
}
|
||||||
|
self.tim.reg().$csd_reg().write(|w| unsafe {
|
||||||
|
w.cassel().bits(CascadeSource::PortABase as u8 + id_num)
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
CascadeSource::PortBBase => {
|
||||||
|
if id_num > 23 {
|
||||||
|
return Err(TimerErrors::InvalidCsdSourceInput);
|
||||||
|
}
|
||||||
|
self.tim.reg().$csd_reg().write(|w| unsafe {
|
||||||
|
w.cassel().bits(CascadeSource::PortBBase as u8 + id_num)
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
CascadeSource::TimBase => {
|
||||||
|
if id_num > 23 {
|
||||||
|
return Err(TimerErrors::InvalidCsdSourceInput);
|
||||||
|
}
|
||||||
|
self.tim.reg().$csd_reg().write(|w| unsafe {
|
||||||
|
w.cassel().bits(CascadeSource::TimBase as u8 + id_num)
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
CascadeSource::ClockDividerBase => {
|
||||||
|
if id_num > 7 {
|
||||||
|
return Err(TimerErrors::InvalidCsdSourceInput);
|
||||||
|
}
|
||||||
|
self.tim.reg().cascade0().write(|w| unsafe {
|
||||||
|
w.cassel()
|
||||||
|
.bits(CascadeSource::ClockDividerBase as u8 + id_num)
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.$csd_reg()
|
||||||
|
.write(|w| unsafe { w.cassel().bits(src as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||||
|
/// Configures a TIM peripheral as a periodic count down timer
|
||||||
|
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self {
|
||||||
|
enable_tim_clk(syscfg, TIM::TIM_ID);
|
||||||
|
let cd_timer = CountDownTimer {
|
||||||
|
tim: unsafe { TimRegister::new(tim) },
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
irq_cfg: None,
|
||||||
|
rst_val: 0,
|
||||||
|
curr_freq: 0.Hz(),
|
||||||
|
listening: false,
|
||||||
|
last_cnt: 0,
|
||||||
|
};
|
||||||
|
cd_timer
|
||||||
|
.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.enable().set_bit());
|
||||||
|
cd_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen for events. Depending on the IRQ configuration, this also activates the IRQ in the
|
||||||
|
/// IRQSEL peripheral for the provided interrupt and unmasks the interrupt
|
||||||
|
pub fn listen(
|
||||||
|
&mut self,
|
||||||
|
event: Event,
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
|
sys_cfg: Option<&mut pac::Sysconfig>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::TimeOut => {
|
||||||
|
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
|
||||||
|
self.irq_cfg = Some(irq_cfg);
|
||||||
|
if irq_cfg.route {
|
||||||
|
if let Some(sys_cfg) = sys_cfg {
|
||||||
|
enable_peripheral_clock(sys_cfg, PeripheralClocks::Irqsel);
|
||||||
|
}
|
||||||
|
if let Some(irq_sel) = irq_sel {
|
||||||
|
irq_sel
|
||||||
|
.tim0(TIM::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.listening = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlisten(
|
||||||
|
&mut self,
|
||||||
|
event: Event,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irqsel: &mut pac::Irqsel,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::TimeOut => {
|
||||||
|
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||||
|
irqsel
|
||||||
|
.tim0(TIM::TIM_ID as usize)
|
||||||
|
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||||
|
self.disable_interrupt();
|
||||||
|
self.listening = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable_interrupt(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable_interrupt(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self, syscfg: &mut pac::Sysconfig) -> TIM {
|
||||||
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
|
syscfg
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << TIM::TIM_ID)) });
|
||||||
|
self.tim.release()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the count down timer with a timeout but do not start it.
|
||||||
|
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
self.curr_freq = timeout.into();
|
||||||
|
self.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
|
||||||
|
self.set_reload(self.rst_val);
|
||||||
|
self.set_count(self.rst_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_reload(&mut self, val: u32) {
|
||||||
|
self.tim.reg().rst_value().write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_count(&mut self, val: u32) {
|
||||||
|
self.tim.reg().cnt_value().write(|w| unsafe { w.bits(val) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn count(&self) -> u32 {
|
||||||
|
self.tim.reg().cnt_value().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||||
|
if let Some(irq_cfg) = self.irq_cfg {
|
||||||
|
self.enable_interrupt();
|
||||||
|
if irq_cfg.enable {
|
||||||
|
unmask_irq(irq_cfg.irq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the counter, setting both enable and active bit to 0
|
||||||
|
pub fn auto_disable(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_disable().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This option only applies when the Auto-Disable functionality is 0.
|
||||||
|
///
|
||||||
|
/// The active bit is changed to 0 when count reaches 0, but the counter stays
|
||||||
|
/// enabled. When Auto-Disable is 1, Auto-Deactivate is implied
|
||||||
|
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||||
|
} else {
|
||||||
|
self.tim
|
||||||
|
.reg()
|
||||||
|
.ctrl()
|
||||||
|
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the cascade parameters
|
||||||
|
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
|
||||||
|
self.tim.reg().csd_ctrl().write(|w| {
|
||||||
|
w.csden0().bit(ctrl.enb_start_src_csd0);
|
||||||
|
w.csdinv0().bit(ctrl.inv_csd0);
|
||||||
|
w.csden1().bit(ctrl.enb_start_src_csd1);
|
||||||
|
w.csdinv1().bit(ctrl.inv_csd1);
|
||||||
|
w.dcasop().bit(ctrl.dual_csd_op);
|
||||||
|
w.csdtrg0().bit(ctrl.trg_csd0);
|
||||||
|
w.csdtrg1().bit(ctrl.trg_csd1);
|
||||||
|
w.csden2().bit(ctrl.enb_stop_src_csd2);
|
||||||
|
w.csdinv2().bit(ctrl.inv_csd2);
|
||||||
|
w.csdtrg2().bit(ctrl.trg_csd2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
csd_sel!(cascade_0_source, cascade0);
|
||||||
|
csd_sel!(cascade_1_source, cascade1);
|
||||||
|
csd_sel!(cascade_2_source, cascade2);
|
||||||
|
|
||||||
|
pub fn curr_freq(&self) -> Hertz {
|
||||||
|
self.curr_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listening(&self) -> bool {
|
||||||
|
self.listening
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CountDown implementation for TIMx
|
||||||
|
impl<TIM: ValidTim> CountDownTimer<TIM> {
|
||||||
|
#[inline]
|
||||||
|
pub fn start<T>(&mut self, timeout: T)
|
||||||
|
where
|
||||||
|
T: Into<Hertz>,
|
||||||
|
{
|
||||||
|
self.load(timeout);
|
||||||
|
self.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||||
|
/// flag and restart the time if configured correctly
|
||||||
|
pub fn wait(&mut self) -> nb::Result<(), void::Void> {
|
||||||
|
let cnt = self.tim.reg().cnt_value().read().bits();
|
||||||
|
if (cnt > self.last_cnt) || cnt == 0 {
|
||||||
|
self.last_cnt = self.rst_val;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.last_cnt = cnt;
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel(&mut self) -> Result<(), TimerErrors> {
|
||||||
|
if !self.tim.reg().ctrl().read().enable().bit_is_set() {
|
||||||
|
return Err(TimerErrors::Canceled);
|
||||||
|
}
|
||||||
|
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountDownTimer<TIM> {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ticks = (u64::from(ns)) * (u64::from(self.sys_clk.raw())) / 1_000_000_000;
|
||||||
|
|
||||||
|
let full_cycles = ticks >> 32;
|
||||||
|
let mut last_count;
|
||||||
|
let mut new_count;
|
||||||
|
if full_cycles > 0 {
|
||||||
|
self.set_reload(u32::MAX);
|
||||||
|
self.set_count(u32::MAX);
|
||||||
|
self.enable();
|
||||||
|
|
||||||
|
for _ in 0..full_cycles {
|
||||||
|
// Always ensure that both values are the same at the start.
|
||||||
|
new_count = self.count();
|
||||||
|
last_count = new_count;
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 {
|
||||||
|
// Wait till timer has wrapped.
|
||||||
|
while self.count() == 0 {
|
||||||
|
cortex_m::asm::nop()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Timer has definitely wrapped.
|
||||||
|
if new_count > last_count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ticks = (ticks & u32::MAX as u64) as u32;
|
||||||
|
self.disable();
|
||||||
|
if ticks > 1 {
|
||||||
|
self.set_reload(ticks);
|
||||||
|
self.set_count(ticks);
|
||||||
|
self.enable();
|
||||||
|
last_count = ticks;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
new_count = self.count();
|
||||||
|
if new_count == 0 || (new_count > last_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_count = new_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
|
||||||
|
// which should call [default_ms_irq_handler].
|
||||||
|
pub fn set_up_ms_tick<TIM: ValidTim>(
|
||||||
|
irq_cfg: IrqCfg,
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
irq_sel: Option<&mut pac::Irqsel>,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
tim0: TIM,
|
||||||
|
) -> CountDownTimer<TIM> {
|
||||||
|
let mut ms_timer = CountDownTimer::new(sys_cfg, sys_clk, tim0);
|
||||||
|
ms_timer.listen(timer::Event::TimeOut, irq_cfg, irq_sel, Some(sys_cfg));
|
||||||
|
ms_timer.start(1000.Hz());
|
||||||
|
ms_timer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up_ms_delay_provider<TIM: ValidTim>(
|
||||||
|
sys_cfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
tim: TIM,
|
||||||
|
) -> CountDownTimer<TIM> {
|
||||||
|
let mut provider = CountDownTimer::new(sys_cfg, sys_clk, tim);
|
||||||
|
provider.start(1000.Hz());
|
||||||
|
provider
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
|
/// the MS counter
|
||||||
|
pub fn default_ms_irq_handler() {
|
||||||
|
cortex_m::interrupt::free(|cs| {
|
||||||
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
|
ms += 1;
|
||||||
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current MS tick count
|
||||||
|
pub fn get_ms_ticks() -> u32 {
|
||||||
|
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Delay implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub struct DelayMs(CountDownTimer<pac::Tim0>);
|
||||||
|
|
||||||
|
impl DelayMs {
|
||||||
|
pub fn new(timer: CountDownTimer<pac::Tim0>) -> Option<Self> {
|
||||||
|
if timer.curr_freq() != Hertz::from_raw(1000) || !timer.listening() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self(timer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This assumes that the user has already set up a MS tick timer in TIM0 as a system tick
|
||||||
|
/// with [`set_up_ms_delay_provider`]
|
||||||
|
impl embedded_hal::delay::DelayNs for DelayMs {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
let ns_as_ms = ns / 1_000_000;
|
||||||
|
if self.0.curr_freq() != Hertz::from_raw(1000) || !self.0.listening() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let start_time = get_ms_ticks();
|
||||||
|
while get_ms_ticks() - start_time < ns_as_ms {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
va108xx-hal/src/typelevel.rs
Normal file
155
va108xx-hal/src/typelevel.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
//! Module supporting type-level programming
|
||||||
|
//!
|
||||||
|
//! This module is identical to the
|
||||||
|
//! [atsamd typelevel](https://docs.rs/atsamd-hal/latest/atsamd_hal/typelevel/index.html).
|
||||||
|
|
||||||
|
use core::ops::{Add, Sub};
|
||||||
|
|
||||||
|
use typenum::{Add1, Bit, Sub1, UInt, Unsigned, B1, U0};
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
/// Super trait used to mark traits with an exhaustive set of
|
||||||
|
/// implementations
|
||||||
|
pub trait Sealed {}
|
||||||
|
|
||||||
|
impl Sealed for u8 {}
|
||||||
|
impl Sealed for i8 {}
|
||||||
|
impl Sealed for u16 {}
|
||||||
|
impl Sealed for i16 {}
|
||||||
|
impl Sealed for u32 {}
|
||||||
|
impl Sealed for i32 {}
|
||||||
|
impl Sealed for f32 {}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its successor
|
||||||
|
pub trait Increment {
|
||||||
|
/// Successor type of `Self`
|
||||||
|
type Inc;
|
||||||
|
/// Consume an instance of `Self` and return its successor
|
||||||
|
fn inc(self) -> Self::Inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its predecessor
|
||||||
|
pub trait Decrement {
|
||||||
|
/// Predecessor type of `Self`
|
||||||
|
type Dec;
|
||||||
|
/// Consume an instance of `Self` and return its predecessor
|
||||||
|
fn dec(self) -> Self::Dec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use private::Decrement as PrivateDecrement;
|
||||||
|
pub(crate) use private::Increment as PrivateIncrement;
|
||||||
|
pub(crate) use private::Sealed;
|
||||||
|
|
||||||
|
/// Type-level version of the [`None`] variant
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NoneT;
|
||||||
|
|
||||||
|
impl Sealed for NoneT {}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Is
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Marker trait for type identity
|
||||||
|
///
|
||||||
|
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
|
||||||
|
/// the concept of type identity, because all implementors have
|
||||||
|
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
|
||||||
|
/// type, it guarantees that the corresponding type parameter is exactly the
|
||||||
|
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||||
|
/// the following example.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// where T: Is<Type = Specific>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||||
|
/// type `T` can be converted into the `Specific` type.
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// fn example<T>(mut any: T)
|
||||||
|
/// where
|
||||||
|
/// T: Is<Type = Specific>,
|
||||||
|
/// {
|
||||||
|
/// let specific_mut: &mut Specific = any.as_mut();
|
||||||
|
/// let specific_ref: &Specific = any.as_ref();
|
||||||
|
/// let specific: Specific = any.into();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: #anykind-trait-pattern
|
||||||
|
pub trait Is
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<IsType<Self>>,
|
||||||
|
Self: Into<IsType<Self>>,
|
||||||
|
Self: AsRef<IsType<Self>>,
|
||||||
|
Self: AsMut<IsType<Self>>,
|
||||||
|
{
|
||||||
|
type Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for [`Is::Type`]
|
||||||
|
pub type IsType<T> = <T as Is>::Type;
|
||||||
|
|
||||||
|
impl<T> Is for T
|
||||||
|
where
|
||||||
|
T: Sealed + AsRef<T> + AsMut<T>,
|
||||||
|
{
|
||||||
|
type Type = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Counting
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Implement `Sealed` for [`U0`]
|
||||||
|
impl Sealed for U0 {}
|
||||||
|
|
||||||
|
/// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`]
|
||||||
|
impl<U: Unsigned, B: Bit> Sealed for UInt<U, B> {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its successor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding successor type. The
|
||||||
|
/// actual implementation of this trait is contained within `PrivateIncrement`.
|
||||||
|
/// Access to `PrivateIncrement` is restricted, so that safe HAL APIs can be
|
||||||
|
/// built with it.
|
||||||
|
pub trait Increment: PrivateIncrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateIncrement> Increment for T {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its predecessor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding predecessor type.
|
||||||
|
/// The actual implementation of this trait is contained within
|
||||||
|
/// `PrivateDecrement`. Access to `PrivateDecrement` is restricted, so that safe
|
||||||
|
/// HAL APIs can be built with it.
|
||||||
|
pub trait Decrement: PrivateDecrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateDecrement> Decrement for T {}
|
||||||
|
|
||||||
|
impl<N> PrivateIncrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Add<B1>,
|
||||||
|
Add1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Inc = Add1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn inc(self) -> Self::Inc {
|
||||||
|
Self::Inc::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> PrivateDecrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Sub<B1>,
|
||||||
|
Sub1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Dec = Sub1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn dec(self) -> Self::Dec {
|
||||||
|
Self::Dec::default()
|
||||||
|
}
|
||||||
|
}
|
1018
va108xx-hal/src/uart.rs
Normal file
1018
va108xx-hal/src/uart.rs
Normal file
File diff suppressed because it is too large
Load Diff
16
va108xx-hal/src/utility.rs
Normal file
16
va108xx-hal/src/utility.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//! # API for utility functions like the Error Detection and Correction (EDAC) block
|
||||||
|
//!
|
||||||
|
//! Some more information about the recommended scrub rates can be found on the
|
||||||
|
//! [Vorago White Paper website](https://www.voragotech.com/resources) in the
|
||||||
|
//! application note AN1212
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
/// Unmask and enable an IRQ with the given interrupt number
|
||||||
|
///
|
||||||
|
/// ## Safety
|
||||||
|
///
|
||||||
|
/// The unmask function can break mask-based critical sections
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn unmask_irq(irq: pac::Interrupt) {
|
||||||
|
unsafe { cortex_m::peripheral::NVIC::unmask(irq) };
|
||||||
|
}
|
5
va108xx-hal/tests/DueSerialTest/.gitignore
vendored
Normal file
5
va108xx-hal/tests/DueSerialTest/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
7
va108xx-hal/tests/DueSerialTest/.vscode/extensions.json
vendored
Normal file
7
va108xx-hal/tests/DueSerialTest/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
]
|
||||||
|
}
|
2
va108xx-hal/tests/DueSerialTest/README.md
Normal file
2
va108xx-hal/tests/DueSerialTest/README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
This is a Platform IO test script for the Arduino Due which can be used to sent different kind
|
||||||
|
of strings via the serial interface (RX1 and TX1) to the Vorago board.
|
39
va108xx-hal/tests/DueSerialTest/include/README
Normal file
39
va108xx-hal/tests/DueSerialTest/include/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
va108xx-hal/tests/DueSerialTest/lib/README
Normal file
46
va108xx-hal/tests/DueSerialTest/lib/README
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
15
va108xx-hal/tests/DueSerialTest/platformio.ini
Normal file
15
va108xx-hal/tests/DueSerialTest/platformio.ini
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:due]
|
||||||
|
platform = atmelsam
|
||||||
|
board = due
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
78
va108xx-hal/tests/DueSerialTest/src/main.cpp
Normal file
78
va108xx-hal/tests/DueSerialTest/src/main.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
enum SendModes {
|
||||||
|
ECHO,
|
||||||
|
ONLY_WRITE,
|
||||||
|
ONLY_READ,
|
||||||
|
WRITE_READ
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StringModes {
|
||||||
|
FIXED,
|
||||||
|
VARIABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure the test application here
|
||||||
|
SendModes SEND_MODE = SendModes::WRITE_READ;
|
||||||
|
StringModes STRING_MODE = StringModes::VARIABLE;
|
||||||
|
uint8_t STRING_IDX = 0;
|
||||||
|
|
||||||
|
String STRINGS[4] = {
|
||||||
|
"$Hi\n",
|
||||||
|
"$Hello\n",
|
||||||
|
"$Hello World\n",
|
||||||
|
"$Hello and Merry Christmas to all of you!\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// put your setup code here, to run once:
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.println("Starting Arduino Serial Test script..");
|
||||||
|
Serial1.begin(115200);
|
||||||
|
if(STRING_MODE == StringModes::VARIABLE) {
|
||||||
|
STRING_IDX = 0;
|
||||||
|
}
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
static byte ICOMING_BYTE = 0;
|
||||||
|
static uint32_t GLOBAL_IDX = 0;
|
||||||
|
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
|
||||||
|
// put your main code here, to run repeatedly:
|
||||||
|
// send data only when you receive data:
|
||||||
|
if (SEND_MODE == SendModes::ONLY_WRITE or SEND_MODE == SendModes::WRITE_READ) {
|
||||||
|
Serial.println("Sending string..");
|
||||||
|
Serial1.write(STRINGS[STRING_IDX].c_str());
|
||||||
|
if(STRING_MODE == StringModes::VARIABLE) {
|
||||||
|
STRING_IDX += 1;
|
||||||
|
if(STRING_IDX > 3) {
|
||||||
|
STRING_IDX = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(
|
||||||
|
SEND_MODE == SendModes::WRITE_READ or
|
||||||
|
SEND_MODE == SendModes::ONLY_READ or
|
||||||
|
SEND_MODE == SendModes::ECHO
|
||||||
|
) {
|
||||||
|
if (Serial1.available() > 0) {
|
||||||
|
// read the incoming byte:
|
||||||
|
String readString = Serial1.readStringUntil('\n');
|
||||||
|
|
||||||
|
Serial.print(GLOBAL_IDX);
|
||||||
|
Serial.print(" - ");
|
||||||
|
GLOBAL_IDX++;
|
||||||
|
// say what you got:
|
||||||
|
Serial.print("I received: ");
|
||||||
|
Serial.println(readString);
|
||||||
|
if(SEND_MODE == SendModes::ECHO) {
|
||||||
|
delay(200);
|
||||||
|
Serial.println("Sending back echo message");
|
||||||
|
String sendBack = readString + '\n';
|
||||||
|
Serial1.write(sendBack.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(3000);
|
||||||
|
}
|
11
va108xx-hal/tests/DueSerialTest/test/README
Normal file
11
va108xx-hal/tests/DueSerialTest/test/README
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
2
va108xx/.github/bors.toml
vendored
Normal file
2
va108xx/.github/bors.toml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
status = ["ci"]
|
||||||
|
delete_merged_branches = true
|
20
va108xx/.github/workflows/changelog.yml
vendored
Normal file
20
va108xx/.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
|
||||||
|
name: Changelog check
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changelog:
|
||||||
|
name: Changelog check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Changelog updated
|
||||||
|
uses: Zomzog/changelog-checker@v1.2.0
|
||||||
|
with:
|
||||||
|
fileName: CHANGELOG.md
|
||||||
|
noChangelogLabel: no changelog
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
61
va108xx/.github/workflows/ci.yml
vendored
Normal file
61
va108xx/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
on: [push]
|
||||||
|
|
||||||
|
name: build
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- run: rustup component add rustfmt
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- run: rustup component add clippy
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: -- -D warnings
|
||||||
|
|
||||||
|
ci:
|
||||||
|
if: ${{ success() }}
|
||||||
|
# all new jobs must be added to this list
|
||||||
|
needs: [check, fmt, clippy]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: CI succeeded
|
||||||
|
run: exit 0
|
6
va108xx/.gitignore
vendored
Normal file
6
va108xx/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
69
va108xx/CHANGELOG.md
Normal file
69
va108xx/CHANGELOG.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
Change Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
|
||||||
|
## [v0.2.4]
|
||||||
|
|
||||||
|
- Added missing bitfield `CSDTRG2` in `CSD_CTRL` register of `TIM0` peripheral
|
||||||
|
|
||||||
|
## [v0.2.3]
|
||||||
|
|
||||||
|
- Added peripheral reset fields for `PERIPHERAL_RESET` register
|
||||||
|
|
||||||
|
## [v0.2.2]
|
||||||
|
|
||||||
|
- README tweks
|
||||||
|
|
||||||
|
## [v0.2.1]
|
||||||
|
|
||||||
|
- Some README and Manifest weaks
|
||||||
|
|
||||||
|
## [v0.2.0]
|
||||||
|
|
||||||
|
- Authorative repository was transferred to https://egit.irs.uni-stuttgart.de/rust/va108xx-rs but
|
||||||
|
there still will be a GitHub mirror. Project relicensed as Apache-2.0 only
|
||||||
|
|
||||||
|
## [v0.1.3]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added two missing bit fields for I2CA STATUS register: I2CIDLE and IDLE
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Made I2CA STATUS register read-only
|
||||||
|
|
||||||
|
## [v0.1.2]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Generated with patched version of `svd2rust`: See
|
||||||
|
https://github.com/rust-embedded/svd2rust/pull/549 for more details.
|
||||||
|
Some bitmasks were missing from register reader definitions.
|
||||||
|
|
||||||
|
## [v0.1.1]
|
||||||
|
|
||||||
|
- Relicensed under dual Apache-2.0 / MIT license
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- SVD file handling improved and new fields added for the peripheral
|
||||||
|
clock enable register
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Helper script to automate all steps for PAC generation
|
||||||
|
- Added badges for README
|
||||||
|
|
||||||
|
## [v0.1.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- First version of the PAC which builds. Uses a patched version
|
||||||
|
of `svd2rust`: https://github.com/rust-embedded/svd2rust
|
23
va108xx/Cargo.toml
Normal file
23
va108xx/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "va108xx"
|
||||||
|
version = "0.3.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2021"
|
||||||
|
description = "PAC for the Vorago VA108xx family of microcontrollers"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "cortex-m", "vorago", "va108xx"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
vcell = "0.1.3"
|
||||||
|
critical-section = { version = "1", optional = true }
|
||||||
|
|
||||||
|
[dependencies.cortex-m-rt]
|
||||||
|
optional = true
|
||||||
|
version = ">=0.6.15,<0.8"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rt = ["cortex-m-rt/device"]
|
201
va108xx/LICENSE-APACHE
Normal file
201
va108xx/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
3
va108xx/NOTICE
Normal file
3
va108xx/NOTICE
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Peripheral access crate for the Vorago VA108xx family microcontrollers
|
||||||
|
|
||||||
|
This software contains code developed at the University of Stuttgart.
|
64
va108xx/README.md
Normal file
64
va108xx/README.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
[![Crates.io](https://img.shields.io/crates/v/va108xx)](https://crates.io/crates/va108xx)
|
||||||
|
[![build](https://github.com/us-irs/va108xx-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/us-irs/va108xx-rs/actions/workflows/ci.yml)
|
||||||
|
[![docs.rs](https://img.shields.io/docsrs/va108xx)](https://docs.rs/va108xx)
|
||||||
|
|
||||||
|
# PAC for the Vorago VA108xx microcontroller family
|
||||||
|
|
||||||
|
This repository contains the Peripheral Access Crate (PAC) for
|
||||||
|
Voragos VA108xx series of Cortex-M0 based microcontrollers.
|
||||||
|
|
||||||
|
The crate was generated using [`svd2rust`](https://github.com/rust-embedded/svd2rust).
|
||||||
|
|
||||||
|
If you are interested in higher-level abstractions, it is recommended you visit
|
||||||
|
the [`va108xx-hal` HAL crate](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal) and
|
||||||
|
the [`vorago-reb1` BSP crate](https://github.com/robamu-org/vorago-reb1-rs) which build on top of
|
||||||
|
this PAC and provide application examples as well.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this crate, add this to your `Cargo.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies.va108xx]
|
||||||
|
version = "<Most Recent Version>"
|
||||||
|
features = ["rt"]
|
||||||
|
```
|
||||||
|
|
||||||
|
The `rt` feature is optional and recommended. It brings in support for `cortex-m-rt`.
|
||||||
|
|
||||||
|
For full details on the autgenerated API, please see the
|
||||||
|
[svd2rust documentation](https://docs.rs/svd2rust/0.19.0/svd2rust/#peripheral-api).
|
||||||
|
|
||||||
|
## Regenerating the PAC
|
||||||
|
|
||||||
|
If you want to re-generate the PAC, for example if the register file `va416xx.svd` changes
|
||||||
|
or the `svd2rust` version is updated, you can do some using the following these steps:
|
||||||
|
|
||||||
|
1. Make sure all necessary tools are installed: [`svd2rust`](https://docs.rs/svd2rust/latest/svd2rust/),
|
||||||
|
[`svdtools`](https://github.com/rust-embedded/svdtools) and [`form`](https://crates.io/crates/form).
|
||||||
|
You can install all tools with `cargo`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install --locked svd2rust svdtools form
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Patch the vendor-provided SVD file `svd/va41xx.svd`. This can be done using `svdtools` in
|
||||||
|
conjunction with the `svd/va108xx-patch.yml` file.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
svdtools patch svd/va108xx-patch.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Use `svd2rust` to generate the Rust library
|
||||||
|
|
||||||
|
```sh
|
||||||
|
svd2rust -i svd/va108xx.svd.patched
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Use the `form` tool to split the generated `lib.rs` into individual modules.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
form -i lib.rs -o src/
|
||||||
|
```
|
||||||
|
|
||||||
|
The `gen-helper.sh` automates steps 2-4.
|
11
va108xx/automation/Dockerfile
Normal file
11
va108xx/automation/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Run the following commands from root directory to build and run locally
|
||||||
|
# docker build -f automation/Dockerfile -t <NAME> .
|
||||||
|
# docker run -it <NAME>
|
||||||
|
FROM rust:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get --yes upgrade
|
||||||
|
# tzdata is a dependency, won't install otherwise
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN rustup target add thumbv6m-none-eabi && \
|
||||||
|
rustup component add rustfmt clippy
|
39
va108xx/automation/Jenkinsfile
vendored
Normal file
39
va108xx/automation/Jenkinsfile
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Clippy') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'cargo clippy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Rustfmt') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'cargo fmt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check') {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
va108xx/build.rs
Normal file
17
va108xx/build.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#![doc = r" Builder file for Peripheral access crate generated by svd2rust tool"]
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
fn main() {
|
||||||
|
if env::var_os("CARGO_FEATURE_RT").is_some() {
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("device.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("device.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
println!("cargo:rerun-if-changed=device.x");
|
||||||
|
}
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
33
va108xx/device.x
Normal file
33
va108xx/device.x
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
PROVIDE(OC0 = DefaultHandler);
|
||||||
|
PROVIDE(OC1 = DefaultHandler);
|
||||||
|
PROVIDE(OC2 = DefaultHandler);
|
||||||
|
PROVIDE(OC3 = DefaultHandler);
|
||||||
|
PROVIDE(OC4 = DefaultHandler);
|
||||||
|
PROVIDE(OC5 = DefaultHandler);
|
||||||
|
PROVIDE(OC6 = DefaultHandler);
|
||||||
|
PROVIDE(OC7 = DefaultHandler);
|
||||||
|
PROVIDE(OC8 = DefaultHandler);
|
||||||
|
PROVIDE(OC9 = DefaultHandler);
|
||||||
|
PROVIDE(OC10 = DefaultHandler);
|
||||||
|
PROVIDE(OC11 = DefaultHandler);
|
||||||
|
PROVIDE(OC12 = DefaultHandler);
|
||||||
|
PROVIDE(OC13 = DefaultHandler);
|
||||||
|
PROVIDE(OC14 = DefaultHandler);
|
||||||
|
PROVIDE(OC15 = DefaultHandler);
|
||||||
|
PROVIDE(OC16 = DefaultHandler);
|
||||||
|
PROVIDE(OC17 = DefaultHandler);
|
||||||
|
PROVIDE(OC18 = DefaultHandler);
|
||||||
|
PROVIDE(OC19 = DefaultHandler);
|
||||||
|
PROVIDE(OC20 = DefaultHandler);
|
||||||
|
PROVIDE(OC21 = DefaultHandler);
|
||||||
|
PROVIDE(OC22 = DefaultHandler);
|
||||||
|
PROVIDE(OC23 = DefaultHandler);
|
||||||
|
PROVIDE(OC24 = DefaultHandler);
|
||||||
|
PROVIDE(OC25 = DefaultHandler);
|
||||||
|
PROVIDE(OC26 = DefaultHandler);
|
||||||
|
PROVIDE(OC27 = DefaultHandler);
|
||||||
|
PROVIDE(OC28 = DefaultHandler);
|
||||||
|
PROVIDE(OC29 = DefaultHandler);
|
||||||
|
PROVIDE(OC30 = DefaultHandler);
|
||||||
|
PROVIDE(OC31 = DefaultHandler);
|
||||||
|
|
43
va108xx/gen-helper.sh
Executable file
43
va108xx/gen-helper.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Use installed tool by default
|
||||||
|
svd2rust_bin="svd2rust"
|
||||||
|
# Automates the steps specified in https://docs.rs/svd2rust/0.19.0/svd2rust/
|
||||||
|
if [ -f svd2rust ]; then
|
||||||
|
# If the local directory contains svd2rust, use that version instead
|
||||||
|
svd2rust_bin="./svd2rust"
|
||||||
|
elif [ -f ../svd2rust ]; then
|
||||||
|
# Keeps the repository clean
|
||||||
|
svd2rust_bin="../svd2rust"
|
||||||
|
fi
|
||||||
|
if [ -x "$(${svd2rust_bin} --version)" ]; then
|
||||||
|
echo "No svd2rust found locally or installed." \
|
||||||
|
"Install it with cargo install svd2rust"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v form &> /dev/null
|
||||||
|
then
|
||||||
|
echo "form tool was not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v svdtools &> /dev/null
|
||||||
|
then
|
||||||
|
echo "svdtools was not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
svdtools patch svd/va108xx-patch.yml
|
||||||
|
# See https://github.com/rust-embedded/svd2rust/issues/830 for required re-export.
|
||||||
|
${svd2rust_bin} --reexport-interrupt -i svd/va108xx.svd.patched
|
||||||
|
|
||||||
|
result=$?
|
||||||
|
if [ $result -ne 0 ]; then
|
||||||
|
echo "svd2rust failed with code $result"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf src
|
||||||
|
form -i lib.rs -o src/ && rm lib.rs
|
||||||
|
cargo fmt
|
618
va108xx/src/generic.rs
Normal file
618
va108xx/src/generic.rs
Normal file
@ -0,0 +1,618 @@
|
|||||||
|
use core::marker;
|
||||||
|
#[doc = " Raw register type (`u8`, `u16`, `u32`, ...)"]
|
||||||
|
pub trait RawReg:
|
||||||
|
Copy
|
||||||
|
+ Default
|
||||||
|
+ From<bool>
|
||||||
|
+ core::ops::BitOr<Output = Self>
|
||||||
|
+ core::ops::BitAnd<Output = Self>
|
||||||
|
+ core::ops::BitOrAssign
|
||||||
|
+ core::ops::BitAndAssign
|
||||||
|
+ core::ops::Not<Output = Self>
|
||||||
|
+ core::ops::Shl<u8, Output = Self>
|
||||||
|
{
|
||||||
|
#[doc = " Mask for bits of width `WI`"]
|
||||||
|
fn mask<const WI: u8>() -> Self;
|
||||||
|
#[doc = " Mask for bits of width 1"]
|
||||||
|
fn one() -> Self;
|
||||||
|
}
|
||||||
|
macro_rules! raw_reg {
|
||||||
|
($ U : ty , $ size : literal , $ mask : ident) => {
|
||||||
|
impl RawReg for $U {
|
||||||
|
#[inline(always)]
|
||||||
|
fn mask<const WI: u8>() -> Self {
|
||||||
|
$mask::<WI>()
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn one() -> Self {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const fn $mask<const WI: u8>() -> $U {
|
||||||
|
<$U>::MAX >> ($size - WI)
|
||||||
|
}
|
||||||
|
impl FieldSpec for $U {
|
||||||
|
type Ux = $U;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
raw_reg!(u8, 8, mask_u8);
|
||||||
|
raw_reg!(u16, 16, mask_u16);
|
||||||
|
raw_reg!(u32, 32, mask_u32);
|
||||||
|
raw_reg!(u64, 64, mask_u64);
|
||||||
|
#[doc = " Raw register type"]
|
||||||
|
pub trait RegisterSpec {
|
||||||
|
#[doc = " Raw register type (`u8`, `u16`, `u32`, ...)."]
|
||||||
|
type Ux: RawReg;
|
||||||
|
}
|
||||||
|
#[doc = " Raw field type"]
|
||||||
|
pub trait FieldSpec: Sized {
|
||||||
|
#[doc = " Raw field type (`u8`, `u16`, `u32`, ...)."]
|
||||||
|
type Ux: Copy + core::fmt::Debug + PartialEq + From<Self>;
|
||||||
|
}
|
||||||
|
#[doc = " Marker for fields with fixed values"]
|
||||||
|
pub trait IsEnum: FieldSpec {}
|
||||||
|
#[doc = " Trait implemented by readable registers to enable the `read` method."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Registers marked with `Writable` can be also be `modify`'ed."]
|
||||||
|
pub trait Readable: RegisterSpec {}
|
||||||
|
#[doc = " Trait implemented by writeable registers."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " This enables the `write`, `write_with_zero` and `reset` methods."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Registers marked with `Readable` can be also be `modify`'ed."]
|
||||||
|
pub trait Writable: RegisterSpec {
|
||||||
|
#[doc = " Is it safe to write any bits to register"]
|
||||||
|
type Safety;
|
||||||
|
#[doc = " Specifies the register bits that are not changed if you pass `1` and are changed if you pass `0`"]
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: Self::Ux;
|
||||||
|
#[doc = " Specifies the register bits that are not changed if you pass `0` and are changed if you pass `1`"]
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: Self::Ux;
|
||||||
|
}
|
||||||
|
#[doc = " Reset value of the register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " This value is the initial value for the `write` method. It can also be directly written to the"]
|
||||||
|
#[doc = " register by using the `reset` method."]
|
||||||
|
pub trait Resettable: RegisterSpec {
|
||||||
|
#[doc = " Reset value of the register."]
|
||||||
|
const RESET_VALUE: Self::Ux;
|
||||||
|
#[doc = " Reset value of the register."]
|
||||||
|
#[inline(always)]
|
||||||
|
fn reset_value() -> Self::Ux {
|
||||||
|
Self::RESET_VALUE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = " This structure provides volatile access to registers."]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Reg<REG: RegisterSpec> {
|
||||||
|
register: vcell::VolatileCell<REG::Ux>,
|
||||||
|
_marker: marker::PhantomData<REG>,
|
||||||
|
}
|
||||||
|
unsafe impl<REG: RegisterSpec> Send for Reg<REG> where REG::Ux: Send {}
|
||||||
|
impl<REG: RegisterSpec> Reg<REG> {
|
||||||
|
#[doc = " Returns the underlying memory address of register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let reg_ptr = periph.reg.as_ptr();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_ptr(&self) -> *mut REG::Ux {
|
||||||
|
self.register.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable> Reg<REG> {
|
||||||
|
#[doc = " Reads the contents of a `Readable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " You can read the raw contents of a register by using `bits`:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let bits = periph.reg.read().bits();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or get the content of a particular field of a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " let reader = periph.reg.read();"]
|
||||||
|
#[doc = " let bits = reader.field1().bits();"]
|
||||||
|
#[doc = " let flag = reader.field2().bit_is_set();"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read(&self) -> R<REG> {
|
||||||
|
R {
|
||||||
|
bits: self.register.get(),
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Resettable + Writable> Reg<REG> {
|
||||||
|
#[doc = " Writes the reset value to `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Resets the register to its initial state."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn reset(&self) {
|
||||||
|
self.register.set(REG::RESET_VALUE)
|
||||||
|
}
|
||||||
|
#[doc = " Writes bits to a `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " You can write raw bits into a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| unsafe { w.bits(rawbits) });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or write only the fields you need:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| w"]
|
||||||
|
#[doc = " .field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT)"]
|
||||||
|
#[doc = " );"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.write(|w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT)"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " In the latter case, other fields will be set to their reset value."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn write<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
||||||
|
{
|
||||||
|
self.register.set(
|
||||||
|
f(&mut W {
|
||||||
|
bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
||||||
|
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
})
|
||||||
|
.bits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Writable> Reg<REG> {
|
||||||
|
#[doc = " Writes 0 to a `Writable` register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Similar to `write`, but unused bits will contain 0."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " # Safety"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Unsafe to use with registers which don't allow to write 0."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn write_with_zero<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut W<REG>) -> &mut W<REG>,
|
||||||
|
{
|
||||||
|
self.register.set(
|
||||||
|
f(&mut W {
|
||||||
|
bits: REG::Ux::default(),
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
})
|
||||||
|
.bits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable + Writable> Reg<REG> {
|
||||||
|
#[doc = " Modifies the contents of the register by reading and then writing it."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " E.g. to do a read-modify-write sequence to change parts of a register:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|r, w| unsafe { w.bits("]
|
||||||
|
#[doc = " r.bits() | 3"]
|
||||||
|
#[doc = " ) });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| w"]
|
||||||
|
#[doc = " .field1().bits(newfield1bits)"]
|
||||||
|
#[doc = " .field2().set_bit()"]
|
||||||
|
#[doc = " .field3().variant(VARIANT)"]
|
||||||
|
#[doc = " );"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " or an alternative way of saying the same:"]
|
||||||
|
#[doc = " ```ignore"]
|
||||||
|
#[doc = " periph.reg.modify(|_, w| {"]
|
||||||
|
#[doc = " w.field1().bits(newfield1bits);"]
|
||||||
|
#[doc = " w.field2().set_bit();"]
|
||||||
|
#[doc = " w.field3().variant(VARIANT)"]
|
||||||
|
#[doc = " });"]
|
||||||
|
#[doc = " ```"]
|
||||||
|
#[doc = " Other fields will have the value they had before the call to `modify`."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn modify<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
for<'w> F: FnOnce(&R<REG>, &'w mut W<REG>) -> &'w mut W<REG>,
|
||||||
|
{
|
||||||
|
let bits = self.register.get();
|
||||||
|
self.register.set(
|
||||||
|
f(
|
||||||
|
&R {
|
||||||
|
bits,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
},
|
||||||
|
&mut W {
|
||||||
|
bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP
|
||||||
|
| REG::ZERO_TO_MODIFY_FIELDS_BITMAP,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.bits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: Readable> core::fmt::Debug for crate::generic::Reg<REG>
|
||||||
|
where
|
||||||
|
R<REG>: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
core::fmt::Debug::fmt(&self.read(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod raw;
|
||||||
|
#[doc = " Register reader."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Result of the `read` methods of registers. Also used as a closure argument in the `modify`"]
|
||||||
|
#[doc = " method."]
|
||||||
|
pub type R<REG> = raw::R<REG>;
|
||||||
|
impl<REG: RegisterSpec> R<REG> {
|
||||||
|
#[doc = " Reads raw bits from register."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bits(&self) -> REG::Ux {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG: RegisterSpec, FI> PartialEq<FI> for R<REG>
|
||||||
|
where
|
||||||
|
REG::Ux: PartialEq,
|
||||||
|
FI: Copy,
|
||||||
|
REG::Ux: From<FI>,
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &FI) -> bool {
|
||||||
|
self.bits.eq(®::Ux::from(*other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = " Register writer."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Used as an argument to the closures in the `write` and `modify` methods of the register."]
|
||||||
|
pub type W<REG> = raw::W<REG>;
|
||||||
|
impl<REG: Writable> W<REG> {
|
||||||
|
#[doc = " Writes raw bits to the register."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " # Safety"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Passing incorrect value can cause undefined behaviour. See reference manual"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn bits(&mut self, bits: REG::Ux) -> &mut Self {
|
||||||
|
self.bits = bits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<REG> W<REG>
|
||||||
|
where
|
||||||
|
REG: Writable<Safety = Safe>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the register."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(&mut self, bits: REG::Ux) -> &mut Self {
|
||||||
|
self.bits = bits;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = " Field reader."]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Result of the `read` methods of fields."]
|
||||||
|
pub type FieldReader<FI = u8> = raw::FieldReader<FI>;
|
||||||
|
#[doc = " Bit-wise field reader"]
|
||||||
|
pub type BitReader<FI = bool> = raw::BitReader<FI>;
|
||||||
|
impl<FI: FieldSpec> FieldReader<FI> {
|
||||||
|
#[doc = " Reads raw bits from field."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bits(&self) -> FI::Ux {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FI: FieldSpec> core::fmt::Debug for FieldReader<FI> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
core::fmt::Debug::fmt(&self.bits, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FI> PartialEq<FI> for FieldReader<FI>
|
||||||
|
where
|
||||||
|
FI: FieldSpec + Copy,
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &FI) -> bool {
|
||||||
|
self.bits.eq(&FI::Ux::from(*other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FI> PartialEq<FI> for BitReader<FI>
|
||||||
|
where
|
||||||
|
FI: Copy,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &FI) -> bool {
|
||||||
|
self.bits.eq(&bool::from(*other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FI> BitReader<FI> {
|
||||||
|
#[doc = " Value of the field as raw bits."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bit(&self) -> bool {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
#[doc = " Returns `true` if the bit is clear (0)."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bit_is_clear(&self) -> bool {
|
||||||
|
!self.bit()
|
||||||
|
}
|
||||||
|
#[doc = " Returns `true` if the bit is set (1)."]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn bit_is_set(&self) -> bool {
|
||||||
|
self.bit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<FI> core::fmt::Debug for BitReader<FI> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
core::fmt::Debug::fmt(&self.bits, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = " Marker for register/field writers which can take any value of specified width"]
|
||||||
|
pub struct Safe;
|
||||||
|
#[doc = " You should check that value is allowed to pass to register/field writer marked with this"]
|
||||||
|
pub struct Unsafe;
|
||||||
|
#[doc = " Marker for field writers are safe to write in specified inclusive range"]
|
||||||
|
pub struct Range<const MIN: u64, const MAX: u64>;
|
||||||
|
#[doc = " Marker for field writers are safe to write in specified inclusive range"]
|
||||||
|
pub struct RangeFrom<const MIN: u64>;
|
||||||
|
#[doc = " Marker for field writers are safe to write in specified inclusive range"]
|
||||||
|
pub struct RangeTo<const MAX: u64>;
|
||||||
|
#[doc = " Write field Proxy"]
|
||||||
|
pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> =
|
||||||
|
raw::FieldWriter<'a, REG, WI, FI, Safety>;
|
||||||
|
impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
{
|
||||||
|
#[doc = " Field width"]
|
||||||
|
pub const WIDTH: u8 = WI;
|
||||||
|
#[doc = " Field width"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn width(&self) -> u8 {
|
||||||
|
WI
|
||||||
|
}
|
||||||
|
#[doc = " Field offset"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn offset(&self) -> u8 {
|
||||||
|
self.o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the field"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " # Safety"]
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = " Passing incorrect value can cause undefined behaviour. See reference manual"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn bits(self, value: FI::Ux) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::mask::<WI>() << self.o);
|
||||||
|
self.w.bits |= (REG::Ux::from(value) & REG::Ux::mask::<WI>()) << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI> FieldWriter<'a, REG, WI, FI, Safe>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
|
||||||
|
unsafe { self.bits(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, const MIN: u64, const MAX: u64>
|
||||||
|
FieldWriter<'a, REG, WI, FI, Range<MIN, MAX>>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
u64: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
|
||||||
|
{
|
||||||
|
let value = u64::from(value);
|
||||||
|
assert!(value >= MIN && value <= MAX);
|
||||||
|
}
|
||||||
|
unsafe { self.bits(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, const MIN: u64> FieldWriter<'a, REG, WI, FI, RangeFrom<MIN>>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
u64: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
|
||||||
|
{
|
||||||
|
let value = u64::from(value);
|
||||||
|
assert!(value >= MIN);
|
||||||
|
}
|
||||||
|
unsafe { self.bits(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, const MAX: u64> FieldWriter<'a, REG, WI, FI, RangeTo<MAX>>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
u64: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes raw bits to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
|
||||||
|
{
|
||||||
|
let value = u64::from(value);
|
||||||
|
assert!(value <= MAX);
|
||||||
|
}
|
||||||
|
unsafe { self.bits(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: IsEnum,
|
||||||
|
REG::Ux: From<FI::Ux>,
|
||||||
|
{
|
||||||
|
#[doc = " Writes `variant` to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn variant(self, variant: FI) -> &'a mut W<REG> {
|
||||||
|
unsafe { self.bits(FI::Ux::from(variant)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! bit_proxy {
|
||||||
|
($ writer : ident , $ mwv : ident) => {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct $mwv;
|
||||||
|
#[doc = " Bit-wise write field proxy"]
|
||||||
|
pub type $writer<'a, REG, FI = bool> = raw::BitWriter<'a, REG, FI, $mwv>;
|
||||||
|
impl<'a, REG, FI> $writer<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = " Field width"]
|
||||||
|
pub const WIDTH: u8 = 1;
|
||||||
|
#[doc = " Field width"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn width(&self) -> u8 {
|
||||||
|
Self::WIDTH
|
||||||
|
}
|
||||||
|
#[doc = " Field offset"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn offset(&self) -> u8 {
|
||||||
|
self.o
|
||||||
|
}
|
||||||
|
#[doc = " Writes bit to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn bit(self, value: bool) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::one() << self.o);
|
||||||
|
self.w.bits |= (REG::Ux::from(value) & REG::Ux::one()) << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
#[doc = " Writes `variant` to the field"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn variant(self, variant: FI) -> &'a mut W<REG> {
|
||||||
|
self.bit(bool::from(variant))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
bit_proxy!(BitWriter, BitM);
|
||||||
|
bit_proxy!(BitWriter1S, Bit1S);
|
||||||
|
bit_proxy!(BitWriter0C, Bit0C);
|
||||||
|
bit_proxy!(BitWriter1C, Bit1C);
|
||||||
|
bit_proxy!(BitWriter0S, Bit0S);
|
||||||
|
bit_proxy!(BitWriter1T, Bit1T);
|
||||||
|
bit_proxy!(BitWriter0T, Bit0T);
|
||||||
|
impl<'a, REG, FI> BitWriter<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = " Sets the field bit"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits |= REG::Ux::one() << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
#[doc = " Clears the field bit"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::one() << self.o);
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter1S<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = " Sets the field bit"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits |= REG::Ux::one() << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter0C<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = " Clears the field bit"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::one() << self.o);
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter1C<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = "Clears the field bit by passing one"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clear_bit_by_one(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits |= REG::Ux::one() << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter0S<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = "Sets the field bit by passing zero"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_bit_by_zero(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::one() << self.o);
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter1T<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = "Toggle the field bit by passing one"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn toggle_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits |= REG::Ux::one() << self.o;
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI> BitWriter0T<'a, REG, FI>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = "Toggle the field bit by passing zero"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn toggle_bit(self) -> &'a mut W<REG> {
|
||||||
|
self.w.bits &= !(REG::Ux::one() << self.o);
|
||||||
|
self.w
|
||||||
|
}
|
||||||
|
}
|
93
va108xx/src/generic/raw.rs
Normal file
93
va108xx/src/generic/raw.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use super::{marker, BitM, FieldSpec, RegisterSpec, Unsafe, Writable};
|
||||||
|
pub struct R<REG: RegisterSpec> {
|
||||||
|
pub(crate) bits: REG::Ux,
|
||||||
|
pub(super) _reg: marker::PhantomData<REG>,
|
||||||
|
}
|
||||||
|
pub struct W<REG: RegisterSpec> {
|
||||||
|
#[doc = "Writable bits"]
|
||||||
|
pub(crate) bits: REG::Ux,
|
||||||
|
pub(super) _reg: marker::PhantomData<REG>,
|
||||||
|
}
|
||||||
|
pub struct FieldReader<FI = u8>
|
||||||
|
where
|
||||||
|
FI: FieldSpec,
|
||||||
|
{
|
||||||
|
pub(crate) bits: FI::Ux,
|
||||||
|
_reg: marker::PhantomData<FI>,
|
||||||
|
}
|
||||||
|
impl<FI: FieldSpec> FieldReader<FI> {
|
||||||
|
#[doc = " Creates a new instance of the reader."]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) const fn new(bits: FI::Ux) -> Self {
|
||||||
|
Self {
|
||||||
|
bits,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct BitReader<FI = bool> {
|
||||||
|
pub(crate) bits: bool,
|
||||||
|
_reg: marker::PhantomData<FI>,
|
||||||
|
}
|
||||||
|
impl<FI> BitReader<FI> {
|
||||||
|
#[doc = " Creates a new instance of the reader."]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) const fn new(bits: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
bits,
|
||||||
|
_reg: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
{
|
||||||
|
pub(crate) w: &'a mut W<REG>,
|
||||||
|
pub(crate) o: u8,
|
||||||
|
_field: marker::PhantomData<(FI, Safety)>,
|
||||||
|
}
|
||||||
|
impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
FI: FieldSpec,
|
||||||
|
{
|
||||||
|
#[doc = " Creates a new instance of the writer"]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn new(w: &'a mut W<REG>, o: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
w,
|
||||||
|
o,
|
||||||
|
_field: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct BitWriter<'a, REG, FI = bool, M = BitM>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
pub(crate) w: &'a mut W<REG>,
|
||||||
|
pub(crate) o: u8,
|
||||||
|
_field: marker::PhantomData<(FI, M)>,
|
||||||
|
}
|
||||||
|
impl<'a, REG, FI, M> BitWriter<'a, REG, FI, M>
|
||||||
|
where
|
||||||
|
REG: Writable + RegisterSpec,
|
||||||
|
bool: From<FI>,
|
||||||
|
{
|
||||||
|
#[doc = " Creates a new instance of the writer"]
|
||||||
|
#[allow(unused)]
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn new(w: &'a mut W<REG>, o: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
w,
|
||||||
|
o,
|
||||||
|
_field: marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
452
va108xx/src/i2ca.rs
Normal file
452
va108xx/src/i2ca.rs
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
#[repr(C)]
|
||||||
|
#[doc = "Register block"]
|
||||||
|
pub struct RegisterBlock {
|
||||||
|
ctrl: Ctrl,
|
||||||
|
clkscale: Clkscale,
|
||||||
|
words: Words,
|
||||||
|
address: Address,
|
||||||
|
data: Data,
|
||||||
|
cmd: Cmd,
|
||||||
|
status: Status,
|
||||||
|
state: State,
|
||||||
|
txcount: Txcount,
|
||||||
|
rxcount: Rxcount,
|
||||||
|
irq_enb: IrqEnb,
|
||||||
|
irq_raw: IrqRaw,
|
||||||
|
irq_end: IrqEnd,
|
||||||
|
irq_clr: IrqClr,
|
||||||
|
rxfifoirqtrg: Rxfifoirqtrg,
|
||||||
|
txfifoirqtrg: Txfifoirqtrg,
|
||||||
|
fifo_clr: FifoClr,
|
||||||
|
tmconfig: Tmconfig,
|
||||||
|
clktolimit: Clktolimit,
|
||||||
|
_reserved19: [u8; 0xb4],
|
||||||
|
s0_ctrl: S0Ctrl,
|
||||||
|
s0_maxwords: S0Maxwords,
|
||||||
|
s0_address: S0Address,
|
||||||
|
s0_addressmask: S0Addressmask,
|
||||||
|
s0_data: S0Data,
|
||||||
|
s0_lastaddress: S0Lastaddress,
|
||||||
|
s0_status: S0Status,
|
||||||
|
s0_state: S0State,
|
||||||
|
s0_txcount: S0Txcount,
|
||||||
|
s0_rxcount: S0Rxcount,
|
||||||
|
s0_irq_enb: S0IrqEnb,
|
||||||
|
s0_irq_raw: S0IrqRaw,
|
||||||
|
s0_irq_end: S0IrqEnd,
|
||||||
|
s0_irq_clr: S0IrqClr,
|
||||||
|
s0_rxfifoirqtrg: S0Rxfifoirqtrg,
|
||||||
|
s0_txfifoirqtrg: S0Txfifoirqtrg,
|
||||||
|
s0_fifo_clr: S0FifoClr,
|
||||||
|
s0_addressb: S0Addressb,
|
||||||
|
s0_addressmaskb: S0Addressmaskb,
|
||||||
|
_reserved38: [u8; 0x0eb0],
|
||||||
|
perid: Perid,
|
||||||
|
}
|
||||||
|
impl RegisterBlock {
|
||||||
|
#[doc = "0x00 - Control Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn ctrl(&self) -> &Ctrl {
|
||||||
|
&self.ctrl
|
||||||
|
}
|
||||||
|
#[doc = "0x04 - Clock Scale divide value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn clkscale(&self) -> &Clkscale {
|
||||||
|
&self.clkscale
|
||||||
|
}
|
||||||
|
#[doc = "0x08 - Word Count value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn words(&self) -> &Words {
|
||||||
|
&self.words
|
||||||
|
}
|
||||||
|
#[doc = "0x0c - I2C Address value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn address(&self) -> &Address {
|
||||||
|
&self.address
|
||||||
|
}
|
||||||
|
#[doc = "0x10 - Data Input/Output"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn data(&self) -> &Data {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
#[doc = "0x14 - Command Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn cmd(&self) -> &Cmd {
|
||||||
|
&self.cmd
|
||||||
|
}
|
||||||
|
#[doc = "0x18 - I2C Controller Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn status(&self) -> &Status {
|
||||||
|
&self.status
|
||||||
|
}
|
||||||
|
#[doc = "0x1c - Internal STATE of I2C Master Controller"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn state(&self) -> &State {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
#[doc = "0x20 - TX Count Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn txcount(&self) -> &Txcount {
|
||||||
|
&self.txcount
|
||||||
|
}
|
||||||
|
#[doc = "0x24 - RX Count Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn rxcount(&self) -> &Rxcount {
|
||||||
|
&self.rxcount
|
||||||
|
}
|
||||||
|
#[doc = "0x28 - Interrupt Enable Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn irq_enb(&self) -> &IrqEnb {
|
||||||
|
&self.irq_enb
|
||||||
|
}
|
||||||
|
#[doc = "0x2c - Raw Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn irq_raw(&self) -> &IrqRaw {
|
||||||
|
&self.irq_raw
|
||||||
|
}
|
||||||
|
#[doc = "0x30 - Enabled Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn irq_end(&self) -> &IrqEnd {
|
||||||
|
&self.irq_end
|
||||||
|
}
|
||||||
|
#[doc = "0x34 - Clear Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn irq_clr(&self) -> &IrqClr {
|
||||||
|
&self.irq_clr
|
||||||
|
}
|
||||||
|
#[doc = "0x38 - Rx FIFO IRQ Trigger Level"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn rxfifoirqtrg(&self) -> &Rxfifoirqtrg {
|
||||||
|
&self.rxfifoirqtrg
|
||||||
|
}
|
||||||
|
#[doc = "0x3c - Tx FIFO IRQ Trigger Level"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn txfifoirqtrg(&self) -> &Txfifoirqtrg {
|
||||||
|
&self.txfifoirqtrg
|
||||||
|
}
|
||||||
|
#[doc = "0x40 - Clear FIFO Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn fifo_clr(&self) -> &FifoClr {
|
||||||
|
&self.fifo_clr
|
||||||
|
}
|
||||||
|
#[doc = "0x44 - Timing Config Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn tmconfig(&self) -> &Tmconfig {
|
||||||
|
&self.tmconfig
|
||||||
|
}
|
||||||
|
#[doc = "0x48 - Clock Low Timeout Limit Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn clktolimit(&self) -> &Clktolimit {
|
||||||
|
&self.clktolimit
|
||||||
|
}
|
||||||
|
#[doc = "0x100 - Slave Control Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_ctrl(&self) -> &S0Ctrl {
|
||||||
|
&self.s0_ctrl
|
||||||
|
}
|
||||||
|
#[doc = "0x104 - Slave MaxWords Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_maxwords(&self) -> &S0Maxwords {
|
||||||
|
&self.s0_maxwords
|
||||||
|
}
|
||||||
|
#[doc = "0x108 - Slave I2C Address Value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_address(&self) -> &S0Address {
|
||||||
|
&self.s0_address
|
||||||
|
}
|
||||||
|
#[doc = "0x10c - Slave I2C Address Mask value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_addressmask(&self) -> &S0Addressmask {
|
||||||
|
&self.s0_addressmask
|
||||||
|
}
|
||||||
|
#[doc = "0x110 - Slave Data Input/Output"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_data(&self) -> &S0Data {
|
||||||
|
&self.s0_data
|
||||||
|
}
|
||||||
|
#[doc = "0x114 - Slave I2C Last Address value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_lastaddress(&self) -> &S0Lastaddress {
|
||||||
|
&self.s0_lastaddress
|
||||||
|
}
|
||||||
|
#[doc = "0x118 - Slave I2C Controller Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_status(&self) -> &S0Status {
|
||||||
|
&self.s0_status
|
||||||
|
}
|
||||||
|
#[doc = "0x11c - Internal STATE of I2C Slave Controller"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_state(&self) -> &S0State {
|
||||||
|
&self.s0_state
|
||||||
|
}
|
||||||
|
#[doc = "0x120 - Slave TX Count Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_txcount(&self) -> &S0Txcount {
|
||||||
|
&self.s0_txcount
|
||||||
|
}
|
||||||
|
#[doc = "0x124 - Slave RX Count Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_rxcount(&self) -> &S0Rxcount {
|
||||||
|
&self.s0_rxcount
|
||||||
|
}
|
||||||
|
#[doc = "0x128 - Slave Interrupt Enable Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_irq_enb(&self) -> &S0IrqEnb {
|
||||||
|
&self.s0_irq_enb
|
||||||
|
}
|
||||||
|
#[doc = "0x12c - Slave Raw Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_irq_raw(&self) -> &S0IrqRaw {
|
||||||
|
&self.s0_irq_raw
|
||||||
|
}
|
||||||
|
#[doc = "0x130 - Slave Enabled Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_irq_end(&self) -> &S0IrqEnd {
|
||||||
|
&self.s0_irq_end
|
||||||
|
}
|
||||||
|
#[doc = "0x134 - Slave Clear Interrupt Status Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_irq_clr(&self) -> &S0IrqClr {
|
||||||
|
&self.s0_irq_clr
|
||||||
|
}
|
||||||
|
#[doc = "0x138 - Slave Rx FIFO IRQ Trigger Level"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_rxfifoirqtrg(&self) -> &S0Rxfifoirqtrg {
|
||||||
|
&self.s0_rxfifoirqtrg
|
||||||
|
}
|
||||||
|
#[doc = "0x13c - Slave Tx FIFO IRQ Trigger Level"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_txfifoirqtrg(&self) -> &S0Txfifoirqtrg {
|
||||||
|
&self.s0_txfifoirqtrg
|
||||||
|
}
|
||||||
|
#[doc = "0x140 - Slave Clear FIFO Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_fifo_clr(&self) -> &S0FifoClr {
|
||||||
|
&self.s0_fifo_clr
|
||||||
|
}
|
||||||
|
#[doc = "0x144 - Slave I2C Address B Value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_addressb(&self) -> &S0Addressb {
|
||||||
|
&self.s0_addressb
|
||||||
|
}
|
||||||
|
#[doc = "0x148 - Slave I2C Address B Mask value"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn s0_addressmaskb(&self) -> &S0Addressmaskb {
|
||||||
|
&self.s0_addressmaskb
|
||||||
|
}
|
||||||
|
#[doc = "0xffc - Peripheral ID Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn perid(&self) -> &Perid {
|
||||||
|
&self.perid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "CTRL (rw) register accessor: Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`ctrl::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`ctrl::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@ctrl`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "CTRL")]
|
||||||
|
pub type Ctrl = crate::Reg<ctrl::CtrlSpec>;
|
||||||
|
#[doc = "Control Register"]
|
||||||
|
pub mod ctrl;
|
||||||
|
#[doc = "CLKSCALE (rw) register accessor: Clock Scale divide value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clkscale::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clkscale::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@clkscale`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "CLKSCALE")]
|
||||||
|
pub type Clkscale = crate::Reg<clkscale::ClkscaleSpec>;
|
||||||
|
#[doc = "Clock Scale divide value"]
|
||||||
|
pub mod clkscale;
|
||||||
|
#[doc = "WORDS (rw) register accessor: Word Count value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`words::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`words::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@words`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "WORDS")]
|
||||||
|
pub type Words = crate::Reg<words::WordsSpec>;
|
||||||
|
#[doc = "Word Count value"]
|
||||||
|
pub mod words;
|
||||||
|
#[doc = "ADDRESS (rw) register accessor: I2C Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`address::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`address::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@address`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "ADDRESS")]
|
||||||
|
pub type Address = crate::Reg<address::AddressSpec>;
|
||||||
|
#[doc = "I2C Address value"]
|
||||||
|
pub mod address;
|
||||||
|
#[doc = "DATA (rw) register accessor: Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`data::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`data::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@data`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "DATA")]
|
||||||
|
pub type Data = crate::Reg<data::DataSpec>;
|
||||||
|
#[doc = "Data Input/Output"]
|
||||||
|
pub mod data;
|
||||||
|
#[doc = "CMD (rw) register accessor: Command Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cmd::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`cmd::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@cmd`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "CMD")]
|
||||||
|
pub type Cmd = crate::Reg<cmd::CmdSpec>;
|
||||||
|
#[doc = "Command Register"]
|
||||||
|
pub mod cmd;
|
||||||
|
#[doc = "STATUS (r) register accessor: I2C Controller Status Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@status`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "STATUS")]
|
||||||
|
pub type Status = crate::Reg<status::StatusSpec>;
|
||||||
|
#[doc = "I2C Controller Status Register"]
|
||||||
|
pub mod status;
|
||||||
|
#[doc = "STATE (r) register accessor: Internal STATE of I2C Master Controller\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@state`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "STATE")]
|
||||||
|
pub type State = crate::Reg<state::StateSpec>;
|
||||||
|
#[doc = "Internal STATE of I2C Master Controller"]
|
||||||
|
pub mod state;
|
||||||
|
#[doc = "TXCOUNT (r) register accessor: TX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@txcount`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "TXCOUNT")]
|
||||||
|
pub type Txcount = crate::Reg<txcount::TxcountSpec>;
|
||||||
|
#[doc = "TX Count Register"]
|
||||||
|
pub mod txcount;
|
||||||
|
#[doc = "RXCOUNT (r) register accessor: RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@rxcount`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "RXCOUNT")]
|
||||||
|
pub type Rxcount = crate::Reg<rxcount::RxcountSpec>;
|
||||||
|
#[doc = "RX Count Register"]
|
||||||
|
pub mod rxcount;
|
||||||
|
#[doc = "IRQ_ENB (rw) register accessor: Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_enb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`irq_enb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_enb`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "IRQ_ENB")]
|
||||||
|
pub type IrqEnb = crate::Reg<irq_enb::IrqEnbSpec>;
|
||||||
|
#[doc = "Interrupt Enable Register"]
|
||||||
|
pub mod irq_enb;
|
||||||
|
pub use irq_enb as irq_raw;
|
||||||
|
pub use irq_enb as irq_end;
|
||||||
|
pub use irq_enb as irq_clr;
|
||||||
|
pub use IrqEnb as IrqRaw;
|
||||||
|
pub use IrqEnb as IrqEnd;
|
||||||
|
pub use IrqEnb as IrqClr;
|
||||||
|
#[doc = "RXFIFOIRQTRG (rw) register accessor: Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`rxfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@rxfifoirqtrg`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "RXFIFOIRQTRG")]
|
||||||
|
pub type Rxfifoirqtrg = crate::Reg<rxfifoirqtrg::RxfifoirqtrgSpec>;
|
||||||
|
#[doc = "Rx FIFO IRQ Trigger Level"]
|
||||||
|
pub mod rxfifoirqtrg;
|
||||||
|
#[doc = "TXFIFOIRQTRG (rw) register accessor: Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`txfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`txfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@txfifoirqtrg`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "TXFIFOIRQTRG")]
|
||||||
|
pub type Txfifoirqtrg = crate::Reg<txfifoirqtrg::TxfifoirqtrgSpec>;
|
||||||
|
#[doc = "Tx FIFO IRQ Trigger Level"]
|
||||||
|
pub mod txfifoirqtrg;
|
||||||
|
#[doc = "FIFO_CLR (w) register accessor: Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@fifo_clr`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "FIFO_CLR")]
|
||||||
|
pub type FifoClr = crate::Reg<fifo_clr::FifoClrSpec>;
|
||||||
|
#[doc = "Clear FIFO Register"]
|
||||||
|
pub mod fifo_clr;
|
||||||
|
#[doc = "TMCONFIG (rw) register accessor: Timing Config Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`tmconfig::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`tmconfig::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@tmconfig`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "TMCONFIG")]
|
||||||
|
pub type Tmconfig = crate::Reg<tmconfig::TmconfigSpec>;
|
||||||
|
#[doc = "Timing Config Register"]
|
||||||
|
pub mod tmconfig;
|
||||||
|
#[doc = "CLKTOLIMIT (rw) register accessor: Clock Low Timeout Limit Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clktolimit::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clktolimit::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@clktolimit`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "CLKTOLIMIT")]
|
||||||
|
pub type Clktolimit = crate::Reg<clktolimit::ClktolimitSpec>;
|
||||||
|
#[doc = "Clock Low Timeout Limit Register"]
|
||||||
|
pub mod clktolimit;
|
||||||
|
#[doc = "S0_CTRL (rw) register accessor: Slave Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_ctrl::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_ctrl::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_ctrl`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_CTRL")]
|
||||||
|
pub type S0Ctrl = crate::Reg<s0_ctrl::S0CtrlSpec>;
|
||||||
|
#[doc = "Slave Control Register"]
|
||||||
|
pub mod s0_ctrl;
|
||||||
|
#[doc = "S0_MAXWORDS (rw) register accessor: Slave MaxWords Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_maxwords::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_maxwords::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_maxwords`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_MAXWORDS")]
|
||||||
|
pub type S0Maxwords = crate::Reg<s0_maxwords::S0MaxwordsSpec>;
|
||||||
|
#[doc = "Slave MaxWords Register"]
|
||||||
|
pub mod s0_maxwords;
|
||||||
|
#[doc = "S0_ADDRESS (rw) register accessor: Slave I2C Address Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_address::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_address::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_address`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_ADDRESS")]
|
||||||
|
pub type S0Address = crate::Reg<s0_address::S0AddressSpec>;
|
||||||
|
#[doc = "Slave I2C Address Value"]
|
||||||
|
pub mod s0_address;
|
||||||
|
#[doc = "S0_ADDRESSMASK (rw) register accessor: Slave I2C Address Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmask::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmask::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressmask`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_ADDRESSMASK")]
|
||||||
|
pub type S0Addressmask = crate::Reg<s0_addressmask::S0AddressmaskSpec>;
|
||||||
|
#[doc = "Slave I2C Address Mask value"]
|
||||||
|
pub mod s0_addressmask;
|
||||||
|
#[doc = "S0_DATA (rw) register accessor: Slave Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_data::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_data::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_data`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_DATA")]
|
||||||
|
pub type S0Data = crate::Reg<s0_data::S0DataSpec>;
|
||||||
|
#[doc = "Slave Data Input/Output"]
|
||||||
|
pub mod s0_data;
|
||||||
|
#[doc = "S0_LASTADDRESS (r) register accessor: Slave I2C Last Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_lastaddress::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_lastaddress`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_LASTADDRESS")]
|
||||||
|
pub type S0Lastaddress = crate::Reg<s0_lastaddress::S0LastaddressSpec>;
|
||||||
|
#[doc = "Slave I2C Last Address value"]
|
||||||
|
pub mod s0_lastaddress;
|
||||||
|
#[doc = "S0_STATUS (r) register accessor: Slave I2C Controller Status Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_status::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_status`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_STATUS")]
|
||||||
|
pub type S0Status = crate::Reg<s0_status::S0StatusSpec>;
|
||||||
|
#[doc = "Slave I2C Controller Status Register"]
|
||||||
|
pub mod s0_status;
|
||||||
|
#[doc = "S0_STATE (r) register accessor: Internal STATE of I2C Slave Controller\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_state::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_state`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_STATE")]
|
||||||
|
pub type S0State = crate::Reg<s0_state::S0StateSpec>;
|
||||||
|
#[doc = "Internal STATE of I2C Slave Controller"]
|
||||||
|
pub mod s0_state;
|
||||||
|
#[doc = "S0_TXCOUNT (r) register accessor: Slave TX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_txcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_txcount`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_TXCOUNT")]
|
||||||
|
pub type S0Txcount = crate::Reg<s0_txcount::S0TxcountSpec>;
|
||||||
|
#[doc = "Slave TX Count Register"]
|
||||||
|
pub mod s0_txcount;
|
||||||
|
#[doc = "S0_RXCOUNT (r) register accessor: Slave RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_rxcount::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_rxcount`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_RXCOUNT")]
|
||||||
|
pub type S0Rxcount = crate::Reg<s0_rxcount::S0RxcountSpec>;
|
||||||
|
#[doc = "Slave RX Count Register"]
|
||||||
|
pub mod s0_rxcount;
|
||||||
|
#[doc = "S0_IRQ_ENB (rw) register accessor: Slave Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_irq_enb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_irq_enb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_irq_enb`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_IRQ_ENB")]
|
||||||
|
pub type S0IrqEnb = crate::Reg<s0_irq_enb::S0IrqEnbSpec>;
|
||||||
|
#[doc = "Slave Interrupt Enable Register"]
|
||||||
|
pub mod s0_irq_enb;
|
||||||
|
pub use s0_irq_enb as s0_irq_raw;
|
||||||
|
pub use s0_irq_enb as s0_irq_end;
|
||||||
|
pub use s0_irq_enb as s0_irq_clr;
|
||||||
|
pub use S0IrqEnb as S0IrqRaw;
|
||||||
|
pub use S0IrqEnb as S0IrqEnd;
|
||||||
|
pub use S0IrqEnb as S0IrqClr;
|
||||||
|
#[doc = "S0_RXFIFOIRQTRG (rw) register accessor: Slave Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_rxfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_rxfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_rxfifoirqtrg`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_RXFIFOIRQTRG")]
|
||||||
|
pub type S0Rxfifoirqtrg = crate::Reg<s0_rxfifoirqtrg::S0RxfifoirqtrgSpec>;
|
||||||
|
#[doc = "Slave Rx FIFO IRQ Trigger Level"]
|
||||||
|
pub mod s0_rxfifoirqtrg;
|
||||||
|
#[doc = "S0_TXFIFOIRQTRG (rw) register accessor: Slave Tx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_txfifoirqtrg::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_txfifoirqtrg::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_txfifoirqtrg`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_TXFIFOIRQTRG")]
|
||||||
|
pub type S0Txfifoirqtrg = crate::Reg<s0_txfifoirqtrg::S0TxfifoirqtrgSpec>;
|
||||||
|
#[doc = "Slave Tx FIFO IRQ Trigger Level"]
|
||||||
|
pub mod s0_txfifoirqtrg;
|
||||||
|
#[doc = "S0_FIFO_CLR (w) register accessor: Slave Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_fifo_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_fifo_clr`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_FIFO_CLR")]
|
||||||
|
pub type S0FifoClr = crate::Reg<s0_fifo_clr::S0FifoClrSpec>;
|
||||||
|
#[doc = "Slave Clear FIFO Register"]
|
||||||
|
pub mod s0_fifo_clr;
|
||||||
|
#[doc = "S0_ADDRESSB (rw) register accessor: Slave I2C Address B Value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressb`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_ADDRESSB")]
|
||||||
|
pub type S0Addressb = crate::Reg<s0_addressb::S0AddressbSpec>;
|
||||||
|
#[doc = "Slave I2C Address B Value"]
|
||||||
|
pub mod s0_addressb;
|
||||||
|
#[doc = "S0_ADDRESSMASKB (rw) register accessor: Slave I2C Address B Mask value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`s0_addressmaskb::R`]. You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`s0_addressmaskb::W`]. You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@s0_addressmaskb`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "S0_ADDRESSMASKB")]
|
||||||
|
pub type S0Addressmaskb = crate::Reg<s0_addressmaskb::S0AddressmaskbSpec>;
|
||||||
|
#[doc = "Slave I2C Address B Mask value"]
|
||||||
|
pub mod s0_addressmaskb;
|
||||||
|
#[doc = "PERID (r) register accessor: Peripheral ID Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`perid::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@perid`]
|
||||||
|
module"]
|
||||||
|
#[doc(alias = "PERID")]
|
||||||
|
pub type Perid = crate::Reg<perid::PeridSpec>;
|
||||||
|
#[doc = "Peripheral ID Register"]
|
||||||
|
pub mod perid;
|
27
va108xx/src/i2ca/address.rs
Normal file
27
va108xx/src/i2ca/address.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[doc = "Register `ADDRESS` reader"]
|
||||||
|
pub type R = crate::R<AddressSpec>;
|
||||||
|
#[doc = "Register `ADDRESS` writer"]
|
||||||
|
pub type W = crate::W<AddressSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {}
|
||||||
|
#[doc = "I2C Address value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`address::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`address::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct AddressSpec;
|
||||||
|
impl crate::RegisterSpec for AddressSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`address::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for AddressSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`address::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for AddressSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets ADDRESS to value 0"]
|
||||||
|
impl crate::Resettable for AddressSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
55
va108xx/src/i2ca/clkscale.rs
Normal file
55
va108xx/src/i2ca/clkscale.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#[doc = "Register `CLKSCALE` reader"]
|
||||||
|
pub type R = crate::R<ClkscaleSpec>;
|
||||||
|
#[doc = "Register `CLKSCALE` writer"]
|
||||||
|
pub type W = crate::W<ClkscaleSpec>;
|
||||||
|
#[doc = "Field `VALUE` reader - Enable FastMode"]
|
||||||
|
pub type ValueR = crate::FieldReader<u32>;
|
||||||
|
#[doc = "Field `VALUE` writer - Enable FastMode"]
|
||||||
|
pub type ValueW<'a, REG> = crate::FieldWriter<'a, REG, 31, u32>;
|
||||||
|
#[doc = "Field `FASTMODE` reader - Enable FastMode"]
|
||||||
|
pub type FastmodeR = crate::BitReader;
|
||||||
|
#[doc = "Field `FASTMODE` writer - Enable FastMode"]
|
||||||
|
pub type FastmodeW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
impl R {
|
||||||
|
#[doc = "Bits 0:30 - Enable FastMode"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn value(&self) -> ValueR {
|
||||||
|
ValueR::new(self.bits & 0x7fff_ffff)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 31 - Enable FastMode"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn fastmode(&self) -> FastmodeR {
|
||||||
|
FastmodeR::new(((self.bits >> 31) & 1) != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {
|
||||||
|
#[doc = "Bits 0:30 - Enable FastMode"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn value(&mut self) -> ValueW<ClkscaleSpec> {
|
||||||
|
ValueW::new(self, 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 31 - Enable FastMode"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn fastmode(&mut self) -> FastmodeW<ClkscaleSpec> {
|
||||||
|
FastmodeW::new(self, 31)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "Clock Scale divide value\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clkscale::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clkscale::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct ClkscaleSpec;
|
||||||
|
impl crate::RegisterSpec for ClkscaleSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`clkscale::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for ClkscaleSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`clkscale::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for ClkscaleSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets CLKSCALE to value 0"]
|
||||||
|
impl crate::Resettable for ClkscaleSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
27
va108xx/src/i2ca/clktolimit.rs
Normal file
27
va108xx/src/i2ca/clktolimit.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[doc = "Register `CLKTOLIMIT` reader"]
|
||||||
|
pub type R = crate::R<ClktolimitSpec>;
|
||||||
|
#[doc = "Register `CLKTOLIMIT` writer"]
|
||||||
|
pub type W = crate::W<ClktolimitSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {}
|
||||||
|
#[doc = "Clock Low Timeout Limit Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`clktolimit::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`clktolimit::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct ClktolimitSpec;
|
||||||
|
impl crate::RegisterSpec for ClktolimitSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`clktolimit::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for ClktolimitSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`clktolimit::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for ClktolimitSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets CLKTOLIMIT to value 0"]
|
||||||
|
impl crate::Resettable for ClktolimitSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
27
va108xx/src/i2ca/cmd.rs
Normal file
27
va108xx/src/i2ca/cmd.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[doc = "Register `CMD` reader"]
|
||||||
|
pub type R = crate::R<CmdSpec>;
|
||||||
|
#[doc = "Register `CMD` writer"]
|
||||||
|
pub type W = crate::W<CmdSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {}
|
||||||
|
#[doc = "Command Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cmd::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`cmd::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct CmdSpec;
|
||||||
|
impl crate::RegisterSpec for CmdSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`cmd::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for CmdSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`cmd::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for CmdSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets CMD to value 0"]
|
||||||
|
impl crate::Resettable for CmdSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
160
va108xx/src/i2ca/ctrl.rs
Normal file
160
va108xx/src/i2ca/ctrl.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#[doc = "Register `CTRL` reader"]
|
||||||
|
pub type R = crate::R<CtrlSpec>;
|
||||||
|
#[doc = "Register `CTRL` writer"]
|
||||||
|
pub type W = crate::W<CtrlSpec>;
|
||||||
|
#[doc = "Field `CLKENABLED` reader - I2C CLK Enabled"]
|
||||||
|
pub type ClkenabledR = crate::BitReader;
|
||||||
|
#[doc = "Field `CLKENABLED` writer - I2C CLK Enabled"]
|
||||||
|
pub type ClkenabledW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `ENABLED` reader - I2C Activated"]
|
||||||
|
pub type EnabledR = crate::BitReader;
|
||||||
|
#[doc = "Field `ENABLED` writer - I2C Activated"]
|
||||||
|
pub type EnabledW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `ENABLE` reader - I2C Active"]
|
||||||
|
pub type EnableR = crate::BitReader;
|
||||||
|
#[doc = "Field `ENABLE` writer - I2C Active"]
|
||||||
|
pub type EnableW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TXFEMD` reader - TX FIFIO Empty Mode"]
|
||||||
|
pub type TxfemdR = crate::BitReader;
|
||||||
|
#[doc = "Field `TXFEMD` writer - TX FIFIO Empty Mode"]
|
||||||
|
pub type TxfemdW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `RXFFMD` reader - RX FIFO Full Mode"]
|
||||||
|
pub type RxffmdR = crate::BitReader;
|
||||||
|
#[doc = "Field `RXFFMD` writer - RX FIFO Full Mode"]
|
||||||
|
pub type RxffmdW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `ALGFILTER` reader - Enable Input Analog Glitch Filter"]
|
||||||
|
pub type AlgfilterR = crate::BitReader;
|
||||||
|
#[doc = "Field `ALGFILTER` writer - Enable Input Analog Glitch Filter"]
|
||||||
|
pub type AlgfilterW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `DLGFILTER` reader - Enable Input Digital Glitch Filter"]
|
||||||
|
pub type DlgfilterR = crate::BitReader;
|
||||||
|
#[doc = "Field `DLGFILTER` writer - Enable Input Digital Glitch Filter"]
|
||||||
|
pub type DlgfilterW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `LOOPBACK` reader - Enable LoopBack Mode"]
|
||||||
|
pub type LoopbackR = crate::BitReader;
|
||||||
|
#[doc = "Field `LOOPBACK` writer - Enable LoopBack Mode"]
|
||||||
|
pub type LoopbackW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TMCONFIGENB` reader - Enable Timing Config Register"]
|
||||||
|
pub type TmconfigenbR = crate::BitReader;
|
||||||
|
#[doc = "Field `TMCONFIGENB` writer - Enable Timing Config Register"]
|
||||||
|
pub type TmconfigenbW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
impl R {
|
||||||
|
#[doc = "Bit 0 - I2C CLK Enabled"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clkenabled(&self) -> ClkenabledR {
|
||||||
|
ClkenabledR::new((self.bits & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 1 - I2C Activated"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enabled(&self) -> EnabledR {
|
||||||
|
EnabledR::new(((self.bits >> 1) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 2 - I2C Active"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn enable(&self) -> EnableR {
|
||||||
|
EnableR::new(((self.bits >> 2) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn txfemd(&self) -> TxfemdR {
|
||||||
|
TxfemdR::new(((self.bits >> 3) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn rxffmd(&self) -> RxffmdR {
|
||||||
|
RxffmdR::new(((self.bits >> 4) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 5 - Enable Input Analog Glitch Filter"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn algfilter(&self) -> AlgfilterR {
|
||||||
|
AlgfilterR::new(((self.bits >> 5) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 6 - Enable Input Digital Glitch Filter"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn dlgfilter(&self) -> DlgfilterR {
|
||||||
|
DlgfilterR::new(((self.bits >> 6) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 8 - Enable LoopBack Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn loopback(&self) -> LoopbackR {
|
||||||
|
LoopbackR::new(((self.bits >> 8) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 9 - Enable Timing Config Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn tmconfigenb(&self) -> TmconfigenbR {
|
||||||
|
TmconfigenbR::new(((self.bits >> 9) & 1) != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {
|
||||||
|
#[doc = "Bit 0 - I2C CLK Enabled"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn clkenabled(&mut self) -> ClkenabledW<CtrlSpec> {
|
||||||
|
ClkenabledW::new(self, 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 1 - I2C Activated"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn enabled(&mut self) -> EnabledW<CtrlSpec> {
|
||||||
|
EnabledW::new(self, 1)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 2 - I2C Active"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn enable(&mut self) -> EnableW<CtrlSpec> {
|
||||||
|
EnableW::new(self, 2)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 3 - TX FIFIO Empty Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn txfemd(&mut self) -> TxfemdW<CtrlSpec> {
|
||||||
|
TxfemdW::new(self, 3)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 4 - RX FIFO Full Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn rxffmd(&mut self) -> RxffmdW<CtrlSpec> {
|
||||||
|
RxffmdW::new(self, 4)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 5 - Enable Input Analog Glitch Filter"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn algfilter(&mut self) -> AlgfilterW<CtrlSpec> {
|
||||||
|
AlgfilterW::new(self, 5)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 6 - Enable Input Digital Glitch Filter"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn dlgfilter(&mut self) -> DlgfilterW<CtrlSpec> {
|
||||||
|
DlgfilterW::new(self, 6)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 8 - Enable LoopBack Mode"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn loopback(&mut self) -> LoopbackW<CtrlSpec> {
|
||||||
|
LoopbackW::new(self, 8)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 9 - Enable Timing Config Register"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn tmconfigenb(&mut self) -> TmconfigenbW<CtrlSpec> {
|
||||||
|
TmconfigenbW::new(self, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`ctrl::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`ctrl::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct CtrlSpec;
|
||||||
|
impl crate::RegisterSpec for CtrlSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`ctrl::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for CtrlSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`ctrl::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for CtrlSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets CTRL to value 0"]
|
||||||
|
impl crate::Resettable for CtrlSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
27
va108xx/src/i2ca/data.rs
Normal file
27
va108xx/src/i2ca/data.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[doc = "Register `DATA` reader"]
|
||||||
|
pub type R = crate::R<DataSpec>;
|
||||||
|
#[doc = "Register `DATA` writer"]
|
||||||
|
pub type W = crate::W<DataSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {}
|
||||||
|
#[doc = "Data Input/Output\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`data::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`data::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct DataSpec;
|
||||||
|
impl crate::RegisterSpec for DataSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`data::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for DataSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`data::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for DataSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets DATA to value 0"]
|
||||||
|
impl crate::Resettable for DataSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
35
va108xx/src/i2ca/fifo_clr.rs
Normal file
35
va108xx/src/i2ca/fifo_clr.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#[doc = "Register `FIFO_CLR` writer"]
|
||||||
|
pub type W = crate::W<FifoClrSpec>;
|
||||||
|
#[doc = "Field `RXFIFO` writer - Clear Rx FIFO"]
|
||||||
|
pub type RxfifoW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TXFIFO` writer - Clear Tx FIFO"]
|
||||||
|
pub type TxfifoW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
impl W {
|
||||||
|
#[doc = "Bit 0 - Clear Rx FIFO"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn rxfifo(&mut self) -> RxfifoW<FifoClrSpec> {
|
||||||
|
RxfifoW::new(self, 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 1 - Clear Tx FIFO"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn txfifo(&mut self) -> TxfifoW<FifoClrSpec> {
|
||||||
|
TxfifoW::new(self, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "Clear FIFO Register\n\nYou can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`fifo_clr::W`](W). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct FifoClrSpec;
|
||||||
|
impl crate::RegisterSpec for FifoClrSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`fifo_clr::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for FifoClrSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets FIFO_CLR to value 0"]
|
||||||
|
impl crate::Resettable for FifoClrSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
235
va108xx/src/i2ca/irq_enb.rs
Normal file
235
va108xx/src/i2ca/irq_enb.rs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#[doc = "Register `IRQ_ENB` reader"]
|
||||||
|
pub type R = crate::R<IrqEnbSpec>;
|
||||||
|
#[doc = "Register `IRQ_ENB` writer"]
|
||||||
|
pub type W = crate::W<IrqEnbSpec>;
|
||||||
|
#[doc = "Field `I2CIDLE` reader - I2C Bus is Idle"]
|
||||||
|
pub type I2cidleR = crate::BitReader;
|
||||||
|
#[doc = "Field `I2CIDLE` writer - I2C Bus is Idle"]
|
||||||
|
pub type I2cidleW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `IDLE` reader - Controller is Idle"]
|
||||||
|
pub type IdleR = crate::BitReader;
|
||||||
|
#[doc = "Field `IDLE` writer - Controller is Idle"]
|
||||||
|
pub type IdleW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `WAITING` reader - Controller is Waiting"]
|
||||||
|
pub type WaitingR = crate::BitReader;
|
||||||
|
#[doc = "Field `WAITING` writer - Controller is Waiting"]
|
||||||
|
pub type WaitingW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `STALLED` reader - Controller is Stalled"]
|
||||||
|
pub type StalledR = crate::BitReader;
|
||||||
|
#[doc = "Field `STALLED` writer - Controller is Stalled"]
|
||||||
|
pub type StalledW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `ARBLOST` reader - I2C Arbitration was lost"]
|
||||||
|
pub type ArblostR = crate::BitReader;
|
||||||
|
#[doc = "Field `ARBLOST` writer - I2C Arbitration was lost"]
|
||||||
|
pub type ArblostW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `NACKADDR` reader - I2C Address was not Acknowledged"]
|
||||||
|
pub type NackaddrR = crate::BitReader;
|
||||||
|
#[doc = "Field `NACKADDR` writer - I2C Address was not Acknowledged"]
|
||||||
|
pub type NackaddrW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `NACKDATA` reader - I2C Data was not Acknowledged"]
|
||||||
|
pub type NackdataR = crate::BitReader;
|
||||||
|
#[doc = "Field `NACKDATA` writer - I2C Data was not Acknowledged"]
|
||||||
|
pub type NackdataW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `CLKLOTO` reader - I2C Clock Low Timeout"]
|
||||||
|
pub type ClklotoR = crate::BitReader;
|
||||||
|
#[doc = "Field `CLKLOTO` writer - I2C Clock Low Timeout"]
|
||||||
|
pub type ClklotoW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TXOVERFLOW` reader - TX FIFO Overflowed"]
|
||||||
|
pub type TxoverflowR = crate::BitReader;
|
||||||
|
#[doc = "Field `TXOVERFLOW` writer - TX FIFO Overflowed"]
|
||||||
|
pub type TxoverflowW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `RXOVERFLOW` reader - TX FIFO Overflowed"]
|
||||||
|
pub type RxoverflowR = crate::BitReader;
|
||||||
|
#[doc = "Field `RXOVERFLOW` writer - TX FIFO Overflowed"]
|
||||||
|
pub type RxoverflowW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TXREADY` reader - TX FIFO Ready"]
|
||||||
|
pub type TxreadyR = crate::BitReader;
|
||||||
|
#[doc = "Field `TXREADY` writer - TX FIFO Ready"]
|
||||||
|
pub type TxreadyW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `RXREADY` reader - RX FIFO Ready"]
|
||||||
|
pub type RxreadyR = crate::BitReader;
|
||||||
|
#[doc = "Field `RXREADY` writer - RX FIFO Ready"]
|
||||||
|
pub type RxreadyW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `TXEMPTY` reader - TX FIFO Empty"]
|
||||||
|
pub type TxemptyR = crate::BitReader;
|
||||||
|
#[doc = "Field `TXEMPTY` writer - TX FIFO Empty"]
|
||||||
|
pub type TxemptyW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
#[doc = "Field `RXFULL` reader - RX FIFO Full"]
|
||||||
|
pub type RxfullR = crate::BitReader;
|
||||||
|
#[doc = "Field `RXFULL` writer - RX FIFO Full"]
|
||||||
|
pub type RxfullW<'a, REG> = crate::BitWriter<'a, REG>;
|
||||||
|
impl R {
|
||||||
|
#[doc = "Bit 0 - I2C Bus is Idle"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn i2cidle(&self) -> I2cidleR {
|
||||||
|
I2cidleR::new((self.bits & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 1 - Controller is Idle"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn idle(&self) -> IdleR {
|
||||||
|
IdleR::new(((self.bits >> 1) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 2 - Controller is Waiting"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn waiting(&self) -> WaitingR {
|
||||||
|
WaitingR::new(((self.bits >> 2) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 3 - Controller is Stalled"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn stalled(&self) -> StalledR {
|
||||||
|
StalledR::new(((self.bits >> 3) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 4 - I2C Arbitration was lost"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn arblost(&self) -> ArblostR {
|
||||||
|
ArblostR::new(((self.bits >> 4) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 5 - I2C Address was not Acknowledged"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn nackaddr(&self) -> NackaddrR {
|
||||||
|
NackaddrR::new(((self.bits >> 5) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 6 - I2C Data was not Acknowledged"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn nackdata(&self) -> NackdataR {
|
||||||
|
NackdataR::new(((self.bits >> 6) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 7 - I2C Clock Low Timeout"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clkloto(&self) -> ClklotoR {
|
||||||
|
ClklotoR::new(((self.bits >> 7) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 10 - TX FIFO Overflowed"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn txoverflow(&self) -> TxoverflowR {
|
||||||
|
TxoverflowR::new(((self.bits >> 10) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 11 - TX FIFO Overflowed"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn rxoverflow(&self) -> RxoverflowR {
|
||||||
|
RxoverflowR::new(((self.bits >> 11) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 12 - TX FIFO Ready"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn txready(&self) -> TxreadyR {
|
||||||
|
TxreadyR::new(((self.bits >> 12) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 13 - RX FIFO Ready"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn rxready(&self) -> RxreadyR {
|
||||||
|
RxreadyR::new(((self.bits >> 13) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 14 - TX FIFO Empty"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn txempty(&self) -> TxemptyR {
|
||||||
|
TxemptyR::new(((self.bits >> 14) & 1) != 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 15 - RX FIFO Full"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn rxfull(&self) -> RxfullR {
|
||||||
|
RxfullR::new(((self.bits >> 15) & 1) != 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {
|
||||||
|
#[doc = "Bit 0 - I2C Bus is Idle"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn i2cidle(&mut self) -> I2cidleW<IrqEnbSpec> {
|
||||||
|
I2cidleW::new(self, 0)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 1 - Controller is Idle"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn idle(&mut self) -> IdleW<IrqEnbSpec> {
|
||||||
|
IdleW::new(self, 1)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 2 - Controller is Waiting"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn waiting(&mut self) -> WaitingW<IrqEnbSpec> {
|
||||||
|
WaitingW::new(self, 2)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 3 - Controller is Stalled"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn stalled(&mut self) -> StalledW<IrqEnbSpec> {
|
||||||
|
StalledW::new(self, 3)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 4 - I2C Arbitration was lost"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn arblost(&mut self) -> ArblostW<IrqEnbSpec> {
|
||||||
|
ArblostW::new(self, 4)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 5 - I2C Address was not Acknowledged"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn nackaddr(&mut self) -> NackaddrW<IrqEnbSpec> {
|
||||||
|
NackaddrW::new(self, 5)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 6 - I2C Data was not Acknowledged"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn nackdata(&mut self) -> NackdataW<IrqEnbSpec> {
|
||||||
|
NackdataW::new(self, 6)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 7 - I2C Clock Low Timeout"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn clkloto(&mut self) -> ClklotoW<IrqEnbSpec> {
|
||||||
|
ClklotoW::new(self, 7)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 10 - TX FIFO Overflowed"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn txoverflow(&mut self) -> TxoverflowW<IrqEnbSpec> {
|
||||||
|
TxoverflowW::new(self, 10)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 11 - TX FIFO Overflowed"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn rxoverflow(&mut self) -> RxoverflowW<IrqEnbSpec> {
|
||||||
|
RxoverflowW::new(self, 11)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 12 - TX FIFO Ready"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn txready(&mut self) -> TxreadyW<IrqEnbSpec> {
|
||||||
|
TxreadyW::new(self, 12)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 13 - RX FIFO Ready"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn rxready(&mut self) -> RxreadyW<IrqEnbSpec> {
|
||||||
|
RxreadyW::new(self, 13)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 14 - TX FIFO Empty"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn txempty(&mut self) -> TxemptyW<IrqEnbSpec> {
|
||||||
|
TxemptyW::new(self, 14)
|
||||||
|
}
|
||||||
|
#[doc = "Bit 15 - RX FIFO Full"]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn rxfull(&mut self) -> RxfullW<IrqEnbSpec> {
|
||||||
|
RxfullW::new(self, 15)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_enb::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`irq_enb::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct IrqEnbSpec;
|
||||||
|
impl crate::RegisterSpec for IrqEnbSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`irq_enb::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for IrqEnbSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`irq_enb::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for IrqEnbSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets IRQ_ENB to value 0"]
|
||||||
|
impl crate::Resettable for IrqEnbSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
18
va108xx/src/i2ca/perid.rs
Normal file
18
va108xx/src/i2ca/perid.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#[doc = "Register `PERID` reader"]
|
||||||
|
pub type R = crate::R<PeridSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "Peripheral ID Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`perid::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct PeridSpec;
|
||||||
|
impl crate::RegisterSpec for PeridSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`perid::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for PeridSpec {}
|
||||||
|
#[doc = "`reset()` method sets PERID to value 0x0014_07e1"]
|
||||||
|
impl crate::Resettable for PeridSpec {
|
||||||
|
const RESET_VALUE: u32 = 0x0014_07e1;
|
||||||
|
}
|
18
va108xx/src/i2ca/rxcount.rs
Normal file
18
va108xx/src/i2ca/rxcount.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#[doc = "Register `RXCOUNT` reader"]
|
||||||
|
pub type R = crate::R<RxcountSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[doc = "RX Count Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxcount::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct RxcountSpec;
|
||||||
|
impl crate::RegisterSpec for RxcountSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`rxcount::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for RxcountSpec {}
|
||||||
|
#[doc = "`reset()` method sets RXCOUNT to value 0"]
|
||||||
|
impl crate::Resettable for RxcountSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
27
va108xx/src/i2ca/rxfifoirqtrg.rs
Normal file
27
va108xx/src/i2ca/rxfifoirqtrg.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[doc = "Register `RXFIFOIRQTRG` reader"]
|
||||||
|
pub type R = crate::R<RxfifoirqtrgSpec>;
|
||||||
|
#[doc = "Register `RXFIFOIRQTRG` writer"]
|
||||||
|
pub type W = crate::W<RxfifoirqtrgSpec>;
|
||||||
|
impl core::fmt::Debug for R {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
write!(f, "{}", self.bits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl W {}
|
||||||
|
#[doc = "Rx FIFO IRQ Trigger Level\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`rxfifoirqtrg::R`](R). You can [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`write_with_zero`](crate::generic::Reg::write_with_zero) this register using [`rxfifoirqtrg::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
|
||||||
|
pub struct RxfifoirqtrgSpec;
|
||||||
|
impl crate::RegisterSpec for RxfifoirqtrgSpec {
|
||||||
|
type Ux = u32;
|
||||||
|
}
|
||||||
|
#[doc = "`read()` method returns [`rxfifoirqtrg::R`](R) reader structure"]
|
||||||
|
impl crate::Readable for RxfifoirqtrgSpec {}
|
||||||
|
#[doc = "`write(|w| ..)` method takes [`rxfifoirqtrg::W`](W) writer structure"]
|
||||||
|
impl crate::Writable for RxfifoirqtrgSpec {
|
||||||
|
type Safety = crate::Unsafe;
|
||||||
|
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
|
||||||
|
}
|
||||||
|
#[doc = "`reset()` method sets RXFIFOIRQTRG to value 0"]
|
||||||
|
impl crate::Resettable for RxfifoirqtrgSpec {
|
||||||
|
const RESET_VALUE: u32 = 0;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user