/Test_you

電子工作やプログラミングなど、やってみたことのメモ

Logicool製Webカメラ C922n のカメラ設定

LogicoolWebカメラ C922n を使うにあたり、カメラ設定をいくつか試したので、その内容をまとめておく。

f:id:sonoka_gi:20201231002523j:plain

1. 環境

  • Windows 10 Pro: 1909
  • カメラアプリ: 2020.504.60.0
  • Logicool カメラ設定: 2.18.8.0
  • Camera Viewer: 2013/11/25版

2. Windows10のカメラアプリでカメラ画像を表示し、フォーカス・明るさを調整する

フォーカス・明るさはカメラアプリ単独で調整できる。

f:id:sonoka_gi:20201230224417j:plain

設定でプロモードをONにすると、アプリの左側にフォーカス・明るさの調整ボタンが追加される。

f:id:sonoka_gi:20201230233850j:plainf:id:sonoka_gi:20201230233857j:plainf:id:sonoka_gi:20201230233901j:plain

フォーカスを押下し、バーを一番下に設定するとオートフォーカス。上に移動するとマニュアルで調整できる。

f:id:sonoka_gi:20201230233906j:plainf:id:sonoka_gi:20201230233914j:plain

明るさも同様に調整できる。

f:id:sonoka_gi:20201230234108j:plainf:id:sonoka_gi:20201230233919j:plain

3. カメラアプリ使用時に、Logicool カメラ設定で各種設定を調整する

Logicool カメラ設定は、Logicoolのカメラ設定用アプリ。ダウンロードはこちらの記事を参考にした。

f:id:sonoka_gi:20201230224423j:plain

起動時の注意点として、カメラアプリ起動してからLogicool カメラ設定を起動する(左)。逆だと、カメラアプリでエラーがでる(右)。おそらくLogicool カメラ設定がC922nを使用している為、カメラアプリでは使えないのだと思う。

f:id:sonoka_gi:20201230195631j:plainf:id:sonoka_gi:20201230195625j:plain

両アプリを起動した後は、Logicool カメラ設定の「高度な」のタブでフォーカスやホワイトバランスを調整できる。 f:id:sonoka_gi:20201230233926j:plain

4. その他のアプリ使用時も同様に調整できる

上記の方法は、他のアプリでも同様に使える。例えば、シンプルなWebカメラ表示ソフト CameraViewer であれば、Run cameraボタンを押下してカメラ画像を表示した後、Logicool カメラ設定を起動すれば、カメラの設定を調整できる。 f:id:sonoka_gi:20201230195620j:plain

その他にも、プログラミング環境 Processing3 のカメラ入力サンプル(GettingStartedCapture)でも、同様に機能した。 f:id:sonoka_gi:20201230233933j:plain


参考にした情報

  1. Rapi TVさん - Windows10 - カメラアプリの便利な使い方(初心者向け)
  2. WEBマスターの手帳さん - Webカメラ(ロジクール)の色や明るさ、フリッカーを調整できる「Camera Setting」
  3. Qiita @michiさん - MacでLogicoolのWebカメラを使ったらフリッカーがでた時は
  4. 工学ナビ さん - BACK YARD
  5. Logicool - C922n Pro Stream Webcam

C言語で、符号やサイズが異なる場合のキャスト動作を確認してみた

C言語のキャスト動作について自分の中で混乱があったので、RXマイコンコンパイラ(CC-RX)を使って実動作を確認してみました。
注意:処理系依存の内容を含むため、他のコンパイラでは異なる可能性があります


分かったこと

CC-RXコンパイラで、キャストの実動作を確認したところ、下記でした

  1. キャスト前後で「ビットパターン」は維持される。「符号付き・なし整数としての値」は変化する事がある
  2. 小さいサイズの整数にキャスト → 上位ビットを切り捨てる
  3. 大きいサイズの整数にキャスト → 元データが符号付き整数なら符号拡張、符号なし整数ならゼロ拡張

キャスト動作の例

  1. 16bit符号付き整数 8000h(-32768) を、同じサイズの16bit符号なし整数にキャスト
    → ビットパターンを維持して 8000h になり、16bit符号なし整数としての値は +32768 に変化

  2. 16bit符号なし整数 7FFFh(32767) を、より小さな8bit符号付き整数にキャスト
    → 上位ビットを切り捨てて FFh(-1) になる

  3. a) 16bit符号付き整数 8000h(-32768) を、より大きな32bit符号なし整数にキャスト
    → 符号拡張して FFFF8000h(+4294967167) になる
    b) 16bit符号なし整数 8000h(+32768) を、より大きな32bit符号付き整数にキャスト
    → ゼロ拡張して 00008000h(+32768) になる

以前、自分は「符号付き・なし整数としての値」に着目して混乱したのですが、「ビットパターン」で見るとシンプルな動作でした


確認内容

1. 方法

次のように、16bit符号付き・なしの整数を、8/16/32bit符号付き・なし整数にキャストして表示します。

// input --------------------------
int16_t i16_input = -128;
// cast result --------------------
int8_t   i8  = (int8_t)   i16_input;
int32_t  i16 = (int16_t)  i16_input;
int32_t  i32 = (int32_t)  i16_input;
uint8_t  u8  = (uint8_t)  i16_input;
uint16_t u16 = (uint16_t) i16_input;
uint32_t u32 = (uint32_t) i16_input;

2. 環境

  • IDE: e2studio V7.4.0
  • Compiler: CC-RX V3.01.00 (C99規格を選択)

上記環境でビルドしたプログラムをRXシミュレータ上で動作させ、Renesas Debug Virtual Consoleにprintf出力して確認します。以前のこの投稿とほぼ同じ環境です。

3. 結果

結果から、①元データ(16bit)のビットパターンが維持される、②小さい型にキャストすると上位ビットが切り捨てられる、③大きい型にキャストした時の上位ビットの埋め方(符号拡張・ゼロ拡張)が読み取れます。

符号付き整数からキャスト 符号なし整数からキャスト

4. テストプログラム

#include <stdio.h>
#include <inttypes.h>

void main(void);
void print_cast_result(int8_t i8, int16_t i16, int32_t i32, uint8_t u8, uint16_t u16, uint32_t u32);

void main(void)
{
    // 符号付き変数からのキャスト確認
    printf("Cast from signed integer.\n");
    int16_t i16_input[] = {-128, -129, -32768, 127, 128, 32767};
    for(int n = 0; n < sizeof(i16_input)/sizeof(i16_input[0]); n++) {
        int8_t   i8  = (int8_t)   i16_input[n];
        int32_t  i16 = (int16_t)  i16_input[n];
        int32_t  i32 = (int32_t)  i16_input[n];
        uint8_t  u8  = (uint8_t)  i16_input[n];
        uint16_t u16 = (uint16_t) i16_input[n];
        uint32_t u32 = (uint32_t) i16_input[n];

        printf("input---------------------\n");
        printf("i16:    %04"PRIX16"h(%"PRId16")\n", i16_input[n], i16_input[n]);
        printf("cast result---------------\n");
        print_cast_result(i8, i16, i32, u8, u16, u32);
        printf("\n");
    }
    printf("\n");

    // 符号なし変数からのキャスト確認
    printf("Cast from unsigned integer.\n");
    uint16_t u16_input[] = {127, 128, 32767, 32768, 65535};
    for(int n = 0; n < sizeof(u16_input)/sizeof(u16_input[0]); n++) {
        int8_t   i8  = (int8_t)   u16_input[n];
        int16_t  i16 = (int16_t)  u16_input[n];
        int32_t  i32 = (int32_t)  u16_input[n];
        uint8_t  u8  = (uint8_t)  u16_input[n];
        uint16_t u16 = (uint16_t) u16_input[n];
        uint32_t u32 = (uint32_t) u16_input[n];

        printf("input---------------------\n");
        printf("u16:    %04"PRIX16"h(%"PRIu16")\n", u16_input[n], u16_input[n]);
        printf("cast result---------------\n");
        print_cast_result(i8, i16, i32, u8, u16, u32);
        printf("\n");
    }

    while(1);
}

void print_cast_result(int8_t i8, int16_t i16, int32_t i32, uint8_t u8, uint16_t u16, uint32_t u32)
{
    // PRI???は、int32_t型などをprintfで指定するためのマクロ(inttypes.hで定義されている)
    // https://qiita.com/hnw/items/0f0bbb943bad40cd0784
    printf("i8 :      %02"PRIX8"h(%"PRId8")\n", i8,  i8 );
    printf("i16:    %04"PRIX16"h(%"PRId16")\n", i16, i16);
    printf("i32:%08"PRIX32"h(%"PRId32")\n",     i32, i32);
    printf("u8 :      %02"PRIX8"h(%"PRIu8")\n", u8,  u8 );
    printf("u16:    %04"PRIX16"h(%"PRIu16")\n", u16, u16);
    printf("u32:%08"PRIX32"h(%"PRIu32")\n",     u32, u32);
}

最後に

CC-RX コンパイラのマニュアル「4.1.4 C99 の処理系定義」に上位ビット切り捨ては記載されていました。 しかし、C言語の規格 6.3.1.3 の部分がよく分からず、全ては理解できていない状態です。ただ、実動作としては把握できたので、今回はそれで良しとしてます。

CC-RX コンパイラ ユーザーズマニュアル

(30) 整数型の値を符号付き整数型に変換する際、値が変換先の型で表現できない場合の結果、あるいは生成されるシグナル。(6.3.1.3)
変換先の型の幅でマスクした(上位ビットを切り捨てた)ビット列とします。

JISX3010:2003 プログラム言語C

6.3.1.3 ・・・ 新しい型で表現できない場合,新しい型が符号無し整数型であれば,新しい型で表現しうる最大の数に1加えた数を加えること又は減じることを,新しい型の範囲に入るまで繰り返すことによって得られる値に変換する(49)。


参考にした情報

  1. 初心者のためのポイント学習C言語 - 少し詳しい型変換の説明
  2. Qiita @sigma_signature さん - キャスト演算子を理解する
  3. Qiita @hnw さん - C99のint32_t型などをprintfに渡す方法
  4. Renesas - CC-RX コンパイラ ユーザーズマニュアル
  5. kikakurui.com- JISX3010:2003 プログラム言語C
  6. Microsoft Docs

Processingで、学戦都市アスタリスクのクレジット表示を真似してみた

アニメ「学戦都市アスタリスク」の2期オープニングのクレジット表示をProcessingで真似してみました

このアニメのCGなどによるエフェクトは、結構気に入っています。その中で目をひかれた、2期オープニングのクレジットタイトル(監督、出演者、スタッフ名など)の表現を、Processingで真似してみました。公式の動画は見当たらず直接紹介できませんが、気になる方はYouTubeなどで探してみてください。

環境

  • Processing 3.5.4

ソースコード

プロジェクト一式は、こちら
sonokagi/asterisk_op2_credit - GitHub

ポイント

1.アニメーションの作り方

Processingの時計関数 millis() を使って現在時刻を取得。時間経過によって draw() 関数の描画内容を変化させる事で、アニメーションを行います。
6角形Haxagonクラスを例にとって説明すると、下記の通りです。

  • start()関数は、アニメーション開始を指示する関数です。
    この関数では time_start_ 変数にアニメーション開始時間を保存しておきます。
  void start(int delay_ms, int priod_show_ms, int priod_hide_ms ) {
    time_start_ = millis();
 ・・・
  }
  • draw()関数は、各フレーム毎に呼び出す描画関数です。
    1. time = millis() - time_start_ で、アニメーション開始からの時間経過を取得(a)
    2. 時間経過に応じて、表示内容を4通りに変更
      (b)表示なし→(c)6角形表示(塗りつぶしあり)→(d)6角形表示(枠のみ)→(e)消去
    // (a) アニメーション開始指示からの時間経過を取得
    int time = millis() - time_start_;

    // 時間経過に応じた処理
    // (b) 表示なし
    if ( time < time_disp_ ) {
    }
    // (c) 6角形表示(塗りつぶしあり)
    else if (time < time_hide_ ) {
      ・・・
    }
    // (d) 6角形表示(枠のみ)
    else if (time < time_end_ ) {
      ・・・
    }
    // (e) 消去
    else {
    }

以上で、start()関数を呼び出すと、時間経過によってdraw()関数の描画内容が変化し、6角形が表示・消滅するアニメーションになります。
この6角形クラスを複数個まとめたものが HexagonLine クラスで、各6角形の表示位置・アニメーション開始タイミングをずらす事でライン状にスキャンするアニメーションとしてます。

2.サイバーテキストの利用

スローで再生するとわかるのですが、クレジットの名前は一時的に違う文字が表示され、徐々に正しい名前に置き換わっていく表現になっています。

尚→釘→小野敦→小野学

今回は、類似表現を工学ナビさんのサイトにあるWatch Dogs Profilerで見つけたので、これを改造して利用しました。ただ、表示速度を早くした結果、ほとんど効果が分からなくなってしまいました・・・オリジナルは、かなりカッコいいです。

参考にした情報

  1. deconbatch さん - ぼんやり光る効果を出す簡単な方法 その2 : Processing Tips
  2. 工学ナビ さん - BACK YARD
  3. おもちゃラボ さん - はてなブログでソースコードを折りたたむ方法

RXマイコンで、Unityを使ってハードウェアの動作確認をしてみた

前回記事「RXマイコンで、Unityを使って単体テストをやってみた」のおまけとして、ハードウェアの動作確認のテストを書いてみました。

  • Unityを使った単体テスト環境のセットアップについては、こちら
  • プロジェクト一式は、こちら(rx231_unit_test_3.zip - Google ドライブ)
    インポート方法
    1. メニュー >ファイル > インポート でインポートのダイアログを表示
    2. 既存プロジェクトをワークスペースへを選択
    3. アーカイブ・ファイルの選択でファイルを選択し、終了ボタンでインポート

環境

お題

ターゲットボードには、下記仕様のLEDとSWがあるので、これらのIOチェックを作ります。

port name dir L H
PD6 LED0 out 点灯 消灯
PD7 LED1 out
PB1 SW1 in 押す 離す

ターゲットボード環境でのテスト

1. テストファイルを用意する

以前の記事で、下記のテストファイルを/srt/test/以下に用意しています。

  • Test0.c - テストグループの定義、および、各テストケースを記述
  • AllTests.c - テストで実行するテストグループを記述

このうち、Test0.cを修正してテストを用意します。

ハードウェアの動作確認では、人がLEDの点灯状態をチェックしたり、SWを操作する必要があります。このため、チェック結果を入力させたり、操作メッセージを表示する補助関数を用意します。

  • MANUAL_CHECK(question)
    確認メッセージを表示し、チェック結果のキー入力y(es)もしくはn(o)を受け取ります。その結果によって、テストをPASS/FAILさせます。FAIL時に行番号がわかるようにマクロで記述しています。
#define MANUAL_CHECK(question)  _ManualCheck(question, __LINE__)
static void _ManualCheck(const char *question, int line)
{
    printf("\nManual Check> %s -> press <y/n>: ", question);
    if ( getchar() == 'y' ) {
        // 手動判定:PASS
    } else {
        // 手動判定:FAIL
        char msg[100];
        sprintf(msg, "Manual Check(line:%d)", line);
        TEST_FAIL_MESSAGE(msg);
    }
}
  • ManualOperation(const char *operation)
    操作メッセージを表示し、キー入力y(es)を受けとると、処理を継続します。
static void ManualOperation(const char *operation){
    printf("\nManual Operation> %s -> press <y>: ", operation);
    getchar();
}

これらを利用すると、テストコードは下記になります。

#include "../unity/unity_fixture.h"
#include <stdio.h>
#include "../io.h"

// 各テストケースの前に実行する共通処理(初期化)
TEST_SETUP(Test0)
{
    LED0 = LED_OFF;
    LED1 = LED_OFF;
}

// テストケース(LED0 の IOチェック)
TEST(Test0, Led0_IoCheck)
{
    LED0 = LED_ON;
    MANUAL_CHECK("Is LED0 on?");
    LED0 = LED_OFF;
    MANUAL_CHECK("Is LED0 off?");
}

// テストケース(LED1 の IOチェック)
TEST(Test0, Led1_IoCheck)
{
    LED1 = LED_ON;
    MANUAL_CHECK("Is LED1 on?");
    LED1 = LED_OFF;
    MANUAL_CHECK("Is LED1 off?");
}

// テストケース(SW1 の IOチェック)
TEST(Test0, SW1_IoCheck)
{
    ManualOperation("push sw1.");
    TEST_ASSERT_EQUAL(SW1_PUSH, SW1);
    ManualOperation("release sw1.");
    TEST_ASSERT_EQUAL(SW1_RELEASE, SW1);
}

// テストグループで、実行するテストケースを列挙する
TEST_GROUP_RUNNER(Test0)
{
    RUN_TEST_CASE(Test0, Led0_IoCheck);
    RUN_TEST_CASE(Test0, Led1_IoCheck);
    RUN_TEST_CASE(Test0, SW1_IoCheck);
}
#define LED_ON          (0)
#define LED_OFF         (1)
#define SW1_PUSH        (0)
#define SW1_RELEASE     (1)

/* Switches */
#define SW1             (PORTB.PIDR.BIT.B1)

/* LED port settings */
#define LED0            (PORTD.PODR.BIT.B6)
#define LED1            (PORTD.PODR.BIT.B7)

2. ターゲットボード環境でのテスト実行

  • ターゲットボードをPCに接続しておく
  • ビルド構成 としてHardwareDebug(Debug on hardware)を選択し、ビルド
  • デバックの構成 として~ HardwareDebugを選択し、デバック開始&プログラム実行
  • Renesas Debug Virtual Consoleの表示に従い、SW操作とキーボードを押下し、テストを進める。
  • 最終的に、下記表示となればOK。
Unity test run 1 of 1
TEST(Test0, Led0_IoCheck)
Manual Check> Is LED0 on? -> press <y/n>: y
Manual Check> Is LED0 off? -> press <y/n>: y PASS
TEST(Test0, Led1_IoCheck)
Manual Check> Is LED1 on? -> press <y/n>: y
Manual Check> Is LED1 off? -> press <y/n>: y PASS
TEST(Test0, SW1_IoCheck)
Manual Operation> push sw1. -> press <y>: y
Manual Operation> release sw1. -> press <y>: y PASS

-----------------------
3 Tests 0 Failures 0 Ignored 
OK

ちなみに、NG時は下記です。手動判定でのNG行番号はManual Check(line:XX)と表示されます。

Unity test run 1 of 1
TEST(Test0, Led0_IoCheck)
Manual Check> Is LED0 on? -> press <y/n>: n../src/test/Test0.c:19::FAIL: Manual Check(line:49)
TEST(Test0, Led1_IoCheck)
Manual Check> Is LED1 on? -> press <y/n>: n../src/test/Test0.c:19::FAIL: Manual Check(line:59)
TEST(Test0, SW1_IoCheck)
Manual Operation> push sw1. -> press <y>: y../src/test/Test0.c:69::FAIL: Expected 0 Was 1

-----------------------
3 Tests 3 Failures 0 Ignored 
FAIL

最後に

今回は、テストフレームワークをハードウェアの動作確認として使ってみました。
ハードウェアの動作確認というと、PCからターゲットボードにコマンドを投げて確認するとか、デバックポートでCUIを作ったりとか、になりそうですが、既存のIDE環境で手をかけずに作れるのはメリットと思いました。また、項目が増えてきた時、確認項目(テストケース)をテストフレームワークの管理方法で整理しておくと、保守しやすいかも知れないです。

RXマイコンで、Unityによる単体テストをやってみた

Qiitaの「TDDによるマイコンのLチカ開発」を、ルネサス製のRXマイコンIDE(e2studio)の組み合わせで、真似してみました。

  1. RXマイコンで、Unityを使って単体テストをしてみる。
  2. シミュレータ環境、ターゲットボード環境の2通り。
  3. 元の記事にあった、ホスト環境は省略。
  • Unityを使った単体テスト環境のセットアップについては、こちら
  • プロジェクト一式は、こちら(rx231_unit_test_2.zip - Google ドライブ)
    インポート方法
    1. メニュー >ファイル > インポート でインポートのダイアログを表示
    2. 既存プロジェクトをワークスペースへを選択
    3. アーカイブ・ファイルの選択でファイルを選択し、終了ボタンでインポート

環境

お題

ハードウェアを絡めた一番簡単なお題を設定します。ターゲットボードには、LED2つとSW1つがあります。これを利用して、次の機能を持つ関数IO_CotrolLedBySw()を作ります。

  • SW1 を押すと、LED0が点灯
  • SW1 を離すと、LED0が消灯

LEDとSWの仕様は下記の通りです。

port name dir L H
PD6 LED0 out 点灯 消灯
PD7 LED1 out
PB1 SW1 in 押す 離す

シミュレータ環境でのテスト

1. テストファイルを用意する

以前の記事で、下記のテストファイルを/srt/test/以下に用意しています。

  • Test0.c - テストグループの定義、および、各テストケースを記述
  • AllTests.c - テストで実行するテストグループを記述

このうち、Test0.cを修正してテストを用意します。

シミュレータ環境の特徴として、ReadOnlyの入力データレジスタ(PIDR)にも書き込み可能です。
このため、SW1 のボタンを押した状態にするには、

// SW1 のボタンを押した状態にする
SW1 = SW1_PUSH;           // PORTB.PIDR.BIT.B1 = 0;

で済みます。これを利用すると、テストコードは下記になります。

#include "../unity/unity_fixture.h"
#include "../io.h"

・・・

// テストケース(IO_CotrolLedBySw() - SW1 を押すと、LED0が点灯)
TEST(Test0, IO_CotrolLedBySw_SW1Push)
{
    // まず、LED0をOFFしておく
    LED0 = LED_OFF;

    // SW1 のボタンを押した状態にする
    // シミュレータ環境では、入力データレジスタに値を書き込める
    SW1 = SW1_PUSH;

    // テスト対象関数を呼ぶ
    IO_CotrolLedBySw();

    // LED0がONに変化した事をチェック
    TEST_ASSERT_EQUAL(LED_ON, LED0);
}

// テストケース(IO_CotrolLedBySw() - SW1 を離すと、LED0が消灯)
TEST(Test0, IO_CotrolLedBySw_SW1Release)
{
    // まず、LED0をONしておく
    LED0 = LED_ON;

    // SW1 のボタンを離した状態にする
    SW1 = SW1_RELEASE;

    // テスト対象関数を呼ぶ
    IO_CotrolLedBySw();

    // LED0がOFFに変化した事をチェック
    TEST_ASSERT_EQUAL(LED_OFF, LED0);
}

// テストグループで、実行するテストケースを列挙する
TEST_GROUP_RUNNER(Test0)
{
    RUN_TEST_CASE(Test0, IO_CotrolLedBySw_SW1Push);
    RUN_TEST_CASE(Test0, IO_CotrolLedBySw_SW1Release);
}

2. テスト対象コードの用意

実際はTDD的に実装を進めるのですが、記事の都合上、最終コードを掲載します。

#ifndef IO_H_
#define IO_H_
#include "r_smc_entry.h"    // iodefine.h を使用するために記載

#define LED_ON          (0)
#define LED_OFF         (1)
#define SW1_PUSH        (0)
#define SW1_RELEASE     (1)

/* Switches */
#define SW1             (PORTB.PIDR.BIT.B1)

/* LED port settings */
#define LED0            (PORTD.PODR.BIT.B6)
#define LED1            (PORTD.PODR.BIT.B7)

void IO_CotrolLedBySw(void);

#endif /* IO_H_ */
#include "io.h"

void IO_CotrolLedBySw(void)
{
    if ( SW1 == SW1_RELEASE ) {
        LED0 = LED_OFF;
    } else {
        LED0 = LED_ON;
    }
}

3. シミュレータ環境でのテスト実行

  • ターゲットボードをPCから外しておく
  • ビルド構成としてDebug(Debug)を設定し、ビルド
  • デバックの構成として~ Debugを選択し、デバック開始&プログラム実行
  • Renesas Debug Virtual Consoleに下記が表示されればOK
Unity test run 1 of 1
TEST(Test0, IO_CotrolLedBySw_SW1Push) PASS
TEST(Test0, IO_CotrolLedBySw_SW1Release) PASS

-----------------------
2 Tests 0 Failures 0 Ignored 
OK

ターゲットボード環境でのテスト

1. テストファイルを用意する

ターゲットボード環境では、SW1 を人に操作してもらいます。このため、メッセージを表示する補助関数を用意します。

// 手動操作の補助関数
static void ManualOperation(const char *operation){
    printf("\nManual Operation> %s -> press <y>: ", operation);
    getchar();
}

これを利用すると、テストコードは下記になります。

USE_SIMULATORはシミュレータ環境のみ定義されるマクロです。

#include "../unity/unity_fixture.h"
#include "../io.h"

・・・

// テストケース(IO_CotrolLedBySw() - SW1 を押すと、LED0が点灯)
TEST(Test0, IO_CotrolLedBySw_SW1Push)
{
    // まず、LED0をOFFしておく
    LED0 = LED_OFF;

    // SW1 のボタンを押した状態にする
#ifdef USE_SIMULATOR
    // シミュレータ環境では、入力データレジスタに値を書き込める
    SW1 = SW1_PUSH;
#else
    // ターゲットボード環境では、手動でSW1を操作し、状態をチェックして進む
    do {
        ManualOperation("push sw1.");
    } while( !(SW1 == SW1_PUSH) );
#endif

    // テスト対象関数を呼ぶ
    IO_CotrolLedBySw();

    // LED0がONに変化した事をチェック
    TEST_ASSERT_EQUAL(LED_ON, LED0);
}

// テストケース(IO_CotrolLedBySw() - SW1 を離すと、LED0が消灯)
TEST(Test0, IO_CotrolLedBySw_SW1Release)
{
    // まず、LED0をONしておく
    LED0 = LED_ON;

    // SW1 のボタンを離した状態にする
#ifdef USE_SIMULATOR
    SW1 = SW1_RELEASE;
#else
    do {
        ManualOperation("release sw1.");
    } while( !(SW1 == SW1_RELEASE) );
#endif

    // テスト対象関数を呼ぶ
    IO_CotrolLedBySw();

    // LED0がOFFに変化した事をチェック
    TEST_ASSERT_EQUAL(LED_OFF, LED0);
}

// テストグループで、実行するテストケースを列挙する
TEST_GROUP_RUNNER(Test0)
{
    RUN_TEST_CASE(Test0, IO_CotrolLedBySw_SW1Push);
    RUN_TEST_CASE(Test0, IO_CotrolLedBySw_SW1Release);
}

2. ターゲットボード環境でのテスト実行

  • ターゲットボードをPCに接続しておく
  • ビルド構成 としてHardwareDebug(Debug on hardware)を選択し、ビルド
  • デバックの構成 として~ HardwareDebugを選択し、デバック開始&プログラム実行
  • Renesas Debug Virtual Consoleの表示に従い、SW操作とキーボードを押下し、テストを進める。
  • 最終的に、下記表示となればOK。
Unity test run 1 of 1
TEST(Test0, IO_CotrolLedBySw_SW1Push)
Manual Operation> push sw1. -> press <y>: y PASS
TEST(Test0, IO_CotrolLedBySw_SW1Release)
Manual Operation> release sw1. -> press <y>: y PASS

-----------------------
2 Tests 0 Failures 0 Ignored 
OK

3. 作った関数を動作させる

メイン関数を書き換えて、関数IO_CotrolLedBySw()を呼び出します。 ターゲットボード環境でビルド・実行。SW1押下に応じて、LED0が点灯・消灯すればOKです。

#include "r_smc_entry.h"
#include "io.h"

void main(void)
{
    LED0 = LED_OFF;
    LED1 = LED_OFF;
    while(1) {
        IO_CotrolLedBySw();
    }
}

最後に

実際に試すことで、シミュレータ環境とターゲットボード環境の差が理解できました。シミュレータ環境では、ReadOnlyのレジスタにライト可能であり、入力信号を任意に設定にできます。同様に、CPUペリフェラルのエラーも任意に設定できるので、テスト作成で大きな助けとなりそうです。
このレジスタ設定の自由度は、Unity作者様の記事"WHICH BUILD METHOD?"でも、シミュレータ環境のメリットとして紹介されています。ただ、ターゲットボード環境も、ハードウェアの動作確認やエージングで活用できるのでは?と思っています。

参考にした情報

  1. Qiita - @iwatake2222 さん

    ⇒ 今回やってみる動機となった記事です。

  2. Throw The Switch - Unity作者様のサイト

    単体テスト環境として、シミュレータ、ホスト(Windowsなど)、ターゲットボードを比較しています。

  3. 書籍 - テスト駆動開発による組み込みプログラミング―C言語とオブジェクト指向で学ぶアジャイルな設計

RXマイコンで、Unityによる単体テスト環境を作ってみた(後編)

Qiitaの記事「TDDによるマイコンのLチカ開発」を、ルネサス製のRXマイコンIDE(e2studio)の組み合わせで、真似してみました。

  1. RXマイコンで、Unityによる単体テスト環境をセットアップする。
  2. シミュレータ環境、ターゲットボード環境の2通りを用意する。
  3. 元の記事にあった、ホスト環境の構築、CMockの導入は省略。
  • 後編では、テストフレームワークUnityを導入し、テストを行います。
  • 前編はこちら。RXマイコンのプロジェクト生成とprintf()での文字出力を行います。
  • プロジェクト一式は、こちら(rx231_unit_test_1.zip - Google ドライブ)
    インポート方法
    1. メニュー >ファイル > インポート でインポートのダイアログを表示
    2. 既存プロジェクトをワークスペースへを選択
    3. アーカイブ・ファイルの選択でファイルを選択し、終了ボタンでインポート

環境

テストフレームワークの導入

元の記事と同様に、Unityを導入していきます。

1.マイコンIDE上でフォルダを作成する


プロジェクト・エクスプローラーで、新規>フォルダーを選択。以下の構造を作ります。

src/
    - smc_gen/              // 自動生成されたコードが格納されている
    - test/                 // テストファイルを格納 <追加>
    - unity/                // Unityのソースを格納 <追加>
    - rx231_unit_test.c     // メインのソースコード

2. Unityのソースコードをコピー


作成した/src/unity/フォルダに、Unity(https://github.com/ThrowTheSwitch/Unity)の下記ソースをドラック&ドロップで追加します。

  • Unity-master/src
    unity.c, unity.h, unity_internals.h
  • Unity-master/extras/fixture/src
    unity_fixture.c, unity_fixture.h, unity_fixture_internals.h, unity_fixture_malloc_overrides.h

3. テストファイルを用意する


テストファイルを/srt/test/以下に用意します。それぞれ次の用途です。

  • Test0.c - テストグループの定義、および、各テストケースを記述
  • AllTests.c - テストで実行するテストグループを記述
#include "../unity/unity_fixture.h"

// テストグループを定義
TEST_GROUP(Test0);

// 各テストケースの前に実行する共通処理(初期化)
TEST_SETUP(Test0)
{
}

// 各テストケースの後に実行する共通処理(後片付け)
TEST_TEAR_DOWN(Test0)
{
}

// テストケース
TEST(Test0, AlwaysFail)
{
    // 何もせず、テストを失敗させて、メッセージを出力する
    TEST_FAIL_MESSAGE("This test always fails.");
}

// テストグループで、実行するテストケースを列挙する
TEST_GROUP_RUNNER(Test0)
{
    RUN_TEST_CASE(Test0, AlwaysFail);
}
#include "../unity/unity_fixture.h"

// 実行するテストグループを列挙する
void RunAllTests(void)
{
    RUN_TEST_GROUP(Test0);
}

4. main関数からテストを呼ぶ


main関数からテストを呼びます。UnityMain()にはコマンドラインオプションが指定できます。ここでは"-v"を渡し、各テストの実行前にテスト名を出力します。

#include "r_smc_entry.h"
#include "unity/unity_fixture.h"

void main(void)
{
#if 1
    // コマンドラインオプション "-v" を指定。
    // -v   詳細(verbose)モード。各テストの実行前にテスト名を出力する。
    int argc = 2;
    const char *argv[] = {"program name", "-v"};
    extern void RunAllTests(void);
    UnityMain(argc, argv, RunAllTests);
#endif
    while(1);
}

5. プロジェクト設定


  • プロジェクトの設定で、math.h のライブラリを生成するよう変更します1
    • プロジェクトエクスプローラから、プロパティ > C/C++ ビルド > 設定 を選択
    • ツール設定のタブより、Library Generator > 構成を選択
    • 構成を[全ての構成]に変更した後、math.hをチェック

    f:id:sonoka_gi:20190623234354p:plain

6. 実行してみる


ターゲットボード環境、シミュレータ環境のそれぞれでビルド・デバック実行し、下記が出力さればOK。

Unity test run 1 of 1
TEST(Test0, AlwaysFail)../src/test/Test0.c:23::FAIL: This test always fails.

-----------------------
1 Tests 1 Failures 0 Ignored 
FAIL

f:id:sonoka_gi:20190623234409p:plain

最後に

シミュレータとRenesas Debug Virtual Consoleのおかげで単体テスト環境構築は簡単です。いつものIDE環境に+αで気軽にテストを始められるのは良いと思います 。(これから先が大変なんでしょうけど・・・)
シミュレータ環境は通常の単体テストに。ターゲットボード環境はハードウェアの動作確認に利用できそうです。

参考にした情報

  1. Qiita - @iwatake2222 さん

    ⇒ 今回やってみる動機となった記事です。

  2. 書籍 - テスト駆動開発による組み込みプログラミング―C言語とオブジェクト指向で学ぶアジャイルな設計

  3. ルネサスのドキュメント - e2 studioでのUnityの使用方法(R20AN0313JJ0100)
    ⇒ 今回は参考にしてませんが、以前、アプリケーションノートを発行していたようです。


  1. これを設定しないと_FDxxxxが見当たらないというエラーがでました。

RXマイコンで、Unityによる単体テスト環境を作ってみた(前編)

Qiitaの記事「TDDによるマイコンのLチカ開発」を、ルネサス製のRXマイコンIDE(e2studio)の組み合わせで、真似してみました。

  1. RXマイコンで、Unityによる単体テスト環境をセットアップする。
  2. シミュレータ環境、ターゲットボード環境の2通りを用意する。
  3. 元の記事にあった、ホスト環境の構築、CMockの導入は省略。 f:id:sonoka_gi:20190629201139p:plain

環境

プロジェクト作成とprintf()での文字出力

e2studio(V7.3.0以降)では、プロジェクトを生成時に「Renesasデバック仮想コンソールを使用する」をチェックすると、IDE内の専用コンソールを標準出力として使えます。※但し、かなりの出力遅延あり。

1. プロジェクト作成


メニュー > ファイル > 新規 > C/C++プロジェクト から作成し、下記を設定。

  • Templete for New C/C++ Project

    • Renesas RX > Renesas CC-RX C/C++ Executable Projectを選択
  • Select toolchain, device&debug settings

    • Device Settingsのターゲット・デバイスは、RX231-100pin>R5F52318AxFPを選択
    • Configrationsの Hardware Debug構成を生成Debug構成を生成 の2つにチェック
      ※それぞれ、ターゲットボード環境、シミュレータ環境に相当
  • コーディングアシストツールの選択

    • スマートコンフィグレータを使用するにチェック
  • 生成する初期化ルーチンの選択

    • Renesasデバック仮想コンソールを使用するをチェック

    f:id:sonoka_gi:20190622235532p:plain

2. ハードウェア初期化コードの生成


スマートコンフィグレータによるコード生成を利用し、クロックとGPIOの初期化コードを生成します。

  • [クロック]タブ

    • メインクロックのチェックを外す(実装されていないので)
    • 高速オンチップオシレータHOCOクロック(54MHz)を選択

    f:id:sonoka_gi:20190623000117p:plain

  • [コンポーネント]タブ

    • コンポーネントの追加ボタンで、ソフトウェアコンポーネントの選択ダイアログを表示。
    • コンポーネント[ポート]を追加

    f:id:sonoka_gi:20190623000137p:plain

  • コンポーネントConfig_PORTを選択。LED出力2点(PD6,7)、SW入力1点(PB1)のGPIOを設定

    f:id:sonoka_gi:20190623000201p:plain

  • 右上にあるコードの生成ボタンを押すと、初期化コードが生成される

3. コード修正


プロジェクトの構成は下記のようになっています。ここでは2か所のコードを修正します。

f:id:sonoka_gi:20190623015542p:plain

  • main関数に、printf() と Unityで使う putchar() の動作確認コードを追加。
#include "r_smc_entry.h"
#include <stdio.h>

void main(void)
{
    printf("Hello World!\n");
    putchar('p');
    while(1);
}
  • Debug構成(シミュレータ環境)に、コンパイルスイッチUSE_SIMULATORを追加し、クロック初期化コードを一部無効にします1
static void clock_source_select (void)
{
    ・・・・
    /* Make sure HOCO is stopped before changing frequency. */
    SYSTEM.HOCOCR.BYTE = 0x01;

    /* Set frequency for the HOCO. */
    SYSTEM.HOCOCR2.BIT.HCFRQ = BSP_CFG_HOCO_FREQUENCY;

    /* HOCO is chosen. Start it operating. */
    SYSTEM.HOCOCR.BYTE = 0x00;
    /* WAIT_LOOP */
#ifndef USE_SIMULATOR
    // シミュレータでは、ここでハードウェアの変化を無限に待ってしまう
    while (SYSTEM.OSCOVFSR.BIT.HCOVF != 1)
    {
        ;   // wait for stabilization
    }
#endif
    ・・・・
}
  • プロジェクトエクスプローラから、プロパティ > C/C++一般 > パスおよびシンボルを選択。
    • 構成:を、Debugに変更
    • #シンボルタブより、コンパイルスイッチUSE_SIMULATORを追加

    f:id:sonoka_gi:20190623014916p:plain

4. ターゲットボード環境でのビルドと動作確認


  • ターゲットボードをPCに接続しておく

  • ビルドする

    • プロジェクトエクスプローラから、ビルド構成 > アクティブにする > HardwareDebug(Debug on hardware)を選択。その後、プロジェクトのビルドを選択。
  • デバック設定とデバックの開始

    • メニュー > 実行 > デバックの構成 より、~ HardwareDebug の構成を選択し、Debuggerタブを開く
    • Debugger hardware をE2 Lite(RX) に設定
    • 電源 > エミュレータから電源を供給する を いいえ に設定
    • デバックのボタンを押下して、デバックを開始する。(再度デバックするときはF11で可)

    f:id:sonoka_gi:20190630185555p:plain

  • ターゲットへの接続・ダウンロードが完了したら、Renesas Debug Virtual Consoleのウィンドウを開く。見当たらない時はメニュー > Renesas Views > デバック > Renesas Debug Virtual Consoleで表示。

    • ウィンドウをドラックして、コンソールと同時に表示したほうが使いやすい感じです。 f:id:sonoka_gi:20190623001423p:plain
      f:id:sonoka_gi:20190623001426p:plain
  • プログラム実行(F8を押下)。コンソールに"Hello World!"と"p"が表示されればOK f:id:sonoka_gi:20190623001405p:plain

5.シミュレータ環境の設定と動作確認


  • ターゲットボードをPCから外しておく

  • ビルドする

    • プロジェクトエクスプローラから、ビルド構成 > アクティブにする > Debug(Debug)を選択。その後、プロジェクトのビルドを選択。
  • デバック設定とデバックの開始

    • メニュー > 実行 > デバックの構成 より、~ Debug の構成を選択。(特に設定項目なし)
    • デバックのボタンを押下して、デバックを開始する。(再度デバックするときはF11で可)

    f:id:sonoka_gi:20190623001043p:plain

  • プログラム実行(F8を押下)。Renesas Debug Virtual Consoleに"Hello World!"と"p"が表示されればOK

    f:id:sonoka_gi:20190623001030p:plain

最後に

e2studioのバージョンアップでRenesas Debug Virtual Consoleが簡単に使えるようになりました。このため、IDE上でのprintf()出力まではスムーズです。次は、Unityを導入して、単体テスト環境を構築します。

参考にした情報

  1. ルネサスのドキュメント

  2. ルネサスのFAQ


  1. シミュレータでは、CPUの周辺ハードウェア動作はサポートされないため、ハードウェアの変化を待つループがあると、無限待ちになります。最初にシミュレータを利用した時、main()関数が呼ばれなくて困りました・・・