今回の講義の目的は、マイコンの使い方を理解することと、デジタルピンの使い方をマスターすることです。
- 3.1 マイクロコントローラの基礎
- 3.2 mbed OS
- 3.3 実習
3.1 マイクロコントローラの基礎
まず、マイコンについて基本的なことを説明しておきます。マイコンは、一般に以下の写真のような見た目をしています。
写真のマイコンは、細長い形をしていて両側に足が出ていますが、形状はいろいろなものが存在します。多くの場合、黒いプラスティックのパッケージになっています。このプラスティックのパッケージの中に、マイコン本体が封入されています。よって、マイコン本体は、もっと小さなものです。
マイコンからは、ゲジゲジの足のようなものが出ています。これをピン(pin)、あるいは端子と呼びます。マイコンは、このピンを通じて電源供給を受けたり、他の部品と連携したりします。
micro:bitのマイコンボードを見ると、5mm四方ほどの黒い正方形のマイコンが実装されているのがわかります。
マイコンからは小さなピンが出ていますが、ものすごく小さくてプロトタイプを作る際には使いづらいことが多いです。そのため、マイコンボードは、ピンから配線を延ばして回路を作りやすい形状にしているのです。実際の製品にするときには、マイコンボードは使わずに、よりコンパクトな基盤にすることが多いです。
マイコンボード
以下のリンクをみてください。これは、mbedの公式Webサイト内でmicro:bitボードの紹介をしているページです。
https://os.mbed.com/platforms/Microbit/
このWebページはとても重要ですから、からなずブックマークなどをして、すぐにアクセスできるようにしておきましょう。特に以下の図が重要です。
この図は、micro:bitのピン配置図です。どの場所に、どういう機能のピンがあるのかを説明した地図のようなものです。幾つか重要なピンに関して説明しておきます。
- +3v3: このピンには3.3Vの電圧がかかっています。他の電子部品に3.3Vを供給したい時には、ここを電源とします。
- GND: グランド、つまりマイナスです。
- P0からP20: 入出力ピン。いくつかのピンは特別な役割が割り当てられています。
- ANALOG IN: アナログ入力に使えるピン。
Nordic nRF51822
ここで、micro:bitついて、もう少し説明しておきます。このマイコンボードにはNordic Semiconductorという企業のnRF51822というマイコンが使われています。このマイコンは、ARM社が設計したCortex-M0というアーキテクチャがベースとなっていて、以下のような特徴があります。
- 32bitマイコン
- フラッシュメモリ: 256kB
- SRAM: 16kB
- 動作クロック: 16MHz
フラッシュメモリは、プログラムを格納する場所です。SRAMは、プログラム中で使用する配列や変数を格納する場所です。
SRAMは16kBしかありません。皆さんが使っているMacBook Airには4GBか8GBのメモリが搭載されています。あまりにも桁違いの大きさですね。
16kBがどのくらいの大きさなのかというと、日本語の文字で8000文字分です。英字アルファベットならば16000文字分、16bitの整数だと8000個分ということになります。ちなみに、mbedのint型は32bitですから、4000個分しか入りません。つまり4000個以上の整数が格納できる配列は宣言できない、ということになります。実際には3000個分くらいが限界でしょう。
mbedには、他にも様々なハードウェアプラットフォームがあり、micro:bitはコンパクトな方です。他のハードウェアには、もっと多くのRAMが搭載されていたりします。
nRF51822に関して、もっと詳細なデータが知りたければ、データシート(datasheet)というものを参照します。nRF51822のデータシートは、以下の場所にあります。ただし、マイコンのデータシートを読むのは難しいので、当面は無視しておいて構いません。
https://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf
デジタル信号・アナログ信号
電気の世界における信号とは、時間とともに変化する電圧によって情報を表すことです。デジタル信号とは、2つの電圧によって0と1を表現したもので、例えば、0Vが0で3.3Vが1のように表現します。以下の図では、0VとxVで説明しています。
実際の電子回路では、0を1V未満、1を2.5V以上のように、ある境界(閾値)を定めて判断しています。しかし、今は、そこまで意識する必要はありません。
アナログ信号の場合には、電圧の大きさで何かの大きさや量を表現します。例えば、部屋の明るさの場合には、光量を知りたいですよね。陽が昇れば徐々に明るくなり、陽が暮れれば徐々に暗くなります。夜中に電気をつければ突然明るくなったりもします。部屋の明るさを光センサを使って連続的に計測して電圧の変化に変換したもの、それがアナログ信号です。
ピン
micro:bitでは、nRF51822というマイコンのピンをより使いやすいエッジコネクタという形に引き出しています。
マイコンのピンとエッジコネクタの端子は厳密に言えば違うものですが、ここではそれを区別せずに同一視することにします。ピンの名前は、mbedの公式Webサイトにあるピン配置図に基づくものとします。
この図をみると、ピンにはP0からP20までと+3V3、GNDという名前が付けられているのがわかります。+3V3やGNDについては既に説明しました。
p12という名前のピンがあります。このピンには”Reserved accessibility”という注釈が付いていますが、使ってはいけなものと考えてください。また、P17とP18という名前がありませんね。これも使えません。
P0からP20のピンのうち、P0からP11、P13からP16、P19からP20は汎用入出力ピン(GPIO)と呼ばれています。これらのピンはプログラムの設定によって、デジタル入出力およびアナログ入力、PWM出力などに使うことができます。ただし、P19とP20は少し特別な用途に使用されます。DragonTail上でもこれらはSCL、SDAと印字されていて通常のGIPOとは区別されているのがわかります。よって、GPIOとして普通に使うことができるのは、P0からP11およびP13からP16までと考えておいた方が良いです。
ここではデジタル入出力の説明をします。デジタル信号の入出力に使うピンのことをデジタルピンと呼びます。GPIOは全てデジタルピンとして使うことができます。デジタルピンは、プログラムでデジタルピンにかかる電圧を0Vにするのか3.3Vにするのかを決められます。入力として使う場合には、デジタルピンにかかっている電圧によって0か1かの値を得ることができます。
micro:bitには小さなLEDが25個付いています。これをLEDマトリックスと呼んだりします。ピン配置図をみると、P3、P4、P6、P7、P9、P10がこのLEDマトリックスに接続されていることがわかります。これらのピンはLEDに繋がっていますから、当然抵抗もマイコンボード上でつながっています。この抵抗の値は220Ωです。よって、これらのピンを使用する場合には、回路上に既に220Ωの抵抗が入っていることを考慮しなければいけません。
GPIOをデジタル入出力に使う場合、P0、P1、P2、P5、P8、P11、P13、P14、P15、P16を優先的に使うのが良いでしょう。
デジタル出力(Digital Output)
デジタルピンから出力してみましょう。以下の図のような回路を作ったとしましょう。
実際の回路の作成例を示しておきます。
これは以下のように作成しても同じことになります。
この回路は、P11→定電流ダイオード→LED→GNDのようにつながっています。定電流ダイオードは抵抗と置き換えても良いです。P11に電圧をかけると、LEDが光ります。P11に電圧をかけるには、以下のようなプログラムを作成してmicro:bitに転送します。
1 2 3 4 5 6 7 8 |
#include "mbed.h" DigitalOut myled(P11); int main() { myled.write(1); } |
DigitalOut myled(P11);
は、P11をデジタル出力として使うという宣言です。DigitalOutはクラス名で、myledはインスタンス名です。myled.write(1);
は、P11をオンにする、つまり電圧をかけるという意味です。この時、P11には、3.3Vの電圧がかかります。myled = 1;
と書くと、myled.write(1);
と同じことが起こります。これは、DigitalOutクラスでそのように定義されているからです。
1 2 3 4 5 6 7 8 |
#include "mbed.h" DigitalOut myled(P11); int main() { myled = 1; } |
それでは、今度はLEDを点滅させてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include "mbed.h" DigitalOut myled(P11); int main() { while(1) { myled = 1; wait(0.5); myled = 0; wait(0.5); } } |
while文の条件式は1
なので常に成り立ちます。このようなループを無限ループと呼びます。組込みソフトウェアでは、一番外側にある無限ループを特にメインループと呼びます。
myled = 0;
は、P10をオフにする、つまり0Vにするという意味です。当然myled .write(0);
と書いても同じです。
wait(0.5);
は、0.5秒間待つという意味です。
つまり、このメインループでは、P10をオンにして0.5秒待ち、P10をオフにして0.5秒待つという処理を無限に繰り返しているわけです。
ソース電流・シンク電流
先ほどの例では、デジタルピンをオン、すなわち3.3VにするとLEDが点灯しました。これを、ソース電流(source current)を使った接続と言います。LEDを点灯させるには、もう1つ方法があります。
こちらは、シンク電流(sink current)を使った接続と言います。電流の流れる向きに注意してください。この回路は、3.3V→定電流ダイオード→LED→P11という向きに電気が流れます。定電流ダイオードは抵抗と置き換えても良いです。
LEDを点灯させるには、以下のようなプログラムを書きます。
1 2 3 4 5 6 7 8 |
#include "mbed.h" DigitalOut myled(P11); int main() { myled = 0; } |
シンク電流を使う場合には、デジタルピンをオフにするとLEDが点灯します。消灯するときには、オンにします。これはソース電流の時とは逆ですね。
LEDと電流
LEDの使い方は、意外と簡単だと思いませんか?しかし、それは、LEDの数が少ない場合です。複数のLEDを光らせるのは、結構難しいことなのです。マイコンのピンは、ソース電流にしてもシンク電流にしても、流せる電流の最大値が決まっています。この講義では、以下の制限を設けることにします。これを超えてLEDを繋いだ場合には、マイコンが壊れる可能性があります。
- 1つのデジタルピンで制御できるLEDは1つだけ(5mA)
- マイコン全体に接続できるLEDは10個まで(合計50mA)
- 同時に点灯できるLEDは3個まで(合計15mA)
※LEDを10個接続していてもONにできるのはその内の3個まで
10個以上のLEDを制御したい場合には、LED用の外部電源(電池など)を用意する必要があります。これに関しては、途端に難しくなりますので、別の機会に説明します。
これらの制約に関してより詳しいことが知りたい場合には、以下のURLを参照してください。
https://tech.microbit.org/hardware/edgeconnector/
3.2 mbed OS
mbed OSは、ライブラリです。APIの集合と言っても良いでしょう。mbed OSには、mbed OS 2とmbed OS 5という2つの大きなカテゴリーがあります。mbedのボードによってどちらのOSをサポートしているかが違います。micro:bitはmbed OS 2しかサポートしていないので、この講義ではmbed OS 2を使います。
APIリファランス
APIリファランスは、mbedの公式Webページトップの”Docs”というメニューの下にある”Mbed OS”にあります。
この資料はMbed OS 5用になっています。Mbed OS 2はMbed OS 5に含まれるので、説明としては網羅されているのですが、Mbed OS 5でしか使えないAPIの説明もあるので注意してください。
APIはいくつかのカテゴリに分かれて説明されています。まずは、Drivers APIsを見てください。ここに挙げられているものは、全てMbed OS 2で使えるAPIです。
ここに今回使っているDigitalOutの説明もあります。
この講義で扱う主なクラスを挙げておきますので、上記の資料からこれらのクラスの説明を探しておいてください。
- DigitalOut
- DigitalIn
- AnalogIn
- PwmOut
- InterruptIn
- Serial
- Timer
その他のライブラリ
mbed OSに含まれるライブラリは、原則すべてのmbedプラットフォームで使用できるAPIです。一方、特殊なデバイスを使うためのライブラリであったり、プラットフォーム依存のライブラリ等は、利用者が協力して整備する仕組みになっています。
以下のWebページに、多くの人たちの成果が公表されています。これらのライブラリやコードは、Apache License 2.0に基づくオープンソースとなっています。
ただし、これらのライブラリは、プラットフォーム依存であったり、不完全であったりしますから、素直に動かないものも多いです。不完全なライブラリを修正した場合には、これを新たに公開し、mbedの活動のために役立てることが理想です。
3.3 実習
実験1. オームの法則の復習1
以下のような回路を考えます。
この時R1とR2にかかる電圧は、それぞれどのくらいでしょうか?まずは、計算によって予想をしてみましょう。
抵抗器が直列に配置されていることに注目しましょう。このように抵抗器が直列に繋がれている場合には、Rの抵抗値はR1の抵抗値とR2の抵抗値の和になるのでしたね。よって、R = R1 + R2 = 2000Ωということになります。今、電圧(E)が3.3V、抵抗(R)が2000Ωであることがわかっているので、この回路を流れる電流(I)はオームの法則を使って計算できます。
I = E / R = 3.3V / 2000Ω = 0.00165A
およそ1.7mAということになります。
では、R1にかかる電圧を計算しましょう。直列回路の場合、回路のどの部分にも同じ電流が流れています。よって、R1にも0.00165A流れているわけです。ということは、R1にかかる電圧はオームの法則を使って以下のように計算できますね。
E = I x R = 0.00165A x 1000Ω = 1.65V
R1とR2は両方とも1KΩですから、R2にかかる電圧も1.65Vになります。これは、抵抗の比を考えても同じことです。R1とR2にはRにかかる電圧の1/2ずつがかかります。これはとても直感的に納得できますね。
それでは、実際にRとR1、R2にかかる電圧をマルチメータで測ってください。おそらくRにかかる電圧は3.3Vよりも少し低くなっているでしょう。
実験2. オームの法則の復習2
以下の回路について、実験1で行ったのと同じように予想して、その後実際にR、R1、R2にかかる電圧をマルチメータで測りましょう。マルチメータで測定する前に、どのような値になるかを計算によって求めてください。
※図ではR1は300Ωになっていますが、実際に実験する際には330Ωを使ってください。
実験3. ソース電流を使う
ソース電流を使って、1つのLEDを点滅させてみましょう。「点滅」とは光ったり消えたりを繰り返すという意味です。点滅間隔は自由に決めて色々と変えて見ましょう。定電流ダイオードを使います。
実験4. シンク電流を使う
シンク電流を使って、1つのLEDを点滅させてみましょう。3.3Vピンを電源とし、定電流ダイオードを使います。
実験5. 点灯パターン
実験4の回路をそのまま使います。整数型の変数xに適当な整数を代入しておきます。この整数を2進数で表現した際の下から3ビット目の値を使ってLEDを点灯させたり、消灯させたりします。3ビット目が1の場合には点灯、0の場合には消灯とします。
x = 10の場合を考えてみましょう。10を2進表現すると1010です。1010が2進表現であることを明示するために0b1010と書くことにしましょう。下から3ビットめは0になります。よって、LEDは消灯したままということになります。x = 12の場合は、0b1100ですから点灯させます。
xのnビット目を取り出すためのプログラムは、いくつかの書き方ができます。一番簡単で直感的なのは、除算と剰余演算を使うことでしょう。xを2で割った余りは、下から1ビット目の値です。0b1010の下から3ビット目を得たければ、2で3回割って余りを求めれば良いです。
0b1010を2で割ると0b101になります。10進で考えると10を2で割ると5になるということですね。0b101を2で割ると0b10となります。しつこいようですが、10進で考えると5を2で割ると2になるということです。求めるべき答えは0b10の下から1ビット目ですから、0b10を2で割った余りを求めて0を得ます。
時間が余ったら、ビット演算を使った方法も試してみましょう。恐ろしいことに、たったの1行で判定できてしまいます。
実験6. 3つのLEDを光らせる
3つのデジタルピンを使って、3つのLEDを光らせてみましょう。ソース電流でもシンク電流でも良いです。LEDは、できるだけまっすぐに並べて配置してください。どのデジタルピンを使うかは各自で決めてください。
ソースコードには、以下のような配列の宣言が必要です。
1 2 |
DigitalOut leds[NUM_OF_LEDS] = { P15, P14, P13 }; |
実験7. LEDを使って0から7までの数を表現する
LEDを3つ使うと、点灯パターンにより8状態が表せますね。これを使って、0から7までの数を順にLEDで表示してください。7まで表示し終わったら、また0から表示していきます。
コードの書き方は色々考えられますが、できるだけコードが短くなるように工夫してください。
— by 石井 健太郎、沼 晃介、飯田 周作 専修大学ネットワーク情報学部