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

Reviewed-on: #1
This commit is contained in:
Robin Müller 2024-06-16 16:20:30 +02:00
commit 42c7f0d3a2
253 changed files with 31172 additions and 100 deletions

1
.cargo/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
config.toml

48
.cargo/def-config.toml Normal file
View 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
View 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
View File

@ -6,11 +6,12 @@
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
/app.map
# These are backup files generated by rustfmt
**/*.rs.bk
/.vscode/.cortex-debug.*
/.vscode/settings.json
/.vscode
# JetBrains IDEs
/.idea

15
.gitmodules vendored
View File

@ -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

View File

@ -1,22 +1,31 @@
[workspace]
resolver = "2"
members = [
"vorago-reb1",
"va108xx",
"va108xx-hal",
"max116xx-10bit",
"examples/simple",
"board-tests",
]
exclude = [
"defmt-testapp",
]
[profile.dev]
lto = false
debug = true
codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 'z' # <-
overflow-checks = true # <-
# cargo build/run --release
[profile.release]
# Can be problematic for debugging and is definitely problematic with RTT
lto = false
debug = true
opt-level = 's'
[profile.release-lto]
inherits = "release"
lto = true
codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-

View File

@ -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
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`
cross-compiler toolchain. If you have not installed it yet, you can do so with
Use the following command to have a starting configuration for VS Code:
```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
Micro-USB cable and an
You can debug applications on the REB1 board with a graphical user interface using VS Code with
## Flashing, running and debugging the software
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).
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.
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
configuration variables in your `settings.json`:

@ -1 +0,0 @@
Subproject commit 8b7121eb4771c2537c6123f632d20fbfb2ba3657

13
automation/Dockerfile Normal file
View 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
View 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
View 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
View 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()
}

View 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
View File

@ -0,0 +1 @@
/target

36
defmt-testapp/Cargo.toml Normal file
View 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
View 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
View 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
View 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 {}
}
}

View 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"

View 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);
}
}

View 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()
}

View 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;
}

View 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);
}
}

View 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 {}
}
}

View 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);
}
}

View 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()
}

View 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);
});
}

View 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);
}
}
}
}
}

View 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);
}
}
}
}

View 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
View 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 +0,0 @@
Subproject commit f626e33e72d4863b68da89099cf60ce14de3e114

@ -1 +0,0 @@
Subproject commit e9f12945725cf8814fb224a30ac513074de624cc

View File

@ -1,10 +1,9 @@
[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 = "arm-none-eabi-gdb -q -x jlink.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
# runner = "gdb -q -x openocd.gdb"
runner = "gdb-multiarch -q -x jlink.gdb"
rustflags = [
# 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
# 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
# use GCC as the linker by commenting out both lines above and then
# 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=-nostartfiles",
]

2
va108xx-hal/.github/bors.toml vendored Normal file
View File

@ -0,0 +1,2 @@
status = ["ci"]
delete_merged_branches = true

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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).

View 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
View 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
View 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
View 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
View 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)) });
}

View 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
View 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;

View 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
View 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
View 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
View 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(())
}
}
}

View File

@ -0,0 +1,3 @@
//! Prelude
pub use fugit::ExtU32 as _;
pub use fugit::RateExtU32 as _;

387
va108xx-hal/src/pwm.rs Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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();
}
}
}

View 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

File diff suppressed because it is too large Load Diff

View 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) };
}

View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View 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"
]
}

View 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.

View 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

View 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

View 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

View 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);
}

View 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
View File

@ -0,0 +1,2 @@
status = ["ci"]
delete_merged_branches = true

20
va108xx/.github/workflows/changelog.yml vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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.

View 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
View 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
View 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
View 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
View 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
View 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(&REG::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
}
}

View 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
View 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;

View 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;
}

View 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;
}

View 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
View 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
View 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
View 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;
}

View 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
View 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
View 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;
}

View 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;
}

View 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