use core::any::Any;
use ufmt::derive::uDebug;
use avr_oxide::concurrency::Isolated;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;
use avr_oxide::hal::generic::port::base::AtmelPortControl;
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum PinMode {
Output,
InputPullup,
InputFloating,
Input
}
#[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 {
#[cfg(feature = "porta")]
PortA(u8),
#[cfg(feature = "portb")]
PortB(u8),
#[cfg(feature = "portc")]
PortC(u8),
#[cfg(feature = "portd")]
PortD(u8),
#[cfg(feature = "porte")]
PortE(u8),
#[cfg(feature = "portf")]
PortF(u8)
}
#[repr(transparent)]
pub struct ProxyPin(u8);
impl ProxyPin {
pub(crate) const fn for_port_and_pin(port: u8, pin: u8) -> Self {
Self(port << 3 | pin)
}
pub(crate) const fn port_number(&self) -> u8 {
self.0 >> 3
}
pub(crate) const fn pin_number(&self) -> u8 {
self.0 & 0b0000_0111
}
pub(crate) const fn to_index(&self) -> usize {
self.0 as usize
}
}
pub trait PinToPort {
fn get_port(&self) -> &'static mut dyn AtmelPortControl;
}
pub type PinIsrFunction = fn(Isolated,&'static dyn Pin,PinIdentity,bool,Option<*const dyn Any>) -> ();
pub type PinIsrCallback = IsrCallback<PinIsrFunction,()>;
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);
}
pub mod handlers {
use avr_oxide::alloc::vec::Vec;
use avr_oxide::hal::generic::port::{Pin, ProxyPin, PinIsrCallback};
use avr_oxide::{isr_cb_invoke, mut_singleton};
use avr_oxide::deviceconsts::oxide::PIN_ARRAY_SIZE;
use avr_oxide::concurrency::Isolated;
pub struct PinHandlerSet {
indexes: [u8; PIN_ARRAY_SIZE],
handlers: Vec<PinIsrCallback>
}
mut_singleton!(
PinHandlerSet,
__INSTANCE,
instance, instance_isolated,
PinHandlerSet {
indexes: [0x00; PIN_ARRAY_SIZE],
handlers: Vec::with_capacity(4)
});
impl PinHandlerSet {
pub(crate) fn set_handler_for_pin(&mut self, _isotoken: Isolated, pin: ProxyPin, handler: PinIsrCallback){
let vec_idx = self.indexes[pin.to_index()];
if vec_idx > 0 {
self.handlers[(vec_idx-1) as usize] = handler;
} else {
self.handlers.push(handler);
self.indexes[pin.to_index()] = self.handlers.len() as u8;
}
}
pub(crate) fn invoke_handler_for_pin(&self, isotoken: Isolated, pin: ProxyPin, source: &'static dyn Pin, state: bool) {
let vec_idx = self.indexes[pin.to_index()];
if vec_idx > 0 {
isr_cb_invoke!(isotoken, self.handlers[(vec_idx-1) as usize], source, pin.into(), state);
}
}
}
}
#[cfg(target_arch="avr")]
pub mod base {
use avr_oxide::concurrency::Isolated;
use avr_oxide::hal::generic::port::{InterruptMode, Pin, ProxyPin, PinIsrCallback, PinMode, PinToPort};
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 isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin);
fn listen(&'static self, p: u8, handler: PinIsrCallback);
}
impl Pin for ProxyPin
where
ProxyPin: PinToPort
{
fn set_mode(&self, mode: PinMode) {
let pin = self.pin_number();
match mode {
PinMode::Output => {
self.get_port().enable_output(pin);
},
PinMode::InputPullup => {
self.get_port().enable_pullup(pin);
self.get_port().disable_output(pin);
},
PinMode::InputFloating => {
self.get_port().disable_pullup(pin);
self.get_port().disable_output(pin);
},
PinMode::Input => {
self.get_port().disable_output(pin);
}
}
}
fn toggle(&self) {
self.get_port().toggle(self.pin_number());
}
fn set_high(&self) {
self.get_port().set_high(self.pin_number());
}
fn set_low(&self) {
self.get_port().set_low(self.pin_number());
}
fn set(&self, high: bool) {
match high {
true => self.get_port().set_high(self.pin_number()),
false => self.get_port().set_low(self.pin_number())
}
}
fn get(&self) -> bool {
self.get_port().get(self.pin_number())
}
fn set_interrupt_mode(&self, mode: InterruptMode) {
self.get_port().set_interrupt_mode(self.pin_number(), mode)
}
fn listen(&self, handler: PinIsrCallback){
self.get_port().listen(self.pin_number(),handler)
}
}
pub mod zeroseries {
use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, ProxyPin, PinIsrCallback};
use avr_oxide::concurrency::Isolated;
use avr_oxide::hal::generic::port::base::{AtmelPortControl};
use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};
impl InterruptMode {
fn to_pinctrl_isc(&self) -> u8 {
match self {
InterruptMode::Disabled => 0x00,
InterruptMode::BothEdges => 0x01,
InterruptMode::RisingEdge => 0x02,
InterruptMode::FallingEdge => 0x03,
InterruptMode::LowLevel => 0x05
}
}
fn pinctrl_mask() -> u8 {
0b00000111
}
}
#[repr(C)]
pub struct PortRegisterBlock {
pub(crate) dir: VolatileBitField,
pub(crate) dir_set: VolatileBitField,
pub(crate) dir_clr: VolatileBitField,
pub(crate) dir_tgl: VolatileBitField,
pub(crate) out: VolatileBitField,
pub(crate) out_set: VolatileBitField,
pub(crate) out_clr: VolatileBitField,
pub(crate) out_tgl: VolatileBitField,
pub(crate) inp: VolatileBitField,
pub(crate) intflags: VolatileBitField,
pub(crate) portctrl: VolatileBitField,
pub(crate) reserved: [u8; 5],
pub(crate) pin_ctl: [Volatile<u8>; 8]
}
impl PortRegisterBlock {
#[cfg(feature="atmega4809")]
fn get_port_num(&self) -> u8 {
(self as *const PortRegisterBlock as u8) / 0x20
}
#[cfg(not(any(feature="atmega4809")))]
fn get_port_num(&self) -> u8 {
unimplemented!()
}
}
impl AtmelPortControl for PortRegisterBlock {
fn enable_output(&mut self, p: u8) {
self.dir_set.exc_set(BitIndex::bit(p as usize));
}
fn disable_output(&mut self, p: u8) {
self.dir_clr.exc_set(BitIndex::bit(p as usize));
}
fn enable_pullup(&mut self, p: u8) {
self.pin_ctl[p as usize] |= 0b00001000;
}
fn disable_pullup(&mut self, p: u8) {
self.pin_ctl[p as usize] &= 0b11110111;
}
fn set_high(&mut self, p: u8) {
self.out_set.exc_set(BitIndex::bit(p as usize));
}
fn set_low(&mut self, p: u8) {
self.out_clr.exc_set(BitIndex::bit(p as usize));
}
fn toggle(&mut self, p: u8) {
self.out_tgl.exc_set(BitIndex::bit(p as usize));
}
fn get(&self, p: u8) -> bool {
self.inp.is_set(BitIndex::bit(p as usize))
}
fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode) {
self.pin_ctl[p as usize] &= !InterruptMode::pinctrl_mask();
self.pin_ctl[p as usize] |= mode.to_pinctrl_isc();
}
fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
let pins_interrupted = self.intflags.snapshot();
let pins_state = self.inp.snapshot();
for pin in 0u8..7u8 {
if pins_interrupted.is_set(BitIndex::bit(pin as usize)) {
let source = get_pin(pin);
handlers::instance_isolated(isotoken).invoke_handler_for_pin(isotoken,
ProxyPin::for_port_and_pin(self.get_port_num() as u8, pin),
source,
pins_state.is_set(BitIndex::bit(pin as usize)));
}
}
self.intflags.set_all();
}
fn listen(&'static self, p: u8, handler: PinIsrCallback) {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
handlers::instance_isolated(isotoken).set_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(self.get_port_num() as u8, p), handler);
})
}
}
}
pub mod simple {
use avr_oxide::hal::generic::port::base::{AtmelPortControl};
use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, PinIsrCallback, ProxyPin};
use avr_oxide::concurrency::Isolated;
use avr_oxide::util::datatypes::{VolatileBitField, BitFieldAccess, BitIndex};
#[repr(C)]
pub struct AvrPortRegisterBlock<const ADDR: u16,const PCIEN: u16, const PCICR: u16, const PCIFR: u16, const PORTNUM: usize> {
pub(crate) pin: VolatileBitField,
pub(crate) ddr: VolatileBitField,
pub(crate) port: VolatileBitField,
}
impl<const ADDR: u16,const PCIEN: u16, const PCICR: u16, const PCIFR:u16, const PORTNUM: usize> AtmelPortControl for AvrPortRegisterBlock<ADDR,PCIEN,PCICR,PCIFR,PORTNUM> {
fn enable_output(&mut self, p: u8) {
self.ddr.set(BitIndex::bit(p as usize));
}
fn disable_output(&mut self, p: u8) {
self.ddr.clr(BitIndex::bit(p as usize));
}
fn enable_pullup(&mut self, p: u8) {
self.port.set(BitIndex::bit(p as usize));
}
fn disable_pullup(&mut self, p: u8) {
self.port.clr(BitIndex::bit(p as usize));
}
fn set_high(&mut self, p: u8) {
self.port.set(BitIndex::bit(p as usize));
}
fn set_low(&mut self, p: u8) {
self.port.clr(BitIndex::bit(p as usize));
}
fn toggle(&mut self, p: u8) {
self.pin.exc_set(BitIndex::bit(p as usize)); }
fn get(&self, p: u8) -> bool {
self.pin.is_set(BitIndex::bit(p as usize))
}
fn set_interrupt_mode(&mut self, p: u8, mode: InterruptMode) {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
let pcien = &mut *(PCIEN as *mut VolatileBitField);
let pcicr = &mut *(PCICR as *mut VolatileBitField);
match mode {
InterruptMode::Disabled => {
pcien.clr_isolated(isotoken, BitIndex::bit(p as usize));
}
InterruptMode::BothEdges => {
pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
},
InterruptMode::RisingEdge => {
pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
},
InterruptMode::FallingEdge => {
pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
},
InterruptMode::LowLevel => {
pcicr.set_isolated(isotoken, BitIndex::bit(PORTNUM));
pcien.set_isolated(isotoken, BitIndex::bit(p as usize));
}
}
})
}
}
fn isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
unsafe {
let pcien = &mut *(PCIEN as *mut VolatileBitField);
for pin in 0u8..=7u8 {
if pcien.is_set(BitIndex::bit(pin as usize)){
let state = self.pin.is_set(BitIndex::bit(pin as usize));
let source = get_pin(pin);
handlers::instance_isolated(isotoken).invoke_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(PORTNUM as u8, pin), source, state);
}
}
let pcifr = &mut *(PCIFR as *mut VolatileBitField);
pcifr.exc_set(BitIndex::bit(PORTNUM));
}
}
fn listen(&'static self, p: u8, handler: PinIsrCallback) {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
handlers::instance_isolated(isotoken).set_handler_for_pin(isotoken, ProxyPin::for_port_and_pin(PORTNUM as u8, p), handler);
})
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_port_tpl {
($ref:expr, $portimpl:ty, $portnum:expr, $pinarray:expr, $isr:expr) => {
use core::any::Any;
use avr_oxide::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
use avr_oxide::hal::generic::port::base::{AtmelPortControl};
use avr_oxide::isr_cb_invoke;
pub type PortImpl = $portimpl;
static mut INITIALISED: bool = false;
pub fn instance() -> &'static mut $portimpl {
unsafe {
core::mem::transmute($ref)
}
}
pub fn pin(pin: u8) -> &'static dyn Pin {
&$pinarray[(($portnum*8)+pin) as usize]
}
#[oxide_macros::interrupt(isr=$isr)]
fn port_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
instance().isr(isotoken, pin);
}
}
}
}
#[cfg(not(target_arch="avr"))]
pub mod base {
use avr_oxide::concurrency::Isolated;
use avr_oxide::hal::generic::port::{InterruptMode, Pin, ProxyPin, PinIsrCallback, PinMode, PinToPort};
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 isr(&'static mut self, isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin);
fn listen(&'static self, p: u8, handler: PinIsrCallback);
}
impl Pin for ProxyPin
where
ProxyPin: PinToPort
{
fn set_mode(&self, mode: PinMode) {
let pin = self.pin_number();
match mode {
PinMode::Output => {
self.get_port().enable_output(pin);
},
PinMode::InputPullup => {
self.get_port().enable_pullup(pin);
self.get_port().disable_output(pin);
},
PinMode::InputFloating => {
self.get_port().disable_pullup(pin);
self.get_port().disable_output(pin);
},
PinMode::Input => {
self.get_port().disable_output(pin);
}
}
}
#[inline(never)]
fn toggle(&self) {
self.get_port().toggle(self.pin_number());
}
fn set_high(&self) {
self.get_port().set_high(self.pin_number());
}
fn set_low(&self) {
self.get_port().set_low(self.pin_number());
}
fn set(&self, high: bool) {
match high {
true => self.get_port().set_high(self.pin_number()),
false => self.get_port().set_low(self.pin_number())
}
}
fn get(&self) -> bool {
self.get_port().get(self.pin_number())
}
fn set_interrupt_mode(&self, mode: InterruptMode) {
self.get_port().set_interrupt_mode(self.pin_number(), mode)
}
fn listen(&self, handler: PinIsrCallback){
self.get_port().listen(self.pin_number(),handler)
}
}
pub mod zeroseries {
use avr_oxide::hal::generic::port::{handlers, InterruptMode, Pin, ProxyPin, PinIsrCallback};
use avr_oxide::concurrency::Isolated;
use avr_oxide::hal::generic::port::base::{AtmelPortControl};
use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};
#[repr(C)]
pub struct PortRegisterBlock {
pub(crate) pin: [bool; 8],
pub(crate) o_en: [bool; 8],
pub(crate) pu_en: [bool; 8]
}
impl AtmelPortControl for PortRegisterBlock {
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 isr(&'static mut self, _isotoken: Isolated, get_pin: fn(u8)->&'static dyn Pin) {
unimplemented!()
}
fn listen(&'static self, p: u8, handler: PinIsrCallback){
unimplemented!()
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_port_tpl {
($ref:expr, $portimpl:ty, $portnum:expr, $pinarray:expr, $isr:expr) => {
use core::any::Any;
use avr_oxide::hal::generic::port::{PinMode,Pin,PinIdentity,InterruptMode,PinIsrCallback};
use avr_oxide::hal::generic::port::base::{AtmelPortControl};
use avr_oxide::isr_cb_invoke;
pub type PortImpl = $portimpl;
static mut INITIALISED: bool = false;
#[inline(always)]
pub fn instance() -> &'static mut $portimpl {
unimplemented!()
}
#[inline(always)]
pub fn pin(pin: u8) -> &'static dyn Pin {
&$pinarray[(($portnum*8)+pin) as usize]
}
}
}
}