big boy update

This commit is contained in:
2025-04-14 20:17:55 +02:00
parent 23a5639ae1
commit 2835231f2d
30 changed files with 2435 additions and 1309 deletions

View File

@ -0,0 +1,185 @@
pub use embedded_hal::digital::PinState;
pub use crate::InvalidOffsetError;
pub use crate::Port;
pub use crate::ioconfig::regs::Pull;
use crate::ioconfig::regs::{FunSel, IoConfig, MmioIoConfig};
pub struct LowLevelGpio {
gpio: super::regs::MmioGpio<'static>,
ioconfig: MmioIoConfig<'static>,
port: Port,
offset: usize,
}
impl LowLevelGpio {
pub fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
if offset >= port.max_offset() {
return Err(InvalidOffsetError {
offset,
port: Port::A,
});
}
Ok(LowLevelGpio {
gpio: super::regs::Gpio::new_mmio(port),
ioconfig: IoConfig::new_mmio(),
port,
offset,
})
}
#[inline]
pub fn port(&self) -> Port {
self.port
}
#[inline]
pub fn offset(&self) -> usize {
self.offset
}
pub fn configure_as_input_floating(&mut self) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |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.offset);
dir
});
}
pub fn configure_as_input_with_pull(&mut self, pull: Pull) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |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.offset);
dir
});
}
pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |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(1 << self.offset),
PinState::High => self.gpio.write_set_out(1 << self.offset),
}
self.gpio.modify_dir(|mut dir| {
dir |= 1 << self.offset;
dir
});
}
pub fn configure_as_output_open_drain(&mut self, init_level: PinState) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |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
});
}
match init_level {
PinState::Low => self.gpio.write_clr_out(1 << self.offset),
PinState::High => self.gpio.write_set_out(1 << self.offset),
}
self.gpio.modify_dir(|mut dir| {
dir |= 1 << self.offset;
dir
});
}
pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunSel, pull: Option<Pull>) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |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(1 << self.offset);
}
#[inline]
pub fn set_low(&mut self) {
self.gpio.write_clr_out(1 << self.offset);
}
#[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(1 << self.offset);
}
}

View File

@ -0,0 +1,290 @@
use core::convert::Infallible;
pub use embedded_hal::digital::PinState;
pub use ll::{Port, Pull};
use crate::ioconfig::regs::FunSel;
pub mod ll;
pub mod regs;
pub trait PinId {
const PORT: Port;
const OFFSET: usize;
}
pub struct Pin<I: PinId> {
phantom: core::marker::PhantomData<I>,
}
impl<I: PinId> Pin<I> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
phantom: core::marker::PhantomData,
}
}
}
pub struct Output(ll::LowLevelGpio);
impl Output {
pub fn new<I: PinId>(_pin: Pin<I>, init_level: PinState) -> Self {
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
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()
}
}
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(())
}
}
pub struct Input(ll::LowLevelGpio);
impl Input {
pub fn new_floating<I: PinId>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
ll.configure_as_input_floating();
Input(ll)
}
pub fn new_with_pull<I: PinId>(_pin: Pin<I>, pull: Pull) -> Self {
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
ll.configure_as_input_with_pull(pull);
Input(ll)
}
#[inline]
pub fn port(&self) -> Port {
self.0.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.0.offset()
}
}
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())
}
}
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()
}
}
pub struct Flex {
ll: ll::LowLevelGpio,
mode: PinMode,
}
impl Flex {
pub fn new<I: PinId>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
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(())
}
}
pub struct IoPeriphPin {
ll: ll::LowLevelGpio,
fun_sel: FunSel,
}
impl IoPeriphPin {
pub fn new<I: PinId>(_pin: Pin<I>, fun_sel: FunSel, pull: Option<Pull>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::PORT, I::OFFSET).unwrap();
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

@ -0,0 +1,123 @@
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_enb: u32,
#[mmio(PureRead)]
irq_raw: u32,
#[mmio(PureRead)]
irq_end: 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

@ -0,0 +1 @@
pub mod regs;

View File

@ -0,0 +1,241 @@
use core::marker::PhantomData;
use crate::{InvalidOffsetError, NUM_PORT_A, NUM_PORT_B};
#[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_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,
port: crate::Port,
offset: usize,
) -> Result<Config, InvalidOffsetError> {
if offset >= port.max_offset() {
return Err(InvalidOffsetError { port, offset });
}
Ok(unsafe { self.read_pin_config_unchecked(port, offset) })
}
/// This function does NOT perform any bounds checking.
///
/// # Safety
///
/// Calling this function with an invalid offset can lead to undefined behaviour.
pub unsafe fn read_pin_config_unchecked(&self, port: crate::Port, offset: usize) -> Config {
match 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: FnMut(Config) -> Config>(
&mut self,
port: crate::Port,
offset: usize,
f: F,
) -> Result<(), InvalidOffsetError> {
if offset >= port.max_offset() {
return Err(InvalidOffsetError { port, offset });
}
unsafe { self.modify_pin_config_unchecked(port, offset, f) };
Ok(())
}
/// This function does NOT perform any bounds checking.
///
/// # Safety
///
/// Calling this function with an invalid offset can lead to undefined behaviour.
pub unsafe fn modify_pin_config_unchecked<F: FnMut(Config) -> Config>(
&mut self,
port: crate::Port,
offset: usize,
mut f: F,
) {
unsafe {
let config = self.read_pin_config_unchecked(port, offset);
self.write_pin_config_unchecked(port, offset, f(config))
}
}
pub fn write_pin_config(
&mut self,
port: crate::Port,
offset: usize,
config: Config,
) -> Result<(), InvalidOffsetError> {
if offset >= port.max_offset() {
return Err(InvalidOffsetError { port, offset });
}
unsafe {
self.write_pin_config_unchecked(port, offset, config);
}
Ok(())
}
/// This function does NOT perform any bounds checking.
///
/// # Safety
///
/// Calling this function with an invalid offset can lead to undefined behaviour.
pub unsafe fn write_pin_config_unchecked(
&mut self,
port: crate::Port,
offset: usize,
config: Config,
) {
match 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

@ -0,0 +1,69 @@
#![no_std]
pub mod gpio;
pub mod ioconfig;
#[cfg(not(feature = "_family-selected"))]
compile_error!("no Vorago CPU family was select. Choices: vor1x or vor4x");
pub use ioconfig::regs::FunSel;
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;
}
}
#[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 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,
}
}
}
#[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,
}
#[allow(dead_code)]
pub(crate) mod sealed {
pub trait Sealed {}
}