Skip to content

Commit e8b7dd7

Browse files
committed
atsam4s added support for gpio interrupts
1 parent 5ecdb4e commit e8b7dd7

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

targets/chip/atsam4s2b/io/port.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cstddef>
66

77
#include <targets/core/atmel/atsam4s/port.hpp>
8+
#include <targets/core/atmel/atsam4s/port_interrupt.hpp>
89

910
// global peripherals, not affected by chip package
1011
namespace klib::atsam4s2b::io::periph {
@@ -56,6 +57,9 @@ namespace klib::atsam4s2b::io {
5657

5758
template <typename Pin>
5859
using pin_in_out_od = klib::core::atsam4s::io::pin_in_out_od<Pin>;
60+
61+
template <typename Pin>
62+
using pin_interrupt = klib::core::atsam4s::io::pin_interrupt<Pin>;
5963
}
6064

6165
#endif
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)