Init Commit
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good

Monorepo for Rust support of VA416XX family of radiation hardened MCUs
This commit is contained in:
Robin Müller 2021-12-07 00:31:51 +01:00 committed by Robin Mueller
commit 5d1740efea
606 changed files with 74678 additions and 0 deletions

40
.cargo/def-config.toml Normal file
View File

@ -0,0 +1,40 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "gdb-multiarch -q -x jlink/jlink.gdb"
# runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb"
# Probe-rs is currently problematic, possibly because of the
# ROM protection?
# runner = "probe-rs run --chip-description-path ./scripts/VA416xx_Series.yaml"
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [
"-C",
"link-arg=-Tlink.x",
# "-C",
# "linker=flip-link",
# "-C",
# "link-arg=-Tdefmt.x",
# 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",
# Can be useful for debugging.
# "-Clink-args=-Map=app.map"
]
[build]
# (`thumbv6m-*` is compatible with all ARM Cortex-M chips but using the right
# target improves performance)
# 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)
[alias]
rb = "run --bin"
rrb = "run --release --bin"
[env]
DEFMT_LOG = "info"

52
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,52 @@
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: "thumbv7em-none-eabihf"
- run: cargo check --target thumbv7em-none-eabihf --release
- run: cargo check --target thumbv7em-none-eabihf --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 -p va416xx-hal
# I think we can skip those on an embedded crate..
# - run: cargo test --doc -p va108xx-hal
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: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: "thumbv7em-none-eabihf"
- run: cargo clippy --target thumbv7em-none-eabihf -- -D warnings

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/.vscode
# Ignore user config
/.cargo/config.toml
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
/app.map

0
.gitmodules vendored Normal file
View File

26
Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[workspace]
resolver = "2"
members = [
"examples/simple",
"va416xx",
"va416xx-hal",
"vorago-peb1"
]
[profile.dev]
codegen-units = 1
debug = 2
debug-assertions = true # <-
incremental = false
opt-level = 'z' # <-
overflow-checks = true # <-
# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
lto = 'fat'
opt-level = 3 # <-
overflow-checks = false # <-

201
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
NOTICE Normal file
View File

@ -0,0 +1,3 @@
Workspace to develop Rust for the VA416xx devices
This software contains code developed at the University of Stuttgart.

109
README.md Normal file
View File

@ -0,0 +1,109 @@
[![build](https://github.com/us-irs/va416xx-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/us-irs/va416xx-rs/actions/workflows/ci.yml)
Vorago VA416xx Rust Support
=========
This crate collection provided support to write Rust applications for the VA416XX family
of devices.
## List of crates
This workspace contains the following crates:
- The [`va416xx`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx)
PAC crate containing basic low-level register definition
- The [`va416xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`vorago-peb1`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/vorago-peb1)
BSP crate containing support for the PEB1 development board.
It also contains the following helper crates:
- 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
cp .cargo/def-config.toml .cargo/config.toml
```
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
Use the following command to have a starting configuration for VS Code:
```sh
cp -rT vscode .vscode
```
You can then adapt the files in `.vscode` to your needs.
## 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-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript
```
After this, you can flash and debug the application with the following command
```sh
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv7em-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 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.
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`:
- `"cortex-debug.gdbPath"`
- `"cortex-debug.gdbPath.linux"`
- `"cortex-debug.gdbPath.windows"`
- `"cortex-debug.gdbPath.osx"`
The provided VS Code configurations also provide an integrated RTT logger, which you can access
via the terminal at `RTT Ch:0 console`.

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 thumbv7em-none-eabihf && \
rustup +nightly target add thumbv7em-none-eabihf && \
rustup component add rustfmt clippy

43
automation/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,43 @@
pipeline {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
stages {
stage('Rust Toolchain Info') {
steps {
sh 'rustc --version'
}
}
stage('Clippy') {
steps {
sh 'cargo clippy --target thumbv7em-none-eabihf'
}
}
stage('Rustfmt') {
steps {
sh 'cargo fmt'
}
}
stage('Docs') {
steps {
sh """
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
"""
}
}
stage('Check') {
steps {
sh 'cargo check --target thumbv7em-none-eabihf'
}
}
stage('Check Examples') {
steps {
sh 'cargo check --target thumbv7em-none-eabihf --examples'
}
}
}
}

View File

@ -0,0 +1,18 @@
[package]
name = "simple_examples"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m-rt = "0.7"
va416xx-hal = { path = "../../va416xx-hal" }
panic-rtt-target = { version = "0.1.3" }
rtt-target = { version = "0.5" }
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
embedded-hal = "1"
embedded-hal-nb = "1"
nb = "1"
embedded-io = "0.6"
panic-halt = "0.2"
vorago-peb1 = { path = "../../vorago-peb1" }
accelerometer = "0.12"

View File

@ -0,0 +1,33 @@
//! Simple blinky example
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use panic_halt as _;
use va416xx_hal::pac;
// Mask for the LED
const LED_PG5: u32 = 1 << 5;
#[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.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
dp.portg
.datamask()
.modify(|_, w| unsafe { w.bits(LED_PG5) });
for _ in 0..10 {
dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) });
cortex_m::asm::delay(2_000_000);
dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) });
cortex_m::asm::delay(2_000_000);
}
loop {
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
cortex_m::asm::delay(2_000_000);
}
}

View File

@ -0,0 +1,24 @@
//! Simple blinky example using the HAL
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::{gpio::PinsG, pac};
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("VA416xx HAL blinky example");
let mut dp = pac::Peripherals::take().unwrap();
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
let mut led = portg.pg5.into_readable_push_pull_output();
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
loop {
cortex_m::asm::delay(2_000_000);
led.toggle().ok();
}
}

View File

@ -0,0 +1,88 @@
//! Example code for the PEB1 development board accelerometer.
#![no_main]
#![no_std]
use accelerometer::{Accelerometer, RawAccelerometer};
use cortex_m_rt::entry;
use embedded_hal::delay::DelayNs;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1;
use va416xx_hal::{
i2c,
pac::{self},
prelude::*,
pwm::CountdownTimer,
};
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
pub enum DisplayMode {
Raw,
Normalized,
}
const DISPLAY_MODE: DisplayMode = DisplayMode::Normalized;
#[entry]
fn main() -> ! {
rtt_init_print!();
let mut dp = pac::Peripherals::take().unwrap();
rprintln!("-- Vorago PEB1 accelerometer example --");
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let mut i2c_master = i2c::I2cMaster::new(
dp.i2c0,
&mut dp.sysconfig,
i2c::MasterConfig::default(),
&clocks,
i2c::I2cSpeed::Regular100khz,
)
.expect("creating I2C master failed");
let mut delay_provider = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
// Detect the I2C address of the accelerometer by scanning all possible values.
let slave_addr = detect_i2c_addr(&mut i2c_master).expect("detecting I2C address failed");
// Create the accelerometer driver using the PEB1 BSP.
let mut accelerometer = vorago_peb1::accelerometer::new_with_i2cm(i2c_master, slave_addr)
.expect("creating accelerometer driver failed");
let device_id = accelerometer.get_device_id().unwrap();
accelerometer
.set_mode(lis2dh12::reg::Mode::Normal)
.expect("setting mode failed");
accelerometer
.set_odr(Odr::Hz100)
.expect("setting ODR failed");
accelerometer
.set_fs(FullScale::G4)
.expect("setting full scale failed");
// This function also enabled BDU.
accelerometer
.enable_temp(true)
.expect("enabling temperature sensor failed");
rprintln!("Device ID: 0x{:02X}", device_id);
// Start reading the accelerometer periodically.
loop {
let temperature = accelerometer
.get_temp_outf()
.expect("reading temperature failed");
match DISPLAY_MODE {
DisplayMode::Normalized => {
let value = accelerometer
.accel_norm()
.expect("reading normalized accelerometer data failed");
rprintln!("Accel Norm F32x3: {:.06?} | Temp {} °C", value, temperature);
}
DisplayMode::Raw => {
let value_raw = accelerometer
.accel_raw()
.expect("reading raw accelerometer data failed");
rprintln!("Accel Raw F32x3: {:?} | Temp {} °C", value_raw, temperature);
}
}
delay_provider.delay_ms(100);
}
}

View File

@ -0,0 +1,81 @@
//! 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 simple_examples::peb1;
use va416xx_hal::{
gpio::PinsA,
pac,
prelude::*,
pwm::{self, get_duty_from_percent, CountdownTimer, PwmA, PwmB, ReducedPwmPin},
};
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("-- VA108xx PWM example application--");
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut pwm = pwm::PwmPin::new(
(pinsa.pa3.into_funsel_1(), dp.tim3),
&mut dp.sysconfig,
&clocks,
10.Hz(),
);
let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim0, &clocks);
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_timer.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_timer.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,36 @@
// Code to test RTT logger functionality.
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va416xx_hal::pac;
// Mask for the LED
const LED_PG5: u32 = 1 << 5;
#[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.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
dp.portg
.datamask()
.modify(|_, w| unsafe { w.bits(LED_PG5) });
rtt_init_print!();
rprintln!("VA416xx RTT Demo");
let mut counter = 0;
loop {
rprintln!("{}: Hello, world!", counter);
// Still toggle LED. If there are issues with the RTT log, the LED
// blinking ensures that the application is actually running.
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
counter += 1;
cortex_m::asm::delay(10_000_000);
}
}

View File

@ -0,0 +1,96 @@
//! SPI example application.
//!
//! If you do not use the loopback mode, MOSI and MISO need to be tied together on the board.
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::spi::{Mode, SpiBus, MODE_0};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1;
use va416xx_hal::spi::{Spi, TransferConfig};
use va416xx_hal::{
gpio::{PinsB, PinsC},
pac,
prelude::*,
spi::SpiConfig,
};
#[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. You need to tie together MOSI/MISO in this
// mode.
TestBuffer,
}
const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback;
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 cp = cortex_m::Peripherals::take().unwrap();
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
let pins_b = PinsB::new(&mut dp.sysconfig, dp.portb);
let pins_c = PinsC::new(&mut dp.sysconfig, dp.portc);
// Configure SPI1 pins.
let (sck, miso, mosi) = (
pins_b.pb15.into_funsel_1(),
pins_c.pc0.into_funsel_1(),
pins_c.pc1.into_funsel_1(),
);
let mut spi_cfg = SpiConfig::default();
if EXAMPLE_SEL == ExampleSelect::Loopback {
spi_cfg = spi_cfg.loopback(true)
}
let transfer_cfg =
TransferConfig::new_no_hw_cs(SPI_SPEED_KHZ.kHz(), SPI_MODE, BLOCKMODE, false);
// Create SPI peripheral.
let mut spi0 = Spi::new(
dp.spi0,
(sck, miso, mosi),
&clocks,
spi_cfg,
&mut dp.sysconfig,
Some(&transfer_cfg.downgrade()),
);
spi0.set_fill_word(FILL_WORD);
loop {
let mut tx_buf: [u8; 3] = [1, 2, 3];
let mut rx_buf: [u8; 3] = [0; 3];
// Can't really verify correct reply here.
spi0.write(&[0x42]).expect("write failed");
// Need small delay.. otherwise we will read back the sent byte (which we don't want here).
// The write function will return as soon as all bytes were shifted out, ignoring the
// reply bytes.
delay_sysclk.delay_us(50);
// Because of the loopback mode, we should get back the fill word here.
spi0.read(&mut rx_buf[0..1]).unwrap();
assert_eq!(rx_buf[0], FILL_WORD);
spi0.transfer_in_place(&mut tx_buf)
.expect("SPI transfer_in_place failed");
assert_eq!([1, 2, 3], tx_buf);
spi0.transfer(&mut rx_buf, &tx_buf)
.expect("SPI transfer failed");
assert_eq!(rx_buf, tx_buf);
delay_sysclk.delay_ms(500);
}
}

View File

@ -0,0 +1,68 @@
//! 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 simple_examples::peb1;
use va416xx_hal::{
pac::{self, interrupt},
prelude::*,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, 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 --");
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
second_timer.start(1.Hz());
second_timer.listen();
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);
}
}
#[interrupt]
#[allow(non_snake_case)]
fn TIM0() {
default_ms_irq_handler()
}
#[interrupt]
#[allow(non_snake_case)]
fn TIM1() {
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,57 @@
// UART example application. Sends a test string over a UART and then enters
// echo mode
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal_nb::serial::Read;
use embedded_io::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use simple_examples::peb1;
use va416xx_hal::clock::ClkgenExt;
use va416xx_hal::time::Hertz;
use va416xx_hal::{gpio::PinsG, pac, uart};
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("-- VA416xx UART example application--");
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let gpiob = PinsG::new(&mut dp.sysconfig, dp.portg);
let tx = gpiob.pg0.into_funsel_1();
let rx = gpiob.pg1.into_funsel_1();
let uart0 = uart::Uart::new(
dp.uart0,
(tx, rx),
Hertz::from_raw(115200),
&mut dp.sysconfig,
&clocks,
);
let (mut tx, mut rx) = uart0.split();
writeln!(tx, "Hello World\n\r").unwrap();
loop {
// Echo what is received on the serial link.
match nb::block!(rx.read()) {
Ok(recvd) => {
if let Err(e) = embedded_hal_nb::serial::Write::write(&mut tx, recvd) {
rprintln!("UART TX error: {:?}", e);
}
}
Err(e) => {
rprintln!("UART RX error {:?}", e);
}
}
}
}

View File

@ -0,0 +1,79 @@
// Code to test the watchdog timer.
#![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 simple_examples::peb1;
use va416xx_hal::pac::{self, interrupt};
use va416xx_hal::prelude::*;
use va416xx_hal::wdt::WdtController;
static WDT_INTRPT_COUNT: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
enum TestMode {
// Watchdog is fed by main loop, which runs with high period.
FedByMain,
// Watchdog is fed by watchdog IRQ.
FedByIrq,
AllowReset,
}
const TEST_MODE: TestMode = TestMode::FedByMain;
const WDT_ROLLOVER_MS: u32 = 100;
#[entry]
fn main() -> ! {
rtt_init_print!();
rprintln!("-- VA416xx WDT example application--");
let cp = cortex_m::Peripherals::take().unwrap();
let mut dp = pac::Peripherals::take().unwrap();
// Use the external clock connected to XTAL_N.
let clocks = dp
.clkgen
.constrain()
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
.freeze(&mut dp.sysconfig)
.unwrap();
let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
let mut last_interrupt_counter = 0;
let mut wdt_ctrl =
WdtController::start(&mut dp.sysconfig, dp.watch_dog, &clocks, WDT_ROLLOVER_MS);
wdt_ctrl.enable_reset();
loop {
if TEST_MODE != TestMode::AllowReset {
wdt_ctrl.feed();
}
let interrupt_counter = cortex_m::interrupt::free(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
if interrupt_counter > last_interrupt_counter {
rprintln!("interrupt counter has increased to {}", interrupt_counter);
last_interrupt_counter = interrupt_counter;
}
match TEST_MODE {
TestMode::FedByMain => delay_sysclk.delay_ms(WDT_ROLLOVER_MS / 5),
TestMode::FedByIrq => delay_sysclk.delay_ms(WDT_ROLLOVER_MS),
_ => (),
}
}
}
#[interrupt]
#[allow(non_snake_case)]
fn WATCHDOG() {
cortex_m::interrupt::free(|cs| {
WDT_INTRPT_COUNT
.borrow(cs)
.set(WDT_INTRPT_COUNT.borrow(cs).get() + 1);
});
let wdt = unsafe { pac::WatchDog::steal() };
// Clear interrupt.
if TEST_MODE != TestMode::AllowReset {
wdt.wdogintclr().write(|w| unsafe { w.bits(1) });
}
}

View File

@ -0,0 +1,11 @@
#![no_std]
/// PEB1 board specific configuration.
pub mod peb1 {
use va416xx_hal::time::Hertz;
// The clock on the PEB1 board has a 20 MHz clock which is increased to 40 MHz with a configurable
// PLL by default.
pub const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000);
pub const XTAL_FREQ: Hertz = Hertz::from_raw(10_000_000);
}

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

5
jlink-gdb.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
# Start the JLinkGDBServer while also specifying the JLinkScript file. The JLinkScript is necessary
# to disable ROM protection to allow flashing
JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript

View File

@ -0,0 +1,62 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
----------------------------------------------------------------------
File : JLinkSettings.JLinkScript
Purpose : J-Link target setup file for VORAGO VA416xx
---------------------------END-OF-HEADER------------------------------
*/
/*********************************************************************
*
* AfterResetTarget
*/
int AfterResetTarget (void) {
JLINK_SYS_Report("AfterResetTarget()");
JLINK_MEM_WriteU32(0x400210C0, 0x1ACCE551); // WDOGLOCK = 0x1ACCE551
JLINK_MEM_WriteU32(0x40021008, 0x0); // WDOGCONTROL = 0x0 (diable)
return JLINK_MEM_WriteU32(0x40010010, 0x1); // ROM_PROT = 0x1
}
/*********************************************************************
*
* BeforeTargetDownload
*/
int BeforeTargetDownload (void) {
JLINK_SYS_Report("BeforeTargetDownload()");
return JLINK_MEM_WriteU32(0x40010010, 0x1); // ROM_PROT = 0x1
}
/*********************************************************************
*
* AfterTargetDownload
*/
int AfterTargetDownload (void) {
JLINK_SYS_Report("AfterTargetDownload()");
return JLINK_MEM_WriteU32(0x40010010, 0x0); // ROM_PROT = 0x0
}
/*********************************************************************
*
* HandleBeforeFlashProg
*/
int HandleBeforeFlashProg(void) {
JLINK_SYS_Report("HandleBeforeFlashProg()");
return JLINK_MEM_WriteU32(0x40010010, 0x1); // ROM_PROT = 0x1
}
/*********************************************************************
*
* HandleAfterFlashProg
*/
int HandleAfterFlashProg(void) {
JLINK_SYS_Report("HandleAfterFlashProg()");
return JLINK_MEM_WriteU32(0x40010010, 0x0); // ROM_PROT = 0x0
}

13
jlink/jlink-reva.gdb Normal file
View File

@ -0,0 +1,13 @@
target remote localhost:2331
# For some reason, this is problematic even if the JLinkScript disabled the remote
# write protection. Therefore, don't do it for now
# This is only problematic on board RevA
# monitor reset
# *try* to stop at the user entry point (it might be gone due to inlining)
break main
load
continue

10
jlink/jlink.gdb Normal file
View File

@ -0,0 +1,10 @@
target remote localhost:2331
monitor halt
# Reset is problematic on RevA, okay for RevB
monitor reset
# *try* to stop at the user entry point (it might be gone due to inlining)
break main
load

23
memory.x Normal file
View File

@ -0,0 +1,23 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 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 */
/* SRAM_0 can be used for all busses: Instruction, Data and System */
/* SRAM_1 only supports the system bus */
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
/* Define sections for placing symbols into the extra memory regions above. */
/* This makes them accessible from code. */
SECTIONS {
.sram1 (NOLOAD) : ALIGN(8) {
*(.sram1 .sram1.*);
. = ALIGN(4);
} > SRAM_1
};

100
scripts/VA416xx_Series.yaml Normal file
View File

@ -0,0 +1,100 @@
name: VA416xx Series
generated_from_pack: true
pack_file_release: 1.0.5
variants:
- name: VA416xx
cores:
- name: main
type: armv7em
core_access_options: !Arm
ap: 0
psel: 0x0
memory_map:
- !Ram
name: IRAM1
range:
start: 0x1fff8000
end: 0x20000000
cores:
- main
- !Nvm
name: IROM1
range:
start: 0x0
end: 0x40000
is_boot_memory: true
cores:
- main
- !Generic
name: IRAM2
range:
start: 0x20000000
end: 0x20008000
cores:
- main
flash_algorithms:
- va416_spi_fram_256kb
- va416_ebiboot_fram_256kb
- va416_ebi_fram_512kb
flash_algorithms:
- name: va416_spi_fram_256kb
default: true
description: VA416_SPI_FRAM_256KB
instructions: RfYMQMTyAQABaMkHCtEBaMkHBL8BaF/qwXED0QFoyQfy0AC/AWjJBgnVAWjJBkS/AWhf6sFhAtUBaMkG8tQDIQFicEdwRwC/WCAIIcTyAQDA8qgBQWBB8ggBTvJRUsTyAgHB9swiwfi4IAAiCmABaCHwCAEBYAC/AL8CaEX2DEFC8AgCxPIBAQJgAiAAv0hgQPIHQEH4DAxA8oIgQfgIDAMgCGIIaMAHCtEIaMAHBL8IaF/qwHAD0QhowAfy0AC/CGjABgnVCGjABkS/CGhf6sBgAtUIaMAG8tQGIMjyAAADIgpiQfgEDApo0gcK0Qpo0gcEvwpoX+rCcgPRCmjSB/LQAL8KaNIGCdUKaNIGRL8KaF/qwmIC1Qpo0gby1AMiCmJB+AQMCGjABwnRCGjABwS/CGhf6sBwAtEIaMAH8tAIaMAGCdUIaMAGRL8IaF/qwGAC1QhowAby1AMgCGIBIEH4BAxP8ABAQfgEDAhowAcK0QhowAcEvwhoX+rAcAPRCGjAB/LQAL8IaMAGCdUIaMAGRL8IaF/qwGAC1QhowAby1AMiACAKYnBHAL9F9gxBxPIBAQhowAcK0QhowAcEvwhoX+rAcAPRCGjAB/LQAL8IaMAGCdUIaMAGRL8IaF/qwGAC1QhowAby1AYgyPIAAAMiCmJB+AQMCmjSBwrRCmjSBwS/Cmhf6sJyA9EKaNIH8tAAvwpo0gYJ1Qpo0gZEvwpoX+rCYgLVCmjSBvLUAyIKYgEi9zBB+AQsQfgEDAhowAcJ0QhowAcEvwhoX+rAcALRCGjAB/LQCGjABgnVCGjABkS/CGhf6sBgAtUIaMAG8tQDIgAgCmJwRwC/cLVF9gxBQPIGDAAgxPIBAcjyAAwDI0/wAg5P8ABCACQG4AC/ATS09YBvC2IA8KyAQfgEzA1o7QcK0Q1o7QcEvw1oX+rFdQPRDWjtB/LQAL8NaO0GCtUNaO0GRL8NaF/qxWUD1Q1o7Qby1AC/xPMHJQtiQfgE7EH4BFzlskH4BFwAJUH4BFwE4AU1/y1B+AQMTNAOaLYHBtQOaLYHXL8OaF/qhnY+1UH4BAwOaLYHCdQOaLYHXL8OaF/qhnYC1A5otgfy1UH4BAwOaLYHCtQOaLYHXL8OaF/qhnYD1A5otgfy1QC/QfgEDA5otgcK1A5otgdcvw5oX+qGdgPUDmi2B/LVAL9B+AQMDmi2B7zUDmi2B1y/Dmhf6oZ2tdQOaLYH8tWx5w5otgez1bznDWitBwrUDWitB1y/DWhf6oV1A9QNaK0H8tUAv0H4BCwNaO0HCtENaO0HBL8NaF/qxXUD0Q1o7Qfy0AC/DWjtBn/1Wq8NaO0GRL8NaF/qxWV/9VKvDWjtBvDUTecAIHC9ACBwR4C1grBF9gxOxPIBDgAjAZPe+AAw2wcN0d74ADDbBwS/3vgAMF/qw3ME0d74ADDbB+7QAL/e+AAw2wYM1d74ADDbBkS/3vgAMF/qw2MD1d74ADDbBu7UAyPO+CAwBiPI8gADTvgEPN74ADDbBwzR3vgAMNsHBL/e+AAwX+rDcwPR3vgAMNsH7tDe+AAw2wYM1d74ADDbBkS/3vgAMF/qw2MD1d74ADDbBu7UAyPO+CAwAiNN9vAMTvgEPMDzB0PP9v98TvgEPMDzByPAsgEpTvgEPE74BAxA8JKA3vgAAIAHAPEGgd74AACAB1y/3vgAAF/qgHAA8fyA3vgAAIAHXL/e+AAAX+qAcADx8oDe+AAAgAdcv974AABf6oBwAPHogN74AACAB1y/3vgAAF/qgHAA8d6A3vgAAIAHXL/e+AAAX+qAcADx1IDe+AAAgAdcv974AABf6oBwAPHKgN74AACAB1y/3vgAAF/qgHAA8cCA3vgAAIAHXL/e+AAAX+qAcADxtoDe+AAAgAdcv974AABf6oBwAPGsgN74AACAB1y/3vgAAF/qgHAA8aKA3vgAAIAHXL/e+AAAX+qAcADxmIDe+AAAgAdcv974AABf6oBwAPGOgBzxGQx/9H+vheAAvxL4AQsBOU74BAxe+AQMASkAkAGYAPEBAAGQP/Rur2BG3vgAMJsH69Te+AAwmwdcv974ADBf6oNz4tTe+AAwmwdcv974ADBf6oNz2dTe+AAwmwdcv974ADBf6oNz0NTe+AAwmwdcv974ADBf6oNzx9Te+AAwmwdcv974ADBf6oNzvtTe+AAwmwdcv974ADBf6oNztdTe+AAwmwdcv974ADBf6oNzrNTe+AAwmwdcv974ADBf6oNzo9Te+AAwmwdcv974ADBf6oNzmtTe+AAwmwdcv974ADBf6oNzkdTe+AAwmwdcv974ADBf6oNzP/WIr974ADCbB1y/3vgAMF/qg3M/9X6vGTB/9IuvASACsIC9EHgA8QBATvgEDN74AADABwzR3vgAAMAHBL/e+AAAX+rAcAPR3vgAAMAH7tDe+AAAwAYM1d74AADABkS/3vgAAF/qwGAD1d74AADABu7UAyDO+CAAACACsIC9AL8AIHBHsLVF9gxExPIBBCNo2wcJ0SNo2wcEvyNoX+rDcwLRI2jbB/LQI2jbBgnVI2jbBkS/I2hf6sNjAtUjaNsG8tQDIyNiRPgEPMDzB0NE+AQ8wPMHI0T4BDzDskT4BDwAI0T4BDwjaFsHCdQjaFsHXL8jaF/qQ3MC1CNoWwfy1VT4BDwAI0T4BDwjaFsHCdQjaFsHXL8jaF/qQ3MC1CNoWwfy1VT4BDwAI0T4BDwjaFsHCdQjaFsHXL8jaF/qQ3MC1CNoWwfy1VT4BDwAI0T4BDwjaFsHCdQjaFsHXL8jaF/qQ3MC1CNoWwfy1VT4BDwBs0/wAAxP8AAOAL9E+ATMI2hbBwrUI2hbB1y/I2hf6kNzA9QjaFsH8tUAv1T4BDwVeNuynUIo0Q7xAQ6ORQLxAQLj0U/wAEJE+AQsImjSBwrRImjSBwS/Imhf6sJyA9EiaNIH8tAAvyJo0gYJ1SJo0gZEvyJoX+rCYgLVImjSBvLUAyIIRCJisL1wRLC9AAAAAAAA
pc_init: 0x49
pc_uninit: 0x1ad
pc_program_page: 0x40d
pc_erase_sector: 0x409
pc_erase_all: 0x27d
data_section_offset: 0x8d8
flash_properties:
address_range:
start: 0x0
end: 0x40000
page_size: 0x100
erased_byte_value: 0x0
program_page_timeout: 3000
erase_sector_timeout: 3000
sectors:
- size: 0x2000
address: 0x0
- name: va416_ebiboot_fram_256kb
description: VA416_EBIBOOT_256KB
instructions: QfKAAsTyAQJS+CAwI/TAQ0PqQTFC+CAQcEcAv0HywALE8gECUvggMCP0wEND6kExQvggEHBHAL9B8gASxPIBAlL4IDAj9MBDQ+pBMUL4IBBwRwC/QfJAEsTyAQJS+CAwI/TAQ0PqQTFC+CAQcEcAv0HyiADE8gEAAmgBIWHzTjICYEJoYfNOMkJggmhh804ygmDCaGHzTjLCYAJpYfNOMgJhQmlh804yQmGCaWHzTjKCYcJpYfNOMsJhAmph804yAmJCamHzTjJCYoJqYfNOMoJiwmph804ywmICa2HzTjICY0JrYfNOMkJjgmth804ygmPCa2HzTjLCYwJsYfNOMgJkQmxh804yQmSCbGHzTjKCZMJsYfNOMsJkAm1h804yAmVCbWHzTjJCZYJtYfNOMoJlwm1h804ywmUCbmHzTjICZkJuYfNOMkJmgm5h804ygmbCbmHzTjLCZgJvYfNOMgJnQm9h804yQmeCb2HzTjKCZ8JvYfNOMsJn0PiAIGHzTjLA+IAg0PiEIGHzTjLA+IQg0PiIIGHzTjLA+Igg0PiMIGHzTjLA+Iwg0PiQIGHzTjLA+JAg0PiUIGHzTjLA+JQg0PiYIGHzTjLA+Jgg0PicIGHzTjLA+Jwg0PioIGHzTjLA+Kgg0PisIGHzTjLA+Kwg0PiwIGHzTjLA+LAg0Pi0IGHzTjLA+LQg0Pi4IGHzTjLA+Lgg0Pi8IGHzTjLA+LwgcEcQtVgkACDE8gEEw/Y5QGBgIGhA9IAwIGD/9xj/T/YAccDytjEAIGFhEL0AvwAgcEeAtU/wwEBP9IAhAPA7+AAggL0AvwAgcEcB8AEDGURLCCnQWR4A8cBAAykD8AMME9Mj8AMDMvgIGwQ7IPgIGzL4Bhwg+AYcMvgEHCD4BBwy+AIcIPgCHO3RvPEADwvQEYi88QEPAYAG0FGIvPECD0GAHL+RiIGAACBwRwC/ACBwRwhEcEdP8AACALUTRpRGlkYgOSK/oOgMUKDoDFCx8SABv/T3rwkHKL+g6AxQSL8MwF34BOuJACi/QPgEKwi/cEdIvyD4AisR8IBPGL8A+AErcEcAAAAAAAA=
pc_init: 0x225
pc_uninit: 0x251
pc_program_page: 0x26d
pc_erase_sector: 0x269
pc_erase_all: 0x255
data_section_offset: 0x328
flash_properties:
address_range:
start: 0x0
end: 0x40000
page_size: 0x100
erased_byte_value: 0x0
program_page_timeout: 3000
erase_sector_timeout: 3000
sectors:
- size: 0x2000
address: 0x0
- name: va416_ebi_fram_512kb
description: VA416_EBI_512KB
instructions: QfKAAsTyAQJS+CAwI/TAQ0PqQTFC+CAQcEcAv0HywALE8gECUvggMCP0wEND6kExQvggEHBHAL9B8gASxPIBAlL4IDAj9MBDQ+pBMUL4IBBwRwC/QfJAEsTyAQJS+CAwI/TAQ0PqQTFC+CAQcEcAv0HyiADE8gEAAmgBIWHzTjICYEJoYfNOMkJggmhh804ygmDCaGHzTjLCYAJpYfNOMgJhQmlh804yQmGCaWHzTjKCYcJpYfNOMsJhAmph804yAmJCamHzTjJCYoJqYfNOMoJiwmph804ywmICa2HzTjICY0JrYfNOMkJjgmth804ygmPCa2HzTjLCYwJsYfNOMgJkQmxh804yQmSCbGHzTjKCZMJsYfNOMsJkAm1h804yAmVCbWHzTjJCZYJtYfNOMoJlwm1h804ywmUCbmHzTjICZkJuYfNOMkJmgm5h804ygmbCbmHzTjLCZgJvYfNOMgJnQm9h804yQmeCb2HzTjKCZ8JvYfNOMsJn0PiAIGHzTjLA+IAg0PiEIGHzTjLA+IQg0PiIIGHzTjLA+Igg0PiMIGHzTjLA+Iwg0PiQIGHzTjLA+JAg0PiUIGHzTjLA+JQg0PiYIGHzTjLA+Jgg0PicIGHzTjLA+Jwg0PioIGHzTjLA+Kgg0PisIGHzTjLA+Kwg0PiwIGHzTjLA+LAg0Pi0IGHzTjLA+LQg0Pi4IGHzTjLA+Lgg0Pi8IGHzTjLA+LwgcEcQtVgkxPIBBCBoQPSAMCBgACDD9jlAYGD/9xj/T/YAcMDytjBgYU7yhBDO8gAAT/QAUQFgRvIAAb/zT4/E8gABGCC/82+PiGAwIgAgCmAQvQAgcEeAtU/wgFBP9AAhAPB4+AAggL0AvwAgcEcB8AEDGURLCCfQWR4DKQPwAwwT0yPwAwMy+AgbBDsg+AgbMvgGHCD4Bhwy+AQcIPgEHDL4Ahwg+AIc7dG88QAPC9ARiLzxAQ8BgAbQUYi88QIPQYAcv5GIgYAAIHBHAL8AIHBHLenwRQHwAQMZRF/qUQwr0OJGvPEBD5i/T/ABCsrxAA6q8QEIACMw+BNQMvgTYLVCItGYRRjQAutDBQDrQwZ3iGyIp0IT0Q7rAwe8HAzQtIipiIxCDdH5HAbQ8YjsiKFCCdEEM5pF3tFgRL3o8IUBMwLgAjMA4AMzT+pDDGBEvejwhU/wAAIAtRNGlEaWRiA5Ir+g6AxQoOgMULHxIAG/9PevCQcov6DoDFBIvwzAXfgE64kAKL9A+AQrCL9wR0i/IPgCKxHwgE8YvwD4AStwRwAAAAA=
pc_init: 0x225
pc_uninit: 0x275
pc_program_page: 0x291
pc_erase_sector: 0x28d
pc_erase_all: 0x279
data_section_offset: 0x3c4
flash_properties:
address_range:
start: 0x10000000
end: 0x10080000
page_size: 0x100
erased_byte_value: 0x0
program_page_timeout: 3000
erase_sector_timeout: 3000
sectors:
- size: 0x2000
address: 0x0

8
scripts/prep-flash.gdb Normal file
View File

@ -0,0 +1,8 @@
target remote localhost:2331
echo Disabling watchdog\n
set *0x400210C0 = 0x1ACCE551
set *0x40021008 = 0x0
echo Disabling Instruction Memory protection\n
set *0x40010010 = 0x1

3
scripts/prep-flash.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
# Alternative way to unlock the ROM protection of the VA416XX to allow flashing
gdb-multiarch -q --batch -ex 'source prep-flash.gdb'

View File

@ -0,0 +1,38 @@
[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
# If the RevA board is used, replace jlink.gdb with jlink-reva.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
# 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",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these 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)

View File

@ -0,0 +1,39 @@
[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
# If the RevA board is used, replace jlink.gdb with jlink-reva.gdb
# runner = "arm-none-eabi-gdb -q -x jlink/jlink.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink.gdb"
# runner = "arm-none-eabi-gdb -q -x jlink/jlink-reva.gdb"
# runner = "gdb-multiarch -q -x jlink/jlink-reva.gdb"
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",
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
# target = "thumbv8m.base-none-eabi" # Cortex-M23
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)

2
va416xx-hal/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

43
va416xx-hal/Cargo.toml Normal file
View File

@ -0,0 +1,43 @@
[package]
name = "va416xx-hal"
version = "0.1.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA416xx family of MCUs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx-hal"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx-hal"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
nb = "1"
paste = "1"
embedded-hal-nb = "1"
embedded-hal = "1"
embedded-io = "0.6"
typenum = "1"
defmt = { version = "0.3", optional = true }
fugit = "0.3"
delegate = "0.12"
[dependencies.void]
version = "1"
default-features = false
[dependencies.va416xx]
path = "../va416xx"
default-features = false
version = "0.1.0"
features = ["critical-section"]
[features]
default = ["rt", "revb"]
rt = ["va416xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"]
revb = []
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--generate-link-to-definition"]

201
va416xx-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
va416xx-hal/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Rust Hardware Abstraction Layer (HAL) crate for the Vorago VA416xx family of MCUs
This software contains code developed at the University of Stuttgart.

67
va416xx-hal/README.md Normal file
View File

@ -0,0 +1,67 @@
# HAL for the Vorago VA416xx 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/va416xx-rs/src/branch/main/va416xx).
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.
## Supported Boards
The first way to use this HAL will probably be with the
[PEB1 development board](https://www.voragotech.com/products/peb1va416x0-development-kit).
The BSP provided for this board also contains instructions how to flash the board.
## Building
Building an application requires the `thumbv7em-none-eabihf` cross-compiler toolchain.
If you have not installed it yet, you can do so with
```sh
rustup target add thumbv7em-none-eabihf
```
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/)
## 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/va416xx-rs/src/branch/main/examples/simple/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.va416xx-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 in primary README.

5
va416xx-hal/jlink-gdb.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
# Start the JLinkGDBServer while also specifying the JLinkScript file. The JLinkScript is necessary
# to disable ROM protection to allow flashing
JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript

View File

@ -0,0 +1,77 @@
/*********************************************************************
* SEGGER Microcontroller GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
----------------------------------------------------------------------
File : JLinkSettings.JLinkScript
Purpose : J-Link target setup file for VORAGO VA416xx
---------------------------END-OF-HEADER------------------------------
*/
int DisableRomProt(void) {
JLINK_SYS_Report("VA416XX: Disabling ROM protection");
return JLINK_MEM_WriteU32(0x40010010, 0x1); // ROM_PROT = 0x1
}
int DisableWatchdog(void) {
JLINK_MEM_WriteU32(0x400210C0, 0x1ACCE551); // WDOGLOCK = 0x1ACCE551
JLINK_MEM_WriteU32(0x40021008, 0x0); // WDOGCONTROL = 0x0 (diable)
}
int SetupTarget (void) {
JLINK_SYS_Report("SetupTarget()");
return DisableRomProt(); // ROM_PROT = 0x1
}
/*********************************************************************
*
* AfterResetTarget
*/
int AfterResetTarget (void) {
JLINK_SYS_Report("AfterResetTarget()");
// disable watchdog and unlock code RAM for write
DisableWatchdog();
return DisableRomProt(); // ROM_PROT = 0x1
}
/*********************************************************************
*
* BeforeTargetDownload
*/
int BeforeTargetDownload (void) {
JLINK_SYS_Report("BeforeTargetDownload()");
return DisableRomProt(); // ROM_PROT = 0x1
}
/*********************************************************************
*
* AfterTargetDownload
*/
int AfterTargetDownload (void) {
JLINK_SYS_Report("AfterTargetDownload()");
return DisableRomProt(); // ROM_PROT = 0x0
}
/*********************************************************************
*
* HandleBeforeFlashProg
*/
int HandleBeforeFlashProg(void) {
JLINK_SYS_Report("HandleBeforeFlashProg()");
return DisableRomProt(); // ROM_PROT = 0x1
}
/*********************************************************************
*
* HandleAfterFlashProg
*/
int HandleAfterFlashProg(void) {
JLINK_SYS_Report("HandleAfterFlashProg()");
return DisableRomProt(); // ROM_PROT = 0x0
}

View File

@ -0,0 +1,13 @@
target remote localhost:2331
# For some reason, this is problematic even if the JLinkScript disabled the remote
# write protection. Therefore, don't do it for now
# This is only problematic on board RevA
# monitor reset
# *try* to stop at the user entry point (it might be gone due to inlining)
break main
load
continue

View File

@ -0,0 +1,11 @@
target remote localhost:2331
# Reset is problematic on RevA, okay for RevB
monitor reset
# *try* to stop at the user entry point (it might be gone due to inlining)
break main
load
continue

14
va416xx-hal/memory.x Normal file
View File

@ -0,0 +1,14 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
/* RAM is a mandatory region. This RAM refers to the SRAM_0 */
RAM : ORIGIN = 0x1FFF8000, LENGTH = 32K
SRAM_1 : ORIGIN = 0x20000000, LENGTH = 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 */
/* SRAM_0 can be used for all busses: Instruction, Data and System */
/* SRAM_1 only supports the system bus */
_stack_start = ORIGIN(RAM) + LENGTH(RAM) - 4;

537
va416xx-hal/src/clock.rs Normal file
View File

@ -0,0 +1,537 @@
//! API for using the [crate::pac::Clkgen] peripheral.
//!
//! It also includes functionality to enable the peripheral clocks.
//! Calling [ClkgenExt::constrain] on the [crate::pac::Clkgen] peripheral generates the
//! [ClkgenCfgr] structure which can be used to configure and set up the clock.
//!
//! Calling [ClkgenCfgr::freeze] returns the frozen clock configuration inside the [Clocks]
//! structure. This structure can also be used to configure other structures provided by this HAL.
//!
//! # Examples
//!
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
use crate::pac;
use crate::time::Hertz;
pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000);
pub const XTAL_OSC_TSTART_MS: u32 = 15;
#[derive(Copy, Clone, PartialEq)]
pub enum PeripheralSelect {
Spi0 = 0,
Spi1 = 1,
Spi2 = 2,
Spi3 = 3,
Uart0 = 4,
Uart1 = 5,
Uart2 = 6,
I2c0 = 7,
I2c1 = 8,
I2c2 = 9,
Can0 = 10,
Can1 = 11,
Rng = 12,
Adc = 13,
Dac = 14,
Dma = 15,
Ebi = 16,
Eth = 17,
Spw = 18,
Clkgen = 19,
IrqRouter = 20,
IoConfig = 21,
Utility = 22,
Watchdog = 23,
PortA = 24,
PortB = 25,
PortC = 26,
PortD = 27,
PortE = 28,
PortF = 29,
PortG = 30,
}
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,
}
#[inline(always)]
pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[inline(always)]
pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
}
#[inline(always)]
pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) });
}
#[inline(always)]
pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
}
pub trait SyscfgExt {
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks);
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks);
fn assert_periph_reset(&mut self, clock: PeripheralSelect);
fn deassert_periph_reset(&mut self, clock: PeripheralSelect);
}
impl SyscfgExt for pac::Sysconfig {
#[inline(always)]
fn enable_peripheral_clock(&mut self, clock: PeripheralClocks) {
enable_peripheral_clock(self, clock)
}
#[inline(always)]
fn disable_peripheral_clock(&mut self, clock: PeripheralClocks) {
disable_peripheral_clock(self, clock)
}
#[inline(always)]
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
assert_periph_reset(self, clock)
}
#[inline(always)]
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
deassert_periph_reset(self, clock)
}
}
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClkselSys {
// Internal Heart-Beat Osciallator. Not tightly controlled (+/-20 %). Not recommended as the regular clock!
Hbo = 0b00,
// External clock signal on XTAL_N line, 1-100 MHz
XtalN = 0b01,
// Internal Phase-Locked Loop.
Pll = 0b10,
// Crystal oscillator amplified, 4-10 MHz.
XtalOsc = 0b11,
}
/// This selects the input clock to the the CLKGEN peripheral in addition to the HBO clock.
///
/// This can either be a clock connected directly on the XTAL_N line or a chrystal on the XTAL_P
/// line which goes through an oscillator amplifier.
///
/// Refer to chapter 8 (p.57) of the programmers guide for detailed information.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RefClkSel {
#[default]
None = 0b00,
XtalOsc = 0b01,
XtalN = 0b10,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClkDivSel {
#[default]
Div1 = 0b00,
Div2 = 0b01,
Div4 = 0b10,
Div8 = 0b11,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcClkDivSel {
Div8 = 0b00,
Div4 = 0b01,
Div2 = 0b10,
Div1 = 0b11,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PllCfg {
/// Reference clock divider.
pub clkr: u8,
/// Clock divider on feedback path
pub clkf: u8,
// Output clock divider.
pub clkod: u8,
/// Bandwidth adjustment
pub bwadj: u8,
}
pub fn clk_after_div(clk: Hertz, div_sel: ClkDivSel) -> Hertz {
match div_sel {
ClkDivSel::Div1 => clk,
ClkDivSel::Div2 => clk / 2,
ClkDivSel::Div4 => clk / 4,
ClkDivSel::Div8 => clk / 8,
}
}
/// Wait for 500 reference clock cycles like specified in the datasheet.
pub fn pll_setup_delay() {
for _ in 0..500 {
cortex_m::asm::nop()
}
}
pub trait ClkgenExt {
fn constrain(self) -> ClkgenCfgr;
}
impl ClkgenExt for pac::Clkgen {
fn constrain(self) -> ClkgenCfgr {
ClkgenCfgr {
source_clk: None,
ref_clk_sel: RefClkSel::None,
clksel_sys: ClkselSys::Hbo,
clk_div_sel: ClkDivSel::Div1,
clk_lost_detection: false,
pll_lock_lost_detection: false,
pll_cfg: None,
clkgen: self,
}
}
}
pub struct ClkgenCfgr {
ref_clk_sel: RefClkSel,
clksel_sys: ClkselSys,
clk_div_sel: ClkDivSel,
/// The source clock frequency which is either an external clock connected to XTAL_N, or a
/// crystal connected to the XTAL_OSC input.
source_clk: Option<Hertz>,
pll_cfg: Option<PllCfg>,
clk_lost_detection: bool,
/// Feature only works on revision B of the board.
#[cfg(feature = "revb")]
pll_lock_lost_detection: bool,
clkgen: pac::Clkgen,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClkSourceFreqNotSet;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ClkCfgError {
ClkSourceFreqNotSet,
PllConfigNotSet,
PllInitError,
InconsistentCfg,
}
/// Delays a given amount of milliseconds.
///
/// Taken from the HAL implementation. This implementation is probably not precise and it
/// also blocks!
pub fn hbo_clock_delay_ms(ms: u32) {
let wdt = unsafe { pac::WatchDog::steal() };
for _ in 0..ms {
for _ in 0..10_000 {
cortex_m::asm::nop();
}
wdt.wdogintclr().write(|w| unsafe { w.bits(1) });
}
}
impl ClkgenCfgr {
#[inline]
pub fn source_clk(mut self, src_clk: Hertz) -> Self {
self.source_clk = Some(src_clk);
self
}
/// This function can be used to utilize the XTAL_N clock input directly without the
/// oscillator.
///
/// It sets the internal configuration to [ClkselSys::XtalN] and [RefClkSel::XtalN].
#[inline]
pub fn xtal_n_clk(mut self) -> Self {
self.clksel_sys = ClkselSys::XtalN;
self.ref_clk_sel = RefClkSel::XtalN;
self
}
#[inline]
pub fn xtal_n_clk_with_src_freq(mut self, src_clk: Hertz) -> Self {
self = self.xtal_n_clk();
self.source_clk(src_clk)
}
#[inline]
pub fn clksel_sys(mut self, clksel_sys: ClkselSys) -> Self {
self.clksel_sys = clksel_sys;
self
}
#[inline]
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
self.ref_clk_sel = ref_clk_sel;
self
}
/// Configures all clocks and return a clock configuration structure containing the final
/// frozen clock.
///
/// Internal implementation details: This implementation is based on the HAL implementation
/// which performs a lot of delays. I do not know if all of those are necessary, but
/// I am going to be conservative here and assume that the vendor has tested though and
/// might have had a reason for those, so I am going to keep them. Chances are, this
/// process only has to be performed once, and it does not matter if it takes a few
/// microseconds or milliseconds longer.
pub fn freeze(self, syscfg: &mut pac::Sysconfig) -> Result<Clocks, ClkCfgError> {
// Sanitize configuration.
if self.source_clk.is_none() {
return Err(ClkCfgError::ClkSourceFreqNotSet);
}
if self.clksel_sys == ClkselSys::XtalOsc && self.ref_clk_sel != RefClkSel::XtalOsc {
return Err(ClkCfgError::InconsistentCfg);
}
if self.clksel_sys == ClkselSys::XtalN && self.ref_clk_sel != RefClkSel::XtalN {
return Err(ClkCfgError::InconsistentCfg);
}
if self.clksel_sys == ClkselSys::Pll && self.pll_cfg.is_none() {
return Err(ClkCfgError::PllConfigNotSet);
}
syscfg.enable_peripheral_clock(PeripheralSelect::Clkgen);
let mut final_sysclk = self.source_clk.unwrap();
// The HAL forces back the HBO clock here with a delay.. Even though this is
// not stricly necessary when coming from a fresh start, it could be still become relevant
// later if the clock lost detection mechanism require a re-configuration of the clocks.
// Therefore, we do it here as well.
self.clkgen
.ctrl0()
.modify(|_, w| unsafe { w.clksel_sys().bits(ClkselSys::Hbo as u8) });
pll_setup_delay();
self.clkgen
.ctrl0()
.modify(|_, w| unsafe { w.clk_div_sel().bits(ClkDivSel::Div1 as u8) });
// Set up oscillator and PLL input clock.
self.clkgen
.ctrl0()
.modify(|_, w| unsafe { w.ref_clk_sel().bits(self.ref_clk_sel as u8) });
self.clkgen.ctrl1().modify(|_, w| {
w.xtal_en().clear_bit();
w.xtal_n_en().clear_bit();
w
});
match self.ref_clk_sel {
RefClkSel::None => pll_setup_delay(),
RefClkSel::XtalOsc => {
self.clkgen.ctrl1().modify(|_, w| w.xtal_en().set_bit());
hbo_clock_delay_ms(XTAL_OSC_TSTART_MS);
}
RefClkSel::XtalN => {
self.clkgen.ctrl1().modify(|_, w| w.xtal_n_en().set_bit());
pll_setup_delay()
}
}
// Set up PLL configuration.
match self.pll_cfg {
Some(cfg) => {
self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().clear_bit());
// Done in C HAL. I guess this gives the PLL some time to power down properly.
cortex_m::asm::nop();
cortex_m::asm::nop();
self.clkgen.ctrl0().modify(|_, w| {
unsafe {
w.pll_clkf().bits(cfg.clkf);
}
unsafe {
w.pll_clkr().bits(cfg.clkr);
}
unsafe {
w.pll_clkod().bits(cfg.clkod);
}
unsafe {
w.pll_bwadj().bits(cfg.bwadj);
}
w.pll_test().clear_bit();
w.pll_bypass().clear_bit();
w.pll_intfb().set_bit()
});
// Taken from SystemCoreClockUpdate implementation from Vorago.
final_sysclk /= cfg.clkr as u32 + 1;
final_sysclk *= cfg.clkf as u32 + 1;
final_sysclk /= cfg.clkod as u32 + 1;
// Reset PLL.
self.clkgen.ctrl0().modify(|_, w| w.pll_reset().set_bit());
// The HAL does this, the datasheet specifies a delay of 5 us. I guess it does not
// really matter because the PLL lock detect is used later..
pll_setup_delay();
self.clkgen.ctrl0().modify(|_, w| w.pll_reset().clear_bit());
pll_setup_delay();
// check for lock
let stat = self.clkgen.stat().read();
if stat.fbslip().bit() || stat.rfslip().bit() {
pll_setup_delay();
if stat.fbslip().bit() || stat.rfslip().bit() {
// This is what the HAL does. We could continue, but then we would at least
// have to somehow report a partial error.. Chances are, the user does not
// want to continue with a broken PLL clock.
return Err(ClkCfgError::PllInitError);
}
}
}
None => self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().set_bit()),
}
if self.clk_lost_detection {
rearm_sysclk_lost_with_periph(&self.clkgen)
}
#[cfg(feature = "revb")]
if self.pll_lock_lost_detection {
rearm_pll_lock_lost_with_periph(&self.clkgen)
}
self.clkgen
.ctrl0()
.modify(|_, w| unsafe { w.clk_div_sel().bits(self.clk_div_sel as u8) });
final_sysclk = clk_after_div(final_sysclk, self.clk_div_sel);
// The HAL does this. I don't know why..
pll_setup_delay();
self.clkgen
.ctrl0()
.modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) });
// I will just do the ADC stuff like Vorago does it.
// ADC clock (must be 2-12.5 MHz)
// NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue
// For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range)
if final_sysclk.raw() <= 50_000_000 {
self.clkgen
.ctrl1()
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div4 as u8) });
} else {
self.clkgen
.ctrl1()
.modify(|_, w| unsafe { w.adc_clk_div_sel().bits(AdcClkDivSel::Div8 as u8) });
}
Ok(Clocks {
sysclk: final_sysclk,
apb1: final_sysclk / 2,
apb2: final_sysclk / 4,
})
}
}
/// Frozen clock frequencies
///
/// The existence of this value indicates that the clock configuration can no longer be changed.
/// The [self] module documentation gives some more information on how to retrieve an instance
/// of this structure.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
sysclk: Hertz,
apb1: Hertz,
apb2: Hertz,
}
impl Clocks {
/// Returns the frequency of the HBO clock
pub fn hbo(&self) -> Hertz {
HBO_FREQ
}
/// Returns the frequency of the APB0 which is equal to the system clock.
pub fn apb0(&self) -> Hertz {
self.sysclk()
}
/// Returns system clock divied by 2.
pub fn apb1(&self) -> Hertz {
self.apb1
}
/// Returns system clock divied by 4.
pub fn apb2(&self) -> Hertz {
self.apb2
}
/// Returns the system (core) frequency
pub fn sysclk(&self) -> Hertz {
self.sysclk
}
}
pub fn rearm_sysclk_lost() {
rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() })
}
fn rearm_sysclk_lost_with_periph(clkgen: &pac::Clkgen) {
clkgen
.ctrl0()
.modify(|_, w| w.sys_clk_lost_det_en().set_bit());
clkgen
.ctrl1()
.write(|w| w.sys_clk_lost_det_rearm().set_bit());
clkgen
.ctrl1()
.write(|w| w.sys_clk_lost_det_rearm().clear_bit());
}
#[cfg(feature = "revb")]
pub fn rearm_pll_lock_lost() {
rearm_pll_lock_lost_with_periph(&unsafe { pac::Clkgen::steal() })
}
fn rearm_pll_lock_lost_with_periph(clkgen: &pac::Clkgen) {
clkgen
.ctrl1()
.modify(|_, w| w.pll_lost_lock_det_en().set_bit());
clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().set_bit());
clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().clear_bit());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_div() {
assert_eq!(
clk_after_div(Hertz::from_raw(10_000_000), super::ClkDivSel::Div2),
Hertz::from_raw(5_000_000)
);
}
}

View File

@ -0,0 +1,453 @@
use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin};
use super::{
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
PinMode, PinState,
};
//==================================================================================================
// 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 = crate::FunSel;
//==================================================================================================
// 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,
C,
D,
E,
F,
G,
}
/// 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 }
}
}
//==============================================================================
// 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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPinTypeError(pub(crate) ());
impl embedded_hal::digital::Error for InvalidPinTypeError {
fn kind(&self) -> embedded_hal::digital::ErrorKind {
ErrorKind::Other
}
}
//==================================================================================================
// 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) -> Result<Self, InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_edge(edge_type);
self.irq_enb();
Ok(self)
}
_ => Err(InvalidPinTypeError(())),
}
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
) -> Result<Self, InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_level(level_type);
self.irq_enb();
Ok(self)
}
_ => 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, M> From<Pin<I, M>> for DynPin
where
I: PinId,
M: PinMode,
{
/// 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, M> TryFrom<DynPin> for Pin<I, M>
where
I: PinId,
M: PinMode,
{
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 v1 traits
//==============================================================================
impl ErrorType for DynPin {
type Error = InvalidPinTypeError;
}
impl 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 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 StatefulOutputPin for DynPin {
#[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
self._is_high()
}
#[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
self._is_low()
}
}

View File

@ -0,0 +1,82 @@
//! # 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, [pin] and [dynpin],
//! representing two different ways to handle GPIO pins. The default, [pin],
//! is a type-level API that tracks the state of each pin at compile-time. The
//! alternative, [dynpin] 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, [dynpin] 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/va416xx-rs/src/branch/main/examples/simple/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) {
self.regs.enable_irq();
}
);
};
}
pub mod pin;
pub use pin::*;
pub mod dynpin;
pub use dynpin::*;
mod reg;

911
va416xx-hal/src/gpio/pin.rs Normal file
View File

@ -0,0 +1,911 @@
//! # 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 to G) and pin number. Each
//! `PinId` instance is named according to its datasheet identifier, e.g.
//! [PA2].
//!
//! A `PinMode` represents the various pin modes. The available `PinMode`
//! variants are [`Input`], [`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 PinsX
//! struct.
//!
//! Example for the pins of PORT A:
//!
//! To create the [PinsA] struct, users must supply the PAC
//! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes
//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`]
//! within the [PinsA] struct can be moved out and used individually.
//!
//!
//! ```no_run
//! let mut peripherals = Peripherals::take().unwrap();
//! let pinsa = PinsA::new(peripherals.porta);
//! ```
//!
//! Pins can be converted between modes using several different methods.
//!
//! ```no_run
//! // Use one of the literal function names
//! let pa0 = pinsa.pa0.into_floating_input();
//! // Use a generic method and one of the `PinMode` variant types
//! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
//! // Specify the target type and use `From`/`Into`
//! let pa0: Pin<PA0, FloatingInput> = pinsa.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`],
//! and [`StatefulOutputPin`].
use core::{convert::Infallible, marker::PhantomData, mem::transmute};
pub use crate::clock::FilterClkSel;
use crate::typelevel::Sealed;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
use super::{
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode,
};
//==================================================================================================
// 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]
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,
}
//==================================================================================================
// 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]
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]
const DYN: DynPinId;
}
macro_rules! pin_id {
($Group:ident, $Id:ident, $NUM:literal) => {
// Need paste macro to use ident in doc attribute
paste::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 _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) -> Self {
self.regs.interrupt_edge(edge_type);
self.irq_enb();
self
}
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
self.regs.interrupt_level(level_type);
self.irq_enb();
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
}
/// 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) -> Self {
self.regs.interrupt_edge(edge_type);
self.irq_enb();
self
}
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
self.regs.interrupt_level(level_type);
self.irq_enb();
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> 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::paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
pub struct $PinsName {
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 va416xx::Sysconfig,
port: $Port
) -> $PinsName {
syscfg.peripheral_clk_enable().modify(|_, w| {
w.[<$Port:lower>]().set_bit();
w.ioconfig().set_bit()
});
$PinsName {
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) -> $Port {
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),
]
);
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),
]
);
declare_pins!(
C,
PinsC,
Portc,
[
(PC0, 0),
(PC1, 1),
(PC2, 2),
(PC3, 3),
(PC4, 4),
(PC5, 5),
(PC6, 6),
(PC7, 7),
(PC8, 8),
(PC9, 9),
(PC10, 10),
(PC11, 11),
(PC12, 12),
(PC13, 13),
(PC14, 14),
(PC15, 15),
]
);
declare_pins!(
D,
PinsD,
Portd,
[
(PD0, 0),
(PD1, 1),
(PD2, 2),
(PD3, 3),
(PD4, 4),
(PD5, 5),
(PD6, 6),
(PD7, 7),
(PD8, 8),
(PD9, 9),
(PD10, 10),
(PD11, 11),
(PD12, 12),
(PD13, 13),
(PD14, 14),
(PD15, 15),
]
);
declare_pins!(
E,
PinsE,
Porte,
[
(PE0, 0),
(PE1, 1),
(PE2, 2),
(PE3, 3),
(PE4, 4),
(PE5, 5),
(PE6, 6),
(PE7, 7),
(PE8, 8),
(PE9, 9),
(PE10, 10),
(PE11, 11),
(PE12, 12),
(PE13, 13),
(PE14, 14),
(PE15, 15),
]
);
declare_pins!(
F,
PinsF,
Portf,
[
(PF0, 0),
(PF1, 1),
(PF2, 2),
(PF3, 3),
(PF4, 4),
(PF5, 5),
(PF6, 6),
(PF7, 7),
(PF8, 8),
(PF9, 9),
(PF10, 10),
(PF11, 11),
(PF12, 12),
(PF13, 13),
(PF14, 14),
(PF15, 15),
]
);
declare_pins!(
G,
PinsG,
Portg,
[
(PG0, 0),
(PG1, 1),
(PG2, 2),
(PG3, 3),
(PG4, 4),
(PG5, 5),
(PG6, 6),
(PG7, 7),
]
);

387
va416xx-hal/src/gpio/reg.rs Normal file
View File

@ -0,0 +1,387 @@
use crate::FunSel;
use super::{
dynpin::{self, DynGroup, DynPinId},
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
};
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
/// 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 dynpin::DynInput::*;
fields.dir = false;
fields.funsel = FunSel::Sel0 as u8;
match config {
Floating => (),
PullUp => {
fields.pull_en = true;
fields.pull_dir = true;
}
PullDown => {
fields.pull_en = true;
}
}
}
Output(config) => {
use dynpin::DynOutput::*;
fields.dir = true;
fields.funsel = FunSel::Sel0 as u8;
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
}
}
//==============================================================================
// RegisterInterface
//==============================================================================
pub type PortReg = ioconfig::Porta;
/// Provide a safe register interface for pin objects
///
/// [`PORT`], 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 = Porta::ptr();
const PORTB: *const PortRegisterBlock = Portb::ptr();
const PORTC: *const PortRegisterBlock = Portc::ptr();
const PORTD: *const PortRegisterBlock = Portd::ptr();
const PORTE: *const PortRegisterBlock = Porte::ptr();
const PORTF: *const PortRegisterBlock = Portf::ptr();
const PORTG: *const PortRegisterBlock = Portg::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) },
DynGroup::C => unsafe { &(*Self::PORTC) },
DynGroup::D => unsafe { &(*Self::PORTD) },
DynGroup::E => unsafe { &(*Self::PORTE) },
DynGroup::F => unsafe { &(*Self::PORTF) },
DynGroup::G => unsafe { &(*Self::PORTG) },
}
}
fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { 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),
DynGroup::C => ioconfig.portc0(self.id().num as usize),
DynGroup::D => ioconfig.portd0(self.id().num as usize),
DynGroup::E => ioconfig.porte0(self.id().num as usize),
DynGroup::F => ioconfig.portf0(self.id().num as usize),
DynGroup::G => ioconfig.portg0(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(())
}
}
}
/// 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()));
}
}
}
}

904
va416xx-hal/src/i2c.rs Normal file
View File

@ -0,0 +1,904 @@
//! API for the I2C peripheral
//!
//! ## Examples
//!
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
use crate::{
clock::{
assert_periph_reset, deassert_periph_reset, enable_peripheral_clock, Clocks,
PeripheralSelect,
},
pac,
time::Hertz,
typelevel::Sealed,
};
use core::{marker::PhantomData, ops::Deref};
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
//==================================================================================================
// Defintions
//==================================================================================================
const CLK_100K: Hertz = Hertz::from_raw(100_000);
const CLK_400K: Hertz = Hertz::from_raw(400_000);
const MIN_CLK_400K: Hertz = Hertz::from_raw(10_000_000);
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoEmptyMode {
Stall = 0,
EndTransaction = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockTooSlowForFastI2c;
#[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,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitError {
/// Wrong address used in constructor
WrongAddrMode,
/// APB1 clock is too slow for fast I2C mode.
ClkTooSlow(ClockTooSlowForFastI2c),
}
impl From<ClockTooSlowForFastI2c> for InitError {
fn from(value: ClockTooSlowForFastI2c) -> Self {
Self::ClkTooSlow(value)
}
}
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::InsufficientDataReceived | Error::InvalidTimingParams => {
embedded_hal::i2c::ErrorKind::Other
}
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum I2cCmd {
Start = 0b00,
Stop = 0b10,
StartWithStop = 0b11,
Cancel = 0b100,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cSpeed {
Regular100khz = 0,
Fast400khz = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cDirection {
Send = 0,
Read = 1,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cAddress {
Regular(u8),
TenBit(u16),
}
pub type I2cRegBlock = pac::i2c0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait Instance: Deref<Target = I2cRegBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
fn ptr() -> *const I2cRegBlock;
}
impl Instance for pac::I2c0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
}
impl Instance for pac::I2c1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
}
impl Instance for pac::I2c2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c2;
fn ptr() -> *const I2cRegBlock {
Self::ptr()
}
}
//==================================================================================================
// 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,
clock: 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),
}
}
}
impl<I2c: Instance> I2cBase<I2c> {
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2c> {
enable_peripheral_clock(sys_cfg, I2c::PERIPH_SEL);
assert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_periph_reset(sys_cfg, I2c::PERIPH_SEL);
let mut i2c_base = I2cBase {
i2c,
clock: clocks.apb1(),
};
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)?;
Ok(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) -> Result<u8, ClockTooSlowForFastI2c> {
if speed_mode == I2cSpeed::Regular100khz {
Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else {
if self.clock.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2c);
}
Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8)
}
}
/// Configures the clock scale for a given speed mode setting
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> {
let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c
.clkscale()
.write(|w| unsafe { w.bits((speed_mode as u32) << 31 | clk_div as u32) });
Ok(())
}
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) });
}
}
//==================================================================================================
// I2C Master
//==================================================================================================
pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
i2c_base: I2cBase<I2c>,
addr: PhantomData<Addr>,
}
impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: MasterConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> {
Ok(I2cMaster {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
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::<I2c>::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<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, SevenBitAddress> {
type Error = Error;
}
impl<I2c: Instance> embedded_hal::i2c::I2c for I2cMaster<I2c, 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<I2c> embedded_hal::i2c::ErrorType for I2cMaster<I2c, TenBitAddress> {
type Error = Error;
}
impl<I2c: Instance> embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<I2c, 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 Slave
//==================================================================================================
pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
i2c_base: I2cBase<I2c>,
addr: PhantomData<Addr>,
}
impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
fn new_generic(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> {
Ok(I2cSlave {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&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<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
/// Create a new I2C slave for seven bit addresses
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, InitError> {
if let I2cAddress::TenBit(_) = cfg.addr {
return Err(InitError::WrongAddrMode);
}
Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?)
}
}
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
pub fn new_ten_bit_addr(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> {
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
}
}

46
va416xx-hal/src/lib.rs Normal file
View File

@ -0,0 +1,46 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(test)]
extern crate std;
pub use va416xx as device;
pub use va416xx as pac;
pub mod prelude;
pub mod clock;
pub mod gpio;
pub mod i2c;
pub mod pwm;
pub mod spi;
pub mod time;
pub mod timer;
pub mod typelevel;
pub mod uart;
pub mod wdt;
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
pub enum FunSel {
Sel0 = 0b00,
Sel1 = 0b01,
Sel2 = 0b10,
Sel3 = 0b11,
}
/// Enable a specific interrupt using the NVIC peripheral.
///
/// # Safety
///
/// This function is `unsafe` because it can break mask-based critical sections.
#[inline]
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
}
}
/// Disable a specific interrupt using the NVIC peripheral.
#[inline]
pub fn disable_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq);
}

View File

@ -0,0 +1,4 @@
//! Prelude
pub use crate::clock::{ClkgenExt, SyscfgExt};
pub use fugit::ExtU32 as _;
pub use fugit::RateExtU32 as _;

388
va416xx-hal/src/pwm.rs Normal file
View File

@ -0,0 +1,388 @@
//! API for Pulse-Width Modulation (PWM)
//!
//! The Vorago VA416xx devices use the TIM peripherals to perform PWM related tasks.
//!
//! ## Examples
//!
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
use core::convert::Infallible;
use core::marker::PhantomData;
use crate::pac;
use crate::{clock::Clocks, gpio::DynPinId};
pub use crate::{gpio::PinId, time::Hertz, timer::*};
const DUTY_MAX: u16 = u16::MAX;
pub struct PwmBase {
clock: 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.clock.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(
pin_and_tim: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
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,
clock: Tim::clock(clocks),
},
reg: unsafe { TimAndPinRegister::new(pin_and_tim.0, pin_and_tim.1) },
mode: PhantomData,
};
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(
tim_and_pin: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmA> =
Self::new(tim_and_pin, sys_cfg, clocks, 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(
tim_and_pin: (Pin, Tim),
sys_cfg: &mut pac::Sysconfig,
clocks: &Clocks,
initial_period: impl Into<Hertz> + Copy,
) -> Self {
let mut pin: PwmPin<Pin, Tim, PwmB> =
Self::new(tim_and_pin, sys_cfg, clocks, 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
}
}

978
va416xx-hal/src/spi.rs Normal file
View File

@ -0,0 +1,978 @@
//! API for the SPI peripheral
//!
//! ## Examples
//!
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/spi.rs)
use core::{convert::Infallible, marker::PhantomData, ops::Deref};
use embedded_hal::spi::Mode;
use crate::{
clock::PeripheralSelect,
gpio::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PB0,
PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1,
PC10, PC11, PC7, PC8, PC9, PE10, PE11, PE12, PE13, PE14, PE15, PE5, PE6, PE7, PE8, PE9,
PF0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PG2, PG3, PG4,
},
pac,
time::Hertz,
typelevel::{NoneT, Sealed},
};
//==================================================================================================
// Defintions
//==================================================================================================
// FIFO has a depth of 16.
const FILL_DEPTH: usize = 12;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
Id2 = 2,
Id3 = 3,
Id4 = 4,
Id5 = 5,
Id6 = 6,
Id7 = 7,
Invalid = 0xff,
}
#[derive(Debug)]
pub enum SpiId {
Spi0,
Spi1,
Spi2,
Spi3,
Invalid,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
EightBits = 0x07,
SixteenBits = 0x0f,
}
//==================================================================================================
// Pin type definitions
//==================================================================================================
pub trait PinSck<SPI>: Sealed {}
pub trait PinMosi<SPI>: Sealed {}
pub trait PinMiso<SPI>: Sealed {}
pub trait HwCsProvider: Sealed {
const CS_ID: HwChipSelectId;
const SPI_ID: SpiId;
}
pub trait OptionalHwCs<Spi>: HwCsProvider + Sealed {}
macro_rules! hw_cs_pins {
($SPIx:path, $portId: path:
$(
($PXx:ident, $AFx:ident, $HwCsIdent:path, $typedef:ident),
)+
) => {
$(
impl HwCsProvider for Pin<$PXx, $AFx> {
const CS_ID: HwChipSelectId = $HwCsIdent;
const SPI_ID: SpiId = $portId;
}
impl OptionalHwCs<$SPIx> for Pin<$PXx, $AFx> {}
pub type $typedef = Pin<$PXx, $AFx>;
)+
};
}
impl HwCsProvider for NoneT {
const CS_ID: HwChipSelectId = HwChipSelectId::Invalid;
const SPI_ID: SpiId = SpiId::Invalid;
}
impl OptionalHwCs<pac::Spi0> for NoneT {}
impl OptionalHwCs<pac::Spi1> for NoneT {}
impl OptionalHwCs<pac::Spi2> for NoneT {}
impl OptionalHwCs<pac::Spi3> for NoneT {}
// SPI 0
impl PinSck<pac::Spi0> for Pin<PB15, AltFunc1> {}
impl PinMosi<pac::Spi0> for Pin<PC1, AltFunc1> {}
impl PinMiso<pac::Spi0> for Pin<PC0, AltFunc1> {}
// SPI 1
impl PinSck<pac::Spi1> for Pin<PB8, AltFunc3> {}
impl PinMosi<pac::Spi1> for Pin<PB10, AltFunc3> {}
impl PinMiso<pac::Spi1> for Pin<PB9, AltFunc3> {}
impl PinSck<pac::Spi1> for Pin<PC9, AltFunc2> {}
impl PinMosi<pac::Spi1> for Pin<PC11, AltFunc2> {}
impl PinMiso<pac::Spi1> for Pin<PC10, AltFunc2> {}
impl PinSck<pac::Spi1> for Pin<PG3, AltFunc2> {}
impl PinMiso<pac::Spi1> for Pin<PG4, AltFunc2> {}
impl PinSck<pac::Spi1> for Pin<PE13, AltFunc2> {}
impl PinMosi<pac::Spi1> for Pin<PE15, AltFunc2> {}
impl PinMiso<pac::Spi1> for Pin<PE14, AltFunc2> {}
impl PinSck<pac::Spi1> for Pin<PF3, AltFunc1> {}
impl PinMosi<pac::Spi1> for Pin<PF5, AltFunc1> {}
impl PinMiso<pac::Spi1> for Pin<PF4, AltFunc1> {}
// SPI 2
impl PinSck<pac::Spi2> for Pin<PA5, AltFunc2> {}
impl PinMosi<pac::Spi2> for Pin<PA7, AltFunc2> {}
impl PinMiso<pac::Spi2> for Pin<PA6, AltFunc2> {}
impl PinSck<pac::Spi2> for Pin<PF5, AltFunc2> {}
impl PinMosi<pac::Spi2> for Pin<PF7, AltFunc2> {}
impl PinMiso<pac::Spi2> for Pin<PF6, AltFunc2> {}
// SPI3 is shared with the ROM SPI pins and has its own dedicated pins.
// SPI 0 HW CS pins
hw_cs_pins!(
pac::Spi0, SpiId::Spi0:
(PB14, AltFunc1, HwChipSelectId::Id0, HwCs0Spi0),
(PB13, AltFunc1, HwChipSelectId::Id1, HwCs1Spi0),
(PB12, AltFunc1, HwChipSelectId::Id2, HwCs2Spi0),
(PB11, AltFunc1, HwChipSelectId::Id3, HwCs3Spi0),
);
hw_cs_pins!(
pac::Spi1, SpiId::Spi1:
(PB7, AltFunc3, HwChipSelectId::Id0, HwCs0Spi1Pb),
(PB6, AltFunc3, HwChipSelectId::Id1, HwCs1Spi1Pb),
(PB5, AltFunc3, HwChipSelectId::Id2, HwCs2Spi1Pb),
(PB4, AltFunc3, HwChipSelectId::Id3, HwCs3Spi1Pb),
(PB3, AltFunc3, HwChipSelectId::Id4, HwCs4Spi1Pb),
(PB2, AltFunc3, HwChipSelectId::Id5, HwCs5Spi1Pb),
(PB1, AltFunc3, HwChipSelectId::Id6, HwCs6Spi1Pb),
(PB0, AltFunc3, HwChipSelectId::Id7, HwCs7Spi1Pb),
(PC8, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pc),
(PC7, AltFunc2, HwChipSelectId::Id1, HwCs1Spi1Pc),
(PE12, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pe),
(PE11, AltFunc2, HwChipSelectId::Id1, HwCs1Spi1Pe),
(PE10, AltFunc2, HwChipSelectId::Id2, HwCs2Spi1Pe),
(PE9, AltFunc2, HwChipSelectId::Id3, HwCs3Spi1Pe),
(PE8, AltFunc2, HwChipSelectId::Id4, HwCs4Spi1Pe),
(PE7, AltFunc3, HwChipSelectId::Id5, HwCs5Spi1Pe),
(PE6, AltFunc3, HwChipSelectId::Id6, HwCs6Spi1Pe),
(PE5, AltFunc3, HwChipSelectId::Id7, HwCs7Spi1Pe),
(PF2, AltFunc1, HwChipSelectId::Id0, HwCs0Spi1Pf),
(PG2, AltFunc2, HwChipSelectId::Id0, HwCs0Spi1Pg),
);
hw_cs_pins!(
pac::Spi2, SpiId::Spi2:
(PA4, AltFunc2, HwChipSelectId::Id0, HwCs0Spi2Pa),
(PA3, AltFunc2, HwChipSelectId::Id1, HwCs1Spi2Pa),
(PA2, AltFunc2, HwChipSelectId::Id2, HwCs2Spi2Pa),
(PA1, AltFunc2, HwChipSelectId::Id3, HwCs3Spi2Pa),
(PA0, AltFunc2, HwChipSelectId::Id4, HwCs4Spi2Pa),
(PA8, AltFunc2, HwChipSelectId::Id6, HwCs6Spi2Pa),
(PA9, AltFunc2, HwChipSelectId::Id5, HwCs5Spi2Pa),
(PF0, AltFunc2, HwChipSelectId::Id4, HwCs4Spi2Pf),
(PF1, AltFunc2, HwChipSelectId::Id3, HwCs3Spi2Pf),
(PF2, AltFunc2, HwChipSelectId::Id2, HwCs2Spi2Pf),
(PF3, AltFunc2, HwChipSelectId::Id1, HwCs1Spi2Pf),
(PF4, AltFunc2, HwChipSelectId::Id0, HwCs0Spi2Pf),
);
//==================================================================================================
// Config
//==================================================================================================
pub trait TransferConfigProvider {
fn sod(&mut self, sod: bool);
fn blockmode(&mut self, blockmode: bool);
fn mode(&mut self, mode: Mode);
fn frequency(&mut self, spi_clk: Hertz);
fn hw_cs_id(&self) -> u8;
}
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone)]
pub struct TransferConfig<HwCs> {
pub spi_clk: Hertz,
pub mode: Mode,
/// This only works if the Slave Output Disable (SOD) bit of the [`SpiConfig`] is set to
/// false
pub hw_cs: Option<HwCs>,
pub sod: bool,
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
/// duration of multiple data words
pub blockmode: bool,
}
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
pub struct ErasedTransferConfig {
pub spi_clk: Hertz,
pub mode: Mode,
pub sod: bool,
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
/// duration of multiple data words
pub blockmode: bool,
pub hw_cs: HwChipSelectId,
}
impl TransferConfig<NoneT> {
pub fn new_no_hw_cs(spi_clk: impl Into<Hertz>, mode: Mode, blockmode: bool, sod: bool) -> Self {
TransferConfig {
spi_clk: spi_clk.into(),
mode,
hw_cs: None,
sod,
blockmode,
}
}
}
impl<HwCs: HwCsProvider> TransferConfig<HwCs> {
pub fn new(
spi_clk: impl Into<Hertz>,
mode: Mode,
hw_cs: Option<HwCs>,
blockmode: bool,
sod: bool,
) -> Self {
TransferConfig {
spi_clk: spi_clk.into(),
mode,
hw_cs,
sod,
blockmode,
}
}
pub fn downgrade(self) -> ErasedTransferConfig {
ErasedTransferConfig {
spi_clk: self.spi_clk,
mode: self.mode,
sod: self.sod,
blockmode: self.blockmode,
hw_cs: HwCs::CS_ID,
}
}
}
impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfig<HwCs> {
/// Slave Output Disable
fn sod(&mut self, sod: bool) {
self.sod = sod;
}
fn blockmode(&mut self, blockmode: bool) {
self.blockmode = blockmode;
}
fn mode(&mut self, mode: Mode) {
self.mode = mode;
}
fn frequency(&mut self, spi_clk: Hertz) {
self.spi_clk = spi_clk;
}
fn hw_cs_id(&self) -> u8 {
HwCs::CS_ID as u8
}
}
#[derive(Default)]
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
pub struct SpiConfig {
/// Serial clock rate divider. Together with the CLKPRESCALE register, it determines
/// the SPI clock rate in master mode. 0 by default. Specifying a higher value
/// limits the maximum attainable SPI speed
pub ser_clock_rate_div: u8,
/// By default, configure SPI for master mode (ms == false)
ms: bool,
/// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
pub slave_output_disable: bool,
/// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
pub loopback_mode: bool,
/// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
pub master_delayer_capture: bool,
}
impl SpiConfig {
pub fn loopback(mut self, enable: bool) -> Self {
self.loopback_mode = enable;
self
}
pub fn master_mode(mut self, master: bool) -> Self {
self.ms = !master;
self
}
pub fn slave_output_disable(mut self, sod: bool) -> Self {
self.slave_output_disable = sod;
self
}
}
//==================================================================================================
// Word Size
//==================================================================================================
/// Configuration trait for the Word Size used by the SPI peripheral
pub trait WordProvider: Copy + Default + Into<u32> + TryFrom<u32> + 'static {
const MASK: u32;
fn word_reg() -> u8;
}
impl WordProvider for u8 {
const MASK: u32 = 0xff;
fn word_reg() -> u8 {
0x07
}
}
impl WordProvider for u16 {
const MASK: u32 = 0xffff;
fn word_reg() -> u8 {
0x0f
}
}
pub type SpiRegBlock = pac::spi0::RegisterBlock;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait Instance: Deref<Target = SpiRegBlock> {
const IDX: u8;
const PERIPH_SEL: PeripheralSelect;
fn ptr() -> *const SpiRegBlock;
}
impl Instance for pac::Spi0 {
const IDX: u8 = 0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi1 {
const IDX: u8 = 1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
impl Instance for pac::Spi2 {
const IDX: u8 = 2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
fn ptr() -> *const SpiRegBlock {
Self::ptr()
}
}
//==================================================================================================
// Spi
//==================================================================================================
pub struct SpiBase<SpiInstance, Word = u8> {
spi: SpiInstance,
cfg: SpiConfig,
apb1_clk: Hertz,
/// Fill word for read-only SPI transactions.
pub fill_word: Word,
blockmode: bool,
word: PhantomData<Word>,
}
pub struct Spi<SpiInstance, Pins, Word = u8> {
inner: SpiBase<SpiInstance, Word>,
pins: Pins,
}
fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode {
embedded_hal::spi::MODE_0 => (false, false),
embedded_hal::spi::MODE_1 => (false, true),
embedded_hal::spi::MODE_2 => (true, false),
embedded_hal::spi::MODE_3 => (true, true),
}
}
impl<SpiInstance: Instance, Word: WordProvider> SpiBase<SpiInstance, Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
#[inline]
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
let clk_prescale =
self.apb1_clk.raw() / (spi_clk.into().raw() * (self.cfg.ser_clock_rate_div as u32 + 1));
self.spi
.clkprescale()
.write(|w| unsafe { w.bits(clk_prescale) });
}
#[inline]
pub fn cfg_mode(&mut self, mode: Mode) {
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
self.spi.ctrl0().modify(|_, w| {
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
});
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.spi.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.spi.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn perid(&self) -> u32 {
self.spi.perid().read().bits()
}
#[inline]
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
if hw_cs == HwChipSelectId::Invalid {
return;
}
self.spi.ctrl1().modify(|_, w| {
w.sod().clear_bit();
unsafe {
w.ss().bits(hw_cs as u8);
}
w
});
}
#[inline]
pub fn cfg_hw_cs_with_pin<HwCs: OptionalHwCs<SpiInstance>>(&mut self, _: &HwCs) {
self.cfg_hw_cs(HwCs::CS_ID);
}
pub fn cfg_hw_cs_disable(&mut self) {
self.spi.ctrl1().modify(|_, w| {
w.sod().set_bit();
w
});
}
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiInstance>>(
&mut self,
transfer_cfg: &TransferConfig<HwCs>,
) {
self.cfg_clock(transfer_cfg.spi_clk);
self.cfg_mode(transfer_cfg.mode);
self.blockmode = transfer_cfg.blockmode;
self.spi.ctrl1().modify(|_, w| {
if transfer_cfg.sod {
w.sod().set_bit();
} else if transfer_cfg.hw_cs.is_some() {
w.sod().clear_bit();
unsafe {
w.ss().bits(HwCs::CS_ID as u8);
}
} else {
w.sod().clear_bit();
}
if transfer_cfg.blockmode {
w.blockmode().set_bit();
} else {
w.blockmode().clear_bit();
}
w
});
}
/// Sends a word to the slave
#[inline(always)]
fn send_blocking(&self, word: Word) {
// TODO: Upper limit for wait cycles to avoid complete hangups?
while self.spi.status().read().tnf().bit_is_clear() {}
self.send(word)
}
#[inline(always)]
fn send(&self, word: Word) {
self.spi.data().write(|w| unsafe { w.bits(word.into()) });
}
/// Read a word from the slave. Must be preceeded by a [`send`](Self::send) call
#[inline(always)]
fn read_blocking(&self) -> Word {
// TODO: Upper limit for wait cycles to avoid complete hangups?
while self.spi.status().read().rne().bit_is_clear() {}
self.read_single_word()
}
#[inline(always)]
fn read_single_word(&self) -> Word {
(self.spi.data().read().bits() & Word::MASK)
.try_into()
.unwrap()
}
fn transfer_preparation(&self, words: &[Word]) -> Result<(), Infallible> {
if words.is_empty() {
return Ok(());
}
let mut status_reg = self.spi.status().read();
// Wait until all bytes have been transferred.
while status_reg.tfe().bit_is_clear() {
// Ignore all received read words.
if status_reg.rne().bit_is_set() {
self.clear_rx_fifo();
}
status_reg = self.spi.status().read();
}
// Ignore all received read words.
if status_reg.rne().bit_is_set() {
self.clear_rx_fifo();
}
Ok(())
}
fn initial_send_fifo_pumping(&self, words: Option<&[Word]>) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
for _ in 0..core::cmp::min(FILL_DEPTH, words.map_or(0, |words| words.len())) {
self.send_blocking(words.map_or(self.fill_word, |words| words[current_write_idx]));
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
}
current_write_idx
}
}
/*
macro_rules! spi_ctor {
($spiI:ident, $PeriphSel: path) => {
/// Create a new SPI struct
///
/// You can delete the pin type information by calling the
/// [`downgrade`](Self::downgrade) function
///
/// ## Arguments
/// * `spi` - SPI bus to use
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
/// to ensure the pins can not be used for other purposes anymore
/// * `spi_cfg` - Configuration specific to the SPI bus
/// * `transfer_cfg` - Optional initial transfer configuration which includes
/// configuration which can change across individual SPI transfers like SPI mode
/// or SPI clock. If only one device is connected, this configuration only needs
/// to be done once.
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
pub fn $spiI(
spi: SpiI,
pins: (Sck, Miso, Mosi),
clocks: &crate::clock::Clocks,
spi_cfg: SpiConfig,
syscfg: &mut pac::Sysconfig,
transfer_cfg: Option<&ErasedTransferConfig>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, $PeriphSel);
let SpiConfig {
ser_clock_rate_div,
ms,
slave_output_disable,
loopback_mode,
master_delayer_capture,
} = spi_cfg;
let mut mode = embedded_hal::spi::MODE_0;
let mut clk_prescale = 0x02;
let mut ss = 0;
let mut init_blockmode = false;
let apb1_clk = clocks.apb1();
if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode;
clk_prescale =
apb1_clk.raw() / (transfer_cfg.spi_clk.raw() * (ser_clock_rate_div as u32 + 1));
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
ss = transfer_cfg.hw_cs as u8;
}
init_blockmode = transfer_cfg.blockmode;
}
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
spi.ctrl0().write(|w| {
unsafe {
w.size().bits(Word::word_reg());
w.scrdv().bits(ser_clock_rate_div);
// Clear clock phase and polarity. Will be set to correct value for each
// transfer
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
}
});
spi.ctrl1().write(|w| {
w.lbm().bit(loopback_mode);
w.sod().bit(slave_output_disable);
w.ms().bit(ms);
w.mdlycap().bit(master_delayer_capture);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
spi.fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
});
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
// Enable the peripheral as the last step as recommended in the
// programmers guide
spi.ctrl1().modify(|_, w| w.enable().set_bit());
Spi {
inner: SpiBase {
spi,
cfg: spi_cfg,
apb1_clk,
fill_word: Default::default(),
blockmode: init_blockmode,
word: PhantomData,
},
pins,
}
}
};
}
*/
impl<
SpiI: Instance,
Sck: PinSck<SpiI>,
Miso: PinMiso<SpiI>,
Mosi: PinMosi<SpiI>,
Word: WordProvider,
> Spi<SpiI, (Sck, Miso, Mosi), Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
/// Create a new SPI struct
///
/// You can delete the pin type information by calling the
/// [`downgrade`](Self::downgrade) function
///
/// ## Arguments
/// * `spi` - SPI bus to use
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
/// to ensure the pins can not be used for other purposes anymore
/// * `spi_cfg` - Configuration specific to the SPI bus
/// * `transfer_cfg` - Optional initial transfer configuration which includes
/// configuration which can change across individual SPI transfers like SPI mode
/// or SPI clock. If only one device is connected, this configuration only needs
/// to be done once.
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
pub fn new(
spi: SpiI,
pins: (Sck, Miso, Mosi),
clocks: &crate::clock::Clocks,
spi_cfg: SpiConfig,
syscfg: &mut pac::Sysconfig,
transfer_cfg: Option<&ErasedTransferConfig>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
let SpiConfig {
ser_clock_rate_div,
ms,
slave_output_disable,
loopback_mode,
master_delayer_capture,
} = spi_cfg;
let mut mode = embedded_hal::spi::MODE_0;
let mut clk_prescale = 0x02;
let mut ss = 0;
let mut init_blockmode = false;
let apb1_clk = clocks.apb1();
if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode;
clk_prescale =
apb1_clk.raw() / (transfer_cfg.spi_clk.raw() * (ser_clock_rate_div as u32 + 1));
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
ss = transfer_cfg.hw_cs as u8;
}
init_blockmode = transfer_cfg.blockmode;
}
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
spi.ctrl0().write(|w| {
unsafe {
w.size().bits(Word::word_reg());
w.scrdv().bits(ser_clock_rate_div);
// Clear clock phase and polarity. Will be set to correct value for each
// transfer
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
}
});
spi.ctrl1().write(|w| {
w.lbm().bit(loopback_mode);
w.sod().bit(slave_output_disable);
w.ms().bit(ms);
w.mdlycap().bit(master_delayer_capture);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
spi.fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
});
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
// Enable the peripheral as the last step as recommended in the
// programmers guide
spi.ctrl1().modify(|_, w| w.enable().set_bit());
Spi {
inner: SpiBase {
spi,
cfg: spi_cfg,
apb1_clk,
fill_word: Default::default(),
blockmode: init_blockmode,
word: PhantomData,
},
pins,
}
}
#[inline]
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
self.inner.cfg_clock(spi_clk);
}
#[inline]
pub fn cfg_mode(&mut self, mode: Mode) {
self.inner.cfg_mode(mode);
}
pub fn set_fill_word(&mut self, fill_word: Word) {
self.inner.fill_word = fill_word;
}
pub fn fill_word(&self) -> Word {
self.inner.fill_word
}
#[inline]
pub fn perid(&self) -> u32 {
self.inner.perid()
}
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(&mut self, transfer_cfg: &TransferConfig<HwCs>) {
self.inner.cfg_transfer(transfer_cfg);
}
/// Releases the SPI peripheral and associated pins
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
(self.inner.spi, self.pins, self.inner.cfg)
}
pub fn downgrade(self) -> SpiBase<SpiI, Word> {
self.inner
}
}
/// Changing the word size also requires a type conversion
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
From<Spi<SpiI, (Sck, Miso, Mosi), u8>> for Spi<SpiI, (Sck, Miso, Mosi), u16>
{
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u8>) -> Self {
old_spi
.inner
.spi
.ctrl0()
.modify(|_, w| unsafe { w.size().bits(WordSize::SixteenBits as u8) });
Spi {
inner: SpiBase {
spi: old_spi.inner.spi,
cfg: old_spi.inner.cfg,
blockmode: old_spi.inner.blockmode,
fill_word: Default::default(),
apb1_clk: old_spi.inner.apb1_clk,
word: PhantomData,
},
pins: old_spi.pins,
}
}
}
/// Changing the word size also requires a type conversion
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
From<Spi<SpiI, (Sck, Miso, Mosi), u16>> for Spi<SpiI, (Sck, Miso, Mosi), u8>
{
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u16>) -> Self {
old_spi
.inner
.spi
.ctrl0()
.modify(|_, w| unsafe { w.size().bits(WordSize::EightBits as u8) });
Spi {
inner: SpiBase {
spi: old_spi.inner.spi,
cfg: old_spi.inner.cfg,
blockmode: old_spi.inner.blockmode,
apb1_clk: old_spi.inner.apb1_clk,
fill_word: Default::default(),
word: PhantomData,
},
pins: old_spi.pins,
}
}
}
impl<SpiI: Instance, Word: WordProvider> embedded_hal::spi::ErrorType for SpiBase<SpiI, Word> {
type Error = Infallible;
}
impl<SpiI: Instance, Word: WordProvider> embedded_hal::spi::SpiBus<Word> for SpiBase<SpiI, Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(None);
loop {
if current_write_idx < words.len() {
self.send_blocking(self.fill_word);
current_write_idx += 1;
}
if current_read_idx < words.len() {
words[current_read_idx] = self.read_blocking();
current_read_idx += 1;
}
if current_read_idx >= words.len() && current_write_idx >= words.len() {
break;
}
}
Ok(())
}
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
while current_write_idx < words.len() {
self.send_blocking(words[current_write_idx]);
current_write_idx += 1;
// Ignore received words.
if self.spi.status().read().rne().bit_is_set() {
self.clear_rx_fifo();
}
}
Ok(())
}
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.transfer_preparation(write)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(write));
while current_read_idx < read.len() || current_write_idx < write.len() {
if current_write_idx < write.len() {
self.send_blocking(write[current_write_idx]);
current_write_idx += 1;
}
if current_read_idx < read.len() {
read[current_read_idx] = self.read_blocking();
current_read_idx += 1;
}
}
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
while current_read_idx < words.len() || current_write_idx < words.len() {
if current_write_idx < words.len() {
self.send_blocking(words[current_write_idx]);
current_write_idx += 1;
}
if current_read_idx < words.len() && current_read_idx < current_write_idx {
words[current_read_idx] = self.read_blocking();
current_read_idx += 1;
}
}
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
let status_reg = self.spi.status().read();
while status_reg.tfe().bit_is_clear() || status_reg.rne().bit_is_set() {
if status_reg.rne().bit_is_set() {
self.read_single_word();
}
}
Ok(())
}
}
impl<
SpiI: Instance,
Word: WordProvider,
Sck: PinSck<SpiI>,
Miso: PinMiso<SpiI>,
Mosi: PinMosi<SpiI>,
> embedded_hal::spi::ErrorType for Spi<SpiI, (Sck, Miso, Mosi), Word>
{
type Error = Infallible;
}
impl<
SpiI: Instance,
Word: WordProvider,
Sck: PinSck<SpiI>,
Miso: PinMiso<SpiI>,
Mosi: PinMosi<SpiI>,
> embedded_hal::spi::SpiBus<Word> for Spi<SpiI, (Sck, Miso, Mosi), Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.inner.read(words)
}
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
self.inner.write(words)
}
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.inner.transfer(read, write)
}
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.inner.transfer_in_place(words)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.inner.flush()
}
}

26
va416xx-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;

801
va416xx-hal/src/timer.rs Normal file
View File

@ -0,0 +1,801 @@
//! API for the TIM peripherals
//!
//! ## Examples
//!
//! TODO.
use core::cell::Cell;
use cortex_m::interrupt::Mutex;
use crate::clock::Clocks;
use crate::gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
PA15, PA2, PA3, PA4, PA5, PA6, PA7, PB0, PB1, PB10, PB11, PB12, PB13, PB14, PB15, PB2, PB3,
PB4, PB5, PB6, PB7, PB8, PB9, PC0, PC1, PD0, PD1, PD10, PD11, PD12, PD13, PD14, PD15, PD2, PD3,
PD4, PD5, PD6, PD7, PD8, PD9, PE0, PE1, PE10, PE11, PE12, PE13, PE14, PE15, PE2, PE3, PE4, PE5,
PE6, PE7, PE8, PE9, PF0, PF1, PF10, PF11, PF12, PF13, PF14, PF15, PF2, PF3, PF4, PF5, PF6, PF7,
PF8, PF9, PG0, PG1, PG2, PG3, PG6,
};
use crate::time::Hertz;
use crate::typelevel::Sealed;
use crate::{disable_interrupt, prelude::*};
use crate::{enable_interrupt, pac};
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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSel {
Sel0 = 0,
Sel1 = 1,
Sel2 = 2,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCascadeSourceId;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSource {
PortA(u8),
PortB(u8),
PortC(u8),
PortD(u8),
PortE(u8),
Tim(u8),
TxEv,
AdcIrq,
RomSbe,
RomMbe,
Ram0Sbe,
Ram0Mbe,
Ram1Sbe,
Ram2Mbe,
WdogIrq,
}
impl CascadeSource {
fn id(&self) -> Result<u8, InvalidCascadeSourceId> {
let port_check = |base: u8, id: u8| {
if id > 15 {
return Err(InvalidCascadeSourceId);
}
Ok(base + id)
};
match self {
CascadeSource::PortA(id) => port_check(0, *id),
CascadeSource::PortB(id) => port_check(16, *id),
CascadeSource::PortC(id) => port_check(32, *id),
CascadeSource::PortD(id) => port_check(48, *id),
CascadeSource::PortE(id) => port_check(65, *id),
CascadeSource::Tim(id) => {
if *id > 23 {
return Err(InvalidCascadeSourceId);
}
Ok(80 + id)
}
CascadeSource::TxEv => Ok(104),
CascadeSource::AdcIrq => Ok(105),
CascadeSource::RomSbe => Ok(106),
CascadeSource::RomMbe => Ok(106),
CascadeSource::Ram0Sbe => Ok(108),
CascadeSource::Ram0Mbe => Ok(109),
CascadeSource::Ram1Sbe => Ok(110),
CascadeSource::Ram2Mbe => Ok(111),
CascadeSource::WdogIrq => Ok(112),
}
}
}
//==================================================================================================
// 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;
const IRQ: pac::Interrupt;
fn clock(clocks: &Clocks) -> Hertz {
if Self::TIM_ID <= 15 {
clocks.apb1()
} else {
clocks.apb2()
}
}
}
macro_rules! tim_markers {
(
$(
($TimX:path, $id:expr, $Irq:path),
)+
) => {
$(
impl ValidTim for $TimX {
const TIM_ID: u8 = $id;
const IRQ: pac::Interrupt = $Irq;
}
)+
};
}
tim_markers!(
(pac::Tim0, 0, pac::Interrupt::TIM0),
(pac::Tim1, 1, pac::Interrupt::TIM1),
(pac::Tim2, 2, pac::Interrupt::TIM2),
(pac::Tim3, 3, pac::Interrupt::TIM3),
(pac::Tim4, 4, pac::Interrupt::TIM4),
(pac::Tim5, 5, pac::Interrupt::TIM5),
(pac::Tim6, 6, pac::Interrupt::TIM6),
(pac::Tim7, 7, pac::Interrupt::TIM7),
(pac::Tim8, 8, pac::Interrupt::TIM8),
(pac::Tim9, 9, pac::Interrupt::TIM9),
(pac::Tim10, 10, pac::Interrupt::TIM10),
(pac::Tim11, 11, pac::Interrupt::TIM11),
(pac::Tim12, 12, pac::Interrupt::TIM12),
(pac::Tim13, 13, pac::Interrupt::TIM13),
(pac::Tim14, 14, pac::Interrupt::TIM14),
(pac::Tim15, 15, pac::Interrupt::TIM15),
(pac::Tim16, 16, pac::Interrupt::TIM16),
(pac::Tim17, 17, pac::Interrupt::TIM17),
(pac::Tim18, 18, pac::Interrupt::TIM18),
(pac::Tim19, 19, pac::Interrupt::TIM19),
(pac::Tim20, 20, pac::Interrupt::TIM20),
(pac::Tim21, 21, pac::Interrupt::TIM21),
(pac::Tim22, 22, pac::Interrupt::TIM22),
(pac::Tim23, 23, pac::Interrupt::TIM23),
);
pub trait ValidTimAndPin<Pin: TimPin, Tim: ValidTim>: Sealed {}
macro_rules! valid_pin_and_tims {
(
$(
($PinX:ident, $AltFunc:ident, $TimX:path),
)+
) => {
$(
impl TimPin for Pin<$PinX, $AltFunc>
where
$PinX: PinId,
{
const DYN: DynPinId = $PinX::DYN;
}
impl<
PinInstance: TimPin,
Tim: ValidTim
> ValidTimAndPin<PinInstance, Tim> for (Pin<$PinX, $AltFunc>, $TimX)
where
Pin<$PinX, $AltFunc>: TimPin,
$PinX: PinId,
{
}
impl Sealed for (Pin<$PinX, $AltFunc>, $TimX) {}
)+
};
}
valid_pin_and_tims!(
(PA0, AltFunc1, pac::Tim0),
(PA1, AltFunc1, pac::Tim1),
(PA2, AltFunc1, pac::Tim2),
(PA3, AltFunc1, pac::Tim3),
(PA4, AltFunc1, pac::Tim4),
(PA5, AltFunc1, pac::Tim5),
(PA6, AltFunc1, pac::Tim6),
(PA7, AltFunc1, pac::Tim7),
(PA10, AltFunc2, pac::Tim23),
(PA11, AltFunc2, pac::Tim22),
(PA12, AltFunc2, pac::Tim21),
(PA13, AltFunc2, pac::Tim20),
(PA14, AltFunc2, pac::Tim19),
(PA15, AltFunc2, pac::Tim18),
(PB0, AltFunc2, pac::Tim17),
(PB1, AltFunc2, pac::Tim16),
(PB2, AltFunc2, pac::Tim15),
(PB3, AltFunc2, pac::Tim14),
(PB4, AltFunc2, pac::Tim13),
(PB5, AltFunc2, pac::Tim12),
(PB6, AltFunc2, pac::Tim11),
(PB7, AltFunc2, pac::Tim10),
(PB8, AltFunc2, pac::Tim9),
(PB9, AltFunc2, pac::Tim8),
(PB10, AltFunc2, pac::Tim7),
(PB11, AltFunc2, pac::Tim6),
(PB12, AltFunc2, pac::Tim5),
(PB13, AltFunc2, pac::Tim4),
(PB14, AltFunc2, pac::Tim3),
(PB15, AltFunc2, pac::Tim2),
(PC0, AltFunc2, pac::Tim1),
(PC1, AltFunc2, pac::Tim0),
(PD0, AltFunc2, pac::Tim0),
(PD1, AltFunc2, pac::Tim1),
(PD2, AltFunc2, pac::Tim2),
(PD3, AltFunc2, pac::Tim3),
(PD4, AltFunc2, pac::Tim4),
(PD5, AltFunc2, pac::Tim5),
(PD6, AltFunc2, pac::Tim6),
(PD7, AltFunc2, pac::Tim7),
(PD8, AltFunc2, pac::Tim8),
(PD9, AltFunc2, pac::Tim9),
(PD10, AltFunc2, pac::Tim10),
(PD11, AltFunc2, pac::Tim11),
(PD12, AltFunc2, pac::Tim12),
(PD13, AltFunc2, pac::Tim13),
(PD14, AltFunc2, pac::Tim14),
(PD15, AltFunc2, pac::Tim15),
(PE0, AltFunc2, pac::Tim16),
(PE1, AltFunc2, pac::Tim17),
(PE2, AltFunc2, pac::Tim18),
(PE3, AltFunc2, pac::Tim19),
(PE4, AltFunc2, pac::Tim20),
(PE5, AltFunc2, pac::Tim21),
(PE6, AltFunc2, pac::Tim22),
(PE7, AltFunc2, pac::Tim23),
(PE8, AltFunc3, pac::Tim16),
(PE9, AltFunc3, pac::Tim17),
(PE10, AltFunc3, pac::Tim18),
(PE11, AltFunc3, pac::Tim19),
(PE12, AltFunc3, pac::Tim20),
(PE13, AltFunc3, pac::Tim21),
(PE14, AltFunc3, pac::Tim22),
(PE15, AltFunc3, pac::Tim23),
(PF0, AltFunc3, pac::Tim0),
(PF1, AltFunc3, pac::Tim1),
(PF2, AltFunc3, pac::Tim2),
(PF3, AltFunc3, pac::Tim3),
(PF4, AltFunc3, pac::Tim4),
(PF5, AltFunc3, pac::Tim5),
(PF6, AltFunc3, pac::Tim6),
(PF7, AltFunc3, pac::Tim7),
(PF8, AltFunc3, pac::Tim8),
(PF9, AltFunc3, pac::Tim9),
(PF10, AltFunc3, pac::Tim10),
(PF11, AltFunc3, pac::Tim11),
(PF12, AltFunc3, pac::Tim12),
(PF13, AltFunc2, pac::Tim19),
(PF14, AltFunc2, pac::Tim20),
(PF15, AltFunc2, pac::Tim21),
(PG0, AltFunc2, pac::Tim22),
(PG1, AltFunc2, pac::Tim23),
(PG2, AltFunc1, pac::Tim9),
(PG3, AltFunc1, pac::Tim10),
(PG6, AltFunc1, pac::Tim12),
);
//==================================================================================================
// Register Interface for TIM registers and TIM pins
//==================================================================================================
/// Clear the reset bit of the TIM, holding it in reset
///
/// # Safety
///
/// Only the bit related to the corresponding TIM peripheral is modified
#[inline]
fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
syscfg
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) })
}
#[inline]
fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
syscfg
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim_id as u32)) })
}
pub type TimRegBlock = pac::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 pac::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 assert_tim_reset(&self, syscfg: &mut pac::Sysconfig) {
assert_tim_reset(syscfg, self.tim_id());
}
#[inline]
#[allow(dead_code)]
fn deassert_time_reset(&self, syscfg: &mut pac::Sysconfig) {
deassert_tim_reset(syscfg, self.tim_id());
}
}
/// 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> {
#[inline(always)]
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,
clock: Hertz,
rst_val: u32,
last_cnt: u32,
listening: bool,
}
#[inline]
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> {
#[inline]
fn tim_id(&self) -> u8 {
TIM::TIM_ID
}
}
impl<Tim: ValidTim> CountdownTimer<Tim> {
/// Create a new countdown timer, but does not start it.
///
/// You can use [Self::start] to start the countdown timer, and you may optionally call
/// [Self::listen] to enable interrupts for the TIM peripheral as well.
pub fn new(syscfg: &mut pac::Sysconfig, tim: Tim, clocks: &Clocks) -> Self {
enable_tim_clk(syscfg, Tim::TIM_ID);
assert_tim_reset(syscfg, Tim::TIM_ID);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_tim_reset(syscfg, Tim::TIM_ID);
CountdownTimer {
tim: unsafe { TimRegister::new(tim) },
clock: Tim::clock(clocks),
rst_val: 0,
curr_freq: 0_u32.Hz(),
listening: false,
last_cnt: 0,
}
}
#[inline]
pub fn start(&mut self, timeout: impl Into<Hertz>) {
self.load(timeout);
self.enable();
}
/// 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) {
self.listening = true;
self.enable_interrupt();
unsafe { enable_interrupt(Tim::IRQ) }
}
/// 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 stop(&mut self) {
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
}
pub fn unlisten(&mut self) {
self.listening = true;
self.disable_interrupt();
disable_interrupt(Tim::IRQ);
}
#[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.clock.raw() / self.curr_freq.raw();
self.set_reload(self.rst_val);
self.set_count(0);
}
#[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());
}
#[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
#[inline]
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
#[inline]
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
#[inline]
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)
});
}
#[inline]
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?;
self.tim
.reg()
.cascade0()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
#[inline]
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?;
self.tim
.reg()
.cascade1()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
#[inline]
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?;
self.tim
.reg()
.cascade2()
.write(|w| unsafe { w.cassel().bits(id) });
Ok(())
}
#[inline]
pub fn curr_freq(&self) -> Hertz {
self.curr_freq
}
#[inline]
pub fn listening(&self) -> bool {
self.listening
}
}
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.clock.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();
}
}
//==================================================================================================
// MS tick implementations
//==================================================================================================
// 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>(
sys_cfg: &mut pac::Sysconfig,
tim: Tim,
clocks: &Clocks,
) -> CountdownTimer<Tim> {
let mut ms_timer = CountdownTimer::new(sys_cfg, tim, clocks);
ms_timer.listen();
ms_timer.start(1000.Hz());
ms_timer
}
/// 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())
}
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
impl<Tim: ValidTim> DelayMs<Tim> {
pub fn new(timer: CountdownTimer<Tim>) -> 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 with [set_up_ms_tick]
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()
}
}

1014
va416xx-hal/src/uart.rs Normal file

File diff suppressed because it is too large Load Diff

118
va416xx-hal/src/wdt.rs Normal file
View File

@ -0,0 +1,118 @@
//! # API for the Watchdog peripheral
//!
//! ## Examples
//!
//! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/wdt.rs)
use crate::time::Hertz;
use crate::{
clock::{Clocks, PeripheralSelect},
pac,
prelude::SyscfgExt,
};
use crate::{disable_interrupt, enable_interrupt};
pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551;
pub struct WdtController {
clock_freq: Hertz,
wdt: pac::WatchDog,
}
/// Enable the watchdog interrupt
///
/// # Safety
///
/// This function is `unsafe` because it can break mask-based critical sections.
#[inline]
pub unsafe fn enable_wdt_interrupts() {
enable_interrupt(pac::Interrupt::WATCHDOG)
}
#[inline]
pub fn disable_wdt_interrupts() {
disable_interrupt(pac::Interrupt::WATCHDOG)
}
impl WdtController {
pub fn new(
&self,
syscfg: &mut pac::Sysconfig,
wdt: pac::WatchDog,
clocks: &Clocks,
wdt_freq_ms: u32,
) -> Self {
Self::start(syscfg, wdt, clocks, wdt_freq_ms)
}
pub fn start(
syscfg: &mut pac::Sysconfig,
wdt: pac::WatchDog,
clocks: &Clocks,
wdt_freq_ms: u32,
) -> Self {
syscfg.enable_peripheral_clock(PeripheralSelect::Watchdog);
// It's done like that in Vorago examples. Not exactly sure why the reset is necessary
// though..
syscfg.assert_periph_reset(PeripheralSelect::Watchdog);
cortex_m::asm::nop();
cortex_m::asm::nop();
syscfg.deassert_periph_reset(PeripheralSelect::Watchdog);
let wdt_clock = clocks.apb2();
let mut wdt_ctrl = Self {
clock_freq: wdt_clock,
wdt,
};
wdt_ctrl.set_freq(wdt_freq_ms);
wdt_ctrl.wdt.wdogcontrol().write(|w| w.inten().set_bit());
wdt_ctrl.feed();
// Unmask the watchdog interrupt
unsafe {
enable_wdt_interrupts();
}
wdt_ctrl
}
#[inline]
pub fn set_freq(&mut self, freq_ms: u32) {
let counter = (self.clock_freq.raw() / 1000) * freq_ms;
self.wdt.wdogload().write(|w| unsafe { w.bits(counter) });
}
#[inline]
pub fn disable_reset(&mut self) {
self.wdt.wdogcontrol().modify(|_, w| w.resen().clear_bit())
}
#[inline]
pub fn enable_reset(&mut self) {
self.wdt.wdogcontrol().modify(|_, w| w.resen().set_bit())
}
#[inline]
pub fn counter(&self) -> u32 {
self.wdt.wdogvalue().read().bits()
}
#[inline]
pub fn feed(&self) {
self.wdt.wdogintclr().write(|w| unsafe { w.bits(1) });
}
#[inline]
pub fn lock(&self) {
self.wdt.wdoglock().write(|w| unsafe { w.bits(0) });
}
#[inline]
pub fn unlock(&self) {
self.wdt
.wdoglock()
.write(|w| unsafe { w.bits(WDT_UNLOCK_VALUE) });
}
#[inline]
pub fn is_locked(&self) -> bool {
self.wdt.wdogload().read().bits() == 1
}
}

2
va416xx/.github/bors.toml vendored Normal file
View File

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

20
va416xx/.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 }}

64
va416xx/.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,64 @@
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: thumbv7em-none-eabihf
override: true
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: check
args: --target thumbv7em-none-eabihf
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: thumbv7em-none-eabihf
override: true
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: clippy
args: --target thumbv7em-none-eabihf -- -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

2
va416xx/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

21
va416xx/CHANGELOG.md Normal file
View File

@ -0,0 +1,21 @@
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.1.1]
- Clippy issue fixed by regenerating PAC with patched `svd2rust`:
https://github.com/rust-embedded/svd2rust/pull/558
## [v0.1.0]
- Clippy currently complains about unsound code which should still work.
Related issue: https://github.com/rust-embedded/svd2rust/issues/557
Clippy is disabled in CI/CD for now.
- Initial release

29
va416xx/Cargo.toml Normal file
View File

@ -0,0 +1,29 @@
[package]
name = "va416xx"
version = "0.1.1"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "PAC for the Vorago VA416xx family of MCUs"
homepage = "https://egit.irs.uni-stuttgart.de/rust/va416xx"
repository = "https://egit.irs.uni-stuttgart.de/rust/va416xx"
license = "Apache-2.0"
keywords = ["no-std", "arm", "cortex-m", "vorago", "va416xx"]
categories = ["embedded", "no-std", "hardware-support"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[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"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--generate-link-to-definition"]

201
va416xx/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
va416xx/NOTICE Normal file
View File

@ -0,0 +1,3 @@
Rust Peripheral Access Crate (PAC) crate for the Vorago VA416xx family of MCUs
This software contains code developed at the University of Stuttgart.

58
va416xx/README.md Normal file
View File

@ -0,0 +1,58 @@
[![Crates.io](https://img.shields.io/crates/v/va416xx)](https://crates.io/crates/va416xx)
[![docs.rs](https://img.shields.io/docsrs/va416xx)](https://docs.rs/va416xx)
# PAC for the Vorago VA416xx microcontroller family
This repository contains the Peripheral Access Crate (PAC) for
Voragos VA416xx series of Cortex-M4 based microcontrollers.
The crate was generated using [`svd2rust`](https://github.com/rust-embedded/svd2rust).
## Usage
To use this crate, add this to your `Cargo.toml`
```toml
[dependencies.va416xx]
version = "<MostRecentVersion>"
features = ["rt"]
```
The `rt` feature is optional and recommended. It brings in support for `cortex-m-rt`.
For full details on the autgenerated API, you can read the
[svd2rust documentation](https://docs.rs/svd2rust/latest/svd2rust/).
## 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/va416xx-patch.yml` file.
```sh
svdtools patch svd/va416xx-patch.yml
```
3. Use `svd2rust` to generate the Rust library
```sh
svd2rust -i svd/va416xx.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 thumbv7em-none-eabihf && \
rustup component add rustfmt clippy

39
va416xx/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 thumbv7em-none-eabihf'
}
}
}
}

17
va416xx/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");
}

197
va416xx/device.x Normal file
View File

@ -0,0 +1,197 @@
PROVIDE(U0 = DefaultHandler);
PROVIDE(U1 = DefaultHandler);
PROVIDE(U2 = DefaultHandler);
PROVIDE(U3 = DefaultHandler);
PROVIDE(U4 = DefaultHandler);
PROVIDE(U5 = DefaultHandler);
PROVIDE(U6 = DefaultHandler);
PROVIDE(U7 = DefaultHandler);
PROVIDE(U8 = DefaultHandler);
PROVIDE(U9 = DefaultHandler);
PROVIDE(U10 = DefaultHandler);
PROVIDE(U11 = DefaultHandler);
PROVIDE(U12 = DefaultHandler);
PROVIDE(U13 = DefaultHandler);
PROVIDE(U14 = DefaultHandler);
PROVIDE(U15 = DefaultHandler);
PROVIDE(SPI0_TX = DefaultHandler);
PROVIDE(SPI0_RX = DefaultHandler);
PROVIDE(SPI1_TX = DefaultHandler);
PROVIDE(SPI1_RX = DefaultHandler);
PROVIDE(SPI2_TX = DefaultHandler);
PROVIDE(SPI2_RX = DefaultHandler);
PROVIDE(SPI3_TX = DefaultHandler);
PROVIDE(SPI3_RX = DefaultHandler);
PROVIDE(UART0_TX = DefaultHandler);
PROVIDE(UART0_RX = DefaultHandler);
PROVIDE(UART1_TX = DefaultHandler);
PROVIDE(UART1_RX = DefaultHandler);
PROVIDE(UART2_TX = DefaultHandler);
PROVIDE(UART2_RX = DefaultHandler);
PROVIDE(I2C0_MS = DefaultHandler);
PROVIDE(I2C0_SL = DefaultHandler);
PROVIDE(I2C1_MS = DefaultHandler);
PROVIDE(I2C1_SL = DefaultHandler);
PROVIDE(I2C2_MS = DefaultHandler);
PROVIDE(I2C2_SL = DefaultHandler);
PROVIDE(Ethernet = DefaultHandler);
PROVIDE(U37 = DefaultHandler);
PROVIDE(SpW = DefaultHandler);
PROVIDE(U39 = DefaultHandler);
PROVIDE(DAC0 = DefaultHandler);
PROVIDE(DAC1 = DefaultHandler);
PROVIDE(TRNG = DefaultHandler);
PROVIDE(DMA_ERROR = DefaultHandler);
PROVIDE(ADC = DefaultHandler);
PROVIDE(LoCLK = DefaultHandler);
PROVIDE(LVD = DefaultHandler);
PROVIDE(WATCHDOG = DefaultHandler);
PROVIDE(TIM0 = DefaultHandler);
PROVIDE(TIM1 = DefaultHandler);
PROVIDE(TIM2 = DefaultHandler);
PROVIDE(TIM3 = DefaultHandler);
PROVIDE(TIM4 = DefaultHandler);
PROVIDE(TIM5 = DefaultHandler);
PROVIDE(TIM6 = DefaultHandler);
PROVIDE(TIM7 = DefaultHandler);
PROVIDE(TIM8 = DefaultHandler);
PROVIDE(TIM9 = DefaultHandler);
PROVIDE(TIM10 = DefaultHandler);
PROVIDE(TIM11 = DefaultHandler);
PROVIDE(TIM12 = DefaultHandler);
PROVIDE(TIM13 = DefaultHandler);
PROVIDE(TIM14 = DefaultHandler);
PROVIDE(TIM15 = DefaultHandler);
PROVIDE(TIM16 = DefaultHandler);
PROVIDE(TIM17 = DefaultHandler);
PROVIDE(TIM18 = DefaultHandler);
PROVIDE(TIM19 = DefaultHandler);
PROVIDE(TIM20 = DefaultHandler);
PROVIDE(TIM21 = DefaultHandler);
PROVIDE(TIM22 = DefaultHandler);
PROVIDE(TIM23 = DefaultHandler);
PROVIDE(CAN0 = DefaultHandler);
PROVIDE(U73 = DefaultHandler);
PROVIDE(CAN1 = DefaultHandler);
PROVIDE(U75 = DefaultHandler);
PROVIDE(EDAC_MBE = DefaultHandler);
PROVIDE(EDAC_SBE = DefaultHandler);
PROVIDE(PORTA0 = DefaultHandler);
PROVIDE(PORTA1 = DefaultHandler);
PROVIDE(PORTA2 = DefaultHandler);
PROVIDE(PORTA3 = DefaultHandler);
PROVIDE(PORTA4 = DefaultHandler);
PROVIDE(PORTA5 = DefaultHandler);
PROVIDE(PORTA6 = DefaultHandler);
PROVIDE(PORTA7 = DefaultHandler);
PROVIDE(PORTA8 = DefaultHandler);
PROVIDE(PORTA9 = DefaultHandler);
PROVIDE(PORTA10 = DefaultHandler);
PROVIDE(PORTA11 = DefaultHandler);
PROVIDE(PORTA12 = DefaultHandler);
PROVIDE(PORTA13 = DefaultHandler);
PROVIDE(PORTA14 = DefaultHandler);
PROVIDE(PORTA15 = DefaultHandler);
PROVIDE(PORTB0 = DefaultHandler);
PROVIDE(PORTB1 = DefaultHandler);
PROVIDE(PORTB2 = DefaultHandler);
PROVIDE(PORTB3 = DefaultHandler);
PROVIDE(PORTB4 = DefaultHandler);
PROVIDE(PORTB5 = DefaultHandler);
PROVIDE(PORTB6 = DefaultHandler);
PROVIDE(PORTB7 = DefaultHandler);
PROVIDE(PORTB8 = DefaultHandler);
PROVIDE(PORTB9 = DefaultHandler);
PROVIDE(PORTB10 = DefaultHandler);
PROVIDE(PORTB11 = DefaultHandler);
PROVIDE(PORTB12 = DefaultHandler);
PROVIDE(PORTB13 = DefaultHandler);
PROVIDE(PORTB14 = DefaultHandler);
PROVIDE(PORTB15 = DefaultHandler);
PROVIDE(PORTC0 = DefaultHandler);
PROVIDE(PORTC1 = DefaultHandler);
PROVIDE(PORTC2 = DefaultHandler);
PROVIDE(PORTC3 = DefaultHandler);
PROVIDE(PORTC4 = DefaultHandler);
PROVIDE(PORTC5 = DefaultHandler);
PROVIDE(PORTC6 = DefaultHandler);
PROVIDE(PORTC7 = DefaultHandler);
PROVIDE(PORTC8 = DefaultHandler);
PROVIDE(PORTC9 = DefaultHandler);
PROVIDE(PORTC10 = DefaultHandler);
PROVIDE(PORTC11 = DefaultHandler);
PROVIDE(PORTC12 = DefaultHandler);
PROVIDE(PORTC13 = DefaultHandler);
PROVIDE(PORTC14 = DefaultHandler);
PROVIDE(PORTC15 = DefaultHandler);
PROVIDE(PORTD0 = DefaultHandler);
PROVIDE(PORTD1 = DefaultHandler);
PROVIDE(PORTD2 = DefaultHandler);
PROVIDE(PORTD3 = DefaultHandler);
PROVIDE(PORTD4 = DefaultHandler);
PROVIDE(PORTD5 = DefaultHandler);
PROVIDE(PORTD6 = DefaultHandler);
PROVIDE(PORTD7 = DefaultHandler);
PROVIDE(PORTD8 = DefaultHandler);
PROVIDE(PORTD9 = DefaultHandler);
PROVIDE(PORTD10 = DefaultHandler);
PROVIDE(PORTD11 = DefaultHandler);
PROVIDE(PORTD12 = DefaultHandler);
PROVIDE(PORTD13 = DefaultHandler);
PROVIDE(PORTD14 = DefaultHandler);
PROVIDE(PORTD15 = DefaultHandler);
PROVIDE(PORTE0 = DefaultHandler);
PROVIDE(PORTE1 = DefaultHandler);
PROVIDE(PORTE2 = DefaultHandler);
PROVIDE(PORTE3 = DefaultHandler);
PROVIDE(PORTE4 = DefaultHandler);
PROVIDE(PORTE5 = DefaultHandler);
PROVIDE(PORTE6 = DefaultHandler);
PROVIDE(PORTE7 = DefaultHandler);
PROVIDE(PORTE8 = DefaultHandler);
PROVIDE(PORTE9 = DefaultHandler);
PROVIDE(PORTE10 = DefaultHandler);
PROVIDE(PORTE11 = DefaultHandler);
PROVIDE(PORTE12 = DefaultHandler);
PROVIDE(PORTE13 = DefaultHandler);
PROVIDE(PORTE14 = DefaultHandler);
PROVIDE(PORTE15 = DefaultHandler);
PROVIDE(PORTF0 = DefaultHandler);
PROVIDE(PORTF1 = DefaultHandler);
PROVIDE(PORTF2 = DefaultHandler);
PROVIDE(PORTF3 = DefaultHandler);
PROVIDE(PORTF4 = DefaultHandler);
PROVIDE(PORTF5 = DefaultHandler);
PROVIDE(PORTF6 = DefaultHandler);
PROVIDE(PORTF7 = DefaultHandler);
PROVIDE(PORTF8 = DefaultHandler);
PROVIDE(PORTF9 = DefaultHandler);
PROVIDE(PORTF10 = DefaultHandler);
PROVIDE(PORTF11 = DefaultHandler);
PROVIDE(PORTF12 = DefaultHandler);
PROVIDE(PORTF13 = DefaultHandler);
PROVIDE(PORTF14 = DefaultHandler);
PROVIDE(PORTF15 = DefaultHandler);
PROVIDE(DMA_ACTIVE0 = DefaultHandler);
PROVIDE(DMA_ACTIVE1 = DefaultHandler);
PROVIDE(DMA_ACTIVE2 = DefaultHandler);
PROVIDE(DMA_ACTIVE3 = DefaultHandler);
PROVIDE(DMA_DONE0 = DefaultHandler);
PROVIDE(DMA_DONE1 = DefaultHandler);
PROVIDE(DMA_DONE2 = DefaultHandler);
PROVIDE(DMA_DONE3 = DefaultHandler);
PROVIDE(I2C0_MS_RX = DefaultHandler);
PROVIDE(I2C0_MS_TX = DefaultHandler);
PROVIDE(I2C0_SL_RX = DefaultHandler);
PROVIDE(I2C0_SL_TX = DefaultHandler);
PROVIDE(I2C1_MS_RX = DefaultHandler);
PROVIDE(I2C1_MS_TX = DefaultHandler);
PROVIDE(I2C1_SL_RX = DefaultHandler);
PROVIDE(I2C1_SL_TX = DefaultHandler);
PROVIDE(I2C2_MS_RX = DefaultHandler);
PROVIDE(I2C2_MS_TX = DefaultHandler);
PROVIDE(I2C2_SL_RX = DefaultHandler);
PROVIDE(I2C2_SL_TX = DefaultHandler);
PROVIDE(FPU = DefaultHandler);
PROVIDE(TXEV = DefaultHandler);

42
va416xx/gen-helper.sh Executable file
View File

@ -0,0 +1,42 @@
#!/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/va416xx-patch.yml
${svd2rust_bin} --reexport-interrupt -i svd/va416xx.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

127
va416xx/src/adc.rs Normal file
View File

@ -0,0 +1,127 @@
#[repr(C)]
#[doc = "Register block"]
pub struct RegisterBlock {
ctrl: Ctrl,
fifo_data: FifoData,
status: Status,
irq_enb: IrqEnb,
irq_raw: IrqRaw,
irq_end: IrqEnd,
irq_clr: IrqClr,
rxfifoirqtrg: Rxfifoirqtrg,
fifo_clr: FifoClr,
_reserved9: [u8; 0x0fd8],
perid: Perid,
}
impl RegisterBlock {
#[doc = "0x00 - Control Register"]
#[inline(always)]
pub const fn ctrl(&self) -> &Ctrl {
&self.ctrl
}
#[doc = "0x04 - FIFO data"]
#[inline(always)]
pub const fn fifo_data(&self) -> &FifoData {
&self.fifo_data
}
#[doc = "0x08 - Status"]
#[inline(always)]
pub const fn status(&self) -> &Status {
&self.status
}
#[doc = "0x0c - Interrupt Enable"]
#[inline(always)]
pub const fn irq_enb(&self) -> &IrqEnb {
&self.irq_enb
}
#[doc = "0x10 - Raw Interrupt Status"]
#[inline(always)]
pub const fn irq_raw(&self) -> &IrqRaw {
&self.irq_raw
}
#[doc = "0x14 - Enabled Interrupt Status"]
#[inline(always)]
pub const fn irq_end(&self) -> &IrqEnd {
&self.irq_end
}
#[doc = "0x18 - Clear Interrupt"]
#[inline(always)]
pub const fn irq_clr(&self) -> &IrqClr {
&self.irq_clr
}
#[doc = "0x1c - Receive FIFO Interrupt Trigger Value"]
#[inline(always)]
pub const fn rxfifoirqtrg(&self) -> &Rxfifoirqtrg {
&self.rxfifoirqtrg
}
#[doc = "0x20 - FIFO Clear"]
#[inline(always)]
pub const fn fifo_clr(&self) -> &FifoClr {
&self.fifo_clr
}
#[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 = "FIFO_DATA (r) register accessor: FIFO data\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`fifo_data::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@fifo_data`]
module"]
#[doc(alias = "FIFO_DATA")]
pub type FifoData = crate::Reg<fifo_data::FifoDataSpec>;
#[doc = "FIFO data"]
pub mod fifo_data;
#[doc = "STATUS (r) register accessor: Status\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 = "Status"]
pub mod status;
#[doc = "IRQ_ENB (rw) register accessor: Interrupt Enable\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"]
pub mod irq_enb;
#[doc = "IRQ_RAW (r) register accessor: Raw Interrupt Status\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_raw::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_raw`]
module"]
#[doc(alias = "IRQ_RAW")]
pub type IrqRaw = crate::Reg<irq_raw::IrqRawSpec>;
#[doc = "Raw Interrupt Status"]
pub mod irq_raw;
#[doc = "IRQ_END (r) register accessor: Enabled Interrupt Status\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_end::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_end`]
module"]
#[doc(alias = "IRQ_END")]
pub type IrqEnd = crate::Reg<irq_end::IrqEndSpec>;
#[doc = "Enabled Interrupt Status"]
pub mod irq_end;
#[doc = "IRQ_CLR (w) register accessor: Clear Interrupt\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 [`irq_clr::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_clr`]
module"]
#[doc(alias = "IRQ_CLR")]
pub type IrqClr = crate::Reg<irq_clr::IrqClrSpec>;
#[doc = "Clear Interrupt"]
pub mod irq_clr;
#[doc = "RXFIFOIRQTRG (rw) register accessor: Receive FIFO Interrupt Trigger Value\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 = "Receive FIFO Interrupt Trigger Value"]
pub mod rxfifoirqtrg;
#[doc = "FIFO_CLR (rw) register accessor: FIFO Clear\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`fifo_clr::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 [`fifo_clr::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@fifo_clr`]
module"]
#[doc(alias = "FIFO_CLR")]
pub type FifoClr = crate::Reg<fifo_clr::FifoClrSpec>;
#[doc = "FIFO Clear"]
pub mod fifo_clr;
#[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;

115
va416xx/src/adc/ctrl.rs Normal file
View File

@ -0,0 +1,115 @@
#[doc = "Register `CTRL` reader"]
pub type R = crate::R<CtrlSpec>;
#[doc = "Register `CTRL` writer"]
pub type W = crate::W<CtrlSpec>;
#[doc = "Field `CHAN_EN` reader - Enables the channel for data collection"]
pub type ChanEnR = crate::FieldReader<u16>;
#[doc = "Field `CHAN_EN` writer - Enables the channel for data collection"]
pub type ChanEnW<'a, REG> = crate::FieldWriter<'a, REG, 16, u16>;
#[doc = "Field `CHAN_TAG_EN` reader - Enables the channel tag to be saved with the ADC data"]
pub type ChanTagEnR = crate::BitReader;
#[doc = "Field `CHAN_TAG_EN` writer - Enables the channel tag to be saved with the ADC data"]
pub type ChanTagEnW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `SWEEP_EN` reader - ADC data acquisition for all enabled channel"]
pub type SweepEnR = crate::BitReader;
#[doc = "Field `SWEEP_EN` writer - ADC data acquisition for all enabled channel"]
pub type SweepEnW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `EXT_TRIG_EN` reader - Allows the external trigger to start analog acquisition"]
pub type ExtTrigEnR = crate::BitReader;
#[doc = "Field `EXT_TRIG_EN` writer - Allows the external trigger to start analog acquisition"]
pub type ExtTrigEnW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `MANUAL_TRIG` reader - Starts analog acquisition"]
pub type ManualTrigR = crate::BitReader;
#[doc = "Field `MANUAL_TRIG` writer - Starts analog acquisition"]
pub type ManualTrigW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `CONV_CNT` reader - Conversion count describes the number of conversions to be applied for triggers/sweeps. (N+1 conversions)"]
pub type ConvCntR = crate::FieldReader;
#[doc = "Field `CONV_CNT` writer - Conversion count describes the number of conversions to be applied for triggers/sweeps. (N+1 conversions)"]
pub type ConvCntW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:15 - Enables the channel for data collection"]
#[inline(always)]
pub fn chan_en(&self) -> ChanEnR {
ChanEnR::new((self.bits & 0xffff) as u16)
}
#[doc = "Bit 16 - Enables the channel tag to be saved with the ADC data"]
#[inline(always)]
pub fn chan_tag_en(&self) -> ChanTagEnR {
ChanTagEnR::new(((self.bits >> 16) & 1) != 0)
}
#[doc = "Bit 17 - ADC data acquisition for all enabled channel"]
#[inline(always)]
pub fn sweep_en(&self) -> SweepEnR {
SweepEnR::new(((self.bits >> 17) & 1) != 0)
}
#[doc = "Bit 18 - Allows the external trigger to start analog acquisition"]
#[inline(always)]
pub fn ext_trig_en(&self) -> ExtTrigEnR {
ExtTrigEnR::new(((self.bits >> 18) & 1) != 0)
}
#[doc = "Bit 19 - Starts analog acquisition"]
#[inline(always)]
pub fn manual_trig(&self) -> ManualTrigR {
ManualTrigR::new(((self.bits >> 19) & 1) != 0)
}
#[doc = "Bits 20:23 - Conversion count describes the number of conversions to be applied for triggers/sweeps. (N+1 conversions)"]
#[inline(always)]
pub fn conv_cnt(&self) -> ConvCntR {
ConvCntR::new(((self.bits >> 20) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:15 - Enables the channel for data collection"]
#[inline(always)]
#[must_use]
pub fn chan_en(&mut self) -> ChanEnW<CtrlSpec> {
ChanEnW::new(self, 0)
}
#[doc = "Bit 16 - Enables the channel tag to be saved with the ADC data"]
#[inline(always)]
#[must_use]
pub fn chan_tag_en(&mut self) -> ChanTagEnW<CtrlSpec> {
ChanTagEnW::new(self, 16)
}
#[doc = "Bit 17 - ADC data acquisition for all enabled channel"]
#[inline(always)]
#[must_use]
pub fn sweep_en(&mut self) -> SweepEnW<CtrlSpec> {
SweepEnW::new(self, 17)
}
#[doc = "Bit 18 - Allows the external trigger to start analog acquisition"]
#[inline(always)]
#[must_use]
pub fn ext_trig_en(&mut self) -> ExtTrigEnW<CtrlSpec> {
ExtTrigEnW::new(self, 18)
}
#[doc = "Bit 19 - Starts analog acquisition"]
#[inline(always)]
#[must_use]
pub fn manual_trig(&mut self) -> ManualTrigW<CtrlSpec> {
ManualTrigW::new(self, 19)
}
#[doc = "Bits 20:23 - Conversion count describes the number of conversions to be applied for triggers/sweeps. (N+1 conversions)"]
#[inline(always)]
#[must_use]
pub fn conv_cnt(&mut self) -> ConvCntW<CtrlSpec> {
ConvCntW::new(self, 20)
}
}
#[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;
}

View File

@ -0,0 +1,31 @@
#[doc = "Register `FIFO_CLR` reader"]
pub type R = crate::R<FifoClrSpec>;
#[doc = "Register `FIFO_CLR` writer"]
pub type W = crate::W<FifoClrSpec>;
#[doc = "Field `FIFO_CLR` writer - Clears the ADC FIFO. Always reads 0"]
pub type FifoClrW<'a, REG> = crate::BitWriter<'a, REG>;
impl W {
#[doc = "Bit 0 - Clears the ADC FIFO. Always reads 0"]
#[inline(always)]
#[must_use]
pub fn fifo_clr(&mut self) -> FifoClrW<FifoClrSpec> {
FifoClrW::new(self, 0)
}
}
#[doc = "FIFO Clear\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`fifo_clr::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 [`fifo_clr::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct FifoClrSpec;
impl crate::RegisterSpec for FifoClrSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`fifo_clr::R`](R) reader structure"]
impl crate::Readable for FifoClrSpec {}
#[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;
}

View File

@ -0,0 +1,29 @@
#[doc = "Register `FIFO_DATA` reader"]
pub type R = crate::R<FifoDataSpec>;
#[doc = "Field `ADC_DATA` reader - ADC acquisition data from the FIFO"]
pub type AdcDataR = crate::FieldReader<u16>;
#[doc = "Field `CHAN_TAG` reader - If enabled, this will include the number of the channel corresponding to the measurement"]
pub type ChanTagR = crate::FieldReader;
impl R {
#[doc = "Bits 0:11 - ADC acquisition data from the FIFO"]
#[inline(always)]
pub fn adc_data(&self) -> AdcDataR {
AdcDataR::new((self.bits & 0x0fff) as u16)
}
#[doc = "Bits 12:15 - If enabled, this will include the number of the channel corresponding to the measurement"]
#[inline(always)]
pub fn chan_tag(&self) -> ChanTagR {
ChanTagR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
#[doc = "FIFO data\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`fifo_data::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct FifoDataSpec;
impl crate::RegisterSpec for FifoDataSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`fifo_data::R`](R) reader structure"]
impl crate::Readable for FifoDataSpec {}
#[doc = "`reset()` method sets FIFO_DATA to value 0"]
impl crate::Resettable for FifoDataSpec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,51 @@
#[doc = "Register `IRQ_CLR` writer"]
pub type W = crate::W<IrqClrSpec>;
#[doc = "Field `FIFO_OFLOW` writer - Clears the FIFO overflow interrupt status. Always reads 0"]
pub type FifoOflowW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `FIFO_UFLOW` writer - Clears the FIFO underflow interrupt status. Always reads 0"]
pub type FifoUflowW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `ADC_DONE` writer - Clears the ADC done interrupt status. Always reads 0"]
pub type AdcDoneW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `TRIG_ERROR` writer - Clears the trigger error interrupt status. Always reads 0"]
pub type TrigErrorW<'a, REG> = crate::BitWriter<'a, REG>;
impl W {
#[doc = "Bit 0 - Clears the FIFO overflow interrupt status. Always reads 0"]
#[inline(always)]
#[must_use]
pub fn fifo_oflow(&mut self) -> FifoOflowW<IrqClrSpec> {
FifoOflowW::new(self, 0)
}
#[doc = "Bit 1 - Clears the FIFO underflow interrupt status. Always reads 0"]
#[inline(always)]
#[must_use]
pub fn fifo_uflow(&mut self) -> FifoUflowW<IrqClrSpec> {
FifoUflowW::new(self, 1)
}
#[doc = "Bit 2 - Clears the ADC done interrupt status. Always reads 0"]
#[inline(always)]
#[must_use]
pub fn adc_done(&mut self) -> AdcDoneW<IrqClrSpec> {
AdcDoneW::new(self, 2)
}
#[doc = "Bit 3 - Clears the trigger error interrupt status. Always reads 0"]
#[inline(always)]
#[must_use]
pub fn trig_error(&mut self) -> TrigErrorW<IrqClrSpec> {
TrigErrorW::new(self, 3)
}
}
#[doc = "Clear Interrupt\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 [`irq_clr::W`](W). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct IrqClrSpec;
impl crate::RegisterSpec for IrqClrSpec {
type Ux = u32;
}
#[doc = "`write(|w| ..)` method takes [`irq_clr::W`](W) writer structure"]
impl crate::Writable for IrqClrSpec {
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_CLR to value 0"]
impl crate::Resettable for IrqClrSpec {
const RESET_VALUE: u32 = 0;
}

130
va416xx/src/adc/irq_enb.rs Normal file
View File

@ -0,0 +1,130 @@
#[doc = "Register `IRQ_ENB` reader"]
pub type R = crate::R<IrqEnbSpec>;
#[doc = "Register `IRQ_ENB` writer"]
pub type W = crate::W<IrqEnbSpec>;
#[doc = "Field `FIFO_EMPTY` reader - Enables the interrupt for FIFO empty"]
pub type FifoEmptyR = crate::BitReader;
#[doc = "Field `FIFO_EMPTY` writer - Enables the interrupt for FIFO empty"]
pub type FifoEmptyW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `FIFO_FULL` reader - Enables the interrupt for FIFO full"]
pub type FifoFullR = crate::BitReader;
#[doc = "Field `FIFO_FULL` writer - Enables the interrupt for FIFO full"]
pub type FifoFullW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `FIFO_OFLOW` reader - Enables the interrupt for a FIFO overflow"]
pub type FifoOflowR = crate::BitReader;
#[doc = "Field `FIFO_OFLOW` writer - Enables the interrupt for a FIFO overflow"]
pub type FifoOflowW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `FIFO_UFLOW` reader - Enables the interrupt for a FIFO underflow"]
pub type FifoUflowR = crate::BitReader;
#[doc = "Field `FIFO_UFLOW` writer - Enables the interrupt for a FIFO underflow"]
pub type FifoUflowW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `ADC_DONE` reader - Enables the interrupt for an ADC data acquisition completion"]
pub type AdcDoneR = crate::BitReader;
#[doc = "Field `ADC_DONE` writer - Enables the interrupt for an ADC data acquisition completion"]
pub type AdcDoneW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `TRIG_ERROR` reader - Enables the interrupt for a trigger error"]
pub type TrigErrorR = crate::BitReader;
#[doc = "Field `TRIG_ERROR` writer - Enables the interrupt for a trigger error"]
pub type TrigErrorW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `FIFO_DEPTH_TRIG` reader - Enables the interrupt for the FIFO entry count meets or exceeds the trigger level"]
pub type FifoDepthTrigR = crate::BitReader;
#[doc = "Field `FIFO_DEPTH_TRIG` writer - Enables the interrupt for the FIFO entry count meets or exceeds the trigger level"]
pub type FifoDepthTrigW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bit 0 - Enables the interrupt for FIFO empty"]
#[inline(always)]
pub fn fifo_empty(&self) -> FifoEmptyR {
FifoEmptyR::new((self.bits & 1) != 0)
}
#[doc = "Bit 1 - Enables the interrupt for FIFO full"]
#[inline(always)]
pub fn fifo_full(&self) -> FifoFullR {
FifoFullR::new(((self.bits >> 1) & 1) != 0)
}
#[doc = "Bit 2 - Enables the interrupt for a FIFO overflow"]
#[inline(always)]
pub fn fifo_oflow(&self) -> FifoOflowR {
FifoOflowR::new(((self.bits >> 2) & 1) != 0)
}
#[doc = "Bit 3 - Enables the interrupt for a FIFO underflow"]
#[inline(always)]
pub fn fifo_uflow(&self) -> FifoUflowR {
FifoUflowR::new(((self.bits >> 3) & 1) != 0)
}
#[doc = "Bit 4 - Enables the interrupt for an ADC data acquisition completion"]
#[inline(always)]
pub fn adc_done(&self) -> AdcDoneR {
AdcDoneR::new(((self.bits >> 4) & 1) != 0)
}
#[doc = "Bit 5 - Enables the interrupt for a trigger error"]
#[inline(always)]
pub fn trig_error(&self) -> TrigErrorR {
TrigErrorR::new(((self.bits >> 5) & 1) != 0)
}
#[doc = "Bit 6 - Enables the interrupt for the FIFO entry count meets or exceeds the trigger level"]
#[inline(always)]
pub fn fifo_depth_trig(&self) -> FifoDepthTrigR {
FifoDepthTrigR::new(((self.bits >> 6) & 1) != 0)
}
}
impl W {
#[doc = "Bit 0 - Enables the interrupt for FIFO empty"]
#[inline(always)]
#[must_use]
pub fn fifo_empty(&mut self) -> FifoEmptyW<IrqEnbSpec> {
FifoEmptyW::new(self, 0)
}
#[doc = "Bit 1 - Enables the interrupt for FIFO full"]
#[inline(always)]
#[must_use]
pub fn fifo_full(&mut self) -> FifoFullW<IrqEnbSpec> {
FifoFullW::new(self, 1)
}
#[doc = "Bit 2 - Enables the interrupt for a FIFO overflow"]
#[inline(always)]
#[must_use]
pub fn fifo_oflow(&mut self) -> FifoOflowW<IrqEnbSpec> {
FifoOflowW::new(self, 2)
}
#[doc = "Bit 3 - Enables the interrupt for a FIFO underflow"]
#[inline(always)]
#[must_use]
pub fn fifo_uflow(&mut self) -> FifoUflowW<IrqEnbSpec> {
FifoUflowW::new(self, 3)
}
#[doc = "Bit 4 - Enables the interrupt for an ADC data acquisition completion"]
#[inline(always)]
#[must_use]
pub fn adc_done(&mut self) -> AdcDoneW<IrqEnbSpec> {
AdcDoneW::new(self, 4)
}
#[doc = "Bit 5 - Enables the interrupt for a trigger error"]
#[inline(always)]
#[must_use]
pub fn trig_error(&mut self) -> TrigErrorW<IrqEnbSpec> {
TrigErrorW::new(self, 5)
}
#[doc = "Bit 6 - Enables the interrupt for the FIFO entry count meets or exceeds the trigger level"]
#[inline(always)]
#[must_use]
pub fn fifo_depth_trig(&mut self) -> FifoDepthTrigW<IrqEnbSpec> {
FifoDepthTrigW::new(self, 6)
}
}
#[doc = "Interrupt Enable\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;
}

View File

@ -0,0 +1,64 @@
#[doc = "Register `IRQ_END` reader"]
pub type R = crate::R<IrqEndSpec>;
#[doc = "Field `FIFO_EMPTY` reader - Indicates the FIFO is empty and the interrupt is enabled"]
pub type FifoEmptyR = crate::BitReader;
#[doc = "Field `FIFO_FULL` reader - Indicates the FIFO is full and the interrupt is enabled"]
pub type FifoFullR = crate::BitReader;
#[doc = "Field `FIFO_OFLOW` reader - Indicates a FIFO overflow occurred and the interrupt is enabled"]
pub type FifoOflowR = crate::BitReader;
#[doc = "Field `FIFO_UFLOW` reader - Indicates a FIFO underflow occurred and the interrupt is enabled"]
pub type FifoUflowR = crate::BitReader;
#[doc = "Field `ADC_DONE` reader - Indicates that a ADC conversion is done and the interrupt is enabled"]
pub type AdcDoneR = crate::BitReader;
#[doc = "Field `TRIG_ERROR` reader - Indicates a manual or external trigger occurred when the ADC was BUSY doing a conversion and the interrupt is enabled"]
pub type TrigErrorR = crate::BitReader;
#[doc = "Field `FIFO_DEPTH_TRIG` reader - Indicates the interrupt for the FIFO entry count meets or exceeds the trigger level"]
pub type FifoDepthTrigR = crate::BitReader;
impl R {
#[doc = "Bit 0 - Indicates the FIFO is empty and the interrupt is enabled"]
#[inline(always)]
pub fn fifo_empty(&self) -> FifoEmptyR {
FifoEmptyR::new((self.bits & 1) != 0)
}
#[doc = "Bit 1 - Indicates the FIFO is full and the interrupt is enabled"]
#[inline(always)]
pub fn fifo_full(&self) -> FifoFullR {
FifoFullR::new(((self.bits >> 1) & 1) != 0)
}
#[doc = "Bit 2 - Indicates a FIFO overflow occurred and the interrupt is enabled"]
#[inline(always)]
pub fn fifo_oflow(&self) -> FifoOflowR {
FifoOflowR::new(((self.bits >> 2) & 1) != 0)
}
#[doc = "Bit 3 - Indicates a FIFO underflow occurred and the interrupt is enabled"]
#[inline(always)]
pub fn fifo_uflow(&self) -> FifoUflowR {
FifoUflowR::new(((self.bits >> 3) & 1) != 0)
}
#[doc = "Bit 4 - Indicates that a ADC conversion is done and the interrupt is enabled"]
#[inline(always)]
pub fn adc_done(&self) -> AdcDoneR {
AdcDoneR::new(((self.bits >> 4) & 1) != 0)
}
#[doc = "Bit 5 - Indicates a manual or external trigger occurred when the ADC was BUSY doing a conversion and the interrupt is enabled"]
#[inline(always)]
pub fn trig_error(&self) -> TrigErrorR {
TrigErrorR::new(((self.bits >> 5) & 1) != 0)
}
#[doc = "Bit 6 - Indicates the interrupt for the FIFO entry count meets or exceeds the trigger level"]
#[inline(always)]
pub fn fifo_depth_trig(&self) -> FifoDepthTrigR {
FifoDepthTrigR::new(((self.bits >> 6) & 1) != 0)
}
}
#[doc = "Enabled Interrupt Status\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_end::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct IrqEndSpec;
impl crate::RegisterSpec for IrqEndSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`irq_end::R`](R) reader structure"]
impl crate::Readable for IrqEndSpec {}
#[doc = "`reset()` method sets IRQ_END to value 0"]
impl crate::Resettable for IrqEndSpec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,64 @@
#[doc = "Register `IRQ_RAW` reader"]
pub type R = crate::R<IrqRawSpec>;
#[doc = "Field `FIFO_EMPTY` reader - Indicates the FIFO is empty"]
pub type FifoEmptyR = crate::BitReader;
#[doc = "Field `FIFO_FULL` reader - Indicates the FIFO is full"]
pub type FifoFullR = crate::BitReader;
#[doc = "Field `FIFO_OFLOW` reader - Indicates a FIFO overflow occurred (FIFO was full when new data was written)"]
pub type FifoOflowR = crate::BitReader;
#[doc = "Field `FIFO_UFLOW` reader - Indicates data was unavailable when a new trigger for ADC update is received"]
pub type FifoUflowR = crate::BitReader;
#[doc = "Field `ADC_DONE` reader - Indicates that a ADC conversion is done"]
pub type AdcDoneR = crate::BitReader;
#[doc = "Field `TRIG_ERROR` reader - Indicates a manual or external trigger occurred when the ADC was BUSY doing a conversion"]
pub type TrigErrorR = crate::BitReader;
#[doc = "Field `FIFO_DEPTH_TRIG` reader - Indicates the interrupt for the FIFO entry count meets or exceeds the trigger level"]
pub type FifoDepthTrigR = crate::BitReader;
impl R {
#[doc = "Bit 0 - Indicates the FIFO is empty"]
#[inline(always)]
pub fn fifo_empty(&self) -> FifoEmptyR {
FifoEmptyR::new((self.bits & 1) != 0)
}
#[doc = "Bit 1 - Indicates the FIFO is full"]
#[inline(always)]
pub fn fifo_full(&self) -> FifoFullR {
FifoFullR::new(((self.bits >> 1) & 1) != 0)
}
#[doc = "Bit 2 - Indicates a FIFO overflow occurred (FIFO was full when new data was written)"]
#[inline(always)]
pub fn fifo_oflow(&self) -> FifoOflowR {
FifoOflowR::new(((self.bits >> 2) & 1) != 0)
}
#[doc = "Bit 3 - Indicates data was unavailable when a new trigger for ADC update is received"]
#[inline(always)]
pub fn fifo_uflow(&self) -> FifoUflowR {
FifoUflowR::new(((self.bits >> 3) & 1) != 0)
}
#[doc = "Bit 4 - Indicates that a ADC conversion is done"]
#[inline(always)]
pub fn adc_done(&self) -> AdcDoneR {
AdcDoneR::new(((self.bits >> 4) & 1) != 0)
}
#[doc = "Bit 5 - Indicates a manual or external trigger occurred when the ADC was BUSY doing a conversion"]
#[inline(always)]
pub fn trig_error(&self) -> TrigErrorR {
TrigErrorR::new(((self.bits >> 5) & 1) != 0)
}
#[doc = "Bit 6 - Indicates the interrupt for the FIFO entry count meets or exceeds the trigger level"]
#[inline(always)]
pub fn fifo_depth_trig(&self) -> FifoDepthTrigR {
FifoDepthTrigR::new(((self.bits >> 6) & 1) != 0)
}
}
#[doc = "Raw Interrupt Status\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`irq_raw::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct IrqRawSpec;
impl crate::RegisterSpec for IrqRawSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`irq_raw::R`](R) reader structure"]
impl crate::Readable for IrqRawSpec {}
#[doc = "`reset()` method sets IRQ_RAW to value 0x01"]
impl crate::Resettable for IrqRawSpec {
const RESET_VALUE: u32 = 0x01;
}

18
va416xx/src/adc/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 0x0019_07e9"]
impl crate::Resettable for PeridSpec {
const RESET_VALUE: u32 = 0x0019_07e9;
}

View File

@ -0,0 +1,40 @@
#[doc = "Register `RXFIFOIRQTRG` reader"]
pub type R = crate::R<RxfifoirqtrgSpec>;
#[doc = "Register `RXFIFOIRQTRG` writer"]
pub type W = crate::W<RxfifoirqtrgSpec>;
#[doc = "Field `LEVEL` reader - Sets the FIFO_ENTRY_CNT value that asserts the FIFO_DEPTH_TRIG interrupt"]
pub type LevelR = crate::FieldReader;
#[doc = "Field `LEVEL` writer - Sets the FIFO_ENTRY_CNT value that asserts the FIFO_DEPTH_TRIG interrupt"]
pub type LevelW<'a, REG> = crate::FieldWriter<'a, REG, 5>;
impl R {
#[doc = "Bits 0:4 - Sets the FIFO_ENTRY_CNT value that asserts the FIFO_DEPTH_TRIG interrupt"]
#[inline(always)]
pub fn level(&self) -> LevelR {
LevelR::new((self.bits & 0x1f) as u8)
}
}
impl W {
#[doc = "Bits 0:4 - Sets the FIFO_ENTRY_CNT value that asserts the FIFO_DEPTH_TRIG interrupt"]
#[inline(always)]
#[must_use]
pub fn level(&mut self) -> LevelW<RxfifoirqtrgSpec> {
LevelW::new(self, 0)
}
}
#[doc = "Receive FIFO Interrupt Trigger Value\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 0x10"]
impl crate::Resettable for RxfifoirqtrgSpec {
const RESET_VALUE: u32 = 0x10;
}

29
va416xx/src/adc/status.rs Normal file
View File

@ -0,0 +1,29 @@
#[doc = "Register `STATUS` reader"]
pub type R = crate::R<StatusSpec>;
#[doc = "Field `FIFO_ENTRY_CNT` reader - Indicates the number of entries in the FIFO"]
pub type FifoEntryCntR = crate::FieldReader;
#[doc = "Field `ADC_BUSY` reader - Indicates an ADC data acquisition is in process"]
pub type AdcBusyR = crate::BitReader;
impl R {
#[doc = "Bits 0:5 - Indicates the number of entries in the FIFO"]
#[inline(always)]
pub fn fifo_entry_cnt(&self) -> FifoEntryCntR {
FifoEntryCntR::new((self.bits & 0x3f) as u8)
}
#[doc = "Bit 7 - Indicates an ADC data acquisition is in process"]
#[inline(always)]
pub fn adc_busy(&self) -> AdcBusyR {
AdcBusyR::new(((self.bits >> 7) & 1) != 0)
}
}
#[doc = "Status\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`status::R`](R). See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct StatusSpec;
impl crate::RegisterSpec for StatusSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`status::R`](R) reader structure"]
impl crate::Readable for StatusSpec {}
#[doc = "`reset()` method sets STATUS to value 0"]
impl crate::Resettable for StatusSpec {
const RESET_VALUE: u32 = 0;
}

1710
va416xx/src/can0.rs Normal file

File diff suppressed because it is too large Load Diff

105
va416xx/src/can0/bmskb.rs Normal file
View File

@ -0,0 +1,105 @@
#[doc = "Register `BMSKB` reader"]
pub type R = crate::R<BmskbSpec>;
#[doc = "Register `BMSKB` writer"]
pub type W = crate::W<BmskbSpec>;
#[doc = "Field `BM0` reader - BM\\[17:15\\]
- Unused in standard, ID\\[17:15\\]
in extended"]
pub type Bm0R = crate::FieldReader;
#[doc = "Field `BM0` writer - BM\\[17:15\\]
- Unused in standard, ID\\[17:15\\]
in extended"]
pub type Bm0W<'a, REG> = crate::FieldWriter<'a, REG, 3>;
#[doc = "Field `IDE` reader - Identifier Extension Bit"]
pub type IdeR = crate::BitReader;
#[doc = "Field `IDE` writer - Identifier Extension Bit"]
pub type IdeW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `RTR` reader - Remote Transmission Request in Standard, Substitute Remote Request (SRR) in extended"]
pub type RtrR = crate::BitReader;
#[doc = "Field `RTR` writer - Remote Transmission Request in Standard, Substitute Remote Request (SRR) in extended"]
pub type RtrW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `BM1` reader - BM\\[28:18\\]
- ID\\[10:0\\]
in standard, ID\\[28:18\\]
in extended"]
pub type Bm1R = crate::FieldReader<u16>;
#[doc = "Field `BM1` writer - BM\\[28:18\\]
- ID\\[10:0\\]
in standard, ID\\[28:18\\]
in extended"]
pub type Bm1W<'a, REG> = crate::FieldWriter<'a, REG, 11, u16>;
impl R {
#[doc = "Bits 0:2 - BM\\[17:15\\]
- Unused in standard, ID\\[17:15\\]
in extended"]
#[inline(always)]
pub fn bm0(&self) -> Bm0R {
Bm0R::new((self.bits & 7) as u8)
}
#[doc = "Bit 3 - Identifier Extension Bit"]
#[inline(always)]
pub fn ide(&self) -> IdeR {
IdeR::new(((self.bits >> 3) & 1) != 0)
}
#[doc = "Bit 4 - Remote Transmission Request in Standard, Substitute Remote Request (SRR) in extended"]
#[inline(always)]
pub fn rtr(&self) -> RtrR {
RtrR::new(((self.bits >> 4) & 1) != 0)
}
#[doc = "Bits 5:15 - BM\\[28:18\\]
- ID\\[10:0\\]
in standard, ID\\[28:18\\]
in extended"]
#[inline(always)]
pub fn bm1(&self) -> Bm1R {
Bm1R::new(((self.bits >> 5) & 0x07ff) as u16)
}
}
impl W {
#[doc = "Bits 0:2 - BM\\[17:15\\]
- Unused in standard, ID\\[17:15\\]
in extended"]
#[inline(always)]
#[must_use]
pub fn bm0(&mut self) -> Bm0W<BmskbSpec> {
Bm0W::new(self, 0)
}
#[doc = "Bit 3 - Identifier Extension Bit"]
#[inline(always)]
#[must_use]
pub fn ide(&mut self) -> IdeW<BmskbSpec> {
IdeW::new(self, 3)
}
#[doc = "Bit 4 - Remote Transmission Request in Standard, Substitute Remote Request (SRR) in extended"]
#[inline(always)]
#[must_use]
pub fn rtr(&mut self) -> RtrW<BmskbSpec> {
RtrW::new(self, 4)
}
#[doc = "Bits 5:15 - BM\\[28:18\\]
- ID\\[10:0\\]
in standard, ID\\[28:18\\]
in extended"]
#[inline(always)]
#[must_use]
pub fn bm1(&mut self) -> Bm1W<BmskbSpec> {
Bm1W::new(self, 5)
}
}
#[doc = "CAN Basic Mask Base\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`bmskb::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 [`bmskb::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct BmskbSpec;
impl crate::RegisterSpec for BmskbSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`bmskb::R`](R) reader structure"]
impl crate::Readable for BmskbSpec {}
#[doc = "`write(|w| ..)` method takes [`bmskb::W`](W) writer structure"]
impl crate::Writable for BmskbSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets BMSKB to value 0"]
impl crate::Resettable for BmskbSpec {
const RESET_VALUE: u32 = 0;
}

63
va416xx/src/can0/bmskx.rs Normal file
View File

@ -0,0 +1,63 @@
#[doc = "Register `BMSKX` reader"]
pub type R = crate::R<BmskxSpec>;
#[doc = "Register `BMSKX` writer"]
pub type W = crate::W<BmskxSpec>;
#[doc = "Field `XRTR` reader - Extended Remote transmission Request Bit"]
pub type XrtrR = crate::BitReader;
#[doc = "Field `XRTR` writer - Extended Remote transmission Request Bit"]
pub type XrtrW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `BM` reader - BM\\[14:0\\]
used when an extended frame is received. ID\\[14:0\\]
in extended, unused standard"]
pub type BmR = crate::FieldReader<u16>;
#[doc = "Field `BM` writer - BM\\[14:0\\]
used when an extended frame is received. ID\\[14:0\\]
in extended, unused standard"]
pub type BmW<'a, REG> = crate::FieldWriter<'a, REG, 15, u16>;
impl R {
#[doc = "Bit 0 - Extended Remote transmission Request Bit"]
#[inline(always)]
pub fn xrtr(&self) -> XrtrR {
XrtrR::new((self.bits & 1) != 0)
}
#[doc = "Bits 1:15 - BM\\[14:0\\]
used when an extended frame is received. ID\\[14:0\\]
in extended, unused standard"]
#[inline(always)]
pub fn bm(&self) -> BmR {
BmR::new(((self.bits >> 1) & 0x7fff) as u16)
}
}
impl W {
#[doc = "Bit 0 - Extended Remote transmission Request Bit"]
#[inline(always)]
#[must_use]
pub fn xrtr(&mut self) -> XrtrW<BmskxSpec> {
XrtrW::new(self, 0)
}
#[doc = "Bits 1:15 - BM\\[14:0\\]
used when an extended frame is received. ID\\[14:0\\]
in extended, unused standard"]
#[inline(always)]
#[must_use]
pub fn bm(&mut self) -> BmW<BmskxSpec> {
BmW::new(self, 1)
}
}
#[doc = "CAN Basic Mask Extension\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`bmskx::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 [`bmskx::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct BmskxSpec;
impl crate::RegisterSpec for BmskxSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`bmskx::R`](R) reader structure"]
impl crate::Readable for BmskxSpec {}
#[doc = "`write(|w| ..)` method takes [`bmskx::W`](W) writer structure"]
impl crate::Writable for BmskxSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets BMSKX to value 0"]
impl crate::Resettable for BmskxSpec {
const RESET_VALUE: u32 = 0;
}

55
va416xx/src/can0/canec.rs Normal file
View File

@ -0,0 +1,55 @@
#[doc = "Register `CANEC` reader"]
pub type R = crate::R<CanecSpec>;
#[doc = "Register `CANEC` writer"]
pub type W = crate::W<CanecSpec>;
#[doc = "Field `TEC` reader - Transmit Error Counter"]
pub type TecR = crate::FieldReader;
#[doc = "Field `TEC` writer - Transmit Error Counter"]
pub type TecW<'a, REG> = crate::FieldWriter<'a, REG, 8>;
#[doc = "Field `REC` reader - Receive Error Counter"]
pub type RecR = crate::FieldReader;
#[doc = "Field `REC` writer - Receive Error Counter"]
pub type RecW<'a, REG> = crate::FieldWriter<'a, REG, 8>;
impl R {
#[doc = "Bits 0:7 - Transmit Error Counter"]
#[inline(always)]
pub fn tec(&self) -> TecR {
TecR::new((self.bits & 0xff) as u8)
}
#[doc = "Bits 8:15 - Receive Error Counter"]
#[inline(always)]
pub fn rec(&self) -> RecR {
RecR::new(((self.bits >> 8) & 0xff) as u8)
}
}
impl W {
#[doc = "Bits 0:7 - Transmit Error Counter"]
#[inline(always)]
#[must_use]
pub fn tec(&mut self) -> TecW<CanecSpec> {
TecW::new(self, 0)
}
#[doc = "Bits 8:15 - Receive Error Counter"]
#[inline(always)]
#[must_use]
pub fn rec(&mut self) -> RecW<CanecSpec> {
RecW::new(self, 8)
}
}
#[doc = "CAN Error Counter Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`canec::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 [`canec::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CanecSpec;
impl crate::RegisterSpec for CanecSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`canec::R`](R) reader structure"]
impl crate::Readable for CanecSpec {}
#[doc = "`write(|w| ..)` method takes [`canec::W`](W) writer structure"]
impl crate::Writable for CanecSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CANEC to value 0"]
impl crate::Resettable for CanecSpec {
const RESET_VALUE: u32 = 0;
}

130
va416xx/src/can0/cediag.rs Normal file
View File

@ -0,0 +1,130 @@
#[doc = "Register `CEDIAG` reader"]
pub type R = crate::R<CediagSpec>;
#[doc = "Register `CEDIAG` writer"]
pub type W = crate::W<CediagSpec>;
#[doc = "Field `EFID` reader - Error Field Identifier"]
pub type EfidR = crate::FieldReader;
#[doc = "Field `EFID` writer - Error Field Identifier"]
pub type EfidW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `EBID` reader - Error Bit Identifier"]
pub type EbidR = crate::FieldReader;
#[doc = "Field `EBID` writer - Error Bit Identifier"]
pub type EbidW<'a, REG> = crate::FieldWriter<'a, REG, 6>;
#[doc = "Field `TXE` reader - Transmit Error"]
pub type TxeR = crate::BitReader;
#[doc = "Field `TXE` writer - Transmit Error"]
pub type TxeW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `STUFF` reader - Stuff Error"]
pub type StuffR = crate::BitReader;
#[doc = "Field `STUFF` writer - Stuff Error"]
pub type StuffW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `CRC` reader - CRC"]
pub type CrcR = crate::BitReader;
#[doc = "Field `CRC` writer - CRC"]
pub type CrcW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `MON` reader - Monitor"]
pub type MonR = crate::BitReader;
#[doc = "Field `MON` writer - Monitor"]
pub type MonW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `DRIVE` reader - Drive"]
pub type DriveR = crate::BitReader;
#[doc = "Field `DRIVE` writer - Drive"]
pub type DriveW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bits 0:3 - Error Field Identifier"]
#[inline(always)]
pub fn efid(&self) -> EfidR {
EfidR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:9 - Error Bit Identifier"]
#[inline(always)]
pub fn ebid(&self) -> EbidR {
EbidR::new(((self.bits >> 4) & 0x3f) as u8)
}
#[doc = "Bit 10 - Transmit Error"]
#[inline(always)]
pub fn txe(&self) -> TxeR {
TxeR::new(((self.bits >> 10) & 1) != 0)
}
#[doc = "Bit 11 - Stuff Error"]
#[inline(always)]
pub fn stuff(&self) -> StuffR {
StuffR::new(((self.bits >> 11) & 1) != 0)
}
#[doc = "Bit 12 - CRC"]
#[inline(always)]
pub fn crc(&self) -> CrcR {
CrcR::new(((self.bits >> 12) & 1) != 0)
}
#[doc = "Bit 13 - Monitor"]
#[inline(always)]
pub fn mon(&self) -> MonR {
MonR::new(((self.bits >> 13) & 1) != 0)
}
#[doc = "Bit 14 - Drive"]
#[inline(always)]
pub fn drive(&self) -> DriveR {
DriveR::new(((self.bits >> 14) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:3 - Error Field Identifier"]
#[inline(always)]
#[must_use]
pub fn efid(&mut self) -> EfidW<CediagSpec> {
EfidW::new(self, 0)
}
#[doc = "Bits 4:9 - Error Bit Identifier"]
#[inline(always)]
#[must_use]
pub fn ebid(&mut self) -> EbidW<CediagSpec> {
EbidW::new(self, 4)
}
#[doc = "Bit 10 - Transmit Error"]
#[inline(always)]
#[must_use]
pub fn txe(&mut self) -> TxeW<CediagSpec> {
TxeW::new(self, 10)
}
#[doc = "Bit 11 - Stuff Error"]
#[inline(always)]
#[must_use]
pub fn stuff(&mut self) -> StuffW<CediagSpec> {
StuffW::new(self, 11)
}
#[doc = "Bit 12 - CRC"]
#[inline(always)]
#[must_use]
pub fn crc(&mut self) -> CrcW<CediagSpec> {
CrcW::new(self, 12)
}
#[doc = "Bit 13 - Monitor"]
#[inline(always)]
#[must_use]
pub fn mon(&mut self) -> MonW<CediagSpec> {
MonW::new(self, 13)
}
#[doc = "Bit 14 - Drive"]
#[inline(always)]
#[must_use]
pub fn drive(&mut self) -> DriveW<CediagSpec> {
DriveW::new(self, 14)
}
}
#[doc = "CAN Error Diagnostic Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cediag::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 [`cediag::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CediagSpec;
impl crate::RegisterSpec for CediagSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cediag::R`](R) reader structure"]
impl crate::Readable for CediagSpec {}
#[doc = "`write(|w| ..)` method takes [`cediag::W`](W) writer structure"]
impl crate::Writable for CediagSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CEDIAG to value 0"]
impl crate::Resettable for CediagSpec {
const RESET_VALUE: u32 = 0;
}

205
va416xx/src/can0/cgcr.rs Normal file
View File

@ -0,0 +1,205 @@
#[doc = "Register `CGCR` reader"]
pub type R = crate::R<CgcrSpec>;
#[doc = "Register `CGCR` writer"]
pub type W = crate::W<CgcrSpec>;
#[doc = "Field `CANEN` reader - CAN Enable"]
pub type CanenR = crate::BitReader;
#[doc = "Field `CANEN` writer - CAN Enable"]
pub type CanenW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `CRX` reader - RW,Control Receive"]
pub type CrxR = crate::BitReader;
#[doc = "Field `CRX` writer - RW,Control Receive"]
pub type CrxW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `CTX` reader - RW,Control Transmit"]
pub type CtxR = crate::BitReader;
#[doc = "Field `CTX` writer - RW,Control Transmit"]
pub type CtxW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `BUFFLOCK` reader - Buffer Lock"]
pub type BufflockR = crate::BitReader;
#[doc = "Field `BUFFLOCK` writer - Buffer Lock"]
pub type BufflockW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `TSTPEN` reader - Time Sync Enable"]
pub type TstpenR = crate::BitReader;
#[doc = "Field `TSTPEN` writer - Time Sync Enable"]
pub type TstpenW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `DDIR` reader - Data Direction"]
pub type DdirR = crate::BitReader;
#[doc = "Field `DDIR` writer - Data Direction"]
pub type DdirW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `LO` reader - Listen Only"]
pub type LoR = crate::BitReader;
#[doc = "Field `LO` writer - Listen Only"]
pub type LoW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `IGNACK` reader - Ignore Acknowledge"]
pub type IgnackR = crate::BitReader;
#[doc = "Field `IGNACK` writer - Ignore Acknowledge"]
pub type IgnackW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `LOOPBACK` reader - Loopback"]
pub type LoopbackR = crate::BitReader;
#[doc = "Field `LOOPBACK` writer - Loopback"]
pub type LoopbackW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `INTERNAL` reader - Internal"]
pub type InternalR = crate::BitReader;
#[doc = "Field `INTERNAL` writer - Internal"]
pub type InternalW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `DIAGEN` reader - Diagnostic Enable"]
pub type DiagenR = crate::BitReader;
#[doc = "Field `DIAGEN` writer - Diagnostic Enable"]
pub type DiagenW<'a, REG> = crate::BitWriter<'a, REG>;
#[doc = "Field `EIT` reader - Error Interrupt Type"]
pub type EitR = crate::BitReader;
#[doc = "Field `EIT` writer - Error Interrupt Type"]
pub type EitW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bit 0 - CAN Enable"]
#[inline(always)]
pub fn canen(&self) -> CanenR {
CanenR::new((self.bits & 1) != 0)
}
#[doc = "Bit 1 - RW,Control Receive"]
#[inline(always)]
pub fn crx(&self) -> CrxR {
CrxR::new(((self.bits >> 1) & 1) != 0)
}
#[doc = "Bit 2 - RW,Control Transmit"]
#[inline(always)]
pub fn ctx(&self) -> CtxR {
CtxR::new(((self.bits >> 2) & 1) != 0)
}
#[doc = "Bit 3 - Buffer Lock"]
#[inline(always)]
pub fn bufflock(&self) -> BufflockR {
BufflockR::new(((self.bits >> 3) & 1) != 0)
}
#[doc = "Bit 4 - Time Sync Enable"]
#[inline(always)]
pub fn tstpen(&self) -> TstpenR {
TstpenR::new(((self.bits >> 4) & 1) != 0)
}
#[doc = "Bit 5 - Data Direction"]
#[inline(always)]
pub fn ddir(&self) -> DdirR {
DdirR::new(((self.bits >> 5) & 1) != 0)
}
#[doc = "Bit 6 - Listen Only"]
#[inline(always)]
pub fn lo(&self) -> LoR {
LoR::new(((self.bits >> 6) & 1) != 0)
}
#[doc = "Bit 7 - Ignore Acknowledge"]
#[inline(always)]
pub fn ignack(&self) -> IgnackR {
IgnackR::new(((self.bits >> 7) & 1) != 0)
}
#[doc = "Bit 8 - Loopback"]
#[inline(always)]
pub fn loopback(&self) -> LoopbackR {
LoopbackR::new(((self.bits >> 8) & 1) != 0)
}
#[doc = "Bit 9 - Internal"]
#[inline(always)]
pub fn internal(&self) -> InternalR {
InternalR::new(((self.bits >> 9) & 1) != 0)
}
#[doc = "Bit 10 - Diagnostic Enable"]
#[inline(always)]
pub fn diagen(&self) -> DiagenR {
DiagenR::new(((self.bits >> 10) & 1) != 0)
}
#[doc = "Bit 11 - Error Interrupt Type"]
#[inline(always)]
pub fn eit(&self) -> EitR {
EitR::new(((self.bits >> 11) & 1) != 0)
}
}
impl W {
#[doc = "Bit 0 - CAN Enable"]
#[inline(always)]
#[must_use]
pub fn canen(&mut self) -> CanenW<CgcrSpec> {
CanenW::new(self, 0)
}
#[doc = "Bit 1 - RW,Control Receive"]
#[inline(always)]
#[must_use]
pub fn crx(&mut self) -> CrxW<CgcrSpec> {
CrxW::new(self, 1)
}
#[doc = "Bit 2 - RW,Control Transmit"]
#[inline(always)]
#[must_use]
pub fn ctx(&mut self) -> CtxW<CgcrSpec> {
CtxW::new(self, 2)
}
#[doc = "Bit 3 - Buffer Lock"]
#[inline(always)]
#[must_use]
pub fn bufflock(&mut self) -> BufflockW<CgcrSpec> {
BufflockW::new(self, 3)
}
#[doc = "Bit 4 - Time Sync Enable"]
#[inline(always)]
#[must_use]
pub fn tstpen(&mut self) -> TstpenW<CgcrSpec> {
TstpenW::new(self, 4)
}
#[doc = "Bit 5 - Data Direction"]
#[inline(always)]
#[must_use]
pub fn ddir(&mut self) -> DdirW<CgcrSpec> {
DdirW::new(self, 5)
}
#[doc = "Bit 6 - Listen Only"]
#[inline(always)]
#[must_use]
pub fn lo(&mut self) -> LoW<CgcrSpec> {
LoW::new(self, 6)
}
#[doc = "Bit 7 - Ignore Acknowledge"]
#[inline(always)]
#[must_use]
pub fn ignack(&mut self) -> IgnackW<CgcrSpec> {
IgnackW::new(self, 7)
}
#[doc = "Bit 8 - Loopback"]
#[inline(always)]
#[must_use]
pub fn loopback(&mut self) -> LoopbackW<CgcrSpec> {
LoopbackW::new(self, 8)
}
#[doc = "Bit 9 - Internal"]
#[inline(always)]
#[must_use]
pub fn internal(&mut self) -> InternalW<CgcrSpec> {
InternalW::new(self, 9)
}
#[doc = "Bit 10 - Diagnostic Enable"]
#[inline(always)]
#[must_use]
pub fn diagen(&mut self) -> DiagenW<CgcrSpec> {
DiagenW::new(self, 10)
}
#[doc = "Bit 11 - Error Interrupt Type"]
#[inline(always)]
#[must_use]
pub fn eit(&mut self) -> EitW<CgcrSpec> {
EitW::new(self, 11)
}
}
#[doc = "CAN Global Configuration Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cgcr::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 [`cgcr::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CgcrSpec;
impl crate::RegisterSpec for CgcrSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cgcr::R`](R) reader structure"]
impl crate::Readable for CgcrSpec {}
#[doc = "`write(|w| ..)` method takes [`cgcr::W`](W) writer structure"]
impl crate::Writable for CgcrSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CGCR to value 0"]
impl crate::Resettable for CgcrSpec {
const RESET_VALUE: u32 = 0;
}

55
va416xx/src/can0/cicen.rs Normal file
View File

@ -0,0 +1,55 @@
#[doc = "Register `CICEN` reader"]
pub type R = crate::R<CicenSpec>;
#[doc = "Register `CICEN` writer"]
pub type W = crate::W<CicenSpec>;
#[doc = "Field `ICEN` reader - Buffer Interrupt Code Enable\\[14:0\\]"]
pub type IcenR = crate::FieldReader<u16>;
#[doc = "Field `ICEN` writer - Buffer Interrupt Code Enable\\[14:0\\]"]
pub type IcenW<'a, REG> = crate::FieldWriter<'a, REG, 15, u16>;
#[doc = "Field `EICEN` reader - Error Interrupt Code Enable"]
pub type EicenR = crate::BitReader;
#[doc = "Field `EICEN` writer - Error Interrupt Code Enable"]
pub type EicenW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bits 0:14 - Buffer Interrupt Code Enable\\[14:0\\]"]
#[inline(always)]
pub fn icen(&self) -> IcenR {
IcenR::new((self.bits & 0x7fff) as u16)
}
#[doc = "Bit 15 - Error Interrupt Code Enable"]
#[inline(always)]
pub fn eicen(&self) -> EicenR {
EicenR::new(((self.bits >> 15) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:14 - Buffer Interrupt Code Enable\\[14:0\\]"]
#[inline(always)]
#[must_use]
pub fn icen(&mut self) -> IcenW<CicenSpec> {
IcenW::new(self, 0)
}
#[doc = "Bit 15 - Error Interrupt Code Enable"]
#[inline(always)]
#[must_use]
pub fn eicen(&mut self) -> EicenW<CicenSpec> {
EicenW::new(self, 15)
}
}
#[doc = "CAN Interrupt Code Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cicen::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 [`cicen::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CicenSpec;
impl crate::RegisterSpec for CicenSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cicen::R`](R) reader structure"]
impl crate::Readable for CicenSpec {}
#[doc = "`write(|w| ..)` method takes [`cicen::W`](W) writer structure"]
impl crate::Writable for CicenSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CICEN to value 0"]
impl crate::Resettable for CicenSpec {
const RESET_VALUE: u32 = 0;
}

55
va416xx/src/can0/ciclr.rs Normal file
View File

@ -0,0 +1,55 @@
#[doc = "Register `CICLR` reader"]
pub type R = crate::R<CiclrSpec>;
#[doc = "Register `CICLR` writer"]
pub type W = crate::W<CiclrSpec>;
#[doc = "Field `ICLR` reader - Buffer Interrupt Clear\\[14:0\\]"]
pub type IclrR = crate::FieldReader<u16>;
#[doc = "Field `ICLR` writer - Buffer Interrupt Clear\\[14:0\\]"]
pub type IclrW<'a, REG> = crate::FieldWriter<'a, REG, 15, u16>;
#[doc = "Field `EICLR` reader - Error Interrupt Clear"]
pub type EiclrR = crate::BitReader;
#[doc = "Field `EICLR` writer - Error Interrupt Clear"]
pub type EiclrW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bits 0:14 - Buffer Interrupt Clear\\[14:0\\]"]
#[inline(always)]
pub fn iclr(&self) -> IclrR {
IclrR::new((self.bits & 0x7fff) as u16)
}
#[doc = "Bit 15 - Error Interrupt Clear"]
#[inline(always)]
pub fn eiclr(&self) -> EiclrR {
EiclrR::new(((self.bits >> 15) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:14 - Buffer Interrupt Clear\\[14:0\\]"]
#[inline(always)]
#[must_use]
pub fn iclr(&mut self) -> IclrW<CiclrSpec> {
IclrW::new(self, 0)
}
#[doc = "Bit 15 - Error Interrupt Clear"]
#[inline(always)]
#[must_use]
pub fn eiclr(&mut self) -> EiclrW<CiclrSpec> {
EiclrW::new(self, 15)
}
}
#[doc = "CAN Interrupt Clear Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`ciclr::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 [`ciclr::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CiclrSpec;
impl crate::RegisterSpec for CiclrSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`ciclr::R`](R) reader structure"]
impl crate::Readable for CiclrSpec {}
#[doc = "`write(|w| ..)` method takes [`ciclr::W`](W) writer structure"]
impl crate::Writable for CiclrSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CICLR to value 0"]
impl crate::Resettable for CiclrSpec {
const RESET_VALUE: u32 = 0;
}

55
va416xx/src/can0/cien.rs Normal file
View File

@ -0,0 +1,55 @@
#[doc = "Register `CIEN` reader"]
pub type R = crate::R<CienSpec>;
#[doc = "Register `CIEN` writer"]
pub type W = crate::W<CienSpec>;
#[doc = "Field `IEN` reader - Buffer Interrupt Enable\\[14:0\\]"]
pub type IenR = crate::FieldReader<u16>;
#[doc = "Field `IEN` writer - Buffer Interrupt Enable\\[14:0\\]"]
pub type IenW<'a, REG> = crate::FieldWriter<'a, REG, 15, u16>;
#[doc = "Field `EIEN` reader - Error Interrupt Enable"]
pub type EienR = crate::BitReader;
#[doc = "Field `EIEN` writer - Error Interrupt Enable"]
pub type EienW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bits 0:14 - Buffer Interrupt Enable\\[14:0\\]"]
#[inline(always)]
pub fn ien(&self) -> IenR {
IenR::new((self.bits & 0x7fff) as u16)
}
#[doc = "Bit 15 - Error Interrupt Enable"]
#[inline(always)]
pub fn eien(&self) -> EienR {
EienR::new(((self.bits >> 15) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:14 - Buffer Interrupt Enable\\[14:0\\]"]
#[inline(always)]
#[must_use]
pub fn ien(&mut self) -> IenW<CienSpec> {
IenW::new(self, 0)
}
#[doc = "Bit 15 - Error Interrupt Enable"]
#[inline(always)]
#[must_use]
pub fn eien(&mut self) -> EienW<CienSpec> {
EienW::new(self, 15)
}
}
#[doc = "CAN Interrupt Enable Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cien::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 [`cien::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CienSpec;
impl crate::RegisterSpec for CienSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cien::R`](R) reader structure"]
impl crate::Readable for CienSpec {}
#[doc = "`write(|w| ..)` method takes [`cien::W`](W) writer structure"]
impl crate::Writable for CienSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CIEN to value 0"]
impl crate::Resettable for CienSpec {
const RESET_VALUE: u32 = 0;
}

55
va416xx/src/can0/cipnd.rs Normal file
View File

@ -0,0 +1,55 @@
#[doc = "Register `CIPND` reader"]
pub type R = crate::R<CipndSpec>;
#[doc = "Register `CIPND` writer"]
pub type W = crate::W<CipndSpec>;
#[doc = "Field `IPND` reader - Buffer Interrupt Pending\\[14:0\\]"]
pub type IpndR = crate::FieldReader<u16>;
#[doc = "Field `IPND` writer - Buffer Interrupt Pending\\[14:0\\]"]
pub type IpndW<'a, REG> = crate::FieldWriter<'a, REG, 15, u16>;
#[doc = "Field `EIPND` reader - Error Interrupt Pending"]
pub type EipndR = crate::BitReader;
#[doc = "Field `EIPND` writer - Error Interrupt Pending"]
pub type EipndW<'a, REG> = crate::BitWriter<'a, REG>;
impl R {
#[doc = "Bits 0:14 - Buffer Interrupt Pending\\[14:0\\]"]
#[inline(always)]
pub fn ipnd(&self) -> IpndR {
IpndR::new((self.bits & 0x7fff) as u16)
}
#[doc = "Bit 15 - Error Interrupt Pending"]
#[inline(always)]
pub fn eipnd(&self) -> EipndR {
EipndR::new(((self.bits >> 15) & 1) != 0)
}
}
impl W {
#[doc = "Bits 0:14 - Buffer Interrupt Pending\\[14:0\\]"]
#[inline(always)]
#[must_use]
pub fn ipnd(&mut self) -> IpndW<CipndSpec> {
IpndW::new(self, 0)
}
#[doc = "Bit 15 - Error Interrupt Pending"]
#[inline(always)]
#[must_use]
pub fn eipnd(&mut self) -> EipndW<CipndSpec> {
EipndW::new(self, 15)
}
}
#[doc = "CAN Interrupt Pending Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cipnd::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 [`cipnd::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CipndSpec;
impl crate::RegisterSpec for CipndSpec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cipnd::R`](R) reader structure"]
impl crate::Readable for CipndSpec {}
#[doc = "`write(|w| ..)` method takes [`cipnd::W`](W) writer structure"]
impl crate::Writable for CipndSpec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CIPND to value 0"]
impl crate::Resettable for CipndSpec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB0` reader"]
pub type R = crate::R<CnstatCmb0Spec>;
#[doc = "Register `CNSTAT_CMB0` writer"]
pub type W = crate::W<CnstatCmb0Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb0Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb0Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb0Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb0::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 [`cnstat_cmb0::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb0Spec;
impl crate::RegisterSpec for CnstatCmb0Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb0::R`](R) reader structure"]
impl crate::Readable for CnstatCmb0Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb0::W`](W) writer structure"]
impl crate::Writable for CnstatCmb0Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB0 to value 0"]
impl crate::Resettable for CnstatCmb0Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB1` reader"]
pub type R = crate::R<CnstatCmb1Spec>;
#[doc = "Register `CNSTAT_CMB1` writer"]
pub type W = crate::W<CnstatCmb1Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb1Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb1Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb1Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb1::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 [`cnstat_cmb1::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb1Spec;
impl crate::RegisterSpec for CnstatCmb1Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb1::R`](R) reader structure"]
impl crate::Readable for CnstatCmb1Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb1::W`](W) writer structure"]
impl crate::Writable for CnstatCmb1Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB1 to value 0"]
impl crate::Resettable for CnstatCmb1Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB10` reader"]
pub type R = crate::R<CnstatCmb10Spec>;
#[doc = "Register `CNSTAT_CMB10` writer"]
pub type W = crate::W<CnstatCmb10Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb10Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb10Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb10Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb10::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 [`cnstat_cmb10::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb10Spec;
impl crate::RegisterSpec for CnstatCmb10Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb10::R`](R) reader structure"]
impl crate::Readable for CnstatCmb10Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb10::W`](W) writer structure"]
impl crate::Writable for CnstatCmb10Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB10 to value 0"]
impl crate::Resettable for CnstatCmb10Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB11` reader"]
pub type R = crate::R<CnstatCmb11Spec>;
#[doc = "Register `CNSTAT_CMB11` writer"]
pub type W = crate::W<CnstatCmb11Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb11Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb11Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb11Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb11::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 [`cnstat_cmb11::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb11Spec;
impl crate::RegisterSpec for CnstatCmb11Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb11::R`](R) reader structure"]
impl crate::Readable for CnstatCmb11Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb11::W`](W) writer structure"]
impl crate::Writable for CnstatCmb11Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB11 to value 0"]
impl crate::Resettable for CnstatCmb11Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB12` reader"]
pub type R = crate::R<CnstatCmb12Spec>;
#[doc = "Register `CNSTAT_CMB12` writer"]
pub type W = crate::W<CnstatCmb12Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb12Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb12Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb12Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb12::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 [`cnstat_cmb12::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb12Spec;
impl crate::RegisterSpec for CnstatCmb12Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb12::R`](R) reader structure"]
impl crate::Readable for CnstatCmb12Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb12::W`](W) writer structure"]
impl crate::Writable for CnstatCmb12Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB12 to value 0"]
impl crate::Resettable for CnstatCmb12Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB13` reader"]
pub type R = crate::R<CnstatCmb13Spec>;
#[doc = "Register `CNSTAT_CMB13` writer"]
pub type W = crate::W<CnstatCmb13Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb13Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb13Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb13Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb13::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 [`cnstat_cmb13::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb13Spec;
impl crate::RegisterSpec for CnstatCmb13Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb13::R`](R) reader structure"]
impl crate::Readable for CnstatCmb13Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb13::W`](W) writer structure"]
impl crate::Writable for CnstatCmb13Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB13 to value 0"]
impl crate::Resettable for CnstatCmb13Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB14` reader"]
pub type R = crate::R<CnstatCmb14Spec>;
#[doc = "Register `CNSTAT_CMB14` writer"]
pub type W = crate::W<CnstatCmb14Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb14Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb14Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb14Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb14::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 [`cnstat_cmb14::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb14Spec;
impl crate::RegisterSpec for CnstatCmb14Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb14::R`](R) reader structure"]
impl crate::Readable for CnstatCmb14Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb14::W`](W) writer structure"]
impl crate::Writable for CnstatCmb14Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB14 to value 0"]
impl crate::Resettable for CnstatCmb14Spec {
const RESET_VALUE: u32 = 0;
}

View File

@ -0,0 +1,70 @@
#[doc = "Register `CNSTAT_CMB2` reader"]
pub type R = crate::R<CnstatCmb2Spec>;
#[doc = "Register `CNSTAT_CMB2` writer"]
pub type W = crate::W<CnstatCmb2Spec>;
#[doc = "Field `ST` reader - Buffer Status"]
pub type StR = crate::FieldReader;
#[doc = "Field `ST` writer - Buffer Status"]
pub type StW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `PRI` reader - Transmit Priority Code"]
pub type PriR = crate::FieldReader;
#[doc = "Field `PRI` writer - Transmit Priority Code"]
pub type PriW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
#[doc = "Field `DLC` reader - Data Length Code"]
pub type DlcR = crate::FieldReader;
#[doc = "Field `DLC` writer - Data Length Code"]
pub type DlcW<'a, REG> = crate::FieldWriter<'a, REG, 4>;
impl R {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
pub fn st(&self) -> StR {
StR::new((self.bits & 0x0f) as u8)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
pub fn pri(&self) -> PriR {
PriR::new(((self.bits >> 4) & 0x0f) as u8)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
pub fn dlc(&self) -> DlcR {
DlcR::new(((self.bits >> 12) & 0x0f) as u8)
}
}
impl W {
#[doc = "Bits 0:3 - Buffer Status"]
#[inline(always)]
#[must_use]
pub fn st(&mut self) -> StW<CnstatCmb2Spec> {
StW::new(self, 0)
}
#[doc = "Bits 4:7 - Transmit Priority Code"]
#[inline(always)]
#[must_use]
pub fn pri(&mut self) -> PriW<CnstatCmb2Spec> {
PriW::new(self, 4)
}
#[doc = "Bits 12:15 - Data Length Code"]
#[inline(always)]
#[must_use]
pub fn dlc(&mut self) -> DlcW<CnstatCmb2Spec> {
DlcW::new(self, 12)
}
}
#[doc = "Buffer Status / Control Register\n\nYou can [`read`](crate::generic::Reg::read) this register and get [`cnstat_cmb2::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 [`cnstat_cmb2::W`](W). You can also [`modify`](crate::generic::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api)."]
pub struct CnstatCmb2Spec;
impl crate::RegisterSpec for CnstatCmb2Spec {
type Ux = u32;
}
#[doc = "`read()` method returns [`cnstat_cmb2::R`](R) reader structure"]
impl crate::Readable for CnstatCmb2Spec {}
#[doc = "`write(|w| ..)` method takes [`cnstat_cmb2::W`](W) writer structure"]
impl crate::Writable for CnstatCmb2Spec {
type Safety = crate::Unsafe;
const ZERO_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
const ONE_TO_MODIFY_FIELDS_BITMAP: u32 = 0;
}
#[doc = "`reset()` method sets CNSTAT_CMB2 to value 0"]
impl crate::Resettable for CnstatCmb2Spec {
const RESET_VALUE: u32 = 0;
}

Some files were not shown because too many files have changed in this diff Show More