This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
vorago-shared-hal/src/gpio/ll.rs
Robin Mueller b61feb62a7
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
Bump PACs
2025-09-03 10:11:46 +02:00

585 lines
19 KiB
Rust

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