1. Home
  2. Docs
  3. ESP-IDF / ESP32
  4. Giriş
  5. ESP-IDF ile FreeRTOS Görevleri (Tasks) ve Kuyruklar (Queues)

ESP-IDF ile FreeRTOS Görevleri (Tasks) ve Kuyruklar (Queues)

Print

Önceki yazımızda GPIO kesmelerini (ISR) işlerken, kesme anında yapılan işlemi bir “Kuyruk” (Queue) üzerinden ana “Göreve” (Task) aktarmıştık. ESP32, çift çekirdekli (dual-core) güçlü bir mikrodenetleyicidir ve ESP-IDF altyapısı, varsayılan gerçek zamanlı işletim sistemi olarak FreeRTOS kullanır.

Arduino’daki tekil loop() döngüsü yerine, FreeRTOS sayesinde aynı anda çalışan, birbirini bekletmeyen ve farklı önceliklere sahip bağımsız görevler (multitasking) oluşturabiliriz.

1. Görev (Task) Nedir ve Nasıl Oluşturulur?

Görevler, kendi sonsuz döngüsü (for(;;) veya while(1)) ve kendi bağımsız bellek alanı (Stack) olan kod parçacıklarıdır. ESP-IDF’te görev oluşturmak için xTaskCreate veya görevi belirli bir çekirdeğe (Core 0 veya Core 1) atamak için xTaskCreatePinnedToCore fonksiyonları kullanılır.

  • Görev Fonksiyonu: Görevin yapacağı işi barındıran C fonksiyonu.
  • Stack Boyutu: Görevin kullanabileceği RAM miktarı (ESP-IDF’te byte cinsinden belirtilir).
  • Öncelik (Priority): Yüksek değere sahip görevler, düşük öncelikli olanları beklemeye alır ve CPU’yu ele geçirir.

2. Kuyruklar (Queues) ile Güvenli Veri Aktarımı

İki farklı görev arasında veri paylaşmak için normal (global) değişkenler kullanmak “Race Condition” (yarış durumu) hatalarına yol açar. Bir görev değişkene yazarken diğeri okumaya çalışırsa veriler bozulabilir. Kuyruklar (Queues), verileri FIFO (İlk giren ilk çıkar) mantığıyla görevler arasında %100 güvenli bir şekilde taşımamızı sağlar.

3. Örnek Senaryo: Sensör Okuma ve Veri İşleme

Aşağıdaki örnekte iki farklı görev oluşturacağız. Birinci görev (Sensör Görevi) periyodik olarak sahte bir sıcaklık verisi üretip bunu kuyruğa ekleyecek; ikinci görev (İşlem Görevi) ise kuyruktan bu veriyi bekleyip ekrana yazdıracaktır.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"

static const char *TAG = "FREERTOS_TEST";

// Veri aktarimi icin kullanilacak kuyruk yapisi
static QueueHandle_t sensor_queue = NULL;

// 1. Gorev: Veri Ureten (Sensor) Gorev
void sensor_task(void *pvParameters) {
    int fake_temperature = 20;

    for (;;) {
        fake_temperature++; // Her calistiginda sicakligi artir
        
        // Veriyi kuyruga gonder (Eger kuyruk doluysa 100 tik bekle)
        if (xQueueSend(sensor_queue, &fake_temperature, pdMS_TO_TICKS(100)) == pdPASS) {
            ESP_LOGI(TAG, "Sensor: %d derece kuyruga eklendi.", fake_temperature);
        } else {
            ESP_LOGW(TAG, "Sensor: Kuyruk dolu, veri gonderilemedi!");
        }

        // Gorevi 2 saniye uyut (Diger gorevlerin calismasina izin ver)
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

// 2. Gorev: Veri Tuketici (Islem) Gorev
void processing_task(void *pvParameters) {
    int received_temp;

    for (;;) {
        // Kuyruktan veri bekle (portMAX_DELAY ile veri gelene kadar sonsuza dek bekler)
        if (xQueueReceive(sensor_queue, &received_temp, portMAX_DELAY) == pdPASS) {
            ESP_LOGI(TAG, "Islem: Kuyruktan %d derece okundu. Ekrana yazdiriliyor...", received_temp);
        }
    }
}

void app_main(void) {
    // Maksimum 5 adet int degeri tutabilen bir kuyruk olustur
    sensor_queue = xQueueCreate(5, sizeof(int));

    if (sensor_queue != NULL) {
        // Sensor gorevini olustur (Oncelik 1)
        xTaskCreate(sensor_task, "SensorTask", 2048, NULL, 1, NULL);
        
        // Islem gorevini olustur (Oncelik 1)
        xTaskCreate(processing_task, "ProcessTask", 2048, NULL, 1, NULL);
        
        ESP_LOGI(TAG, "Gorevler ve Kuyruk basariyla baslatildi.");
    } else {
        ESP_LOGE(TAG, "Kuyruk olusturulamadi!");
    }
}

4. Dikkat Edilmesi Gereken Kritik Noktalar

  • Watchdog Timer (WDT) Tetiklenmesi: İçinde vTaskDelay bulunmayan bir sonsuz döngü, işlemci çekirdeğini tamamen meşgul eder. ESP32’nin donanımsal Watchdog Timer’ı, kilitlendiğini düşünerek sistemi yeniden başlatır (Reset). Döngülerde mutlaka vTaskDelay kullanın.
  • Stack Overflow (Bellek Taşması): Görevin içinde çok büyük diziler (array) tanımlarsanız ayrılan Stack boyutu (örneğin 2048 byte) yetmez ve cihaz “Stack smashing protect failure!” hatası verip çöker. Büyük tampon bellekleri (buffer) malloc ile Heap’te oluşturun.
  • Gereksiz Yüksek Öncelik: Sürekli çalışan bir göreve çok yüksek öncelik (Priority) verirseniz, düşük öncelikli sistem görevleri (örneğin Wi-Fi veya Bluetooth yönetim görevleri) çalışma zamanı bulamaz ve bağlantı kopmaları yaşanabilir.
Tags , , , , ,

How can we help?