1+ #ifndef KLIB_ATMEL_ATSAM4S_PORT_INTERRUPT_HPP
2+ #define KLIB_ATMEL_ATSAM4S_PORT_INTERRUPT_HPP
3+
4+ #include < cstdint>
5+
6+ #include < klib/klib.hpp>
7+ #include < klib/irq_helper.hpp>
8+
9+ #include " clocks.hpp"
10+
11+ namespace klib ::core::atsam4s::io {
12+ /* *
13+ * @brief Interrupt handler for a port. Uses irq helper to handle the interrupts
14+ *
15+ * @tparam Port
16+ */
17+ template <typename Port>
18+ class port_interrupt {
19+ protected:
20+ using irq_helper = klib::irq_helper<32 >;
21+
22+ // irq helper to handle all interrups
23+ static inline irq_helper helper;
24+
25+ /* *
26+ * @brief Interrupt handler
27+ *
28+ */
29+ static void irq_handler () {
30+ // get the interrupt status from the port. This will automatically
31+ // clear the interrupts for all the pins that are triggered
32+ const uint32_t status = Port::port->ISR ;
33+ const uint32_t mask = Port::port->IMR ;
34+
35+ // handle the interrupts that are registered
36+ helper.handle_irq (status, mask);
37+ }
38+
39+ public:
40+ // using for the interrupt callback
41+ using interrupt_callback = irq_helper::interrupt_callback;
42+
43+ /* *
44+ * @brief Init the port interrupt
45+ *
46+ */
47+ static void init () {
48+ // enable the power for the pio
49+ target::io::power_control::enable<Port>();
50+
51+ // register the interrupt handler for the port to our local handler
52+ target::irq::template register_irq<Port::interrupt_id>(irq_handler);
53+
54+ // enable the interrupt on the port
55+ target::enable_irq<Port::interrupt_id>();
56+ }
57+
58+ /* *
59+ * @brief Register a interrupt for a specific pin
60+ *
61+ * @tparam Pin
62+ * @param callback
63+ */
64+ template <typename Pin>
65+ static void register_irq (irq_helper::interrupt_callback callback) {
66+ // check if the port is correct
67+ static_assert (std::is_same_v<typename Pin::port, Port>, " Pin is not for this port" );
68+
69+ // register the interrupt with a specific pin
70+ helper.register_irq <Pin::number>(callback);
71+ }
72+
73+ /* *
74+ * @brief Unregister a interrupt for a specific pin
75+ *
76+ * @tparam Pin
77+ */
78+ template <typename Pin>
79+ static void unregister_irq () {
80+ // check if the port is correct
81+ static_assert (std::is_same_v<typename Pin::port, Port>, " Pin is not for this port" );
82+
83+ // clear the interrupt from the helper irq
84+ helper.unregister_irq <Pin::number>();
85+ }
86+ };
87+
88+ template <typename Pin>
89+ class pin_interrupt {
90+ public:
91+ /* *
92+ * @brief Using for the interrupt callback used in the pin irq
93+ *
94+ */
95+ using interrupt_callback = port_interrupt<typename Pin::port>::interrupt_callback;
96+
97+ public:
98+ /* *
99+ * @brief Supported trigger modes
100+ *
101+ */
102+ enum class edge {
103+ falling,
104+ rising,
105+ dual_edge,
106+ low_level,
107+ high_level,
108+ };
109+
110+ /* *
111+ * @brief Initialize a pin with a callback
112+ *
113+ * @tparam Edge
114+ * @param callback
115+ */
116+ template <edge Edge>
117+ constexpr static void init (interrupt_callback callback) {
118+ // setup the pin as input
119+ pin_in<Pin>::init ();
120+
121+ // set the edge select based on the edge mode
122+ if constexpr ((Edge == edge::falling) || (Edge == edge::rising) || (Edge == edge::dual_edge)) {
123+ // set the pin to edge select
124+ Pin::port::port->ESR = detail::pins::mask<Pin>;
125+ }
126+ else {
127+ // set the pin to level select
128+ Pin::port::port->LSR = detail::pins::mask<Pin>;
129+ }
130+
131+ // check if we have a falling or rising edge
132+ if constexpr ((Edge == edge::falling) || (Edge == edge::low_level)) {
133+ // enable falling edge or low level
134+ Pin::port::port->FELLSR = detail::pins::mask<Pin>;
135+
136+ // enable the additional interrupt modes register to trigger only
137+ // on the falling edge/low level
138+ Pin::port::port->AIMER = detail::pins::mask<Pin>;
139+ }
140+ else if constexpr ((Edge == edge::rising) || (Edge == edge::high_level)) {
141+ // enable rising edge or high level
142+ Pin::port::port->REHLSR = detail::pins::mask<Pin>;
143+
144+ // enable the additional interrupt modes register to trigger only
145+ // on the rising edge/high level
146+ Pin::port::port->AIMER = detail::pins::mask<Pin>;
147+ }
148+ else {
149+ // clear the additional interrupt modes register to trigger on both edges
150+ Pin::port::port->AIMDR = detail::pins::mask<Pin>;
151+ }
152+
153+ // enable the interrupt for the pin
154+ Pin::port::port->IER = detail::pins::mask<Pin>;
155+
156+ // register the interrupt in the port
157+ port_interrupt<typename Pin::port>::template register_irq<Pin>(callback);
158+
159+ // init the port
160+ port_interrupt<typename Pin::port>::init ();
161+ }
162+
163+ /* *
164+ * @brief Disable the interrupt on the pin
165+ *
166+ */
167+ constexpr static void disable () {
168+ // disable the interrupt for the pin
169+ Pin::port::port->IDR = detail::pins::mask<Pin>;
170+ }
171+ };
172+ }
173+
174+ #endif
0 commit comments