moved shared crate into separate crate

This commit is contained in:
Robin Müller 2025-04-22 13:50:21 +02:00
parent afa2849fa7
commit f31497b024
Signed by: muellerr
GPG Key ID: A649FB78196E3849
27 changed files with 1 additions and 8041 deletions

View File

@ -11,7 +11,6 @@ members = [
"board-tests",
"bootloader",
"flashloader",
"vorago-shared-periphs",
]
exclude = [
"flashloader/slot-a-blinky",

View File

@ -15,7 +15,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
cortex-m-rt = "0.7"
nb = "1"
paste = "1"
vorago-shared-periphs = { path = "../vorago-shared-periphs", features = ["vor1x"] }
vorago-shared-periphs = { path = "../../vorago-shared-periphs", features = ["vor1x"] }
embedded-hal = "1"
embedded-hal-async = "1"
embedded-hal-nb = "1"

View File

@ -1,40 +0,0 @@
[package]
name = "vorago-shared-periphs"
version = "0.1.0"
description = "Peripheral drivers shared between Vorago families"
edition = "2024"
[dependencies]
cortex-m = { version = "0.7" }
cfg-if = "1"
derive-mmio = { path = "../../../ROMEO/derive-mmio" }
bitbybit = "1.3"
arbitrary-int = "1.3"
static_assertions = "1.1"
nb = "1"
heapless = "0.8"
critical-section = "1"
embedded-hal = { version = "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"
embassy-sync = "0.6"
defmt = { version = "1", optional = true }
va108xx = { version = "0.5", default-features = false, optional = true }
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features]
vor1x = ["_family-selected", "dep:va108xx"]
vor4x = ["_family-selected"]
defmt = ["dep:defmt", "arbitrary-int/defmt"]
_family-selected = []

View File

@ -1,231 +0,0 @@
//! # 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;
use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
pub use super::ll::InterruptEdge;
use super::{
Input, Port,
ll::{LowLevelGpio, PinId},
};
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];
/// 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 tocomplete
/// complete async operations.
pub fn on_interrupt_for_async_gpio_for_port(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) = 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()),
};
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 {
#[inline]
pub fn pin_group_to_waker_and_edge_detection_group(
group: Port,
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
match group {
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()),
}
}
pub fn new_with_input_pin(
pin: &mut Input,
irq: va108xx::Interrupt,
edge: InterruptEdge,
) -> Self {
let (waker_group, edge_detection_group) =
Self::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,
}
}
}
impl Drop for InputPinFuture {
fn drop(&mut self) {
let mut ll = LowLevelGpio::new(self.id);
ll.disable_interrupt(false);
}
}
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 }
}
/// 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.
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
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.
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
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.
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).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.
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.
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).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(())
}
}

View File

@ -1,424 +0,0 @@
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,
}
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
}
}
/// 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 = "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
});
}
/// 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()
}
}

View File

@ -1,374 +0,0 @@
//! GPIO support module.
use core::convert::Infallible;
pub use crate::ioconfig::{FilterClkSel, FilterType, regs::FunSel};
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();
}
}
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);
}
#[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)]
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
}
}

View File

@ -1,126 +0,0 @@
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"),
}
}
}

View File

@ -1,665 +0,0 @@
pub mod regs;
use crate::{
enable_peripheral_clock, sealed::Sealed, sysconfig::reset_peripheral_for_cycles, time::Hertz,
PeripheralSelect,
};
use arbitrary_int::{u10, u11, u20, u4};
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;
//==================================================================================================
// 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,
}
}
}
pub type I2cRegBlock = pac::i2ca::RegisterBlock;
/// 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;
}
impl I2cMarker for pac::I2ca {
const ID: Bank = Bank::I2c0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
}
impl Sealed for pac::I2ca {}
impl I2cMarker for pac::I2cb {
const ID: Bank = Bank::I2c1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
}
impl Sealed for pac::I2cb {}
//==================================================================================================
// Config
//==================================================================================================
fn calc_clk_div(sys_clk: Hertz, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
if speed_mode == I2cSpeed::Regular100khz {
Ok(((sys_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else {
if sys_clk.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2cError);
}
Ok(((sys_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
}
}
#[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>(
sysclk: Hertz,
_i2c: I2c,
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);
let clk_div = calc_clk_div(sysclk, 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 fn id(&self) -> Bank {
self.id
}
/// Configures the clock scale for a given speed mode setting
pub fn set_clk_scale(
&mut self,
sys_clk: Hertz,
speed_mode: I2cSpeed,
) -> Result<(), ClockTooSlowForFastI2cError> {
self.disable();
let clk_div = calc_clk_div(sys_clk, 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)
}
}

View File

@ -1,671 +0,0 @@
use core::marker::PhantomData;
use arbitrary_int::{u10, u11, u20, u4, u5, u9};
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::{u10, u11, u3, u4, u5};
#[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::Spi3 => Self::new_mmio_at(BASE_ADDR_2),
}
}
}

View File

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

View File

@ -1,177 +0,0 @@
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) },
}
}
}

View File

@ -1,182 +0,0 @@
#![no_std]
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::{disable_peripheral_clock, enable_peripheral_clock};
#[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")]
#[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_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.
#[cfg(feature = "vor1x")]
#[inline]
pub unsafe fn enable_nvic_interrupt(irq: va108xx::Interrupt) {
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
}
}
/// Disable a specific interrupt using the NVIC peripheral.
#[cfg(feature = "vor1x")]
#[inline]
pub fn disable_nvic_interrupt(irq: va108xx::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,
}
}

View File

@ -1,236 +0,0 @@
use crate::sysconfig::reset_peripheral_for_cycles;
pub use crate::gpio::{Pin, PinId, PinIdProvider, Port};
use crate::PeripheralSelect;
use crate::sealed::Sealed;
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);
pin_id!(Pa16, Port::A, 16);
pin_id!(Pa17, Port::A, 17);
pin_id!(Pa18, Port::A, 18);
pin_id!(Pa19, Port::A, 19);
pin_id!(Pa20, Port::A, 20);
pin_id!(Pa21, Port::A, 21);
pin_id!(Pa22, Port::A, 22);
pin_id!(Pa23, Port::A, 23);
pin_id!(Pa24, Port::A, 24);
pin_id!(Pa25, Port::A, 25);
pin_id!(Pa26, Port::A, 26);
pin_id!(Pa27, Port::A, 27);
pin_id!(Pa28, Port::A, 28);
pin_id!(Pa29, Port::A, 29);
pin_id!(Pa30, Port::A, 30);
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);
pin_id!(Pb5, Port::B, 5);
pin_id!(Pb6, Port::B, 6);
pin_id!(Pb7, Port::B, 7);
pin_id!(Pb8, Port::B, 8);
pin_id!(Pb9, Port::B, 9);
pin_id!(Pb10, Port::B, 10);
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);
pin_id!(Pb16, Port::B, 16);
pin_id!(Pb17, Port::B, 17);
pin_id!(Pb18, Port::B, 18);
pin_id!(Pb19, Port::B, 19);
pin_id!(Pb20, Port::B, 20);
pin_id!(Pb21, Port::B, 21);
pin_id!(Pb22, Port::B, 22);
pin_id!(Pb23, Port::B, 23);
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>,
pub pa16: Pin<Pa16>,
pub pa17: Pin<Pa17>,
pub pa18: Pin<Pa18>,
pub pa19: Pin<Pa19>,
pub pa20: Pin<Pa20>,
pub pa21: Pin<Pa21>,
pub pa22: Pin<Pa22>,
pub pa23: Pin<Pa23>,
pub pa24: Pin<Pa24>,
pub pa25: Pin<Pa25>,
pub pa26: Pin<Pa26>,
pub pa27: Pin<Pa27>,
pub pa28: Pin<Pa28>,
pub pa29: Pin<Pa29>,
pub pa30: Pin<Pa30>,
pub pa31: Pin<Pa31>,
}
impl PinsA {
pub fn new(_port_a: va108xx::Porta) -> Self {
let syscfg = unsafe { va108xx::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortA, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.porta().set_bit();
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(),
pa16: Pin::__new(),
pa17: Pin::__new(),
pa18: Pin::__new(),
pa19: Pin::__new(),
pa20: Pin::__new(),
pa21: Pin::__new(),
pa22: Pin::__new(),
pa23: Pin::__new(),
pa24: Pin::__new(),
pa25: Pin::__new(),
pa26: Pin::__new(),
pa27: Pin::__new(),
pa28: Pin::__new(),
pa29: Pin::__new(),
pa30: Pin::__new(),
pa31: Pin::__new(),
}
}
}
pub struct PinsB {
pub pb0: Pin<Pb0>,
pub pb1: Pin<Pb1>,
pub pb2: Pin<Pb2>,
pub pb3: Pin<Pb3>,
pub pb4: Pin<Pb4>,
pub pb5: Pin<Pb5>,
pub pb6: Pin<Pb6>,
pub pb7: Pin<Pb7>,
pub pb8: Pin<Pb8>,
pub pb9: Pin<Pb9>,
pub pb10: Pin<Pb10>,
pub pb11: Pin<Pb11>,
pub pb12: Pin<Pb12>,
pub pb13: Pin<Pb13>,
pub pb14: Pin<Pb14>,
pub pb15: Pin<Pb15>,
pub pb16: Pin<Pb16>,
pub pb17: Pin<Pb17>,
pub pb18: Pin<Pb18>,
pub pb19: Pin<Pb19>,
pub pb20: Pin<Pb20>,
pub pb21: Pin<Pb21>,
pub pb22: Pin<Pb22>,
pub pb23: Pin<Pb23>,
}
impl PinsB {
pub fn new(_port_b: va108xx::Portb) -> Self {
let syscfg = unsafe { va108xx::Sysconfig::steal() };
reset_peripheral_for_cycles(PeripheralSelect::PortB, 2);
syscfg.peripheral_clk_enable().modify(|_, w| {
w.portb().set_bit();
w.gpio().set_bit();
w.ioconfig().set_bit()
});
Self {
pb0: Pin::__new(),
pb1: Pin::__new(),
pb2: Pin::__new(),
pb3: Pin::__new(),
pb4: Pin::__new(),
pb5: Pin::__new(),
pb6: Pin::__new(),
pb7: Pin::__new(),
pb8: Pin::__new(),
pb9: Pin::__new(),
pb10: Pin::__new(),
pb11: Pin::__new(),
pb12: Pin::__new(),
pb13: Pin::__new(),
pb14: Pin::__new(),
pb15: Pin::__new(),
pb16: Pin::__new(),
pb17: Pin::__new(),
pb18: Pin::__new(),
pb19: Pin::__new(),
pb20: Pin::__new(),
pb21: Pin::__new(),
pb22: Pin::__new(),
pb23: Pin::__new(),
}
}
}

View File

@ -1,246 +0,0 @@
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>,
sys_clk: Hertz,
/// For PWMB, this is the upper limit
current_duty: u16,
/// For PWMA, this value will not be used
current_lower_limit: u16,
current_period: Hertz,
current_rst_val: u32,
mode: PhantomData<Mode>,
}
impl<Mode> PwmPin<Mode> {
/// Create a new strongly typed PWM pin
pub fn new<Pin: TimPin, Tim: TimMarker>(
sys_clk: Hertz,
_pin_and_tim: (Pin, Tim),
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,
sys_clk,
mode: PhantomData,
};
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.sys_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,
sys_clk: other.sys_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,
sys_clk: other.sys_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
}
}

View File

@ -1,868 +0,0 @@
use crate::gpio::{IoPeriphPin, PinId};
use crate::FunSel;
use crate::{
enable_peripheral_clock, pins::PinMarker, sealed::Sealed, time::Hertz, PeripheralSelect,
};
use core::{convert::Infallible, fmt::Debug, marker::PhantomData};
use embedded_hal::spi::{Mode, MODE_0};
use regs::{ClkPrescaler, Data, FifoClear, WordSize};
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor1x")]
pub mod pins_vor1x;
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
}
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;
}
//==================================================================================================
// 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;
}
impl SpiMarker for pac::Spia {
const ID: Bank = Bank::Spi0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
}
impl Sealed for pac::Spia {}
impl SpiMarker for pac::Spib {
const ID: Bank = Bank::Spi1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
}
impl Sealed for pac::Spib {}
impl SpiMarker for pac::Spic {
const ID: Bank = Bank::Spi2;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
}
impl Sealed for pac::Spic {}
//==================================================================================================
// 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)
}
pub fn from_clk(sys_clk: impl Into<Hertz>, spi_clk: impl Into<Hertz>) -> Option<Self> {
clk_div_for_target_clock(sys_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: impl Into<Hertz>,
spi_clk: impl Into<Hertz>,
) -> Option<u16> {
let spi_clk = spi_clk.into();
let sys_clk = sys_clk.into();
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,
sys_clk: Hertz,
/// 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.
///
/// ## Arguments
/// * `sys_clk` - System clock
/// * `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_for_rom<SpiI: SpiMarker>(
sys_clk: Hertz,
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(sys_clk, spi, spi_cfg))
}
/// Create a new SPI struct.
///
/// ## Arguments
/// * `sys_clk` - System clock
/// * `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>(
sys_clk: Hertz,
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(sys_clk, spi, spi_cfg))
}
pub fn new_generic<SpiI: SpiMarker>(sys_clk: Hertz, _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,
sys_clk,
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,
sys_clk: old_spi.sys_clk,
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,
sys_clk: old_spi.sys_clk,
word: PhantomData,
}
}
}

View File

@ -1,355 +0,0 @@
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,
},
sealed::Sealed,
};
use super::{Bank, HwChipSelectId};
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;
}
)+
};
}
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 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;
}
};
}
// 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
);

View File

@ -1,279 +0,0 @@
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),
}
}
}

View File

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

View File

@ -1,26 +0,0 @@
//! 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;

View File

@ -1,466 +0,0 @@
pub mod regs;
use core::convert::Infallible;
#[cfg(feature = "vor1x")]
pub use crate::InterruptConfig;
pub use regs::{CascadeSource, InvalidTimerIndex, TimId};
use crate::{
PeripheralSelect,
gpio::{Pin, PinId, PinIdProvider},
ioconfig::regs::FunSel,
pins::PinMarker,
sysconfig::enable_peripheral_clock,
};
use crate::{
enable_nvic_interrupt,
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,
},
sealed::Sealed,
time::Hertz,
};
use fugit::RateExtU32;
#[cfg(feature = "vor1x")]
use va108xx as pac;
#[cfg(feature = "vor4x")]
use va416xx as pac;
//==================================================================================================
// 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;
}
macro_rules! tim_marker {
($TIMX:path, $ID:expr) => {
impl TimMarker for $TIMX {
const ID: TimId = TimId::new_unchecked($ID);
}
impl Sealed for $TIMX {}
};
}
tim_marker!(pac::Tim0, 0);
tim_marker!(pac::Tim1, 1);
tim_marker!(pac::Tim2, 2);
tim_marker!(pac::Tim3, 3);
tim_marker!(pac::Tim4, 4);
tim_marker!(pac::Tim5, 5);
tim_marker!(pac::Tim6, 6);
tim_marker!(pac::Tim7, 7);
tim_marker!(pac::Tim8, 8);
tim_marker!(pac::Tim9, 9);
tim_marker!(pac::Tim10, 10);
tim_marker!(pac::Tim11, 11);
tim_marker!(pac::Tim12, 12);
tim_marker!(pac::Tim13, 13);
tim_marker!(pac::Tim14, 14);
tim_marker!(pac::Tim15, 15);
tim_marker!(pac::Tim16, 16);
tim_marker!(pac::Tim17, 17);
tim_marker!(pac::Tim18, 18);
tim_marker!(pac::Tim19, 19);
tim_marker!(pac::Tim20, 20);
tim_marker!(pac::Tim21, 21);
tim_marker!(pac::Tim22, 22);
tim_marker!(pac::Tim23, 23);
pub trait ValidTimAndPin<Pin: TimPin, Tim: TimMarker>: Sealed {}
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);
}
};
}
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);
//==================================================================================================
// Timers
//==================================================================================================
/// Hardware timers
pub struct CountdownTimer {
id: TimId,
regs: regs::MmioTimer<'static>,
curr_freq: Hertz,
sys_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.
pub fn new<Tim: TimMarker>(sys_clk: Hertz, _tim: Tim) -> 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),
sys_clk,
rst_val: 0,
curr_freq: 0.Hz(),
last_cnt: 0,
}
}
#[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());
}
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
});
}
/// 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.sys_clk.raw() / self.curr_freq.raw();
self.set_reload(self.rst_val);
self.set_count(self.rst_val);
}
#[inline(always)]
pub fn set_reload(&mut self, val: u32) {
self.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 { va108xx::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.sys_clk.raw())) / 1_000_000_000;
let full_cycles = ticks >> 32;
let mut last_count;
let mut new_count;
if full_cycles > 0 {
self.set_reload(u32::MAX);
self.set_count(u32::MAX);
self.enable();
for _ in 0..full_cycles {
// Always ensure that both values are the same at the start.
new_count = self.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]
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]
fn deassert_tim_reset(tim: TimId) {
unsafe { pac::Peripherals::steal() }
.sysconfig
.tim_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim.value())) });
}
fn assert_tim_reset_for_cycles(tim: TimId, cycles: u32) {
assert_tim_reset(tim);
cortex_m::asm::delay(cycles);
deassert_tim_reset(tim);
}

View File

@ -1,309 +0,0 @@
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;
/// The numbers are the base numbers for bundles like PORTA, PORTB or TIM
#[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),
}
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 = "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),
}
}
}
#[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
}
}
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))
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,112 +0,0 @@
// 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;
}

View File

@ -1,304 +0,0 @@
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)
}
}
#[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),
}
}
}

View File

@ -1,437 +0,0 @@
//! # Async UART reception functionality for the VA416xx family.
//!
//! 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(|_| {
rx.enable_interrupts(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(|_| {
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)
}
}

View File

@ -1,205 +0,0 @@
//! # Async UART transmission functionality for the VA108xx family.
//!
//! 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();
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
}
}