use crate::peripherals::UlpGpio;
use core::marker::PhantomData;
pub struct Input;
pub struct Output;
pub struct UlpGpioPin<'d, MODE> {
bit: u8,
_mode: PhantomData<&'d MODE>,
}
fn regs() -> &'static crate::soc::pac::gpio0::RegisterBlock {
unsafe { &*UlpGpio::ptr() }
}
impl<MODE> UlpGpioPin<'_, MODE> {
pub fn number(&self) -> u8 {
107 + self.bit
}
}
impl UlpGpioPin<'_, Output> {
pub fn set_high(&mut self) {
unsafe { regs().gpio_data_set().write(|w| w.bits(1 << self.bit)) };
}
pub fn set_low(&mut self) {
unsafe { regs().gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
}
pub fn toggle(&mut self) {
let val = regs().gpio_sw_out().read().bits();
if val & (1 << self.bit) != 0 {
unsafe { regs().gpio_data_clr().write(|w| w.bits(1 << self.bit)) };
} else {
unsafe { regs().gpio_data_set().write(|w| w.bits(1 << self.bit)) };
}
}
pub fn is_set_high(&self) -> bool {
(regs().gpio_sw_out().read().bits() >> self.bit) & 1 != 0
}
pub fn into_input(self) -> UlpGpioPin<'static, Input> {
regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
UlpGpioPin { bit: self.bit, _mode: PhantomData }
}
}
impl UlpGpioPin<'_, Input> {
pub fn is_high(&self) -> bool {
(regs().gpio_sw_out().read().bits() >> self.bit) & 1 != 0
}
pub fn is_low(&self) -> bool {
!self.is_high()
}
pub fn enable_interrupt(&self) {
regs().gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.bit)) });
}
pub fn disable_interrupt(&self) {
regs().gpio_int_en().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
}
pub fn clear_interrupt(&self) {
unsafe { regs().gpio_int_eoi().write(|w| w.bits(1 << self.bit)) };
}
pub fn interrupt_pending(&self) -> bool {
(regs().gpio_int_raw().read().bits() >> self.bit) & 1 != 0
}
pub fn into_output(self) -> UlpGpioPin<'static, Output> {
regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.bit)) });
UlpGpioPin { bit: self.bit, _mode: PhantomData }
}
}
impl embedded_hal::digital::ErrorType for UlpGpioPin<'_, Output> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::OutputPin for UlpGpioPin<'_, Output> {
fn set_low(&mut self) -> Result<(), Self::Error> {
UlpGpioPin::set_low(self);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
UlpGpioPin::set_high(self);
Ok(())
}
}
impl embedded_hal::digital::StatefulOutputPin for UlpGpioPin<'_, Output> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(UlpGpioPin::is_set_high(self))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!UlpGpioPin::is_set_high(self))
}
}
impl embedded_hal::digital::ErrorType for UlpGpioPin<'_, Input> {
type Error = core::convert::Infallible;
}
impl embedded_hal::digital::InputPin for UlpGpioPin<'_, Input> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(UlpGpioPin::is_high(self))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!UlpGpioPin::is_high(self))
}
}
pub fn create_input_pin(pin: u8) -> UlpGpioPin<'static, Input> {
let bit = pin - 107;
assert!(bit < 8, "ULP GPIO pin must be 107-114");
regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() | (1 << bit)) });
UlpGpioPin { bit, _mode: PhantomData }
}
pub fn create_output_pin(pin: u8) -> UlpGpioPin<'static, Output> {
let bit = pin - 107;
assert!(bit < 8, "ULP GPIO pin must be 107-114");
regs().gpio_sw_oen().modify(|r, w| unsafe { w.bits(r.bits() & !(1 << bit)) });
UlpGpioPin { bit, _mode: PhantomData }
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod tests {
const BASE_PIN: u8 = 107;
const PIN_COUNT: u8 = 8;
fn pin_number(bit: u8) -> u8 {
BASE_PIN + bit
}
fn pin_to_bit(pin: u8) -> u8 {
pin - BASE_PIN
}
#[test]
fn number_of_first_pin() {
assert_eq!(pin_number(0), 107);
}
#[test]
fn number_of_last_pin() {
assert_eq!(pin_number(PIN_COUNT - 1), 114);
}
#[test]
fn pin_to_bit_round_trips() {
for bit in 0..PIN_COUNT {
let pin = pin_number(bit);
assert_eq!(pin_to_bit(pin), bit);
}
}
#[test]
fn valid_pins_pass_bit_bound() {
for pin in 107..=114u8 {
assert!(pin_to_bit(pin) < PIN_COUNT, "pin {pin} should be valid");
}
}
#[test]
fn first_out_of_range_pin_fails_bit_bound() {
assert_eq!(pin_to_bit(115), PIN_COUNT);
assert!(!(pin_to_bit(115) < PIN_COUNT));
}
#[test]
fn bit_mask_is_single_bit() {
let mut seen: u32 = 0;
for bit in 0..PIN_COUNT {
let mask = 1u32 << bit;
assert_eq!(mask.count_ones(), 1);
assert_eq!(seen & mask, 0, "bit {bit} mask overlaps a previous pin");
seen |= mask;
}
assert_eq!(seen, 0xFF);
}
}
#[cfg(all(test, not(target_arch = "riscv32")))]
mod proptests {
use proptest::prelude::*;
const BASE_PIN: u8 = 107;
const PIN_COUNT: u8 = 8;
proptest! {
#[test]
fn pin_bit_round_trip(pin in 107u8..=114) {
let bit = pin - BASE_PIN;
prop_assert!(bit < PIN_COUNT);
prop_assert_eq!(BASE_PIN + bit, pin);
}
#[test]
fn single_bit_mask(bit in 0u8..PIN_COUNT) {
let mask = 1u32 << bit;
prop_assert_eq!(mask.count_ones(), 1);
}
}
}