use crate::pac::GPIO;
use core::marker::PhantomData;
#[cfg(feature = "async")]
mod asynch;
#[cfg(feature = "async")]
pub use asynch::on_irq;
pub struct Output;
pub struct Input;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Pull {
Up,
Down,
None,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Trigger {
RisingEdge,
FallingEdge,
AnyEdge,
HighLevel,
LowLevel,
AsyncRisingEdge,
AsyncFallingEdge,
AsyncAnyEdge,
}
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,
_mode: PhantomData,
}
}
}
impl<const N: u8, MODE> Pin<N, MODE> {
const VALID_PIN: () = assert!(N <= 53, "BCM2836/2837 only has GPIO pins 0..=53");
pub fn into_output(self) -> Pin<N, Output> {
set_fsel(&self.gpio, N, FSEL_OUTPUT);
Pin {
gpio: self.gpio,
_mode: PhantomData,
}
}
pub fn into_input(self) -> Pin<N, Input> {
set_fsel(&self.gpio, N, FSEL_INPUT);
Pin {
gpio: self.gpio,
_mode: PhantomData,
}
}
pub fn set_pull(&self, pull: Pull) {
set_pull(&self.gpio, N, pull);
}
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
}
#[cfg(feature = "bcm2711")]
pub fn pull(&self) -> Option<Pull> {
read_pull(&self.gpio, N)
}
pub unsafe fn assume_mode(gpio: GPIO) -> Self {
const { Self::VALID_PIN };
Self {
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> {
set_level(&self.gpio, N, false);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
set_level(&self.gpio, N, true);
Ok(())
}
}
impl<const N: u8> embedded_hal::digital::StatefulOutputPin for Pin<N, Output> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(read_level(&self.gpio, N))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_level(&self.gpio, N))
}
}
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(&self.gpio, N))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_level(&self.gpio, N))
}
}
impl<const N: u8> Pin<N, Input> {
pub fn enable_interrupt(&self, trigger: Trigger) {
set_trigger(&self.gpio, N, Some(trigger));
}
pub fn disable_interrupt(&self) {
set_trigger(&self.gpio, N, None);
}
pub fn clear_interrupt(&self) {
clear_event(&self.gpio, N);
}
pub fn is_interrupt_pending(&self) -> bool {
event_detected(&self.gpio, N)
}
pub fn wait_for_high(&self) {
while !read_level(&self.gpio, N) {
core::hint::spin_loop();
}
}
pub fn wait_for_low(&self) {
while read_level(&self.gpio, N) {
core::hint::spin_loop();
}
}
}
const FSEL_INPUT: u32 = 0b000;
const FSEL_OUTPUT: u32 = 0b001;
fn set_fsel(gpio: &GPIO, pin: u8, value: u32) {
let shift = ((pin % 10) as u32) * 3;
let mask = !(0b111u32 << shift);
macro_rules! read_modify_write {
($reg:ident) => {{
let current = gpio.$reg().read().bits();
let new = (current & mask) | (value << shift);
unsafe { gpio.$reg().write_with_zero(|w| w.bits(new)) };
}};
}
match pin / 10 {
0 => read_modify_write!(gpfsel0),
1 => read_modify_write!(gpfsel1),
2 => read_modify_write!(gpfsel2),
3 => read_modify_write!(gpfsel3),
4 => read_modify_write!(gpfsel4),
_ => read_modify_write!(gpfsel5),
}
}
pub(crate) fn set_pull(gpio: &GPIO, pin: u8, pull: Pull) {
set_pull_bank(gpio, pin / 32, 1 << (pin % 32), pull);
}
#[cfg(not(feature = "bcm2711"))]
pub(crate) fn set_pull_bank(_gpio: &GPIO, bank: u8, mask: u32, pull: Pull) {
const GPPUD: usize = 0x94;
const GPPUDCLK0: usize = 0x98;
let base = crate::pac::GPIO::PTR as usize;
let gppud = (base + GPPUD) as *mut u32;
let gppudclk = (base + GPPUDCLK0 + 4 * bank as usize) as *mut u32;
let pud = match pull {
Pull::None => 0,
Pull::Down => 1,
Pull::Up => 2,
};
unsafe {
core::ptr::write_volatile(gppud, pud);
spin_delay(150);
core::ptr::write_volatile(gppudclk, mask);
spin_delay(150);
core::ptr::write_volatile(gppud, 0);
core::ptr::write_volatile(gppudclk, 0);
}
}
#[cfg(feature = "bcm2711")]
pub(crate) fn set_pull_bank(gpio: &GPIO, bank: u8, mask: u32, pull: Pull) {
for bit in 0..32u8 {
if mask & (1 << bit) != 0 {
set_pull_2711(gpio, bank * 32 + bit, pull);
}
}
}
#[cfg(feature = "bcm2711")]
fn set_pull_2711(gpio: &GPIO, pin: u8, pull: Pull) {
let value: u32 = match pull {
Pull::None => 0,
Pull::Up => 1,
Pull::Down => 2,
};
let shift = ((pin % 16) as u32) * 2;
let mask = !(0b11u32 << shift);
macro_rules! read_modify_write {
($reg:ident) => {{
let current = gpio.$reg().read().bits();
let new = (current & mask) | (value << shift);
unsafe { gpio.$reg().write_with_zero(|w| w.bits(new)) };
}};
}
match pin / 16 {
0 => read_modify_write!(gpio_pup_pdn_cntrl_reg0),
1 => read_modify_write!(gpio_pup_pdn_cntrl_reg1),
2 => read_modify_write!(gpio_pup_pdn_cntrl_reg2),
_ => read_modify_write!(gpio_pup_pdn_cntrl_reg3),
}
}
#[cfg(feature = "bcm2711")]
fn read_pull(gpio: &GPIO, pin: u8) -> Option<Pull> {
let shift = ((pin % 16) as u32) * 2;
let bits = match pin / 16 {
0 => gpio.gpio_pup_pdn_cntrl_reg0().read().bits(),
1 => gpio.gpio_pup_pdn_cntrl_reg1().read().bits(),
2 => gpio.gpio_pup_pdn_cntrl_reg2().read().bits(),
_ => gpio.gpio_pup_pdn_cntrl_reg3().read().bits(),
};
match (bits >> shift) & 0b11 {
0 => Some(Pull::None),
1 => Some(Pull::Up),
2 => Some(Pull::Down),
_ => None,
}
}
#[cfg(not(feature = "bcm2711"))]
fn spin_delay(cycles: u32) {
for _ in 0..cycles {
unsafe { core::arch::asm!("nop") };
}
}
fn set_level(gpio: &GPIO, pin: u8, high: bool) {
let bit = 1u32 << (pin % 32);
if pin < 32 {
if high {
unsafe { gpio.gpset0().write_with_zero(|w| w.bits(bit)) };
} else {
unsafe { gpio.gpclr0().write_with_zero(|w| w.bits(bit)) };
}
} else if high {
unsafe { gpio.gpset1().write_with_zero(|w| w.bits(bit)) };
} else {
unsafe { gpio.gpclr1().write_with_zero(|w| w.bits(bit)) };
}
}
fn read_level(gpio: &GPIO, pin: u8) -> bool {
let bit = 1u32 << (pin % 32);
let bits = if pin < 32 {
gpio.gplev0().read().bits()
} else {
gpio.gplev1().read().bits()
};
bits & bit != 0
}
fn set_trigger(gpio: &GPIO, pin: u8, trigger: Option<Trigger>) {
let bit = 1u32 << (pin % 32);
macro_rules! set_bit_in {
($reg0:ident, $reg1:ident, $on:expr) => {{
macro_rules! rmw {
($reg:ident) => {{
let current = gpio.$reg().read().bits();
let new = if $on { current | bit } else { current & !bit };
unsafe { gpio.$reg().write_with_zero(|w| w.bits(new)) };
}};
}
if pin < 32 {
rmw!($reg0)
} else {
rmw!($reg1)
}
}};
}
use Trigger::*;
set_bit_in!(
gpren0,
gpren1,
matches!(trigger, Some(RisingEdge | AnyEdge))
);
set_bit_in!(
gpfen0,
gpfen1,
matches!(trigger, Some(FallingEdge | AnyEdge))
);
set_bit_in!(gphen0, gphen1, matches!(trigger, Some(HighLevel)));
set_bit_in!(gplen0, gplen1, matches!(trigger, Some(LowLevel)));
set_bit_in!(
gparen0,
gparen1,
matches!(trigger, Some(AsyncRisingEdge | AsyncAnyEdge))
);
set_bit_in!(
gpafen0,
gpafen1,
matches!(trigger, Some(AsyncFallingEdge | AsyncAnyEdge))
);
}
fn clear_event(gpio: &GPIO, pin: u8) {
let bit = 1u32 << (pin % 32);
if pin < 32 {
unsafe { gpio.gpeds0().write_with_zero(|w| w.bits(bit)) };
} else {
unsafe { gpio.gpeds1().write_with_zero(|w| w.bits(bit)) };
}
}
fn event_detected(gpio: &GPIO, pin: u8) -> bool {
let bit = 1u32 << (pin % 32);
let bits = if pin < 32 {
gpio.gpeds0().read().bits()
} else {
gpio.gpeds1().read().bits()
};
bits & bit != 0
}