#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h> // For semaphores (mutex)
// --- Pin Definitions ---
const int greenLedPin = 7;
const int yellowLedPin = 6;
const int redLedPin = 5;
const int buttonPin = 8; // GPIO for the button
// --- Shared Resource & Synchronization ---
volatile int sharedCounter = 0; // Use volatile as it's modified by ISR/multiple tasks
SemaphoreHandle_t xMutex; // Mutex to protect sharedCounter
// --- Interrupt Service Routine (ISR) for Button ---
// This function is called when the button is pressed.
// It MUST be short and fast, and avoid blocking calls.
void IRAM_ATTR buttonISR() {
// Toggle the yellow LED directly here, as it's a simple, fast operation.
// In a real application, you might just signal a task via a queue/semaphore
// to handle more complex actions.
digitalWrite(yellowLedPin, !digitalRead(yellowLedPin));
// If you needed to update sharedCounter from ISR, you'd use a queue/atomic operation
// For example, sending a message to a queue which a task processes:
// BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// xQueueSendFromISR(xQueueHandle, &data, &xHigherPriorityTaskWoken);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
// --- Task 1: Green LED Blink (Regular Blinking) ---
void greenLedBlinkTask(void *parameter) {
const long blinkInterval = 200; // 0.2 seconds
pinMode(greenLedPin, OUTPUT);
for (;;) {
digitalWrite(greenLedPin, HIGH);
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
digitalWrite(greenLedPin, LOW);
vTaskDelay(pdMS_TO_TICKS(blinkInterval));
}
}
// --- Task 2: Shared Counter Updater (with Mutex Protection) ---
// This task increments a shared counter and protects it with a mutex.
// It also uses the Red LED to indicate when the mutex is held.
void sharedCounterTask(void *parameter) {
const long updateInterval = 1000; // 1 second
pinMode(redLedPin, OUTPUT); // Red LED as mutex indicator
for (;;) {
// Attempt to acquire the mutex. If unavailable, this task will block
// until the mutex is released by another task.
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
// Mutex acquired! Now it's safe to access sharedCounter.
digitalWrite(redLedPin, HIGH); // Red LED ON to indicate mutex held
Serial.println("Mutex acquired. Updating sharedCounter...");
sharedCounter++; // Accessing the shared resource
Serial.print("sharedCounter value: ");
Serial.println(sharedCounter);
vTaskDelay(pdMS_TO_TICKS(200)); // Simulate some work while holding the mutex
// Release the mutex, allowing other tasks to acquire it.
xSemaphoreGive(xMutex);
digitalWrite(redLedPin, LOW); // Red LED OFF
Serial.println("Mutex released.");
} else {
Serial.println("Failed to acquire mutex (should not happen with portMAX_DELAY)");
}
vTaskDelay(pdMS_TO_TICKS(updateInterval)); // Delay before next attempt
}
}
void setup() {
Serial.begin(115200);
Serial.println("Interrupt Handling and Synchronization Demo");
// --- Initialize Pins ---
pinMode(greenLedPin, OUTPUT);
pinMode(yellowLedPin, OUTPUT);
pinMode(redLedPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP); // Use internal pull-up for the button
// --- Create Mutex ---
// A binary semaphore (mutex) for protecting sharedCounter
xMutex = xSemaphoreCreateMutex();
if (xMutex == NULL) {
Serial.println("Failed to create mutex!");
// Handle error, maybe go into an infinite loop or reset
while (true);
}
// --- Attach Interrupt ---
// Attach the buttonISR to the button pin.
// RISING: Interrupt on button release (pull-up, then button connects to GND)
// FALLING: Interrupt on button press (pull-up, then button connects to GND)
// We're using FALLING because button connects to GND when pressed.
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonISR, FALLING);
// --- Create FreeRTOS Tasks ---
xTaskCreate(
greenLedBlinkTask, // Task function
"Green LED Task", // Name of task
1024, // Stack size (bytes)
NULL, // Parameter to pass to the task
1, // Task priority
NULL // No task handle needed for this demo
);
xTaskCreate(
sharedCounterTask, // Task function
"Counter Task", // Name of task
2048, // Stack size (larger for more complex tasks)
NULL, // Parameter
2, // Higher priority than LED task to show mutex in action
NULL
);
// setup() exits, and FreeRTOS scheduler takes over
}
void loop() {
// loop() is generally empty when using FreeRTOS tasks
// You might put very low-priority, non-critical background tasks here
// or a simple vTaskDelay(1) to yield to other tasks.
vTaskDelay(1); // Yield to other tasks
}