Seeeduino XIAOでマルチタスク（FreeRTOS理解:番外編2）

# Seeeduino XiaoでFreeRTOS
SeeeduinoXiaoでFreeRTOSを用い、マルチタスクを試しみたかったので、ここに記録を残す。

## Seeeduino XIAO
本家サイトのボードの説明は、[ここ](https://wiki.seeedstudio.com/jp/Seeeduino-XIAO/)にあり。FreeRTOSについては、同じ本家サイトの[こちら](https://wiki.seeedstudio.com/Software-FreeRTOS/)に説明があり、ライブラリとして、[Seeed_Arduino_FreeRTOS](https://github.com/Seeed-Studio/Seeed_Arduino_FreeRTOS)にあるzipファイルを、Arduino IDEにインクルードした。

## 題材
マルチタスクの題材として、ある一つのタスクがシリアル出力中に、別タスクによるキーボード入力をシリアル出力するもの。なお、ここでは、簡略化のため、シリアル出力の排他制御は未実施。キーボードについては、[FreeRTOS理解:番外編1](https://qiita.com/infinite1oop/items/cbb31bc6363103dac6dd)で用いた、I2Cキーボードを使用。下記写真（左にSeeeduino XIAOあり）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/3778fa2c-62bc-59ab-380a-ad1e7dd5960c.jpeg", width=70%>
Seeeduino XIAOでのI2Cについては、本家サイト情報から下記SDA及びSCL（3V3、GNDも）を参照。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/05846cc9-d27b-ba22-8405-e9e5d5f2b5f4.jpeg", width=70%>

### ソースコード
定義関連。
```c++
#include <Seeed_Arduino_FreeRTOS.h>
#include <Wire.h>

#define CARDKB_ADDR   0x5F    // I2C slave address
#define KBD_BUF_SIZE  32
#define NL            0x0d
#define BS            0x08

#define LED           12

TaskHandle_t xKbd, xOnlyPrt;
```
- Seeeduino Xiao用FreeRTOSおよびI2C関連インクルード
- I2Cキーボード関連定義
- LEDのGPIO（おまけ）
- タスクハンドラ

キーボードハンドリングタスク（TaskOneLine）。
```c++
void TaskOneLine(void *arg) {
  char      key[KBD_BUF_SIZE];
  uint16_t  i = 0;
  for (;;) {  
    Wire.requestFrom(CARDKB_ADDR, 1);
    while (Wire.available()) {
      char c = Wire.read();   // receive a byte
      if (c != 0) {
        if (c == BS) {        // Back space
          if (i != 0) {
            i--;
          }          
        } else {              // Store input data in buffer
          key[i++] = c;       // Not consider KBD_BUF_SIZE for input
          if (c == NL) {      // Read until new line
            key[i] = 0;
            digitalWrite(LED, LOW);     // HIGH:OFF  LOW:ON
            Serial.println("Input key:");
            vTaskDelay(3000); // For task switch
            Serial.print("            "); Serial.println(key);
            digitalWrite(LED, HIGH);
            i = 0;
          }
        }
      }
    }
    vTaskDelay(1);            // For task switch
  }  
}
```
- 改行コードがあるまでキーボード入力をバッファkeyに格納
- 改行コード入力直後、LED点灯およびシリアル出力
- 改行コード入力3秒後、入力された文字をシリアル出力
- バッファkeyの初期化
- vTaskDelay()がCallされるとタスクスイッチが可能となる。

常時（1秒毎に）シリアル出力を行うタスク（TaskOnlyPrt）。
```c++
void TaskOnlyPrt(void *arg) {
  int i = 0;
  for (;;) {
    Serial.print("Count = ");
    Serial.println(i++);
    vTaskDelay(1000);         // For task switch
  }
}
```

Arduinoお決まり部分。
```c++
void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);    // HIGH:OFF  LOW:ON
  
  xTaskCreate(TaskOneLine, "TaskOneLine", 128, NULL, 1, &xKbd);
  xTaskCreate(TaskOnlyPrt, "TaskOnlyPrt", 128, NULL, 1, &xOnlyPrt);
  vTaskStartScheduler();
}

void loop() {
  // Nothing works here in Seeeduno Xiao 
}
```
- シリアル、I2C、GPIO(LED)の初期化
- キーボードハンドリングタスク（TaskOneLine）および常時シリアル出力タスク（TaskOnlyPrt）生成

### テスト
青枠が常時シリアル出力タスク（TaskOnlyPrt）によるシリアル出力、赤枠がキーボードハンドリングタスク（TaskOneLine）によるシリアル出力である。
![Consoleのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/970732c1-a75b-8277-e1f7-ec45b69e6d46.png)
常時シリアル出力タスクによる1秒毎の出力中に、キーボード入力を行っている。1秒毎の出力中にも、キーボードハンドリングタスクによる入力文字がバッファに格納され、3秒後に文字が出力される間にも、1秒毎の出力が継続しており、vTaskDelay()による２つのタスクのタスクスイッチが機能していることがわかる。

## 最後に
Seeeduino Xiaoのリセットの仕方やBootloaderモードへの入り方については、[こちらの記事](https://qiita.com/nanase/items/0fed598975c49b1d707e)に解説がある。