
前回は規定温度を超えで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が送られてきた文字っす。次は指令を受けてなにか仕事しないと。
