本連載では、小学生向きのプログラミング教育などに使われているBBC micro:bit(以下「micro:bit」)の上で動くようになったµT-Kernel 3.0をご紹介している。連載第11回の本号では、micro:bitのA/D変換(アナログ/デジタル変換)の機能を試す。ジョイスティックや光センサーを使ってA/D変換の基本機能を確認してみよう。
A/D変換の 'A' はアナログ(Analog)を表しており、その本来の意味としては、連続量を表現できるという性質を示す。世の中の多くの事象、たとえば明るさ、温度、位置や速度、力、時間などはすべて連続量であり、世の中をマクロに見ればすべてアナログな世界である。
これらのアナログ量を電子回路やコンピュータで扱う場合は、各種のセンサーなどを使って、対象となるアナログ量を電圧値に変換して入力することが多い。たとえば、温度をコンピュータで扱う場合には、温度センサーを使って温度を電圧値に変換してからコンピュータに入力する。コンピュータ側から見ると、電圧値を入力する機能があれば、その先にいろいろなセンサーや電子回路を接続することによって、いろいろなアナログのデータを得ることができる。
一方、'D' のデジタル(Digital)の本来の意味としては、それとは反対に、1と0のような離散量、離散値で表現されるものを表す。しかし、現代のコンピュータがネイティブで扱う情報はすべてデジタルであることから、離散値という本来のデジタルの意味から離れて、コンピュータで扱う情報処理の対象が全般的にデジタルとよばれるようになった。最近ではデジタル・トランスフォーメーション(DX)といった語も盛んに使われているが、DXの「デジタル」ではすでに離散値という意味は消えており、高度な情報処理の代名詞として「デジタル」という語が使われている。
話をA/D変換に戻す。micro:bitの場合も、A/D変換のアナログ入力としては連続量の電圧値を使い、それを8ビットないしは14ビットのデジタル値に変換してmicro:bitに入力する。変換後のデジタル値のビット幅は、A/D変換器のレジスタの設定によって変更できる。たとえば8ビットの設定にした場合は、micro:bitへのアナログ入力電圧が、概ね0から256(=2の8乗)のデジタル値に変換される。
図1 micro:bitのSAADCのブロック図
(*1のFigure 1より引用)
図2 CH[n].CONFIGレジスタの構成と
各ビットフィールドの機能
(*1の資料の一部を要約)
micro:bitでA/D変換を行うためには、Target MCUのnRF52833に内蔵されているSAADC(Successive Approximation Analog-to-Digital Converter)という機能を利用する。Successive ApproximationとはA/D変換の実現方式の一つであり、逐次比較型とよばれる。この方式では、A/D変換とは逆のD/A変換の機能と、アナログ値同士の比較の機能を使うことによって、A/D変換の処理を行う。具体的には、まず出力結果となるべきデジタル値を仮決めし、その値をD/A変換してアナログ電圧にしてから、変換対象のアナログ入力電圧と比較する。前者の方が高ければ出力結果となるべきデジタル値を小さくし、後者の方が高ければデジタル値を大きくする。この処理を繰り返せば、デジタル値のD/A変換の結果が次第にアナログ入力電圧に近づき、最終的にはほぼ一致するようになる。この状況で、原因のデジタル値と結果のアナログ値を入れ替えて解釈すると、アナログ入力電圧(=アナログ値)をA/D変換した結果が出力のデジタル値になるというわけである。この方式では、アナログ値の比較とデジタル値の修正を行うたびに、出力結果となるべきデジタル値の精度が高まっていく。これが逐次比較型のA/D変換とよばれる所以である。なお、デジタル値からアナログ電圧へのD/A変換は、デジタル値の各ビットの重みに応じて抵抗器を組み合わせた電子回路を使えば簡単に実現できる。
nRF52833のSAADCのマニュアル(*1) に書かれたブロック図を図1に示す。このSAADCでは、最大で8チャンネルのA/D変換処理を行うことができる。A/D変換器のアナログ入力にはAIN0からAIN7まで8組の線が接続されており(図の左側)、このうちのどこから入力するかをA/D変換器のレジスタで指定できる。また、1組の入力にはP入力(Positive)とN入力(Negative)の2本の線があるが、この2本の線の接続先についても、AIN0からAIN7への直接接続(Bypass)、電源電圧へのプルアップ、GNDへのプルダウンなどを選択できる(図2)。
A/D変換の処理結果は、SAADCの中のレジスタに置かれるのではなく、EasyDMAという機能を通じてメモリ上に設定される。時間的に連続した複数回のA/D変換を行い、その変換出力のデジタル値をメモリ上の連続したアドレスに書き込んでいくことも可能である。このデータをプログラムから見ると、複数回のA/D変換の結果がメモリ上に置かれた配列になる。
SAADCの使い方を知るため、例によって、Target MCUであるnRF52833のマニュアル(*1) を参照する。A/D変換の基本的な機能や電気的な特性の説明に加えて、制限値(limit)の設定、割込みの発生、キャリブレーションなど、多くの機能がある。本稿ではA/D変換を行うために必要な最小限の機能のみを試すが、他の高度な機能もmicro:bit上で動作するので、興味があればこのマニュアルを見ながらチャレンジしてみてほしい。
nRF52833のSAADCのベースアドレスは0x40007000である。A/D変換の基本動作に必要なレジスタの説明を要約したものを表1に示す。また、このうちのCH[n].CONFIGレジスタとCH[n].PSELPレジスタについて、レジスタの構成と各ビットフィールドの位置や機能を図2と図3に示す。これらのレジスタを制御して、アナログ入力をデジタル値に変換するサンプルプログラムがリスト1である。以下、リスト1にそってSAADCの使い方を説明する。
レジスタ | アドレスの オフセット |
説明 |
---|---|---|
TASKS_START | 0x000 | A/D変換の開始 |
TASKS_SAMPLE | 0x004 | アナログ入力の1回のサンプル |
TASKS_STOP | 0x008 | A/D変換の停止 |
EVENTS_END | 0x104 | A/D変換結果の書込完了イベント |
EVENTS_DONE | 0x108 | A/D変換処理の完了イベント |
STATUS | 0x400 | ステータス |
ENABLE | 0x500 | SAADCのイネーブル |
CH[n].PSELP | 0x510+(n*0x10) | チャンネルnのP入力の選択 |
CH[n].PSELN | 0x514+(n*0x10) | チャンネルnのN入力の選択 |
CH[n].CONFIG | 0x518+(n*0x10) | チャンネルnの入力のコンフィグレーション |
RESOLUTION | 0x5F0 | 解像度(精度)の設定 |
RESULT.PTR | 0x62C | A/D変換の結果を入れるRAMの先頭アドレス |
RESULT.MAXCNT | 0x630 | A/D変換の結果(16ビット)を入れるRAMのデータ数 |
図3 CH[n].PSELPレジスタの構成と
各ビットフィールドの機能
(*1の資料の一部を要約)
micro:bit用のµT-Kernel 3.0のソースプログラムにはSAADC関連の定義が含まれていないので、最初にdefineマクロを使ってSAADCの各レジスタのアドレスを定義している(※A)。次に、SAADCの機能を使ってアナログ値を入力し、A/D変換を行ってその結果を取得する関数がanalogRead2である(※B)。この関数では、複数チャンネルのA/D変換機能の確認も兼ねるため、SAADCのCH[0]とCH[1]の二つのチャンネルを使い、2組のアナログ入力に対してA/D変換を行う。各チャンネルの入力の選択については、引数のch0_ainとch1_ainで指定する。ch0_ainやch1_ainではAIN0からAIN7のアナログ入力の選択を行うが、この具体的な意味については後述する。
analogRead2では、まず、A/D変換の各チャンネル(CH[0],CH[1])について、P入力の選択およびコンフィグレーションを行う(※C)。CH[0]の場合は、CH[0].PSELPのレジスタに関数の引数ch0_ainを、CH[0].CONFIGのレジスタに0を設定する。
micro:bitのSAADCには、1本のアナログ入力の電圧値、すなわちGNDレベルとの電位差をデジタル値に変換するSingle-ended modeと、2本のアナログ入力の電位差をデジタル値に変換するDifferential modeの二つの機能があり、どちらか一方を指定して利用できる。この指定は、CH[n].CONFIGレジスタのMODEのフィールド(最下位から21番目のビット、2^20のビット)によって行う。リスト1ではSingle-ended modeを使うので、このビットを0にする。この場合はN入力が使用されず、N入力を選択するCH[n].PSELNレジスタを設定する必要はない。
CH[n].CONFIGに0を設定することで、このレジスタ内のすべてのフィールド(RESP、RESN、GAIN、TACQ、MODEなど)に0が設定され、それぞれのデフォルトの機能を使うようになる。具体的には、MODE=0なので前述のようにSingle-ended modeで動作し、RESP=0によりP入力はBypass(直接接続)、GAIN=0によりGain1_6(6分の1)、TACQ=0によりデータ取得時間として3µsが設定される。詳細については、SAADCのマニュアル(*1) のCH[n].CONFIGの説明と図2を参照されたい。
次に、A/D変換の結果を入れるRAMの先頭アドレスとデータ数を、RESULT.PTRとRESULT.MAXCNTのレジスタに設定する(※D)。今回の場合は、二つのチャンネルから二つのデータを取得するが、analogRead2が呼ばれるたびに1回だけA/D変換を行うので、RESULT.MAXCNTには2を設定する。もし、複数回のA/D変換を連続して行うのであれば、(A/D変換の実行回数×チャンネル数)がRAM上の総データ数になるので、この数をRESULT.MAXCNTに設定する。
その後は、ENABLEレジスタに1を(※E)、RESOLUTIONのレジスタには解像度8ビットを表す0を設定して(※F)、A/D変換の処理の準備を進める。ここで、もしRESOLUTIONに0ではなく1、2、3を設定した場合は、A/D変換の処理結果のビット数が10、12、14と変化する。最後に、A/D変換結果の書込完了を判定するためのEVENTS_ENDのレジスタを0にクリアしてから(※G)、TASKS_STARTとTASKS_SAMPLEのレジスタに1を設定してA/D変換を開始する(※H)。今回のanalogRead2では1回だけA/D変換を行うが、複数回のA/D変換を連続して行う場合には、そのたびにTASKS_SAMPLEに1を書き込む。
ここまでの処理で、SAADCを使ったA/D変換処理が始まった。A/D変換にはある程度の時間がかかるため、その処理完了を待ってから結果を取得する必要がある。結果の書込完了はEVENTS_ENDレジスタの最下位ビットが1に変わることによって示されるので、それを確認した後に、analogRead2からリターンする(※J)。結果の書込がまだ完了していない場合には、10ミリ秒(ms)だけ待ってから、forループで書込完了の確認を繰り返す。ちなみに、A/D変換の処理に要する時間は、実際にはミリ秒以下であり、10ミリ秒も待つ必要はない。ただ、今回の用途では特に急ぐ必要もないので、tk_dly_tskを使った簡単な時間待ちにしている。A/D変換の結果をできるだけ早く知りたいのであれば、A/D変換結果の書込完了、すなわちEVENTS_ENDレジスタなどの変化を割込みで通知する方法がある。
A/D変換の結果を確認するには、とりあえず、tm_printfを使ってコンソールに出力すればよいのだが、せっかくなのでひと工夫してみよう。連載第7回で紹介した5行5列のマトリクスLEDを使って、変換されたデジタル値に応じた位置のLEDを点灯し、入力されたアナログ値を視覚的に表示する。analogRead2では2組のアナログ入力を扱えるので、一方のアナログ入力で列方向(横方向)のLEDの点灯位置を制御し、もう一方のアナログ入力で行方向(縦方向)のLEDの点灯位置を制御する。二つのアナログ入力値によってX座標とY座標を指定し、指定の位置にLEDを点灯するというイメージだ。LEDが5行5列なので、X座標やY座標といってもそれぞれ5段階ずつしかないが、アナログ入力による中間値が得られることは確認できる。
A/D変換の解像度は8ビットに設定したので、変換結果は概ね0から255の範囲の整数となる。この変換結果から5段階のLED座標を決める関数がscale_1to5である(※K)。0から255の範囲の数を1から5の範囲の数に縮退させるため、引数in_dataを256÷5(=51)で割ってから、範囲をはみ出した部分を調整している。その結果、in_dataが51未満の場合は1、51以上で102未満の場合は2を返し、さらにin_dataが大きくなると3や4を返す。in_dataが204以上の場合は5を返す。ちなみに、SAADCのマニュアルによると、A/D変換の結果は符号付き整数と定義されており、実際に試してみた範囲でも絶対値の小さい負の数((-1)など)になることがある。このため、A/D変換の変換結果を入れる変数も符号無し整数(UINT)ではなく符号付き整数(INT)にしておかないと、想定外の値に解釈されてしまう可能性がある。
scale_1to5の結果を使って点灯させたいLEDを制御するには、連載第7回のリスト4で使用したled_init、out_gpio_pin、out_row_gpio、set_row_gpioの関数をそのまま利用する(※L)。set_row_gpioを実行すると、引数rowで指定した行のLEDのみを点灯し、他の行のLEDは消灯する。したがって、scale_1to5から戻された1から5の値を引数としてset_row_gpioを呼び出せば、これだけで行方向のLEDの制御ができる。列方向のLEDの制御も同様に可能だが、連載第7回ではダイナミック点灯により複数のLEDを同時に点灯させていたので、列方向のLEDの制御関数は用意していなかった。今回点灯するLEDは一つのみなので、ダイナミック点灯にする必要はないが、列方向のLEDの制御を行う関数を追加する必要がある。このために追加した関数がout_col_gpioとset_col_gpioであり(※M)、行(ROW)ではなく列(COL)の方向に対して、out_row_gpioやset_row_gpioと同様の処理を行う。なお、連載第7回でも説明したように、LEDが点灯するのはROWが1でCOLが0の場合のみなので、set_col_gpioでGPIOに設定する値はset_row_gpioの逆になっている。
ここまでに説明した関数を使ったA/D変換のサンプルプログラムを、リスト1のusermainに示す(※N)。for文の無限ループの中で、analogRead2を使ったA/D変換を行い、その結果を文字によるコンソール出力とLEDマトリクスの点灯によって表示する。A/D変換の処理結果のうち、CH[0]の結果は配列変数saadc_data[0]に入るので、これをscale_1to5により1から5の値に変換してch0_1to5に格納し(※P)、set_col_gpioでLEDに表示する(※Q)。アナログ入力値が小さい場合には左方のCOL1側のLEDを点灯し、大きい場合には右方のCOL5側のLEDを点灯する。一方、CH[1]の結果は配列変数saadc_data[1]に入るので、これもscale_1to5により1から5の値に変換して同様の処理を行う。ただし、上下方向についてはLEDのROW1が上、ROW5が下なので、アナログ入力値が大きい場合に上方のLEDを点灯させるには、数字の大小関係を逆にする必要がある。そのため、scale_1to5の結果を6から引いた値をch1_1to5に格納し(※R)、scale_1to5の結果が1の場合に下方のROW5、5の場合に上方のROW1のLEDを点灯するように制御している。
このほか、tm_printfを使ってA/D変換の処理結果をコンソールに出力する(※S)。ここでは、動作状況を確認するために、起動からのミリ秒単位の経過時間や、A/D変換の処理終了をチェックしたEVENTS_ENDレジスタのポーリング回数adc_poll_cntも表示するようにした。for文の最後のtk_dly_tskにより、これらの処理を約100ミリ秒ごとに繰り返す。
/*------------------------------------------------------------------ * SAADCによるA/D変換の動作確認(µT-Kernel 3.0用) * * Copyright (C) 2022-2024 by T3 WG of TRON Forum *----------------------------------------------------------------*/ #include <tk/tkernel.h> #include <tm/tmonitor.h> //-------- SAADCのレジスタ定義(※A) ----------------------------------- // SAADCのレジスタ定義 #define SAADC(r) (SAADC_BASE + SAADC_##r) #define SAADC_BASE 0x40007000 #define SAADC_TASKS_START 0x000 #define SAADC_TASKS_SAMPLE 0x004 #define SAADC_TASKS_STOP 0x008 #define SAADC_EVENTS_END 0x104 #define SAADC_EVENTS_DONE 0x108 #define SAADC_STATUS 0x400 #define SAADC_ENABLE 0x500 #define SAADC_PSELP(n) (0x510 + (n) * 0x10) #define SAADC_PSELN(n) (0x514 + (n) * 0x10) #define SAADC_CONFIG(n) (0x518 + (n) * 0x10) #define SAADC_RESOLUTION 0x5F0 #define SAADC_RESULT_PTR 0x62C #define SAADC_RESULT_MAXCNT 0x630 ・ ・ ・ //-------- LEDの制御(連載第7回のリスト4と同じ) (※L)------------ // ROW1..ROW5のいずれか(rowで番号指定)に接続されたGPIOピンに // valを設定 LOCAL void out_row_gpio(UW row, UW val) { ・ ・ ・ } // rowで指定した行のみを点灯させるためのGPIOピンの設定 // ROW1..ROW5に接続されたGPIOピンのうち、 // rowで指定した1本のみを1に設定し、他は0に設定する LOCAL void set_row_gpio(UW row) { UW cnt; for(cnt = 1; cnt <= 5; cnt++){ out_row_gpio(cnt, ((cnt == row) ? 1 : 0)); // 指定の1本のみ1を設定 } } //-------- LEDの制御(連載第11回での追加分) (※M) --------- // COL1..COL5のいずれか(colで番号指定)に接続されたGPIOピンに // valを設定 LOCAL void out_col_gpio(UW col, UW val) { switch(col){ case 1: out_gpio_pin(0, 28, val); // GPIO P0.28(COL1に接続)にvalを設定 return; case 2: out_gpio_pin(0, 11, val); // GPIO P0.11(COL2に接続)にvalを設定 return; case 3: out_gpio_pin(0, 31, val); // GPIO P0.31(COL3に接続)にvalを設定 return; case 4: out_gpio_pin(1, 5, val); // GPIO P1.05(COL4に接続)にvalを設定 return; case 5: out_gpio_pin(0, 30, val); // GPIO P0.30(COL5に接続)にvalを設定 return; } } // colで指定した行のみを点灯させるためのGPIOピンの設定 // COL1..COL5に接続されたGPIOピンのうち、 // colで指定した1本のみを0に設定し、他は1に設定する LOCAL void set_col_gpio(UW col) { UW cnt; for(cnt = 1; cnt <= 5; cnt++){ out_col_gpio(cnt, ((cnt == col) ? 0 : 1)); // 指定の1本のみ0を設定 } } //-------- SAADCを使ったA/D変換 -------------------------------------- // AD変換の結果を入れるメモリ #define SAADC_DATACNT 2 static _H saadc_data[SAADC_DATACNT]; INT adc_poll_cnt; // A/D変換の終了までのポーリング回数 // 2チャンネルのアナログ入力値をデジタル値に変換(※B) LOCAL void analogRead2(INT ch0_ain, INT ch1_ain){ out_w(SAADC(PSELP(0)), ch0_ain); // CH[0]のP入力の設定(※C) out_w(SAADC(CONFIG(0)), 0); // CH[0]のCONFIGの設定 out_w(SAADC(PSELP(1)), ch1_ain); // CH[1]のP入力の設定(※C) out_w(SAADC(CONFIG(1)), 0); // CH[1]のCONFIGの設定 out_w(SAADC(RESULT_PTR), (UW)saadc_data); // 結果を入れるRAMアドレス(※D) out_w(SAADC(RESULT_MAXCNT), SAADC_DATACNT); // 結果を入れるRAMのデータ数 out_w(SAADC(ENABLE), 1); // SAADCのイネーブル(※E) out_w(SAADC(RESOLUTION), 0); // resolution=8bitの設定(※F) out_w(SAADC(EVENTS_END), 0); // 書込完了のフラグをクリア(※G) out_w(SAADC(TASKS_START), 1); // A/D変換の開始(※H) out_w(SAADC(TASKS_SAMPLE), 1); // サンプル入力 adc_poll_cnt = 0; for(;;){ // A/D変換の書込完了を待つループ if(in_w(SAADC(EVENTS_END))) // 書込完了のレジスタを確認(※J) return; adc_poll_cnt++; // ポーリング回数をプラス1 tk_dly_tsk(10); } } //------------------------------------------------------------------ LOCAL W cur_time(void) // ミリ秒単位の現在時刻の下位32ビットを返す { SYSTIM tim; tk_get_tim(&tim); // 現在時刻を取得 return(tim.lo); } LOCAL INT scale_1to5(INT in_data){ // 0..255の範囲のin_dataを1..5に変換(※K) INT out_data = in_data / (256 / 5) + 1; if(out_data < 1) out_data = 1; if(out_data > 5) out_data = 5; return(out_data); } //------------------------------------------------------------------ EXPORT void usermain( void ) // (※N) { W itime = cur_time(); // プログラム開始時刻 led_init(); // 無限ループでA/D変換を連続実行 for(;;){ analogRead2(1, 2); // ch0=AIN0(RING0),ch1=AIN1(RING1) で // A/D変換 // ch0:入力値小→左方のCOL1を点灯、大→右方のCOL5を点灯(※P) INT ch0_1to5 = scale_1to5(saadc_data[0]); // ch1:入力値小→下方のROW5、入力値大→上方のROW1を点灯、 // 上下反転(※R) INT ch1_1to5 = 6 - scale_1to5(saadc_data[1]); // A/D変換結果をコンソールに出力(※S) tm_printf("%6d: poll=%d, ch0=%4d (COL%d), ch1=%4d (ROW%d)\n", (cur_time() - itime), adc_poll_cnt, saadc_data[0], ch0_1to5, saadc_data[1], ch1_1to5); set_col_gpio(ch0_1to5); // ch0の入力値を示すCOL位置のLEDを // 点灯(※Q) set_row_gpio(ch1_1to5); // ch1の入力値を示すROW位置のLEDを // 点灯 tk_dly_tsk(100); } tk_slp_tsk(TMO_FEVR); } |
micro:bitのアナログ入力に使えるのは、Target MCUのAIN0からAIN7のピンである。analogRead2を使ったA/D変換のサンプルプログラムを実行したいのだが、何のアナログ入力を試せばよいだろうか。
回路図(*2) を見ると、Target MCUのAIN0はGPIOのP0.02と共用のピンであり、RING0に接続されている。RING0は、micro:bitのエッジコネクタに出ているリング状の端子のうち、一番左側の0と書かれた箇所である。同様に、AIN1はGPIOのP0.03と共用のピンであり、RING1に接続されている。RING1は、左から2番目の1と書かれたリング状の端子である。ここに何らかの外部回路を接続し、0Vから3.3Vの電圧をかければ、A/D変換の動作確認ができるはずだ。
このほか、回路図を見るとAIN3がMIC INに接続されており、micro:bitの内蔵マイクからのアナログ入力もできそうだ。しかし、マイクを使った場合には、音波からA/D変換を行うことになるため、簡単なプログラムで動作確認するのが難しい。これ以外に、micro:bitの内蔵のデバイスやセンサーから、直接アナログ入力が可能なものは見当たらない。
結局、micro:bitのA/D変換を試すには、エッジコネクタのRING0やRING1に何らかの電子回路を追加して、そこからアナログ電圧を入力する必要がある。電子工作が得意な人であれば自分で工夫すればよいが、そうでない読者もいるだろう。そこで、micro:bitのオプション品として売られている周辺機器を使ってみることにした。
micro:bit用の周辺機器は、教材用を中心に多くの種類が販売されている。そのうち、今回はイフティニー ストア(*3) というmicro:bit専門のウェブストアから、「ワールドオブモジュール センサーキット」(*4) という各種センサーモジュールの組み合わせキットを購入した。このキットには、micro:bitのエッジコネクタを挿し込める拡張ボードに加えて、この拡張ボードに接続可能なLED、ボタンスイッチ、各種センサーモジュールやサーボモーターが含まれており、micro:bit用のビジュアルプログラミング言語であるMakeCodeを使ってこれらの周辺機器を制御できる。さらに、各種の装置やロボットなどの物理的な形状を組み立てられるように、ブロックとよばれる組み立て用のパーツが約300個付属している。LED、各種センサー、モーターにmicro:bitまで含めたおもちゃの電子機器を、自分で簡単に何度も組み立てて遊べるのだ。ウェブから取得できるチュートリアルや組み立て用の説明書、技術情報なども充実しており、IoT時代の電子ブロックといったイメージの商品である。
このキットの中には、ロッカー(Rocker)(*5) (*6) とよばれるジョイスティック風の入力デバイスが含まれている。真ん中のボタンを上下左右に動かすことにより、動かした向きや角度に応じてX軸とY軸のアナログ値を出力するデバイスである。付属のケーブルと拡張ボードを使って、ウェブサイト(図4)で紹介されているとおりに組み立てると、X軸とY軸のアナログ値がmicro:bitのエッジコネクタのP0(=RING0)とP1(=RING1)に入力される。つまり、ロッカーのボタンを上下左右に動かすと、RING0とRING1への入力電圧がGNDから3.3Vの範囲で変化する。この動作をリスト1のプログラムを使って確認する。
この実験の様子を図5に、コンソール出力の一部をリスト2に示す。リスト2の各行の左端の数字では起動後の時刻(ミリ秒単位)を示しているが、この時刻が670の付近でロッカーのボタンを上に動かすと、コンソール出力に表示されるch1の値(Y軸から入力したAIN1)が大きくなるとともに、LEDの点灯位置が上に移動している。また、時刻が2100の付近でボタンを左に動かすと、ch0の値(X軸から入力したAIN0)が小さくなるとともに、LEDの点灯位置が左に移動している。左右方向(X軸)と上下方向(Y軸)の操作は独立して行うことができ、各方向のアナログ値に応じてch0とch1の値やLEDの点灯位置が変化する。これで、micro:bitのSAADCを使ったA/D変換の動作が確認できた。
図4 ロッカーモジュールとmicro:bitの接続
(*6の「ハードウェアの接続」より引用)
図5 ロッカー(ジョイスティック)を使ったA/D変換の
実験の様子
microT-Kernel Version 3.00 20: poll=1, ch0= 116 (COL3), ch1= 119 (ROW3) 150: poll=1, ch0= 117 (COL3), ch1= 118 (ROW3) 280: poll=1, ch0= 117 (COL3), ch1= 118 (ROW3) 410: poll=1, ch0= 117 (COL3), ch1= 119 (ROW3) 540: poll=1, ch0= 116 (COL3), ch1= 118 (ROW3) 670: poll=1, ch0= 116 (COL3), ch1= 200 (ROW2) ←ボタンを上に動かす 800: poll=1, ch0= 116 (COL3), ch1= 234 (ROW1) ・ ・ ・ 1840: poll=1, ch0= 116 (COL3), ch1= 200 (ROW2) 1970: poll=1, ch0= 116 (COL3), ch1= 234 (ROW1) 2100: poll=1, ch0= 58 (COL2), ch1= 235 (ROW1) ←ボタンを左に動かす 2230: poll=1, ch0= -2 (COL1), ch1= 159 (ROW2) 2360: poll=1, ch0= -1 (COL1), ch1= 118 (ROW3) ←ボタンを下に動かす 2490: poll=1, ch0= -1 (COL1), ch1= 47 (ROW5) ←ボタンを下に動かす 2620: poll=1, ch0= 103 (COL3), ch1= -1 (ROW5) 2750: poll=1, ch0= 117 (COL3), ch1= -1 (ROW5) 2880: poll=1, ch0= 116 (COL3), ch1= -1 (ROW5) 3010: poll=1, ch0= 168 (COL4), ch1= -1 (ROW5) ←ボタンを右に動かす 3140: poll=1, ch0= 233 (COL5), ch1= 56 (ROW4) 3270: poll=1, ch0= 234 (COL5), ch1= 118 (ROW3) |
「ワールドオブモジュール センサーキット」には、環境光センサーモジュールという名称の光センサー(照度センサー)も含まれている(*7) 。CdS(硫化カドミウム)という物質の性質を利用して、光の強さ(照度)に応じたアナログ値を出力するデバイスだ。ロッカーとまったく同じように、ケーブルと拡張ボードを経由してmicro:bitに接続でき、照度を示すアナログ値がmicro:bitのエッジコネクタのP0(=RING0)に入力される。動作確認にはリスト1のプログラムがそのまま使えるので、早速試してみよう。センサーの前を手で遮るなどして暗くすると、コンソール出力に表示されるch0の値(光センサーから入力したAIN0)が大きくなるとともに、LEDの点灯位置が右に移動する。手を離してセンサーに光が当たるようにすると、ch0の値が小さくなるとともに、LEDの点灯位置が左に移動する。P1(=RING1)は使用していないため、ch1の値は200以上であまり変化せず、LEDは一番上の行(ROW1)のままである。この実験の様子を図6に、コンソール出力の一部をリスト3に示す。
micro:bit用のオプションの周辺機器が入手できず、自分で電子工作をするのも苦手という場合は、やや乱暴ではあるが次のような方法もある。リスト1のプログラムを動かした状態で、micro:bitのエッジコネクタ左端のRING0の部分を、スプーンなどの金属類で少し触ってみよう。導電性の物体を経由してRING0が人体に触れることにより、RING0の電位が変化するため、コンソール出力に表示されるch0の数字やLEDの点灯位置が変化するはずだ。指などで直接触っても同じように確認できるが、エッジコネクタを直接手で触れるのは、電子回路の扱いとして好ましいことではない。また、静電気などの条件によっては、稼働中の急激な電位差によりハードウェアが故障する可能性もなくはない。とはいえ、小学生も使うmicro:bitにはある程度の保護回路も入っており、実際問題として、ちょっと触った程度で壊れる可能性は考えにくい。自己責任で試してほしい。
* * *
今回ご紹介したmicro:bitの拡張ボードでは、ロッカーとの接続のために4ピンのケーブルと2mmピッチ(間隔)のコネクタを使っている。このコネクタはGroveという名称で標準化されており、これに接続可能なセンサーモジュール類が多数市販されているので、温度センサーや水分センサーなど、他のセンサーを接続して試すことも可能である。ArduinoやRaspberry Piといった他の著名なボードでも、Groveの使える拡張ボードが用意されている。
ただし、Groveの信号線のインタフェース規格としては、今回使ったロッカーや光センサーのようなアナログ入力に決まっているわけではない。I2C(Inter-Integrated Circuit)やUART(シリアル通信)といったデジタル信号を使って入力する場合もあるし、LEDなどを接続するために信号線から出力する場合もある。今回のA/D変換のプログラムはアナログ入力用なので、その仕様に合ったセンサーモジュールを使う必要がある。仕様をよく確認したうえで、他のセンサー類との接続も試してみることができれば面白いだろう。
microT-Kernel Version 3.00 20: poll=1, ch0= 57 (COL2), ch1= 214 (ROW1) 150: poll=1, ch0= 57 (COL2), ch1= 218 (ROW1) 280: poll=1, ch0= 57 (COL2), ch1= 216 (ROW1) 410: poll=1, ch0= 57 (COL2), ch1= 217 (ROW1) 540: poll=1, ch0= 90 (COL2), ch1= 219 (ROW1) 670: poll=1, ch0= 131 (COL3), ch1= 224 (ROW1) ←手をかざして暗くする 800: poll=1, ch0= 168 (COL4), ch1= 223 (ROW1) 930: poll=1, ch0= 185 (COL4), ch1= 237 (ROW1) 1060: poll=1, ch0= 218 (COL5), ch1= 226 (ROW1) ←さらに暗くする 1190: poll=1, ch0= 220 (COL5), ch1= 239 (ROW1) 1320: poll=1, ch0= 220 (COL5), ch1= 226 (ROW1) 1450: poll=1, ch0= 205 (COL5), ch1= 235 (ROW1) 1580: poll=1, ch0= 112 (COL3), ch1= 221 (ROW1) ←手を離して明るくする 1710: poll=1, ch0= 58 (COL2), ch1= 218 (ROW1) 1840: poll=1, ch0= 57 (COL2), ch1= 216 (ROW1) 1970: poll=1, ch0= 57 (COL2), ch1= 217 (ROW1) |
■ 本稿で説明したプログラムのソースコードのダウンロード |
---|
https://www.personal-media.co.jp/book/tw/tw_index/383.html ソースコードをご利用になる前に、必ず上記ページ掲載の「ご利用条件およびご利用上のご注意」をお読みください。 |