kea-hal 0.5.0

HAL for NXP KEA devices.
Documentation
//! Port peripheral module.
//!
//! This module contains only the IO Filter part of the port peripheral.
//! The high current driver and pull-up resist peripherals are implemented as
//! part of the GPIO module.

use crate::pac::PORT;
use core::marker::PhantomData;

/// Default Divisor for each port
pub type DefaultDiv = DivNone;

/// No Divisor (Filter runs a busclk, or disabled)
pub struct DivNone;

/// Use Divisor 1
pub struct Div1;

/// Use Divisor 2
pub struct Div2;

/// Use Divisor 3
pub struct Div3;

/// A struct that represents one of the divisors in the filter module
pub struct Divisor<Offset, Size> {
    _offset: PhantomData<Offset>,
    _size: PhantomData<Size>,
}

fn set_port_divisor(divisor: u32, offset: usize) {
    unsafe {
        let port = &(*PORT::ptr());
        port.ioflt
            .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (divisor << offset)));
    }
}

macro_rules! port_filter {
    ([ $($PORT:ident: ($port:ident, $PORTOffset:expr),)+ ],
     [ $($DIV:ident: ($div:ident, $DIVOffset:expr, $DIVSize:expr), )+ ]) => {
        /// Input Filter control
        ///
        /// per 11.4 of KEA64 ref man, filter acts as lowpass with adjustable
        /// timing. holds the pin in previous state until new state exists for
        /// longer than adjustable time. This feature is effectively a lowpass
        /// filter.

        // @TODO This should probably have some knowledge of the clocks involved
        // in order to abstract the divisors into a cut-off frequency or
        // something.
        // FLTDIV3 -> LPOCLK, FLTDIV2 -> BUSCLK, FLTDIV1 -> BUSCLK
        pub struct Filter {
            $(
                /// Divisor Controller
                pub $div: $DIV,
            )+
            $(
                /// Filterable Port
                pub $port: $PORT<DefaultDiv>,
            )+
        }

        impl Filter {
            /// Get the Filter interface.
            pub fn get() -> Self {
                Filter {
                    $(
                        $div: $DIV {},
                    )+
                    $(
                        $port: $PORT { _div: PhantomData },
                    )+
                }
            }
        }

        $(
            /// Divisor Controller
            pub struct $DIV;


            impl $DIV {

                /// Sets the divisor the filtered port.
                ///
                /// See the PORT_IOFLT documentation in the manual for values.
                /// @TODO Use (cutoff) frequency or the actual value of the
                /// divisor. In either case match it to one of the available
                /// hardware divisors
                pub fn set_divisor(self, divisor: u8) {
                    assert!(divisor & !($DIVSize) == 0);
                    unsafe {
                        (*PORT::ptr()).ioflt.modify(|r, w| {
                            w.bits((r.bits() & !($DIVSize << $DIVOffset)) | ((divisor as u32) << $DIVOffset))
                        })
                    }
                }
            }
        )+
        $(
            /// A Filterable Port type
            pub struct $PORT<DIV> {
                _div: PhantomData<DIV>,
            }

            impl<DIV> $PORT<DIV> {
                /// Configure $PORT to use no divisor (or disable filter)
                pub fn into_no_div(self) -> $PORT<DivNone> {
                    set_port_divisor(0b00, $PORTOffset);
                    $PORT {_div: PhantomData}
                }

                /// Configure $PORT to use Div1
                pub fn into_div1(self) -> $PORT<Div1> {
                    set_port_divisor(0b01, $PORTOffset);
                    $PORT {_div: PhantomData}
                }

                /// Configure $PORT to use Div2
                pub fn into_div2(self) -> $PORT<Div2> {
                    set_port_divisor(0b10, $PORTOffset);
                    $PORT {_div: PhantomData}
                }

                /// Configure $PORT to use Div3
                pub fn into_div3(self) -> $PORT<Div3> {
                    set_port_divisor(0b11, $PORTOffset);
                    $PORT {_div: PhantomData}
                }
            }
        )+
    }
}

port_filter!([
    PORTA: (porta, 0),
    PORTB: (portb, 2),
    PORTC: (portc, 4),
    PORTD: (portd, 8),
    PORTE: (porte, 10),
    PORTF: (portf, 12),
    PORTG: (portg, 14),
], [
    DIV1: (div1, 29, 7),
    DIV2: (div2, 26, 7),
    DIV3: (div3, 24, 3),
]);