use core::any::Any;
use ufmt::derive::uDebug;
use oxide_macros::Persist;
use crate::hal::generic::callback::IsrCallback;
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum PinMode {
Output,
InputPullup,
InputFloating
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum InterruptMode {
Disabled,
BothEdges,
RisingEdge,
FallingEdge,
LowLevel
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum PinIdentity {
PortA(u8),
PortB(u8),
PortC(u8),
PortD(u8),
PortE(u8),
PortF(u8)
}
pub type PinIsrFunction<P> = fn(&'static P,PinIdentity,bool,Option<*const dyn Any>) -> ();
pub type PinIsrCallback<P> = IsrCallback<PinIsrFunction<P>,()>;
pub trait Pin {
fn set_mode(&self, mode: PinMode);
fn toggle(&self);
fn set_high(&self);
fn set_low(&self);
fn set(&self, high: bool);
fn get(&self) -> bool;
fn set_interrupt_mode(&self, mode: InterruptMode);
fn listen(&'static self, handler: PinIsrCallback<Self>);
}
pub trait Port<P>
where
P: Pin
{
fn pin_instance(&self, pin: u8) -> &P;
}
#[cfg(target_arch="avr")]
pub mod base {
use crate::hal::generic::port::InterruptMode;
pub trait AtmelPortControl {
fn enable_output(&mut self, p: u8);
fn disable_output(&mut self, p: u8);
fn enable_pullup(&mut self, p: u8);
fn disable_pullup(&mut self, p: u8);
fn set_high(&mut self, p: u8);
fn set_low(&mut self, p: u8);
fn toggle(&mut self, p: u8);
fn get(&self, p: u8) -> bool;
fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode);
fn interrupted(&self, p: u8) -> bool;
fn clear_interrupts(&mut self);
}
impl InterruptMode {
#[inline(always)]
fn to_pinctrl_isc(&self) -> u8 {
match self {
InterruptMode::Disabled => 0x00,
InterruptMode::BothEdges => 0x01,
InterruptMode::RisingEdge => 0x02,
InterruptMode::FallingEdge => 0x03,
InterruptMode::LowLevel => 0x05
}
}
#[inline(always)]
fn pinctrl_mask() -> u8 {
0b00000111
}
}
#[repr(C)]
pub struct AvrPortRegisterBlock {
pub(crate) dir: u8,
pub(crate) dir_set: u8,
pub(crate) dir_clr: u8,
pub(crate) dir_tgl: u8,
pub(crate) out: u8,
pub(crate) out_set: u8,
pub(crate) out_clr: u8,
pub(crate) out_tgl: u8,
pub(crate) inp: u8,
pub(crate) intflags: u8,
pub(crate) portctrl: u8,
pub(crate) reserved: [u8; 5],
pub(crate) pinctrl: [u8; 8]
}
impl AtmelPortControl for AvrPortRegisterBlock {
#[inline(always)]
fn enable_output(&mut self, p: u8) {
unsafe {
core::ptr::write_volatile(&mut self.dir_set as *mut u8, 0x01 << p);
}
}
#[inline(always)]
fn disable_output(&mut self, p: u8) {
unsafe {
core::ptr::write_volatile(&mut self.dir_clr as *mut u8, 0x01 << p);
}
}
fn enable_pullup(&mut self, p: u8) {
unsafe {
let pinctrl_reg = &mut self.pinctrl[p as usize] as *mut u8;
let pinctrl = core::ptr::read_volatile(pinctrl_reg);
core::ptr::write_volatile(pinctrl_reg, pinctrl | 0b00001000);
}
}
fn disable_pullup(&mut self, p: u8) {
unsafe {
let pinctrl_reg = &mut self.pinctrl[p as usize] as *mut u8;
let pinctrl = core::ptr::read_volatile(pinctrl_reg);
core::ptr::write_volatile(pinctrl_reg, pinctrl & 0b11110111);
}
}
#[inline(always)]
fn set_high(&mut self, p: u8) {
unsafe {
core::ptr::write_volatile(&mut self.out_set as *mut u8, 0x01 << p);
}
}
#[inline(always)]
fn set_low(&mut self, p: u8) {
unsafe {
core::ptr::write_volatile(&mut self.out_clr as *mut u8, 0x01 << p);
}
}
#[inline(always)]
fn toggle(&mut self, p: u8) {
unsafe {
core::ptr::write_volatile(&mut self.out_tgl as *mut u8, 0x01 << p);
}
}
#[inline(always)]
fn get(&self, p: u8) -> bool {
unsafe {
core::ptr::read_volatile(&self.inp as *const u8) & (0x01 << p) != 0
}
}
fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode) {
unsafe {
let pinctrl_reg = &mut self.pinctrl[p as usize] as *mut u8;
let pinctrl = core::ptr::read_volatile(pinctrl_reg);
core::ptr::write_volatile(pinctrl_reg,
(pinctrl & !InterruptMode::pinctrl_mask()) | mode.to_pinctrl_isc());
}
}
#[inline(always)]
fn interrupted(&self, p: u8) -> bool {
unsafe {
core::ptr::read_volatile(&self.intflags as *const u8) & (0x01 << p) != 0
}
}
#[inline(always)]
fn clear_interrupts(&mut self) {
unsafe {
core::ptr::write_volatile(&mut self.intflags as *mut u8, 0xff);
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_port_tpl {
($ref:expr, $portsrc:expr, $isr:ident) => {
use core::any::Any;
use crate::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
use crate::hal::generic::port::base::{AtmelPortControl,AvrPortRegisterBlock};
use crate::isr_cb_invoke;
pub type PortImpl = AvrPortRegisterBlock;
pub type PinImpl = AtmelPin;
pub struct AtmelPin {
n: u8,
handler: PinIsrCallback<Self>
}
impl Pin for AtmelPin {
#[inline(always)]
fn set_mode(&self, mode: PinMode) {
match mode {
PinMode::Output => {
instance().enable_output(self.n);
},
PinMode::InputPullup => {
instance().enable_pullup(self.n);
instance().disable_output(self.n);
},
PinMode::InputFloating => {
instance().disable_pullup(self.n);
instance().disable_output(self.n);
}
}
}
#[inline(always)]
fn toggle(&self) {
instance().toggle(self.n);
}
#[inline(always)]
fn set_high(&self) {
instance().set_high(self.n);
}
#[inline(always)]
fn set_low(&self) {
instance().set_low(self.n);
}
#[inline(always)]
fn set(&self, high: bool) {
match high {
true => instance().set_high(self.n),
false => instance().set_low(self.n)
}
}
#[inline(always)]
fn get(&self) -> bool {
instance().get(self.n)
}
fn set_interrupt_mode(&self, mode: InterruptMode) {
instance().set_interrupt_mode(self.n, mode)
}
fn listen(&self, handler: PinIsrCallback<Self>){
crate::hal::concurrency::interrupt::isolated(||{
unsafe {
PINS[self.n as usize].handler = handler;
}
});
}
}
static mut INITIALISED: bool = false;
#[inline(always)]
pub fn instance() -> &'static mut AvrPortRegisterBlock {
unsafe {
if(!INITIALISED){
for pin in 0..=7 {
PINS[pin].n = pin as u8;
PINS[pin].handler = PinIsrCallback::Nop(());
}
INITIALISED = true;
}
core::mem::transmute($ref)
}
}
static mut PINS : [AtmelPin; 8] = [
AtmelPin { n: 0, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 1, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 2, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 3, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 4, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 5, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 6, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 7, handler: PinIsrCallback::Nop(()) },
];
pub fn pin_instance(pin: u8) -> &'static mut AtmelPin {
unsafe {
&mut PINS[pin as usize]
}
}
#[no_mangle]
pub unsafe extern "avr-interrupt" fn $isr() {
crate::hal::concurrency::interrupt::isr(||{
let intflags:u8 = core::ptr::read_volatile(&instance().intflags as *const u8);
let state:u8 = core::ptr::read_volatile(&instance().inp as *const u8);
let mut mask = 0x01;
for pin in 0..=7 {
if (intflags & mask) > 0 {
isr_cb_invoke!(PINS[pin].handler, &mut PINS[pin], $portsrc(pin as u8), (state & mask) > 0);
}
mask <<= 1;
}
instance().clear_interrupts();
})
}
}
}
}
#[cfg(not(target_arch="avr"))]
pub mod base {
use crate::hal::generic::port::InterruptMode;
pub trait AtmelPortControl {
fn enable_output(&mut self, p: u8);
fn disable_output(&mut self, p: u8);
fn enable_pullup(&mut self, p: u8);
fn disable_pullup(&mut self, p: u8);
fn set_high(&mut self, p: u8);
fn set_low(&mut self, p: u8);
fn toggle(&mut self, p: u8);
fn get(&self, p: u8) -> bool;
fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode);
fn interrupted(&self, p: u8) -> bool;
fn clear_interrupts(&mut self);
}
#[repr(C)]
pub struct DummyPortRegisterBlock {
pub(crate) pin: [bool; 8],
pub(crate) o_en: [bool; 8],
pub(crate) pu_en: [bool; 8]
}
impl AtmelPortControl for DummyPortRegisterBlock {
fn enable_output(&mut self, p: u8) {
println!("*** PORT: Enable output pin {}", p);
self.o_en[p as usize] = true;
}
fn disable_output(&mut self, p: u8) {
println!("*** PORT: Disable output pin {}", p);
self.o_en[p as usize] = false;
}
fn enable_pullup(&mut self, p: u8) {
println!("*** PORT: Enable pullup pin {}", p);
self.pu_en[p as usize] = true;
}
fn disable_pullup(&mut self, p: u8) {
println!("*** PORT: Disable pullup pin {}", p);
self.pu_en[p as usize] = false;
}
fn set_high(&mut self, p: u8) {
println!("*** PORT: Set HIGH pin {}", p);
self.pin[p as usize] = true;
}
fn set_low(&mut self, p: u8) {
println!("*** PORT: Set LOW pin {}", p);
self.pin[p as usize] = false;
}
fn toggle(&mut self, p: u8) {
let old = self.pin[p as usize];
println!("*** PORT: TOGGLE pin {} ({} => {})", p, old, !old);
self.pin[p as usize] = !old;
}
fn get(&self, p: u8) -> bool {
let val = self.pin[p as usize];
println!("*** PORT: GET pin {} ({})", p, val);
val
}
fn set_interrupt_mode(&mut self, p: u8, _mode: InterruptMode) {
println!("*** PORT: Set interrupt mode pin {}", p);
}
fn interrupted(&self, p: u8) -> bool {
println!("*** PORT: Get interrupt flag pin {}", p);
false
}
fn clear_interrupts(&mut self) {
println!("*** PORT: Clear all interrupts");
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_port_tpl {
($ref:expr, $portsrc:expr, $isr:ident) => {
use core::any::Any;
use crate::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
use crate::hal::generic::port::base::{AtmelPortControl,DummyPortRegisterBlock};
pub type PortImpl = DummyPortRegisterBlock;
pub type PinImpl = AtmelPin;
pub struct AtmelPin {
n: u8,
handler: PinIsrCallback<Self>
}
impl Pin for AtmelPin {
#[inline(always)]
fn set_mode(&self, mode: PinMode) {
match mode {
PinMode::Output => {
instance().enable_output(self.n);
},
PinMode::InputPullup => {
instance().enable_pullup(self.n);
instance().disable_output(self.n);
},
PinMode::InputFloating => {
instance().disable_pullup(self.n);
instance().disable_output(self.n);
}
}
}
#[inline(always)]
fn toggle(&self) {
instance().toggle(self.n);
}
#[inline(always)]
fn set_high(&self) {
instance().set_high(self.n);
}
#[inline(always)]
fn set_low(&self) {
instance().set_low(self.n);
}
#[inline(always)]
fn set(&self, high: bool) {
match high {
true => instance().set_high(self.n),
false => instance().set_low(self.n)
}
}
#[inline(always)]
fn get(&self) -> bool {
instance().get(self.n)
}
fn set_interrupt_mode(&self, mode: InterruptMode) {
instance().set_interrupt_mode(self.n, mode)
}
fn listen(&self, handler: PinIsrCallback<Self>){
crate::hal::concurrency::interrupt::isolated(||{
unsafe {
PINS[self.n as usize].handler = handler;
}
});
}
}
static mut INITIALISED: bool = false;
static mut INSTANCE : DummyPortRegisterBlock = DummyPortRegisterBlock {
pin: [false; 8],
o_en: [false; 8],
pu_en: [false; 8]
};
#[inline(always)]
pub fn instance() -> &'static mut DummyPortRegisterBlock {
unsafe {
if(!INITIALISED){
for pin in 0..=7 {
PINS[pin].n = pin as u8;
PINS[pin].handler = PinIsrCallback::Nop(());
}
INITIALISED = true;
}
&mut INSTANCE
}
}
static mut PINS : [AtmelPin; 8] = [
AtmelPin { n: 0, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 1, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 2, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 3, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 4, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 5, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 6, handler: PinIsrCallback::Nop(()) },
AtmelPin { n: 7, handler: PinIsrCallback::Nop(()) },
];
pub fn pin_instance(pin: u8) -> &'static mut AtmelPin {
unsafe {
&mut PINS[pin as usize]
}
}
}
}
}