5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Arduino M0 Pro でマルチタスクプログラミング : NCES IoT Base Shield : GROVE・RTOS機能

Last updated at Posted at 2016-06-01

#はじめに

この記事では,TOPPERS/R2CAによる NCES IoT Base Shield のGrove機能の+RTOS機能使い方について説明します.

NCES IoT Base Shiledについてはこの記事を参照して下さい.

NCES IoT Base ShiledでGroveモジュールを使用する方法についてはこの記事を参照して下さい.

必要なハードウェア

アプリケーション仕様

作成するアプリケーションの仕様は次の通り.

  • 機能1:LEDを1秒周期で点滅する
  • 機能2:周期的にDigital Light センサーの値を読み込んで,値をOLED Display に表示する.
  • 機能3:Touch Sensorを押している間は,Light センサーの値の表示の更新を停止して,OLED Displayの表示を反転させる.
  • 機能4:Chainable LEDを周期的に色を変更する.

ハードウェアセットアップ

各種センサーをIoTシールドに接続します.Grove Digital LightとGrove OLED Displayは,I2Cポートに接続します.Touch SensorはD4以外のデジタルのコネクタに接続しますここではD3に接続します.LEDはD4に,Chainable RGB LEDはD8に接続します

aFullSizeRender.jpg.jpg

プロジェクトフォルダ

プロジェクトフォルダは,NCESIoT_RTOS にあります.必要なライブラリ等は入っていますので,ハードウェアの
セットアップ後に動作させることは可能です.

割込み無し版

まず割込みを使用しないバージョンを作成します.

各モジュールのライブラリはGroveのWikiからダウンロードしてプロジェクトフォルダ以下に置きます.今回はDigital Light,OLED Display,ChainableLEDのライブラリを使用します.

まず機能1-4を別々のタスクで実現するためにタスクを追加で3個(合計4個)作成します.

example/NCESIoT_RTOS/r2ca_app.h
#define R2CA_NUM_TASK 3

割込みを使わないのでUSE_INTERRUPTをコメントアウトします.ifdefが入っていると読みにくいのでifdefをとった状態のコードを掲載します.

example/NCESIoT_RTOS/r2ca_app.cpp
#include "r2ca.h"
#include <Wire.h>
#include <Digital_Light_TSL2561.h>
#include <SeeedOLED.h>
#include <ChainableLED.h>

#define LED_PIN  4

extern void task1_setup();
extern void task2_setup();
extern void task3_setup();

void setup()
{
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);

    task1_setup();
    task2_setup();
    task3_setup();
}

void loop() 
{
    digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);                   // wait for a second
    digitalWrite(LED_PIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);                   // wait for a second    
}

#define TOUCH_PIN 3

int is_update_oled;

void task1_setup() 
{
    Wire.begin();
    TSL2561.init();

    SeeedOled.init();
    SeeedOled.deactivateScroll();

    is_update_oled = 1;
} 
 
void task1_loop() 
{    
    Serial.print("The Light value is: ");
    Serial.println(TSL2561.readVisibleLux());

    if (is_update_oled == 1) {
        wai_sem(OLED_SEM);
        SeeedOled.clearDisplay();
        SeeedOled.putNumber(TSL2561.readVisibleLux());
        sig_sem(OLED_SEM);
    }
    
    delay(1000);
}

void task2_setup()
{
    pinMode(TOUCH_PIN, INPUT_PULLUP);
}

void task2_loop() 
{
    delay(1);    

    int TouchSensorValue = digitalRead(TOUCH_PIN);

    if(TouchSensorValue==1) {
        is_update_oled = 0;
        wai_sem(OLED_SEM);
        SeeedOled.setInverseDisplay();
        sig_sem(OLED_SEM);
    }else{        
        is_update_oled = 1;
        wai_sem(OLED_SEM);
        SeeedOled.setNormalDisplay();
        sig_sem(OLED_SEM);
    }
}

#define NUM_LEDS  1

ChainableLED leds(8, 9, NUM_LEDS);

void task3_setup()
{
    leds.init();
}

float hue = 0.0;
boolean up = true;
int count = 0;

void task3_loop()
{
    for (byte i=0; i<NUM_LEDS; i++)
      leds.setColorHSB(i, hue, 1.0, 0.5);
    
    delay(50);
    
    if (up)
      hue+= 0.025;
    else
      hue-= 0.025;
    
    if (hue>=1.0 && up)
      up = false;
    else if (hue<=0.0 && !up)
      up = true;
}

メインタスクセットアップ(setup())はシリアルポートとLEDのための初期化を行っています.
メインタスクループ(loop())では,LEDを一秒間隔で点滅させます.

タスク1セットアップ(task1_setup())では, Digital Light と OLED Display を初期化しています.
タスク1ループ(task1_loop())では,1秒ごとにDigital Light の値を読み込んでその結果をOLEDに表示します.

タスク2セットアップ(task2_setup())では,Touch Sensorのためにポートを初期化します.Touch Sensorを接続しているポートはマクロTOUCH_PINで定義しています.

タスク2ループ(task2_loop())では,Touch Sensorを読み込んで,押されていればOLEDを反転させます.ここで重要なのは,delay(1)を入れることです.これを入れないとメインタスクに処理が移りません.

タスク間の通信はグローバル変数で実現しています.具体的には,is_update_oledを使用しています.Touch Sensorが押されていれば is_update_oled を0に設定します.is_update_oled が0だとメインタスクはOLEDの更新を停止します.

OLEDは両方のタスクから操作するため,セマフォにより排他制御します.操作前にwai_sem()でセマフォを取得して,使用後にsig_sem()でセマフォを返却します.

セマフォはr2ca_app.cfgに宣言します.

example/NCESIoT_RTOS/r2ca_app.cfg
INCLUDE("r2ca_lib.cfg");

CRE_SEM(OLED_SEM, {TA_NULL, 1, 1});

タスク3セットアップ(task3_setup())では,ChainableLEDのための初期化を行います.
タスク2ループ(task3_loop())では,ChainableLEDの色を周期的に変更します.

割込みあり版

次に割込みを使用するバージョンを作成します.

USE_INTERRUPTの定義を有効にします.ifdefが入っていると読みにくいのでifdefをとった状態のコードを掲載します.

example/NCESIoT_RTOS/r2ca_app.cpp
#include "r2ca.h"
#include <Wire.h>
#include <Digital_Light_TSL2561.h>
#include <SeeedOLED.h>
#include <ChainableLED.h>

#define LED_PIN  4

void task1_setup();
void task2_setup();
void task3_setup();

void setup()
{
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);

    task1_setup();
    task2_setup();
    task3_setup();
}

void loop() 
{
    digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);                   // wait for a second
    digitalWrite(LED_PIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);                   // wait for a second    
}

#define TOUCH_PIN 3

int is_update_oled;

void task1_setup() 
{
    Wire.begin();
    TSL2561.init();

    SeeedOled.init();
    SeeedOled.deactivateScroll();

    is_update_oled = 1;
} 
 
void task1_loop() 
{    
    Serial.print("The Light value is: ");
    Serial.println(TSL2561.readVisibleLux());

    if (is_update_oled == 1) {
        wai_sem(OLED_SEM);
        SeeedOled.clearDisplay();
        SeeedOled.putNumber(TSL2561.readVisibleLux());
        sig_sem(OLED_SEM);
    }
    
    delay(1000);
}

void onTouch(void) {
    iwup_tsk(R2CA_TASK1);
}

void task2_setup()
{
    pinMode(TOUCH_PIN, INPUT_PULLUP);
    attachInterrupt(TOUCH_PIN, onTouch, CHANGE);
}

void task2_loop() 
{
    slp_tsk();
    
    int TouchSensorValue = digitalRead(TOUCH_PIN);

    if(TouchSensorValue==1) {
        is_update_oled = 0;
        wai_sem(OLED_SEM);
        SeeedOled.setInverseDisplay();
        sig_sem(OLED_SEM);
    }else{        
        is_update_oled = 1;
        wai_sem(OLED_SEM);
        SeeedOled.setNormalDisplay();
        sig_sem(OLED_SEM);
    }
}

#define NUM_LEDS  1

ChainableLED leds(8, 9, NUM_LEDS);

void task3_setup()
{
    leds.init();
}

float hue = 0.0;
boolean up = true;
int count = 0;

void task3_loop()
{
    for (byte i=0; i<NUM_LEDS; i++)
      leds.setColorHSB(i, hue, 1.0, 0.5);
    
    delay(50);
    
    if (up)
      hue+= 0.025;
    else
      hue-= 0.025;
    
    if (hue>=1.0 && up)
      up = false;
    else if (hue<=0.0 && !up)
      up = true;
}

割込み無し版との差分について説明します.

割込みあり版では,Touch Sensor の状態が変化すると割込みハンドラを起動するようにします.

まずタスク2セットアップ(task2_setup())で,attachInterrupt()を呼び出して割込みを設定します.Touch Sensor を押した際にも話した際にも割込みを発生させたいため,引数にCHANGEを指定します.

割込みハンドラは onTouch()として宣言します.onTouch()は,iwup_tsk()でタスク1を起動します.

タスク2メインループ(task2_loop())ではonTouch()からiwup_tsk()により起動されるのを待ち,起動されたらTouch Sensor の値を読み,割込みなし版と同じ処理を実施します.

おわりに

この記事では,RTOSのAPIの実例を紹介しました.

5
4
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
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?