Added GPIO IRQ interface, refactoring
- Adds the IRQ interface to configure interrupts on output and input pins - Moved the `FilterClkSel` struct to the `clock` module, reexporting in `gpio` - Added function to set clock divisor registers - Clearing output state at initialization of Output pins - Added utility function to set up millisecond timer
This commit is contained in:
@ -57,9 +57,17 @@
|
||||
//! operation, the trait functions will return
|
||||
//! [`InvalidPinType`](Error::InvalidPinType).
|
||||
|
||||
use super::pins::{FilterClkSel, FilterType, Pin, PinError, PinId, PinMode, PinState};
|
||||
use super::pins::{
|
||||
common_reg_if_functions, FilterType, InterruptEdge, InterruptLevel, Pin, PinError, PinId,
|
||||
PinMode, PinState,
|
||||
};
|
||||
use super::reg::RegisterInterface;
|
||||
use crate::{
|
||||
clock::FilterClkSel,
|
||||
pac::{self, IRQSEL, SYSCONFIG},
|
||||
};
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||
use paste::paste;
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode configurations
|
||||
@ -293,42 +301,7 @@ impl DynPin {
|
||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(self) -> Self {
|
||||
self.regs.clear_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(self) -> Self {
|
||||
self.regs.set_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
common_reg_if_functions!();
|
||||
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
@ -371,6 +344,40 @@ impl DynPin {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
edge_type: InterruptEdge,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: pac::Interrupt,
|
||||
) -> Result<Self, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
level_type: InterruptLevel,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: crate::pac::Interrupt,
|
||||
) -> Result<Self, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_level(level_type);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _read(&self) -> Result<bool, PinError> {
|
||||
match self.mode {
|
||||
|
193
src/gpio/pins.rs
193
src/gpio/pins.rs
@ -89,9 +89,11 @@
|
||||
|
||||
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::reg::RegisterInterface;
|
||||
use crate::pac::{IOCONFIG, PORTA, PORTB, SYSCONFIG};
|
||||
use crate::typelevel::Is;
|
||||
use crate::Sealed;
|
||||
use crate::{
|
||||
pac::{self, IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG},
|
||||
typelevel::Is,
|
||||
Sealed,
|
||||
};
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||
@ -101,6 +103,19 @@ use paste::paste;
|
||||
// Errors and Definitions
|
||||
//==================================================================================================
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InterruptEdge {
|
||||
HighToLow,
|
||||
LowToHigh,
|
||||
BothEdges,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InterruptLevel {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PinState {
|
||||
Low = 0,
|
||||
@ -164,6 +179,7 @@ pub struct Input<C: InputConfig> {
|
||||
|
||||
impl<C: InputConfig> Sealed for Input<C> {}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FilterType {
|
||||
SystemClock = 0,
|
||||
DirectInputWithSynchronization = 1,
|
||||
@ -173,16 +189,7 @@ pub enum FilterType {
|
||||
FilterFourClockCycles = 5,
|
||||
}
|
||||
|
||||
pub enum FilterClkSel {
|
||||
SysClk = 0,
|
||||
Clk1 = 1,
|
||||
Clk2 = 2,
|
||||
Clk3 = 3,
|
||||
Clk4 = 4,
|
||||
Clk5 = 5,
|
||||
Clk6 = 6,
|
||||
Clk7 = 7,
|
||||
}
|
||||
pub use crate::clock::FilterClkSel;
|
||||
|
||||
//==================================================================================================
|
||||
// Output configuration
|
||||
@ -347,6 +354,77 @@ impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {
|
||||
type Mode = M;
|
||||
}
|
||||
|
||||
macro_rules! common_reg_if_functions {
|
||||
() => {
|
||||
paste!(
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(self) -> Self {
|
||||
self.regs.clear_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(self) -> Self {
|
||||
self.regs.set_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
|
||||
fn _irq_enb(
|
||||
&mut self,
|
||||
syscfg: Option<&mut va108xx::SYSCONFIG>,
|
||||
irqsel: &mut va108xx::IRQSEL,
|
||||
interrupt: va108xx::Interrupt,
|
||||
) {
|
||||
if syscfg.is_some() {
|
||||
crate::clock::enable_peripheral_clock(
|
||||
syscfg.unwrap(),
|
||||
crate::clock::PeripheralClocks::Irqsel,
|
||||
);
|
||||
}
|
||||
self.regs.enable_irq();
|
||||
match self.regs.id().group {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
DynGroup::A => {
|
||||
irqsel.porta[self.regs.id().num as usize]
|
||||
.write(|w| unsafe { w.bits(interrupt as u32) });
|
||||
}
|
||||
DynGroup::B => {
|
||||
irqsel.portb[self.regs.id().num as usize]
|
||||
.write(|w| unsafe { w.bits(interrupt as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use common_reg_if_functions;
|
||||
|
||||
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
/// Create a new [`Pin`]
|
||||
///
|
||||
@ -429,42 +507,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(self) -> Self {
|
||||
self.regs.clear_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(self) -> Self {
|
||||
self.regs.set_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
common_reg_if_functions!();
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _set_high(&mut self) {
|
||||
@ -526,6 +569,32 @@ impl<I: PinId, M: PinMode> AsMut<Self> for Pin<I, M> {
|
||||
// Additional functionality
|
||||
//==================================================================================================
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
edge_type: InterruptEdge,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: pac::Interrupt,
|
||||
) -> Self {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
level_type: InterruptLevel,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: pac::Interrupt,
|
||||
) -> Self {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_level(level_type);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
@ -545,6 +614,30 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
self.regs.pulse_mode(enable, default_state);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
edge_type: InterruptEdge,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: pac::Interrupt,
|
||||
) -> Self {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
level_type: InterruptLevel,
|
||||
syscfg: Option<&mut SYSCONFIG>,
|
||||
irqsel: &mut IRQSEL,
|
||||
interrupt: pac::Interrupt,
|
||||
) -> Self {
|
||||
self._irq_enb(syscfg, irqsel, interrupt);
|
||||
self.regs.interrupt_level(level_type);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
@ -687,7 +780,7 @@ macro_rules! pins {
|
||||
)+
|
||||
}
|
||||
|
||||
impl $PinsName{
|
||||
impl $PinsName {
|
||||
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
|
||||
/// is optional because it might be required to create pin definitions for both
|
||||
/// ports.
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode};
|
||||
use super::pins::{FilterClkSel, FilterType, PinError, PinState};
|
||||
use super::pins::{FilterType, InterruptEdge, InterruptLevel, PinError, PinState};
|
||||
use crate::clock::FilterClkSel;
|
||||
use va108xx::{ioconfig, porta, IOCONFIG, PORTA, PORTB};
|
||||
|
||||
/// Type definition to avoid confusion: These register blocks are identical
|
||||
@ -31,16 +32,13 @@ impl From<DynPinMode> for ModeFields {
|
||||
use dynpins::DynInput::*;
|
||||
fields.dir = false;
|
||||
match config {
|
||||
Floating => {
|
||||
fields.pull_en = false;
|
||||
}
|
||||
Floating => (),
|
||||
PullUp => {
|
||||
fields.pull_en = true;
|
||||
fields.pull_dir = true;
|
||||
}
|
||||
PullDown => {
|
||||
fields.pull_en = true;
|
||||
fields.pull_dir = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,9 +46,7 @@ impl From<DynPinMode> for ModeFields {
|
||||
use dynpins::DynOutput::*;
|
||||
fields.dir = true;
|
||||
match config {
|
||||
PushPull => {
|
||||
fields.opendrn = false;
|
||||
}
|
||||
PushPull => (),
|
||||
OpenDrain => {
|
||||
fields.opendrn = true;
|
||||
}
|
||||
@ -60,7 +56,6 @@ impl From<DynPinMode> for ModeFields {
|
||||
}
|
||||
ReadablePushPull => {
|
||||
fields.enb_input = true;
|
||||
fields.opendrn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,6 +149,8 @@ pub(super) unsafe trait RegisterInterface {
|
||||
unsafe {
|
||||
if dir {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||
// Clear output
|
||||
portreg.clrout().write(|w| w.bits(mask));
|
||||
} else {
|
||||
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||
}
|
||||
@ -200,6 +197,20 @@ pub(super) unsafe trait RegisterInterface {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_irq(&self) {
|
||||
self.port_reg()
|
||||
.irq_enb
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn disable_irq(&self) {
|
||||
self.port_reg()
|
||||
.irq_enb
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_perid(&self) -> u32 {
|
||||
let portreg = self.port_reg();
|
||||
@ -273,6 +284,53 @@ pub(super) unsafe trait RegisterInterface {
|
||||
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
|
||||
}
|
||||
|
||||
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||
#[inline]
|
||||
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
match edge_type {
|
||||
InterruptEdge::HighToLow => {
|
||||
self.port_reg()
|
||||
.irq_evt
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
}
|
||||
InterruptEdge::LowToHigh => {
|
||||
self.port_reg()
|
||||
.irq_evt
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
InterruptEdge::BothEdges => {
|
||||
self.port_reg()
|
||||
.irq_edge
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure which edge or level type triggers an interrupt
|
||||
#[inline]
|
||||
fn interrupt_level(&mut self, level: InterruptLevel) {
|
||||
unsafe {
|
||||
self.port_reg()
|
||||
.irq_sen
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
if level == InterruptLevel::Low {
|
||||
self.port_reg()
|
||||
.irq_evt
|
||||
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||
} else {
|
||||
self.port_reg()
|
||||
.irq_evt
|
||||
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
||||
|
Reference in New Issue
Block a user