#include <Arduino_FreeRTOS.h>
#include <task.h>
#include <semphr.h> // add the FreeRTOS functions for Semaphores (or Flags).
#include <FreeRTOSConfig.h>
/* Define the traceTASK_SWITCHED_IN() and *_OUT() macros become HIGH when the tasks is switched in, and low w
when switched out. This has to be done in the RTOS Library. */
//#define traceTASK_SWITCHED_IN() digitalWrite( (int)pxCurrentTCB->pxTaskTag, HIGH )
//#define traceTASK_SWITCHED_OUT() digitalWrite((int)pxCurrentTCB->pxTaskTag, LOW )
/* Define the pin on which the tasks is being monitored (to be put in each task) */ /*modified by Markus Pfeil*/
/* vTaskSetApplicationTaskTag( NULL, ( void * )data.debug_pin); */
#include "portmacro.h"
// --- Hardware Definitions (Angepasst für Arduino Uno) ---
// Pin 2 ist der Interrupt-Pin 0 (INT0) beim Uno und eignet sich ideal für den Taster.
#define BUTTON_PIN 2
#define PERIODIC_LED_PIN 13 // On-Board LED des Uno für periodische Aufgabe
#define BUTTON_TASK_LED_PIN 12 // Zusätzliche LED für die Taster-Aufgabe
// --- Semaphore Handles ---
// Der periodische Task wartet auf dieses Semaphor, das vom Timer ISR gegeben wird.
SemaphoreHandle_t xTimerSemaphore = NULL;
// Der Taster-Task wartet auf dieses Semaphor, das vom Pin ISR gegeben wird.
SemaphoreHandle_t xButtonSemaphore = NULL;
// --- Timer Konfiguration (ATmega328P spezifisch: Timer1 im CTC-Modus) ---
// Wir verwenden Timer1, um genau alle 2 Sekunden (2000 ms) auszulösen.
// F_CPU (16MHz) / Prescaler (1024) / (OCR1A + 1) = 0.5 Hz
// OCR1A = (16,000,000 / 1024 / 0.5) - 1 = 31249
const unsigned int OCR1A_VALUE = 31249;
// =================================================================
// 1. INTERRUPT SERVICE ROUTINES (ISRs)
// =================================================================
// ISR für den Taster-Pin-Interrupt (INT0, Pin 2)
void vButtonISR() {
BaseType_t xHigherPriorityTaskWoken pdFALSE;
// Gib das Semaphor. xSemaphoreGiveFromISR muss in einer ISR verwendet werden.
xSemaphoreGiveFromISR(xButtonSemaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
taskYIELD(); // if this is left out, the scheduler will not be invoked directly and the task only switched in on the next tick!!!
}
}
ISR(TIMER1_COMPA_vect){//timer1 interrupt 100Hz gives semaphore for Toggle Task
BaseType_t xHigherPriorityTaskWoken pdFALSE;
digitalWrite(8, !digitalRead(8));
xSemaphoreGiveFromISR(xTimerSemaphore, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
taskYIELD(); // if this is left out, the scheduler will not be invoked directly and the task only switched in on the next tick!!!
}
}
// =================================================================
// 2. TASKS
// =================================================================
// Task ausgelöst durch den Timer ISR (periodisch).
void vPeriodicTask(void *pvParameters) {
vTaskSetApplicationTaskTag( NULL, ( void * )11);
bool ledState = LOW;
Serial.println("Periodic Task: Ready to blink LED (Pin 13) and listen for Timer Sem.");
// Buffer für Serial-Ausgabe, da printf nicht unterstützt wird.
char buffer[60];
for (;;) {
// Warte unendlich (portMAX_DELAY) auf das Semaphor-Signal vom Timer ISR.
if (xSemaphoreTake(xTimerSemaphore, portMAX_DELAY) == pdTRUE) {
// Semaphor empfangen! Führe die periodische Aktion aus.
ledState = !ledState;
digitalWrite(PERIODIC_LED_PIN, ledState);
// FIX: Serial.printf durch sprintf und Serial.println ersetzt.
sprintf(buffer, "Periodic Task: Triggered! LED %s.", ledState ? "ON" : "OFF");
Serial.println(buffer);
}
}
}
// Task ausgelöst durch den Taster Pin ISR (asynchron).
void vButtonTask(void *pvParameters) {
vTaskSetApplicationTaskTag( NULL, ( void * )10);
Serial.println("Button Task: Ready to listen for Button Sem (Pin 12 LED).");
for (;;) {
// Warte unendlich auf das Semaphor-Signal vom Taster ISR.
if (xSemaphoreTake(xButtonSemaphore, portMAX_DELAY) == pdTRUE) {
// Semaphor empfangen! Führe die Taster-Aktion aus.
Serial.println("Button Task: Button Press detected! Executing action.");
// Blinkt die dedizierte LED einmal kurz, um die Aktion zu zeigen
digitalWrite(BUTTON_TASK_LED_PIN, !digitalRead(BUTTON_TASK_LED_PIN));
}
}
}
// =================================================================
// 3. ARDUINO SETUP UND LOOP
// =================================================================
void setup() {
Serial.begin(9600); // Übliche Uno-Baudrate
// --- Pin Setup ---
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(PERIODIC_LED_PIN, OUTPUT);
pinMode(BUTTON_TASK_LED_PIN, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
digitalWrite(PERIODIC_LED_PIN, LOW);
digitalWrite(BUTTON_TASK_LED_PIN, LOW);
// --- Semaphore Creation ---
xTimerSemaphore = xSemaphoreCreateBinary();
xButtonSemaphore = xSemaphoreCreateBinary();
if (xTimerSemaphore == NULL || xButtonSemaphore == NULL) {
Serial.println("ERROR: Could not create one or more semaphores!");
// Bei Fehler: Aufgabe beenden
for(;;) { vTaskDelay(pdMS_TO_TICKS(1000)); }
}
// --- Interrupt Setup (Button Pin 2) ---
// Der Taster-Interrupt ruft vButtonISR() auf der FALLING-Flanke auf.
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), vButtonISR, FALLING);
Serial.println("Interrupt attached to Button Pin 2 (INT0).");
// --- Timer Setup (Periodic: ATmega328P Timer1) ---
cli(); // Deaktiviere globale Interrupts während der Timer-Konfiguration
// Setze Timer/Counter Control Register A (TCCR1A)
// COM1A1:0=00, COM1B1:0=00 (Normal Port Operation)
// WGM11:10=01 (CTC Mode)
TCCR1A = 0;
TCCR1A = (1 << WGM11);
// Setze Timer/Counter Control Register B (TCCR1B)
// WGM13:12=10 (CTC Mode)
// CS12:10=101 (Prescaler 1024)
TCCR1B = 0;
TCCR1B |= (1 << WGM12) | (1 << WGM13);
TCCR1B |= (1 << CS10) | (1 << CS12);
// Setze Compare Match Register 1 A (Zählwert für 2s)
OCR1A = OCR1A_VALUE;
// Timer/Counter 1 Interrupt Mask Register (TIMSK1)
// OCIE1A = 1 (Aktiviere Interrupt für Compare Match A)
TIMSK1 |= (1 << OCIE1A);
sei(); // Aktiviere globale Interrupts
// Buffer für Serial-Ausgabe im Setup
char setup_buffer[60];
// FIX: Serial.printf durch sprintf und Serial.println ersetzt.
sprintf(setup_buffer, "Hardware Timer1 set to trigger every 2 seconds (OCR1A=%d).", OCR1A_VALUE);
Serial.println(setup_buffer);
// --- Task Creation ---
// Die Prioritäten bleiben gleich.
xTaskCreate(vPeriodicTask, "TimerTask", 128, NULL, 1, NULL);
xTaskCreate(vButtonTask, "ButtonTask", 128, NULL, 2, NULL);
Serial.println("All tasks and interrupts set up. Running FreeRTOS...");
}
// Mit FreeRTOS auf dem Uno kann loop() leer bleiben.
void loop() {
// Die Tasks erledigen die Arbeit.
}