use crate::pac::GPIO;
use core::marker::PhantomData;
pub struct Output;
pub struct Input;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Pull {
Up,
Down,
None,
}
pub struct Pin<const N: u8, MODE> {
_gpio: GPIO,
_mode: PhantomData<MODE>,
}
impl<const N: u8> Pin<N, Input> {
pub fn new(gpio: GPIO) -> Self {
const { Self::VALID_PIN };
Self {
_gpio: gpio,
_mode: PhantomData,
}
}
}
impl<const N: u8, MODE> Pin<N, MODE> {
const VALID_PIN: () = assert!(N <= 27, "the Pi 5 header is RP1 GPIO0..=27");
pub fn into_output(self) -> Pin<N, Output> {
select_rio(N);
rio_write(RIO_OE + SET_ALIAS, 1 << N);
Pin {
_gpio: self._gpio,
_mode: PhantomData,
}
}
pub fn into_input(self) -> Pin<N, Input> {
select_rio(N);
rio_write(RIO_OE + CLR_ALIAS, 1 << N);
Pin {
_gpio: self._gpio,
_mode: PhantomData,
}
}
pub fn set_pull(&self, pull: Pull) {
set_pull(N, pull);
}
pub fn pull(&self) -> Option<Pull> {
let pad = pad_read(N);
match (pad & PAD_PUE != 0, pad & PAD_PDE != 0) {
(false, false) => Some(Pull::None),
(true, false) => Some(Pull::Up),
(false, true) => Some(Pull::Down),
(true, true) => None,
}
}
pub fn into_pull_up_input(self) -> Pin<N, Input> {
let pin = self.into_input();
pin.set_pull(Pull::Up);
pin
}
pub fn into_pull_down_input(self) -> Pin<N, Input> {
let pin = self.into_input();
pin.set_pull(Pull::Down);
pin
}
pub fn into_floating_input(self) -> Pin<N, Input> {
let pin = self.into_input();
pin.set_pull(Pull::None);
pin
}
pub unsafe fn assume_mode(gpio: GPIO) -> Self {
const { Self::VALID_PIN };
Self {
_gpio: gpio,
_mode: PhantomData,
}
}
}
impl<const N: u8> embedded_hal::digital::ErrorType for Pin<N, Output> {
type Error = core::convert::Infallible;
}
impl<const N: u8> embedded_hal::digital::OutputPin for Pin<N, Output> {
fn set_low(&mut self) -> Result<(), Self::Error> {
rio_write(RIO_OUT + CLR_ALIAS, 1 << N);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
rio_write(RIO_OUT + SET_ALIAS, 1 << N);
Ok(())
}
}
impl<const N: u8> embedded_hal::digital::StatefulOutputPin for Pin<N, Output> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(rio_read(RIO_OUT) & (1 << N) != 0)
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(rio_read(RIO_OUT) & (1 << N) == 0)
}
}
impl<const N: u8> embedded_hal::digital::ErrorType for Pin<N, Input> {
type Error = core::convert::Infallible;
}
impl<const N: u8> embedded_hal::digital::InputPin for Pin<N, Input> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(read_level(N))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_level(N))
}
}
impl<const N: u8> Pin<N, Input> {
pub fn wait_for_high(&self) {
while !read_level(N) {
core::hint::spin_loop();
}
}
pub fn wait_for_low(&self) {
while read_level(N) {
core::hint::spin_loop();
}
}
}
const RIO_OUT: usize = 0x00;
const RIO_OE: usize = 0x04;
const RIO_IN: usize = 0x08;
const SET_ALIAS: usize = 0x2000;
const CLR_ALIAS: usize = 0x3000;
const CTRL: usize = 0x04;
const CTRL_FUNCSEL: u32 = 0x1f;
const CTRL_OUTOVER: u32 = 0b11 << 12;
const CTRL_OEOVER: u32 = 0b11 << 14;
const FUNCSEL_SYS_RIO: u32 = 5;
const PAD_PDE: u32 = 1 << 2;
const PAD_PUE: u32 = 1 << 3;
const PAD_IE: u32 = 1 << 6;
const PAD_OD: u32 = 1 << 7;
fn select_rio(n: u8) {
select_function(n, FUNCSEL_SYS_RIO);
}
pub(crate) fn select_function(n: u8, funcsel: u32) {
pad_modify(n, PAD_IE | PAD_OD, PAD_IE);
let ctrl = (GPIO::IO_BANK0 + 8 * n as usize + CTRL) as *mut u32;
unsafe {
let value = ctrl.read_volatile() & !(CTRL_FUNCSEL | CTRL_OUTOVER | CTRL_OEOVER);
ctrl.write_volatile(value | funcsel);
}
}
pub(crate) fn set_drive_12ma(n: u8) {
const PAD_DRIVE: u32 = 0b11 << 4;
pad_modify(n, PAD_DRIVE, PAD_DRIVE);
}
pub(crate) fn set_pull(n: u8, pull: Pull) {
let field = match pull {
Pull::None => 0,
Pull::Down => PAD_PDE,
Pull::Up => PAD_PUE,
};
pad_modify(n, PAD_PDE | PAD_PUE, field);
}
fn pad_register(n: u8) -> *mut u32 {
(GPIO::PADS_BANK0 + 4 + 4 * n as usize) as *mut u32
}
fn pad_read(n: u8) -> u32 {
unsafe { pad_register(n).read_volatile() }
}
fn pad_modify(n: u8, mask: u32, value: u32) {
let pad = pad_register(n);
unsafe { pad.write_volatile((pad.read_volatile() & !mask) | value) };
}
fn rio_write(offset: usize, value: u32) {
unsafe { ((GPIO::SYS_RIO0 + offset) as *mut u32).write_volatile(value) };
}
fn rio_read(offset: usize) -> u32 {
unsafe { ((GPIO::SYS_RIO0 + offset) as *const u32).read_volatile() }
}
fn read_level(n: u8) -> bool {
rio_read(RIO_IN) & (1 << n) != 0
}