前回は規定温度を超えでPassiveブザーをPWMで鳴らす機能を追加。今回は第2UARTを使って外部から「指令」を受信する機能を付け加えたいと思います。UNO R4はUSBシリアルに向いているチャネルとは別にもう一本UARTを使えるのでこれとのやり取りをTask1にお任せします。今のところ指令を受信するだけだけれども。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?ArmコアのルネサスRA4M1マイコン搭載機です。
Arduino UNO R4 MinimaのUART
UNO R4はR3と違って、USBシリアルに向いているUARTとは別にデジタル端子D0, D1に第2のUARTが接続されています。規定のオブジェクトは以下のとおり
-
- Serial、ボード上のUSBポートのUSBシリアル
- Serial1、D0にRX、D1にTXが向いている
Serialの方は、printfデバッグ的に使っているのでこれはこのまま維持し、Serial1の方を外部デバイスとの接続に利用したいと思います。今回は接続のテストなので接続先は定番のFT232RLを搭載したモジュールです。その先の仮想端末ソフト(TeratermPro)から手動で文字入力してテストしてます。この部分は後でホスト側のプログラムに置き換える予定。
TASKのお仕事のおさらい
このところチョイ変につぐチョイ変で、徐々に機能を付け加えていますが、大分構成部品が増えました。ここで現在4個ある各TASKのお仕事は以下のとおりです。
-
- OLED_TASK
SSD1306とソフト「コンパチ」なSSD1315搭載のOLED、128×64ドット表示に表示を行うためのタスクです。TASK1とTASK2のそれぞれから表示すべき内容をQueueを使って受信し、表示を行います。
-
- TASK1
前回までは定周期でカウントアップするだけのタスクでしたが、今回は第2のUARTからの指令を受けて「何かする」仕事を割り当てられました。といっても今のところ指令内容をOLED_TASKに送って表示してもらうだけですが。今後に期待?
-
- TASK2
AD22100温度センサの温度をADコンバータを使って読み取ります。読み取り結果をOLED_TASKに送って表示するとともに、読み取った温度が既定の範囲(今のところ40℃)を超えるとTASK3にnotificationします。
-
- TASK3
他TASKの誰かがnotificationするのを常に待っており、notificationされるとパッシブブザーをPWM駆動して警告音を鳴らして注意を喚起します。notificationが到来しなくなると規定の秒数で警報は止まります。今のところnotificationしてくるのはTASK2だけですが他のTASKからも可能。
ボード回路
今回実験している部分の回路図は以下です。FT232RLと書かれた部分が追加のところ。
今回実験のソース
あちらこちらチョイ直ししているので、全文を以下に掲げます。
#include <Arduino_FreeRTOS.h> #include <U8x8lib.h> #define AWAITMAX1 (10) #define AWAITMAX2 (2000) #define BuzzorON (128) #define BuzzorOFF (0) #define BuzzorWait (3000) U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); TaskHandle_t oled_task, task1, task2, task3; const uint8_t queueSize = 1; QueueHandle_t msgQueue1, msgQueue2; int msg1, msg2; int ad22100pin = A3; int ad22100val = 0; int buzzor = 6; int rCount = 0; int getTemperature() { ad22100val = analogRead(ad22100pin); return -(562650-2000*ad22100val)/9207; } void setup() { Serial.begin(115200); while (!Serial) { } Serial1.begin(9600); while (!Serial1) { } u8x8.begin(); u8x8.setPowerSave(0); msgQueue1 = xQueueCreate(queueSize, sizeof(int)); msgQueue2 = xQueueCreate(queueSize, sizeof(int)); auto const rc_oled = xTaskCreate ( oled_thread_func, static_cast<const char*>("OLED Thread"), 512 / 4, nullptr, 1, &oled_task ); if (rc_oled != pdPASS) { Serial.println("Failed to create 'OLED' thread."); return; } auto const rc_task1 = xTaskCreate ( task1_func, static_cast<const char*>("Task1"), 512 / 4, nullptr, 1, &task1 ); if (rc_task1 != pdPASS) { Serial.println("Failed to create 'task1' thread"); return; } auto const rc_task2 = xTaskCreate ( task2_func, static_cast<const char*>("Task2"), 512 / 4, nullptr, 1, &task2 ); if (rc_task2 != pdPASS) { Serial.println("Failed to create 'task2' thread"); return; } auto const rc_task3 = xTaskCreate ( task3_func, static_cast<const char*>("Task3"), 512 / 4, nullptr, 1, &task3 ); if (rc_task3 != pdPASS) { Serial.println("Failed to create 'task3' thread"); return; } u8x8.setFont(u8x8_font_chroma48medium8_r); u8x8.setInverseFont(1); u8x8.drawString(0,0,"TASK->OLED"); u8x8.setInverseFont(0); u8x8.drawString(0,1,"TASK1: "); u8x8.drawString(0,2,"TEMP : "); Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { Serial.println(millis()); vTaskDelay(configTICK_RATE_HZ); } void oled_thread_func(void *pvParameters) { char buf[16]; int ts1, ts2; while (1) { if (xQueueReceive(msgQueue1, (void *)&ts1, 0) == pdTRUE) { sprintf(buf, "%c", ts1); u8x8.drawString(7,1,buf); } if (xQueueReceive(msgQueue2, (void *)&ts2, 0) == pdTRUE) { sprintf(buf, "%d", ts2); u8x8.drawString(7,2,buf); } sprintf(buf, "%d", rCount); u8x8.drawString(0,3,buf); u8x8.refreshDisplay(); taskYIELD(); } } void task1_func(void *pvParameters) { int count = 0; while (1) { msg1 = 0x20; while(Serial1.available()){ delay(2); msg1 = (int)Serial1.read(); if (xQueueSend(msgQueue1, (void*)&msg1, 2) == pdTRUE) { rCount++; } } vTaskDelay(AWAITMAX1); } } void task2_func(void *pvParameters) { int ecount = 0; while (1) { msg2 = getTemperature(); if (msg2 > 40) { xTaskNotifyGive(task3); } if (xQueueSend(msgQueue2, (void*)&msg2, 2) != pdTRUE) { ecount++; } vTaskDelay(AWAITMAX2); } } void task3_func(void *pvParameters) { while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); analogWrite(buzzor, BuzzorON); vTaskDelay(BuzzorWait); analogWrite(buzzor, BuzzorOFF); } }
実機動作確認
Serial1の先に接続したTeraterm Pro(8ビット、ノーパリティ、9600ボー、ローカルエコー設定)から一文字「a」を送ったところが以下に。
UNO R4上のTASK1で受信され、OLED_TASKに送られた様子が以下に。
小さいけどね、TASK1: a の aが送られてきた文字っす。次は指令を受けてなにか仕事しないと。