Pengenalan FreeRTOS – Memahami Queue

Pada contoh ini akan mengumpulkan data dari beberapa digital sensor kemudian memproses dalam FreeRTOS task. Digunakan single FreeRTOS queue.

FreeRTOS adalah cara sharing data antara interrupts dan tasks. Cocok digunakan untuk data rate tidak tinggi. Mekanisme queue diimplementasikan dengan method by-copy, yang artinya ketika data di pushed kedalam queue, data dicopykan ke memory dari queue.


Demikian juga consumer akan menyediakan lokasi memory ketika data ditarik dari queue. Dengan cara ini, tidak akan terjadi data corruption atau tidak diperlukan data synchronization, hingga software design lebih sederhana dan jelas.

Component

  • tilt sensor
  • tap sensor
  • shock sensor

Diagram

Code

Pertama kita tambahkan konfigurasi pada platform.ini untuk monitor_speed

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = espidf

monitor_speed = 115200

Kemudian bukan main.c, gunakan code dibawah:

//bagian import library
#include <inttypes.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#define TILT_SWITCH_PIN 16
#define SHOCK_SWITCH_PIN 5
#define TAP_SWITCH_PIN 17
#define MS_100 100000
typedef struct
{
    gpio_num_t pin;
    int64_t time;
} queue_data_t;
static QueueHandle_t sensor_event_queue = NULL;
static bool filter_out(queue_data_t *);

//bagian ISR Handler
static void IRAM_ATTR producer(void *arg)
{
    queue_data_t data = {
    .pin = (uint32_t)arg,
    .time = esp_timer_get_time()};
    xQueueSendToBackFromISR(sensor_event_queue, &data, NULL);
}

//bagian fungsi consumer
static void consumer(void *arg)
{
    queue_data_t data;
    while (1)
    {
        if (xQueueReceive(sensor_event_queue, &data, portMAX_DELAY))
        {
            if (filter_out(&data))
            {
            continue;
            }
            switch (data.pin)
            {
                case SHOCK_SWITCH_PIN:
                    printf("> shock sensor");
                    break;
                case TILT_SWITCH_PIN:
                    printf("> tilt sensor");
                    break;
                case TAP_SWITCH_PIN:
                    printf("> tap sensor");
                    break;
                default:
                    break;
            }
            printf(" at %" PRId64 "(us)\n", data.time);
        }
    }
    vTaskDelete(NULL);
}

//bagian fungsi filter
static bool filter_out(queue_data_t *d)
{
    static int64_t tilt_time = 0;
    static int64_t tap_time = 0;
    static int64_t shock_time = 0;
    switch (d->pin)
    {
        case TILT_SWITCH_PIN:
            if (d->time - tilt_time < MS_100)
            {
            return true;
            }
            tilt_time = d->time;
            break;
        case TAP_SWITCH_PIN:
            if (d->time - tap_time < MS_100)
            {
            return true;
            }
            tap_time = d->time;
            break;
        case SHOCK_SWITCH_PIN:
            if (d->time - shock_time < MS_100)
            {
            return true;
            }
            shock_time = d->time;
            break;
        default:
            break;
    }
    return false;
}

//bagian init hardware
static void init_hw(void)
{
    uint64_t pin_select = 0;
    pin_select |= (1ULL << SHOCK_SWITCH_PIN);
    pin_select |= (1ULL << TILT_SWITCH_PIN);
    pin_select |= (1ULL << TAP_SWITCH_PIN);
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;
    io_conf.pin_bit_mask = pin_select;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);
    gpio_install_isr_service(0);
    gpio_isr_handler_add(SHOCK_SWITCH_PIN, producer, (void *)SHOCK_SWITCH_PIN);
    gpio_isr_handler_add(TILT_SWITCH_PIN, producer, (void *)TILT_SWITCH_PIN);
    gpio_isr_handler_add(TAP_SWITCH_PIN, producer, (void *)TAP_SWITCH_PIN);
}

void app_main(void)
{
    init_hw();
    sensor_event_queue = xQueueCreate(20, sizeof(queue_data_t));
    xTaskCreate(consumer, "consumer", 2048, NULL, 10, NULL);
}

Penjelasan Code

Bagian import library

FreeRTOS queue API disediakan oleh file freertos/queue.h. Kemudian definsikan pin numbers dari sensors.

queue_data_t adalah data type of elements yang akan di push ke FreeRTOS queue

sensor_event_queue akan menyimpan semua data dari sensors.

filter_out adalah fungsi untuk membuang beberapa data sebelum processing.

Bagian ISR Handler

Fungsi producer akan melakukan push data dari sensor kedalam queue. Kita definisikan data variable dan fields.

Kemudian kita antrikan sebagai element dalam sensor_event_queue dengan perintah xQueueSendToBackFromISR, yang nanti akan diproses oleh FreeRTOS task.

Semua interupts sensor akan di attach ke handler ketika dilakukan konfigurasi GPIO.

Bagian Fungsi Consumer

Fungsi consumer adalah FreeRTOS task.

Pertama kita definisikan variable untuk menyimpan data element. Kemudian panggil xQueueReceive untuk membuang element dari awal queue. Fungsi ini akan memblock task sampai data element tersedia dalam queue. Setelah sensor data di push, xQueueReceive dijalankan dan menyimpan data tersebut dalam data variable.

Setalah data element tersedia, kita bisa proses, pada contoh code, pertama kita filter dahulu, kemudian tampilkan sensor event pada serial monitor.

Bagian Fungsi Filter

Fungsi filter_out menerima queue element sebagai parameter. Dalam switch statement, kita mulai dengan tilt event. Bandingkan timestamp dengan event sebelumnya. Jika perbedaan waktuk lebih kecil dari 100 ms, akan mengembalikan true.

Kita gunakan cara perbandingan yang sama untuk kedua sensor lainnya.

Return false berarti data siap untuk diproses.

Bagian init hardware

Pada init_hw, pertama konfigurasi pin sensor sebagai input. Kemudian attach producer sebagai ISR handler. Dengan begitu, kita set sensor sebagai data source dari queue.

Bagian Main Function

Setelah proses inisialisasi hardware, panggil xQueueCreate untuk membuat queue dengan kapasitas 20 elements. Sebelum keluar dari app_main, kita pass control ke fungsi consumer
dengan membuat FreeRTOS task.

Berikutnya kita test aplikasi. Ketika sensor digoyang, data akan digenerate yang kemudian diantrikan untuk ditampilkan ke serial monitor.


Sharing is caring:

Leave a Comment