init commit

This commit is contained in:
Robin Müller 2025-04-22 13:45:36 +02:00
commit ba8d817a73
36 changed files with 10348 additions and 0 deletions

1
.cargo/.gitignore vendored Normal file
View File

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

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

@ -0,0 +1,41 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x jlink.gdb"
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
"-C", "linker=flip-link",
# Unfortunately, defmt is clunky to use without probe-rs..
"-C", "link-arg=-Tdefmt.x",
# Can be useful for debugging.
# "-Clink-args=-Map=app.map"
]
[build]
# Pick ONE of these compilation targets
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ for VA108xx
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) for VA416xx
[alias]
re = "run --example"
rb = "run --bin"
rrb = "run --release --bin"
ut = "test --target x86_64-unknown-linux-gnu"
[env]
DEFMT_LOG = "info"

22
.gitignore vendored Normal file
View File

@ -0,0 +1,22 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Keep config.toml configurable.
.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
/app.map
# These are backup files generated by rustfmt
**/*.rs.bk
/.vscode
# JetBrains IDEs
/.idea
*.iml
/Embed.toml

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
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.0]
Init commit.
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/v0.1.0...HEAD
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs/src/tag/v0.1.0

48
Cargo.toml Normal file
View File

@ -0,0 +1,48 @@
[package]
name = "vorago-shared-periphs"
version = "0.1.0"
description = "Peripheral drivers shared between Vorago families"
edition = "2024"
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs"
repository = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs"
license = "Apache-2.0"
[dependencies]
cortex-m = { version = "0.7" }
cfg-if = "1"
derive-mmio = { git = "https://github.com/knurling-rs/derive-mmio.git" }
bitbybit = "1.3"
arbitrary-int = "1.3"
static_assertions = "1.1"
nb = "1"
heapless = "0.8"
critical-section = "1"
embedded-hal = "1.0"
embedded-hal-async = "1"
embedded-hal-nb = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
raw-slicee = "0.1"
thiserror = { version = "2", default-features = false }
paste = "1"
fugit = "0.3"
defmt = { version = "1", optional = true }
va108xx = { version = "0.5", default-features = false, optional = true }
va416xx = { version = "0.4", default-features = false, optional = true }
portable-atomic = "1"
embassy-sync = "0.6"
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[features]
vor1x = ["_family-selected", "dep:va108xx"]
vor4x = ["_family-selected", "dep:va416xx"]
va41628 = []
defmt = ["dep:defmt", "arbitrary-int/defmt", "fugit/defmt", "embedded-hal/defmt-03"]
_family-selected = []
[package.metadata.cargo-machete]
ignored = ["raw-slicee"]

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.

8
README.md Normal file
View File

@ -0,0 +1,8 @@
Vorago Shared Peripherals
========
Peripheral drivers shared between Vorago families.
This library should not used directly. Instead, use the re-exported modules of the repective
[VA108xx HAL](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal) and
[VA416xx HAL](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs).

62
src/clock.rs Normal file
View File

@ -0,0 +1,62 @@
use crate::time::Hertz;
pub const HBO_FREQ: Hertz = Hertz::from_raw(20_000_000);
/// 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,
#[cfg(not(feature = "va41628"))]
adc_clk: Hertz,
}
impl Clocks {
#[doc(hidden)]
pub fn __new(final_sysclk: Hertz, #[cfg(not(feature = "va41628"))] adc_clk: Hertz) -> Self {
Self {
sysclk: final_sysclk,
apb1: final_sysclk / 2,
apb2: final_sysclk / 4,
#[cfg(not(feature = "va41628"))]
adc_clk,
}
}
/// Returns the frequency of the HBO clock
pub const fn hbo(&self) -> Hertz {
HBO_FREQ
}
/// Returns the frequency of the APB0 which is equal to the system clock.
pub const fn apb0(&self) -> Hertz {
self.sysclk()
}
/// Returns system clock divied by 2.
pub const fn apb1(&self) -> Hertz {
self.apb1
}
/// Returns system clock divied by 4.
pub const fn apb2(&self) -> Hertz {
self.apb2
}
/// Returns the system (core) frequency
pub const fn sysclk(&self) -> Hertz {
self.sysclk
}
/// Returns the ADC clock frequency which has a separate divider.
#[cfg(not(feature = "va41628"))]
pub const fn adc_clk(&self) -> Hertz {
self.adc_clk
}
}

340
src/embassy.rs Normal file
View File

@ -0,0 +1,340 @@
use core::cell::{Cell, RefCell};
use crate::{
enable_nvic_interrupt,
timer::{
TimId, TimMarker, assert_tim_reset_for_cycles, enable_tim_clk,
regs::{EnableControl, MmioTimer},
},
};
use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::TICK_HZ;
use embassy_time_driver::{Driver, time_driver_impl};
use embassy_time_queue_utils::Queue;
use once_cell::sync::OnceCell;
use portable_atomic::{AtomicU32, Ordering};
#[cfg(feature = "vor1x")]
use crate::time::Hertz;
#[cfg(feature = "vor1x")]
use crate::{PeripheralSelect, enable_peripheral_clock};
time_driver_impl!(
static TIME_DRIVER: TimerDriver = TimerDriver {
periods: AtomicU32::new(0),
alarms: Mutex::new(AlarmState::new()),
queue: Mutex::new(RefCell::new(Queue::new())),
});
/// Expose the time driver so the user can specify the IRQ handlers themselves.
pub fn time_driver() -> &'static TimerDriver {
&TIME_DRIVER
}
struct AlarmState {
timestamp: Cell<u64>,
}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
unsafe impl Send for AlarmState {}
static SCALE: OnceCell<u64> = OnceCell::new();
static TIMEKEEPER_TIM: OnceCell<TimId> = OnceCell::new();
static ALARM_TIM: OnceCell<TimId> = OnceCell::new();
pub struct TimerDriver {
periods: AtomicU32,
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<AlarmState>,
queue: Mutex<RefCell<Queue>>,
}
impl TimerDriver {
#[cfg(feature = "vor1x")]
#[doc(hidden)]
pub fn __init<TimekeeperTim: TimMarker, AlarmTim: TimMarker>(
&self,
sysclk: Hertz,
_timekeeper_tim: TimekeeperTim,
_alarm_tim: AlarmTim,
timekeeper_irq: va108xx::Interrupt,
alarm_irq: va108xx::Interrupt,
) {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
return;
}
ALARM_TIM.set(AlarmTim::ID).ok();
TIMEKEEPER_TIM.set(TimekeeperTim::ID).ok();
enable_peripheral_clock(PeripheralSelect::Irqsel);
enable_tim_clk(TimekeeperTim::ID);
assert_tim_reset_for_cycles(TimekeeperTim::ID, 2);
let mut timekeeper_reg_block = unsafe { TimekeeperTim::ID.steal_regs() };
let mut alarm_tim_reg_block = unsafe { AlarmTim::ID.steal_regs() };
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
timekeeper_reg_block.write_reset_value(u32::MAX);
// Decrementing counter.
timekeeper_reg_block.write_count_value(u32::MAX);
let irqsel = unsafe { va108xx::Irqsel::steal() };
// Switch on. Timekeeping should always be done.
irqsel
.tim0(TimekeeperTim::ID.value() as usize)
.write(|w| unsafe { w.bits(timekeeper_irq as u32) });
unsafe {
enable_nvic_interrupt(timekeeper_irq);
}
timekeeper_reg_block.modify_control(|mut value| {
value.set_irq_enable(true);
value
});
timekeeper_reg_block.write_enable_control(EnableControl::new_enable());
enable_tim_clk(AlarmTim::ID);
assert_tim_reset_for_cycles(AlarmTim::ID, 2);
// Explicitely disable alarm timer until needed.
alarm_tim_reg_block.modify_control(|mut value| {
value.set_irq_enable(false);
value.set_enable(false);
value
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_nvic_interrupt(alarm_irq);
}
irqsel
.tim0(AlarmTim::ID.value() as usize)
.write(|w| unsafe { w.bits(alarm_irq as u32) });
}
#[cfg(feature = "vor4x")]
#[doc(hidden)]
pub fn __init<TimekeeperTim: TimMarker, AlarmTim: TimMarker>(
&self,
_timekeeper_tim: TimekeeperTim,
_alarm_tim: AlarmTim,
clocks: &crate::clock::Clocks,
) {
if ALARM_TIM.get().is_some() || TIMEKEEPER_TIM.get().is_some() {
return;
}
ALARM_TIM.set(AlarmTim::ID).ok();
TIMEKEEPER_TIM.set(TimekeeperTim::ID).ok();
let mut timekeeper_regs = unsafe { TimekeeperTim::ID.steal_regs() };
let mut alarm_regs = unsafe { AlarmTim::ID.steal_regs() };
enable_tim_clk(TimekeeperTim::ID);
assert_tim_reset_for_cycles(TimekeeperTim::ID, 2);
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE
.set((TimekeeperTim::clock(clocks).raw() / TICK_HZ as u32) as u64)
.unwrap();
timekeeper_regs.write_reset_value(u32::MAX);
// Decrementing counter.
timekeeper_regs.write_count_value(u32::MAX);
// Switch on. Timekeeping should always be done.
unsafe {
enable_nvic_interrupt(TimekeeperTim::IRQ);
}
timekeeper_regs.modify_control(|mut value| {
value.set_irq_enable(true);
value
});
timekeeper_regs.write_enable_control(EnableControl::new_enable());
enable_tim_clk(AlarmTim::ID);
assert_tim_reset_for_cycles(AlarmTim::ID, 2);
// Explicitely disable alarm timer until needed.
alarm_regs.modify_control(|mut value| {
value.set_irq_enable(false);
value.set_enable(false);
value
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_nvic_interrupt(AlarmTim::IRQ);
}
}
fn timekeeper_tim() -> MmioTimer<'static> {
TIMEKEEPER_TIM
.get()
.map(|tim| unsafe { tim.steal_regs() })
.unwrap()
}
fn alarm_tim() -> MmioTimer<'static> {
ALARM_TIM
.get()
.map(|tim| unsafe { tim.steal_regs() })
.unwrap()
}
/// Should be called inside the IRQ of the timekeeper timer.
///
/// # Safety
///
/// This function has to be called once by the TIM IRQ used for the timekeeping.
pub unsafe fn on_interrupt_timekeeping(&self) {
self.next_period();
}
/// Should be called inside the IRQ of the alarm timer.
///
/// # Safety
///
///This function has to be called once by the TIM IRQ used for the timekeeping.
pub unsafe fn on_interrupt_alarm(&self) {
critical_section::with(|cs| {
if self.alarms.borrow(cs).timestamp.get() <= self.now() {
self.trigger_alarm(cs)
}
})
}
fn next_period(&self) {
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
let t = (period as u64) << 32;
critical_section::with(|cs| {
let alarm = &self.alarms.borrow(cs);
let at = alarm.timestamp.get();
if at < t {
self.trigger_alarm(cs);
} else {
let mut alarm_tim = Self::alarm_tim();
let remaining_ticks = (at - t).checked_mul(*SCALE.get().unwrap());
if remaining_ticks.is_some_and(|v| v <= u32::MAX as u64) {
alarm_tim.write_enable_control(EnableControl::new_disable());
alarm_tim.write_count_value(remaining_ticks.unwrap() as u32);
alarm_tim.modify_control(|mut value| {
value.set_irq_enable(true);
value
});
alarm_tim.write_enable_control(EnableControl::new_enable());
}
}
})
}
fn trigger_alarm(&self, cs: CriticalSection) {
Self::alarm_tim().modify_control(|mut value| {
value.set_irq_enable(false);
value.set_enable(false);
value
});
let alarm = &self.alarms.borrow(cs);
// Setting the maximum value disables the alarm.
alarm.timestamp.set(u64::MAX);
// Call after clearing alarm, so the callback can set another alarm.
let mut next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
}
}
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
if SCALE.get().is_none() {
return false;
}
let mut alarm_tim = Self::alarm_tim();
alarm_tim.modify_control(|mut value| {
value.set_irq_enable(false);
value.set_enable(false);
value
});
let alarm = self.alarms.borrow(cs);
alarm.timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
// the interrupts are enabled or not. When they are enabled at a later point, the
// right value is already set.
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
// is not missed.
//
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
// and we don't do that here.
let safe_timestamp = timestamp.max(t + 3);
let timer_ticks = (safe_timestamp - t).checked_mul(*SCALE.get().unwrap());
alarm_tim.write_reset_value(u32::MAX);
if timer_ticks.is_some_and(|v| v <= u32::MAX as u64) {
alarm_tim.write_count_value(timer_ticks.unwrap() as u32);
alarm_tim.modify_control(|mut value| {
value.set_irq_enable(true);
value.set_enable(true);
value
});
}
// If it's too far in the future, don't enable timer yet.
// It will be enabled later by `next_period`.
true
}
}
impl Driver for TimerDriver {
fn now(&self) -> u64 {
if SCALE.get().is_none() {
return 0;
}
let mut period1: u32;
let mut period2: u32;
let mut counter_val: u32;
loop {
// Acquire ensures that we get the latest value of `periods` and
// no instructions can be reordered before the load.
period1 = self.periods.load(Ordering::Acquire);
counter_val = u32::MAX - Self::timekeeper_tim().read_count_value();
// Double read to protect against race conditions when the counter is overflowing.
period2 = self.periods.load(Ordering::Relaxed);
if period1 == period2 {
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
return now;
}
}
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}
}
})
}
}

333
src/gpio/asynch.rs Normal file
View File

@ -0,0 +1,333 @@
//! # Async GPIO functionality for the Vorago GPIO peripherals.
//!
//! This module provides the [InputPinAsync] which implements
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
//! which must be provided for async support to work. However, it provides the
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
use core::future::Future;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool;
#[cfg(feature = "vor4x")]
use crate::NUM_PORT_DEFAULT;
#[cfg(feature = "vor1x")]
use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
#[cfg(feature = "vor4x")]
use super::ll::PortDoesNotSupportInterrupts;
#[cfg(feature = "vor1x")]
use va108xx as pac;
pub use super::ll::InterruptEdge;
use super::{
Input, Port,
ll::{LowLevelGpio, PinId},
};
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_A] = [const { AtomicWaker::new() }; NUM_PORT_A];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_B] = [const { AtomicWaker::new() }; NUM_PORT_B];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_A] =
[const { AtomicBool::new(false) }; NUM_PORT_A];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] =
[const { AtomicBool::new(false) }; NUM_PORT_B];
} else {
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PORT_DEFAULT] =
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PORT_DEFAULT] =
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
}
}
#[inline]
fn pin_group_to_waker_and_edge_detection_group(
port: Port,
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
match port {
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
#[cfg(feature = "vor4x")]
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
#[cfg(feature = "vor4x")]
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
#[cfg(feature = "vor4x")]
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
#[cfg(feature = "vor4x")]
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
#[cfg(feature = "vor4x")]
Port::G => unreachable!(),
}
}
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
///
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
/// matching the [Port] argument.
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
/// as well as update the static edge detection structures. This allows the pin future to complete
/// complete async operations.
#[cfg(feature = "vor1x")]
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
on_interrupt_for_async_gpio_for_port_generic(port);
}
#[cfg(feature = "vor4x")]
pub fn on_interrupt_for_async_gpio_for_port(
port: Port,
) -> Result<(), PortDoesNotSupportInterrupts> {
if port == Port::G {
return Err(PortDoesNotSupportInterrupts);
}
on_interrupt_for_async_gpio_for_port_generic(port);
Ok(())
}
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
let gpio = unsafe { port.steal_gpio() };
let irq_enb = gpio.read_irq_enable();
let edge_status = gpio.read_edge_status();
let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
}
#[inline]
fn on_interrupt_for_port(
mut irq_enb: u32,
edge_status: u32,
wakers: &'static [AtomicWaker],
edge_detection: &'static [AtomicBool],
) {
while irq_enb != 0 {
let bit_pos = irq_enb.trailing_zeros() as usize;
let bit_mask = 1 << bit_pos;
wakers[bit_pos].wake();
if edge_status & bit_mask != 0 {
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
// Clear the processed bit
irq_enb &= !bit_mask;
}
}
}
/// Input pin future which implements the [Future] trait.
///
/// Generally, you want to use the [InputPinAsync] types instead of this
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
/// struture is granted to allow writing custom async structures.
pub struct InputPinFuture {
id: PinId,
waker_group: &'static [AtomicWaker],
edge_detection_group: &'static [AtomicBool],
}
impl InputPinFuture {
#[cfg(feature = "vor1x")]
pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
let (waker_group, edge_detection_group) =
pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(edge);
#[cfg(feature = "vor1x")]
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Self {
id: pin.id(),
waker_group,
edge_detection_group,
}
}
#[cfg(feature = "vor4x")]
pub fn new_with_input_pin(
pin: &mut Input,
edge: InterruptEdge,
) -> Result<Self, PortDoesNotSupportInterrupts> {
let (waker_group, edge_detection_group) =
pin_group_to_waker_and_edge_detection_group(pin.id().port());
pin.configure_edge_interrupt(edge);
pin.enable_interrupt(true)?;
Ok(Self {
id: pin.id(),
waker_group,
edge_detection_group,
})
}
}
impl Drop for InputPinFuture {
fn drop(&mut self) {
let mut ll = LowLevelGpio::new(self.id);
#[cfg(feature = "vor1x")]
ll.disable_interrupt(false);
#[cfg(feature = "vor4x")]
ll.disable_interrupt();
}
}
impl Future for InputPinFuture {
type Output = ();
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let idx = self.id.offset();
self.waker_group[idx].register(cx.waker());
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
return core::task::Poll::Ready(());
}
core::task::Poll::Pending
}
}
pub struct InputPinAsync {
pin: Input,
#[cfg(feature = "vor1x")]
irq: va108xx::Interrupt,
}
impl InputPinAsync {
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
/// for the asynchronous functionality to work.
#[cfg(feature = "vor1x")]
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
Self { pin, irq }
}
#[cfg(feature = "vor4x")]
pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
if pin.id().port() == Port::G {
return Err(PortDoesNotSupportInterrupts);
}
Ok(Self { pin })
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
// Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")]
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
#[cfg(feature = "vor4x")]
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
if self.pin.is_high() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin is low.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_low(&mut self) {
// Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")]
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
#[cfg(feature = "vor4x")]
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
if self.pin.is_low() {
return;
}
fut.await;
}
/// Asynchronously wait until the pin sees a falling edge.
pub async fn wait_for_falling_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
#[cfg(feature = "vor4x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
.unwrap()
.await;
}
/// Asynchronously wait until the pin sees a rising edge.
pub async fn wait_for_rising_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
}
/// Asynchronously wait until the pin sees any edge (either rising or falling).
pub async fn wait_for_any_edge(&mut self) {
// Unwrap okay, checked pin in constructor.
#[cfg(feature = "vor1x")]
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
#[cfg(feature = "vor4x")]
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
.unwrap()
.await;
}
pub fn release(self) -> Input {
self.pin
}
}
impl embedded_hal::digital::ErrorType for InputPinAsync {
type Error = core::convert::Infallible;
}
impl Wait for InputPinAsync {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
self.wait_for_high().await;
Ok(())
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
self.wait_for_low().await;
Ok(())
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_rising_edge().await;
Ok(())
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_falling_edge().await;
Ok(())
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
self.wait_for_any_edge().await;
Ok(())
}
}

584
src/gpio/ll.rs Normal file
View File

@ -0,0 +1,584 @@
pub use embedded_hal::digital::PinState;
use crate::ioconfig::FilterClkSel;
use crate::ioconfig::FilterType;
#[cfg(feature = "vor1x")]
use crate::{PeripheralSelect, sysconfig::enable_peripheral_clock};
pub use crate::InvalidOffsetError;
pub use crate::Port;
pub use crate::ioconfig::regs::Pull;
use crate::ioconfig::regs::{FunSel, IoConfig, MmioIoConfig};
use super::Pin;
use super::PinIdProvider;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
/// Pin identifier for all physical pins exposed by Vorago MCUs.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PinId {
port: Port,
/// Offset within the port.
offset: u8,
}
#[derive(Debug, thiserror::Error)]
#[cfg(feature = "vor4x")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("port G does not support interrupts")]
pub struct PortDoesNotSupportInterrupts;
impl PinId {
/// Unchecked constructor which panics on invalid offsets.
pub const fn new_unchecked(port: Port, offset: usize) -> Self {
if offset >= port.max_offset() {
panic!("Pin ID construction: offset is out of range");
}
PinId {
port,
offset: offset as u8,
}
}
pub const fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
if offset >= port.max_offset() {
return Err(InvalidOffsetError { offset, port });
}
Ok(PinId {
port,
offset: offset as u8,
})
}
pub const fn port(&self) -> Port {
self.port
}
pub const fn offset(&self) -> usize {
self.offset as usize
}
/// This function panics if the port is [Port::G].
#[cfg(feature = "vor4x")]
pub fn irq(&self) -> Result<va416xx::Interrupt, PortDoesNotSupportInterrupts> {
if self.port() == Port::G {
return Err(PortDoesNotSupportInterrupts);
}
Ok(self.irq_unchecked())
}
/// This function panics if the port is [Port::G].
#[cfg(feature = "vor4x")]
pub const fn irq_unchecked(&self) -> va416xx::Interrupt {
match self.port() {
Port::A => match self.offset() {
0 => va416xx::Interrupt::PORTA0,
1 => va416xx::Interrupt::PORTA1,
2 => va416xx::Interrupt::PORTA2,
3 => va416xx::Interrupt::PORTA3,
4 => va416xx::Interrupt::PORTA4,
5 => va416xx::Interrupt::PORTA5,
6 => va416xx::Interrupt::PORTA6,
7 => va416xx::Interrupt::PORTA7,
8 => va416xx::Interrupt::PORTA8,
9 => va416xx::Interrupt::PORTA9,
10 => va416xx::Interrupt::PORTA10,
11 => va416xx::Interrupt::PORTA11,
12 => va416xx::Interrupt::PORTA12,
13 => va416xx::Interrupt::PORTA13,
14 => va416xx::Interrupt::PORTA14,
15 => va416xx::Interrupt::PORTA15,
_ => unreachable!(),
},
Port::B => match self.offset() {
0 => va416xx::Interrupt::PORTB0,
1 => va416xx::Interrupt::PORTB1,
2 => va416xx::Interrupt::PORTB2,
3 => va416xx::Interrupt::PORTB3,
4 => va416xx::Interrupt::PORTB4,
5 => va416xx::Interrupt::PORTB5,
6 => va416xx::Interrupt::PORTB6,
7 => va416xx::Interrupt::PORTB7,
8 => va416xx::Interrupt::PORTB8,
9 => va416xx::Interrupt::PORTB9,
10 => va416xx::Interrupt::PORTB10,
11 => va416xx::Interrupt::PORTB11,
12 => va416xx::Interrupt::PORTB12,
13 => va416xx::Interrupt::PORTB13,
14 => va416xx::Interrupt::PORTB14,
15 => va416xx::Interrupt::PORTB15,
_ => unreachable!(),
},
Port::C => match self.offset() {
0 => va416xx::Interrupt::PORTC0,
1 => va416xx::Interrupt::PORTC1,
2 => va416xx::Interrupt::PORTC2,
3 => va416xx::Interrupt::PORTC3,
4 => va416xx::Interrupt::PORTC4,
5 => va416xx::Interrupt::PORTC5,
6 => va416xx::Interrupt::PORTC6,
7 => va416xx::Interrupt::PORTC7,
8 => va416xx::Interrupt::PORTC8,
9 => va416xx::Interrupt::PORTC9,
10 => va416xx::Interrupt::PORTC10,
11 => va416xx::Interrupt::PORTC11,
12 => va416xx::Interrupt::PORTC12,
13 => va416xx::Interrupt::PORTC13,
14 => va416xx::Interrupt::PORTC14,
15 => va416xx::Interrupt::PORTC15,
_ => unreachable!(),
},
Port::D => match self.offset() {
0 => va416xx::Interrupt::PORTD0,
1 => va416xx::Interrupt::PORTD1,
2 => va416xx::Interrupt::PORTD2,
3 => va416xx::Interrupt::PORTD3,
4 => va416xx::Interrupt::PORTD4,
5 => va416xx::Interrupt::PORTD5,
6 => va416xx::Interrupt::PORTD6,
7 => va416xx::Interrupt::PORTD7,
8 => va416xx::Interrupt::PORTD8,
9 => va416xx::Interrupt::PORTD9,
10 => va416xx::Interrupt::PORTD10,
11 => va416xx::Interrupt::PORTD11,
12 => va416xx::Interrupt::PORTD12,
13 => va416xx::Interrupt::PORTD13,
14 => va416xx::Interrupt::PORTD14,
15 => va416xx::Interrupt::PORTD15,
_ => unreachable!(),
},
Port::E => match self.offset() {
0 => va416xx::Interrupt::PORTE0,
1 => va416xx::Interrupt::PORTE1,
2 => va416xx::Interrupt::PORTE2,
3 => va416xx::Interrupt::PORTE3,
4 => va416xx::Interrupt::PORTE4,
5 => va416xx::Interrupt::PORTE5,
6 => va416xx::Interrupt::PORTE6,
7 => va416xx::Interrupt::PORTE7,
8 => va416xx::Interrupt::PORTE8,
9 => va416xx::Interrupt::PORTE9,
10 => va416xx::Interrupt::PORTE10,
11 => va416xx::Interrupt::PORTE11,
12 => va416xx::Interrupt::PORTE12,
13 => va416xx::Interrupt::PORTE13,
14 => va416xx::Interrupt::PORTE14,
15 => va416xx::Interrupt::PORTE15,
_ => unreachable!(),
},
Port::F => match self.offset() {
0 => va416xx::Interrupt::PORTF0,
1 => va416xx::Interrupt::PORTF1,
2 => va416xx::Interrupt::PORTF2,
3 => va416xx::Interrupt::PORTF3,
4 => va416xx::Interrupt::PORTF4,
5 => va416xx::Interrupt::PORTF5,
6 => va416xx::Interrupt::PORTF6,
7 => va416xx::Interrupt::PORTF7,
8 => va416xx::Interrupt::PORTF8,
9 => va416xx::Interrupt::PORTF9,
10 => va416xx::Interrupt::PORTF10,
11 => va416xx::Interrupt::PORTF11,
12 => va416xx::Interrupt::PORTF12,
13 => va416xx::Interrupt::PORTF13,
14 => va416xx::Interrupt::PORTF14,
15 => va416xx::Interrupt::PORTF15,
_ => unreachable!(),
},
Port::G => panic!("port G does not have interrupts"),
}
}
}
/// Low-level driver structure for GPIO pins.
pub struct LowLevelGpio {
gpio: super::regs::MmioGpio<'static>,
ioconfig: MmioIoConfig<'static>,
id: PinId,
}
impl core::fmt::Debug for LowLevelGpio {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LowLevelGpio")
.field("gpio", &self.gpio.port())
.field("id", &self.id)
.finish()
}
}
impl LowLevelGpio {
/// Create a new low-level GPIO pin instance from a given [Pin].
///
/// Can be used for performing resource management of the [Pin]s.
pub fn new_with_pin<I: PinIdProvider>(_pin: Pin<I>) -> Self {
Self::new(I::ID)
}
/// Create a new low-level GPIO pin instance using only the [PinId].
pub fn new(id: PinId) -> Self {
LowLevelGpio {
gpio: super::regs::Gpio::new_mmio(id.port),
ioconfig: IoConfig::new_mmio(),
id,
}
}
#[inline]
pub fn id(&self) -> PinId {
self.id
}
#[inline]
pub fn port(&self) -> Port {
self.id.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.id.offset()
}
pub fn configure_as_input_floating(&mut self) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
config.set_open_drain(false);
config.set_pull_enable(false);
config.set_pull_when_output_active(false);
config.set_invert_output(false);
config.set_input_enable_when_output(false);
config
});
self.gpio.modify_dir(|mut dir| {
dir &= !(1 << self.id.offset());
dir
});
}
pub fn configure_as_input_with_pull(&mut self, pull: Pull) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
config.set_open_drain(false);
config.set_pull_enable(true);
config.set_pull_dir(pull);
config.set_pull_when_output_active(false);
config.set_invert_output(false);
config.set_input_enable_when_output(false);
config
});
self.gpio.modify_dir(|mut dir| {
dir &= !(1 << self.id.offset());
dir
});
}
pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
config.set_open_drain(false);
config.set_pull_enable(false);
config.set_pull_when_output_active(false);
config.set_invert_output(false);
config.set_input_enable_when_output(true);
config
});
match init_level {
PinState::Low => self.gpio.write_clr_out(self.mask_32()),
PinState::High => self.gpio.write_set_out(self.mask_32()),
}
self.gpio.modify_dir(|mut dir| {
dir |= 1 << self.id.offset();
dir
});
}
pub fn configure_as_output_open_drain(&mut self, init_level: PinState) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
config.set_open_drain(true);
config.set_pull_enable(true);
config.set_pull_dir(Pull::Up);
config.set_pull_when_output_active(false);
config.set_invert_output(false);
config.set_input_enable_when_output(true);
config
});
let mask32 = self.mask_32();
match init_level {
PinState::Low => self.gpio.write_clr_out(mask32),
PinState::High => self.gpio.write_set_out(mask32),
}
self.gpio.modify_dir(|mut dir| {
dir |= mask32;
dir
});
}
pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunSel, pull: Option<Pull>) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_funsel(fun_sel);
config.set_io_disable(false);
config.set_invert_input(false);
config.set_open_drain(false);
config.set_pull_enable(pull.is_some());
config.set_pull_dir(pull.unwrap_or(Pull::Up));
config.set_invert_output(false);
config
});
}
#[inline]
pub fn is_high(&self) -> bool {
(self.gpio.read_data_in() >> self.offset()) & 1 == 1
}
#[inline]
pub fn is_low(&self) -> bool {
!self.is_high()
}
#[inline]
pub fn set_high(&mut self) {
self.gpio.write_set_out(self.mask_32());
}
#[inline]
pub fn set_low(&mut self) {
self.gpio.write_clr_out(self.mask_32());
}
#[inline]
pub fn is_set_high(&self) -> bool {
(self.gpio.read_data_out() >> self.offset()) & 1 == 1
}
#[inline]
pub fn is_set_low(&self) -> bool {
!self.is_set_high()
}
#[inline]
pub fn toggle(&mut self) {
self.gpio.write_tog_out(self.mask_32());
}
#[cfg(feature = "vor1x")]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
if irq_cfg.route {
self.configure_irqsel(irq_cfg.id);
}
if irq_cfg.enable_in_nvic {
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
}
self.gpio.modify_irq_enable(|mut value| {
value |= 1 << self.id.offset;
value
});
}
#[cfg(feature = "vor4x")]
pub fn enable_interrupt(
&mut self,
enable_in_nvic: bool,
) -> Result<(), PortDoesNotSupportInterrupts> {
if enable_in_nvic {
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
}
self.gpio.modify_irq_enable(|mut value| {
value |= 1 << self.id.offset;
value
});
Ok(())
}
#[cfg(feature = "vor1x")]
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
if reset_irqsel {
self.reset_irqsel();
}
// We only manipulate our own bit.
self.gpio.modify_irq_enable(|mut value| {
value &= !(1 << self.id.offset);
value
});
}
#[cfg(feature = "vor4x")]
pub fn disable_interrupt(&mut self) {
self.gpio.modify_irq_enable(|mut value| {
value &= !(1 << self.id.offset);
value
});
}
/// 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]
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
let mask32 = self.mask_32();
self.gpio.modify_irq_sen(|mut value| {
value &= !mask32;
value
});
match edge_type {
InterruptEdge::HighToLow => {
self.gpio.modify_irq_evt(|mut value| {
value &= !mask32;
value
});
}
InterruptEdge::LowToHigh => {
self.gpio.modify_irq_evt(|mut value| {
value |= mask32;
value
});
}
InterruptEdge::BothEdges => {
self.gpio.modify_irq_edge(|mut value| {
value |= mask32;
value
});
}
}
}
/// Configure which edge or level type triggers an interrupt
#[inline]
pub fn configure_level_interrupt(&mut self, level: InterruptLevel) {
let mask32 = self.mask_32();
self.gpio.modify_irq_sen(|mut value| {
value |= mask32;
value
});
if level == InterruptLevel::Low {
self.gpio.modify_irq_evt(|mut value| {
value &= !mask32;
value
});
} else {
self.gpio.modify_irq_evt(|mut value| {
value |= mask32;
value
});
}
}
/// Only useful for input pins
#[inline]
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.ioconfig.modify_pin_config(self.id, |mut config| {
config.set_filter_type(filter);
config.set_filter_clk_sel(clksel);
config
});
}
/// Only useful for output pins.
#[inline]
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.gpio.modify_pulse(|mut value| {
if enable {
value |= 1 << self.id.offset;
} else {
value &= !(1 << self.id.offset);
}
value
});
self.gpio.modify_pulsebase(|mut value| {
if default_state == PinState::High {
value |= 1 << self.id.offset;
} else {
value &= !(1 << self.id.offset);
}
value
});
}
/// Only useful for output pins
#[inline]
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.gpio.modify_delay1(|mut value| {
if delay_1 {
value |= 1 << self.id.offset;
} else {
value &= !(1 << self.id.offset);
}
value
});
self.gpio.modify_delay2(|mut value| {
if delay_2 {
value |= 1 << self.id.offset;
} else {
value &= !(1 << self.id.offset);
}
value
});
}
#[cfg(feature = "vor1x")]
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
pub fn configure_irqsel(&mut self, id: va108xx::Interrupt) {
let irqsel = unsafe { va108xx::Irqsel::steal() };
enable_peripheral_clock(PeripheralSelect::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().offset())
.write(|w| unsafe { w.bits(id as u32) });
}
super::Port::B => {
irqsel
.portb0(self.id().offset())
.write(|w| unsafe { w.bits(id as u32) });
}
}
}
#[cfg(feature = "vor1x")]
/// Reset the IRQSEL peripheral value for this particular pin.
pub fn reset_irqsel(&mut self) {
let irqsel = unsafe { va108xx::Irqsel::steal() };
enable_peripheral_clock(PeripheralSelect::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().offset())
.write(|w| unsafe { w.bits(u32::MAX) });
}
super::Port::B => {
irqsel
.portb0(self.id().offset())
.write(|w| unsafe { w.bits(u32::MAX) });
}
}
}
#[inline(always)]
pub const fn mask_32(&self) -> u32 {
1 << self.id.offset()
}
}

394
src/gpio/mod.rs Normal file
View File

@ -0,0 +1,394 @@
//! GPIO support module.
use core::convert::Infallible;
pub use crate::ioconfig::{regs::FunSel, FilterClkSel, FilterType};
pub use embedded_hal::digital::PinState;
pub use ll::{InterruptEdge, InterruptLevel, PinId, Port, Pull};
pub mod asynch;
pub mod ll;
pub mod regs;
/// Trait implemented by data structures assocaited with pin identifiacation.
pub trait PinIdProvider {
const ID: ll::PinId;
}
/// Primary Pin structure for the physical pins exposed by Vorago MCUs.
///
/// This pin structure is only used for resource management and does not do anything on its
/// own.
pub struct Pin<I: PinIdProvider> {
phantom: core::marker::PhantomData<I>,
}
impl<I: PinIdProvider> Pin<I> {
#[allow(clippy::new_without_default)]
#[doc(hidden)]
pub const fn __new() -> Self {
Self {
phantom: core::marker::PhantomData,
}
}
/// Create a new pin instance.
///
/// # Safety
///
/// This circumvents ownership rules of the HAL and allows creating multiple instances
/// of the same pin.
pub const unsafe fn steal() -> Self {
Self::__new()
}
}
/// Push-Pull output pin.
#[derive(Debug)]
pub struct Output(ll::LowLevelGpio);
impl Output {
pub fn new<I: PinIdProvider>(_pin: Pin<I>, init_level: PinState) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_output_push_pull(init_level);
Output(ll)
}
#[inline]
pub fn port(&self) -> Port {
self.0.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.0.offset()
}
#[inline]
pub fn set_high(&mut self) {
self.0.set_high();
}
#[inline]
pub fn set_low(&mut self) {
self.0.set_low();
}
#[inline]
pub fn is_set_high(&self) -> bool {
self.0.is_set_high()
}
#[inline]
pub fn is_set_low(&self) -> bool {
self.0.is_set_low()
}
/// Toggle pin output with dedicated HW feature.
#[inline]
pub fn toggle(&mut self) {
self.0.toggle();
}
#[inline]
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.0.configure_pulse_mode(enable, default_state);
}
#[inline]
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.0.configure_delay(delay_1, delay_2);
}
}
impl embedded_hal::digital::ErrorType for Output {
type Error = Infallible;
}
impl embedded_hal::digital::OutputPin for Output {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.0.set_low();
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.0.set_high();
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for Output {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.0.is_set_high())
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.0.is_set_low())
}
/// Toggle pin output with dedicated HW feature.
fn toggle(&mut self) -> Result<(), Self::Error> {
self.0.toggle();
Ok(())
}
}
/// Input pin.
///
/// Can be created as a floating input pin or as an input pin with pull-up or pull-down.
#[derive(Debug)]
pub struct Input(ll::LowLevelGpio);
impl Input {
pub fn new_floating<I: PinIdProvider>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_floating();
Input(ll)
}
pub fn new_with_pull<I: PinIdProvider>(_pin: Pin<I>, pull: Pull) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_with_pull(pull);
Input(ll)
}
#[inline]
pub fn id(&self) -> PinId {
self.0.id()
}
#[cfg(feature = "vor1x")]
#[inline]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
self.0.enable_interrupt(irq_cfg);
}
#[cfg(feature = "vor4x")]
#[inline]
pub fn enable_interrupt(
&mut self,
enable_in_nvic: bool,
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
self.0.enable_interrupt(enable_in_nvic)
}
#[inline]
pub fn configure_edge_interrupt(&mut self, edge: InterruptEdge) {
self.0.configure_edge_interrupt(edge);
}
#[inline]
pub fn configure_level_interrupt(&mut self, edge: InterruptLevel) {
self.0.configure_level_interrupt(edge);
}
#[inline]
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.0.configure_delay(delay_1, delay_2);
}
#[inline]
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.0.configure_filter_type(filter, clksel);
}
#[inline]
pub fn is_low(&self) -> bool {
self.0.is_low()
}
#[inline]
pub fn is_high(&self) -> bool {
self.0.is_high()
}
}
impl embedded_hal::digital::ErrorType for Input {
type Error = Infallible;
}
impl embedded_hal::digital::InputPin for Input {
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.0.is_low())
}
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.0.is_high())
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PinMode {
InputFloating,
InputWithPull(Pull),
OutputPushPull,
OutputOpenDrain,
}
impl PinMode {
pub fn is_input(&self) -> bool {
matches!(self, PinMode::InputFloating | PinMode::InputWithPull(_))
}
pub fn is_output(&self) -> bool {
!self.is_input()
}
}
/// Flex pin abstraction which can be dynamically re-configured.
///
/// The following functions can be configured at run-time:
///
/// - Input Floating
/// - Input with Pull-Up
/// - Output Push-Pull
/// - Output Open-Drain.
///
/// Flex pins are always floating input pins after construction.
#[derive(Debug)]
pub struct Flex {
ll: ll::LowLevelGpio,
mode: PinMode,
}
impl Flex {
pub fn new<I: PinIdProvider>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_floating();
Flex {
ll,
mode: PinMode::InputFloating,
}
}
#[inline]
pub fn port(&self) -> Port {
self.ll.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.ll.offset()
}
/// Reads the input state of the pin, regardless of configured mode.
#[inline]
pub fn is_low(&self) -> bool {
self.ll.is_low()
}
/// Reads the input state of the pin, regardless of configured mode.
#[inline]
pub fn is_high(&self) -> bool {
self.ll.is_high()
}
/// If the pin is configured as an input pin, this function does nothing.
#[inline]
pub fn set_low(&mut self) {
if !self.mode.is_input() {
return;
}
self.ll.set_low();
}
/// If the pin is configured as an input pin, this function does nothing.
#[inline]
pub fn set_high(&mut self) {
if !self.mode.is_input() {
return;
}
self.ll.set_high();
}
}
impl embedded_hal::digital::ErrorType for Flex {
type Error = Infallible;
}
impl embedded_hal::digital::InputPin for Flex {
/// Reads the input state of the pin, regardless of configured mode.
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.ll.is_low())
}
/// Reads the input state of the pin, regardless of configured mode.
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.ll.is_high())
}
}
impl embedded_hal::digital::OutputPin for Flex {
/// If the pin is configured as an input pin, this function does nothing.
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low();
Ok(())
}
/// If the pin is configured as an input pin, this function does nothing.
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high();
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for Flex {
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
/// of this function is undefined.
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.ll.is_set_high())
}
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
/// of this function is undefined.
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.ll.is_set_low())
}
/// Toggle pin output.
///
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
/// of this function is undefined.
fn toggle(&mut self) -> Result<(), Self::Error> {
self.ll.toggle();
Ok(())
}
}
/// IO peripheral pin structure.
///
/// Can be used to configure pins as IO peripheral pins.
pub struct IoPeriphPin {
ll: ll::LowLevelGpio,
fun_sel: FunSel,
}
impl IoPeriphPin {
pub fn new_with_pin<I: PinIdProvider>(
_pin: Pin<I>,
fun_sel: FunSel,
pull: Option<Pull>,
) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_peripheral_pin(fun_sel, pull);
IoPeriphPin { ll, fun_sel }
}
pub fn new(pin_id: PinId, fun_sel: FunSel, pull: Option<Pull>) -> Self {
let mut ll = ll::LowLevelGpio::new(pin_id);
ll.configure_as_peripheral_pin(fun_sel, pull);
IoPeriphPin { ll, fun_sel }
}
pub fn port(&self) -> Port {
self.ll.port()
}
pub fn offset(&self) -> usize {
self.ll.offset()
}
pub fn fun_sel(&self) -> FunSel {
self.fun_sel
}
}

126
src/gpio/regs.rs Normal file
View File

@ -0,0 +1,126 @@
use crate::Port;
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// PORT A base address.
pub const GPIO_0_BASE: usize = 0x5000_0000;
/// PORT B base address.
pub const GPIO_1_BASE: usize = 0x5000_1000;
} else if #[cfg(feature = "vor4x")] {
/// PORT A base address.
pub const GPIO_0_BASE: usize = 0x4001_2000;
/// PORT B base address.
pub const GPIO_1_BASE: usize = 0x4001_2400;
/// PORT C base address.
pub const GPIO_2_BASE: usize = 0x4001_2800;
/// PORT D base address.
pub const GPIO_3_BASE: usize = 0x4001_2C00;
/// PORT E base address.
pub const GPIO_4_BASE: usize = 0x4001_3000;
/// PORT F base address.
pub const GPIO_5_BASE: usize = 0x4001_3400;
/// PORT G base address.
pub const GPIO_6_BASE: usize = 0x4001_3800;
}
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct Gpio {
#[mmio(PureRead)]
data_in: u32,
#[mmio(PureRead)]
data_in_raw: u32,
data_out: u32,
data_out_raw: u32,
#[mmio(Write)]
set_out: u32,
#[mmio(Write)]
clr_out: u32,
#[mmio(Write)]
tog_out: u32,
data_mask: u32,
/// Direction bits. 1 for output, 0 for input.
dir: u32,
pulse: u32,
pulsebase: u32,
delay1: u32,
delay2: u32,
irq_sen: u32,
irq_edge: u32,
irq_evt: u32,
irq_enable: u32,
/// Raw interrupt status. This register is not latched and may not indicated edge sensitive
/// events.
#[mmio(PureRead)]
irq_raw: u32,
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
#[mmio(PureRead)]
irq_status: u32,
#[mmio(PureRead)]
edge_status: u32,
#[cfg(feature = "vor1x")]
_reserved: [u32; 0x3eb],
#[cfg(feature = "vor4x")]
_reserved: [u32; 0xeb],
/// Peripheral ID. Vorago 1x reset value: 0x0040_07e1. Vorago 4x reset value: 0x0210_07E9.
perid: u32,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x1000);
} else if #[cfg(feature = "vor4x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x400);
}
}
impl Gpio {
const fn new_mmio_at(base: usize) -> MmioGpio<'static> {
MmioGpio {
ptr: base as *mut _,
phantom: core::marker::PhantomData,
}
}
pub const fn new_mmio(port: Port) -> MmioGpio<'static> {
match port {
Port::A => Self::new_mmio_at(GPIO_0_BASE),
Port::B => Self::new_mmio_at(GPIO_1_BASE),
#[cfg(feature = "vor4x")]
Port::C => Self::new_mmio_at(GPIO_2_BASE),
#[cfg(feature = "vor4x")]
Port::D => Self::new_mmio_at(GPIO_3_BASE),
#[cfg(feature = "vor4x")]
Port::E => Self::new_mmio_at(GPIO_4_BASE),
#[cfg(feature = "vor4x")]
Port::F => Self::new_mmio_at(GPIO_5_BASE),
#[cfg(feature = "vor4x")]
Port::G => Self::new_mmio_at(GPIO_6_BASE),
}
}
}
impl MmioGpio<'_> {
pub fn port(&self) -> Port {
match unsafe { self.ptr() } as usize {
GPIO_0_BASE => Port::A,
GPIO_1_BASE => Port::B,
#[cfg(feature = "vor4x")]
GPIO_2_BASE => Port::C,
#[cfg(feature = "vor4x")]
GPIO_3_BASE => Port::D,
#[cfg(feature = "vor4x")]
GPIO_4_BASE => Port::E,
#[cfg(feature = "vor4x")]
GPIO_5_BASE => Port::F,
#[cfg(feature = "vor4x")]
GPIO_6_BASE => Port::G,
// Constructors were disabled, so this should really not happen.
_ => panic!("unexpected base address of GPIO register block"),
}
}
}

705
src/i2c/mod.rs Normal file
View File

@ -0,0 +1,705 @@
pub mod regs;
use crate::{
PeripheralSelect, enable_peripheral_clock, sealed::Sealed,
sysconfig::reset_peripheral_for_cycles, time::Hertz,
};
use arbitrary_int::{u4, u10, u11, u20};
use core::marker::PhantomData;
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
use regs::ClkTimeoutLimit;
pub use regs::{Bank, I2cSpeed, RxFifoFullMode, TxFifoEmptyMode};
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
//==================================================================================================
// 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(8_000_000);
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("clock too slow for fast I2C mode")]
pub struct ClockTooSlowForFastI2cError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[error("invalid timing parameters")]
pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
#[error("arbitration lost")]
ArbitrationLost,
#[error("nack address")]
NackAddr,
/// Data not acknowledged in write operation
#[error("data not acknowledged in write operation")]
NackData,
/// Not enough data received in read operation
#[error("insufficient data received")]
InsufficientDataReceived,
/// Number of bytes in transfer too large (larger than 0x7fe)
#[error("data too large (larger than 0x7fe)")]
DataTooLarge,
#[error("clock timeout, SCL was low for {0} clock cycles")]
ClockTimeout(u20),
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitError {
/// Wrong address used in constructor
#[error("wrong address mode")]
WrongAddrMode,
/// APB1 clock is too slow for fast I2C mode.
#[error("clock too slow for fast I2C mode: {0}")]
ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
}
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::ClockTimeout(_) => {
embedded_hal::i2c::ErrorKind::Other
}
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cCmd {
Start = 0b01,
Stop = 0b10,
StartWithStop = 0b11,
Cancel = 0b100,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cAddress {
Regular(u8),
TenBit(u16),
}
impl I2cAddress {
pub fn ten_bit_addr(&self) -> bool {
match self {
I2cAddress::Regular(_) => false,
I2cAddress::TenBit(_) => true,
}
}
pub fn raw(&self) -> u16 {
match self {
I2cAddress::Regular(addr) => *addr as u16,
I2cAddress::TenBit(addr) => *addr,
}
}
}
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait I2cMarker: Sealed {
const ID: Bank;
const PERIPH_SEL: PeripheralSelect;
}
#[cfg(feature = "vor1x")]
pub type I2c0 = pac::I2ca;
#[cfg(feature = "vor4x")]
pub type I2c0 = pac::I2c0;
impl I2cMarker for I2c0 {
const ID: Bank = Bank::I2c0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
}
impl Sealed for I2c0 {}
#[cfg(feature = "vor1x")]
pub type I2c1 = pac::I2cb;
#[cfg(feature = "vor4x")]
pub type I2c1 = pac::I2c1;
impl I2cMarker for I2c1 {
const ID: Bank = Bank::I2c1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
}
impl Sealed for I2c1 {}
//==================================================================================================
// Config
//==================================================================================================
fn calc_clk_div_generic(
ref_clk: Hertz,
speed_mode: I2cSpeed,
) -> Result<u8, ClockTooSlowForFastI2cError> {
if speed_mode == I2cSpeed::Regular100khz {
Ok(((ref_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else {
if ref_clk.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2cError);
}
Ok(((ref_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
}
}
#[cfg(feature = "vor4x")]
fn calc_clk_div(
clks: &crate::clock::Clocks,
speed_mode: I2cSpeed,
) -> Result<u8, ClockTooSlowForFastI2cError> {
calc_clk_div_generic(clks.apb1(), speed_mode)
}
#[cfg(feature = "vor1x")]
fn calc_clk_div(sys_clk: Hertz, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
calc_clk_div_generic(sys_clk, speed_mode)
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimingConfig {
pub t_rise: u4,
pub t_fall: u4,
pub t_high: u4,
pub t_low: u4,
pub tsu_stop: u4,
pub tsu_start: u4,
pub thd_start: u4,
pub t_buf: u4,
}
/// Default configuration are the register reset value which are used by default.
impl Default for TimingConfig {
fn default() -> Self {
TimingConfig {
t_rise: u4::new(0b0010),
t_fall: u4::new(0b0001),
t_high: u4::new(0b1000),
t_low: u4::new(0b1001),
tsu_stop: u4::new(0b1000),
tsu_start: u4::new(0b1010),
thd_start: u4::new(0b1000),
t_buf: u4::new(0b1010),
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MasterConfig {
pub tx_empty_mode: TxFifoEmptyMode,
pub rx_full_mode: RxFifoFullMode,
/// Enable the analog delay glitch filter
pub alg_filt: bool,
/// Enable the digital glitch filter
pub dlg_filt: bool,
pub timing_config: Option<TimingConfig>,
/// See [I2cMaster::set_clock_low_timeout] documentation.
pub timeout: Option<u20>,
// Loopback mode
// lbm: bool,
}
impl Default for MasterConfig {
fn default() -> Self {
MasterConfig {
tx_empty_mode: TxFifoEmptyMode::Stall,
rx_full_mode: RxFifoFullMode::Stall,
alg_filt: false,
dlg_filt: false,
timeout: None,
timing_config: None,
}
}
}
impl Sealed for MasterConfig {}
#[derive(Debug, PartialEq, Eq)]
enum WriteCompletionCondition {
Idle,
Waiting,
}
struct TimeoutGuard {
clk_timeout_enabled: bool,
regs: regs::MmioI2c<'static>,
}
impl TimeoutGuard {
fn new(regs: &regs::MmioI2c<'static>) -> Self {
let clk_timeout_enabled = regs.read_clk_timeout_limit().value().value() > 0;
let mut guard = TimeoutGuard {
clk_timeout_enabled,
regs: unsafe { regs.clone() },
};
if clk_timeout_enabled {
// Clear any interrupts which might be pending.
guard.regs.write_irq_clear(
regs::InterruptClear::builder()
.with_clock_timeout(true)
.with_tx_overflow(false)
.with_rx_overflow(false)
.build(),
);
guard.regs.modify_irq_enb(|mut value| {
value.set_clock_timeout(true);
value
});
}
guard
}
fn timeout_enabled(&self) -> bool {
self.clk_timeout_enabled
}
}
impl Drop for TimeoutGuard {
fn drop(&mut self) {
if self.clk_timeout_enabled {
self.regs.modify_irq_enb(|mut value| {
value.set_clock_timeout(false);
value
});
}
}
}
//==================================================================================================
// I2C Master
//==================================================================================================
pub struct I2cMaster<Addr = SevenBitAddress> {
id: Bank,
regs: regs::MmioI2c<'static>,
addr: PhantomData<Addr>,
}
impl<Addr> I2cMaster<Addr> {
pub fn new<I2c: I2cMarker>(
_i2c: I2c,
#[cfg(feature = "vor1x")] sysclk: Hertz,
#[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
cfg: MasterConfig,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
reset_peripheral_for_cycles(I2c::PERIPH_SEL, 2);
enable_peripheral_clock(I2c::PERIPH_SEL);
let mut regs = regs::I2c::new_mmio(I2c::ID);
#[cfg(feature = "vor1x")]
let clk_div = calc_clk_div(sysclk, speed_mode)?;
#[cfg(feature = "vor4x")]
let clk_div = calc_clk_div(clks, speed_mode)?;
regs.write_clkscale(
regs::ClkScale::builder()
.with_div(clk_div)
.with_fastmode(speed_mode)
.build(),
);
regs.modify_control(|mut value| {
value.set_tx_fifo_empty_mode(cfg.tx_empty_mode);
value.set_rx_fifo_full_mode(cfg.rx_full_mode);
value.set_analog_filter(cfg.alg_filt);
value.set_digital_filter(cfg.dlg_filt);
value
});
if let Some(ref timing_cfg) = cfg.timing_config {
regs.modify_control(|mut value| {
value.set_enable_timing_config(true);
value
});
regs.write_timing_config(
regs::TimingConfig::builder()
.with_t_rise(timing_cfg.t_rise)
.with_t_fall(timing_cfg.t_fall)
.with_t_high(timing_cfg.t_high)
.with_t_low(timing_cfg.t_low)
.with_tsu_stop(timing_cfg.tsu_stop)
.with_tsu_start(timing_cfg.tsu_start)
.with_thd_start(timing_cfg.thd_start)
.with_t_buf(timing_cfg.t_buf)
.build(),
);
}
regs.write_fifo_clear(
regs::FifoClear::builder()
.with_tx_fifo(true)
.with_rx_fifo(true)
.build(),
);
if let Some(timeout) = cfg.timeout {
regs.write_clk_timeout_limit(ClkTimeoutLimit::new(timeout));
}
let mut i2c_master = I2cMaster {
addr: PhantomData,
id: I2c::ID,
regs,
};
i2c_master.enable();
Ok(i2c_master)
}
pub const fn id(&self) -> Bank {
self.id
}
#[inline]
pub fn perid(&self) -> u32 {
self.regs.read_perid()
}
/// Configures the clock scale for a given speed mode setting
pub fn set_clk_scale(
&mut self,
#[cfg(feature = "vor1x")] sys_clk: Hertz,
#[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
speed_mode: I2cSpeed,
) -> Result<(), ClockTooSlowForFastI2cError> {
self.disable();
#[cfg(feature = "vor1x")]
let clk_div = calc_clk_div(sys_clk, speed_mode)?;
#[cfg(feature = "vor4x")]
let clk_div = calc_clk_div(clks, speed_mode)?;
self.regs.write_clkscale(
regs::ClkScale::builder()
.with_div(clk_div)
.with_fastmode(speed_mode)
.build(),
);
self.enable();
Ok(())
}
#[inline]
pub fn cancel_transfer(&mut self) {
self.regs.write_cmd(
regs::Command::builder()
.with_start(false)
.with_stop(false)
.with_cancel(true)
.build(),
);
}
#[inline]
pub fn clear_tx_fifo(&mut self) {
self.regs.write_fifo_clear(
regs::FifoClear::builder()
.with_tx_fifo(true)
.with_rx_fifo(false)
.build(),
);
}
#[inline]
pub fn clear_rx_fifo(&mut self) {
self.regs.write_fifo_clear(
regs::FifoClear::builder()
.with_tx_fifo(false)
.with_rx_fifo(true)
.build(),
);
}
/// Configure a timeout limit on the amount of time the I2C clock is seen to be low.
/// The timeout is specified as I2C clock cycles.
///
/// If the timeout is enabled, the blocking transaction handlers provided by the [I2cMaster]
/// will poll the interrupt status register to check for timeouts. This can be used to avoid
/// hang-ups of the I2C bus.
#[inline]
pub fn set_clock_low_timeout(&mut self, clock_cycles: u20) {
self.regs
.write_clk_timeout_limit(ClkTimeoutLimit::new(clock_cycles));
}
#[inline]
pub fn disable_clock_low_timeout(&mut self) {
self.regs
.write_clk_timeout_limit(ClkTimeoutLimit::new(u20::new(0)));
}
#[inline]
pub fn enable(&mut self) {
self.regs.modify_control(|mut value| {
value.set_enable(true);
value
});
}
#[inline]
pub fn disable(&mut self) {
self.regs.modify_control(|mut value| {
value.set_enable(false);
value
});
}
#[inline(always)]
fn write_fifo_unchecked(&mut self, word: u8) {
self.regs.write_data(regs::Data::new(word));
}
#[inline(always)]
fn read_fifo_unchecked(&self) -> u8 {
self.regs.read_data().data()
}
#[inline]
pub fn read_status(&mut self) -> regs::Status {
self.regs.read_status()
}
#[inline]
pub fn write_command(&mut self, cmd: I2cCmd) {
self.regs
.write_cmd(regs::Command::new_with_raw_value(cmd as u32));
}
#[inline]
pub fn write_address(&mut self, addr: I2cAddress, dir: regs::Direction) {
self.regs.write_address(
regs::Address::builder()
.with_direction(dir)
.with_address(u10::new(addr.raw()))
.with_a10_mode(addr.ten_bit_addr())
.build(),
);
}
fn error_handler_write(&mut self, init_cmd: I2cCmd) {
if init_cmd == I2cCmd::Start {
self.write_command(I2cCmd::Stop);
}
// The other case is start with stop where, so a CANCEL command should not be necessary
// because the hardware takes care of it.
self.clear_tx_fifo();
}
/// Blocking write transaction on the I2C bus.
pub fn write_blocking(&mut self, addr: I2cAddress, output: &[u8]) -> Result<(), Error> {
self.write_blocking_generic(
I2cCmd::StartWithStop,
addr,
output,
WriteCompletionCondition::Idle,
)
}
/// Blocking read transaction on the I2C bus.
pub fn read_blocking(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
let len = buffer.len();
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Clear the receive FIFO
self.clear_rx_fifo();
let timeout_guard = TimeoutGuard::new(&self.regs);
// Load number of words
self.regs
.write_words(regs::Words::new(u11::new(len as u16)));
// Load address
self.write_address(addr, regs::Direction::Receive);
let mut buf_iter = buffer.iter_mut();
let mut read_bytes = 0;
// Start receive transfer
self.write_command(I2cCmd::StartWithStop);
loop {
let status = self.read_status();
if status.arb_lost() {
self.clear_rx_fifo();
return Err(Error::ArbitrationLost);
}
if status.nack_addr() {
self.clear_rx_fifo();
return Err(Error::NackAddr);
}
if status.idle() {
if read_bytes != len {
return Err(Error::InsufficientDataReceived);
}
return Ok(());
}
if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() {
return Err(Error::ClockTimeout(
self.regs.read_clk_timeout_limit().value(),
));
}
if status.rx_not_empty() {
if let Some(next_byte) = buf_iter.next() {
*next_byte = self.read_fifo_unchecked();
}
read_bytes += 1;
}
}
}
fn write_blocking_generic(
&mut self,
init_cmd: I2cCmd,
addr: I2cAddress,
output: &[u8],
end_condition: WriteCompletionCondition,
) -> Result<(), Error> {
let len = output.len();
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Clear the send FIFO
self.clear_tx_fifo();
let timeout_guard = TimeoutGuard::new(&self.regs);
// Load number of words
self.regs
.write_words(regs::Words::new(u11::new(len as u16)));
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;
let mut current_index = core::cmp::min(FILL_DEPTH, len);
// load the FIFO
for _ in 0..current_index {
self.write_fifo_unchecked(*bytes.next().unwrap());
}
self.write_address(addr, regs::Direction::Send);
self.write_command(init_cmd);
loop {
let status = self.regs.read_status();
if status.arb_lost() {
self.error_handler_write(init_cmd);
return Err(Error::ArbitrationLost);
}
if status.nack_addr() {
self.error_handler_write(init_cmd);
return Err(Error::NackAddr);
}
if status.nack_data() {
self.error_handler_write(init_cmd);
return Err(Error::NackData);
}
match end_condition {
WriteCompletionCondition::Idle => {
if status.idle() {
return Ok(());
}
}
WriteCompletionCondition::Waiting => {
if status.waiting() {
return Ok(());
}
}
}
if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() {
return Err(Error::ClockTimeout(
self.regs.read_clk_timeout_limit().value(),
));
}
if status.tx_not_full() && current_index < len {
self.write_fifo_unchecked(output[current_index]);
current_index += 1;
}
}
}
/// Blocking write-read transaction on the I2C bus.
pub fn write_read_blocking(
&mut self,
address: I2cAddress,
write: &[u8],
read: &mut [u8],
) -> Result<(), Error> {
self.write_blocking_generic(
I2cCmd::Start,
address,
write,
WriteCompletionCondition::Waiting,
)?;
self.read_blocking(address, read)
}
}
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================
impl embedded_hal::i2c::ErrorType for I2cMaster<SevenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c for I2cMaster<SevenBitAddress> {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_blocking(I2cAddress::Regular(address), buf)?,
Operation::Write(buf) => self.write_blocking(I2cAddress::Regular(address), buf)?,
}
}
Ok(())
}
fn write_read(
&mut self,
address: u8,
write: &[u8],
read: &mut [u8],
) -> Result<(), Self::Error> {
let addr = I2cAddress::Regular(address);
self.write_read_blocking(addr, write, read)
}
}
impl embedded_hal::i2c::ErrorType for I2cMaster<TenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<TenBitAddress> {
fn transaction(
&mut self,
address: TenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_blocking(I2cAddress::TenBit(address), buf)?,
Operation::Write(buf) => self.write_blocking(I2cAddress::TenBit(address), buf)?,
}
}
Ok(())
}
fn write_read(
&mut self,
address: TenBitAddress,
write: &[u8],
read: &mut [u8],
) -> Result<(), Self::Error> {
let addr = I2cAddress::TenBit(address);
self.write_read_blocking(addr, write, read)
}
}

671
src/i2c/regs.rs Normal file
View File

@ -0,0 +1,671 @@
use core::marker::PhantomData;
use arbitrary_int::{u4, u5, u9, u10, u11, u20};
pub use crate::shared::{FifoClear, TriggerLevel};
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// I2C A base address
pub const BASE_ADDR_0: usize = 0x4006_0000;
/// I2C B base address
pub const BASE_ADDR_1: usize = 0x4006_1000;
} else if #[cfg(feature = "vor4x")] {
/// I2C 0 base address
pub const BASE_ADDR_0: usize = 0x4001_6000;
/// I2C 1 base address
pub const BASE_ADDR_1: usize = 0x4001_6400;
/// I2C 2 base address
pub const BASE_ADDR_2: usize = 0x4001_6800;
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
I2c0 = 0,
I2c1 = 1,
#[cfg(feature = "vor4x")]
I2c2 = 2,
}
impl Bank {
/// Unsafely steal the I2C peripheral block for the given port.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_regs(&self) -> MmioI2c<'static> {
I2c::new_mmio(*self)
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TxFifoEmptyMode {
/// I2C clock is stretched until data is available.
#[default]
Stall = 0,
EndTransaction = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RxFifoFullMode {
/// I2C clock is stretched until data is available.
#[default]
Stall = 0,
Nack = 1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Control {
#[bit(0, r)]
clk_enabled: bool,
#[bit(1, r)]
enabled: bool,
#[bit(2, rw)]
enable: bool,
#[bit(3, rw)]
tx_fifo_empty_mode: TxFifoEmptyMode,
#[bit(4, rw)]
rx_fifo_full_mode: RxFifoFullMode,
/// Enables the analog delay glitch filter.
#[bit(5, rw)]
analog_filter: bool,
/// Enables the digital glitch filter.
#[bit(6, rw)]
digital_filter: bool,
#[bit(8, rw)]
loopback: bool,
#[bit(9, rw)]
enable_timing_config: bool,
}
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cSpeed {
Regular100khz = 0,
Fast400khz = 1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct ClkScale {
/// Clock divide value. Reset value: 0x18.
#[bits(0..=7, rw)]
div: u8,
#[bit(31, rw)]
fastmode: I2cSpeed,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Words(arbitrary_int::UInt<u32, 11>);
impl Words {
pub const fn new(value: u11) -> Self {
Words(arbitrary_int::UInt::<u32, 11>::new(value.value() as u32))
}
pub const fn value(&self) -> u11 {
u11::new(self.0.value() as u16)
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Direction {
#[default]
Send = 0,
Receive = 1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Address {
#[bit(0, rw)]
direction: Direction,
#[bits(1..=10, rw)]
address: u10,
/// Enables 10-bit addressing mode.
#[bit(15, rw)]
a10_mode: bool,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Data(arbitrary_int::UInt<u32, 8>);
impl Data {
pub const fn new(value: u8) -> Self {
Data(arbitrary_int::UInt::<u32, 8>::new(value as u32))
}
pub const fn data(&self) -> u8 {
self.0.value() as u8
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Command {
#[bit(0, w)]
start: bool,
#[bit(1, w)]
stop: bool,
#[bit(2, w)]
cancel: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
#[bit(0, r)]
i2c_idle: bool,
#[bit(1, r)]
idle: bool,
#[bit(2, r)]
waiting: bool,
#[bit(3, r)]
stalled: bool,
#[bit(4, r)]
arb_lost: bool,
#[bit(5, r)]
nack_addr: bool,
#[bit(6, r)]
nack_data: bool,
#[bit(8, r)]
rx_not_empty: bool,
#[bit(9, r)]
rx_full: bool,
#[bit(11, r)]
rx_trigger: bool,
#[bit(12, r)]
tx_empty: bool,
#[bit(13, r)]
tx_not_full: bool,
#[bit(15, r)]
tx_trigger: bool,
#[bit(30, r)]
raw_sda: bool,
#[bit(31, r)]
raw_scl: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct State {
#[bits(0..=3, rw)]
state: u4,
#[bits(4..=7, rw)]
step: u4,
#[bits(8..=12, rw)]
rx_fifo: u5,
#[bits(14..=18, rw)]
tx_fifo: u5,
#[bits(20..=28, rw)]
bitstate: u9,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataCount(arbitrary_int::UInt<u32, 11>);
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(0, rw)]
i2c_idle: bool,
#[bit(1, rw)]
idle: bool,
#[bit(2, rw)]
waiting: bool,
#[bit(3, rw)]
stalled: bool,
#[bit(4, rw)]
arb_lost: bool,
#[bit(5, rw)]
nack_addr: bool,
#[bit(6, rw)]
nack_data: bool,
#[bit(7, rw)]
clock_timeout: bool,
#[bit(10, rw)]
tx_overflow: bool,
#[bit(11, rw)]
rx_overflow: bool,
#[bit(12, rw)]
tx_ready: bool,
#[bit(13, rw)]
rx_ready: bool,
#[bit(14, rw)]
tx_empty: bool,
#[bit(15, rw)]
rx_full: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(0, r)]
i2c_idle: bool,
#[bit(1, r)]
idle: bool,
#[bit(2, r)]
waiting: bool,
#[bit(3, r)]
stalled: bool,
#[bit(4, r)]
arb_lost: bool,
#[bit(5, r)]
nack_addr: bool,
#[bit(6, r)]
nack_data: bool,
#[bit(7, r)]
clock_timeout: bool,
#[bit(10, r)]
tx_overflow: bool,
#[bit(11, r)]
rx_overflow: bool,
#[bit(12, r)]
tx_ready: bool,
#[bit(13, r)]
rx_ready: bool,
#[bit(14, r)]
tx_empty: bool,
#[bit(15, r)]
rx_full: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptClear {
#[bit(7, w)]
clock_timeout: bool,
#[bit(10, w)]
tx_overflow: bool,
#[bit(11, w)]
rx_overflow: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct TimingConfig {
/// Rise time.
#[bits(0..=3, rw)]
t_rise: u4,
/// Fall time.
#[bits(4..=7, rw)]
t_fall: u4,
/// Duty cycle high time of SCL.
#[bits(8..=11, rw)]
t_high: u4,
/// Duty cycle low time of SCL.
#[bits(12..=15, rw)]
t_low: u4,
/// Setup time for STOP.
#[bits(16..=19, rw)]
tsu_stop: u4,
/// Setup time for START.
#[bits(20..=23, rw)]
tsu_start: u4,
/// Data hold time.
#[bits(24..=27, rw)]
thd_start: u4,
/// TBus free time between STOP and START.
#[bits(28..=31, rw)]
t_buf: u4,
}
pub struct ClkTimeoutLimit(pub arbitrary_int::UInt<u32, 20>);
impl ClkTimeoutLimit {
pub fn new(value: u20) -> Self {
ClkTimeoutLimit(arbitrary_int::UInt::<u32, 20>::new(value.value()))
}
pub fn value(&self) -> u20 {
self.0
}
}
pub mod slave {
use super::{Data, DataCount, FifoClear, RxFifoFullMode, TriggerLevel, TxFifoEmptyMode};
use arbitrary_int::{u3, u4, u5, u10, u11};
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Control {
#[bit(0, r)]
clk_enabled: bool,
#[bit(1, r)]
enabled: bool,
#[bit(2, rw)]
enable: bool,
#[bit(3, rw)]
tx_fifo_empty_mode: TxFifoEmptyMode,
#[bit(4, rw)]
rx_fifo_full_mode: RxFifoFullMode,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Maxwords {
#[bits(0..=10, rw)]
maxwords: u11,
#[bit(31, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Address {
#[bit(0, rw)]
rw: bool,
#[bits(1..=10, rw)]
address: u10,
#[bit(15, rw)]
a10_mode: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct AddressMask {
/// Will normally be 0 to match both read and write addresses.
#[bit(0, rw)]
rw_mask: bool,
/// Reset value 0x3FF.
#[bits(1..=10, rw)]
mask: u10,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum Direction {
#[default]
MasterSend = 0,
MasterReceive = 1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct LastAddress {
#[bit(0, rw)]
direction: Direction,
#[bits(1..=10, rw)]
address: u10,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
#[bit(0, r)]
completed: bool,
#[bit(1, r)]
idle: bool,
#[bit(2, r)]
waiting: bool,
#[bit(3, r)]
tx_stalled: bool,
#[bit(4, r)]
rx_stalled: bool,
#[bit(5, r)]
address_match: bool,
#[bit(6, r)]
nack_data: bool,
#[bit(7, r)]
rx_data_first: bool,
#[bit(8, r)]
rx_not_empty: bool,
#[bit(9, r)]
rx_full: bool,
#[bit(11, r)]
rx_trigger: bool,
#[bit(12, r)]
tx_empty: bool,
#[bit(13, r)]
tx_not_full: bool,
#[bit(15, r)]
tx_trigger: bool,
#[bit(28, r)]
raw_busy: bool,
#[bit(30, r)]
raw_sda: bool,
#[bit(31, r)]
raw_scl: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct State {
#[bits(0..=2, rw)]
state: u3,
#[bits(4..=7, rw)]
step: u4,
#[bits(8..=12, rw)]
rx_fifo: u5,
#[bits(14..=18, rw)]
tx_fifo: u5,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(0, rw)]
completed: bool,
#[bit(1, rw)]
idle: bool,
#[bit(2, rw)]
waiting: bool,
#[bit(3, rw)]
tx_stalled: bool,
#[bit(4, rw)]
rx_stalled: bool,
#[bit(5, rw)]
address_match: bool,
#[bit(6, rw)]
nack_data: bool,
#[bit(7, rw)]
rx_data_first: bool,
#[bit(8, rw)]
i2c_start: bool,
#[bit(9, rw)]
i2c_stop: bool,
#[bit(10, rw)]
tx_underflow: bool,
#[bit(11, rw)]
rx_underflow: bool,
#[bit(12, rw)]
tx_ready: bool,
#[bit(13, rw)]
rx_ready: bool,
#[bit(14, rw)]
tx_empty: bool,
#[bit(15, rw)]
rx_full: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(0, r)]
completed: bool,
#[bit(1, r)]
idle: bool,
#[bit(2, r)]
waiting: bool,
#[bit(3, r)]
tx_stalled: bool,
#[bit(4, r)]
rx_stalled: bool,
#[bit(5, r)]
address_match: bool,
#[bit(6, r)]
nack_data: bool,
#[bit(7, r)]
rx_data_first: bool,
#[bit(8, r)]
i2c_start: bool,
#[bit(9, r)]
i2c_stop: bool,
#[bit(10, r)]
tx_underflow: bool,
#[bit(11, r)]
rx_underflow: bool,
#[bit(12, r)]
tx_ready: bool,
#[bit(13, r)]
rx_ready: bool,
#[bit(14, r)]
tx_empty: bool,
#[bit(15, r)]
rx_full: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptClear {
#[bit(0, w)]
completed: bool,
#[bit(1, w)]
idle: bool,
#[bit(2, w)]
waiting: bool,
#[bit(3, w)]
tx_stalled: bool,
#[bit(4, w)]
rx_stalled: bool,
#[bit(5, w)]
address_match: bool,
#[bit(6, w)]
nack_data: bool,
#[bit(7, w)]
rx_data_first: bool,
#[bit(8, w)]
i2c_start: bool,
#[bit(9, w)]
i2c_stop: bool,
#[bit(10, w)]
tx_underflow: bool,
#[bit(11, w)]
rx_underflow: bool,
#[bit(12, w)]
tx_ready: bool,
#[bit(13, w)]
rx_ready: bool,
#[bit(14, w)]
tx_empty: bool,
#[bit(15, w)]
rx_full: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct I2cSlave {
s0_ctrl: Control,
s0_maxwords: Maxwords,
s0_address: Address,
s0_addressmask: AddressMask,
s0_data: Data,
s0_lastaddress: LastAddress,
#[mmio(PureRead)]
s0_status: Status,
#[mmio(PureRead)]
s0_state: State,
#[mmio(PureRead)]
s0_tx_count: DataCount,
#[mmio(PureRead)]
s0_rx_count: DataCount,
s0_irq_enb: InterruptControl,
#[mmio(PureRead)]
s0_irq_raw: InterruptStatus,
#[mmio(PureRead)]
s0_irq_status: InterruptStatus,
#[mmio(Write)]
s0_irq_clear: InterruptClear,
s0_rx_fifo_trigger: TriggerLevel,
s0_tx_fifo_trigger: TriggerLevel,
#[mmio(Write)]
s0_fifo_clear: FifoClear,
s0_address_b: Address,
s0_addressmask_b: AddressMask,
}
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct I2c {
control: Control,
clkscale: ClkScale,
words: Words,
address: Address,
data: Data,
#[mmio(Write)]
cmd: Command,
#[mmio(PureRead)]
status: Status,
#[mmio(PureRead)]
state: State,
#[mmio(PureRead)]
tx_count: DataCount,
#[mmio(PureRead)]
rx_count: DataCount,
irq_enb: InterruptControl,
#[mmio(PureRead)]
irq_raw: InterruptStatus,
#[mmio(PureRead)]
irq_status: InterruptStatus,
#[mmio(Write)]
irq_clear: InterruptClear,
rx_fifo_trigger: TriggerLevel,
tx_fifo_trigger: TriggerLevel,
#[mmio(Write)]
fifo_clear: FifoClear,
timing_config: TimingConfig,
clk_timeout_limit: ClkTimeoutLimit,
_reserved_0: [u32; 0x2D],
#[mmio(inner)]
slave: slave::I2cSlave,
#[cfg(feature = "vor1x")]
_reserved_1: [u32; 0x3AC],
#[cfg(feature = "vor4x")]
_reserved_1: [u32; 0xAC],
/// Vorago 4x: 0x0214_07E9. Vorago 1x: 0x0014_07E1.
#[mmio(PureRead)]
perid: u32,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x1000);
} else if #[cfg(feature = "vor4x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x400);
}
}
impl I2c {
fn new_mmio_at(base: usize) -> MmioI2c<'static> {
MmioI2c {
ptr: base as *mut _,
phantom: PhantomData,
}
}
pub fn new_mmio(bank: Bank) -> MmioI2c<'static> {
match bank {
Bank::I2c0 => Self::new_mmio_at(BASE_ADDR_0),
Bank::I2c1 => Self::new_mmio_at(BASE_ADDR_1),
#[cfg(feature = "vor4x")]
Bank::I2c2 => Self::new_mmio_at(BASE_ADDR_2),
}
}
}

3
src/ioconfig/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub use regs::{FilterClkSel, FilterType};
pub mod regs;

177
src/ioconfig/regs.rs Normal file
View File

@ -0,0 +1,177 @@
use core::marker::PhantomData;
use crate::{NUM_PORT_A, NUM_PORT_B, gpio::PinId};
#[cfg(feature = "vor4x")]
use crate::{NUM_PORT_DEFAULT, NUM_PORT_G};
#[cfg(feature = "vor1x")]
pub const BASE_ADDR: usize = 0x4000_2000;
#[cfg(feature = "vor4x")]
pub const BASE_ADDR: usize = 0x4001_1000;
#[bitbybit::bitenum(u3)]
pub enum FilterType {
SysClk = 0,
DirectInput = 1,
FilterOneCycle = 2,
FilterTwoCycles = 3,
FilterThreeCycles = 4,
FilterFourCycles = 5,
}
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u3, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FilterClkSel {
SysClk = 0,
Clk1 = 1,
Clk2 = 2,
Clk3 = 3,
Clk4 = 4,
Clk5 = 5,
Clk6 = 6,
Clk7 = 7,
}
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
Up = 0,
Down = 1,
}
#[derive(Debug, Eq, PartialEq)]
#[bitbybit::bitenum(u2, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FunSel {
Sel0 = 0b00,
Sel1 = 0b01,
Sel2 = 0b10,
Sel3 = 0b11,
}
#[bitbybit::bitfield(u32)]
pub struct Config {
#[bit(16, rw)]
io_disable: bool,
#[bits(13..=14, rw)]
funsel: FunSel,
#[bit(12, rw)]
pull_when_output_active: bool,
#[bit(11, rw)]
pull_enable: bool,
#[bit(10, rw)]
pull_dir: Pull,
#[bit(9, rw)]
invert_output: bool,
#[bit(8, rw)]
open_drain: bool,
/// IEWO bit. Allows monitoring of output values.
#[bit(7, rw)]
input_enable_when_output: bool,
#[bit(6, rw)]
invert_input: bool,
#[bits(3..=5, rw)]
filter_clk_sel: FilterClkSel,
#[bits(0..=2, rw)]
filter_type: Option<FilterType>,
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct IoConfig {
port_a: [Config; NUM_PORT_A],
port_b: [Config; NUM_PORT_B],
#[cfg(feature = "vor4x")]
port_c: [Config; NUM_PORT_DEFAULT],
#[cfg(feature = "vor4x")]
port_d: [Config; NUM_PORT_DEFAULT],
#[cfg(feature = "vor4x")]
port_e: [Config; NUM_PORT_DEFAULT],
#[cfg(feature = "vor4x")]
port_f: [Config; NUM_PORT_DEFAULT],
#[cfg(feature = "vor4x")]
port_g: [Config; NUM_PORT_G],
#[cfg(feature = "vor4x")]
_reserved0: [u32; 0x8],
#[cfg(feature = "vor4x")]
#[mmio(PureRead)]
clk_div_0: u32,
#[cfg(feature = "vor4x")]
clk_div_1: u32,
#[cfg(feature = "vor4x")]
clk_div_2: u32,
#[cfg(feature = "vor4x")]
clk_div_3: u32,
#[cfg(feature = "vor4x")]
clk_div_4: u32,
#[cfg(feature = "vor4x")]
clk_div_5: u32,
#[cfg(feature = "vor4x")]
clk_div_6: u32,
#[cfg(feature = "vor4x")]
clk_div_7: u32,
#[cfg(feature = "vor4x")]
_reserved1: [u32; 0x387],
#[cfg(feature = "vor1x")]
_reserved1: [u32; 0x3c7],
#[mmio(PureRead)]
/// Reset value: 0x0282_07E9 for Vorago 4x, and 0x0182_07E1 for Vorago 1x
perid: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<IoConfig>(), 0x1000);
impl IoConfig {
pub const fn new_mmio() -> MmioIoConfig<'static> {
MmioIoConfig {
ptr: BASE_ADDR as *mut _,
phantom: PhantomData,
}
}
}
impl MmioIoConfig<'_> {
pub fn read_pin_config(&self, id: PinId) -> Config {
let offset = id.offset();
match id.port() {
crate::Port::A => unsafe { self.read_port_a_unchecked(offset) },
crate::Port::B => unsafe { self.read_port_b_unchecked(offset) },
#[cfg(feature = "vor4x")]
crate::Port::C => unsafe { self.read_port_c_unchecked(offset) },
#[cfg(feature = "vor4x")]
crate::Port::D => unsafe { self.read_port_d_unchecked(offset) },
#[cfg(feature = "vor4x")]
crate::Port::E => unsafe { self.read_port_e_unchecked(offset) },
#[cfg(feature = "vor4x")]
crate::Port::F => unsafe { self.read_port_f_unchecked(offset) },
#[cfg(feature = "vor4x")]
crate::Port::G => unsafe { self.read_port_g_unchecked(offset) },
}
}
pub fn modify_pin_config<F: FnOnce(Config) -> Config>(&mut self, id: PinId, f: F) {
let config = self.read_pin_config(id);
self.write_pin_config(id, f(config))
}
pub fn write_pin_config(&mut self, id: PinId, config: Config) {
let offset = id.offset();
match id.port() {
crate::Port::A => unsafe { self.write_port_a_unchecked(offset, config) },
crate::Port::B => unsafe { self.write_port_b_unchecked(offset, config) },
#[cfg(feature = "vor4x")]
crate::Port::C => unsafe { self.write_port_c_unchecked(offset, config) },
#[cfg(feature = "vor4x")]
crate::Port::D => unsafe { self.write_port_d_unchecked(offset, config) },
#[cfg(feature = "vor4x")]
crate::Port::E => unsafe { self.write_port_e_unchecked(offset, config) },
#[cfg(feature = "vor4x")]
crate::Port::F => unsafe { self.write_port_f_unchecked(offset, config) },
#[cfg(feature = "vor4x")]
crate::Port::G => unsafe { self.write_port_g_unchecked(offset, config) },
}
}
}

227
src/lib.rs Normal file
View File

@ -0,0 +1,227 @@
#![no_std]
#[cfg(feature = "vor4x")]
pub mod clock;
pub mod embassy;
pub mod gpio;
pub mod i2c;
pub mod ioconfig;
pub mod pins;
pub mod pwm;
pub mod spi;
pub mod sysconfig;
pub mod time;
pub mod timer;
pub mod uart;
pub use sysconfig::{
assert_peripheral_reset, deassert_peripheral_reset, disable_peripheral_clock,
enable_peripheral_clock, reset_peripheral_for_cycles,
};
#[cfg(not(feature = "_family-selected"))]
compile_error!("no Vorago CPU family was select. Choices: vor1x or vor4x");
pub use ioconfig::regs::FunSel;
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
#[cfg(feature = "vor1x")]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
PortB = 1,
Spi0 = 4,
Spi1 = 5,
Spi2 = 6,
Uart0 = 8,
Uart1 = 9,
I2c0 = 16,
I2c1 = 17,
Irqsel = 21,
IoConfig = 22,
Utility = 23,
Gpio = 24,
}
#[cfg(feature = "vor4x")]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// Number of GPIO ports and IOCONFIG registers for PORT A
pub const NUM_PORT_A: usize = 32;
/// Number of GPIO ports and IOCONFIG registers for PORT B
pub const NUM_PORT_B: usize = 24;
} else if #[cfg(feature = "vor4x")] {
/// Number of GPIO ports and IOCONFIG registers for PORT C to Port F
pub const NUM_PORT_DEFAULT: usize = 16;
/// Number of GPIO ports and IOCONFIG registers for PORT A
pub const NUM_PORT_A: usize = NUM_PORT_DEFAULT;
/// Number of GPIO ports and IOCONFIG registers for PORT B
pub const NUM_PORT_B: usize = NUM_PORT_DEFAULT;
/// Number of GPIO ports and IOCONFIG registers for PORT G
pub const NUM_PORT_G: usize = 8;
}
}
/// GPIO port enumeration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Port {
A = 0,
B = 1,
#[cfg(feature = "vor4x")]
C = 2,
#[cfg(feature = "vor4x")]
D = 3,
#[cfg(feature = "vor4x")]
E = 4,
#[cfg(feature = "vor4x")]
F = 5,
#[cfg(feature = "vor4x")]
G = 6,
}
impl Port {
pub const fn max_offset(&self) -> usize {
match self {
Port::A => NUM_PORT_A,
Port::B => NUM_PORT_B,
#[cfg(feature = "vor4x")]
Port::C | Port::D | Port::E | Port::F => NUM_PORT_DEFAULT,
#[cfg(feature = "vor4x")]
Port::G => NUM_PORT_G,
}
}
/// Unsafely steal the GPIO peripheral block for the given port.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
gpio::regs::Gpio::new_mmio(*self)
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("invalid GPIO offset {offset} for port {port:?}")]
pub struct InvalidOffsetError {
offset: usize,
port: Port,
}
/// Generic interrupt config which can be used to specify whether the HAL driver will
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves.
#[cfg(feature = "vor1x")]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub id: va108xx::Interrupt,
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
pub route: bool,
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
/// multiple purposes, the user can enable the interrupts themselves.
pub enable_in_nvic: bool,
}
#[cfg(feature = "vor1x")]
impl InterruptConfig {
pub fn new(id: va108xx::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
InterruptConfig {
id,
route,
enable_in_nvic,
}
}
}
/// 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_nvic_interrupt(irq: pac::Interrupt) {
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
}
}
/// Disable a specific interrupt using the NVIC peripheral.
#[inline]
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq);
}
#[allow(dead_code)]
pub(crate) mod sealed {
pub trait Sealed {}
}
pub(crate) mod shared {
use arbitrary_int::u5;
#[derive(Debug)]
pub struct TriggerLevel(arbitrary_int::UInt<u32, 5>);
impl TriggerLevel {
pub const fn new(value: u5) -> Self {
TriggerLevel(arbitrary_int::UInt::<u32, 5>::new(value.value() as u32))
}
pub const fn value(&self) -> u5 {
u5::new(self.0.value() as u8)
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct FifoClear {
#[bit(1, w)]
tx_fifo: bool,
#[bit(0, w)]
rx_fifo: bool,
}
}

718
src/pins.rs Normal file
View File

@ -0,0 +1,718 @@
use crate::sysconfig::reset_peripheral_for_cycles;
pub use crate::gpio::{Pin, PinId, PinIdProvider, Port};
use crate::PeripheralSelect;
use crate::sealed::Sealed;
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
pub trait PinMarker: Sealed {
const ID: PinId;
}
macro_rules! pin_id {
($Id:ident, $Port:path, $num:literal) => {
// Need paste macro to use ident in doc attribute
paste::paste! {
#[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {}
impl $crate::sealed::Sealed for $Id {}
impl PinIdProvider for $Id {
const ID: PinId = PinId::new_unchecked($Port, $num);
}
impl PinMarker for Pin<$Id> {
const ID: PinId = $Id::ID;
}
}
};
}
impl<I: PinIdProvider + Sealed> Sealed for Pin<I> {}
pin_id!(Pa0, Port::A, 0);
pin_id!(Pa1, Port::A, 1);
pin_id!(Pa2, Port::A, 2);
pin_id!(Pa3, Port::A, 3);
pin_id!(Pa4, Port::A, 4);
pin_id!(Pa5, Port::A, 5);
pin_id!(Pa6, Port::A, 6);
pin_id!(Pa7, Port::A, 7);
pin_id!(Pa8, Port::A, 8);
pin_id!(Pa9, Port::A, 9);
pin_id!(Pa10, Port::A, 10);
pin_id!(Pa11, Port::A, 11);
pin_id!(Pa12, Port::A, 12);
pin_id!(Pa13, Port::A, 13);
pin_id!(Pa14, Port::A, 14);
pin_id!(Pa15, Port::A, 15);
#[cfg(feature = "vor1x")]
pin_id!(Pa16, Port::A, 16);
#[cfg(feature = "vor1x")]
pin_id!(Pa17, Port::A, 17);
#[cfg(feature = "vor1x")]
pin_id!(Pa18, Port::A, 18);
#[cfg(feature = "vor1x")]
pin_id!(Pa19, Port::A, 19);
#[cfg(feature = "vor1x")]
pin_id!(Pa20, Port::A, 20);
#[cfg(feature = "vor1x")]
pin_id!(Pa21, Port::A, 21);
#[cfg(feature = "vor1x")]
pin_id!(Pa22, Port::A, 22);
#[cfg(feature = "vor1x")]
pin_id!(Pa23, Port::A, 23);
#[cfg(feature = "vor1x")]
pin_id!(Pa24, Port::A, 24);
#[cfg(feature = "vor1x")]
pin_id!(Pa25, Port::A, 25);
#[cfg(feature = "vor1x")]
pin_id!(Pa26, Port::A, 26);
#[cfg(feature = "vor1x")]
#[cfg(feature = "vor1x")]
pin_id!(Pa27, Port::A, 27);
#[cfg(feature = "vor1x")]
pin_id!(Pa28, Port::A, 28);
#[cfg(feature = "vor1x")]
pin_id!(Pa29, Port::A, 29);
#[cfg(feature = "vor1x")]
pin_id!(Pa30, Port::A, 30);
#[cfg(feature = "vor1x")]
pin_id!(Pa31, Port::A, 31);
pin_id!(Pb0, Port::B, 0);
pin_id!(Pb1, Port::B, 1);
pin_id!(Pb2, Port::B, 2);
pin_id!(Pb3, Port::B, 3);
pin_id!(Pb4, Port::B, 4);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb5, Port::B, 5);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb6, Port::B, 6);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb7, Port::B, 7);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb8, Port::B, 8);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb9, Port::B, 9);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb10, Port::B, 10);
#[cfg(not(feature = "va41628"))]
pin_id!(Pb11, Port::B, 11);
pin_id!(Pb12, Port::B, 12);
pin_id!(Pb13, Port::B, 13);
pin_id!(Pb14, Port::B, 14);
pin_id!(Pb15, Port::B, 15);
#[cfg(feature = "vor1x")]
pin_id!(Pb16, Port::B, 16);
#[cfg(feature = "vor1x")]
pin_id!(Pb17, Port::B, 17);
#[cfg(feature = "vor1x")]
pin_id!(Pb18, Port::B, 18);
#[cfg(feature = "vor1x")]
pin_id!(Pb19, Port::B, 19);
#[cfg(feature = "vor1x")]
pin_id!(Pb20, Port::B, 20);
#[cfg(feature = "vor1x")]
pin_id!(Pb21, Port::B, 21);
#[cfg(feature = "vor1x")]
pin_id!(Pb22, Port::B, 22);
#[cfg(feature = "vor1x")]
pin_id!(Pb23, Port::B, 23);
cfg_if::cfg_if! {
if #[cfg(feature = "vor4x")] {
pin_id!(Pc0, Port::C, 0);
pin_id!(Pc1, Port::C, 1);
pin_id!(Pc2, Port::C, 2);
pin_id!(Pc3, Port::C, 3);
pin_id!(Pc4, Port::C, 4);
pin_id!(Pc5, Port::C, 5);
pin_id!(Pc6, Port::C, 6);
pin_id!(Pc7, Port::C, 7);
pin_id!(Pc8, Port::C, 8);
pin_id!(Pc9, Port::C, 9);
pin_id!(Pc10, Port::C, 10);
pin_id!(Pc11, Port::C, 11);
pin_id!(Pc12, Port::C, 12);
#[cfg(not(feature = "va41628"))]
pin_id!(Pc13, Port::C, 13);
pin_id!(Pc14, Port::C, 14);
#[cfg(not(feature = "va41628"))]
pin_id!(Pc15, Port::C, 15);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd0, Port::D, 0);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd1, Port::D, 1);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd2, Port::D, 2);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd3, Port::D, 3);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd4, Port::D, 4);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd5, Port::D, 5);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd6, Port::D, 6);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd7, Port::D, 7);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd8, Port::D, 8);
#[cfg(not(feature = "va41628"))]
pin_id!(Pd9, Port::D, 9);
pin_id!(Pd10, Port::D, 10);
pin_id!(Pd11, Port::D, 11);
pin_id!(Pd12, Port::D, 12);
pin_id!(Pd13, Port::D, 13);
pin_id!(Pd14, Port::D, 14);
pin_id!(Pd15, Port::D, 15);
pin_id!(Pe0, Port::E, 0);
pin_id!(Pe1, Port::E, 1);
pin_id!(Pe2, Port::E, 2);
pin_id!(Pe3, Port::E, 3);
pin_id!(Pe4, Port::E, 4);
pin_id!(Pe5, Port::E, 5);
pin_id!(Pe6, Port::E, 6);
pin_id!(Pe7, Port::E, 7);
pin_id!(Pe8, Port::E, 8);
pin_id!(Pe9, Port::E, 9);
#[cfg(not(feature = "va41628"))]
pin_id!(Pe10, Port::E, 10);
#[cfg(not(feature = "va41628"))]
pin_id!(Pe11, Port::E, 11);
pin_id!(Pe12, Port::E, 12);
pin_id!(Pe13, Port::E, 13);
pin_id!(Pe14, Port::E, 14);
pin_id!(Pe15, Port::E, 15);
pin_id!(Pf0, Port::F, 0);
pin_id!(Pf1, Port::F, 1);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf2, Port::F, 2);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf3, Port::F, 3);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf4, Port::F, 4);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf5, Port::F, 5);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf6, Port::F, 6);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf7, Port::F, 7);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf8, Port::F, 8);
pin_id!(Pf9, Port::F, 9);
#[cfg(not(feature = "va41628"))]
pin_id!(Pf10, Port::F, 10);
pin_id!(Pf11, Port::F, 11);
pin_id!(Pf12, Port::F, 12);
pin_id!(Pf13, Port::F, 13);
pin_id!(Pf14, Port::F, 14);
pin_id!(Pf15, Port::F, 15);
pin_id!(Pg0, Port::G, 0);
pin_id!(Pg1, Port::G, 1);
pin_id!(Pg2, Port::G, 2);
pin_id!(Pg3, Port::G, 3);
pin_id!(Pg4, Port::G, 4);
pin_id!(Pg5, Port::G, 5);
pin_id!(Pg6, Port::G, 6);
pin_id!(Pg7, Port::G, 7);
}
}
/// Resource management singleton for GPIO PORT A.
pub struct PinsA {
pub pa0: Pin<Pa0>,
pub pa1: Pin<Pa1>,
pub pa2: Pin<Pa2>,
pub pa3: Pin<Pa3>,
pub pa4: Pin<Pa4>,
pub pa5: Pin<Pa5>,
pub pa6: Pin<Pa6>,
pub pa7: Pin<Pa7>,
pub pa8: Pin<Pa8>,
pub pa9: Pin<Pa9>,
pub pa10: Pin<Pa10>,
pub pa11: Pin<Pa11>,
pub pa12: Pin<Pa12>,
pub pa13: Pin<Pa13>,
pub pa14: Pin<Pa14>,
pub pa15: Pin<Pa15>,
#[cfg(feature = "vor1x")]
pub pa16: Pin<Pa16>,
#[cfg(feature = "vor1x")]
pub pa17: Pin<Pa17>,
#[cfg(feature = "vor1x")]
pub pa18: Pin<Pa18>,
#[cfg(feature = "vor1x")]
pub pa19: Pin<Pa19>,
#[cfg(feature = "vor1x")]
pub pa20: Pin<Pa20>,
#[cfg(feature = "vor1x")]
pub pa21: Pin<Pa21>,
#[cfg(feature = "vor1x")]
pub pa22: Pin<Pa22>,
#[cfg(feature = "vor1x")]
pub pa23: Pin<Pa23>,
#[cfg(feature = "vor1x")]
pub pa24: Pin<Pa24>,
#[cfg(feature = "vor1x")]
pub pa25: Pin<Pa25>,
#[cfg(feature = "vor1x")]
pub pa26: Pin<Pa26>,
#[cfg(feature = "vor1x")]
pub pa27: Pin<Pa27>,
#[cfg(feature = "vor1x")]
pub pa28: Pin<Pa28>,
#[cfg(feature = "vor1x")]
pub pa29: Pin<Pa29>,
#[cfg(feature = "vor1x")]
pub pa30: Pin<Pa30>,
#[cfg(feature = "vor1x")]
pub pa31: Pin<Pa31>,
}
impl PinsA {
pub fn new(_port_a: pac::Porta) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortA, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.porta().set_bit();
#[cfg(feature = "vor1x")]
w.gpio().set_bit();
w.ioconfig().set_bit()
});
Self {
pa0: Pin::__new(),
pa1: Pin::__new(),
pa2: Pin::__new(),
pa3: Pin::__new(),
pa4: Pin::__new(),
pa5: Pin::__new(),
pa6: Pin::__new(),
pa7: Pin::__new(),
pa8: Pin::__new(),
pa9: Pin::__new(),
pa10: Pin::__new(),
pa11: Pin::__new(),
pa12: Pin::__new(),
pa13: Pin::__new(),
pa14: Pin::__new(),
pa15: Pin::__new(),
#[cfg(feature = "vor1x")]
pa16: Pin::__new(),
#[cfg(feature = "vor1x")]
pa17: Pin::__new(),
#[cfg(feature = "vor1x")]
pa18: Pin::__new(),
#[cfg(feature = "vor1x")]
pa19: Pin::__new(),
#[cfg(feature = "vor1x")]
pa20: Pin::__new(),
#[cfg(feature = "vor1x")]
pa21: Pin::__new(),
#[cfg(feature = "vor1x")]
pa22: Pin::__new(),
#[cfg(feature = "vor1x")]
pa23: Pin::__new(),
#[cfg(feature = "vor1x")]
pa24: Pin::__new(),
#[cfg(feature = "vor1x")]
pa25: Pin::__new(),
#[cfg(feature = "vor1x")]
pa26: Pin::__new(),
#[cfg(feature = "vor1x")]
pa27: Pin::__new(),
#[cfg(feature = "vor1x")]
pa28: Pin::__new(),
#[cfg(feature = "vor1x")]
pa29: Pin::__new(),
#[cfg(feature = "vor1x")]
pa30: Pin::__new(),
#[cfg(feature = "vor1x")]
pa31: Pin::__new(),
}
}
}
/// Resource management singleton for GPIO PORT B.
pub struct PinsB {
pub pb0: Pin<Pb0>,
pub pb1: Pin<Pb1>,
pub pb2: Pin<Pb2>,
pub pb3: Pin<Pb3>,
pub pb4: Pin<Pb4>,
#[cfg(not(feature = "va41628"))]
pub pb5: Pin<Pb5>,
#[cfg(not(feature = "va41628"))]
pub pb6: Pin<Pb6>,
#[cfg(not(feature = "va41628"))]
pub pb7: Pin<Pb7>,
#[cfg(not(feature = "va41628"))]
pub pb8: Pin<Pb8>,
#[cfg(not(feature = "va41628"))]
pub pb9: Pin<Pb9>,
#[cfg(not(feature = "va41628"))]
pub pb10: Pin<Pb10>,
#[cfg(not(feature = "va41628"))]
pub pb11: Pin<Pb11>,
pub pb12: Pin<Pb12>,
pub pb13: Pin<Pb13>,
pub pb14: Pin<Pb14>,
pub pb15: Pin<Pb15>,
#[cfg(feature = "vor1x")]
pub pb16: Pin<Pb16>,
#[cfg(feature = "vor1x")]
pub pb17: Pin<Pb17>,
#[cfg(feature = "vor1x")]
pub pb18: Pin<Pb18>,
#[cfg(feature = "vor1x")]
pub pb19: Pin<Pb19>,
#[cfg(feature = "vor1x")]
pub pb20: Pin<Pb20>,
#[cfg(feature = "vor1x")]
pub pb21: Pin<Pb21>,
#[cfg(feature = "vor1x")]
pub pb22: Pin<Pb22>,
#[cfg(feature = "vor1x")]
pub pb23: Pin<Pb23>,
}
impl PinsB {
pub fn new(_port_b: pac::Portb) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortB, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portb().set_bit();
#[cfg(feature = "vor1x")]
w.gpio().set_bit();
w.ioconfig().set_bit()
});
Self {
pb0: Pin::__new(),
pb1: Pin::__new(),
pb2: Pin::__new(),
pb3: Pin::__new(),
pb4: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb5: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb6: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb7: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb8: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb9: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb10: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pb11: Pin::__new(),
pb12: Pin::__new(),
pb13: Pin::__new(),
pb14: Pin::__new(),
pb15: Pin::__new(),
#[cfg(feature = "vor1x")]
pb16: Pin::__new(),
#[cfg(feature = "vor1x")]
pb17: Pin::__new(),
#[cfg(feature = "vor1x")]
pb18: Pin::__new(),
#[cfg(feature = "vor1x")]
pb19: Pin::__new(),
#[cfg(feature = "vor1x")]
pb20: Pin::__new(),
#[cfg(feature = "vor1x")]
pb21: Pin::__new(),
#[cfg(feature = "vor1x")]
pb22: Pin::__new(),
#[cfg(feature = "vor1x")]
pb23: Pin::__new(),
}
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor4x")] {
/// Resource management singleton for GPIO PORT C.
pub struct PinsC {
pub pc0: Pin<Pc0>,
pub pc1: Pin<Pc1>,
pub pc2: Pin<Pc2>,
pub pc3: Pin<Pc3>,
pub pc4: Pin<Pc4>,
pub pc5: Pin<Pc5>,
pub pc6: Pin<Pc6>,
pub pc7: Pin<Pc7>,
pub pc8: Pin<Pc8>,
pub pc9: Pin<Pc9>,
pub pc10: Pin<Pc10>,
pub pc11: Pin<Pc11>,
pub pc12: Pin<Pc12>,
#[cfg(not(feature = "va41628"))]
pub pc13: Pin<Pc13>,
pub pc14: Pin<Pc14>,
#[cfg(not(feature = "va41628"))]
pub pc15: Pin<Pc15>,
}
impl PinsC {
pub fn new(_port_c: pac::Portc) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortC, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portc().set_bit();
w.ioconfig().set_bit()
});
Self {
pc0: Pin::__new(),
pc1: Pin::__new(),
pc2: Pin::__new(),
pc3: Pin::__new(),
pc4: Pin::__new(),
pc5: Pin::__new(),
pc6: Pin::__new(),
pc7: Pin::__new(),
pc8: Pin::__new(),
pc9: Pin::__new(),
pc10: Pin::__new(),
pc11: Pin::__new(),
pc12: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pc13: Pin::__new(),
pc14: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pc15: Pin::__new(),
}
}
}
/// Resource management singleton for GPIO PORT D.
pub struct PinsD {
#[cfg(not(feature = "va41628"))]
pub pd0: Pin<Pd0>,
#[cfg(not(feature = "va41628"))]
pub pd1: Pin<Pd1>,
#[cfg(not(feature = "va41628"))]
pub pd2: Pin<Pd2>,
#[cfg(not(feature = "va41628"))]
pub pd3: Pin<Pd3>,
#[cfg(not(feature = "va41628"))]
pub pd4: Pin<Pd4>,
#[cfg(not(feature = "va41628"))]
pub pd5: Pin<Pd5>,
#[cfg(not(feature = "va41628"))]
pub pd6: Pin<Pd6>,
#[cfg(not(feature = "va41628"))]
pub pd7: Pin<Pd7>,
#[cfg(not(feature = "va41628"))]
pub pd8: Pin<Pd8>,
#[cfg(not(feature = "va41628"))]
pub pd9: Pin<Pd9>,
pub pd10: Pin<Pd10>,
pub pd11: Pin<Pd11>,
pub pd12: Pin<Pd12>,
pub pd13: Pin<Pd13>,
pub pd14: Pin<Pd14>,
pub pd15: Pin<Pd15>,
}
impl PinsD {
pub fn new(_port_d: pac::Portd) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortD, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portd().set_bit();
w.ioconfig().set_bit()
});
Self {
#[cfg(not(feature = "va41628"))]
pd0: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd1: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd2: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd3: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd4: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd5: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd6: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd7: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd8: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pd9: Pin::__new(),
pd10: Pin::__new(),
pd11: Pin::__new(),
pd12: Pin::__new(),
pd13: Pin::__new(),
pd14: Pin::__new(),
pd15: Pin::__new(),
}
}
}
/// Resource management singleton for GPIO PORT E.
pub struct PinsE {
pub pe0: Pin<Pe0>,
pub pe1: Pin<Pe1>,
pub pe2: Pin<Pe2>,
pub pe3: Pin<Pe3>,
pub pe4: Pin<Pe4>,
pub pe5: Pin<Pe5>,
pub pe6: Pin<Pe6>,
pub pe7: Pin<Pe7>,
pub pe8: Pin<Pe8>,
pub pe9: Pin<Pe9>,
#[cfg(not(feature = "va41628"))]
pub pe10: Pin<Pe10>,
#[cfg(not(feature = "va41628"))]
pub pe11: Pin<Pe11>,
pub pe12: Pin<Pe12>,
pub pe13: Pin<Pe13>,
pub pe14: Pin<Pe14>,
pub pe15: Pin<Pe15>,
}
impl PinsE {
pub fn new(_port_e: pac::Porte) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortE, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.porte().set_bit();
w.ioconfig().set_bit()
});
Self {
pe0: Pin::__new(),
pe1: Pin::__new(),
pe2: Pin::__new(),
pe3: Pin::__new(),
pe4: Pin::__new(),
pe5: Pin::__new(),
pe6: Pin::__new(),
pe7: Pin::__new(),
pe8: Pin::__new(),
pe9: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pe10: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pe11: Pin::__new(),
pe12: Pin::__new(),
pe13: Pin::__new(),
pe14: Pin::__new(),
pe15: Pin::__new(),
}
}
}
/// Resource management singleton for GPIO PORT F.
pub struct PinsF {
pub pf0: Pin<Pf0>,
pub pf1: Pin<Pf1>,
#[cfg(not(feature = "va41628"))]
pub pf2: Pin<Pf2>,
#[cfg(not(feature = "va41628"))]
pub pf3: Pin<Pf3>,
#[cfg(not(feature = "va41628"))]
pub pf4: Pin<Pf4>,
#[cfg(not(feature = "va41628"))]
pub pf5: Pin<Pf5>,
#[cfg(not(feature = "va41628"))]
pub pf6: Pin<Pf6>,
#[cfg(not(feature = "va41628"))]
pub pf7: Pin<Pf7>,
#[cfg(not(feature = "va41628"))]
pub pf8: Pin<Pf8>,
pub pf9: Pin<Pf9>,
#[cfg(not(feature = "va41628"))]
pub pf10: Pin<Pf10>,
pub pf11: Pin<Pf11>,
pub pf12: Pin<Pf12>,
pub pf13: Pin<Pf13>,
pub pf14: Pin<Pf14>,
pub pf15: Pin<Pf15>,
}
impl PinsF {
pub fn new(_port_f: pac::Portf) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortF, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portf().set_bit();
w.ioconfig().set_bit()
});
Self {
pf0: Pin::__new(),
pf1: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf2: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf3: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf4: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf5: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf6: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf7: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf8: Pin::__new(),
pf9: Pin::__new(),
#[cfg(not(feature = "va41628"))]
pf10: Pin::__new(),
pf11: Pin::__new(),
pf12: Pin::__new(),
pf13: Pin::__new(),
pf14: Pin::__new(),
pf15: Pin::__new(),
}
}
}
/// Resource management singleton for GPIO PORT G.
pub struct PinsG {
pub pg0: Pin<Pg0>,
pub pg1: Pin<Pg1>,
pub pg2: Pin<Pg2>,
pub pg3: Pin<Pg3>,
pub pg4: Pin<Pg4>,
pub pg5: Pin<Pg5>,
pub pg6: Pin<Pg6>,
pub pg7: Pin<Pg7>,
}
impl PinsG {
pub fn new(_port_g: pac::Portg) -> Self {
let syscfg = unsafe { pac::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortG, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portg().set_bit();
w.ioconfig().set_bit()
});
Self {
pg0: Pin::__new(),
pg1: Pin::__new(),
pg2: Pin::__new(),
pg3: Pin::__new(),
pg4: Pin::__new(),
pg5: Pin::__new(),
pg6: Pin::__new(),
pg7: Pin::__new(),
}
}
}
}
}

254
src/pwm.rs Normal file
View File

@ -0,0 +1,254 @@
use core::convert::Infallible;
use core::marker::PhantomData;
use crate::gpio::IoPeriphPin;
use crate::timer::enable_tim_clk;
use crate::timer::regs::{EnableControl, StatusSelect};
use crate::{PeripheralSelect, enable_peripheral_clock};
use crate::time::Hertz;
use crate::timer::{self, TimId, TimMarker, TimPin};
const DUTY_MAX: u16 = u16::MAX;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PwmA {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PwmB {}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("pin tim ID {pin_tim:?} and timer tim id {tim_id:?} do not match")]
pub struct TimMissmatchError {
pin_tim: TimId,
tim_id: TimId,
}
//==================================================================================================
// PWM pin
//==================================================================================================
/// Reduced version where type information is deleted
pub struct PwmPin<Mode = PwmA> {
tim_id: TimId,
regs: timer::regs::MmioTimer<'static>,
ref_clk: Hertz,
/// For PWMB, this is the upper limit
current_duty: u16,
/// For PWMA, this value will not be used
current_lower_limit: u16,
current_period: Hertz,
current_rst_val: u32,
mode: PhantomData<Mode>,
}
impl<Mode> PwmPin<Mode> {
/// Create a new PWM pin
pub fn new<Pin: TimPin, Tim: TimMarker>(
_pin: Pin,
_tim: Tim,
#[cfg(feature = "vor1x")] sys_clk: Hertz,
#[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
initial_frequency: Hertz,
) -> Result<Self, TimMissmatchError> {
if Pin::TIM_ID != Tim::ID {
return Err(TimMissmatchError {
pin_tim: Pin::TIM_ID,
tim_id: Tim::ID,
});
}
IoPeriphPin::new(Pin::PIN_ID, Pin::FUN_SEL, None);
let mut pin = PwmPin {
tim_id: Tim::ID,
regs: timer::regs::Timer::new_mmio(Tim::ID),
current_duty: 0,
current_lower_limit: 0,
current_period: initial_frequency,
current_rst_val: 0,
#[cfg(feature = "vor1x")]
ref_clk: sys_clk,
#[cfg(feature = "vor4x")]
ref_clk: clks.apb1(),
mode: PhantomData,
};
// For Vorago 4x, the presence of the pin structure ensures that its respective peripheral
// clock was already enabled.
#[cfg(feature = "vor1x")]
enable_peripheral_clock(PeripheralSelect::Gpio);
enable_peripheral_clock(PeripheralSelect::IoConfig);
enable_tim_clk(Tim::ID);
pin.enable_pwm_a();
pin.set_period(initial_frequency);
Ok(pin)
}
#[inline]
fn enable_pwm_a(&mut self) {
self.regs.modify_control(|mut value| {
value.set_status_sel(StatusSelect::PwmaOutput);
value
});
}
#[inline]
fn enable_pwm_b(&mut self) {
self.regs.modify_control(|mut value| {
value.set_status_sel(StatusSelect::PwmbOutput);
value
});
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.current_period
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.current_period = period.into();
// Avoid division by 0
if self.current_period.raw() == 0 {
return;
}
self.current_rst_val = self.ref_clk.raw() / self.current_period.raw();
self.regs.write_reset_value(self.current_rst_val);
}
#[inline]
pub fn disable(&mut self) {
self.regs.write_enable_control(EnableControl::new_disable());
}
#[inline]
pub fn enable(&mut self) {
self.regs.write_enable_control(EnableControl::new_enable());
}
#[inline]
pub fn period(&self) -> Hertz {
self.current_period
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.current_duty
}
}
impl From<PwmPin<PwmA>> for PwmPin<PwmB> {
fn from(other: PwmPin<PwmA>) -> Self {
let mut pwmb = Self {
mode: PhantomData,
regs: other.regs,
tim_id: other.tim_id,
ref_clk: other.ref_clk,
current_duty: other.current_duty,
current_lower_limit: other.current_lower_limit,
current_period: other.current_period,
current_rst_val: other.current_rst_val,
};
pwmb.enable_pwm_b();
pwmb
}
}
impl From<PwmPin<PwmB>> for PwmPin<PwmA> {
fn from(other: PwmPin<PwmB>) -> Self {
let mut pwmb = Self {
mode: PhantomData,
tim_id: other.tim_id,
regs: other.regs,
ref_clk: other.ref_clk,
current_duty: other.current_duty,
current_lower_limit: other.current_lower_limit,
current_period: other.current_period,
current_rst_val: other.current_rst_val,
};
pwmb.enable_pwm_a();
pwmb
}
}
//==================================================================================================
// PWMB implementations
//==================================================================================================
impl PwmPin<PwmB> {
#[inline(always)]
pub fn pwmb_lower_limit(&self) -> u16 {
self.current_lower_limit
}
#[inline(always)]
pub fn pwmb_upper_limit(&self) -> u16 {
self.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
#[inline(always)]
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.current_lower_limit = duty;
let pwmb_val: u64 =
(self.current_rst_val as u64 * self.current_lower_limit as u64) / DUTY_MAX as u64;
self.regs.write_pwmb_value(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.current_duty = duty;
let pwma_val: u64 =
(self.current_rst_val as u64 * self.current_duty as u64) / DUTY_MAX as u64;
self.regs.write_pwma_value(pwma_val as u32);
}
}
//==================================================================================================
// Embedded HAL implementation: PWMA only
//==================================================================================================
impl embedded_hal::pwm::ErrorType for PwmPin {
type Error = Infallible;
}
impl embedded_hal::pwm::SetDutyCycle for PwmPin {
#[inline]
fn max_duty_cycle(&self) -> u16 {
DUTY_MAX
}
#[inline]
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.current_duty = duty;
let pwma_val: u64 = (self.current_rst_val as u64
* (DUTY_MAX as u64 - self.current_duty as u64))
/ DUTY_MAX as u64;
self.regs.write_pwma_value(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
}
}

952
src/spi/mod.rs Normal file
View File

@ -0,0 +1,952 @@
use crate::FunSel;
use crate::gpio::{IoPeriphPin, PinId};
use crate::{
PeripheralSelect, enable_peripheral_clock, pins::PinMarker, sealed::Sealed, time::Hertz,
};
use core::{convert::Infallible, fmt::Debug, marker::PhantomData};
use embedded_hal::spi::{MODE_0, Mode};
use regs::{ClkPrescaler, Data, FifoClear, WordSize};
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
pub use regs::{Bank, HwChipSelectId};
pub mod regs;
pub fn configure_pin_as_hw_cs_pin<P: PinMarker + HwCsProvider>(_pin: P) -> HwChipSelectId {
IoPeriphPin::new(P::ID, P::FUN_SEL, None);
P::CS_ID
}
//==================================================================================================
// Pins and traits.
//==================================================================================================
pub trait PinSck: PinMarker {
const SPI_ID: Bank;
const FUN_SEL: FunSel;
}
pub trait PinMosi: PinMarker {
const SPI_ID: Bank;
const FUN_SEL: FunSel;
}
pub trait PinMiso: PinMarker {
const SPI_ID: Bank;
const FUN_SEL: FunSel;
}
pub trait HwCsProvider {
const PIN_ID: PinId;
const SPI_ID: Bank;
const FUN_SEL: FunSel;
const CS_ID: HwChipSelectId;
}
#[macro_use]
mod macros {
#[cfg(not(feature = "va41628"))]
macro_rules! hw_cs_multi_pin {
(
// name of the newtype wrapper struct
$name:ident,
// Pb0
$pin_id:ident,
// SpiId::B
$spi_id:path,
// FunSel::Sel1
$fun_sel:path,
// HwChipSelectId::Id2
$cs_id:path
) => {
#[doc = concat!(
"Newtype wrapper to use [Pin] [`", stringify!($pin_id), "`] as a HW CS pin for [`", stringify!($spi_id), "`] with [`", stringify!($cs_id), "`]."
)]
pub struct $name(Pin<$pin_id>);
impl $name {
pub fn new(pin: Pin<$pin_id>) -> Self {
Self(pin)
}
}
impl crate::sealed::Sealed for $name {}
impl HwCsProvider for $name {
const PIN_ID: PinId = <$pin_id as PinIdProvider>::ID;
const SPI_ID: Bank = $spi_id;
const FUN_SEL: FunSel = $fun_sel;
const CS_ID: HwChipSelectId = $cs_id;
}
};
}
#[macro_export]
macro_rules! hw_cs_pins {
($SpiId:path, $(($Px:ident, $FunSel:path, $HwCsIdent:path)$(,)?)+) => {
$(
impl HwCsProvider for Pin<$Px> {
const PIN_ID: PinId = $Px::ID;
const SPI_ID: Bank = $SpiId;
const FUN_SEL: FunSel = $FunSel;
const CS_ID: HwChipSelectId = $HwCsIdent;
}
)+
};
}
}
#[cfg(feature = "vor1x")]
pub mod pins_vor1x;
#[cfg(feature = "vor4x")]
pub mod pins_vor4x;
//==================================================================================================
// Defintions
//==================================================================================================
// FIFO has a depth of 16.
const FILL_DEPTH: usize = 12;
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
pub const DEFAULT_CLK_DIV: u16 = 2;
/// Common trait implemented by all PAC peripheral access structures. The register block
/// format is the same for all SPI blocks.
pub trait SpiMarker: Sealed {
const ID: Bank;
const PERIPH_SEL: PeripheralSelect;
}
#[cfg(feature = "vor1x")]
pub type Spi0 = pac::Spia;
#[cfg(feature = "vor4x")]
pub type Spi0 = pac::Spi0;
impl SpiMarker for Spi0 {
const ID: Bank = Bank::Spi0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
}
impl Sealed for Spi0 {}
#[cfg(feature = "vor1x")]
pub type Spi1 = pac::Spib;
#[cfg(feature = "vor4x")]
pub type Spi1 = pac::Spi1;
impl SpiMarker for Spi1 {
const ID: Bank = Bank::Spi1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
}
impl Sealed for Spi1 {}
#[cfg(feature = "vor1x")]
pub type Spi2 = pac::Spic;
#[cfg(feature = "vor4x")]
pub type Spi2 = pac::Spi2;
impl SpiMarker for Spi2 {
const ID: Bank = Bank::Spi2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
}
impl Sealed for Spi2 {}
#[cfg(feature = "vor4x")]
impl SpiMarker for pac::Spi3 {
const ID: Bank = Bank::Spi3;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
}
#[cfg(feature = "vor4x")]
impl Sealed for pac::Spi3 {}
//==================================================================================================
// Config
//==================================================================================================
pub trait TransferConfigProvider {
fn sod(&mut self, sod: bool);
fn blockmode(&mut self, blockmode: bool);
fn mode(&mut self, mode: Mode);
fn clk_cfg(&mut self, clk_cfg: SpiClkConfig);
fn hw_cs_id(&self) -> u8;
}
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig {
pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<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,
/// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit
/// is set on a written word.
pub bmstall: bool,
pub hw_cs: Option<HwChipSelectId>,
}
impl TransferConfig {
pub fn new_with_hw_cs(
clk_cfg: Option<SpiClkConfig>,
mode: Option<Mode>,
blockmode: bool,
bmstall: bool,
sod: bool,
hw_cs_id: HwChipSelectId,
) -> Self {
TransferConfig {
clk_cfg,
mode,
sod,
blockmode,
bmstall,
hw_cs: Some(hw_cs_id),
}
}
}
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiConfig {
clk: SpiClkConfig,
// SPI mode configuration
pub init_mode: Mode,
/// 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. Defaults to true.
pub blockmode: bool,
/// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
/// Currently enabled by default.
pub bmstall: 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 Default for SpiConfig {
fn default() -> Self {
Self {
init_mode: MODE_0,
blockmode: true,
bmstall: true,
// Default value is definitely valid.
clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
slave_output_disable: Default::default(),
loopback_mode: Default::default(),
master_delayer_capture: Default::default(),
}
}
}
impl SpiConfig {
pub fn loopback(mut self, enable: bool) -> Self {
self.loopback_mode = enable;
self
}
pub fn blockmode(mut self, enable: bool) -> Self {
self.blockmode = enable;
self
}
pub fn bmstall(mut self, enable: bool) -> Self {
self.bmstall = enable;
self
}
pub fn mode(mut self, mode: Mode) -> Self {
self.init_mode = mode;
self
}
pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self {
self.clk = clk_cfg;
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;
const WORD_SIZE: regs::WordSize;
fn word_reg() -> u8;
}
impl WordProvider for u8 {
const MASK: u32 = 0xff;
const WORD_SIZE: regs::WordSize = regs::WordSize::EightBits;
fn word_reg() -> u8 {
0x07
}
}
impl WordProvider for u16 {
const MASK: u32 = 0xffff;
const WORD_SIZE: regs::WordSize = regs::WordSize::SixteenBits;
fn word_reg() -> u8 {
0x0f
}
}
//==================================================================================================
// Spi
//==================================================================================================
/// Low level access trait for the SPI peripheral.
pub trait SpiLowLevel {
/// Low level function to write a word to the SPI FIFO but also checks whether
/// there is actually data in the FIFO.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>;
/// Low level function to write a word to the SPI FIFO without checking whether
/// there FIFO is full.
///
/// This does not necesarily mean there is a space in the FIFO available.
/// Use [Self::write_fifo] function to write a word into the FIFO reliably.
fn write_fifo_unchecked(&mut self, data: u32);
/// Low level function to read a word from the SPI FIFO. Must be preceeded by a
/// [Self::write_fifo] call.
///
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
fn read_fifo(&mut self) -> nb::Result<u32, Infallible>;
/// Low level function to read a word from from the SPI FIFO.
///
/// This does not necesarily mean there is a word in the FIFO available.
/// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
/// API.
/// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
fn read_fifo_unchecked(&mut self) -> u32;
}
#[inline(always)]
pub 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),
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiClkConfig {
prescale_val: u8,
scrdv: u8,
}
impl SpiClkConfig {
pub fn prescale_val(&self) -> u8 {
self.prescale_val
}
pub fn scrdv(&self) -> u8 {
self.scrdv
}
}
impl SpiClkConfig {
pub fn new(prescale_val: u8, scrdv: u8) -> Self {
Self {
prescale_val,
scrdv,
}
}
pub fn from_div(div: u16) -> Result<Self, SpiClkConfigError> {
spi_clk_config_from_div(div)
}
#[cfg(feature = "vor1x")]
pub fn from_clk(sys_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
}
#[cfg(feature = "vor4x")]
pub fn from_clks(clks: &crate::clock::Clocks, spi_clk: Hertz) -> Option<Self> {
Self::from_apb1_clk(clks.apb1(), spi_clk)
}
#[cfg(feature = "vor4x")]
pub fn from_apb1_clk(apb1_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
clk_div_for_target_clock(apb1_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClkConfigError {
#[error("division by zero")]
DivIsZero,
#[error("divide value is not even")]
DivideValueNotEven,
#[error("scrdv value is too large")]
ScrdvValueTooLarge,
}
#[inline]
pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClkConfig, SpiClkConfigError> {
if div == 0 {
return Err(SpiClkConfigError::DivIsZero);
}
if div % 2 != 0 {
return Err(SpiClkConfigError::DivideValueNotEven);
}
let mut prescale_val = 0;
// find largest (even) prescale value that divides into div
for i in (2..=0xfe).rev().step_by(2) {
if div % i == 0 {
prescale_val = i;
break;
}
}
if prescale_val == 0 {
return Err(SpiClkConfigError::DivideValueNotEven);
}
div /= prescale_val;
if div > u8::MAX as u16 + 1 {
return Err(SpiClkConfigError::ScrdvValueTooLarge);
}
Ok(SpiClkConfig {
prescale_val: prescale_val as u8,
scrdv: (div - 1) as u8,
})
}
#[inline]
pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
if spi_clk > sys_clk {
return None;
}
// Step 1: Calculate raw divider.
let raw_div = sys_clk.raw() / spi_clk.raw();
let remainder = sys_clk.raw() % spi_clk.raw();
// Step 2: Round up if necessary.
let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
raw_div + 1
} else {
raw_div
};
if rounded_div % 2 != 0 {
// Take slower clock conservatively.
rounded_div += 1;
}
if rounded_div > u16::MAX as u32 {
return None;
}
Some(rounded_div as u16)
}
#[derive(Debug, thiserror::Error)]
#[error("peripheral or peripheral pin ID is not consistent")]
pub struct SpiIdMissmatchError;
/// SPI peripheral driver structure.
pub struct Spi<Word = u8> {
id: Bank,
regs: regs::MmioSpi<'static>,
cfg: SpiConfig,
/// Fill word for read-only SPI transactions.
fill_word: Word,
blockmode: bool,
bmstall: bool,
word: PhantomData<Word>,
}
impl<Word: WordProvider> Spi<Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
/// Create a new SPI struct for using SPI with the fixed ROM SPI pins.
///
/// ## Arguments
///
/// * `spi` - SPI bus to use
/// * `spi_cfg` - Configuration specific to the SPI bus
pub fn new_for_rom<SpiI: SpiMarker>(
spi: SpiI,
spi_cfg: SpiConfig,
) -> Result<Self, SpiIdMissmatchError> {
#[cfg(feature = "vor1x")]
if SpiI::ID != Bank::Spi2 {
return Err(SpiIdMissmatchError);
}
#[cfg(feature = "vor4x")]
if SpiI::ID != Bank::Spi3 {
return Err(SpiIdMissmatchError);
}
Ok(Self::new_generic(spi, spi_cfg))
}
/// Create a new SPI peripheral driver.
///
/// ## 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
pub fn new<SpiI: SpiMarker, Sck: PinSck, Miso: PinMiso, Mosi: PinMosi>(
spi: SpiI,
_pins: (Sck, Miso, Mosi),
spi_cfg: SpiConfig,
) -> Result<Self, SpiIdMissmatchError> {
if SpiI::ID != Sck::SPI_ID || SpiI::ID != Miso::SPI_ID || SpiI::ID != Mosi::SPI_ID {
return Err(SpiIdMissmatchError);
}
IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
Ok(Self::new_generic(spi, spi_cfg))
}
pub fn new_generic<SpiI: SpiMarker>(_spi: SpiI, spi_cfg: SpiConfig) -> Self {
enable_peripheral_clock(SpiI::PERIPH_SEL);
let mut regs = regs::Spi::new_mmio(SpiI::ID);
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(spi_cfg.init_mode);
regs.write_ctrl0(
regs::Control0::builder()
.with_scrdv(spi_cfg.clk.scrdv)
.with_sph(cph_bit)
.with_spo(cpo_bit)
.with_word_size(Word::WORD_SIZE)
.build(),
);
regs.write_ctrl1(
regs::Control1::builder()
.with_mtxpause(false)
.with_mdlycap(spi_cfg.master_delayer_capture)
.with_bm_stall(spi_cfg.bmstall)
.with_bm_start(false)
.with_blockmode(spi_cfg.blockmode)
.with_ss(HwChipSelectId::Id0)
.with_sod(spi_cfg.slave_output_disable)
.with_slave_mode(false)
.with_enable(false)
.with_lbm(spi_cfg.loopback_mode)
.build(),
);
regs.write_clkprescale(ClkPrescaler::new(spi_cfg.clk.prescale_val));
regs.write_fifo_clear(
FifoClear::builder()
.with_tx_fifo(true)
.with_rx_fifo(true)
.build(),
);
// Enable the peripheral as the last step as recommended in the
// programmers guide
regs.modify_ctrl1(|mut value| {
value.set_enable(true);
value
});
Spi {
id: SpiI::ID,
regs: regs::Spi::new_mmio(SpiI::ID),
cfg: spi_cfg,
fill_word: Default::default(),
bmstall: spi_cfg.bmstall,
blockmode: spi_cfg.blockmode,
word: PhantomData,
}
}
#[inline]
pub fn cfg_clock(&mut self, cfg: SpiClkConfig) {
self.regs.modify_ctrl0(|mut value| {
value.set_scrdv(cfg.scrdv);
value
});
self.regs
.write_clkprescale(regs::ClkPrescaler::new(cfg.prescale_val));
}
pub fn set_fill_word(&mut self, fill_word: Word) {
self.fill_word = fill_word;
}
#[inline]
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> {
let val = spi_clk_config_from_div(div)?;
self.cfg_clock(val);
Ok(())
}
#[inline]
pub fn cfg_mode(&mut self, mode: Mode) {
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
self.regs.modify_ctrl0(|mut value| {
value.set_spo(cpo_bit);
value.set_sph(cph_bit);
value
});
}
#[inline]
pub fn fill_word(&self) -> Word {
self.fill_word
}
#[inline]
pub fn clear_tx_fifo(&mut self) {
self.regs.write_fifo_clear(
regs::FifoClear::builder()
.with_tx_fifo(true)
.with_rx_fifo(false)
.build(),
);
}
#[inline]
pub fn clear_rx_fifo(&mut self) {
self.regs.write_fifo_clear(
regs::FifoClear::builder()
.with_tx_fifo(false)
.with_rx_fifo(true)
.build(),
);
}
#[inline]
pub fn perid(&self) -> u32 {
self.regs.read_perid()
}
/// Configure the hardware chip select given a hardware chip select ID.
///
/// The pin also needs to be configured to be used as a HW CS pin. This can be done
/// by using the [configure_pin_as_hw_cs_pin] function which also returns the
/// corresponding [HwChipSelectId].
#[inline]
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
self.regs.modify_ctrl1(|mut value| {
value.set_sod(false);
value.set_ss(hw_cs);
value
});
}
/// Disables the hardware chip select functionality. This can be used when performing
/// external chip select handling, for example with GPIO pins.
#[inline]
pub fn cfg_hw_cs_disable(&mut self) {
self.regs.modify_ctrl1(|mut value| {
value.set_sod(true);
value
});
}
/// Utility function to configure all relevant transfer parameters in one go.
/// This is useful if multiple devices with different clock and mode configurations
/// are connected to one bus.
pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) {
if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
self.cfg_clock(trans_clk_div);
}
if let Some(mode) = transfer_cfg.mode {
self.cfg_mode(mode);
}
self.blockmode = transfer_cfg.blockmode;
self.regs.modify_ctrl1(|mut value| {
if transfer_cfg.sod {
value.set_sod(transfer_cfg.sod);
} else {
value.set_sod(false);
if let Some(hw_cs) = transfer_cfg.hw_cs {
value.set_ss(hw_cs);
}
}
value.set_blockmode(transfer_cfg.blockmode);
value.set_bm_stall(transfer_cfg.bmstall);
value
});
}
fn flush_internal(&mut self) {
let mut status_reg = self.regs.read_status();
while !status_reg.tx_empty() || status_reg.rx_not_empty() || status_reg.busy() {
if status_reg.rx_not_empty() {
self.read_fifo_unchecked();
}
status_reg = self.regs.read_status();
}
}
fn transfer_preparation(&mut self, words: &[Word]) -> Result<(), Infallible> {
if words.is_empty() {
return Ok(());
}
self.flush_internal();
Ok(())
}
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
// initialization. Returns the amount of written bytes.
fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
//let reg_block = self.reg_block();
if self.blockmode {
self.regs.modify_ctrl1(|mut value| {
value.set_mtxpause(true);
value
});
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
let smaller_idx = core::cmp::min(FILL_DEPTH, words.len());
for _ in 0..smaller_idx {
if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
} else {
self.write_fifo_unchecked(words[current_write_idx].into());
}
current_write_idx += 1;
}
if self.blockmode {
self.regs.modify_ctrl1(|mut value| {
value.set_mtxpause(false);
value
});
}
current_write_idx
}
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
// initialization.
fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize {
if self.blockmode {
self.regs.modify_ctrl1(|mut value| {
value.set_mtxpause(true);
value
});
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
let smaller_idx = core::cmp::min(FILL_DEPTH, send_len);
for _ in 0..smaller_idx {
if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK);
} else {
self.write_fifo_unchecked(self.fill_word.into());
}
current_write_idx += 1;
}
if self.blockmode {
self.regs.modify_ctrl1(|mut value| {
value.set_mtxpause(false);
value
});
}
current_write_idx
}
}
impl<Word: WordProvider> SpiLowLevel for Spi<Word>
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
#[inline(always)]
fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> {
if !self.regs.read_status().tx_not_full() {
return Err(nb::Error::WouldBlock);
}
self.write_fifo_unchecked(data);
Ok(())
}
#[inline(always)]
fn write_fifo_unchecked(&mut self, data: u32) {
self.regs.write_data(Data::new_with_raw_value(data));
}
#[inline(always)]
fn read_fifo(&mut self) -> nb::Result<u32, Infallible> {
if !self.regs.read_status().rx_not_empty() {
return Err(nb::Error::WouldBlock);
}
Ok(self.read_fifo_unchecked())
}
#[inline(always)]
fn read_fifo_unchecked(&mut self) -> u32 {
self.regs.read_data().raw_value()
}
}
impl<Word: WordProvider> embedded_hal::spi::ErrorType for Spi<Word> {
type Error = Infallible;
}
impl<Word: WordProvider> embedded_hal::spi::SpiBus<Word> for Spi<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_with_fill_words(words.len());
loop {
if current_read_idx < words.len() {
words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
.try_into()
.unwrap();
current_read_idx += 1;
}
if current_write_idx < words.len() {
if current_write_idx == words.len() - 1 && self.bmstall {
nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?;
} else {
nb::block!(self.write_fifo(self.fill_word.into()))?;
}
current_write_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_with_words(words);
while current_write_idx < words.len() {
if current_write_idx == words.len() - 1 && self.bmstall {
nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?;
} else {
nb::block!(self.write_fifo(words[current_write_idx].into()))?;
}
current_write_idx += 1;
// Ignore received words.
if self.regs.read_status().rx_not_empty() {
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_with_words(write);
while current_read_idx < read.len() || current_write_idx < write.len() {
if current_write_idx < write.len() {
if current_write_idx == write.len() - 1 && self.bmstall {
nb::block!(
self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK)
)?;
} else {
nb::block!(self.write_fifo(write[current_write_idx].into()))?;
}
current_write_idx += 1;
}
if current_read_idx < read.len() {
read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
.try_into()
.unwrap();
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_with_words(words);
while current_read_idx < words.len() || current_write_idx < words.len() {
if current_write_idx < words.len() {
if current_write_idx == words.len() - 1 && self.bmstall {
nb::block!(
self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK)
)?;
} else {
nb::block!(self.write_fifo(words[current_write_idx].into()))?;
}
current_write_idx += 1;
}
if current_read_idx < words.len() && current_read_idx < current_write_idx {
words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
.try_into()
.unwrap();
current_read_idx += 1;
}
}
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_internal();
Ok(())
}
}
/// Changing the word size also requires a type conversion
impl From<Spi<u8>> for Spi<u16> {
fn from(mut old_spi: Spi<u8>) -> Self {
old_spi.regs.modify_ctrl0(|mut value| {
value.set_word_size(WordSize::SixteenBits);
value
});
Spi {
id: old_spi.id,
regs: old_spi.regs,
cfg: old_spi.cfg,
blockmode: old_spi.blockmode,
fill_word: Default::default(),
bmstall: old_spi.bmstall,
word: PhantomData,
}
}
}
impl From<Spi<u16>> for Spi<u8> {
fn from(mut old_spi: Spi<u16>) -> Self {
old_spi.regs.modify_ctrl0(|mut value| {
value.set_word_size(WordSize::EightBits);
value
});
Spi {
id: old_spi.id,
regs: old_spi.regs,
cfg: old_spi.cfg,
blockmode: old_spi.blockmode,
fill_word: Default::default(),
bmstall: old_spi.bmstall,
word: PhantomData,
}
}
}

303
src/spi/pins_vor1x.rs Normal file
View File

@ -0,0 +1,303 @@
use super::{HwCsProvider, PinMiso, PinMosi, PinSck};
use crate::FunSel;
use crate::gpio::{PinId, PinIdProvider};
use crate::pins::{
Pa10, Pa11, Pa12, Pa13, Pa14, Pa15, Pa16, Pa17, Pa18, Pa19, Pa20, Pa21, Pa22, Pa23, Pa24, Pa25,
Pa26, Pa27, Pa28, Pa29, Pa30, Pa31, Pb0, Pb1, Pb2, Pb3, Pb4, Pb5, Pb6, Pb7, Pb8, Pb9, Pb10,
Pb11, Pb12, Pb13, Pb14, Pb15, Pb16, Pb17, Pb18, Pb19, Pb22, Pb23, Pin,
};
use super::{Bank, HwChipSelectId};
// SPIA
impl PinSck for Pin<Pa31> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMosi for Pin<Pa30> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMiso for Pin<Pa29> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinSck for Pin<Pb9> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMosi for Pin<Pb8> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pb7> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
hw_cs_pins!(
Bank::Spi0,
(Pb0, FunSel::Sel2, HwChipSelectId::Id1),
(Pb1, FunSel::Sel2, HwChipSelectId::Id2),
(Pb2, FunSel::Sel2, HwChipSelectId::Id3),
(Pb3, FunSel::Sel2, HwChipSelectId::Id4),
(Pb4, FunSel::Sel2, HwChipSelectId::Id5),
(Pb5, FunSel::Sel2, HwChipSelectId::Id6),
(Pb6, FunSel::Sel2, HwChipSelectId::Id0),
(Pa24, FunSel::Sel1, HwChipSelectId::Id4),
(Pa25, FunSel::Sel1, HwChipSelectId::Id3),
(Pa26, FunSel::Sel1, HwChipSelectId::Id2),
(Pa27, FunSel::Sel1, HwChipSelectId::Id1),
(Pa28, FunSel::Sel1, HwChipSelectId::Id0),
);
hw_cs_multi_pin!(
PinPb0SpiaHwCsId1,
Pb0,
Bank::Spi0,
FunSel::Sel2,
HwChipSelectId::Id1
);
hw_cs_multi_pin!(
PinPb1SpiaHwCsId2,
Pb1,
Bank::Spi0,
FunSel::Sel2,
HwChipSelectId::Id2
);
hw_cs_multi_pin!(
PinPb2SpiaHwCsId3,
Pb2,
Bank::Spi0,
FunSel::Sel2,
HwChipSelectId::Id3
);
hw_cs_multi_pin!(
PinPa21SpiaHwCsId7,
Pa21,
Bank::Spi0,
FunSel::Sel1,
HwChipSelectId::Id7
);
hw_cs_multi_pin!(
PinPa22SpiaHwCsId6,
Pa22,
Bank::Spi0,
FunSel::Sel1,
HwChipSelectId::Id6
);
hw_cs_multi_pin!(
PinPa23SpiaHwCsId5,
Pa23,
Bank::Spi0,
FunSel::Sel1,
HwChipSelectId::Id5
);
// SPIB
impl PinSck for Pin<Pa20> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMosi for Pin<Pa19> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pa18> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
pub type SpiBPortASck = Pin<Pa20>;
pub type SpiBPortAMosi = Pin<Pa19>;
pub type SpiBPortAMiso = Pin<Pa18>;
impl PinSck for Pin<Pb19> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMosi for Pin<Pb18> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMiso for Pin<Pb17> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinSck for Pin<Pb5> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMosi for Pin<Pb4> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMiso for Pin<Pb3> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
// TODO: Need to deal with these duplications..
hw_cs_pins!(
Bank::Spi1,
(Pb16, FunSel::Sel1, HwChipSelectId::Id0),
(Pb15, FunSel::Sel1, HwChipSelectId::Id1),
(Pb14, FunSel::Sel1, HwChipSelectId::Id2),
(Pb13, FunSel::Sel1, HwChipSelectId::Id3),
(Pa17, FunSel::Sel2, HwChipSelectId::Id0),
(Pa16, FunSel::Sel2, HwChipSelectId::Id1),
(Pa15, FunSel::Sel2, HwChipSelectId::Id2),
(Pa14, FunSel::Sel2, HwChipSelectId::Id3),
(Pa13, FunSel::Sel2, HwChipSelectId::Id4),
(Pa12, FunSel::Sel2, HwChipSelectId::Id5),
(Pa11, FunSel::Sel2, HwChipSelectId::Id6),
(Pa10, FunSel::Sel2, HwChipSelectId::Id7),
(Pa23, FunSel::Sel2, HwChipSelectId::Id5),
(Pa22, FunSel::Sel2, HwChipSelectId::Id6),
(Pa21, FunSel::Sel2, HwChipSelectId::Id7),
);
hw_cs_multi_pin!(
PinPb0SpibHwCsId2,
Pb0,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id2
);
hw_cs_multi_pin!(
PinPb1SpibHwCsId1,
Pb1,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id1
);
hw_cs_multi_pin!(
PinPb2SpibHwCsId0,
Pb2,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id0
);
hw_cs_multi_pin!(
PinPb10SpibHwCsId6,
Pb10,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id6
);
hw_cs_multi_pin!(
PinPb11SpibHwCsId5,
Pb11,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id5
);
hw_cs_multi_pin!(
PinPb12SpibHwCsId4,
Pb12,
Bank::Spi1,
FunSel::Sel1,
HwChipSelectId::Id4
);
hw_cs_multi_pin!(
PinPb10SpibHwCsId2,
Pb10,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id2
);
hw_cs_multi_pin!(
PinPb11SpibHwCsId1,
Pb11,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id1
);
hw_cs_multi_pin!(
PinPb12SpibHwCsId0,
Pb12,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id0
);
hw_cs_multi_pin!(
PinPa21SpibHwCsId7,
Pa21,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id7
);
hw_cs_multi_pin!(
PinPa22SpibHwCsId6,
Pa22,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id6
);
hw_cs_multi_pin!(
PinPa23SpibHwCsId5,
Pa23,
Bank::Spi1,
FunSel::Sel2,
HwChipSelectId::Id5
);
// SPIC
hw_cs_pins!(
Bank::Spi2,
(Pb9, FunSel::Sel3, HwChipSelectId::Id1),
(Pb8, FunSel::Sel3, HwChipSelectId::Id2),
(Pb7, FunSel::Sel3, HwChipSelectId::Id3),
(Pb23, FunSel::Sel3, HwChipSelectId::Id2),
(Pb22, FunSel::Sel3, HwChipSelectId::Id1),
(Pa20, FunSel::Sel1, HwChipSelectId::Id1),
(Pa19, FunSel::Sel1, HwChipSelectId::Id2),
(Pb18, FunSel::Sel1, HwChipSelectId::Id3),
);
hw_cs_multi_pin!(
PinPa21SpicHwCsId3,
Pa21,
Bank::Spi2,
FunSel::Sel3,
HwChipSelectId::Id3
);
hw_cs_multi_pin!(
PinPa22SpicHwCsId2,
Pa22,
Bank::Spi2,
FunSel::Sel3,
HwChipSelectId::Id2
);
hw_cs_multi_pin!(
PinPa23SpicHwCsId1,
Pa23,
Bank::Spi2,
FunSel::Sel3,
HwChipSelectId::Id1
);
hw_cs_multi_pin!(
PinPa20SpicHwCsId1,
Pa20,
Bank::Spi2,
FunSel::Sel1,
HwChipSelectId::Id1
);
hw_cs_multi_pin!(
PinPa20SpicHwCsId4,
Pa20,
Bank::Spi2,
FunSel::Sel3,
HwChipSelectId::Id4
);

205
src/spi/pins_vor4x.rs Normal file
View File

@ -0,0 +1,205 @@
use crate::{
FunSel,
gpio::{Pin, PinId, PinIdProvider},
pins::{
Pa0, Pa1, Pa2, Pa3, Pa4, Pa5, Pa6, Pa7, Pa8, Pa9, Pb0, Pb1, Pb2, Pb3, Pb4, Pb12, Pb13,
Pb14, Pb15, Pc0, Pc1, Pc7, Pc8, Pc9, Pc10, Pc11, Pe5, Pe6, Pe7, Pe8, Pe9, Pe12, Pe13, Pe14,
Pe15, Pf0, Pf1, Pg2, Pg3, Pg4,
},
};
#[cfg(not(feature = "va41628"))]
use crate::pins::{Pb5, Pb6, Pb7, Pb8, Pb9, Pb10, Pb11, Pe10, Pe11, Pf2, Pf3, Pf4, Pf5, Pf6, Pf7};
use super::{Bank, HwChipSelectId, HwCsProvider, PinMiso, PinMosi, PinSck};
// SPI0
impl PinSck for Pin<Pb15> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMosi for Pin<Pc1> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinMiso for Pin<Pc0> {
const SPI_ID: Bank = Bank::Spi0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
hw_cs_pins!(
Bank::Spi0,
(Pb14, FunSel::Sel1, HwChipSelectId::Id0),
(Pb13, FunSel::Sel1, HwChipSelectId::Id1),
(Pb12, FunSel::Sel1, HwChipSelectId::Id2),
);
#[cfg(not(feature = "va41628"))]
hw_cs_pins!(Bank::Spi0, (Pb11, FunSel::Sel1, HwChipSelectId::Id3));
// SPI1
#[cfg(not(feature = "va41628"))]
impl PinSck for Pin<Pb8> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
#[cfg(not(feature = "va41628"))]
impl PinMosi for Pin<Pb10> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
#[cfg(not(feature = "va41628"))]
impl PinMiso for Pin<Pb9> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl PinSck for Pin<Pc9> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMosi for Pin<Pc11> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pc10> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinSck for Pin<Pe13> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMosi for Pin<Pe15> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pe14> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl PinSck for Pin<Pf3> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
#[cfg(not(feature = "va41628"))]
impl PinMosi for Pin<Pf5> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
#[cfg(not(feature = "va41628"))]
impl PinMiso for Pin<Pf4> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl PinSck for Pin<Pg3> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pg4> {
const SPI_ID: Bank = Bank::Spi1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
hw_cs_pins!(
Bank::Spi1,
(Pb4, FunSel::Sel3, HwChipSelectId::Id3),
(Pb3, FunSel::Sel3, HwChipSelectId::Id4),
(Pb2, FunSel::Sel3, HwChipSelectId::Id5),
(Pb1, FunSel::Sel3, HwChipSelectId::Id6),
(Pb0, FunSel::Sel3, HwChipSelectId::Id7),
(Pc8, FunSel::Sel2, HwChipSelectId::Id0),
(Pc7, FunSel::Sel2, HwChipSelectId::Id1),
(Pe12, FunSel::Sel2, HwChipSelectId::Id0),
(Pe9, FunSel::Sel2, HwChipSelectId::Id3),
(Pe8, FunSel::Sel2, HwChipSelectId::Id4),
(Pe7, FunSel::Sel3, HwChipSelectId::Id5),
(Pe6, FunSel::Sel3, HwChipSelectId::Id6),
(Pe5, FunSel::Sel3, HwChipSelectId::Id7),
(Pg2, FunSel::Sel2, HwChipSelectId::Id0),
);
#[cfg(not(feature = "va41628"))]
hw_cs_pins!(
Bank::Spi1,
(Pb7, FunSel::Sel3, HwChipSelectId::Id0),
(Pb6, FunSel::Sel3, HwChipSelectId::Id1),
(Pb5, FunSel::Sel3, HwChipSelectId::Id2),
(Pe11, FunSel::Sel2, HwChipSelectId::Id1),
(Pe10, FunSel::Sel2, HwChipSelectId::Id2),
);
#[cfg(not(feature = "va41628"))]
hw_cs_multi_pin!(
PinPf2Spi1HwCsId0,
Pf2,
Bank::Spi2,
FunSel::Sel1,
HwChipSelectId::Id0
);
// SPI2
impl PinSck for Pin<Pa5> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMosi for Pin<Pa7> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl PinMiso for Pin<Pa6> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl PinSck for Pin<Pf5> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl PinMosi for Pin<Pf7> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl PinMiso for Pin<Pf6> {
const SPI_ID: Bank = Bank::Spi2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
hw_cs_pins!(
Bank::Spi1,
(Pa4, FunSel::Sel2, HwChipSelectId::Id0),
(Pa3, FunSel::Sel2, HwChipSelectId::Id1),
(Pa2, FunSel::Sel2, HwChipSelectId::Id2),
(Pa1, FunSel::Sel2, HwChipSelectId::Id3),
(Pa0, FunSel::Sel2, HwChipSelectId::Id4),
(Pa8, FunSel::Sel2, HwChipSelectId::Id5),
(Pa9, FunSel::Sel2, HwChipSelectId::Id6),
(Pf0, FunSel::Sel2, HwChipSelectId::Id4),
(Pf1, FunSel::Sel2, HwChipSelectId::Id3),
);
#[cfg(not(feature = "va41628"))]
hw_cs_pins!(
Bank::Spi1,
(Pf3, FunSel::Sel2, HwChipSelectId::Id1),
(Pf4, FunSel::Sel2, HwChipSelectId::Id0),
);
#[cfg(not(feature = "va41628"))]
hw_cs_multi_pin!(
PinPf2Spi2HwCsId2,
Pf2,
Bank::Spi2,
FunSel::Sel2,
HwChipSelectId::Id2
);

279
src/spi/regs.rs Normal file
View File

@ -0,0 +1,279 @@
use core::marker::PhantomData;
pub use crate::shared::{FifoClear, TriggerLevel};
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// SPI A base address
pub const BASE_ADDR_0: usize = 0x4005_0000;
/// SPI B base address
pub const BASE_ADDR_1: usize = 0x4005_1000;
/// SPI C base address
pub const BASE_ADDR_2: usize = 0x4005_2000;
} else if #[cfg(feature = "vor4x")] {
/// SPI 0 base address
pub const BASE_ADDR_0: usize = 0x4001_5000;
/// SPI 1 base address
pub const BASE_ADDR_1: usize = 0x4001_5400;
/// SPI 2 base address
pub const BASE_ADDR_2: usize = 0x4001_5800;
/// SPI 3 base address
pub const BASE_ADDR_3: usize = 0x4001_5C00;
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
Spi0,
Spi1,
Spi2,
#[cfg(feature = "vor4x")]
Spi3,
}
impl Bank {
/// Unsafely steal the SPI peripheral block for the given port.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_regs(&self) -> MmioSpi<'static> {
Spi::new_mmio(*self)
}
}
#[bitbybit::bitenum(u4)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
EightBits = 0x07,
SixteenBits = 0x0f,
}
#[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u3, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
Id2 = 2,
Id3 = 3,
Id4 = 4,
Id5 = 5,
Id6 = 6,
Id7 = 7,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Control0 {
#[bits(8..=15, rw)]
scrdv: u8,
#[bit(7, rw)]
sph: bool,
#[bit(6, rw)]
spo: bool,
#[bits(0..=3, rw)]
word_size: Option<WordSize>,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Control1 {
#[bit(11, rw)]
mtxpause: bool,
#[bit(10, rw)]
mdlycap: bool,
#[bit(9, rw)]
bm_stall: bool,
#[bit(8, rw)]
bm_start: bool,
#[bit(7, rw)]
blockmode: bool,
#[bits(4..=6, rw)]
ss: HwChipSelectId,
#[bit(3, rw)]
sod: bool,
#[bit(2, rw)]
slave_mode: bool,
#[bit(1, rw)]
enable: bool,
#[bit(0, rw)]
lbm: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Data {
/// Only used for BLOCKMODE. For received data, this bit indicated that the data was the first
/// word after the chip select went active. For transmitted data, setting this bit to 1
/// will end an SPI frame (deassert CS) after the specified data word.
#[bit(31, rw)]
bm_start_stop: bool,
/// Only used for BLOCKMODE. Setting this bit to 1 along with the BMSTOP bit will end an SPI
/// frame without any additional data to be transmitted. If BMSTOP is not set, this bit is
/// ignored.
#[bit(30, rw)]
bm_skipdata: bool,
#[bits(0..=15, rw)]
data: u16,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
/// TX FIFO below the trigger level.
#[bit(7, r)]
tx_trigger: bool,
/// RX FIFO above or equals the trigger level.
#[bit(6, r)]
rx_trigger: bool,
#[bit(5, r)]
rx_data_first: bool,
#[bit(4, r)]
busy: bool,
#[bit(3, r)]
rx_full: bool,
#[bit(2, r)]
rx_not_empty: bool,
#[bit(1, r)]
tx_not_full: bool,
#[bit(0, r)]
tx_empty: bool,
}
/// Clock divisor value. Bit 0 is ignored and always 0. This means that only the even values
/// are used as clock divisor values, and uneven values are truncated to the next even value.
/// A value of 0 acts as a 1 for the divisor value.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ClkPrescaler(arbitrary_int::UInt<u32, 8>);
impl ClkPrescaler {
pub const fn new(value: u8) -> Self {
ClkPrescaler(arbitrary_int::UInt::<u32, 8>::new(value as u32))
}
pub const fn value(&self) -> u8 {
self.0.value() as u8
}
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptControl {
/// TX FIFO count <= TX FIFO trigger level.
#[bit(3, rw)]
tx: bool,
/// RX FIFO count >= RX FIFO trigger level.
#[bit(2, rw)]
rx: bool,
/// Occurs when the RX FIFO has not been read within 32 clock ticks of the SPICLKx2 clock
/// within the RX FIFO not being empty. Clearing the RX interrupt or reading data from the
/// FIFO resets the timeout counter.
#[bit(1, rw)]
rx_timeout: bool,
#[bit(0, rw)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptStatus {
/// TX FIFO count <= TX FIFO trigger level.
#[bit(3, r)]
tx: bool,
/// RX FIFO count >= RX FIFO trigger level.
#[bit(2, r)]
rx: bool,
/// Occurs when the RX FIFO has not been read within 32 clock ticks of the SPICLKx2 clock
/// within the RX FIFO not being empty. Clearing the RX interrupt or reading data from the
/// FIFO resets the timeout counter.
#[bit(1, r)]
rx_timeout: bool,
#[bit(0, r)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptClear {
/// Clearing the RX interrupt or reading data from the FIFO resets the timeout counter.
#[bit(1, w)]
rx_timeout: bool,
#[bit(0, w)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct State {
#[bits(0..=7, r)]
rx_state: u8,
#[bits(8..=15, r)]
rx_fifo: u8,
#[bits(24..=31, r)]
tx_fifo: u8,
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct Spi {
ctrl0: Control0,
ctrl1: Control1,
data: Data,
#[mmio(PureRead)]
status: Status,
clkprescale: ClkPrescaler,
irq_enb: InterruptControl,
/// Raw interrupt status.
#[mmio(PureRead)]
irq_raw: InterruptStatus,
/// Enabled interrupt status.
#[mmio(PureRead)]
irq_status: InterruptStatus,
#[mmio(Write)]
irq_clear: InterruptClear,
rx_fifo_trigger: TriggerLevel,
tx_fifo_trigger: TriggerLevel,
#[mmio(Write)]
fifo_clear: FifoClear,
#[mmio(PureRead)]
state: u32,
#[cfg(feature = "vor1x")]
_reserved: [u32; 0x3F2],
#[cfg(feature = "vor4x")]
_reserved: [u32; 0xF2],
/// Vorago 1x: 0x0113_07E1. Vorago 4x: 0x0213_07E9.
#[mmio(PureRead)]
perid: u32,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x1000);
} else if #[cfg(feature = "vor4x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x400);
}
}
impl Spi {
fn new_mmio_at(base: usize) -> MmioSpi<'static> {
MmioSpi {
ptr: base as *mut _,
phantom: PhantomData,
}
}
pub fn new_mmio(bank: Bank) -> MmioSpi<'static> {
match bank {
Bank::Spi0 => Self::new_mmio_at(BASE_ADDR_0),
Bank::Spi1 => Self::new_mmio_at(BASE_ADDR_1),
Bank::Spi2 => Self::new_mmio_at(BASE_ADDR_2),
#[cfg(feature = "vor4x")]
Bank::Spi3 => Self::new_mmio_at(BASE_ADDR_2),
}
}
}

43
src/sysconfig.rs Normal file
View File

@ -0,0 +1,43 @@
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
#[inline]
pub fn enable_peripheral_clock(clock: crate::PeripheralSelect) {
let syscfg = unsafe { pac::Sysconfig::steal() };
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[inline]
pub fn disable_peripheral_clock(clock: crate::PeripheralSelect) {
let syscfg = unsafe { pac::Sysconfig::steal() };
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
}
#[inline]
pub fn assert_peripheral_reset(periph_sel: crate::PeripheralSelect) {
let syscfg = unsafe { pac::Sysconfig::steal() };
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph_sel as u8)) });
}
#[inline]
pub fn deassert_peripheral_reset(periph_sel: crate::PeripheralSelect) {
let syscfg = unsafe { pac::Sysconfig::steal() };
syscfg
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph_sel as u8)) });
}
#[inline]
pub fn reset_peripheral_for_cycles(periph_sel: crate::PeripheralSelect, cycles: usize) {
assert_peripheral_reset(periph_sel);
cortex_m::asm::delay(cycles as u32);
deassert_peripheral_reset(periph_sel);
}

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

504
src/timer/mod.rs Normal file
View File

@ -0,0 +1,504 @@
pub mod regs;
use core::convert::Infallible;
#[cfg(feature = "vor1x")]
pub use crate::InterruptConfig;
#[cfg(feature = "vor1x")]
use crate::sysconfig::enable_peripheral_clock;
pub use regs::{CascadeSource, InvalidTimerIndex, TimId};
use crate::{enable_nvic_interrupt, sealed::Sealed, time::Hertz};
use crate::{gpio::PinId, ioconfig::regs::FunSel, pins::PinMarker};
use fugit::RateExtU32;
#[cfg(feature = "vor1x")]
use crate::PeripheralSelect;
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
#[cfg(feature = "vor4x")]
pub const TIM_IRQ_OFFSET: usize = 48;
//==================================================================================================
// Defintions
//==================================================================================================
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CascadeControl {
/// Enable Cascade 0 signal active as a requirement for counting
pub enable_src_0: bool,
/// Invert Cascade 0, making it active low
pub inv_src_0: regs::CascadeInvert,
/// Enable Cascade 1 signal active as a requirement for counting
pub enable_src_1: bool,
/// Invert Cascade 1, making it active low
pub inv_src_1: regs::CascadeInvert,
/// 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_operation: regs::DualCascadeOp,
/// 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 trigger_mode_0: bool,
/// Trigger mode, identical to [Self::trigger_mode_0] but for Cascade 1
pub trigger_mode_1: 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 enable_stop_src_2: bool,
/// Invert Cascade 2, making it active low
pub inv_src_2: regs::CascadeInvert,
/// 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 trigger_mode_2: bool,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSelect {
Csd0 = 0,
Csd1 = 1,
Csd2 = 2,
}
//==================================================================================================
// Valid TIM and PIN combinations
//==================================================================================================
pub trait TimPin: PinMarker {
const PIN_ID: PinId;
const FUN_SEL: FunSel;
const TIM_ID: TimId;
}
pub trait TimMarker: Sealed {
// TIM ID ranging from 0 to 23 for 24 TIM peripherals
const ID: TimId;
#[cfg(feature = "vor4x")]
const IRQ: va416xx::Interrupt;
#[cfg(feature = "vor4x")]
fn clock(clocks: &crate::clock::Clocks) -> Hertz {
if Self::ID.value() <= 15 {
clocks.apb1()
} else {
clocks.apb2()
}
}
}
macro_rules! tim_marker {
($TIMX:path, $ID:expr) => {
impl TimMarker for $TIMX {
const ID: TimId = TimId::new_unchecked($ID);
}
impl Sealed for $TIMX {}
};
($TIMX:path, $ID:expr, $IrqId:ident) => {
impl TimMarker for $TIMX {
const ID: TimId = TimId::new_unchecked($ID);
const IRQ: va416xx::Interrupt = va416xx::Interrupt::$IrqId;
}
impl Sealed for $TIMX {}
};
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
tim_marker!(pac::Tim0, 0);
tim_marker!(pac::Tim1, 1);
tim_marker!(pac::Tim2, 2);
tim_marker!(pac::Tim3, 3);
tim_marker!(pac::Tim4, 4);
tim_marker!(pac::Tim5, 5);
tim_marker!(pac::Tim6, 6);
tim_marker!(pac::Tim7, 7);
tim_marker!(pac::Tim8, 8);
tim_marker!(pac::Tim9, 9);
tim_marker!(pac::Tim10, 10);
tim_marker!(pac::Tim11, 11);
tim_marker!(pac::Tim12, 12);
tim_marker!(pac::Tim13, 13);
tim_marker!(pac::Tim14, 14);
tim_marker!(pac::Tim15, 15);
tim_marker!(pac::Tim16, 16);
tim_marker!(pac::Tim17, 17);
tim_marker!(pac::Tim18, 18);
tim_marker!(pac::Tim19, 19);
tim_marker!(pac::Tim20, 20);
tim_marker!(pac::Tim21, 21);
tim_marker!(pac::Tim22, 22);
tim_marker!(pac::Tim23, 23);
} else if #[cfg(feature = "vor4x")] {
tim_marker!(pac::Tim0, 0, TIM0);
tim_marker!(pac::Tim1, 1, TIM1);
tim_marker!(pac::Tim2, 2, TIM2);
tim_marker!(pac::Tim3, 3, TIM3);
tim_marker!(pac::Tim4, 4, TIM4);
tim_marker!(pac::Tim5, 5, TIM5);
tim_marker!(pac::Tim6, 6, TIM6);
tim_marker!(pac::Tim7, 7, TIM7);
tim_marker!(pac::Tim8, 8, TIM8);
tim_marker!(pac::Tim9, 9, TIM9);
tim_marker!(pac::Tim10, 10, TIM10);
tim_marker!(pac::Tim11, 11, TIM11);
tim_marker!(pac::Tim12, 12, TIM12);
tim_marker!(pac::Tim13, 13, TIM13);
tim_marker!(pac::Tim14, 14, TIM14);
tim_marker!(pac::Tim15, 15, TIM15);
tim_marker!(pac::Tim16, 16, TIM16);
tim_marker!(pac::Tim17, 17, TIM17);
tim_marker!(pac::Tim18, 18, TIM18);
tim_marker!(pac::Tim19, 19, TIM19);
tim_marker!(pac::Tim20, 20, TIM20);
tim_marker!(pac::Tim21, 21, TIM21);
tim_marker!(pac::Tim22, 22, TIM22);
tim_marker!(pac::Tim23, 23, TIM23);
}
}
pub trait ValidTimAndPin<Pin: TimPin, Tim: TimMarker>: Sealed {}
#[macro_use]
mod macros {
macro_rules! pin_and_tim {
($Px:ident, $FunSel:path, $ID:expr) => {
impl TimPin for Pin<$Px>
where
$Px: PinIdProvider,
{
const PIN_ID: PinId = $Px::ID;
const FUN_SEL: FunSel = $FunSel;
const TIM_ID: TimId = TimId::new_unchecked($ID);
}
};
}
}
#[cfg(feature = "vor1x")]
pub mod pins_vor1x;
#[cfg(feature = "vor4x")]
pub mod pins_vor4x;
//==================================================================================================
// Timers
//==================================================================================================
/// Hardware timers
pub struct CountdownTimer {
id: TimId,
regs: regs::MmioTimer<'static>,
curr_freq: Hertz,
ref_clk: Hertz,
rst_val: u32,
last_cnt: u32,
}
impl CountdownTimer {
/// Create a countdown timer structure for a given TIM peripheral.
///
/// This does not enable the timer. You can use the [Self::load], [Self::start],
/// [Self::enable_interrupt] and [Self::enable] API to set up and configure the countdown
/// timer.
#[cfg(feature = "vor1x")]
pub fn new<Tim: TimMarker>(_tim: Tim, sys_clk: Hertz) -> Self {
enable_tim_clk(Tim::ID);
assert_tim_reset_for_cycles(Tim::ID, 2);
CountdownTimer {
id: Tim::ID,
regs: regs::Timer::new_mmio(Tim::ID),
ref_clk: sys_clk,
rst_val: 0,
curr_freq: 0.Hz(),
last_cnt: 0,
}
}
/// Create a countdown timer structure for a given TIM peripheral.
///
/// This does not enable the timer. You can use the [Self::load], [Self::start],
/// [Self::enable_interrupt] and [Self::enable] API to set up and configure the countdown
/// timer.
#[cfg(feature = "vor4x")]
pub fn new<Tim: TimMarker>(_tim: Tim, clks: &crate::clock::Clocks) -> Self {
enable_tim_clk(Tim::ID);
assert_tim_reset_for_cycles(Tim::ID, 2);
CountdownTimer {
id: Tim::ID,
regs: regs::Timer::new_mmio(Tim::ID),
ref_clk: clks.apb1(),
rst_val: 0,
curr_freq: 0.Hz(),
last_cnt: 0,
}
}
#[inline]
pub fn perid(&self) -> u32 {
self.regs.read_perid()
}
#[inline(always)]
pub fn enable(&mut self) {
self.regs
.write_enable_control(regs::EnableControl::new_enable());
}
#[inline(always)]
pub fn disable(&mut self) {
self.regs
.write_enable_control(regs::EnableControl::new_disable());
}
#[cfg(feature = "vor1x")]
pub fn enable_interrupt(&mut self, irq_cfg: InterruptConfig) {
if irq_cfg.route {
let irqsel = unsafe { pac::Irqsel::steal() };
enable_peripheral_clock(PeripheralSelect::Irqsel);
irqsel
.tim0(self.id.value() as usize)
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
self.regs.modify_control(|mut value| {
value.set_irq_enable(true);
value
});
}
#[cfg(feature = "vor4x")]
#[inline(always)]
pub fn enable_interrupt(&mut self, enable_in_nvic: bool) {
if enable_in_nvic {
unsafe { enable_nvic_interrupt(self.id.interrupt_id()) };
}
self.regs.modify_control(|mut value| {
value.set_irq_enable(true);
value
});
}
/// This function only clears the interrupt enable bit.
///
/// It does not mask the interrupt in the NVIC or un-route the IRQ.
#[inline(always)]
pub fn disable_interrupt(&mut self) {
self.regs.modify_control(|mut value| {
value.set_irq_enable(false);
value
});
}
/// Calls [Self::load] to configure the specified frequency and then calls [Self::enable].
pub fn start(&mut self, frequency: impl Into<Hertz>) {
self.load(frequency);
self.enable();
}
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
/// flag and restart the time if configured correctly
pub fn wait(&mut self) -> nb::Result<(), Infallible> {
let cnt = self.counter();
if (cnt > self.last_cnt) || cnt == 0 {
self.last_cnt = self.rst_val;
Ok(())
} else {
self.last_cnt = cnt;
Err(nb::Error::WouldBlock)
}
}
/// Load the count down timer with a timeout but do not start it.
pub fn load(&mut self, timeout: impl Into<Hertz>) {
self.disable();
self.curr_freq = timeout.into();
self.rst_val = self.ref_clk.raw() / self.curr_freq.raw();
self.set_reload(self.rst_val);
self.set_count(self.rst_val);
}
#[inline(always)]
pub fn set_reload(&mut self, val: u32) {
self.regs.write_reset_value(val);
}
#[inline(always)]
pub fn set_count(&mut self, val: u32) {
self.regs.write_count_value(val);
}
#[inline(always)]
pub fn counter(&self) -> u32 {
self.regs.read_count_value()
}
/// Disable the counter, setting both enable and active bit to 0
#[inline]
pub fn auto_disable(&mut self, enable: bool) {
self.regs.modify_control(|mut value| {
value.set_auto_disable(enable);
value
});
}
/// 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(&mut self, enable: bool) {
self.regs.modify_control(|mut value| {
value.set_auto_deactivate(enable);
value
});
}
/// Configure the cascade parameters
pub fn cascade_control(&mut self, ctrl: CascadeControl) {
self.regs.write_cascade_control(
regs::CascadeControl::builder()
.with_trigger2(ctrl.trigger_mode_2)
.with_inv2(ctrl.inv_src_2)
.with_en2(ctrl.enable_stop_src_2)
.with_trigger1(ctrl.trigger_mode_1)
.with_trigger0(ctrl.trigger_mode_0)
.with_dual_cascade_op(ctrl.dual_operation)
.with_inv1(ctrl.inv_src_1)
.with_en1(ctrl.enable_src_1)
.with_inv0(ctrl.inv_src_0)
.with_en0(ctrl.enable_src_0)
.build(),
);
}
pub fn cascade_source(
&mut self,
cascade_index: CascadeSelect,
src: regs::CascadeSource,
) -> Result<(), regs::InvalidCascadeSourceId> {
// Safety: Index range safe by enum values.
unsafe {
self.regs
.write_cascade_unchecked(cascade_index as usize, regs::CascadeSourceReg::new(src)?);
}
Ok(())
}
pub fn curr_freq(&self) -> Hertz {
self.curr_freq
}
/// Disables the TIM and the dedicated TIM clock.
pub fn stop_with_clock_disable(mut self) {
self.disable();
unsafe { pac::Sysconfig::steal() }
.tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.id.value())) });
}
}
//==================================================================================================
// Delay implementations
//==================================================================================================
//
impl embedded_hal::delay::DelayNs for CountdownTimer {
fn delay_ns(&mut self, ns: u32) {
let ticks = (u64::from(ns)) * (u64::from(self.ref_clk.raw())) / 1_000_000_000;
let full_cycles = ticks >> 32;
let mut last_count;
let mut new_count;
if full_cycles > 0 {
self.set_reload(u32::MAX);
self.set_count(u32::MAX);
self.enable();
for _ in 0..full_cycles {
// Always ensure that both values are the same at the start.
new_count = self.counter();
last_count = new_count;
loop {
new_count = self.counter();
if new_count == 0 {
// Wait till timer has wrapped.
while self.counter() == 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.counter();
if new_count == 0 || (new_count > last_count) {
break;
}
last_count = new_count;
}
}
self.disable();
}
}
#[inline(always)]
pub fn enable_tim_clk(id: TimId) {
unsafe { pac::Sysconfig::steal() }
.tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << id.value())) });
}
#[inline(always)]
pub fn disable_tim_clk(id: TimId) {
unsafe { pac::Sysconfig::steal() }
.tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (id.value()))) });
}
/// Clear the reset bit of the TIM, holding it in reset
///
/// # Safety
///
/// Only the bit related to the corresponding TIM peripheral is modified
#[inline]
pub fn assert_tim_reset(id: TimId) {
unsafe { pac::Peripherals::steal() }
.sysconfig
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << id.value())) });
}
#[inline]
pub fn deassert_tim_reset(tim: TimId) {
unsafe { pac::Peripherals::steal() }
.sysconfig
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim.value())) });
}
pub fn assert_tim_reset_for_cycles(tim: TimId, cycles: u32) {
assert_tim_reset(tim);
cortex_m::asm::delay(cycles);
deassert_tim_reset(tim);
}

56
src/timer/pins_vor1x.rs Normal file
View File

@ -0,0 +1,56 @@
use super::{TimId, TimPin};
use crate::FunSel;
use crate::pins::{
Pa0, Pa1, Pa2, Pa3, Pa4, Pa5, Pa6, Pa7, Pa8, Pa9, Pa10, Pa11, Pa12, Pa13, Pa14, Pa15, Pa24,
Pa25, Pa26, Pa27, Pa28, Pa29, Pa30, Pa31, Pb0, Pb1, Pb2, Pb3, Pb4, Pb5, Pb6, Pb10, Pb11, Pb12,
Pb13, Pb14, Pb15, Pb16, Pb17, Pb18, Pb19, Pb20, Pb21, Pb22, Pb23, Pin, PinId, PinIdProvider,
};
pin_and_tim!(Pa0, FunSel::Sel1, 0);
pin_and_tim!(Pa1, FunSel::Sel1, 1);
pin_and_tim!(Pa2, FunSel::Sel1, 2);
pin_and_tim!(Pa3, FunSel::Sel1, 3);
pin_and_tim!(Pa4, FunSel::Sel1, 4);
pin_and_tim!(Pa5, FunSel::Sel1, 5);
pin_and_tim!(Pa6, FunSel::Sel1, 6);
pin_and_tim!(Pa7, FunSel::Sel1, 7);
pin_and_tim!(Pa8, FunSel::Sel1, 8);
pin_and_tim!(Pa9, FunSel::Sel1, 9);
pin_and_tim!(Pa10, FunSel::Sel1, 10);
pin_and_tim!(Pa11, FunSel::Sel1, 11);
pin_and_tim!(Pa12, FunSel::Sel1, 12);
pin_and_tim!(Pa13, FunSel::Sel1, 13);
pin_and_tim!(Pa14, FunSel::Sel1, 14);
pin_and_tim!(Pa15, FunSel::Sel1, 15);
pin_and_tim!(Pa24, FunSel::Sel2, 16);
pin_and_tim!(Pa25, FunSel::Sel2, 17);
pin_and_tim!(Pa26, FunSel::Sel2, 18);
pin_and_tim!(Pa27, FunSel::Sel2, 19);
pin_and_tim!(Pa28, FunSel::Sel2, 20);
pin_and_tim!(Pa29, FunSel::Sel2, 21);
pin_and_tim!(Pa30, FunSel::Sel2, 22);
pin_and_tim!(Pa31, FunSel::Sel2, 23);
pin_and_tim!(Pb0, FunSel::Sel3, 0);
pin_and_tim!(Pb1, FunSel::Sel3, 1);
pin_and_tim!(Pb2, FunSel::Sel3, 2);
pin_and_tim!(Pb3, FunSel::Sel3, 3);
pin_and_tim!(Pb4, FunSel::Sel3, 4);
pin_and_tim!(Pb5, FunSel::Sel3, 5);
pin_and_tim!(Pb6, FunSel::Sel3, 6);
pin_and_tim!(Pb10, FunSel::Sel3, 10);
pin_and_tim!(Pb11, FunSel::Sel3, 11);
pin_and_tim!(Pb12, FunSel::Sel3, 12);
pin_and_tim!(Pb13, FunSel::Sel3, 13);
pin_and_tim!(Pb14, FunSel::Sel3, 14);
pin_and_tim!(Pb15, FunSel::Sel3, 15);
pin_and_tim!(Pb16, FunSel::Sel3, 16);
pin_and_tim!(Pb17, FunSel::Sel3, 17);
pin_and_tim!(Pb18, FunSel::Sel3, 18);
pin_and_tim!(Pb19, FunSel::Sel3, 19);
pin_and_tim!(Pb20, FunSel::Sel3, 20);
pin_and_tim!(Pb21, FunSel::Sel3, 21);
pin_and_tim!(Pb22, FunSel::Sel3, 22);
pin_and_tim!(Pb23, FunSel::Sel3, 23);

132
src/timer/pins_vor4x.rs Normal file
View File

@ -0,0 +1,132 @@
use super::{FunSel, TimId, TimPin};
use crate::pins::{
Pa0, Pa1, Pa2, Pa3, Pa4, Pa5, Pa6, Pa7, Pa8, Pa10, Pa11, Pa12, Pa13, Pa14, Pa15, Pb0, Pb1, Pb2,
Pb3, Pb4, Pb12, Pb13, Pb14, Pb15, Pc0, Pc1, Pd10, Pd11, Pd12, Pd13, Pd14, Pd15, Pe0, Pe1, Pe2,
Pe3, Pe4, Pe5, Pe6, Pe7, Pe8, Pe9, Pe12, Pe13, Pe14, Pe15, Pf0, Pf1, Pf9, Pf11, Pf12, Pf13,
Pf14, Pf15, Pg0, Pg1, Pg2, Pg3, Pg6, Pin, PinId, PinIdProvider,
};
#[cfg(not(feature = "va41628"))]
use crate::pins::{
Pb5, Pb6, Pb7, Pb8, Pb9, Pb10, Pb11, Pd0, Pd1, Pd2, Pd3, Pd4, Pd5, Pd6, Pd7, Pd8, Pd9, Pe10,
Pe11, Pf2, Pf3, Pf4, Pf5, Pf6, Pf7, Pf8, Pf10,
};
pin_and_tim!(Pa0, FunSel::Sel1, 0);
pin_and_tim!(Pa1, FunSel::Sel1, 1);
pin_and_tim!(Pa2, FunSel::Sel1, 2);
pin_and_tim!(Pa3, FunSel::Sel1, 3);
pin_and_tim!(Pa4, FunSel::Sel1, 4);
pin_and_tim!(Pa5, FunSel::Sel1, 5);
pin_and_tim!(Pa6, FunSel::Sel1, 6);
pin_and_tim!(Pa7, FunSel::Sel1, 7);
pin_and_tim!(Pa8, FunSel::Sel3, 8);
pin_and_tim!(Pa10, FunSel::Sel2, 23);
pin_and_tim!(Pa11, FunSel::Sel2, 22);
pin_and_tim!(Pa12, FunSel::Sel2, 21);
pin_and_tim!(Pa13, FunSel::Sel2, 20);
pin_and_tim!(Pa14, FunSel::Sel2, 19);
pin_and_tim!(Pa15, FunSel::Sel2, 18);
pin_and_tim!(Pb0, FunSel::Sel2, 17);
pin_and_tim!(Pb1, FunSel::Sel2, 16);
pin_and_tim!(Pb2, FunSel::Sel2, 15);
pin_and_tim!(Pb3, FunSel::Sel2, 14);
pin_and_tim!(Pb4, FunSel::Sel2, 13);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb5, FunSel::Sel2, 12);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb6, FunSel::Sel2, 11);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb7, FunSel::Sel2, 10);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb8, FunSel::Sel2, 9);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb9, FunSel::Sel2, 8);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb10, FunSel::Sel2, 7);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pb11, FunSel::Sel2, 6);
pin_and_tim!(Pb12, FunSel::Sel2, 5);
pin_and_tim!(Pb13, FunSel::Sel2, 4);
pin_and_tim!(Pb14, FunSel::Sel2, 3);
pin_and_tim!(Pb15, FunSel::Sel2, 2);
pin_and_tim!(Pc0, FunSel::Sel2, 1);
pin_and_tim!(Pc1, FunSel::Sel2, 0);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd0, FunSel::Sel2, 0);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd1, FunSel::Sel2, 1);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd2, FunSel::Sel2, 2);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd3, FunSel::Sel2, 3);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd4, FunSel::Sel2, 4);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd5, FunSel::Sel2, 5);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd6, FunSel::Sel2, 6);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd7, FunSel::Sel2, 7);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd8, FunSel::Sel2, 8);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pd9, FunSel::Sel2, 9);
pin_and_tim!(Pd10, FunSel::Sel2, 10);
pin_and_tim!(Pd11, FunSel::Sel2, 11);
pin_and_tim!(Pd12, FunSel::Sel2, 12);
pin_and_tim!(Pd13, FunSel::Sel2, 13);
pin_and_tim!(Pd14, FunSel::Sel2, 14);
pin_and_tim!(Pd15, FunSel::Sel2, 15);
pin_and_tim!(Pe0, FunSel::Sel2, 16);
pin_and_tim!(Pe1, FunSel::Sel2, 17);
pin_and_tim!(Pe2, FunSel::Sel2, 18);
pin_and_tim!(Pe3, FunSel::Sel2, 19);
pin_and_tim!(Pe4, FunSel::Sel2, 20);
pin_and_tim!(Pe5, FunSel::Sel2, 21);
pin_and_tim!(Pe6, FunSel::Sel2, 22);
pin_and_tim!(Pe7, FunSel::Sel2, 23);
pin_and_tim!(Pe8, FunSel::Sel3, 16);
pin_and_tim!(Pe9, FunSel::Sel3, 17);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pe10, FunSel::Sel3, 18);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pe11, FunSel::Sel3, 19);
pin_and_tim!(Pe12, FunSel::Sel3, 20);
pin_and_tim!(Pe13, FunSel::Sel3, 21);
pin_and_tim!(Pe14, FunSel::Sel3, 22);
pin_and_tim!(Pe15, FunSel::Sel3, 23);
pin_and_tim!(Pf0, FunSel::Sel3, 0);
pin_and_tim!(Pf1, FunSel::Sel3, 1);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf2, FunSel::Sel3, 2);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf3, FunSel::Sel3, 3);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf4, FunSel::Sel3, 4);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf5, FunSel::Sel3, 5);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf6, FunSel::Sel3, 6);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf7, FunSel::Sel3, 7);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf8, FunSel::Sel3, 8);
pin_and_tim!(Pf9, FunSel::Sel3, 9);
#[cfg(not(feature = "va41628"))]
pin_and_tim!(Pf10, FunSel::Sel3, 10);
pin_and_tim!(Pf11, FunSel::Sel3, 11);
pin_and_tim!(Pf12, FunSel::Sel3, 12);
pin_and_tim!(Pf13, FunSel::Sel2, 19);
pin_and_tim!(Pf14, FunSel::Sel2, 20);
pin_and_tim!(Pf15, FunSel::Sel2, 21);
pin_and_tim!(Pg0, FunSel::Sel2, 22);
pin_and_tim!(Pg1, FunSel::Sel2, 23);
pin_and_tim!(Pg2, FunSel::Sel1, 9);
pin_and_tim!(Pg3, FunSel::Sel1, 10);
pin_and_tim!(Pg6, FunSel::Sel1, 12);

428
src/timer/regs.rs Normal file
View File

@ -0,0 +1,428 @@
use core::marker::PhantomData;
use arbitrary_int::{Number, u7};
#[cfg(feature = "vor1x")]
const BASE_ADDR: usize = 0x4002_0000;
#[cfg(feature = "vor4x")]
const BASE_ADDR: usize = 0x4001_8000;
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
pub enum StatusSelect {
/// Pulse when timer reaches 0.
OneCyclePulse = 0b000,
OutputActiveBit = 0b001,
/// Creates a divide by two output clock of the timer.
ToggleOnEachCycle = 0b010,
/// 1 when count value >= PWM A value, 0 otherwise
PwmaOutput = 0b011,
/// 1 when count value < PWM A value and >= PWM B, 0 when counter value >= PWM A value or < PWM
/// B value
PwmbOutput = 0b100,
EnabledBit = 0b101,
/// 1 when counter value <= PWM A value and 0 otherwise.
PwmaActiveBit = 0b110,
}
#[bitbybit::bitfield(u32)]
pub struct Control {
/// The counter is requested to stop on the next normal count cycle.
#[bit(9, rw)]
request_stop: bool,
#[bit(8, rw)]
status_invert: bool,
#[bits(5..=7, rw)]
status_sel: Option<StatusSelect>,
#[bit(4, rw)]
irq_enable: bool,
/// Only applies if the Auto-Disable bit is 0. The ACTIVE bit goes to 0 when the count reaches
/// 0, but the timer remains enabled.
#[bit(3, rw)]
auto_deactivate: bool,
/// Counter is fully disabled when count reaches 0, which means that both the ENABLE
/// and ACTIVE bits go to 0.
#[bit(2, rw)]
auto_disable: bool,
#[bit(1, r)]
active: bool,
#[bit(0, rw)]
enable: bool,
}
pub struct EnableControl(arbitrary_int::UInt<u32, 1>);
impl EnableControl {
pub fn new_disable() -> Self {
EnableControl(arbitrary_int::UInt::<u32, 1>::from_u32(0))
}
pub fn new_enable() -> Self {
EnableControl(arbitrary_int::UInt::<u32, 1>::from_u32(1))
}
pub fn enabled(&self) -> bool {
self.0.value() != 0
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeInvert {
#[default]
ActiveHigh = 0,
ActiveLow = 1,
}
/// When two cascade sources are selected, configure the required operation.
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DualCascadeOp {
#[default]
LogicalAnd = 0,
LogicalOr = 1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct CascadeControl {
/// 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.
#[bit(10, rw)]
trigger2: bool,
#[bit(9, rw)]
inv2: CascadeInvert,
/// 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.
#[bit(8, rw)]
en2: bool,
/// Same as the trigger field for Cascade 0.
#[bit(7, rw)]
trigger1: 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.
#[bit(6, rw)]
trigger0: 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.
#[bit(4, rw)]
dual_cascade_op: DualCascadeOp,
/// Inversion bit for Cascade 1
#[bit(3, rw)]
inv1: CascadeInvert,
/// Enable Cascade 1 signal active as a requirement for counting.
#[bit(2, rw)]
en1: bool,
/// Inversion bit for Cascade 0.
#[bit(1, rw)]
inv0: CascadeInvert,
/// Enable Cascade 0 signal active as a requirement for counting.
#[bit(0, rw)]
en0: bool,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidCascadeSourceId;
#[cfg(feature = "vor1x")]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum CascadeSource {
PortA(u8),
PortB(u8),
Tim(u8),
RamSbe = 96,
RamMbe = 97,
RomSbe = 98,
RomMbe = 99,
Txev = 100,
ClockDivider(u8),
}
#[cfg(feature = "vor4x")]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum CascadeSource {
PortA(u8),
PortB(u8),
PortC(u8),
PortD(u8),
PortE(u8),
Tim(u8),
TxEv,
AdcIrq,
RomSbe,
RomMbe,
Ram0Sbe,
Ram0Mbe,
Ram1Sbe,
Ram1Mbe,
WdogIrq,
}
impl CascadeSource {
#[cfg(feature = "vor1x")]
pub fn id(&self) -> Result<u7, InvalidCascadeSourceId> {
let port_check = |base: u8, id: u8, len: u8| -> Result<u7, InvalidCascadeSourceId> {
if id > len - 1 {
return Err(InvalidCascadeSourceId);
}
Ok(u7::new(base + id))
};
match self {
CascadeSource::PortA(id) => port_check(0, *id, 32),
CascadeSource::PortB(id) => port_check(32, *id, 32),
CascadeSource::Tim(id) => port_check(64, *id, 24),
CascadeSource::RamSbe => Ok(u7::new(96)),
CascadeSource::RamMbe => Ok(u7::new(97)),
CascadeSource::RomSbe => Ok(u7::new(98)),
CascadeSource::RomMbe => Ok(u7::new(99)),
CascadeSource::Txev => Ok(u7::new(100)),
CascadeSource::ClockDivider(id) => port_check(120, *id, 8),
}
}
#[cfg(feature = "vor4x")]
fn id(&self) -> Result<u7, InvalidCascadeSourceId> {
let port_check = |base: u8, id: u8| -> Result<u7, InvalidCascadeSourceId> {
if id > 15 {
return Err(InvalidCascadeSourceId);
}
Ok(u7::new(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(64, *id),
CascadeSource::Tim(id) => {
if *id > 23 {
return Err(InvalidCascadeSourceId);
}
Ok(u7::new(80 + id))
}
CascadeSource::TxEv => Ok(u7::new(104)),
CascadeSource::AdcIrq => Ok(u7::new(105)),
CascadeSource::RomSbe => Ok(u7::new(106)),
CascadeSource::RomMbe => Ok(u7::new(106)),
CascadeSource::Ram0Sbe => Ok(u7::new(108)),
CascadeSource::Ram0Mbe => Ok(u7::new(109)),
CascadeSource::Ram1Sbe => Ok(u7::new(110)),
CascadeSource::Ram1Mbe => Ok(u7::new(111)),
CascadeSource::WdogIrq => Ok(u7::new(112)),
}
}
#[cfg(feature = "vor1x")]
pub fn from_raw(raw: u32) -> Result<Self, InvalidCascadeSourceId> {
let id = u7::new((raw & 0x7F) as u8);
if id.value() > 127 {
return Err(InvalidCascadeSourceId);
}
let id = id.as_u8();
if id < 32 {
return Ok(CascadeSource::PortA(id));
} else if (32..56).contains(&id) {
return Ok(CascadeSource::PortB(id - 32));
} else if (64..88).contains(&id) {
return Ok(CascadeSource::Tim(id - 64));
} else if id > 120 {
return Ok(CascadeSource::ClockDivider(id - 120));
}
match id {
96 => Ok(CascadeSource::RamSbe),
97 => Ok(CascadeSource::RamMbe),
98 => Ok(CascadeSource::RomSbe),
99 => Ok(CascadeSource::RomMbe),
100 => Ok(CascadeSource::Txev),
_ => Err(InvalidCascadeSourceId),
}
}
#[cfg(feature = "vor4x")]
pub fn from_raw(raw: u32) -> Result<Self, InvalidCascadeSourceId> {
use crate::NUM_PORT_DEFAULT;
let id = u7::new((raw & 0x7F) as u8);
if id.value() > 127 {
return Err(InvalidCascadeSourceId);
}
let id = id.as_u8();
if id < 16 {
return Ok(CascadeSource::PortA(id));
} else if (16..16 + NUM_PORT_DEFAULT as u8).contains(&id) {
return Ok(CascadeSource::PortB(id - 16));
} else if (32..32 + NUM_PORT_DEFAULT as u8).contains(&id) {
return Ok(CascadeSource::PortC(id - 32));
} else if (48..48 + NUM_PORT_DEFAULT as u8).contains(&id) {
return Ok(CascadeSource::PortD(id - 48));
} else if (64..64 + NUM_PORT_DEFAULT as u8).contains(&id) {
return Ok(CascadeSource::PortE(id - 64));
} else if (80..104).contains(&id) {
return Ok(CascadeSource::Tim(id - 80));
}
match id {
104 => Ok(CascadeSource::TxEv),
105 => Ok(CascadeSource::AdcIrq),
106 => Ok(CascadeSource::RomSbe),
107 => Ok(CascadeSource::RomMbe),
108 => Ok(CascadeSource::Ram0Sbe),
109 => Ok(CascadeSource::Ram0Mbe),
110 => Ok(CascadeSource::Ram1Sbe),
111 => Ok(CascadeSource::Ram1Mbe),
112 => Ok(CascadeSource::WdogIrq),
_ => Err(InvalidCascadeSourceId),
}
}
}
#[bitbybit::bitfield(u32)]
pub struct CascadeSourceReg {
#[bits(0..=6, rw)]
raw: u7,
}
impl CascadeSourceReg {
pub fn new(source: CascadeSource) -> Result<Self, InvalidCascadeSourceId> {
let id = source.id()?;
Ok(Self::new_with_raw_value(id.as_u32()))
}
pub fn as_cascade_source(&self) -> Result<CascadeSource, InvalidCascadeSourceId> {
CascadeSource::from_raw(self.raw().as_u32())
}
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct Timer {
control: Control,
reset_value: u32,
count_value: u32,
enable_control: EnableControl,
cascade_control: CascadeControl,
/// CASCADE0 and CASCADE1 are used to control the counting and activation of the counter.
/// CASCADE2 is used to request stopping of the timer.
cascade: [CascadeSourceReg; 3],
/// PWM A compare value.
pwma_value: u32,
/// PWM B compare value.
pwmb_value: u32,
#[cfg(feature = "vor1x")]
_reserved: [u32; 0x3f5],
#[cfg(feature = "vor4x")]
_reserved: [u32; 0xf5],
/// Vorago 1x: 0x0111_07E1. Vorago 4x: 0x0211_07E9
perid: u32,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Timer>(), 0x1000);
} else if #[cfg(feature = "vor4x")] {
static_assertions::const_assert_eq!(core::mem::size_of::<Timer>(), 0x400);
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidTimerIndex(pub usize);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimId(u8);
impl TimId {
pub const fn new(index: usize) -> Result<Self, InvalidTimerIndex> {
if index > 23 {
return Err(InvalidTimerIndex(index));
}
Ok(TimId(index as u8))
}
pub const fn new_unchecked(index: usize) -> Self {
if index > 23 {
panic!("invalid timer index");
}
TimId(index as u8)
}
/// Unsafely steal the TIM peripheral block for the TIM ID.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL.
pub const unsafe fn steal_regs(&self) -> MmioTimer<'static> {
Timer::new_mmio(*self)
}
pub const fn value(&self) -> u8 {
self.0
}
#[cfg(feature = "vor4x")]
pub const fn interrupt_id(&self) -> va416xx::Interrupt {
match self.value() {
0 => va416xx::Interrupt::TIM0,
1 => va416xx::Interrupt::TIM1,
2 => va416xx::Interrupt::TIM2,
3 => va416xx::Interrupt::TIM3,
4 => va416xx::Interrupt::TIM4,
5 => va416xx::Interrupt::TIM5,
6 => va416xx::Interrupt::TIM6,
7 => va416xx::Interrupt::TIM7,
8 => va416xx::Interrupt::TIM8,
9 => va416xx::Interrupt::TIM9,
10 => va416xx::Interrupt::TIM10,
11 => va416xx::Interrupt::TIM11,
12 => va416xx::Interrupt::TIM12,
13 => va416xx::Interrupt::TIM13,
14 => va416xx::Interrupt::TIM14,
15 => va416xx::Interrupt::TIM15,
16 => va416xx::Interrupt::TIM16,
17 => va416xx::Interrupt::TIM17,
18 => va416xx::Interrupt::TIM18,
19 => va416xx::Interrupt::TIM19,
20 => va416xx::Interrupt::TIM20,
21 => va416xx::Interrupt::TIM21,
22 => va416xx::Interrupt::TIM22,
23 => va416xx::Interrupt::TIM23,
_ => unreachable!(),
}
}
}
impl Timer {
const fn new_mmio_at(base: usize) -> MmioTimer<'static> {
MmioTimer {
ptr: base as *mut _,
phantom: PhantomData,
}
}
pub const fn new_mmio(id: TimId) -> MmioTimer<'static> {
if cfg!(feature = "vor1x") {
Timer::new_mmio_at(BASE_ADDR + 0x1000 * id.value() as usize)
} else {
Timer::new_mmio_at(BASE_ADDR + 0x400 * id.value() as usize)
}
}
pub fn new_mmio_with_raw_index(
timer_index: usize,
) -> Result<MmioTimer<'static>, InvalidTimerIndex> {
if timer_index > 23 {
return Err(InvalidTimerIndex(timer_index));
}
if cfg!(feature = "vor1x") {
Ok(Timer::new_mmio_at(BASE_ADDR + 0x1000 * timer_index))
} else {
Ok(Timer::new_mmio_at(BASE_ADDR + 0x400 * timer_index))
}
}
}

1306
src/uart/mod.rs Normal file

File diff suppressed because it is too large Load Diff

112
src/uart/pins_vor1x.rs Normal file
View File

@ -0,0 +1,112 @@
// UART A pins
use crate::{
FunSel,
pins::{
Pa2, Pa3, Pa8, Pa9, Pa16, Pa17, Pa18, Pa19, Pa26, Pa27, Pa30, Pa31, Pb6, Pb7, Pb8, Pb9,
Pb18, Pb19, Pb20, Pb21, Pb22, Pb23, Pin,
},
};
use super::{Bank, RxPin, TxPin};
impl TxPin for Pin<Pa9> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl RxPin for Pin<Pa8> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl TxPin for Pin<Pa17> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pa16> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pa31> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pa30> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pb9> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pb8> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl TxPin for Pin<Pb23> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pb22> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
// UART B pins
impl TxPin for Pin<Pa3> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl RxPin for Pin<Pa2> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl TxPin for Pin<Pa19> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pa18> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pa27> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pa26> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pb7> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pb6> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl TxPin for Pin<Pb19> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl RxPin for Pin<Pb18> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl TxPin for Pin<Pb21> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pb20> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}

98
src/uart/pins_vor4x.rs Normal file
View File

@ -0,0 +1,98 @@
#[cfg(not(feature = "va41628"))]
use crate::pins::{Pc15, Pf8};
use crate::{
FunSel,
gpio::Pin,
pins::{Pa2, Pa3, Pb14, Pb15, Pc4, Pc5, Pc14, Pd11, Pd12, Pe2, Pe3, Pf9, Pf12, Pf13, Pg0, Pg1},
};
use super::{Bank, RxPin, TxPin};
// UART 0 pins
impl TxPin for Pin<Pa2> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pa3> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pc4> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl RxPin for Pin<Pc5> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel2;
}
impl TxPin for Pin<Pe2> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pe3> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pg0> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pg1> {
const BANK: Bank = Bank::Uart0;
const FUN_SEL: FunSel = FunSel::Sel1;
}
// UART 1 pins
impl TxPin for Pin<Pb14> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pb15> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pd11> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl RxPin for Pin<Pd12> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel3;
}
impl TxPin for Pin<Pf12> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pf13> {
const BANK: Bank = Bank::Uart1;
const FUN_SEL: FunSel = FunSel::Sel1;
}
// UART 2 pins
impl TxPin for Pin<Pc14> {
const BANK: Bank = Bank::Uart2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl RxPin for Pin<Pc15> {
const BANK: Bank = Bank::Uart2;
const FUN_SEL: FunSel = FunSel::Sel2;
}
#[cfg(not(feature = "va41628"))]
impl TxPin for Pin<Pf8> {
const BANK: Bank = Bank::Uart2;
const FUN_SEL: FunSel = FunSel::Sel1;
}
impl RxPin for Pin<Pf9> {
const BANK: Bank = Bank::Uart2;
const FUN_SEL: FunSel = FunSel::Sel1;
}

322
src/uart/regs.rs Normal file
View File

@ -0,0 +1,322 @@
use core::marker::PhantomData;
use arbitrary_int::{u5, u6, u18};
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// UART A base address
pub const BASE_ADDR_0: usize = 0x4004_0000;
/// UART B base address
pub const BASE_ADDR_1: usize = 0x4004_1000;
} else if #[cfg(feature = "vor4x")] {
/// UART 0 base address
pub const BASE_ADDR_0: usize = 0x4002_4000;
/// UART 1 base address
pub const BASE_ADDR_1: usize = 0x4002_5000;
/// UART 2 base address
pub const BASE_ADDR_2: usize = 0x4001_7000;
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
Uart0 = 0,
Uart1 = 1,
#[cfg(feature = "vor4x")]
Uart2 = 2,
}
impl Bank {
/// Unsafely steal the GPIO peripheral block for the given port.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_regs(&self) -> MmioUart<'static> {
Uart::new_mmio(*self)
}
#[cfg(feature = "vor4x")]
pub const fn interrupt_id_tx(&self) -> va416xx::Interrupt {
match self {
Bank::Uart0 => va416xx::Interrupt::UART0_TX,
Bank::Uart1 => va416xx::Interrupt::UART1_TX,
Bank::Uart2 => va416xx::Interrupt::UART2_TX,
}
}
#[cfg(feature = "vor4x")]
pub const fn interrupt_id_rx(&self) -> va416xx::Interrupt {
match self {
Bank::Uart0 => va416xx::Interrupt::UART0_RX,
Bank::Uart1 => va416xx::Interrupt::UART1_RX,
Bank::Uart2 => va416xx::Interrupt::UART2_RX,
}
}
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Data {
#[bit(15, rw)]
dparity: bool,
#[bits(0..=7, rw)]
value: u8,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Enable {
#[bit(1, rw)]
tx: bool,
#[bit(0, rw)]
rx: bool,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Stopbits {
One = 0,
Two = 1,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
Five = 0b00,
Six = 0b01,
Seven = 0b10,
Eight = 0b11,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Control {
#[bit(11, rw)]
baud8: bool,
#[bit(10, rw)]
auto_rts: bool,
#[bit(9, rw)]
def_rts: bool,
#[bit(8, rw)]
auto_cts: bool,
#[bit(7, rw)]
loopback_block: bool,
#[bit(6, rw)]
loopback: bool,
#[bits(4..=5, rw)]
wordsize: WordSize,
#[bit(3, rw)]
stopbits: Stopbits,
#[bit(2, rw)]
parity_manual: bool,
#[bit(1, rw)]
parity_even: bool,
#[bit(0, rw)]
parity_enable: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct ClkScale {
#[bits(6..=23, rw)]
int: u18,
#[bits(0..=5, rw)]
frac: u6,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct RxStatus {
#[bit(15, r)]
rx_rtsn: bool,
#[bit(9, r)]
rx_addr9: bool,
#[bit(8, r)]
busy_break: bool,
#[bit(7, r)]
break_error: bool,
#[bit(6, r)]
parity_error: bool,
#[bit(5, r)]
framing_error: bool,
#[bit(4, r)]
overrun_error: bool,
#[bit(3, r)]
timeout: bool,
#[bit(2, r)]
busy: bool,
#[bit(1, r)]
not_full: bool,
#[bit(0, r)]
data_available: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct TxStatus {
#[bit(15, r)]
tx_ctsn: bool,
#[bit(3, r)]
wr_lost: bool,
#[bit(2, r)]
tx_busy: bool,
#[bit(1, r)]
write_busy: bool,
/// There is space in the FIFO to write data.
#[bit(0, r)]
ready: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct FifoClear {
#[bit(1, w)]
tx: bool,
#[bit(0, w)]
rx: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptControl {
/// Generate an interrrupt when the RX FIFO is at least half-full (FIFO count >= trigger level)
#[bit(0, rw)]
rx: bool,
/// Interrupts for status conditions (overrun, framing, parity and break)
#[bit(1, rw)]
rx_status: bool,
/// Interrupt on timeout conditions.
#[bit(2, rw)]
rx_timeout: bool,
/// Generates an interrupt when the TX FIFO is at least half-empty (FIFO count < trigger level)
#[bit(4, rw)]
tx: bool,
/// Generates an interrupt on TX FIFO overflow.
#[bit(5, rw)]
tx_status: bool,
/// Generates an interrupt when the transmit FIFO is empty and TXBUSY is 0.
#[bit(6, rw)]
tx_empty: bool,
#[bit(7, rw)]
tx_cts: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptStatus {
/// Generate an interrrupt when the RX FIFO is at least half-full (FIFO count >= trigger level)
#[bit(0, r)]
rx: bool,
/// Interrupts for status conditions (overrun, framing, parity and break)
#[bit(1, r)]
rx_status: bool,
/// Interrupt on timeout conditions.
#[bit(2, r)]
rx_timeout: bool,
/// Generates an interrupt when the TX FIFO is at least half-empty (FIFO count < trigger level)
#[bit(4, r)]
tx: bool,
/// Generates an interrupt on TX FIFO overflow.
#[bit(5, r)]
tx_status: bool,
/// Generates an interrupt when the transmit FIFO is empty and TXBUSY is 0.
#[bit(6, r)]
tx_empty: bool,
#[bit(7, r)]
tx_cts: bool,
}
/// As specified in the VA416x0 Programmers Guide, only the RX overflow bit can be cleared.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptClear {
#[bit(1, w)]
rx_overrun: bool,
/// Not sure if this does anything, the programmer guides are not consistent on this..
#[bit(5, w)]
tx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FifoTrigger {
#[bits(0..=4, rw)]
level: u5,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct State {
#[bits(0..=7, r)]
rx_state: u8,
/// Data count.
#[bits(8..=12, r)]
rx_fifo: u5,
#[bits(16..=23, r)]
tx_state: u8,
/// Data count.
#[bits(24..=28, r)]
tx_fifo: u5,
}
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
#[repr(C)]
pub struct Uart {
data: Data,
enable: Enable,
ctrl: Control,
clkscale: ClkScale,
#[mmio(PureRead)]
rx_status: RxStatus,
#[mmio(PureRead)]
tx_status: TxStatus,
#[mmio(Write)]
fifo_clr: FifoClear,
#[mmio(Write)]
txbreak: u32,
addr9: u32,
addr9mask: u32,
irq_enabled: InterruptControl,
#[mmio(PureRead)]
irq_raw: InterruptStatus,
#[mmio(PureRead)]
irq_status: InterruptStatus,
#[mmio(Write)]
irq_clr: InterruptClear,
rx_fifo_trigger: FifoTrigger,
tx_fifo_trigger: FifoTrigger,
rx_fifo_rts_trigger: u32,
#[mmio(PureRead)]
state: State,
_reserved: [u32; 0x3ED],
/// Vorago 1x value: 0x0112_07E1. Vorago 4x value: 0x0212_07E9
#[mmio(PureRead)]
perid: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Uart>(), 0x1000);
impl Uart {
fn new_mmio_at(base: usize) -> MmioUart<'static> {
MmioUart {
ptr: base as *mut _,
phantom: PhantomData,
}
}
pub fn new_mmio(bank: Bank) -> MmioUart<'static> {
match bank {
Bank::Uart0 => Self::new_mmio_at(BASE_ADDR_0),
Bank::Uart1 => Self::new_mmio_at(BASE_ADDR_1),
#[cfg(feature = "vor4x")]
Bank::Uart2 => Self::new_mmio_at(BASE_ADDR_2),
}
}
}

443
src/uart/rx_asynch.rs Normal file
View File

@ -0,0 +1,443 @@
//! # Async UART reception functionality.
//!
//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the
//! [embedded_io_async::Read] trait.
//! This trait allows for asynchronous reception of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides two interrupt handlers:
//!
//! - [on_interrupt_rx]
//! - [on_interrupt_rx_overwriting]
//!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer.
//!
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
//! structure returned by the interrupt handlers.
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
use arbitrary_int::Number;
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io::ErrorType;
use portable_atomic::AtomicBool;
use super::{
Bank, Rx, UartErrors,
regs::{InterruptClear, MmioUart},
};
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
struct RxFuture {
id: Bank,
}
impl RxFuture {
pub fn new(rx: &mut Rx) -> Self {
RX_READ_ACTIVE[rx.id as usize].store(true, Ordering::Relaxed);
Self { id: rx.id }
}
}
impl Future for RxFuture {
type Output = Result<(), Infallible>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_RX_WAKERS[self.id as usize].register(cx.waker());
if RX_HAS_DATA[self.id as usize].load(Ordering::Relaxed) {
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AsyncUartErrors {
/// Queue has overflowed, data might have been lost.
pub queue_overflow: bool,
/// UART errors.
pub uart_errors: UartErrors,
}
fn on_interrupt_handle_rx_errors(uart: &mut MmioUart<'static>) -> Option<UartErrors> {
let rx_status = uart.read_rx_status();
if rx_status.overrun_error() || rx_status.framing_error() || rx_status.parity_error() {
let mut errors_val = UartErrors::default();
if rx_status.overrun_error() {
errors_val.overflow = true;
}
if rx_status.framing_error() {
errors_val.framing = true;
}
if rx_status.parity_error() {
errors_val.parity = true;
}
return Some(errors_val);
}
None
}
fn on_interrupt_rx_common_post_processing(
id: Bank,
rx_enabled: bool,
read_some_data: bool,
) -> Option<UartErrors> {
let idx = id as usize;
if read_some_data {
RX_HAS_DATA[idx].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) {
UART_RX_WAKERS[idx].wake();
}
}
let mut errors = None;
let mut uart_regs = unsafe { id.steal_regs() };
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(&mut uart_regs);
}
// Clear the interrupt status bits
uart_regs.write_irq_clr(
InterruptClear::builder()
.with_rx_overrun(true)
.with_tx_overrun(false)
.build(),
);
errors
}
/// Interrupt handler with overwriting behaviour when the ring buffer is full.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_rx_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let uart_regs = unsafe { bank.steal_regs() };
let irq_status = uart_regs.read_irq_status();
let irq_enabled = uart_regs.read_irq_enabled();
let rx_enabled = irq_enabled.rx();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_status.rx() {
let available_bytes = uart_regs.read_rx_fifo_trigger().level().as_usize();
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart_regs.read_data().value();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_status.rx_timeout() {
while uart_regs.read_rx_status().data_available() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart_regs.read_data().value();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte).ok();
}
read_some_data = true;
}
let uart_errors = on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data);
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
/// Interrupt handler for asynchronous RX operations.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_rx<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(bank, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<const N: usize>(
bank: Bank,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
let uart_regs = unsafe { bank.steal_regs() };
let irq_status = uart_regs.read_irq_status();
let irq_enabled = uart_regs.read_irq_enabled();
let rx_enabled = irq_enabled.rx();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_status.rx() {
let available_bytes = uart_regs.read_rx_fifo_trigger().level().as_usize();
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart_regs.read_data().value();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_status.rx_timeout() {
while uart_regs.read_rx_status().data_available() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart_regs.read_data().value();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte).ok();
}
read_some_data = true;
}
let uart_errors = on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data);
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
struct ActiveReadGuard(usize);
impl Drop for ActiveReadGuard {
fn drop(&mut self) {
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
}
}
struct RxAsyncInner<const N: usize> {
rx: Rx,
pub queue: heapless::spsc::Consumer<'static, u8, N>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, data will be lost.
pub struct RxAsync<const N: usize>(Option<RxAsyncInner<N>>);
impl<const N: usize> ErrorType for RxAsync<N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
fn stop_async_rx(rx: &mut Rx) {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
}
impl<const N: usize> RxAsync<N> {
/// Create a new asynchronous receiver.
///
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
/// is filled by the interrupt handler [on_interrupt_rx].
pub fn new(mut rx: Rx, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
#[cfg(feature = "vor1x")]
rx.enable_interrupts(true);
#[cfg(feature = "vor4x")]
rx.enable_interrupts(true, true);
rx.enable();
});
Self(Some(RxAsyncInner { rx, queue }))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> (Rx, heapless::spsc::Consumer<'static, u8, N>) {
self.stop();
let inner = self.0.take().unwrap();
(inner.rx, inner.queue)
}
}
impl<const N: usize> Drop for RxAsync<N> {
fn drop(&mut self) {
self.stop();
}
}
impl<const N: usize> embedded_io_async::Read for RxAsync<N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let inner = self.0.as_ref().unwrap();
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
if inner.queue.len() == 0 {
RX_HAS_DATA[inner.rx.id as usize].store(false, Ordering::Relaxed);
}
let _guard = ActiveReadGuard(inner.rx.id as usize);
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
};
let mut_ref = self.0.as_mut().unwrap();
let fut = RxFuture::new(&mut mut_ref.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut mut_ref.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut mut_ref.queue))
}
}
struct RxAsyncOverwritingInner<const N: usize> {
rx: Rx,
pub shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_rx_overwriting] interrupt handlers.
pub struct RxAsyncOverwriting<const N: usize>(Option<RxAsyncOverwritingInner<N>>);
impl<const N: usize> ErrorType for RxAsyncOverwriting<N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<const N: usize> RxAsyncOverwriting<N> {
/// Create a new asynchronous receiver.
///
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
/// which is filled by the interrupt handler. The shared property allows using it in the
/// interrupt handler to overwrite old data.
pub fn new(
mut rx: Rx,
shared_consumer: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
#[cfg(feature = "vor4x")]
rx.enable_interrupts(true, true);
#[cfg(feature = "vor1x")]
rx.enable_interrupts(true);
rx.enable();
});
Self(Some(RxAsyncOverwritingInner {
rx,
shared_consumer,
}))
}
pub fn stop(&mut self) {
stop_async_rx(&mut self.0.as_mut().unwrap().rx);
}
pub fn release(mut self) -> Rx {
self.stop();
let inner = self.0.take().unwrap();
inner.rx
}
}
impl<const N: usize> Drop for RxAsyncOverwriting<N> {
fn drop(&mut self) {
self.stop();
}
}
impl<const N: usize> embedded_io_async::Read for RxAsyncOverwriting<N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let inner = self.0.as_ref().unwrap();
let id = inner.rx.id as usize;
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
critical_section::with(|cs| {
let queue = inner.shared_consumer.borrow(cs);
if queue.borrow().as_ref().unwrap().len() == 0 {
RX_HAS_DATA[id].store(false, Ordering::Relaxed);
}
});
let _guard = ActiveReadGuard(id);
let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner<N>| {
critical_section::with(|cs| {
let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut();
let consumer = consumer_ref.as_mut().unwrap();
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
})
};
let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue(self.0.as_mut().unwrap());
Ok(read_data)
}
}

208
src/uart/tx_asynch.rs Normal file
View File

@ -0,0 +1,208 @@
//! # Async UART transmission functionality.
//!
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
//! This trait allows for asynchronous sending of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it the [on_interrupt_tx] interrupt handler.
//!
//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts
//! for a given UART bank.
use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io_async::Write;
use portable_atomic::AtomicBool;
use raw_slice::RawBufSlice;
use super::*;
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
/// UART bank.
///
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
/// the given UART bank.
pub fn on_interrupt_tx(bank: Bank) {
let mut uart = unsafe { bank.steal_regs() };
let idx = bank as usize;
let irq_enabled = uart.read_irq_enabled();
// IRQ is not related to TX.
if !irq_enabled.tx() && !irq_enabled.tx_empty() {
return;
}
let tx_status = uart.read_tx_status();
let unexpected_overrun = tx_status.wr_lost();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
// Safety: We documented that the user provided slice must outlive the future, so we convert
// the raw pointer back to the slice here.
let slice = unsafe { context.slice.get().unwrap() };
if context.progress >= slice.len() && !tx_status.tx_busy() {
uart.modify_irq_enabled(|mut value| {
value.set_tx(false);
value.set_tx_empty(false);
value.set_tx_status(false);
value
});
uart.modify_enable(|mut value| {
value.set_tx(false);
value
});
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[idx].wake();
return;
}
while context.progress < slice.len() {
if !uart.read_tx_status().ready() {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
// register, so we can assume we are the only one writing to the data register.
uart.write_data(Data::new_with_raw_value(slice[context.progress] as u32));
context.progress += 1;
}
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
}
#[derive(Debug, Copy, Clone)]
pub struct TxContext {
progress: usize,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
tx_overrun: false,
slice: RawBufSlice::new_nulled(),
}
}
}
pub struct TxFuture {
id: Bank,
}
impl TxFuture {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new(tx: &mut Tx, data: &[u8]) -> Self {
TX_DONE[tx.id as usize].store(false, core::sync::atomic::Ordering::Relaxed);
tx.disable_interrupts();
tx.disable();
tx.clear_fifo();
let init_fill_count = core::cmp::min(data.len(), 16);
// We fill the FIFO.
for data in data.iter().take(init_fill_count) {
tx.regs.write_data(Data::new_with_raw_value(*data as u32));
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[tx.id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
unsafe { context.slice.set(data) };
context.progress = init_fill_count;
// Ensure those are enabled inside a critical section at the same time. Can lead to
// weird glitches otherwise.
tx.enable_interrupts(
#[cfg(feature = "vor4x")]
true,
);
tx.enable();
});
Self { id: tx.id }
}
}
impl Future for TxFuture {
type Output = Result<usize, TxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_TX_WAKERS[self.id as usize].register(cx.waker());
if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
let progress = critical_section::with(|cs| {
TX_CONTEXTS[self.id as usize].borrow(cs).borrow().progress
});
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
impl Drop for TxFuture {
fn drop(&mut self) {
let mut reg_block = unsafe { self.id.steal_regs() };
disable_tx_interrupts(&mut reg_block);
disable_tx(&mut reg_block);
}
}
pub struct TxAsync(Tx);
impl TxAsync {
pub fn new(tx: Tx) -> Self {
Self(tx)
}
pub fn release(self) -> Tx {
self.0
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl embedded_io::ErrorType for TxAsync {
type Error = TxOverrunError;
}
impl Write for TxAsync {
/// Write a buffer asynchronously.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.0, buf) };
fut.await
}
}