forked from FastLED/FastLED
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathisr.h
More file actions
474 lines (414 loc) · 17.2 KB
/
isr.h
File metadata and controls
474 lines (414 loc) · 17.2 KB
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
/*
FastLED — Cross-Platform ISR Handler API
-----------------------------------------
Unified interrupt service routine attachment API that works across
all FastLED-supported platforms: ESP32, Teensy, AVR, STM32, stub, etc.
License: MIT (FastLED)
*/
#pragma once
// allow-include-after-namespace
#include "fl/compiler_control.h"
#include "fl/force_inline.h"
#include "fl/stl/bit_cast.h"
#include "fl/stl/stdint.h"
#include "fl/stl/cstddef.h"
namespace fl {
namespace isr {
// =============================================================================
// ISR Handler Function Type
// =============================================================================
/**
* ISR handler function signature.
*
* @param user_data: User-provided context pointer (can be nullptr)
*
* Requirements:
* - Must be placed in fast memory using the FL_IRAM attribute (see fl/compiler_control.h)
* - ESP32/ESP8266: Places code in IRAM (internal SRAM)
* - STM32: Places code in .text_ram section
* - Other platforms: No-op (functions execute from normal memory)
* - Should execute quickly (typically <10us)
* - Cannot use blocking operations
* - Cannot use malloc/free
* - Platform-specific restrictions may apply (see platform docs)
*
* Example:
* void FL_IRAM my_timer_isr(void* user_data) {
* // Fast ISR handler code here
* }
*/
typedef void (*isr_handler_t)(void* user_data);
// =============================================================================
// Priority Level Constants (Forward Declaration)
// =============================================================================
// Abstract priority levels (platform-independent, higher value = higher priority)
// Platforms map these to their native priority schemes internally
// These are abstract priority levels that map to platform-specific values
// Declared here so they can be used in isr_config_t constructor
constexpr u8 ISR_PRIORITY_LOW = 1; // Lowest priority
constexpr u8 ISR_PRIORITY_DEFAULT = 1; // Default priority (alias for LOW)
constexpr u8 ISR_PRIORITY_MEDIUM = 2; // Medium priority
constexpr u8 ISR_PRIORITY_HIGH = 3; // High priority (recommended max)
constexpr u8 ISR_PRIORITY_CRITICAL = 4; // Critical (experimental, platform-dependent)
constexpr u8 ISR_PRIORITY_MAX = 7; // Maximum (may require assembly, platform-dependent)
// =============================================================================
// ISR Configuration Structure
// =============================================================================
/**
* Configuration for ISR attachment.
*
* This structure provides platform-independent ISR configuration while
* allowing platform-specific optimizations through the flags field.
*/
struct isr_config_t {
isr_handler_t handler; // Handler function pointer
void* user_data; // User context (passed to handler)
u32 frequency_hz; // Timer frequency in Hz (0 = GPIO/external interrupt)
u8 priority; // Priority level (platform-dependent range)
u32 flags; // Platform-specific flags (see below)
// Constructor with defaults
isr_config_t()
: handler(nullptr)
, user_data(nullptr)
, frequency_hz(0)
, priority(ISR_PRIORITY_DEFAULT)
, flags(0)
{}
};
// =============================================================================
// ISR Configuration Flags (Platform-Specific)
// =============================================================================
// Common flags (applicable to most platforms)
constexpr u32 ISR_FLAG_IRAM_SAFE = (1u << 0); // Place in IRAM (ESP32)
constexpr u32 ISR_FLAG_EDGE_RISING = (1u << 1); // Edge-triggered, rising edge
constexpr u32 ISR_FLAG_EDGE_FALLING = (1u << 2); // Edge-triggered, falling edge
constexpr u32 ISR_FLAG_LEVEL_HIGH = (1u << 3); // Level-triggered, high level
constexpr u32 ISR_FLAG_LEVEL_LOW = (1u << 4); // Level-triggered, low level
constexpr u32 ISR_FLAG_ONE_SHOT = (1u << 5); // One-shot timer (vs auto-reload)
constexpr u32 ISR_FLAG_MANUAL_TICK = (1u << 6); // Manual tick mode (simulation/testing)
// ESP32-specific flags
constexpr u32 ISR_FLAG_ESP_SHARED = (1ul << 16); // Shared interrupt (ESP32)
constexpr u32 ISR_FLAG_ESP_LOWMED = (1ul << 17); // Low/medium priority shared (ESP32)
// STM32-specific flags
constexpr u32 ISR_FLAG_STM32_PREEMPT = (1ul << 18); // Use preemption priority (STM32)
// =============================================================================
// ISR Handle Type
// =============================================================================
/**
* Opaque handle to an attached ISR.
* Platform implementations store their native handle here.
*/
struct isr_handle_t {
void* platform_handle; // Platform-specific handle
isr_handler_t handler; // Handler function (for validation)
void* user_data; // User data (for validation)
u8 platform_id; // Platform identifier (for runtime checks)
isr_handle_t()
: platform_handle(nullptr)
, handler(nullptr)
, user_data(nullptr)
, platform_id(0)
{}
// Check if handle is valid
bool is_valid() const { return platform_handle != nullptr; }
};
// =============================================================================
// Priority Level Documentation
// =============================================================================
// Priority constants are declared earlier in the file (before isr_config_t)
// Platform-specific priority ranges:
// - ESP32 (Xtensa): 1-3 (C handlers), 4-5 (assembly required)
// - ESP32-C3 (RISC-V): 1-7 (C handlers, but 4-7 may have limitations)
// - Teensy: NVIC priority 0-255 (lower number = higher priority)
// - AVR: No hardware priority (interrupts can't nest by default)
// - STM32: NVIC priority 0-15 (configurable preemption/sub-priority)
// - Stub: All priorities treated equally
// =============================================================================
// Cross-Platform ISR API
// =============================================================================
/**
* Attach a timer-based ISR handler.
*
* @param config: ISR configuration structure
* @param handle: Output handle for detachment (can be nullptr if handle not needed)
* @return: 0 on success, negative error code on failure
*
* Example (ESP32):
* isr_config_t cfg;
* cfg.handler = my_isr_handler;
* cfg.user_data = &my_context;
* cfg.frequency_hz = 1000000; // 1 MHz timer
* cfg.priority = ISR_PRIORITY_HIGH;
* cfg.flags = ISR_FLAG_IRAM_SAFE;
*
* isr_handle_t handle;
* int result = isr::attachTimerHandler(cfg, &handle);
*
* Platform-specific notes:
* - ESP32: Uses gptimer, supports frequencies 1 Hz - 80 MHz
* - Teensy: Uses IntervalTimer, supports up to 150kHz typical
* - AVR: Uses Timer1, frequency depends on prescaler settings
* - STM32: Uses hardware timer, frequency depends on system clock
* - Stub: Uses software simulation, unlimited frequency
*/
int attachTimerHandler(const isr_config_t& config, isr_handle_t* handle = nullptr);
/**
* Attach an external interrupt handler (GPIO-based).
*
* @param pin: GPIO pin number (platform-specific numbering)
* @param config: ISR configuration structure (frequency_hz ignored)
* @param handle: Output handle for detachment (can be nullptr if handle not needed)
* @return: 0 on success, negative error code on failure
*
* Example:
* isr_config_t cfg;
* cfg.handler = my_gpio_handler;
* cfg.flags = ISR_FLAG_EDGE_RISING;
* cfg.priority = ISR_PRIORITY_MEDIUM;
*
* int result = isr::attachExternalHandler(2, cfg);
*
* Platform-specific notes:
* - ESP32: Uses esp_intr_alloc with GPIO interrupt
* - Teensy: Uses attachInterrupt()
* - AVR: Uses attachInterrupt() or direct register manipulation
* - STM32: Uses HAL_NVIC_SetPriority and EXTI configuration
* - Stub: Simulates GPIO events
*/
int attachExternalHandler(u8 pin, const isr_config_t& config, isr_handle_t* handle = nullptr);
/**
* Detach an ISR handler.
*
* @param handle: Handle returned by attachTimerHandler or attachExternalHandler
* @return: 0 on success, negative error code on failure
*
* After detachment, the handle is invalidated and should not be reused.
*/
int detachHandler(isr_handle_t& handle);
/**
* Enable an ISR (after temporary disable).
*
* @param handle: Handle to the ISR
* @return: 0 on success, negative error code on failure
*/
int enableHandler(isr_handle_t& handle);
/**
* Disable an ISR temporarily (without detaching).
*
* @param handle: Handle to the ISR
* @return: 0 on success, negative error code on failure
*/
int disableHandler(isr_handle_t& handle);
/**
* Query if an ISR is currently enabled.
*
* @param handle: Handle to the ISR
* @return: true if enabled, false if disabled or invalid
*/
bool isHandlerEnabled(const isr_handle_t& handle);
/**
* Get platform-specific error description.
*
* @param error_code: Error code returned by attachTimerHandler/attachExternalHandler
* @return: Human-readable error string
*/
const char* getErrorString(int error_code);
// =============================================================================
// Platform Information
// =============================================================================
/**
* Get the platform name.
* @return: String like "ESP32", "Teensy", "AVR", "STM32", "Stub"
*/
const char* getPlatformName();
/**
* Get the maximum timer frequency supported by this platform.
* @return: Maximum frequency in Hz, or 0 if unlimited
*/
u32 getMaxTimerFrequency();
/**
* Get the minimum timer frequency supported by this platform.
* @return: Minimum frequency in Hz
*/
u32 getMinTimerFrequency();
/**
* Get the maximum priority level supported by this platform.
* @return: Maximum priority value
*/
u8 getMaxPriority();
/**
* Check if assembly is required for a given priority level.
* @param priority: Priority level to check
* @return: true if assembly handler required, false if C handler allowed
*/
bool requiresAssemblyHandler(u8 priority);
// =============================================================================
// ISR-Optimized Memory Copy Utilities
// =============================================================================
/// @brief Check if a pointer is aligned to a specific byte boundary
/// @param ptr Pointer to check
/// @param alignment Alignment requirement in bytes (must be power of 2)
/// @return true if aligned, false otherwise
FASTLED_FORCE_INLINE bool is_aligned(const void* ptr, size_t alignment) {
return (fl::ptr_to_int(ptr) & (alignment - 1)) == 0;
}
/// @brief ISR-optimized 32-bit block copy for 4-byte aligned memory
/// @param dst Destination pointer (must be 4-byte aligned)
/// @param src Source pointer (must be 4-byte aligned)
/// @param count Number of 32-bit words to copy (NOT bytes)
/// @note Only call with 4-byte aligned pointers and valid count
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memcpy32(u32* FL_RESTRICT_PARAM dst,
const u32* FL_RESTRICT_PARAM src,
size_t count) {
// Optimized 32-bit word copy - compiler will often vectorize this
for (size_t i = 0; i < count; i++) {
dst[i] = src[i];
}
}
/// @brief ISR-optimized 16-bit block copy for 2-byte aligned memory
/// @param dst Destination pointer (must be 2-byte aligned)
/// @param src Source pointer (must be 2-byte aligned)
/// @param count Number of 16-bit words to copy (NOT bytes)
/// @note Only call with 2-byte aligned pointers and valid count
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memcpy16(u16* FL_RESTRICT_PARAM dst,
const u16* FL_RESTRICT_PARAM src,
size_t count) {
// Optimized 16-bit word copy
for (size_t i = 0; i < count; i++) {
dst[i] = src[i];
}
}
/// @brief ISR-optimized byte copy
/// @param dst Destination pointer
/// @param src Source pointer
/// @param count Number of bytes to copy
/// @note No alignment requirements
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memcpybyte(u8* FL_RESTRICT_PARAM dst,
const u8* FL_RESTRICT_PARAM src,
size_t count) {
// Byte-by-byte copy
for (size_t i = 0; i < count; i++) {
dst[i] = src[i];
}
}
/// @brief ISR-optimized memcpy with alignment detection and switch dispatch
/// @param dst Destination pointer
/// @param src Source pointer
/// @param num_bytes Number of bytes to copy
/// @note Automatically uses 32-bit, 16-bit, or byte copy based on alignment
/// @note Uses switch for dispatch (compiler may optimize to jump table)
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memcpy(void* FL_RESTRICT_PARAM dst,
const void* FL_RESTRICT_PARAM src,
size_t num_bytes) {
// Branchless index calculation (but switch still has branches):
// - Both 4-byte aligned AND size multiple of 4: index 2 (memcpy32)
// - Both 2-byte aligned AND size multiple of 2: index 1 (memcpy16)
// - Otherwise: index 0 (memcpybyte)
uintptr_t dst_addr = fl::ptr_to_int(dst);
uintptr_t src_addr = fl::ptr_to_int(src);
// Branchless: Convert boolean to integer (0 or 1) using bitwise ops
// Check if all are 2-byte aligned (LSB is 0)
size_t align2 = (((dst_addr | src_addr | num_bytes) & 1) == 0);
// Check if all are 4-byte aligned (lower 2 bits are 0)
size_t align4 = (((dst_addr | src_addr | num_bytes) & 3) == 0);
// Branchless index calculation using arithmetic
int index = align2 + align4;
// Switch dispatch (compiler optimizes to jump table on most platforms)
// Note: This introduces a branch, but it's a predicted indirect jump
switch (index) {
case 2:
memcpy32(static_cast<u32*>(dst),
static_cast<const u32*>(src),
num_bytes >> 2);
break;
case 1:
memcpy16(static_cast<u16*>(dst),
static_cast<const u16*>(src),
num_bytes >> 1);
break;
case 0:
default:
memcpybyte(static_cast<u8*>(dst),
static_cast<const u8*>(src),
num_bytes);
break;
}
}
// =============================================================================
// ISR-Safe Memory Zero Utilities
// =============================================================================
/// @brief ISR-safe memset replacement (byte-by-byte zero)
/// @param dest Destination pointer
/// @param count Number of bytes to zero
/// @note fl::memset is not allowed in ISR context on some platforms
/// @note This function uses a simple loop to zero memory
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memset_zero_byte(u8* dest, size_t count) {
for (size_t i = 0; i < count; i++) {
dest[i] = 0x0;
}
}
/// @brief ISR-safe word-aligned memset (4-byte writes)
/// @param dest Destination pointer (must be 4-byte aligned)
/// @param count Number of bytes to zero (will process in 4-byte chunks)
/// @note Handles remainder bytes using byte writes
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memset_zero_word(u8* dest, size_t count) {
u32* dest32 = fl::bit_cast<u32*>(dest);
size_t count32 = count / 4;
size_t remainder = count % 4;
for (size_t i = 0; i < count32; i++) {
dest32[i] = 0;
}
if (remainder > 0) {
u8* remainder_ptr = dest + (count32 * 4);
memset_zero_byte(remainder_ptr, remainder);
}
}
/// @brief ISR-safe memset with alignment optimization
/// @param dest Destination pointer
/// @param count Number of bytes to zero
/// @note Automatically selects word or byte writes based on alignment
FL_OPTIMIZE_FUNCTION FL_IRAM FASTLED_FORCE_INLINE
void memset_zero(u8* dest, size_t count) {
uintptr_t address = fl::ptr_to_int(dest);
// If aligned AND large enough, use fast word writes
if ((address % 4 == 0) && (count >= 4)) {
memset_zero_word(dest, count);
} else {
// Unaligned or small: use byte writes
memset_zero_byte(dest, count);
}
}
// =============================================================================
// CriticalSection RAII Helper
// =============================================================================
/// @brief RAII helper for critical sections (interrupt disable/enable)
/// Automatically disables interrupts on construction and enables on destruction
/// Use this for protecting shared data accessed from both ISR and main context
///
/// Example:
/// {
/// CriticalSection cs; // Interrupts disabled here
/// shared_data = new_value;
/// } // Interrupts automatically re-enabled here
class CriticalSection {
public:
CriticalSection();
~CriticalSection();
// Non-copyable
CriticalSection(const CriticalSection&) = delete;
CriticalSection& operator=(const CriticalSection&) = delete;
};
} // namespace isr
} // namespace fl
// =============================================================================
// Platform-Specific ISR Implementation (includes noInterrupts/interrupts)
// =============================================================================
// Include platform-specific ISR implementation after API declaration
// This provides both ISR handler functions AND interrupt control functions
// NOTE: Platform implementations are in platforms/isr.h, included by fl/isr.cpp.hpp