-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterrupt.hpp
200 lines (165 loc) · 6.13 KB
/
interrupt.hpp
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#pragma once
#include "avr/interrupt/detail/global.hpp"
#include "avr/interrupt/detail/is_same.hpp"
#include <stdint.h>
#if __has_include(<avr/io.hpp>)
#include <avr/io.hpp>
#else
#include <avr/io.h> //We use avr-libc if avrIO isn't available.
namespace avr { namespace io {
#if __cplusplus >= 201703L
inline volatile uint8_t& sreg{SREG};
#else
namespace {
volatile uint8_t& sreg{SREG};
}//anonymous namespace
#endif
}}
#endif
//We need to undef the macros defined by avr-libc to avoid collision
//when <avr/interrupt.h> is included before this header.
#undef cli
#undef sei
namespace avr { namespace interrupt {
/** Enable the global interrupts */
[[gnu::always_inline]] inline void on() noexcept
{ asm("sei" ::: "memory"); }
/** Enable the global interrupts */
[[gnu::always_inline]] inline void sei() noexcept
{ on(); }
/** Disable the global interrupts */
[[gnu::always_inline]] inline void off() noexcept
{ asm("cli" ::: "memory"); }
/** Disable the global interrupts */
[[gnu::always_inline]] inline void cli() noexcept
{ off(); }
inline void clobber() {}
template<typename T, typename... Rest>
[[gnu::always_inline]] inline void clobber(const T& v, Rest&... rest) {
asm("" : : "g"(v) : "memory");
clobber(rest...);
}
/** Tag to be used as a parameter to the constructor of the class
'atomic'. It informs that the status register state should be
recovered at the end of the atomic scope. */
struct restore_t{};
/** Tag to be used as a parameter to the constructor of the class
'atomic'. It informs that the global interrupts must always be
enabled at the end of the scope. */
struct on_at_the_end_t{};
/** Tag to be used as a parameter to the constructor of the class
'interruptible'. It informs that the global interrupts must always
be disabled at the end of the scope. */
struct off_at_the_end_t{};
/** RAII to execute code without being disturbed by interrupts
By default the state of the status register that was before the
atomic scope is recovered at the end of the scope, which means
that interrupts are enabled only if they were enabled
before. If the argument 'on_at_the_end' is passed then the
interrupts are always enabled at the end of the scope.
Example:
{
atomic sa; //same as 'atomic sa(restore)'
//my code
}
{
atomic sa(on_at_the_end);
//my code
}
Note, the compiler optimizer can reorder instructions around the
atomic scope when that instructions doesn't have observable side
effects. Take a look at 'reordering_code.org' to know more about
it.
[optional] clobbers: list of lvalues that can have an observable
side effect. This should only be used to do fine adjustments in
the generated code in order to move to the outside of the atomic
section some code that does't need to be there and, first of
all, when the timing is a critical factor.
*/
template<typename AtTheEnd = restore_t>
class atomic {
uint8_t _sreg;
static_assert(!detail::is_same<AtTheEnd, off_at_the_end_t>::value,
"It isn't allowed the policy 'off_at_the_end' to use "
"'atomic' because it doesn't make sense.");
public:
constexpr static bool is_restore
{detail::is_same<AtTheEnd, restore_t>::value};
template<typename... Clobbers>
explicit atomic(AtTheEnd = AtTheEnd{}, Clobbers&... clobbers) {
clobber(clobbers...);
if(is_restore) _sreg = io::sreg;
off();
}
~atomic() {
if(is_restore) io::sreg = _sreg;
else on();
}
};
/** RAII to enable interrupts in the beginning and restore SREG state
or disable at the end.
By default the state of the status register that was before the
interruptible scope is recovered at the end of the scope, which
means that interrupts are disabled only if they were disabled
before. If the argument 'off_at_the_end' is passed then the
interrupts are always disabled at the end of the scope.
Example:
{
interruptible si; //same as 'interruptible sa(restore)'
//my code
}
{
interruptible si(off_at_the_end);
//my code
}
Note, the compiler optimizer can reorder instructions around the
interruptible scope when that instructions doesn't have observable
side effects. Take a look at 'reordering_code.org' to know more
about it.
[optional] clobbers: list of lvalues that can have an observable
side effect. This should only be used to do fine adjustments in
the generated code in order to move to the outside of the atomic
section some code that does't need to be there and, first of
all, when the timing is a critical factor.
*/
template<typename AtTheEnd = restore_t>
class interruptible {
uint8_t _sreg;
static_assert(!detail::is_same<AtTheEnd, on_at_the_end_t>::value,
"It isn't allowed the policy 'on_at_the_end' to use "
"'interruptible' because it doesn't make sense.");
public:
constexpr static bool is_restore
{detail::is_same<AtTheEnd, restore_t>::value};
template<typename... Clobbers>
explicit interruptible(AtTheEnd = AtTheEnd{}, Clobbers&... clobbers) {
clobber(clobbers...);
if(is_restore) _sreg = io::sreg;
on();
}
~interruptible() {
if(is_restore) io::sreg = _sreg;
else off();
}
};
#if __cplusplus >= 201703L
inline constexpr restore_t restore;
inline constexpr on_at_the_end_t on_at_the_end;
inline constexpr off_at_the_end_t off_at_the_end;
#else
namespace {
constexpr auto& restore{detail::global<restore_t>::instance};
constexpr auto& on_at_the_end{detail::global<on_at_the_end_t>::instance};
constexpr auto& off_at_the_end{detail::global<off_at_the_end_t>::instance};
}//anonymous namespace
#endif
/** Defines an ISR(Interrupt Service Routine)
The parameter 'vector' is the name of an interrupt vector name
used by the vector table. The variable argument can be use to put
additional attributes.
*/
#define AVRINT_ISR(vector, ...) \
extern "C" \
[[gnu::signal, gnu::used, gnu::externally_visible, __VA_ARGS__]] void vector()
}}
#include "avr/interrupt/mcu.hpp"