1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! External interrupt controller (EXTI).

use crate::pac::EXTI;

/// An `ExtiLine` that can be `listen()`ed for interrupt
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ExtiLine(u8);

/// Internal sources(lines) for ExtiLine
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum InternalLine {
    Lvd = 16,
    RtcAlarm = 17,
    UsbWakeup = 18,
}

/// Enable/Disable event genration to wakeup unit for an EXTI line
#[derive(Copy, Clone)]
pub enum ExtiEvent{
    Enable,
    Disable,
}

impl ExtiLine {
    /// Generate an `Option<ExtiLine>` from a GPIO pin configured by Afio
    ///
    /// A line from gpio can be obtained through its `pin_number()` method
    /// ```no_run
    /// extiline = ExtiLine::from_gpio_line(some_pin.pin_number()).unwrap();
    /// exti.listen(extiline, TriggerEdge::Falling);
    /// ```
    pub fn from_gpio_line(line: u8) -> Option<Self> {
        match line {
            0..=15 => Some(ExtiLine(line)),
            _ => None,
        }
    }

    /// Generate an `ExtiLine` from internal source specified by `InternalLine`
    pub fn from_internal_line(line: InternalLine) -> Self {
        ExtiLine(line as u8)
    }
}

/// Edges that can trigger a configurable interrupt line.
pub enum TriggerEdge {
    /// Trigger on rising edges only.
    Rising,
    /// Trigger on falling edges only.
    Falling,
    /// Trigger on both rising and falling edges.
    Both,
}

/// Higher-lever wrapper around the `EXTI` peripheral.
pub struct Exti {
    raw: EXTI,
}

impl Exti {
    /// Creates a new `Exti` wrapper from the raw `EXTI` peripheral.
    pub fn new(raw: EXTI) -> Self {
        Self { raw }
    }

    /// Destroys this `Exti` instance, returning the raw `EXTI` peripheral.
    pub fn release(self) -> EXTI {
        self.raw
    }

    /// Listen on one of the Lines
    #[inline]
    pub fn listen(&mut self, line: ExtiLine, edge: TriggerEdge) {
        let bm: u32 = 1 << line.0;

        unsafe {
            match edge {
                TriggerEdge::Falling => {
                    self.raw.ften.modify(|r, w| w.bits(r.bits() | bm));
                    self.raw.rten.modify(|r, w| w.bits(r.bits() & !bm));
                },
                TriggerEdge::Rising => {
                    self.raw.ften.modify(|r, w| w.bits(r.bits() & !bm));
                    self.raw.rten.modify(|r, w| w.bits(r.bits() | bm));
                },
                TriggerEdge::Both => {
                    self.raw.ften.modify(|r, w| w.bits(r.bits() | bm));
                    self.raw.rten.modify(|r, w| w.bits(r.bits() | bm));
                }
            }

            self.raw.inten.modify(|r, w| w.bits(r.bits() | bm));
        }
    }

    /// Unlisten on the specified line
    #[inline]
    pub fn unlisten(&mut self, line: ExtiLine) {
        let bm: u32 = 1 << line.0;

        unsafe {
            self.raw.rten.modify(|r, w| w.bits(r.bits() & !bm));
            self.raw.ften.modify(|r, w| w.bits(r.bits() & !bm));
            self.raw.inten.modify(|r, w| w.bits(r.bits() & !bm));
        }
    }

    /// Enable/Disable event generation on line to wakeup unit
    #[inline]
    pub fn gen_event(&mut self, line: ExtiLine, enable: ExtiEvent) {
        let bm: u32 = 1 << line.0;

        if let ExtiEvent::Enable = enable {
            unsafe { (*EXTI::ptr()).even.modify(|r, w| w.bits(r.bits() | bm)) };
        } else {
            unsafe { (*EXTI::ptr()).even.modify(|r, w| w.bits(r.bits() & !bm)) };
        }
    }

    /// `true` if this line has a pending interrupt
    #[inline]
    pub fn is_pending(line: ExtiLine) -> bool {
        let bm: u32 = 1 << line.0;
        unsafe { (*EXTI::ptr()).pd.read().bits() & bm != 0 }
    }

    /// Clear the pending interrupt flag
    #[inline]
    pub fn clear(line: ExtiLine) {
        let bm: u32 = 1 << line.0;
        unsafe { (*EXTI::ptr()).pd.write(|w| w.bits(bm)) };
    }

    /// Request a pending interrupt for this line from software
    #[inline]
    pub fn pend(line: ExtiLine) {
        let bm: u32 = 1 << line.0;
        unsafe { (*EXTI::ptr()).swiev.modify(|r, w| w.bits(r.bits() | bm)) };
    }

    /// Deactivate a software pending request for this line
    #[inline]
    pub fn unpend(line: ExtiLine) {
        let bm: u32 = 1 << line.0;
        unsafe { (*EXTI::ptr()).swiev.modify(|r, w| w.bits(r.bits() & !bm)) };
    }
}