src/platforms/arm/teensy/MUTEX_README.md
The Teensy platform now supports optional real mutex implementations when threading libraries are available. This provides thread-safe synchronization for Teensy 3.x and 4.x boards.
Platforms: Primarily Teensy 4.x (Cortex-M7) Type: Preemptive multitasking Thread Safety: True concurrent thread safety
FreeRTOS provides preemptive multitasking where the scheduler can interrupt a running thread at any time to switch to another thread. This offers the strongest thread safety guarantees.
Installation:
# Arduino Library Manager or PlatformIO
# Search for: "FreeRTOS" or "Arduino_FreeRTOS_ARM"
Usage:
#include <Arduino_FreeRTOS_ARM.h> // Can be included in any order
#include <FastLED.h>
// Mutexes will automatically use FreeRTOS implementation
Platforms: Teensy 3.x, 4.x Type: Cooperative multitasking (yield-based) Thread Safety: Cooperative synchronization
TeensyThreads uses cooperative multitasking where threads explicitly yield control. Thread switches only occur at yield points, not during arbitrary code execution.
Important: This is NOT preemptive multitasking. Interrupts can still occur at any time.
Installation:
# Arduino Library Manager or PlatformIO
# Search for: "TeensyThreads"
# GitHub: https://github.com/ftrias/TeensyThreads
Usage:
#include <TeensyThreads.h> // Can be included in any order
#include <FastLED.h>
// Mutexes will automatically use TeensyThreads implementation
If neither FreeRTOS nor TeensyThreads is detected, FastLED automatically falls back to fake mutexes that provide basic lock tracking for debugging but are NOT thread-safe.
The implementation automatically detects threading libraries in this priority order:
INC_FREERTOS_H is defined) - highest priorityTEENSY_THREADS_H is defined)Key Point: If both libraries are included, FreeRTOS takes priority because it provides preemptive multitasking.
Both real and fake implementations provide the same API:
namespace fl {
class MutexTeensy {
public:
void lock(); // Block until lock acquired
void unlock(); // Release the lock
bool try_lock(); // Try to acquire without blocking (returns true/false)
};
class RecursiveMutexTeensy {
public:
void lock(); // Block until lock acquired (same thread can lock multiple times)
void unlock(); // Release one lock level
bool try_lock(); // Try to acquire without blocking
};
}
#include <TeensyThreads.h>
#include <FastLED.h>
fl::MutexTeensy gLedMutex;
CRGB leds[NUM_LEDS];
void thread1() {
while (true) {
gLedMutex.lock();
leds[0] = CRGB::Red;
FastLED.show();
gLedMutex.unlock();
threads.yield(); // Cooperative: must explicitly yield
}
}
void thread2() {
while (true) {
gLedMutex.lock();
leds[0] = CRGB::Blue;
FastLED.show();
gLedMutex.unlock();
threads.yield();
}
}
void setup() {
FastLED.addLeds<WS2812, DATA_PIN>(leds, NUM_LEDS);
threads.addThread(thread1);
threads.addThread(thread2);
}
#include <Arduino_FreeRTOS_ARM.h>
#include <FastLED.h>
fl::RecursiveMutexTeensy gRecursiveMutex;
void recursiveFunction(int depth) {
gRecursiveMutex.lock();
if (depth > 0) {
recursiveFunction(depth - 1); // Can lock multiple times
}
gRecursiveMutex.unlock();
}
void task1(void* params) {
while (true) {
recursiveFunction(5); // Locks 5 times recursively
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void setup() {
xTaskCreate(task1, "Task1", 1000, NULL, 1, NULL);
vTaskStartScheduler();
}
#include <TeensyThreads.h>
#include <FastLED.h>
fl::MutexTeensy gMutex;
void criticalSection() {
fl::unique_lock<fl::MutexTeensy> lock(gMutex);
// Mutex automatically locked here
leds[0] = CRGB::Green;
FastLED.show();
// Mutex automatically unlocked when 'lock' goes out of scope
}
xSemaphoreCreateMutex() for standard mutexesxSemaphoreCreateRecursiveMutex() for recursive mutexesFiles:
mutex_teensy.h - Class declarationsmutex_teensy.cpp - FreeRTOS implementation (only compiled when FreeRTOS detected)Threads::Mutex for standard mutexesFiles:
mutex_teensy.h - Complete inline implementation (no .cpp needed)bool mLocked for standard mutex trackingint mLockCount for recursive mutex trackingFiles:
mutex_teensy.h - Complete inline implementationAll Teensy platforms support ARM memory barriers (DMB instruction), including Cortex-M0+ on Teensy LC:
FastLED's atomic operations automatically use appropriate memory barriers on all Teensy platforms.
| Feature | FreeRTOS | TeensyThreads |
|---|---|---|
| Scheduling | Preemptive | Cooperative |
| Thread Safety | True concurrent | Yield-based |
| Overhead | Higher (scheduler) | Lower |
| Priority | Task priorities | No priorities |
| ISR Safety | ISR-safe primitives | Not ISR-safe |
| Recommended For | Teensy 4.x | Teensy 3.x, simple apps |
Choose the right library:
Include order is flexible:
#include <Threading_Library.h> // Can be in any order
#include <FastLED.h>
FastLED automatically detects threading libraries using __has_include.
Use RAII lock guards:
fl::unique_lock<fl::MutexTeensy> lock(mutex); // Automatic unlock
Avoid long critical sections:
FastLED.show() inside locks if possibleTeensyThreads requires explicit yields:
threads.yield(); // Must yield in cooperative mode
Problem: Threading library not installed or not in library search path.
Solution:
Problem: FreeRTOS mutex creation or locking failed.
Solution:
xPortGetFreeHeapSize())vTaskStartScheduler())Problem: Trying to unlock a recursive mutex from a different thread.
Solution: Only the thread that locked the mutex can unlock it. Check your threading logic.
FASTLED_TEENSY_HAS_FREERTOS // 1 if FreeRTOS detected, 0 otherwise
FASTLED_TEENSY_HAS_THREADS // 1 if TeensyThreads detected, 0 otherwise
FASTLED_TEENSY_REAL_MUTEX // 1 if real mutex available, 0 for fake
FastLED uses C++17's __has_include preprocessor feature to automatically detect available threading libraries:
<FreeRTOS.h> or <Arduino_FreeRTOS_ARM.h> at compile time<TeensyThreads.h> at compile timeThis approach eliminates include-order dependencies and makes FastLED easier to use.
This implementation is part of FastLED and follows the FastLED license.