From 9a39912edcaf6d067c1a22db516f8059095b6868 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 12 Jun 2024 10:12:11 +0200 Subject: [PATCH 1/5] gpio init --- .gitignore | 2 + va416xx-hal/Cargo.toml | 3 + va416xx-hal/examples/blinky-pac.rs | 34 ++ va416xx-hal/examples/blinky.rs | 25 +- va416xx-hal/src/clock.rs | 14 + va416xx-hal/src/gpio/dynpin.rs | 442 ++++++++++++++++ va416xx-hal/src/gpio/mod.rs | 82 +++ va416xx-hal/src/gpio/pin.rs | 822 +++++++++++++++++++++++++++++ va416xx-hal/src/gpio/reg.rs | 391 ++++++++++++++ va416xx-hal/src/lib.rs | 41 +- va416xx-hal/src/prelude.rs | 0 va416xx-hal/src/typelevel.rs | 155 ++++++ 12 files changed, 1993 insertions(+), 18 deletions(-) create mode 100644 va416xx-hal/examples/blinky-pac.rs create mode 100644 va416xx-hal/src/gpio/dynpin.rs create mode 100644 va416xx-hal/src/gpio/mod.rs create mode 100644 va416xx-hal/src/gpio/pin.rs create mode 100644 va416xx-hal/src/gpio/reg.rs create mode 100644 va416xx-hal/src/prelude.rs create mode 100644 va416xx-hal/src/typelevel.rs diff --git a/.gitignore b/.gitignore index ee46a83..4f9c3c0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +/app.map diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index 91f4c7e..ae093dd 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -14,6 +14,9 @@ categories = ["embedded", "no-std", "hardware-support"] cortex-m = "0.7" cortex-m-rt = "0.7" nb = "1" +paste = "1" +embedded-hal = "1" +typenum = "1.12.0" [dependencies.va416xx] path = "../va416xx" diff --git a/va416xx-hal/examples/blinky-pac.rs b/va416xx-hal/examples/blinky-pac.rs new file mode 100644 index 0000000..8db2fa1 --- /dev/null +++ b/va416xx-hal/examples/blinky-pac.rs @@ -0,0 +1,34 @@ +//! Simple blinky example +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use panic_halt as _; +use va416xx_hal::pac; + +// Mask for the LED +const LED_PG5: u32 = 1 << 5; + +#[entry] +fn main() -> ! { + // SAFETY: Peripherals are only stolen once here. + let dp = unsafe { pac::Peripherals::steal() }; + // Enable all peripheral clocks + dp.sysconfig + .peripheral_clk_enable() + .modify(|_, w| unsafe { w.bits(0xffffffff) }); + dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) }); + dp.portg + .datamask() + .modify(|_, w| unsafe { w.bits(LED_PG5) }); + for _ in 0..10 { + dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) }); + cortex_m::asm::delay(2_000_000); + dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) }); + cortex_m::asm::delay(2_000_000); + } + loop { + dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) }); + cortex_m::asm::delay(2_000_000); + } +} diff --git a/va416xx-hal/examples/blinky.rs b/va416xx-hal/examples/blinky.rs index 8db2fa1..59da3f1 100644 --- a/va416xx-hal/examples/blinky.rs +++ b/va416xx-hal/examples/blinky.rs @@ -1,34 +1,25 @@ -//! Simple blinky example +//! Simple blinky example using the HAL #![no_main] #![no_std] use cortex_m_rt::entry; +use embedded_hal::digital::StatefulOutputPin; use panic_halt as _; -use va416xx_hal::pac; - -// Mask for the LED -const LED_PG5: u32 = 1 << 5; +use va416xx_hal::{gpio::PinsG, pac}; #[entry] fn main() -> ! { // SAFETY: Peripherals are only stolen once here. - let dp = unsafe { pac::Peripherals::steal() }; + let mut dp = unsafe { pac::Peripherals::steal() }; // Enable all peripheral clocks dp.sysconfig .peripheral_clk_enable() .modify(|_, w| unsafe { w.bits(0xffffffff) }); - dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) }); - dp.portg - .datamask() - .modify(|_, w| unsafe { w.bits(LED_PG5) }); - for _ in 0..10 { - dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) }); - cortex_m::asm::delay(2_000_000); - dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) }); - cortex_m::asm::delay(2_000_000); - } + let portg = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg); + let mut led = portg.pg5.into_readable_push_pull_output(); + //let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0); loop { - dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) }); + led.toggle().ok(); cortex_m::asm::delay(2_000_000); } } diff --git a/va416xx-hal/src/clock.rs b/va416xx-hal/src/clock.rs index 1b20ccd..a08d1ab 100644 --- a/va416xx-hal/src/clock.rs +++ b/va416xx-hal/src/clock.rs @@ -36,6 +36,20 @@ pub enum PeripheralSelect { PortG = 30, } +pub type PeripheralClocks = PeripheralSelect; + +#[derive(Debug, PartialEq, Eq)] +pub enum FilterClkSel { + SysClk = 0, + Clk1 = 1, + Clk2 = 2, + Clk3 = 3, + Clk4 = 4, + Clk5 = 5, + Clk6 = 6, + Clk7 = 7, +} + pub fn enable_peripheral_clock(syscfg: &mut Sysconfig, clock: PeripheralSelect) { syscfg .peripheral_clk_enable() diff --git a/va416xx-hal/src/gpio/dynpin.rs b/va416xx-hal/src/gpio/dynpin.rs new file mode 100644 index 0000000..07455e3 --- /dev/null +++ b/va416xx-hal/src/gpio/dynpin.rs @@ -0,0 +1,442 @@ +use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin}; + +use super::{ + reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId, + PinMode, PinState, +}; + +//================================================================================================== +// DynPinMode configurations +//================================================================================================== + +/// Value-level `enum` for disabled configurations +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DynDisabled { + Floating, + PullDown, + PullUp, +} + +/// Value-level `enum` for input configurations +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DynInput { + Floating, + PullDown, + PullUp, +} + +/// Value-level `enum` for output configurations +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DynOutput { + PushPull, + OpenDrain, + ReadablePushPull, + ReadableOpenDrain, +} + +pub type DynAlternate = crate::FunSel; + +//================================================================================================== +// DynPinMode +//================================================================================================== + +/// Value-level `enum` representing pin modes +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DynPinMode { + Input(DynInput), + Output(DynOutput), + Alternate(DynAlternate), +} + +/// Value-level variant of [`DynPinMode`] for floating input mode +pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); +/// Value-level variant of [`DynPinMode`] for pull-down input mode +pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); +/// Value-level variant of [`DynPinMode`] for pull-up input mode +pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); + +/// Value-level variant of [`DynPinMode`] for push-pull output mode +pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); +/// Value-level variant of [`DynPinMode`] for open-drain output mode +pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain); +/// Value-level variant of [`DynPinMode`] for readable push-pull output mode +pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull); +/// Value-level variant of [`DynPinMode`] for readable opendrain output mode +pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain); + +/// Value-level variant of [`DynPinMode`] for function select 1 +pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1); +/// Value-level variant of [`DynPinMode`] for function select 2 +pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2); +/// Value-level variant of [`DynPinMode`] for function select 3 +pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3); + +//================================================================================================== +// DynGroup & DynPinId +//================================================================================================== + +/// Value-level `enum` for pin groups +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DynGroup { + A, + B, + C, + D, + E, + F, + G, +} + +/// Value-level `struct` representing pin IDs +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct DynPinId { + pub group: DynGroup, + pub num: u8, +} + +//============================================================================== +// DynRegisters +//============================================================================== + +/// Provide a safe register interface for [`DynPin`]s +/// +/// This `struct` takes ownership of a [`DynPinId`] and provides an API to +/// access the corresponding regsiters. +struct DynRegisters { + id: DynPinId, +} + +// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] +// guarantees that each pin is a singleton, so this implementation is safe. +unsafe impl RegisterInterface for DynRegisters { + #[inline] + fn id(&self) -> DynPinId { + self.id + } +} + +impl DynRegisters { + /// Create a new instance of [`DynRegisters`] + /// + /// # Safety + /// + /// Users must never create two simultaneous instances of this `struct` with + /// the same [`DynPinId`] + #[inline] + unsafe fn new(id: DynPinId) -> Self { + DynRegisters { id } + } +} + +//============================================================================== +// Error +//============================================================================== + +/// GPIO error type +/// +/// [`DynPin`]s are not tracked and verified at compile-time, so run-time +/// operations are fallible. This `enum` represents the corresponding errors. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidPinTypeError(pub (crate) ()); + +impl embedded_hal::digital::Error for InvalidPinTypeError { + fn kind(&self) -> embedded_hal::digital::ErrorKind { + ErrorKind::Other + } +} + +//================================================================================================== +// DynPin +//================================================================================================== + +/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`] +/// +/// This type acts as a type-erased version of [`Pin`]. Every pin is represented +/// by the same type, and pins are tracked and distinguished at run-time. +pub struct DynPin { + regs: DynRegisters, + mode: DynPinMode, +} + +impl DynPin { + /// Create a new [`DynPin`] + /// + /// # Safety + /// + /// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there + /// must be at most one corresponding [`DynPin`] in existence at any given + /// time. Violating this requirement is `unsafe`. + #[inline] + unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { + DynPin { + regs: DynRegisters::new(id), + mode, + } + } + + /// Return a copy of the pin ID + #[inline] + pub fn id(&self) -> DynPinId { + self.regs.id + } + + /// Return a copy of the pin mode + #[inline] + pub fn mode(&self) -> DynPinMode { + self.mode + } + + /// Convert the pin to the requested [`DynPinMode`] + #[inline] + pub fn into_mode(&mut self, mode: DynPinMode) { + // Only modify registers if we are actually changing pin mode + if mode != self.mode { + self.regs.change_mode(mode); + self.mode = mode; + } + } + + #[inline] + pub fn into_funsel_1(&mut self) { + self.into_mode(DYN_ALT_FUNC_1); + } + + #[inline] + pub fn into_funsel_2(&mut self) { + self.into_mode(DYN_ALT_FUNC_2); + } + + #[inline] + pub fn into_funsel_3(&mut self) { + self.into_mode(DYN_ALT_FUNC_3); + } + + /// Configure the pin to operate as a floating input + #[inline] + pub fn into_floating_input(&mut self) { + self.into_mode(DYN_FLOATING_INPUT); + } + + /// Configure the pin to operate as a pulled down input + #[inline] + pub fn into_pull_down_input(&mut self) { + self.into_mode(DYN_PULL_DOWN_INPUT); + } + + /// Configure the pin to operate as a pulled up input + #[inline] + pub fn into_pull_up_input(&mut self) { + self.into_mode(DYN_PULL_UP_INPUT); + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_push_pull_output(&mut self) { + self.into_mode(DYN_PUSH_PULL_OUTPUT); + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_open_drain_output(&mut self) { + self.into_mode(DYN_OPEN_DRAIN_OUTPUT); + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_readable_push_pull_output(&mut self) { + self.into_mode(DYN_RD_PUSH_PULL_OUTPUT); + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_readable_open_drain_output(&mut self) { + self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); + } + + common_reg_if_functions!(); + + /// See p.53 of the programmers guide for more information. + /// Possible delays in clock cycles: + /// - Delay 1: 1 + /// - Delay 2: 2 + /// - Delay 1 + Delay 2: 3 + #[inline] + pub fn delay(self, delay_1: bool, delay_2: bool) -> Result { + match self.mode { + DynPinMode::Output(_) => { + self.regs.delay(delay_1, delay_2); + Ok(self) + } + _ => Err(InvalidPinTypeError(())) + } + } + + /// See p.52 of the programmers guide for more information. + /// When configured for pulse mode, a given pin will set the non-default state for exactly + /// one clock cycle before returning to the configured default state + pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Result { + match self.mode { + DynPinMode::Output(_) => { + self.regs.pulse_mode(enable, default_state); + Ok(self) + } + _ => Err(InvalidPinTypeError(())) + } + } + + /// See p.37 and p.38 of the programmers guide for more information. + #[inline] + pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Result { + match self.mode { + DynPinMode::Input(_) => { + self.regs.filter_type(filter, clksel); + Ok(self) + } + _ => Err(InvalidPinTypeError(())) + } + } + + pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Result { + match self.mode { + DynPinMode::Input(_) | DynPinMode::Output(_) => { + self.regs.interrupt_edge(edge_type); + self.irq_enb(); + Ok(self) + } + _ => Err(InvalidPinTypeError(())) + } + } + + pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Result { + match self.mode { + DynPinMode::Input(_) | DynPinMode::Output(_) => { + self.regs.interrupt_level(level_type); + self.irq_enb(); + Ok(self) + } + _ => Err(InvalidPinTypeError(())) + } + } + + #[inline] + fn _read(&self) -> Result { + match self.mode { + DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { + Ok(self.regs.read_pin()) + } + _ => Err(InvalidPinTypeError(())) + } + } + #[inline] + fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> { + match self.mode { + DynPinMode::Output(_) => { + self.regs.write_pin(bit); + Ok(()) + } + _ => Err(InvalidPinTypeError(())) + } + } + + #[inline] + fn _is_low(&self) -> Result { + self._read().map(|v| !v) + } + #[inline] + fn _is_high(&self) -> Result { + self._read() + } + #[inline] + fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> { + self._write(false) + } + #[inline] + fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> { + self._write(true) + } +} + +//============================================================================== +// Convert between Pin and DynPin +//============================================================================== + +impl From> for DynPin +where + I: PinId, + M: PinMode, +{ + /// Erase the type-level information in a [`Pin`] and return a value-level + /// [`DynPin`] + #[inline] + fn from(_pin: Pin) -> Self { + // The `Pin` is consumed, so it is safe to replace it with the + // corresponding `DynPin` + unsafe { DynPin::new(I::DYN, M::DYN) } + } +} + +impl TryFrom for Pin +where + I: PinId, + M: PinMode, +{ + type Error = InvalidPinTypeError; + + /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] + /// + /// There is no way for the compiler to know if the conversion will be + /// successful at compile-time. We must verify the conversion at run-time + /// or refuse to perform it. + #[inline] + fn try_from(pin: DynPin) -> Result { + if pin.regs.id == I::DYN && pin.mode == M::DYN { + // The `DynPin` is consumed, so it is safe to replace it with the + // corresponding `Pin` + Ok(unsafe { Self::new() }) + } else { + Err(InvalidPinTypeError(())) + } + } +} + +//============================================================================== +// Embedded HAL v1 traits +//============================================================================== + +impl ErrorType for DynPin { + type Error = InvalidPinTypeError; +} + +impl OutputPin for DynPin { + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high() + } + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low() + } +} + +impl InputPin for DynPin { + #[inline] + fn is_high(&mut self) -> Result { + self._is_high() + } + #[inline] + fn is_low(&mut self) -> Result { + self._is_low() + } +} + +impl StatefulOutputPin for DynPin { + #[inline] + fn is_set_high(&mut self) -> Result { + self._is_high() + } + #[inline] + fn is_set_low(&mut self) -> Result { + self._is_low() + } +} diff --git a/va416xx-hal/src/gpio/mod.rs b/va416xx-hal/src/gpio/mod.rs new file mode 100644 index 0000000..ce4d202 --- /dev/null +++ b/va416xx-hal/src/gpio/mod.rs @@ -0,0 +1,82 @@ +//! # API for the GPIO peripheral +//! +//! The implementation of this GPIO module is heavily based on the +//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html). +//! +//! This API provides two different submodules, [`mod@pins`] and [`dynpins`], +//! representing two different ways to handle GPIO pins. The default, [`mod@pins`], +//! is a type-level API that tracks the state of each pin at compile-time. The +//! alternative, [`dynpins`] is a type-erased, value-level API that tracks the +//! state of each pin at run-time. +//! +//! The type-level API is strongly preferred. By representing the state of each +//! pin within the type system, the compiler can detect logic errors at +//! compile-time. Furthermore, the type-level API has absolutely zero run-time +//! cost. +//! +//! If needed, [`dynpins`] can be used to erase the type-level differences +//! between pins. However, by doing so, pins must now be tracked at run-time, +//! and each pin has a non-zero memory footprint. +//! +//! ## Examples +//! +//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs) + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct IsMaskedError(pub(crate) ()); + +macro_rules! common_reg_if_functions { + () => { + paste::paste!( + #[inline] + pub fn datamask(&self) -> bool { + self.regs.datamask() + } + + #[inline] + pub fn clear_datamask(self) -> Self { + self.regs.clear_datamask(); + self + } + + #[inline] + pub fn set_datamask(self) -> Self { + self.regs.set_datamask(); + self + } + + #[inline] + pub fn is_high_masked(&self) -> Result { + self.regs.read_pin_masked() + } + + #[inline] + pub fn is_low_masked(&self) -> Result { + self.regs.read_pin_masked().map(|v| !v) + } + + #[inline] + pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.regs.write_pin_masked(true) + } + + #[inline] + pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.regs.write_pin_masked(false) + } + + fn irq_enb(&mut self) { + self.regs.enable_irq(); + } + ); + }; +} + +pub mod pin; +pub use pin::*; + +pub mod dynpin; +pub use dynpin::*; + +mod reg; diff --git a/va416xx-hal/src/gpio/pin.rs b/va416xx-hal/src/gpio/pin.rs new file mode 100644 index 0000000..d01dcc6 --- /dev/null +++ b/va416xx-hal/src/gpio/pin.rs @@ -0,0 +1,822 @@ +use core::{convert::Infallible, marker::PhantomData, mem::transmute}; + +pub use crate::clock::FilterClkSel; +use crate::Sealed; +use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; +use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg}; + +use super::{ + reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode, +}; + +//================================================================================================== +// Errors and Definitions +//================================================================================================== + +#[derive(Debug, PartialEq, Eq)] +pub enum InterruptEdge { + HighToLow, + LowToHigh, + BothEdges, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum InterruptLevel { + Low = 0, + High = 1, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum PinState { + Low = 0, + High = 1, +} + +//================================================================================================== +// Input configuration +//================================================================================================== + +/// Type-level enum for input configurations +/// +/// The valid options are [`Floating`], [`PullDown`] and [`PullUp`]. +pub trait InputConfig: Sealed { + /// Corresponding [`DynInput`](super::DynInput) + const DYN: DynInput; +} + +pub enum Floating {} +pub enum PullDown {} +pub enum PullUp {} + +impl InputConfig for Floating { + const DYN: DynInput = DynInput::Floating; +} +impl InputConfig for PullDown { + const DYN: DynInput = DynInput::PullDown; +} +impl InputConfig for PullUp { + const DYN: DynInput = DynInput::PullUp; +} + +impl Sealed for Floating {} +impl Sealed for PullDown {} +impl Sealed for PullUp {} + +/// Type-level variant of [`PinMode`] for floating input mode +pub type InputFloating = Input; +/// Type-level variant of [`PinMode`] for pull-down input mode +pub type InputPullDown = Input; +/// Type-level variant of [`PinMode`] for pull-up input mode +pub type InputPullUp = Input; + +/// Type-level variant of [`PinMode`] for input modes +/// +/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or +/// [`PullUp`] +pub struct Input { + cfg: PhantomData, +} + +impl Sealed for Input {} + +#[derive(Debug, PartialEq, Eq)] +pub enum FilterType { + SystemClock = 0, + DirectInputWithSynchronization = 1, + FilterOneClockCycle = 2, + FilterTwoClockCycles = 3, + FilterThreeClockCycles = 4, + FilterFourClockCycles = 5, +} + +//================================================================================================== +// Output configuration +//================================================================================================== + +pub trait OutputConfig: Sealed { + const DYN: DynOutput; +} + +/// Type-level variant of [`OutputConfig`] for a push-pull configuration +pub enum PushPull {} +/// Type-level variant of [`OutputConfig`] for an open drain configuration +pub enum OpenDrain {} + +/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration +pub enum ReadablePushPull {} +/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration +pub enum ReadableOpenDrain {} + +impl Sealed for PushPull {} +impl Sealed for OpenDrain {} +impl Sealed for ReadableOpenDrain {} +impl Sealed for ReadablePushPull {} + +impl OutputConfig for PushPull { + const DYN: DynOutput = DynOutput::PushPull; +} +impl OutputConfig for OpenDrain { + const DYN: DynOutput = DynOutput::OpenDrain; +} +impl OutputConfig for ReadablePushPull { + const DYN: DynOutput = DynOutput::ReadablePushPull; +} +impl OutputConfig for ReadableOpenDrain { + const DYN: DynOutput = DynOutput::ReadableOpenDrain; +} + +/// Type-level variant of [`PinMode`] for output modes +/// +/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or +/// their respective readable versions +pub struct Output { + cfg: PhantomData, +} + +impl Sealed for Output {} + +/// Type-level variant of [`PinMode`] for push-pull output mode +pub type PushPullOutput = Output; +/// Type-level variant of [`PinMode`] for open drain output mode +pub type OutputOpenDrain = Output; + +pub type OutputReadablePushPull = Output; +pub type OutputReadableOpenDrain = Output; + +//================================================================================================== +// Alternate configurations +//================================================================================================== + +/// Type-level enum for alternate peripheral function configurations +pub trait AlternateConfig: Sealed { + const DYN: DynAlternate; +} + +pub enum Funsel1 {} +pub enum Funsel2 {} +pub enum Funsel3 {} + +impl AlternateConfig for Funsel1 { + const DYN: DynAlternate = DynAlternate::Sel1; +} +impl AlternateConfig for Funsel2 { + const DYN: DynAlternate = DynAlternate::Sel2; +} +impl AlternateConfig for Funsel3 { + const DYN: DynAlternate = DynAlternate::Sel3; +} + +impl Sealed for Funsel1 {} +impl Sealed for Funsel2 {} +impl Sealed for Funsel3 {} + +/// Type-level variant of [`PinMode`] for alternate peripheral functions +/// +/// Type `C` is an [`AlternateConfig`] +pub struct Alternate { + cfg: PhantomData, +} + +impl Sealed for Alternate {} + +pub type AltFunc1 = Alternate; +pub type AltFunc2 = Alternate; +pub type AltFunc3 = Alternate; + +/// Type alias for the [`PinMode`] at reset +pub type Reset = InputFloating; + +//================================================================================================== +// Pin modes +//================================================================================================== + +/// Type-level enum representing pin modes +/// +/// The valid options are [`Input`], [`Output`] and [`Alternate`]. +pub trait PinMode: Sealed { + /// Corresponding [`DynPinMode`](super::DynPinMode) + const DYN: DynPinMode; +} + +impl PinMode for Input { + const DYN: DynPinMode = DynPinMode::Input(C::DYN); +} +impl PinMode for Output { + const DYN: DynPinMode = DynPinMode::Output(C::DYN); +} +impl PinMode for Alternate { + const DYN: DynPinMode = DynPinMode::Alternate(C::DYN); +} + +//================================================================================================== +// Pin IDs +//================================================================================================== + +/// Type-level enum for pin IDs +pub trait PinId: Sealed { + /// Corresponding [`DynPinId`](super::DynPinId) + const DYN: DynPinId; +} + +macro_rules! pin_id { + ($Group:ident, $Id:ident, $NUM:literal) => { + // Need paste macro to use ident in doc attribute + paste::paste! { + #[doc = "Pin ID representing pin " $Id] + pub enum $Id {} + impl Sealed for $Id {} + impl PinId for $Id { + const DYN: DynPinId = DynPinId { + group: DynGroup::$Group, + num: $NUM, + }; + } + } + }; +} + +//================================================================================================== +// Pin +//================================================================================================== + +/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types + +pub struct Pin { + pub(in crate::gpio) regs: Registers, + mode: PhantomData, +} + +impl Pin { + /// Create a new [`Pin`] + /// + /// # Safety + /// + /// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be + /// at most one corresponding [`Pin`] in existence at any given time. + /// Violating this requirement is `unsafe`. + #[inline] + pub(crate) unsafe fn new() -> Pin { + Pin { + regs: Registers::new(), + mode: PhantomData, + } + } + + /// Convert the pin to the requested [`PinMode`] + #[inline] + pub fn into_mode(mut self) -> Pin { + // Only modify registers if we are actually changing pin mode + // This check should compile away + if N::DYN != M::DYN { + self.regs.change_mode::(); + } + // Safe because we drop the existing Pin + unsafe { Pin::new() } + } + + /// Configure the pin for function select 1. See Programmer Guide p.40 for the function table + #[inline] + pub fn into_funsel_1(self) -> Pin { + self.into_mode() + } + + /// Configure the pin for function select 2. See Programmer Guide p.40 for the function table + #[inline] + pub fn into_funsel_2(self) -> Pin { + self.into_mode() + } + + /// Configure the pin for function select 3. See Programmer Guide p.40 for the function table + #[inline] + pub fn into_funsel_3(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a floating input + #[inline] + pub fn into_floating_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a pulled down input + #[inline] + pub fn into_pull_down_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a pulled up input + #[inline] + pub fn into_pull_up_input(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a push-pull output + #[inline] + pub fn into_push_pull_output(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a readable push-pull output + #[inline] + pub fn into_readable_push_pull_output(self) -> Pin { + self.into_mode() + } + + /// Configure the pin to operate as a readable open-drain output + #[inline] + pub fn into_readable_open_drain_output(self) -> Pin { + self.into_mode() + } + + common_reg_if_functions!(); + + #[inline] + pub(crate) fn _set_high(&mut self) { + self.regs.write_pin(true) + } + + #[inline] + pub(crate) fn _set_low(&mut self) { + self.regs.write_pin(false) + } + + #[inline] + pub(crate) fn _is_low(&self) -> bool { + !self.regs.read_pin() + } + + #[inline] + pub(crate) fn _is_high(&self) -> bool { + self.regs.read_pin() + } +} + +//============================================================================== +// AnyPin +//============================================================================== + +/// Type class for [`Pin`] types +/// +/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for +/// [`Pin`] types. See the `AnyKind` documentation for more details on the +/// pattern. +/// +/// ## `v1` Compatibility +/// +/// Normally, this trait would use `Is>` as a super +/// trait. But doing so would restrict implementations to only the `v2` `Pin` +/// type in this module. To aid in backwards compatibility, we want to implement +/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few +/// reasons. First, both structs are zero-sized, so there is no meaningful +/// memory layout to begin with. And even if there were, the `v1` `Pin` type is +/// a newtype wrapper around a `v2` `Pin`, and single-field structs are +/// guaranteed to have the same layout as the field, even for `repr(Rust)`. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +/// [type class]: crate::typelevel#type-classes +pub trait AnyPin +where + Self: Sealed, + Self: From>, + Self: Into>, + Self: AsRef>, + Self: AsMut>, +{ + /// [`PinId`] of the corresponding [`Pin`] + type Id: PinId; + /// [`PinMode`] of the corresponding [`Pin`] + type Mode: PinMode; +} + +impl Sealed for Pin +where + I: PinId, + M: PinMode, +{ +} + +impl AnyPin for Pin +where + I: PinId, + M: PinMode, +{ + type Id = I; + type Mode = M; +} + +/// Type alias to recover the specific [`Pin`] type from an implementation of +/// [`AnyPin`] +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +pub type SpecificPin

= Pin<

::Id,

::Mode>; + +impl AsRef

for SpecificPin

{ + #[inline] + fn as_ref(&self) -> &P { + // SAFETY: This is guaranteed to be safe, because P == SpecificPin

+ // Transmuting between `v1` and `v2` `Pin` types is also safe, because + // both are zero-sized, and single-field, newtype structs are guaranteed + // to have the same layout as the field anyway, even for repr(Rust). + unsafe { transmute(self) } + } +} + +impl AsMut

for SpecificPin

{ + #[inline] + fn as_mut(&mut self) -> &mut P { + // SAFETY: This is guaranteed to be safe, because P == SpecificPin

+ // Transmuting between `v1` and `v2` `Pin` types is also safe, because + // both are zero-sized, and single-field, newtype structs are guaranteed + // to have the same layout as the field anyway, even for repr(Rust). + unsafe { transmute(self) } + } +} + +//================================================================================================== +// Additional functionality +//================================================================================================== + +impl Pin> { + pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self { + self.regs.interrupt_edge(edge_type); + self.irq_enb(); + self + } + + pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self { + self.regs.interrupt_level(level_type); + self.irq_enb(); + self + } +} + +impl Pin> { + /// See p.53 of the programmers guide for more information. + /// Possible delays in clock cycles: + /// - Delay 1: 1 + /// - Delay 2: 2 + /// - Delay 1 + Delay 2: 3 + #[inline] + pub fn delay(self, delay_1: bool, delay_2: bool) -> Self { + self.regs.delay(delay_1, delay_2); + self + } + + /// See p.52 of the programmers guide for more information. + /// When configured for pulse mode, a given pin will set the non-default state for exactly + /// one clock cycle before returning to the configured default state + pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self { + self.regs.pulse_mode(enable, default_state); + self + } + + pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self { + self.regs.interrupt_edge(edge_type); + self.irq_enb(); + self + } + + pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self { + self.regs.interrupt_level(level_type); + self.irq_enb(); + self + } +} + +impl Pin> { + /// See p.37 and p.38 of the programmers guide for more information. + #[inline] + pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self { + self.regs.filter_type(filter, clksel); + self + } +} + +//================================================================================================== +// Embedded HAL traits +//================================================================================================== + +impl ErrorType for Pin +where + I: PinId, + M: PinMode, +{ + type Error = Infallible; +} + +impl OutputPin for Pin> { + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } +} + +impl InputPin for Pin> +where + I: PinId, + C: InputConfig, +{ + #[inline] + fn is_high(&mut self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_low(&mut self) -> Result { + Ok(self._is_low()) + } +} + +impl StatefulOutputPin for Pin> +where + I: PinId, + C: OutputConfig, +{ + #[inline] + fn is_set_high(&mut self) -> Result { + Ok(self._is_high()) + } + #[inline] + fn is_set_low(&mut self) -> Result { + Ok(self._is_low()) + } +} + +//================================================================================================== +// Registers +//================================================================================================== + +/// Provide a safe register interface for [`Pin`]s +/// +/// This `struct` takes ownership of a [`PinId`] and provides an API to +/// access the corresponding registers. +pub(in crate::gpio) struct Registers { + id: PhantomData, +} + +// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that +// each pin is a singleton, so this implementation is safe. +unsafe impl RegisterInterface for Registers { + #[inline] + fn id(&self) -> DynPinId { + I::DYN + } +} + +impl Registers { + /// Create a new instance of [`Registers`] + /// + /// # Safety + /// + /// Users must never create two simultaneous instances of this `struct` with + /// the same [`PinId`] + #[inline] + unsafe fn new() -> Self { + Registers { id: PhantomData } + } + + /// Provide a type-level equivalent for the + /// [`RegisterInterface::change_mode`] method. + #[inline] + pub(in crate::gpio) fn change_mode(&mut self) { + RegisterInterface::change_mode(self, M::DYN); + } +} + +//================================================================================================== +// Pin definitions +//================================================================================================== + +macro_rules! pins { + ( + $Port:ident, $PinsName:ident, $($Id:ident,)+, + ) => { + paste::paste!( + /// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB) + pub struct $PinsName { + iocfg: Option, + port: $Port, + $( + #[doc = "Pin " $Id] + pub [<$Id:lower>]: Pin<$Id, Reset>, + )+ + } + + impl $PinsName { + /// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral + /// is optional because it might be required to create pin definitions for both + /// ports. + #[inline] + pub fn new( + syscfg: &mut va416xx::Sysconfig, + iocfg: Option, + port: $Port + ) -> $PinsName { + syscfg.peripheral_clk_enable().modify(|_, w| { + w.[<$Port:lower>]().set_bit(); + w.ioconfig().set_bit() + }); + $PinsName { + iocfg, + port, + // Safe because we only create one `Pin` per `PinId` + $( + [<$Id:lower>]: unsafe { Pin::new() }, + )+ + } + } + + /// Get the peripheral ID + /// Safety: Read-only register + pub fn get_perid() -> u32 { + let port = unsafe { &(*$Port::ptr()) }; + port.perid().read().bits() + } + + /// Consumes the Pins struct and returns the port definitions + pub fn release(self) -> (Option, $Port) { + (self.iocfg, self.port) + } + } + ); + } +} + +macro_rules! declare_pins { + ( + $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+] + ) => { + pins!($Port, $PinsName, $($Id,)+,); + $( + pin_id!($Group, $Id, $NUM); + )+ + } +} + +declare_pins!( + A, + PinsA, + Porta, + [ + (PA0, 0), + (PA1, 1), + (PA2, 2), + (PA3, 3), + (PA4, 4), + (PA5, 5), + (PA6, 6), + (PA7, 7), + (PA8, 8), + (PA9, 9), + (PA10, 10), + (PA11, 11), + (PA12, 12), + (PA13, 13), + (PA14, 14), + (PA15, 15), + ] +); + +declare_pins!( + B, + PinsB, + Portb, + [ + (PB0, 0), + (PB1, 1), + (PB2, 2), + (PB3, 3), + (PB4, 4), + (PB5, 5), + (PB6, 6), + (PB7, 7), + (PB8, 8), + (PB9, 9), + (PB10, 10), + (PB11, 11), + (PB12, 12), + (PB13, 13), + (PB14, 14), + (PB15, 15), + ] +); + +declare_pins!( + C, + PinsC, + Portc, + [ + (PC0, 0), + (PC1, 1), + (PC2, 2), + (PC3, 3), + (PC4, 4), + (PC5, 5), + (PC6, 6), + (PC7, 7), + (PC8, 8), + (PC9, 9), + (PC10, 10), + (PC11, 11), + (PC12, 12), + (PC13, 13), + (PC14, 14), + (PC15, 15), + ] +); + +declare_pins!( + D, + PinsD, + Portd, + [ + (PD0, 0), + (PD1, 1), + (PD2, 2), + (PD3, 3), + (PD4, 4), + (PD5, 5), + (PD6, 6), + (PD7, 7), + (PD8, 8), + (PD9, 9), + (PD10, 10), + (PD11, 11), + (PD12, 12), + (PD13, 13), + (PD14, 14), + (PD15, 15), + ] +); + +declare_pins!( + E, + PinsE, + Porte, + [ + (PE0, 0), + (PE1, 1), + (PE2, 2), + (PE3, 3), + (PE4, 4), + (PE5, 5), + (PE6, 6), + (PE7, 7), + (PE8, 8), + (PE9, 9), + (PE10, 10), + (PE11, 11), + (PE12, 12), + (PE13, 13), + (PE14, 14), + (PE15, 15), + ] +); + +declare_pins!( + F, + PinsF, + Portf, + [ + (PF0, 0), + (PF1, 1), + (PF2, 2), + (PF3, 3), + (PF4, 4), + (PF5, 5), + (PF6, 6), + (PF7, 7), + (PF8, 8), + (PF9, 9), + (PF10, 10), + (PF11, 11), + (PF12, 12), + (PF13, 13), + (PF14, 14), + (PF15, 15), + ] +); + +declare_pins!( + G, + PinsG, + Portg, + [ + (PG0, 0), + (PG1, 1), + (PG2, 2), + (PG3, 3), + (PG4, 4), + (PG5, 5), + (PG6, 6), + (PG7, 7), + ] +); diff --git a/va416xx-hal/src/gpio/reg.rs b/va416xx-hal/src/gpio/reg.rs new file mode 100644 index 0000000..9c81c12 --- /dev/null +++ b/va416xx-hal/src/gpio/reg.rs @@ -0,0 +1,391 @@ +use crate::FunSel; + +use super::{ + dynpin::{self, DynGroup, DynPinId}, + DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState, +}; +use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg}; + +/// Type definition to avoid confusion: These register blocks are identical +type PortRegisterBlock = porta::RegisterBlock; + +//================================================================================================== +// ModeFields +//================================================================================================== + +/// Collect all fields needed to set the [`PinMode`](super::PinMode) +#[derive(Default)] +struct ModeFields { + dir: bool, + opendrn: bool, + pull_en: bool, + /// true for pullup, false for pulldown + pull_dir: bool, + funsel: u8, + enb_input: bool, +} + +impl From for ModeFields { + #[inline] + fn from(mode: DynPinMode) -> Self { + let mut fields = Self::default(); + use DynPinMode::*; + match mode { + Input(config) => { + use dynpin::DynInput::*; + fields.dir = false; + fields.funsel = FunSel::Sel0 as u8; + match config { + Floating => (), + PullUp => { + fields.pull_en = true; + fields.pull_dir = true; + } + PullDown => { + fields.pull_en = true; + } + } + } + Output(config) => { + use dynpin::DynOutput::*; + fields.dir = true; + fields.funsel = FunSel::Sel0 as u8; + match config { + PushPull => (), + OpenDrain => { + fields.opendrn = true; + } + ReadableOpenDrain => { + fields.enb_input = true; + fields.opendrn = true; + } + ReadablePushPull => { + fields.enb_input = true; + } + } + } + Alternate(config) => { + fields.funsel = config as u8; + } + } + fields + } +} + +//============================================================================== +// RegisterInterface +//============================================================================== + +pub type IocfgPort = ioconfig::Porta; +#[repr(C)] +pub(super) struct IocfgConfigGroupDefault { + port: [IocfgPort; 16], +} + +/// Provide a safe register interface for pin objects +/// +/// [`PORT`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it +/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an +/// interface is quite restrictive. Instead, it would be ideal if we could split +/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`]. +/// +/// [`PORT`] is a single, zero-sized marker `struct` that provides access to +/// every [`PORT`] register. Instead, we would like to create zero-sized marker +/// `struct`s for every pin, where each pin is only allowed to control its own +/// registers. Furthermore, each pin `struct` should be a singleton, so that +/// exclusive access to the `struct` also guarantees exclusive access to the +/// corresponding registers. Finally, the pin `struct`s should not have any +/// interior mutability. Together, these requirements would allow the pin +/// `struct`s to be both [`Send`] and [`Sync`]. +/// +/// This trait creates a safe API for accomplishing these goals. Implementers +/// supply a pin ID through the [`id`] function. The remaining functions provide +/// a safe API for accessing the registers associated with that pin ID. Any +/// modification of the registers requires `&mut self`, which destroys interior +/// mutability. +/// +/// # Safety +/// +/// Users should only implement the [`id`] function. No default function +/// implementations should be overridden. The implementing type must also have +/// "control" over the corresponding pin ID, i.e. it must guarantee that a each +/// pin ID is a singleton. +/// +/// [`id`]: Self::id +pub(super) unsafe trait RegisterInterface { + /// Provide a [`DynPinId`] identifying the set of registers controlled by + /// this type. + fn id(&self) -> DynPinId; + + const PORTA: *const PortRegisterBlock = Porta::ptr(); + const PORTB: *const PortRegisterBlock = Portb::ptr(); + const PORTC: *const PortRegisterBlock = Portc::ptr(); + const PORTD: *const PortRegisterBlock = Portd::ptr(); + const PORTE: *const PortRegisterBlock = Porte::ptr(); + const PORTF: *const PortRegisterBlock = Portf::ptr(); + const PORTG: *const PortRegisterBlock = Portg::ptr(); + const PORT_CFG_BASE: *const IocfgConfigGroupDefault = Ioconfig::ptr() as *const _; + + /// Change the pin mode + #[inline] + fn change_mode(&mut self, mode: DynPinMode) { + let ModeFields { + dir, + funsel, + opendrn, + pull_dir, + pull_en, + enb_input, + } = mode.into(); + let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); + iocfg.port[self.id().num as usize].write(|w| { + w.opendrn().bit(opendrn); + w.pen().bit(pull_en); + w.plevel().bit(pull_dir); + w.iewo().bit(enb_input); + unsafe { w.funsel().bits(funsel) } + }); + let mask = self.mask_32(); + unsafe { + if dir { + portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); + // Clear output + portreg.clrout().write(|w| w.bits(mask)); + } else { + portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); + } + } + } + + #[inline] + fn port_reg(&self) -> &PortRegisterBlock { + match self.id().group { + DynGroup::A => unsafe { &(*Self::PORTA) }, + DynGroup::B => unsafe { &(*Self::PORTB) }, + DynGroup::C => unsafe { &(*Self::PORTC) }, + DynGroup::D => unsafe { &(*Self::PORTD) }, + DynGroup::E => unsafe { &(*Self::PORTE) }, + DynGroup::F => unsafe { &(*Self::PORTF) }, + DynGroup::G => unsafe { &(*Self::PORTG) }, + } + } + + fn iocfg_port(&self) -> &IocfgConfigGroupDefault { + match self.id().group { + DynGroup::A => unsafe { &*Self::PORT_CFG_BASE }, + DynGroup::B => unsafe { &*Self::PORT_CFG_BASE.add(1) }, + DynGroup::C => unsafe { &*Self::PORT_CFG_BASE.add(3) }, + DynGroup::D => unsafe { &*Self::PORT_CFG_BASE.add(4) }, + DynGroup::E => unsafe { &*Self::PORT_CFG_BASE.add(5) }, + DynGroup::F => unsafe { &*Self::PORT_CFG_BASE.add(6) }, + DynGroup::G => unsafe { &*Self::PORT_CFG_BASE.add(7) }, + } + } + + #[inline] + fn mask_32(&self) -> u32 { + 1 << self.id().num + } + + #[inline] + fn enable_irq(&self) { + self.port_reg() + .irq_enb() + .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); + } + + #[inline] + /// Read the logic level of an output pin + fn read_pin(&self) -> bool { + let portreg = self.port_reg(); + ((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1 + } + + // Get DATAMASK bit for this particular pin + #[inline(always)] + fn datamask(&self) -> bool { + let portreg = self.port_reg(); + (portreg.datamask().read().bits() >> self.id().num) == 1 + } + + /// Read a pin but use the masked version but check whether the datamask for the pin is + /// cleared as well + #[inline(always)] + fn read_pin_masked(&self) -> Result { + if !self.datamask() { + Err(IsMaskedError(())) + } else { + Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) + } + } + + /// Write the logic level of an output pin + #[inline(always)] + fn write_pin(&mut self, bit: bool) { + // Safety: SETOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { + if bit { + self.port_reg().setout().write(|w| w.bits(self.mask_32())); + } else { + self.port_reg().clrout().write(|w| w.bits(self.mask_32())); + } + } + } + + /// Write the logic level of an output pin but check whether the datamask for the pin is + /// cleared as well + #[inline] + fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { + if !self.datamask() { + Err(IsMaskedError(())) + } else { + // Safety: SETOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { + if bit { + self.port_reg().setout().write(|w| w.bits(self.mask_32())); + } else { + self.port_reg().clrout().write(|w| w.bits(self.mask_32())); + } + Ok(()) + } + } + } + + /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure + /// When using edge mode, it is possible to generate interrupts on both edges as well + #[inline] + fn interrupt_edge(&mut self, edge_type: InterruptEdge) { + unsafe { + self.port_reg() + .irq_sen() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + match edge_type { + InterruptEdge::HighToLow => { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + InterruptEdge::LowToHigh => { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + InterruptEdge::BothEdges => { + self.port_reg() + .irq_edge() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + } + + /// Configure which edge or level type triggers an interrupt + #[inline] + fn interrupt_level(&mut self, level: InterruptLevel) { + unsafe { + self.port_reg() + .irq_sen() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + if level == InterruptLevel::Low { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } else { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + + /// Only useful for input pins + #[inline] + fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) { + self.iocfg_port().port[self.id().num as usize].modify(|_, w| { + // Safety: Only write to register for this Pin ID + unsafe { + w.flttype().bits(filter as u8); + w.fltclk().bits(clksel as u8) + } + }); + } + + /// Set DATAMASK bit for this particular pin. 1 is the default + /// state of the bit and allows access of the corresponding bit + #[inline(always)] + fn set_datamask(&self) { + let portreg = self.port_reg(); + unsafe { + portreg + .datamask() + .modify(|r, w| w.bits(r.bits() | self.mask_32())) + } + } + + /// Clear DATAMASK bit for this particular pin. This prevents access + /// of the corresponding bit for output and input operations + #[inline(always)] + fn clear_datamask(&self) { + let portreg = self.port_reg(); + unsafe { + portreg + .datamask() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())) + } + } + + /// Only useful for output pins + /// See p.52 of the programmers guide for more information. + /// When configured for pulse mode, a given pin will set the non-default state for exactly + /// one clock cycle before returning to the configured default state + fn pulse_mode(&self, enable: bool, default_state: PinState) { + let portreg = self.port_reg(); + unsafe { + if enable { + portreg + .pulse() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .pulse() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + if default_state == PinState::Low { + portreg + .pulsebase() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } else { + portreg + .pulsebase() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + + /// Only useful for output pins + fn delay(&self, delay_1: bool, delay_2: bool) { + let portreg = self.port_reg(); + unsafe { + if delay_1 { + portreg + .delay1() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .delay1() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + if delay_2 { + portreg + .delay2() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .delay2() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + } + } +} diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index 7d65709..c672edd 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -2,6 +2,45 @@ pub use va416xx; pub use va416xx as pac; +pub mod prelude; pub mod clock; -pub mod time; \ No newline at end of file +pub mod time; +pub mod gpio; +pub mod typelevel; + +#[derive(Debug, Eq, Copy, Clone, PartialEq)] +pub enum FunSel { + Sel0 = 0b00, + Sel1 = 0b01, + Sel2 = 0b10, + Sel3 = 0b11, +} + +/// Generic IRQ config which can be used to specify whether the HAL driver will +/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the +/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform +/// this steps themselves +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct IrqCfg { + /// Interrupt target vector. Should always be set, might be required for disabling IRQs + pub irq: pac::Interrupt, + /// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral + pub route: bool, + /// Specify whether the IRQ is unmasked in the Cortex-M NVIC + pub enable: bool, +} + +impl IrqCfg { + pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self { + IrqCfg { irq, route, enable } + } +} + +mod private { + /// Super trait used to mark traits with an exhaustive set of + /// implementations + pub trait Sealed {} +} + +pub(crate) use private::Sealed; diff --git a/va416xx-hal/src/prelude.rs b/va416xx-hal/src/prelude.rs new file mode 100644 index 0000000..e69de29 diff --git a/va416xx-hal/src/typelevel.rs b/va416xx-hal/src/typelevel.rs new file mode 100644 index 0000000..991593b --- /dev/null +++ b/va416xx-hal/src/typelevel.rs @@ -0,0 +1,155 @@ +//! Module supporting type-level programming +//! +//! This module is identical to the +//! [atsamd typelevel](https://docs.rs/atsamd-hal/latest/atsamd_hal/typelevel/index.html). + +use core::ops::{Add, Sub}; + +use typenum::{Add1, Bit, Sub1, UInt, Unsigned, B1, U0}; + +mod private { + /// Super trait used to mark traits with an exhaustive set of + /// implementations + pub trait Sealed {} + + impl Sealed for u8 {} + impl Sealed for i8 {} + impl Sealed for u16 {} + impl Sealed for i16 {} + impl Sealed for u32 {} + impl Sealed for i32 {} + impl Sealed for f32 {} + + /// Mapping from an instance of a countable type to its successor + pub trait Increment { + /// Successor type of `Self` + type Inc; + /// Consume an instance of `Self` and return its successor + fn inc(self) -> Self::Inc; + } + + /// Mapping from an instance of a countable type to its predecessor + pub trait Decrement { + /// Predecessor type of `Self` + type Dec; + /// Consume an instance of `Self` and return its predecessor + fn dec(self) -> Self::Dec; + } +} + +pub(crate) use private::Decrement as PrivateDecrement; +pub(crate) use private::Increment as PrivateIncrement; +pub(crate) use private::Sealed; + +/// Type-level version of the [`None`] variant +#[derive(Default)] +pub struct NoneT; + +impl Sealed for NoneT {} + +//============================================================================== +// Is +//============================================================================== + +/// Marker trait for type identity +/// +/// This trait is used as part of the [`AnyKind`] trait pattern. It represents +/// the concept of type identity, because all implementors have +/// `::Type == Self`. When used as a trait bound with a specific +/// type, it guarantees that the corresponding type parameter is exactly the +/// specific type. Stated differently, it guarantees that `T == Specific` in +/// the following example. +/// +/// ``` +/// where T: Is +/// ``` +/// +/// Moreover, the super traits guarantee that any instance of or reference to a +/// type `T` can be converted into the `Specific` type. +/// +/// ``` +/// fn example(mut any: T) +/// where +/// T: Is, +/// { +/// let specific_mut: &mut Specific = any.as_mut(); +/// let specific_ref: &Specific = any.as_ref(); +/// let specific: Specific = any.into(); +/// } +/// ``` +/// +/// [`AnyKind`]: #anykind-trait-pattern +pub trait Is +where + Self: Sealed, + Self: From>, + Self: Into>, + Self: AsRef>, + Self: AsMut>, +{ + type Type; +} + +/// Type alias for [`Is::Type`] +pub type IsType = ::Type; + +impl Is for T +where + T: Sealed + AsRef + AsMut, +{ + type Type = T; +} + +//============================================================================== +// Counting +//============================================================================== + +/// Implement `Sealed` for [`U0`] +impl Sealed for U0 {} + +/// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`] +impl Sealed for UInt {} + +/// Trait mapping each countable type to its successor +/// +/// This trait maps each countable type to its corresponding successor type. The +/// actual implementation of this trait is contained within `PrivateIncrement`. +/// Access to `PrivateIncrement` is restricted, so that safe HAL APIs can be +/// built with it. +pub trait Increment: PrivateIncrement {} + +impl Increment for T {} + +/// Trait mapping each countable type to its predecessor +/// +/// This trait maps each countable type to its corresponding predecessor type. +/// The actual implementation of this trait is contained within +/// `PrivateDecrement`. Access to `PrivateDecrement` is restricted, so that safe +/// HAL APIs can be built with it. +pub trait Decrement: PrivateDecrement {} + +impl Decrement for T {} + +impl PrivateIncrement for N +where + N: Unsigned + Add, + Add1: Unsigned, +{ + type Inc = Add1; + #[inline] + fn inc(self) -> Self::Inc { + Self::Inc::default() + } +} + +impl PrivateDecrement for N +where + N: Unsigned + Sub, + Sub1: Unsigned, +{ + type Dec = Sub1; + #[inline] + fn dec(self) -> Self::Dec { + Self::Dec::default() + } +} From 5179a79225b7900a215d794c7d34945bf2711e8e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 12 Jun 2024 10:15:46 +0200 Subject: [PATCH 2/5] update VS code files --- vscode/launch.json | 2 +- vscode/tasks.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vscode/launch.json b/vscode/launch.json index cf81817..d6d10d1 100644 --- a/vscode/launch.json +++ b/vscode/launch.json @@ -50,4 +50,4 @@ "runToMain": true, }, ] -} \ No newline at end of file +} diff --git a/vscode/tasks.json b/vscode/tasks.json index 6d36d81..2a52e98 100644 --- a/vscode/tasks.json +++ b/vscode/tasks.json @@ -32,7 +32,7 @@ "type": "shell", "command": "~/.cargo/bin/cargo", // note: full path to the cargo "args": [ - "build", "-p", "vorago-peb1", "--example", "blinky" + "build", "-p", "vorago-hal", "--example", "blinky" ], "group": { "kind": "build", @@ -40,4 +40,4 @@ } }, ] -} \ No newline at end of file +} From 622c0573f748595f2876447d8358436aaf12ea31 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 12 Jun 2024 10:30:22 +0200 Subject: [PATCH 3/5] update VS Code files --- va416xx-hal/.vscode/launch.json | 26 -------------------------- va416xx-hal/.vscode/tasks.json | 19 ------------------- va416xx-hal/examples/blinky.rs | 6 ++++-- vscode/launch.json | 20 ++++++++++---------- vscode/tasks.json | 26 +++++++++++++++++++------- 5 files changed, 33 insertions(+), 64 deletions(-) delete mode 100644 va416xx-hal/.vscode/launch.json delete mode 100644 va416xx-hal/.vscode/tasks.json diff --git a/va416xx-hal/.vscode/launch.json b/va416xx-hal/.vscode/launch.json deleted file mode 100644 index 6f90c38..0000000 --- a/va416xx-hal/.vscode/launch.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "cortex-debug", - "request": "launch", - "name": "Debug LED Blinky", - // The user should start the J-Link server themselves for now. This is because the - // Cortex-Debug will issue a reset command, which is problematic even with - // a valid JLinkScript file - "servertype": "external", - "gdbTarget": "localhost:2331", - "gdbPath": "/usr/bin/gdb-multiarch", - "cwd": "${workspaceRoot}", - "device": "Cortex-M4", - "svdFile": "../va416xx/svd/va416xx-base.svd", - "preLaunchTask": "rust: cargo build led blinky", - "executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky", - "interface": "swd", - "runToMain": true, - }, - ] -} \ No newline at end of file diff --git a/va416xx-hal/.vscode/tasks.json b/va416xx-hal/.vscode/tasks.json deleted file mode 100644 index b17869b..0000000 --- a/va416xx-hal/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "rust: cargo build led blinky", - "type": "shell", - "command": "~/.cargo/bin/cargo", // note: full path to the cargo - "args": [ - "build", "--example", "blinky" - ], - "group": { - "kind": "build", - "isDefault": true - } - }, - ] -} \ No newline at end of file diff --git a/va416xx-hal/examples/blinky.rs b/va416xx-hal/examples/blinky.rs index 59da3f1..be5633d 100644 --- a/va416xx-hal/examples/blinky.rs +++ b/va416xx-hal/examples/blinky.rs @@ -3,7 +3,7 @@ #![no_std] use cortex_m_rt::entry; -use embedded_hal::digital::StatefulOutputPin; +use embedded_hal::digital::{OutputPin, StatefulOutputPin}; use panic_halt as _; use va416xx_hal::{gpio::PinsG, pac}; @@ -19,7 +19,9 @@ fn main() -> ! { let mut led = portg.pg5.into_readable_push_pull_output(); //let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0); loop { - led.toggle().ok(); + led.set_high().ok(); + cortex_m::asm::delay(2_000_000); + led.set_low().ok(); cortex_m::asm::delay(2_000_000); } } diff --git a/vscode/launch.json b/vscode/launch.json index d6d10d1..88040ee 100644 --- a/vscode/launch.json +++ b/vscode/launch.json @@ -15,11 +15,11 @@ "gdbTarget": "localhost:2331", "cwd": "${workspaceRoot}", "device": "Cortex-M4", - "svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd", - "preLaunchTask": "rust: cargo build led blinky pac", + "svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched", + "preLaunchTask": "blinky-pac-example", "executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky-pac", "interface": "swd", - "runToMain": true, + "runToEntryPoint": "main", }, { "type": "cortex-debug", @@ -29,11 +29,11 @@ "gdbTarget": "localhost:2331", "cwd": "${workspaceRoot}", "device": "Cortex-M4", - "svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd", - "preLaunchTask": "rust: cargo build led blinky", + "svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched", + "preLaunchTask": "blinky-example", "executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky", "interface": "swd", - "runToMain": true, + "runToEntryPoint": "main", }, { "type": "cortex-debug", @@ -43,11 +43,11 @@ "gdbTarget": "localhost:2331", "cwd": "${workspaceRoot}", "device": "Cortex-M4", - "svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd", - "preLaunchTask": "rust: cargo build rtt", + "svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched", + "preLaunchTask": "rtt-log-example", "executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log", "interface": "swd", - "runToMain": true, + "runToEntryPoint": "main", }, ] -} +} \ No newline at end of file diff --git a/vscode/tasks.json b/vscode/tasks.json index 2a52e98..d7113a3 100644 --- a/vscode/tasks.json +++ b/vscode/tasks.json @@ -4,11 +4,15 @@ "version": "2.0.0", "tasks": [ { - "label": "rust: cargo build led blinky pac", + "label": "blinky-pac-example", "type": "shell", "command": "~/.cargo/bin/cargo", // note: full path to the cargo "args": [ - "build", "-p", "va416xx-hal", "--example", "blinky-pac" + "build", + "-p", + "va416xx-hal", + "--example", + "blinky-pac" ], "group": { "kind": "build", @@ -16,11 +20,15 @@ } }, { - "label": "rust: cargo build rtt", + "label": "rtt-log-example", "type": "shell", "command": "~/.cargo/bin/cargo", // note: full path to the cargo "args": [ - "build", "-p", "va416xx-hal", "--example", "rtt-log" + "build", + "-p", + "va416xx-hal", + "--example", + "rtt-log" ], "group": { "kind": "build", @@ -28,11 +36,15 @@ } }, { - "label": "rust: cargo build led blinky", + "label": "blinky-example", "type": "shell", "command": "~/.cargo/bin/cargo", // note: full path to the cargo "args": [ - "build", "-p", "vorago-hal", "--example", "blinky" + "build", + "-p", + "va416xx-hal", + "--example", + "blinky" ], "group": { "kind": "build", @@ -40,4 +52,4 @@ } }, ] -} +} \ No newline at end of file From cd7a6499d4c4714f295c16796be4be7525ef6725 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 12 Jun 2024 11:29:12 +0200 Subject: [PATCH 4/5] GPIO example works --- va416xx-hal/examples/blinky.rs | 10 ++-------- va416xx-hal/src/gpio/dynpin.rs | 33 ++++++++++++++++++++++----------- va416xx-hal/src/gpio/pin.rs | 6 +++++- va416xx-hal/src/gpio/reg.rs | 28 ++++++++++++---------------- va416xx-hal/src/lib.rs | 2 +- va416xx-hal/src/prelude.rs | 1 + va416xx/src/porta.rs | 3 +++ vorago-peb1/examples/blinky.rs | 9 +++------ 8 files changed, 49 insertions(+), 43 deletions(-) diff --git a/va416xx-hal/examples/blinky.rs b/va416xx-hal/examples/blinky.rs index be5633d..5ed2c39 100644 --- a/va416xx-hal/examples/blinky.rs +++ b/va416xx-hal/examples/blinky.rs @@ -3,7 +3,7 @@ #![no_std] use cortex_m_rt::entry; -use embedded_hal::digital::{OutputPin, StatefulOutputPin}; +use embedded_hal::digital::StatefulOutputPin; use panic_halt as _; use va416xx_hal::{gpio::PinsG, pac}; @@ -11,17 +11,11 @@ use va416xx_hal::{gpio::PinsG, pac}; fn main() -> ! { // SAFETY: Peripherals are only stolen once here. let mut dp = unsafe { pac::Peripherals::steal() }; - // Enable all peripheral clocks - dp.sysconfig - .peripheral_clk_enable() - .modify(|_, w| unsafe { w.bits(0xffffffff) }); let portg = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg); let mut led = portg.pg5.into_readable_push_pull_output(); //let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0); loop { - led.set_high().ok(); - cortex_m::asm::delay(2_000_000); - led.set_low().ok(); cortex_m::asm::delay(2_000_000); + led.toggle().ok(); } } diff --git a/va416xx-hal/src/gpio/dynpin.rs b/va416xx-hal/src/gpio/dynpin.rs index 07455e3..ab1c20c 100644 --- a/va416xx-hal/src/gpio/dynpin.rs +++ b/va416xx-hal/src/gpio/dynpin.rs @@ -138,7 +138,7 @@ impl DynRegisters { /// operations are fallible. This `enum` represents the corresponding errors. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidPinTypeError(pub (crate) ()); +pub struct InvalidPinTypeError(pub(crate) ()); impl embedded_hal::digital::Error for InvalidPinTypeError { fn kind(&self) -> embedded_hal::digital::ErrorKind { @@ -268,32 +268,40 @@ impl DynPin { self.regs.delay(delay_1, delay_2); Ok(self) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state - pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Result { + pub fn pulse_mode( + self, + enable: bool, + default_state: PinState, + ) -> Result { match self.mode { DynPinMode::Output(_) => { self.regs.pulse_mode(enable, default_state); Ok(self) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } /// See p.37 and p.38 of the programmers guide for more information. #[inline] - pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Result { + pub fn filter_type( + self, + filter: FilterType, + clksel: FilterClkSel, + ) -> Result { match self.mode { DynPinMode::Input(_) => { self.regs.filter_type(filter, clksel); Ok(self) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } @@ -304,18 +312,21 @@ impl DynPin { self.irq_enb(); Ok(self) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } - pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Result { + pub fn interrupt_level( + mut self, + level_type: InterruptLevel, + ) -> Result { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { self.regs.interrupt_level(level_type); self.irq_enb(); Ok(self) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } @@ -325,7 +336,7 @@ impl DynPin { DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { Ok(self.regs.read_pin()) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } #[inline] @@ -335,7 +346,7 @@ impl DynPin { self.regs.write_pin(bit); Ok(()) } - _ => Err(InvalidPinTypeError(())) + _ => Err(InvalidPinTypeError(())), } } diff --git a/va416xx-hal/src/gpio/pin.rs b/va416xx-hal/src/gpio/pin.rs index d01dcc6..ddd73b6 100644 --- a/va416xx-hal/src/gpio/pin.rs +++ b/va416xx-hal/src/gpio/pin.rs @@ -97,6 +97,8 @@ pub trait OutputConfig: Sealed { const DYN: DynOutput; } +pub trait ReadableOutput: Sealed {} + /// Type-level variant of [`OutputConfig`] for a push-pull configuration pub enum PushPull {} /// Type-level variant of [`OutputConfig`] for an open drain configuration @@ -111,6 +113,8 @@ impl Sealed for PushPull {} impl Sealed for OpenDrain {} impl Sealed for ReadableOpenDrain {} impl Sealed for ReadablePushPull {} +impl ReadableOutput for ReadableOpenDrain {} +impl ReadableOutput for ReadablePushPull {} impl OutputConfig for PushPull { const DYN: DynOutput = DynOutput::PushPull; @@ -538,7 +542,7 @@ where impl StatefulOutputPin for Pin> where I: PinId, - C: OutputConfig, + C: OutputConfig + ReadableOutput, { #[inline] fn is_set_high(&mut self) -> Result { diff --git a/va416xx-hal/src/gpio/reg.rs b/va416xx-hal/src/gpio/reg.rs index 9c81c12..2067fb9 100644 --- a/va416xx-hal/src/gpio/reg.rs +++ b/va416xx-hal/src/gpio/reg.rs @@ -76,11 +76,7 @@ impl From for ModeFields { // RegisterInterface //============================================================================== -pub type IocfgPort = ioconfig::Porta; -#[repr(C)] -pub(super) struct IocfgConfigGroupDefault { - port: [IocfgPort; 16], -} +pub type PortReg = ioconfig::Porta; /// Provide a safe register interface for pin objects /// @@ -124,7 +120,6 @@ pub(super) unsafe trait RegisterInterface { const PORTE: *const PortRegisterBlock = Porte::ptr(); const PORTF: *const PortRegisterBlock = Portf::ptr(); const PORTG: *const PortRegisterBlock = Portg::ptr(); - const PORT_CFG_BASE: *const IocfgConfigGroupDefault = Ioconfig::ptr() as *const _; /// Change the pin mode #[inline] @@ -138,7 +133,7 @@ pub(super) unsafe trait RegisterInterface { enb_input, } = mode.into(); let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); - iocfg.port[self.id().num as usize].write(|w| { + iocfg.write(|w| { w.opendrn().bit(opendrn); w.pen().bit(pull_en); w.plevel().bit(pull_dir); @@ -170,15 +165,16 @@ pub(super) unsafe trait RegisterInterface { } } - fn iocfg_port(&self) -> &IocfgConfigGroupDefault { + fn iocfg_port(&self) -> &PortReg { + let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() }; match self.id().group { - DynGroup::A => unsafe { &*Self::PORT_CFG_BASE }, - DynGroup::B => unsafe { &*Self::PORT_CFG_BASE.add(1) }, - DynGroup::C => unsafe { &*Self::PORT_CFG_BASE.add(3) }, - DynGroup::D => unsafe { &*Self::PORT_CFG_BASE.add(4) }, - DynGroup::E => unsafe { &*Self::PORT_CFG_BASE.add(5) }, - DynGroup::F => unsafe { &*Self::PORT_CFG_BASE.add(6) }, - DynGroup::G => unsafe { &*Self::PORT_CFG_BASE.add(7) }, + DynGroup::A => ioconfig.porta(self.id().num as usize), + DynGroup::B => ioconfig.portb0(self.id().num as usize), + DynGroup::C => ioconfig.portc0(self.id().num as usize), + DynGroup::D => ioconfig.portd0(self.id().num as usize), + DynGroup::E => ioconfig.porte0(self.id().num as usize), + DynGroup::F => ioconfig.portf0(self.id().num as usize), + DynGroup::G => ioconfig.portg0(self.id().num as usize), } } @@ -303,7 +299,7 @@ pub(super) unsafe trait RegisterInterface { /// Only useful for input pins #[inline] fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) { - self.iocfg_port().port[self.id().num as usize].modify(|_, w| { + self.iocfg_port().modify(|_, w| { // Safety: Only write to register for this Pin ID unsafe { w.flttype().bits(filter as u8); diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index c672edd..6aad93a 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -5,8 +5,8 @@ pub use va416xx as pac; pub mod prelude; pub mod clock; -pub mod time; pub mod gpio; +pub mod time; pub mod typelevel; #[derive(Debug, Eq, Copy, Clone, PartialEq)] diff --git a/va416xx-hal/src/prelude.rs b/va416xx-hal/src/prelude.rs index e69de29..8b13789 100644 --- a/va416xx-hal/src/prelude.rs +++ b/va416xx-hal/src/prelude.rs @@ -0,0 +1 @@ + diff --git a/va416xx/src/porta.rs b/va416xx/src/porta.rs index 99b21b6..7d18568 100644 --- a/va416xx/src/porta.rs +++ b/va416xx/src/porta.rs @@ -1,3 +1,6 @@ +// Manually inserted. +#![allow(clippy::identity_op)] + #[repr(C)] #[doc = "Register block"] pub struct RegisterBlock { diff --git a/vorago-peb1/examples/blinky.rs b/vorago-peb1/examples/blinky.rs index 44d14f7..a261fcb 100644 --- a/vorago-peb1/examples/blinky.rs +++ b/vorago-peb1/examples/blinky.rs @@ -3,12 +3,9 @@ #![no_std] use cortex_m_rt::entry; +use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin}; use panic_halt as _; -use va416xx_hal::{ - pac, - gpio::PinsG -}; -use embedded_hal::digital::v2::{ToggleableOutputPin, OutputPin}; +use va416xx_hal::{gpio::PinsG, pac}; // Mask for the LED const LED_PG5: u32 = 1 << 5; @@ -54,7 +51,7 @@ fn main() -> ! { led.set_low().ok(); cortex_m::asm::delay(5_000_000); led.set_high().ok(); - }; + } loop { led.toggle().ok(); cortex_m::asm::delay(25_000_000); From 5b1b6c4b777205645a947e1bbbbc8292fab9111d Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 12 Jun 2024 11:45:30 +0200 Subject: [PATCH 5/5] README --- README.md | 76 ++++++++++++++++++++++++++++++++++++++---- vscode/extensions.json | 12 +++++++ 2 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 vscode/extensions.json diff --git a/README.md b/README.md index 4573c82..87252cb 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ -# Vorago VA416xx Rust Workspace +Vorago VA416xx Rust Support +========= -After cloning, run +This crate collection provided support to write Rust applications for the VA416XX family +of devices. -```sh -git submodule init -git submodule update -``` +## List of crates + +This workspace contains the following crates: + +- The `va416xx` PAC crate containing basic low-level register definition +- The `va416xx-hal` HAL crate containing higher-level abstractions on top of + the PAC register crate. +- The `vorago-peb1` BSP crate containing support for the PEB1 development + board. ## Using the `.cargo/config.toml` file @@ -17,3 +24,60 @@ cp .cargo/def-config.toml .cargo/config.toml You then can adapt the `config.toml` to your needs. For example, you can configure runners to conveniently flash with `cargo run`. + +## Using the sample VS Code files + +Use the following command to have a starting configuration for VS Code: + +```sh +cp vscode .vscode -r +``` + +You can then adapt the files in `.vscode` to your needs. + +## Flashing, running and debugging with the command line + +### Prerequisites + +1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed +2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar + cross-architecture debugger installed. All commands here assume `gdb-multiarch`. + +### Flashing and debugging the blinky application + +You can build the blinky example application with the following command + +```sh +cargo build -p va416xx-hal --example blinky +``` + +Start the GDB server first. The server needs to be started with a certain configuration and with +a JLink script to disable ROM protection. +For example, on Debian based system the following command can be used to do this (this command +is also run when running the `jlink-gdb.sh` script) + +```sh +JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \ + -LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript +``` + +After this, you can flash and debug the application with the following command + +```sh +gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv7em-none-eabihf/debug/examples/blinky +``` + +Please note that you can automate all steps except starting the GDB server by using a cargo +runner configuration, for example with the following lines in your `.cargo/config.toml` file: + +```toml +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "gdb-multiarch -q -x jlink/jlink.gdb" +``` + +After that, you can simply use `cargo run -p va416xx-hal --example blinky` to flash the blinky +example. + +## Flashing, running and debugging with VS Code + +TODO diff --git a/vscode/extensions.json b/vscode/extensions.json new file mode 100644 index 0000000..d2cfd28 --- /dev/null +++ b/vscode/extensions.json @@ -0,0 +1,12 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "rust-lang.rust", + "marus25.cortex-debug" + // "probe-rs.probe-rs-debugger" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} \ No newline at end of file