esp-open-rtosを使ってみる

Software Timer機能

FreeRTOSにはSoftwareタイマーの機能があります。
タイマーの機能を使えば、ループの中でDelayによる時間待ちを行わなくても、タイマーからのコールバックで処理を行うことができます。
以下のコードで、タイマーによるコールバックの基本的な確認をしてみました。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "task.h"
#include "esp8266.h"

// Timer call back
static void my_timer_cb(TimerHandle_t xTimer)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("my_timer is called, now=%d\n",nowTick);
}

void user_init(void)
{
    static TimerHandle_t timerHandle;
    uart_set_baud(0, 115200);
    printf("SDK version:%s\n", sdk_system_get_sdk_version());
    printf("pcTaskGetName=%s\n",pcTaskGetName(0));
    // Create Timer (Trigger a measurement every second)
    timerHandle = xTimerCreate("MY Trigger", 1000/portTICK_PERIOD_MS, pdTRUE, NULL, my_timer_cb);
    if (timerHandle != NULL)
    {
        if (xTimerStart(timerHandle, 0) != pdPASS) {
            printf("%s: Unable to start Timer ...\n", __FUNCTION__);
        } else {
            printf("%s: Success to start Timer ...\n", __FUNCTION__);
        }
    }
    else
    {
        printf("%s: Unable to create Timer ...\n", __FUNCTION__);
    }
}

結果は以下の様になります。
一番最初以外は、きっちり、100Tick(1000mSec)ごとに割り込みが発生しています。
SDK version:0.9.9
pcTaskGetName=uiT
user_init: Success to start Timer ...
mode : sta(84:0d:8e:8c:f4:00)
add if0
scandone
my_timer is called, now=138
add 0
aid 10
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
my_timer is called, now=200
my_timer is called, now=300
ip:192.168.10.186,mask:255.255.255.0,gw:192.168.10.1
my_timer is called, now=400
my_timer is called, now=500
my_timer is called, now=600
my_timer is called, now=700
my_timer is called, now=800
my_timer is called, now=900
my_timer is called, now=1000
my_timer is called, now=1100



タイマーには定期的なタイマーとOne-shotタイマーの2つの種類が有ります。
以下の様にxTimerCreate()の3番目の引数をpdFALSEに変えるとOne-shotタイマーになります。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "task.h"
#include "esp8266.h"

// Timer call back
static void my_timer_cb(TimerHandle_t xTimer)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("my_timer is called, now=%d\n",nowTick);
}

void user_init(void)
{
    static TimerHandle_t timerHandle;
    uart_set_baud(0, 115200);
    printf("SDK version:%s\n", sdk_system_get_sdk_version());
    printf("pcTaskGetName=%s\n",pcTaskGetName(0));
    // Create Timer (Trigger a measurement after 10 second)
    timerHandle = xTimerCreate("MY Trigger", 1000, pdFALSE, NULL, my_timer_cb);
    if (timerHandle != NULL)
    {
        if (xTimerStart(timerHandle, 0) != pdPASS) {
            printf("%s: Unable to start Timer ...\n", __FUNCTION__);
        } else {
            printf("%s: Success to start Timer ...\n", __FUNCTION__);
        }
    }
    else
    {
        printf("%s: Unable to create Timer ...\n", __FUNCTION__);
    }
}


結果は以下のようになります。
1000Tick後に1回だけ割り込みが発生します。
SDK version:0.9.9
pcTaskGetName=uiT
user_init: Success to start Timer ...
mode : sta(18:fe:34:d4:2f:77)
add if0
scandone
add 0
aid 10
cnt

connected with aterm-e625c0-g, channel 11
dhcp client start...
ip:192.168.10.154,mask:255.255.255.0,gw:192.168.10.1
my_timer is called, now=1000



複数のタイマーを生成した場合、各タイマーの割り込み処理を1つのタイマーコールバックで処理することができます。
タイマーコールバックの引数に、時間切れとなったタイマーのタイマーIDが渡ります。
100Tick毎のタイマー(xTimer=1073669272)と、200Tick毎のタイマー(xTimer=1073690352) を1つのコールバックで処理してみました。
my_timer is called, xTimer=1073690352 now=400
my_timer is called, xTimer=1073669272 now=400
my_timer is called, xTimer=1073669272 now=500
my_timer is called, xTimer=1073690352 now=600
my_timer is called, xTimer=1073669272 now=600
my_timer is called, xTimer=1073669272 now=700
my_timer is called, xTimer=1073690352 now=800
my_timer is called, xTimer=1073669272 now=800
my_timer is called, xTimer=1073669272 now=900
my_timer is called, xTimer=1073690352 now=1000
my_timer is called, xTimer=1073669272 now=1000



タイマーコールバック関数の中でできる処理には制限が有ります。
こちらの ページにこのようなことが書かれていました。

Important information on writing timer callback functions
Timer callback functions execute in the context of the timer service task.
It is therefore essential that timer callback functions never attempt to block.
For example, a timer callback function must not call vTaskDelay(), vTaskDelayUntil(), or specify a non zero block time when accessing a queue or a semaphore.

タイマーコールバック関数の作成に関する重要な情報
タイマーコールバック関数は、タイマーサービスタスクのコンテキストで実行されます。
したがって、タイマーコールバック関数がブロックしようとしないことが不可欠です。
たとえば、タイマーコールバック関数は、vTaskDelay()、vTaskDelayUntil()を呼び出したり、
キューまたはセマフォに アクセスするときにゼロ以外のブロック時間を指定したりしてはなりません。

要するにタイマーコールバックの中では待つな!!ということです。



また、こ ちらのページにtimer service/daemon taskのことが書かれています。

Timer functionality is optional, and not part of the core FreeRTOS kernel.
It is instead provided by a timer service (or daemon) task.
FreeRTOS provides a set of timer related API functions.
Many of these functions use a standard FreeRTOS queue to send commands to the timer service task.
The queue used for this purpose is called the 'timer command queue'.
The 'timer command queue' is private to the FreeRTOS timer implementation, and cannot be accessed directly

タイマー機能はオプションであり、FreeRTOSコアカーネルの一部ではありません。
代わりに、タイマーサービス(またはデーモン)タスクに よって提供されます。
FreeRTOSはタイマー関連のAPI関数のセットを提供します。
これらの機能の多くは、標準のFreeRTOSキューを使用してコマンドをタ イマーサービスタスクに送信します。
この目的で使用されるキューは、「タイマーコマンドキュー」と呼ばれます。
「タイマーコマンドキュー」はFreeRTOSタイマー実装に固有のものであり、直接アクセスすることはできません。

きっと「Tmr Svc」というタスクがtimer service/daemon taskで、これが裏で時間管理をしているのでしょう。

続く....