teensy-lc-macros 0.1.1

Procedural macros for teensy-lc crate
Documentation
extern crate proc_macro;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::parse_macro_input;

#[proc_macro]
pub fn fgpio(item: TokenStream) -> TokenStream {
    let name = parse_macro_input!(item as syn::Ident).to_string();
    let name_uc = name.to_uppercase();

    let fgpio = format_ident!("fgpio{}", name);
    let fgpio_uc = format_ident!("FGPIO{}", name_uc);
    let port = format_ident!("PORT{}", name_uc);

    let pin_fields = (0..32 as usize).map(|i| {
        let pxi = format_ident!("p{}{}", name, i);
        let pxi_uc = format_ident!("P{}{}", name_uc, i);
        quote! { pub #pxi: #pxi_uc<IN> }
    });

    let pin_init = (0..32 as usize).map(|i| {
        let pxi = format_ident!("p{}{}", name, i);
        let pxi_uc = format_ident!("P{}{}", name_uc, i);
        quote! { #pxi: #pxi_uc::<IN>{_mode: PhantomData} }
    });

    let pin = (0..32 as usize).map(|i| {
        let pxi_uc = format_ident!("P{}{}", name_uc, i);
        let pcr = format_ident!("pcr{}", i);
        quote! {
            pub struct #pxi_uc<MODE> {
                _mode: PhantomData<MODE>
            }

            impl<MODE> #pxi_uc<MODE> {
                pub fn enable(&self) {
                    unsafe { &(*#port::ptr()) }.#pcr.write(|w| w.mux()._001());
                }

                pub fn into_input(self) -> #pxi_uc<IN> {
                    self.regblock().pddr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << #i)) });
                    #pxi_uc{_mode: PhantomData}
                }

                pub fn into_output(self) -> #pxi_uc<OUT> {
                    self.regblock().pddr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << #i)) });
                    #pxi_uc{_mode: PhantomData}
                }

                fn regblock(&self) -> &#fgpio::RegisterBlock {
                    unsafe { &(*#fgpio_uc::ptr()) }
                }

                fn value(&self) -> u32 {
                    self.regblock().pdir.read().pdi().bits() & (1 << #i)
                }
            }

            impl InputPin for #pxi_uc<IN> {
                type Error = Void;

                fn is_high(&self) -> Result<bool, Self::Error> {
                    Ok(self.value() != 0)
                }

                fn is_low(&self) -> Result<bool, Self::Error> {
                    Ok(self.value() == 0)
                }
            }

            impl OutputPin for #pxi_uc<OUT> {
                type Error = Void;

                fn set_low(&mut self) -> Result<(), Self::Error> {
                    self.regblock().pcor.write(|w| unsafe { w.bits(1<<#i) });
                    Ok(())
                }

                fn set_high(&mut self) -> Result<(), Self::Error> {
                    self.regblock().psor.write(|w| unsafe { w.bits(1<<#i) });
                    Ok(())
                }
            }

            impl #pxi_uc<OUT> {
                pub fn toggle(&mut self) {
                    self.regblock().ptor.write(|w| unsafe { w.bits(1<<#i) });
                }
            }
        }
    });

    let result = quote! {
        pub mod #fgpio {
            use core::marker::PhantomData;
            use embedded_hal::digital::v2::{InputPin, OutputPin};
            use void::Void;
            use crate::dev::{#fgpio_uc, #port, #fgpio};
            use super::{FgpioExt, IN, OUT};

            pub struct Parts {
                pub #fgpio: #fgpio_uc,
                #(#pin_fields,)*
            }

            impl Parts {
                pub fn into_fgpio(self) -> #fgpio_uc {
                    self.#fgpio
                }
            }

            impl FgpioExt for #fgpio_uc {
                type Parts = Parts;
                fn split(self) -> Parts {
                    Parts {
                        #fgpio: self,
                        #(#pin_init,)*
                    }
                }
            }

            #(#pin)*
        }
    };

    TokenStream::from(result)
}