443 lines
15 KiB
Rust
443 lines
15 KiB
Rust
//! Pin Interrupt module.
|
|
use core::cell::RefCell;
|
|
use core::future::Future;
|
|
use core::pin::Pin as FuturePin;
|
|
use core::task::{Context, Poll};
|
|
|
|
use critical_section::Mutex;
|
|
use embassy_sync::waitqueue::AtomicWaker;
|
|
|
|
use crate::gpio::{self, AnyPin, Level, SealedPin};
|
|
use crate::pac::interrupt;
|
|
use crate::pac_utils::*;
|
|
use crate::Peri;
|
|
|
|
struct PinInterrupt {
|
|
assigned: bool,
|
|
waker: AtomicWaker,
|
|
/// If true, the interrupt was triggered due to this PinInterrupt. This is used to determine if
|
|
/// an [InputFuture] should return Poll::Ready.
|
|
at_fault: bool,
|
|
}
|
|
|
|
impl PinInterrupt {
|
|
pub fn interrupt_active(&self) -> bool {
|
|
self.assigned
|
|
}
|
|
|
|
/// Mark the interrupt as assigned to a pin.
|
|
pub fn enable(&mut self) {
|
|
self.assigned = true;
|
|
self.at_fault = false;
|
|
}
|
|
|
|
/// Mark the interrupt as available.
|
|
pub fn disable(&mut self) {
|
|
self.assigned = false;
|
|
self.at_fault = false;
|
|
}
|
|
|
|
/// Returns true if the interrupt was triggered due to this PinInterrupt.
|
|
///
|
|
/// If this function returns true, it will also reset the at_fault flag.
|
|
pub fn at_fault(&mut self) -> bool {
|
|
let val = self.at_fault;
|
|
self.at_fault = false;
|
|
val
|
|
}
|
|
|
|
/// Set the at_fault flag to true.
|
|
pub fn fault(&mut self) {
|
|
self.at_fault = true;
|
|
}
|
|
}
|
|
|
|
const INTERUPT_COUNT: usize = 8;
|
|
static PIN_INTERRUPTS: Mutex<RefCell<[PinInterrupt; INTERUPT_COUNT]>> = Mutex::new(RefCell::new(
|
|
[const {
|
|
PinInterrupt {
|
|
assigned: false,
|
|
waker: AtomicWaker::new(),
|
|
at_fault: false,
|
|
}
|
|
}; INTERUPT_COUNT],
|
|
));
|
|
|
|
fn next_available_interrupt() -> Option<usize> {
|
|
critical_section::with(|cs| {
|
|
for (i, pin_interrupt) in PIN_INTERRUPTS.borrow(cs).borrow().iter().enumerate() {
|
|
if !pin_interrupt.interrupt_active() {
|
|
return Some(i);
|
|
}
|
|
}
|
|
|
|
None
|
|
})
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
enum Edge {
|
|
Rising,
|
|
Falling,
|
|
Both,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
enum InterruptOn {
|
|
Level(Level),
|
|
Edge(Edge),
|
|
}
|
|
|
|
pub(crate) fn init() {
|
|
syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable());
|
|
|
|
// Enable interrupts
|
|
unsafe {
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6);
|
|
crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7);
|
|
};
|
|
}
|
|
|
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
struct InputFuture<'d> {
|
|
#[allow(dead_code)]
|
|
pin: Peri<'d, AnyPin>,
|
|
interrupt_number: usize,
|
|
}
|
|
|
|
impl<'d> InputFuture<'d> {
|
|
/// Create a new input future. Returns None if all interrupts are in use.
|
|
fn new(pin: Peri<'d, impl gpio::Pin>, interrupt_on: InterruptOn) -> Option<Self> {
|
|
let pin = pin.into();
|
|
let interrupt_number = next_available_interrupt()?;
|
|
|
|
// Clear interrupt, just in case
|
|
pint_reg()
|
|
.rise
|
|
.write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
|
|
pint_reg()
|
|
.fall
|
|
.write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
|
|
|
|
// Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number)
|
|
inputmux_reg().pintsel[interrupt_number]
|
|
.write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) });
|
|
|
|
match interrupt_on {
|
|
InterruptOn::Level(level) => {
|
|
// Set pin interrupt register to edge sensitive or level sensitive
|
|
// 0 = edge sensitive, 1 = level sensitive
|
|
pint_reg()
|
|
.isel
|
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) });
|
|
|
|
// Enable level interrupt.
|
|
//
|
|
// Note: Level sensitive interrupts are enabled by the same register as rising edge
|
|
// is activated.
|
|
|
|
// 0 = no-op, 1 = enable
|
|
pint_reg()
|
|
.sienr
|
|
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
|
|
|
// Set active level
|
|
match level {
|
|
Level::Low => {
|
|
// 0 = no-op, 1 = select LOW
|
|
pint_reg()
|
|
.cienf
|
|
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
|
}
|
|
Level::High => {
|
|
// 0 = no-op, 1 = select HIGH
|
|
pint_reg()
|
|
.sienf
|
|
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
|
}
|
|
}
|
|
}
|
|
InterruptOn::Edge(edge) => {
|
|
// Set pin interrupt register to edge sensitive or level sensitive
|
|
// 0 = edge sensitive, 1 = level sensitive
|
|
pint_reg()
|
|
.isel
|
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) });
|
|
|
|
// Enable rising/falling edge detection
|
|
match edge {
|
|
Edge::Rising => {
|
|
// 0 = no-op, 1 = enable rising edge
|
|
pint_reg()
|
|
.sienr
|
|
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
|
// 0 = no-op, 1 = disable falling edge
|
|
pint_reg()
|
|
.cienf
|
|
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
|
}
|
|
Edge::Falling => {
|
|
// 0 = no-op, 1 = enable falling edge
|
|
pint_reg()
|
|
.sienf
|
|
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
|
// 0 = no-op, 1 = disable rising edge
|
|
pint_reg()
|
|
.cienr
|
|
.write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
|
|
}
|
|
Edge::Both => {
|
|
// 0 = no-op, 1 = enable
|
|
pint_reg()
|
|
.sienr
|
|
.write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) });
|
|
pint_reg()
|
|
.sienf
|
|
.write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
critical_section::with(|cs| {
|
|
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
|
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
|
|
|
pin_interrupt.enable();
|
|
});
|
|
|
|
Some(Self { pin, interrupt_number })
|
|
}
|
|
|
|
/// Returns true if the interrupt was triggered for this pin.
|
|
fn interrupt_triggered(&self) -> bool {
|
|
let interrupt_number = self.interrupt_number;
|
|
|
|
// Initially, we determine if the interrupt was triggered by this InputFuture by checking
|
|
// the flags of the interrupt_number. However, by the time we get to this point, the
|
|
// interrupt may have been triggered again, so we needed to clear the cpu flags immediately.
|
|
// As a solution, we mark which [PinInterrupt] is responsible for the interrupt ("at fault")
|
|
critical_section::with(|cs| {
|
|
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
|
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
|
|
|
pin_interrupt.at_fault()
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'d> Drop for InputFuture<'d> {
|
|
fn drop(&mut self) {
|
|
let interrupt_number = self.interrupt_number;
|
|
|
|
// Disable pin interrupt
|
|
// 0 = no-op, 1 = disable
|
|
pint_reg()
|
|
.cienr
|
|
.write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) });
|
|
pint_reg()
|
|
.cienf
|
|
.write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) });
|
|
|
|
critical_section::with(|cs| {
|
|
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
|
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
|
|
|
pin_interrupt.disable();
|
|
});
|
|
}
|
|
}
|
|
|
|
impl<'d> Future for InputFuture<'d> {
|
|
type Output = ();
|
|
|
|
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
let interrupt_number = self.interrupt_number;
|
|
|
|
critical_section::with(|cs| {
|
|
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
|
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
|
|
|
pin_interrupt.waker.register(cx.waker());
|
|
});
|
|
|
|
if self.interrupt_triggered() {
|
|
Poll::Ready(())
|
|
} else {
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
|
|
fn handle_interrupt(interrupt_number: usize) {
|
|
pint_reg()
|
|
.rise
|
|
.write(|w| unsafe { w.rdet().bits(1 << interrupt_number) });
|
|
pint_reg()
|
|
.fall
|
|
.write(|w| unsafe { w.fdet().bits(1 << interrupt_number) });
|
|
|
|
critical_section::with(|cs| {
|
|
let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();
|
|
let pin_interrupt = &mut pin_interrupts[interrupt_number];
|
|
|
|
pin_interrupt.fault();
|
|
pin_interrupt.waker.wake();
|
|
});
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT0() {
|
|
handle_interrupt(0);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT1() {
|
|
handle_interrupt(1);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT2() {
|
|
handle_interrupt(2);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT3() {
|
|
handle_interrupt(3);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT4() {
|
|
handle_interrupt(4);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT5() {
|
|
handle_interrupt(5);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT6() {
|
|
handle_interrupt(6);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
#[interrupt]
|
|
fn PIN_INT7() {
|
|
handle_interrupt(7);
|
|
}
|
|
|
|
impl gpio::Flex<'_> {
|
|
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
|
/// try to wait for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
|
InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Both))?.await;
|
|
Some(())
|
|
}
|
|
|
|
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
|
InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Falling))?.await;
|
|
Some(())
|
|
}
|
|
|
|
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
|
InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Rising))?.await;
|
|
Some(())
|
|
}
|
|
|
|
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_low(&mut self) -> Option<()> {
|
|
InputFuture::new(self.pin.reborrow(), InterruptOn::Level(Level::Low))?.await;
|
|
Some(())
|
|
}
|
|
|
|
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_high(&mut self) -> Option<()> {
|
|
InputFuture::new(self.pin.reborrow(), InterruptOn::Level(Level::High))?.await;
|
|
Some(())
|
|
}
|
|
}
|
|
|
|
impl gpio::Input<'_> {
|
|
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
|
/// try to wait for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_any_edge().await
|
|
}
|
|
|
|
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_falling_edge().await
|
|
}
|
|
|
|
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_rising_edge().await
|
|
}
|
|
|
|
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_low(&mut self) -> Option<()> {
|
|
self.pin.wait_for_low().await
|
|
}
|
|
|
|
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_high(&mut self) -> Option<()> {
|
|
self.pin.wait_for_high().await
|
|
}
|
|
}
|
|
|
|
impl gpio::Output<'_> {
|
|
/// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you
|
|
/// try to wait for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_any_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_any_edge().await
|
|
}
|
|
|
|
/// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_falling_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_falling_edge().await
|
|
}
|
|
|
|
/// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait
|
|
/// for more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_rising_edge(&mut self) -> Option<()> {
|
|
self.pin.wait_for_rising_edge().await
|
|
}
|
|
|
|
/// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_low(&mut self) -> Option<()> {
|
|
self.pin.wait_for_low().await
|
|
}
|
|
|
|
/// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for
|
|
/// more than 8 pins, this function will return `None`.
|
|
pub async fn wait_for_high(&mut self) -> Option<()> {
|
|
self.pin.wait_for_high().await
|
|
}
|
|
}
|