use core::convert::Infallible;
use core::marker::PhantomData;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use crate::pac;
use crate::rcu::Rcu;
const CTL_INPUT: u32 = 0b00;
const CTL_OUTPUT: u32 = 0b01;
const CTL_AF: u32 = 0b10;
const CTL_ANALOG: u32 = 0b11;
const OMODE_PUSH_PULL: u32 = 0b0;
const OMODE_OPEN_DRAIN: u32 = 0b1;
pub struct Input;
pub struct PushPull;
pub struct OpenDrain;
pub struct Output<OTYPE> {
_otype: PhantomData<OTYPE>,
}
pub struct Analog;
pub struct Alternate<const AF: u8, OTYPE = PushPull> {
_otype: PhantomData<OTYPE>,
}
pub struct Debugger;
pub struct Locked<MODE> {
_mode: PhantomData<MODE>,
}
pub struct Pin<const P: char, const N: u8, MODE> {
_mode: PhantomData<MODE>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Port {
A,
B,
F,
}
impl Port {
const fn from_char(port: char) -> Self {
match port {
'A' => Port::A,
'B' => Port::B,
'F' => Port::F,
_ => unreachable!(),
}
}
}
pub struct ErasedPin<MODE> {
port: Port,
number: u8,
_mode: PhantomData<MODE>,
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pull {
Floating = 0b00,
Up = 0b01,
Down = 0b10,
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Speed {
Mhz2 = 0b00,
Mhz10 = 0b01,
Mhz50 = 0b11,
}
pub trait ValidAf<const AF: u8> {}
macro_rules! pin_af {
( $( $p:literal $n:literal => [ $($af:literal),* $(,)? ] ),* $(,)? ) => {
$( $( impl<MODE> ValidAf<$af> for Pin<$p, $n, MODE> {} )* )*
};
}
pin_af! {
'A' 0 => [1, 7], 'A' 1 => [0, 1], 'A' 2 => [1], 'A' 3 => [1], 'A' 4 => [0, 1, 4], 'A' 5 => [0], 'A' 6 => [0, 1, 2, 5, 6, 7], 'A' 7 => [0, 1, 2, 4, 5, 6], 'A' 8 => [0, 1, 2, 3], 'A' 9 => [1, 2, 4, 5], 'A' 10 => [0, 1, 2, 4], 'A' 11 => [0, 1, 2, 4, 7], 'A' 12 => [0, 1, 2, 4], 'A' 13 => [0, 1], 'A' 14 => [0, 1], 'A' 15 => [0, 1, 3], 'B' 0 => [0, 1, 2], 'B' 1 => [1, 2, 3], 'B' 2 => [1], 'B' 3 => [0, 1], 'B' 4 => [0, 1, 2, 4, 6], 'B' 5 => [0, 1, 2, 3], 'B' 6 => [0, 1, 2], 'B' 7 => [0, 1, 2], 'B' 8 => [1, 2], 'B' 9 => [0, 1, 2, 3, 5], 'B' 11 => [0], 'B' 12 => [1, 2], 'B' 13 => [2], 'B' 14 => [2], 'B' 15 => [2], }
#[cfg(feature = "gd32e230x4")]
pin_af! {
'B' 10 => [1], 'B' 11 => [1], 'B' 12 => [0], 'B' 13 => [0], 'B' 14 => [0], 'B' 15 => [0], }
#[cfg(any(feature = "gd32e230x6", feature = "gd32e230x8"))]
pin_af! {
'A' 8 => [4], 'B' 0 => [4], }
#[cfg(feature = "gd32e230x8")]
pin_af! {
'A' 0 => [4], 'A' 1 => [4, 5], 'A' 2 => [0], 'A' 3 => [0], 'A' 4 => [6], 'A' 9 => [0], 'A' 11 => [5, 6], 'A' 12 => [5, 6], 'A' 13 => [6], 'A' 14 => [6], 'A' 15 => [6], 'B' 1 => [6], 'B' 9 => [7], 'B' 10 => [1, 6, 7], 'B' 11 => [1, 6], 'B' 12 => [0, 4], 'B' 13 => [0, 1, 5], 'B' 14 => [0, 1, 5], 'B' 15 => [0, 1, 3], }
pub trait Active {}
impl Active for Input {}
impl Active for Analog {}
impl<const AF: u8, OTYPE> Active for Alternate<AF, OTYPE> {}
impl<OTYPE> Active for Output<OTYPE> {}
pub trait HasLock {}
impl<const N: u8, MODE> HasLock for Pin<'A', N, MODE> {}
impl<const N: u8, MODE> HasLock for Pin<'B', N, MODE> {}
impl<const P: char, const N: u8> Pin<P, N, Debugger> {
pub fn activate_into_input(self) -> Pin<P, N, Input> {
self.set_mode(CTL_INPUT);
Pin { _mode: PhantomData }
}
pub fn activate(self) -> Pin<P, N, Input> {
self.activate_into_input()
}
pub fn activate_into_push_pull_output(self) -> Pin<P, N, Output<PushPull>> {
self.set_mode(CTL_OUTPUT);
self.set_omode(OMODE_PUSH_PULL);
Pin { _mode: PhantomData }
}
pub fn activate_into_open_drain_output(self) -> Pin<P, N, Output<OpenDrain>> {
self.set_mode(CTL_OUTPUT);
self.set_omode(OMODE_OPEN_DRAIN);
Pin { _mode: PhantomData }
}
pub fn activate_into_output(self) -> Pin<P, N, Output<PushPull>> {
self.activate_into_push_pull_output()
}
pub fn activate_into_analog(self) -> Pin<P, N, Analog> {
self.set_mode(CTL_ANALOG);
Pin { _mode: PhantomData }
}
pub fn activate_into_alternate<const AF: u8>(self) -> Pin<P, N, Alternate<AF>>
where
Self: ValidAf<AF>,
{
self.set_alternate(AF as u32, OMODE_PUSH_PULL);
Pin { _mode: PhantomData }
}
pub fn activate_into_alternate_open_drain<const AF: u8>(
self,
) -> Pin<P, N, Alternate<AF, OpenDrain>>
where
Self: ValidAf<AF>,
{
self.set_alternate(AF as u32, OMODE_OPEN_DRAIN);
Pin { _mode: PhantomData }
}
}
fn reg(port: Port) -> &'static pac::gpioa::RegisterBlock {
let ptr = match port {
Port::A => pac::Gpioa::ptr(),
Port::B => pac::Gpiob::ptr() as *const _,
Port::F => pac::Gpiof::ptr() as *const _, };
unsafe { &*ptr }
}
fn read_pin(port: Port, number: u8) -> bool {
let bits = reg(port).istat().read().bits();
((bits >> number) & 0b1) == 0b1
}
fn read_octl(port: Port, number: u8) -> bool {
let bits = reg(port).octl().read().bits();
((bits >> number) & 0b1) == 0b1
}
fn set_bop(port: Port, number: u8) {
reg(port).bop().write(|w| unsafe { w.bits(1 << number) });
}
fn set_bc(port: Port, number: u8) {
reg(port).bc().write(|w| unsafe { w.bits(1 << number) });
}
fn set_tg(port: Port, number: u8) {
reg(port).tg().write(|w| unsafe { w.bits(1 << number) });
}
impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
fn reg(&self) -> &'static pac::gpioa::RegisterBlock {
reg(Port::from_char(P))
}
fn read_pin(&self) -> bool {
read_pin(Port::from_char(P), N)
}
fn read_octl(&self) -> bool {
read_octl(Port::from_char(P), N)
}
fn set_bop(&self) {
set_bop(Port::from_char(P), N);
}
fn set_bc(&self) {
set_bc(Port::from_char(P), N);
}
fn set_tg(&self) {
set_tg(Port::from_char(P), N);
}
}
impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
fn set_mode(&self, mode: u32) {
let offset = N * 2;
self.reg()
.ctl()
.modify(|r, w| unsafe { w.bits((r.bits() & !(0b11 << offset)) | (mode << offset)) });
}
fn set_af(&self, af: u32) {
let is_afsel0 = N < 8;
let offset = (N % 8) * 4;
if is_afsel0 {
self.reg().afsel0().modify(|r, w| unsafe {
w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
});
} else {
self.reg().afsel1().modify(|r, w| unsafe {
w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
});
}
}
fn set_pud(&self, bits: u32) {
let offset = N * 2;
self.reg()
.pud()
.modify(|r, w| unsafe { w.bits((r.bits() & !(0b11 << offset)) | (bits << offset)) });
}
fn set_ospd(&self, bits: u32) {
let offset = N * 2;
self.reg()
.ospd()
.modify(|r, w| unsafe { w.bits((r.bits() & !(0b11 << offset)) | (bits << offset)) });
}
fn set_omode(&self, bits: u32) {
let offset = N;
self.reg()
.omode()
.modify(|r, w| unsafe { w.bits((r.bits() & !(0b1 << offset)) | (bits << offset)) });
}
fn set_alternate(&self, af: u32, omode: u32) {
self.set_mode(CTL_AF);
self.set_af(af);
self.set_omode(omode);
}
fn set_lk(&self, lkk: bool) {
self.reg().lock().modify(|_, w| {
let w = match N {
0 => w.lk0().locked(),
1 => w.lk1().locked(),
2 => w.lk2().locked(),
3 => w.lk3().locked(),
4 => w.lk4().locked(),
5 => w.lk5().locked(),
6 => w.lk6().locked(),
7 => w.lk7().locked(),
8 => w.lk8().locked(),
9 => w.lk9().locked(),
10 => w.lk10().locked(),
11 => w.lk11().locked(),
12 => w.lk12().locked(),
13 => w.lk13().locked(),
14 => w.lk14().locked(),
15 => w.lk15().locked(),
_ => unreachable!(),
};
if lkk {
w.lkk().active()
} else {
w.lkk().not_active()
}
});
}
}
impl<const P: char, const N: u8, MODE> Pin<P, N, MODE>
where
MODE: Active,
{
pub fn into_input(self) -> Pin<P, N, Input> {
self.set_mode(CTL_INPUT);
Pin { _mode: PhantomData }
}
pub fn into_push_pull_output(self) -> Pin<P, N, Output<PushPull>> {
self.set_mode(CTL_OUTPUT);
self.set_omode(OMODE_PUSH_PULL);
Pin { _mode: PhantomData }
}
pub fn into_open_drain_output(self) -> Pin<P, N, Output<OpenDrain>> {
self.set_mode(CTL_OUTPUT);
self.set_omode(OMODE_OPEN_DRAIN);
Pin { _mode: PhantomData }
}
pub fn into_output(self) -> Pin<P, N, Output<PushPull>> {
self.into_push_pull_output()
}
pub fn into_analog(self) -> Pin<P, N, Analog> {
self.set_mode(CTL_ANALOG);
Pin { _mode: PhantomData }
}
pub fn into_alternate<const AF: u8>(self) -> Pin<P, N, Alternate<AF>>
where
Self: ValidAf<AF>,
{
self.set_alternate(AF as u32, OMODE_PUSH_PULL);
Pin { _mode: PhantomData }
}
pub fn into_alternate_open_drain<const AF: u8>(self) -> Pin<P, N, Alternate<AF, OpenDrain>>
where
Self: ValidAf<AF>,
{
self.set_alternate(AF as u32, OMODE_OPEN_DRAIN);
Pin { _mode: PhantomData }
}
pub fn lock(self) -> Pin<P, N, Locked<MODE>>
where
Self: HasLock,
{
self.set_lk(true);
self.set_lk(false);
self.set_lk(true);
for _ in 0..2 {
self.reg().lock().read();
}
Pin { _mode: PhantomData }
}
pub fn erase(self) -> ErasedPin<MODE> {
ErasedPin {
port: Port::from_char(P),
number: N,
_mode: PhantomData,
}
}
pub fn set_pull(&self, p: Pull) {
self.set_pud(p as u32);
}
pub fn set_speed(&self, s: Speed) {
self.set_ospd(s as u32);
}
}
impl<const P: char, const N: u8, OTYPE> Pin<P, N, Output<OTYPE>> {
pub fn set_high(&self) {
self.set_bop();
}
pub fn set_low(&self) {
self.set_bc();
}
pub fn toggle(&self) {
self.set_tg();
}
pub fn is_set_high(&self) -> bool {
self.read_octl()
}
pub fn is_set_low(&self) -> bool {
!self.read_octl()
}
}
impl<const P: char, const N: u8> Pin<P, N, Input> {
pub fn is_high(&self) -> bool {
self.read_pin()
}
pub fn is_low(&self) -> bool {
!self.read_pin()
}
}
impl<const P: char, const N: u8> Pin<P, N, Output<OpenDrain>> {
pub fn is_high(&self) -> bool {
self.read_pin()
}
pub fn is_low(&self) -> bool {
!self.read_pin()
}
}
impl<MODE> ErasedPin<MODE> {
pub fn port(&self) -> Port {
self.port
}
pub fn number(&self) -> u8 {
self.number
}
}
impl<OTYPE> ErasedPin<Output<OTYPE>> {
pub fn set_high(&self) {
set_bop(self.port, self.number);
}
pub fn set_low(&self) {
set_bc(self.port, self.number);
}
pub fn toggle(&self) {
set_tg(self.port, self.number);
}
pub fn is_set_high(&self) -> bool {
read_octl(self.port, self.number)
}
pub fn is_set_low(&self) -> bool {
!read_octl(self.port, self.number)
}
}
impl ErasedPin<Input> {
pub fn is_high(&self) -> bool {
read_pin(self.port, self.number)
}
pub fn is_low(&self) -> bool {
!read_pin(self.port, self.number)
}
}
impl ErasedPin<Output<OpenDrain>> {
pub fn is_high(&self) -> bool {
read_pin(self.port, self.number)
}
pub fn is_low(&self) -> bool {
!read_pin(self.port, self.number)
}
}
impl<const P: char, const N: u8, OTYPE> ErrorType for Pin<P, N, Output<OTYPE>> {
type Error = Infallible;
}
impl<const P: char, const N: u8, OTYPE> OutputPin for Pin<P, N, Output<OTYPE>> {
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_bop();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_bc();
Ok(())
}
}
impl<const P: char, const N: u8, OTYPE> StatefulOutputPin for Pin<P, N, Output<OTYPE>> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.read_octl())
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.read_octl())
}
fn toggle(&mut self) -> Result<(), Self::Error> {
self.set_tg();
Ok(())
}
}
impl<const P: char, const N: u8> ErrorType for Pin<P, N, Input> {
type Error = Infallible;
}
impl<const P: char, const N: u8> InputPin for Pin<P, N, Input> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.read_pin())
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.read_pin())
}
}
impl<const P: char, const N: u8> InputPin for Pin<P, N, Output<OpenDrain>> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.read_pin())
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.read_pin())
}
}
impl<OTYPE> ErrorType for ErasedPin<Output<OTYPE>> {
type Error = Infallible;
}
impl<OTYPE> OutputPin for ErasedPin<Output<OTYPE>> {
fn set_high(&mut self) -> Result<(), Self::Error> {
set_bop(self.port, self.number);
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
set_bc(self.port, self.number);
Ok(())
}
}
impl<OTYPE> StatefulOutputPin for ErasedPin<Output<OTYPE>> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(read_octl(self.port, self.number))
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_octl(self.port, self.number))
}
fn toggle(&mut self) -> Result<(), Self::Error> {
set_tg(self.port, self.number);
Ok(())
}
}
impl ErrorType for ErasedPin<Input> {
type Error = Infallible;
}
impl InputPin for ErasedPin<Input> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(read_pin(self.port, self.number))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_pin(self.port, self.number))
}
}
impl InputPin for ErasedPin<Output<OpenDrain>> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(read_pin(self.port, self.number))
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!read_pin(self.port, self.number))
}
}
impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: OutputPin,
{
pub fn set_high(&self) {
self.set_bop();
}
pub fn set_low(&self) {
self.set_bc();
}
}
impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: StatefulOutputPin,
{
pub fn toggle(&self) {
self.set_tg();
}
pub fn is_set_high(&self) -> bool {
self.read_octl()
}
pub fn is_set_low(&self) -> bool {
!self.read_octl()
}
}
impl<const P: char, const N: u8, MODE> Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: InputPin,
{
pub fn is_high(&self) -> bool {
self.read_pin()
}
pub fn is_low(&self) -> bool {
!self.read_pin()
}
}
impl<const P: char, const N: u8, MODE> ErrorType for Pin<P, N, Locked<MODE>> {
type Error = Infallible;
}
impl<const P: char, const N: u8, MODE> OutputPin for Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: OutputPin,
{
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_bop();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_bc();
Ok(())
}
}
impl<const P: char, const N: u8, MODE> StatefulOutputPin for Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: StatefulOutputPin,
{
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.read_octl())
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.read_octl())
}
fn toggle(&mut self) -> Result<(), Self::Error> {
self.set_tg();
Ok(())
}
}
impl<const P: char, const N: u8, MODE> InputPin for Pin<P, N, Locked<MODE>>
where
Pin<P, N, MODE>: InputPin,
{
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.read_pin())
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.read_pin())
}
}
pub trait GpioExt {
type Parts;
fn split(self, rcu: &mut Rcu) -> Self::Parts;
}
macro_rules! gpio {
($Parts:ident, $Gpio:ty, $P:literal, [ $($name:ident : $num:literal : $mode:ty),+ $(,)? ]) => {
pub struct $Parts {
$(
#[doc = concat!("Pin ", stringify!($name), ".")]
pub $name: Pin<$P, $num, $mode>,
)+
}
impl GpioExt for $Gpio {
type Parts = $Parts;
fn split(self, rcu: &mut Rcu) -> Self::Parts {
<$Gpio as crate::rcu::Enable>::enable(rcu);
$Parts { $( $name: Pin::<$P, $num, $mode> { _mode: PhantomData }, )+ }
}
}
};
}
gpio!(PartsA, pac::Gpioa, 'A',
[pa0:0:Input, pa1:1:Input, pa2:2:Input, pa3:3:Input, pa4:4:Input, pa5:5:Input, pa6:6:Input, pa7:7:Input,
pa8:8:Input, pa9:9:Input, pa10:10:Input, pa11:11:Input, pa12:12:Input, pa13:13:Debugger, pa14:14:Debugger, pa15:15:Input]);
gpio!(PartsB, pac::Gpiob, 'B',
[pb0:0:Input, pb1:1:Input, pb2:2:Input, pb3:3:Input, pb4:4:Input, pb5:5:Input, pb6:6:Input, pb7:7:Input,
pb8:8:Input, pb9:9:Input, pb10:10:Input, pb11:11:Input, pb12:12:Input, pb13:13:Input, pb14:14:Input, pb15:15:Input]);
gpio!(PartsF, pac::Gpiof, 'F', [pf0:0:Input, pf1:1:Input]);