0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FRDM-MCXA153(その3:FreeRTOS続き)

0
Posted at

割り込み

その2」に記載した「あるべき姿」を求め(ポーリングではなく割り込み)、AIさんにたずねて作成した内容。今回も備忘録レベルの内容。

CPU消費を減らすために割り込みを利用(いまさらだけど)

ソフトウェア開発、特にRTOSでは、CPUリソースの関連から、ポーリングは嫌われるため、最大限、割り込みを用いるのが王道である。調べたところ(AIさんなど)、当然、FreeRTOSにも用意されている。後は、MCUXpresso環境での使い方(Call方法)の話。

ストーリー

  • タスク2つ
    • タスクA:一定周期でメッセージ出力
      • キューが空:固定文字列およびインクリメントされるカウンタを表示
      • キューが空でない:キューの内容を表示
    • タスクB:シリアル入力をキューにためる
  • シリアル入力にて割り込み利用

ソースコード

今回は、割り込み周辺のコードの説明が主目的。全コードを掲載、適宜、下記に記載。

/* System includes.*/
#include <stdio.h>

/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"

/* Freescale includes. */
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_lpuart.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* タスクプライオリティ */
#define hello_task_PRIORITY (configMAX_PRIORITIES - 1)
#define serial_task_PRIORITY (configMAX_PRIORITIES - 1)
/* 入力バッファ及びキューのサイズ */
#define BUFFER_SIZE 32
/* キュー内のメッセージの数 */
#define NUM_QUEUE 3
/* hello_taskでSleepする時間(ms) */
#define HELLO_SLEEP 5000

ここは説明略。

/*******************************************************************************
 * Globals
 ******************************************************************************/
/* シリアルタスクからハロータスクへのメッセージ用Queue */
static QueueHandle_t msg_queue = NULL;
/* UART受信バッファ用Queue */
static QueueHandle_t char_queue = NULL;

シリアル入力を割り込みハンドラで検出し、シリアルタスクに渡すQueueが必要となる。

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static void init_lpuart(void);
void LPUART0_IRQHandler(void);
static void hello_task(void *pvParameters);
static void serial_task(void *pvParameters);

ここは自明。

/*******************************************************************************
 * Code
 ******************************************************************************/
int main(void)
{
    /* ボード初期化 */
    BOARD_InitHardware();
    /* UART初期化 */
    init_lpuart();
    /* タスク間メッセージQueue作成 */
    msg_queue = xQueueCreate(NUM_QUEUE, BUFFER_SIZE);
    /* UART受信バッファ用Queue作成(ISRから1文字受信ISR) */
    char_queue = xQueueCreate(BUFFER_SIZE, sizeof(char));
    /* タスク生成 */
    if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=
    		pdPASS ||
		xTaskCreate(serial_task, "Serial_task", configMINIMAL_STACK_SIZE + 100, NULL, serial_task_PRIORITY, NULL) !=
				pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        return 1;
    }
    vTaskStartScheduler();
    for (;;)
        ;
}

ここはコメント参照。

/* UART初期化 */
void init_lpuart(void) {
    lpuart_config_t config;

    /* シリアルパラメータ設定 */
    LPUART_GetDefaultConfig(&config);
    config.baudRate_Bps = 115200U;
    config.enableRx = true;
    config.enableTx = true;

    /* UART初期化 */
    LPUART_Init(LPUART0, &config, BOARD_DEBUG_UART_CLK_FREQ);

    /* configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITYよりも大きな数値(低い優先度) */
    NVIC_SetPriority(LPUART0_IRQn, 2); // 1:NG
    /* 受信割り込みを有効 */
    LPUART_EnableInterrupts(LPUART0, kLPUART_RxDataRegFullInterruptEnable);
    /* NVIC側でLPUART割り込みを有効 */
    EnableIRQ(LPUART0_IRQn);
}

ここでのミソは下記。

  • 本プログラムのNVIC(Nested Vectored Interrupt Controller:ARM Cortex-M内)の割り込み優先度を適宜設定(OS自体より低く)
  • 割り込み有効化
/* 割り込みハンドラ */
void LPUART0_IRQHandler(void)
{
    uint8_t data;

    /* 受信フラグが立っているか確認 */
    if ((kLPUART_RxDataRegFullFlag) & LPUART_GetStatusFlags(LPUART0))
    /* その2の (LPUART0->STAT & LPUART_STAT_RDRF_MASK) != 0 と同じだがこちらの方がベター */
    {
        data = LPUART_ReadByte(LPUART0);
        /* 明示的にフラグをクリアする(チップ依存、今回は不要であった) */
        /* LPUART_ClearStatusFlags(LPUART0, kLPUART_RxDataRegFullFlag); */
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        /* シリアルタスクに1文字送る */
        xQueueSendFromISR(char_queue, &data, &xHigherPriorityTaskWoken);
        /* 割り込み後に即座にタスクを切り替える必要があるか確認 */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

ここでのミソは下記。

  • LPUART0_IRQHandler():この関数名を割り込みハンドラとして用いるのがルール(らしい)
  • 確実に受信しているかの確認(受信フラグ)
  • データ(入力文字)を取得
  • データをシリアルタスクへ送信(xQueueSendFromISR())
  • シリアルタスクをWakeUp(portYIELD_FROM_ISR)
/* タスクA(ハロータスク) */
static void hello_task(void *pvParameters)
{
	int count = 0;
	char buf[BUFFER_SIZE];
    for (;;)
    {
    	if (xQueueReceive(msg_queue, buf, 0) == pdTRUE)
    	{
    		PRINTF("\tReceived Message: %s\r\n", buf);
    	} else
    	{
    		PRINTF("\tHello %d\r\n", ++count);
    	}
        vTaskDelay(pdMS_TO_TICKS(HELLO_SLEEP));
    }
}

ここは自明、説明略。

/* タスクB(シリアルタスク) */
static void serial_task(void *pvParameters)
{
    char msg[BUFFER_SIZE];
    int idx = 0;
    char c;

    while (1)
    {
    	/* キューに1文字来るまで待機(割り込み発生待ち)、タスクスイッチ */
    	if (xQueueReceive(char_queue, &c, portMAX_DELAY) == pdPASS)
        {
    		/* 取得した文字 c の処理 */
    		if (c == '\r' || c == '\n')
    		{
    			msg[idx] = '\0';
                /* タスクAへ入力文字列送信 */
    			xQueueSend(msg_queue, msg, 0);
    			idx = 0;
    			PRINTF("\r\n");
    		}
    		else if (idx < (BUFFER_SIZE - 1))
    		{
    			msg[idx++] = c;
    			PUTCHAR(c); // エコーバック
    		}
        }
    }
}

下記が大事。

  • xQueueReceive()のportMAX_DELAYにて無限待ち
    • タスクスイッチあり(割り込みや他のタスクが動作可能)
  • 改行コードが入力されたら、それまでに入力された文字列をタスクAへ送信

結果

ターミナルでの出力状況は、「その2」と同じのため、省略。

終わりに

FreeRTOSを使うというより、NXP提供プラットフォーム(MCUXpresso)での、APIなどの使い方を学ぶのがメインとなってしまった気がする。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?