前回から引き続きのテーマですので、早速、詳細設計から始めましょう。
今回は、170穴ミニブレッドボードへの実装で縛ってあるので、まず、回路がミニブレッドボードに載るのか?という観点で回路を設計しました。従って、最初に完成したのは以下に示す配置配線(レイアウト)図でした。
以下、レイアウト図を検討する段階で決めて行った事柄をまとめておきます。
ご覧の様にカツカツですが、何とか押し込むことが出来ました。
通常の設計とは順番が逆となりますが、レイアウト図から起こした回路図は以下となります。
328duinoにプログラムを書き込むときには、FTDI(USB⇔UART変換モジュール、下図)等を使ってシリアル接続(DTR,RXI,TXO)します。Arduino as ISPなどを使って書き込む場合には、SPI接続(RES,SCK,MISO,MOSI)して使います。
328duinoは第二形態なので、「328duino (RC 8MHz)」のブートプログラムを書き込んでおいてください。
なお、電池ボックスなどから給電して独立動作する事を目的としていますので、328duinoへ供給する電源電圧は電池の種別や本数などによって増減します。FTDIやArduino as ISPを接続して書き込む時は、L/Sが要らない様に3.3Vなり5Vなり、相手のI/Oレベルに合わせて給電してください。
肝心なIR LEDですが、「#8-8 反射型赤外線センサ」で試してみた通り、反射型赤外線センサTCRT5000L(下図)のエミッタ側だけを取り出して使います(前回検討の通り)。
※もちろん、普通に市販されているIR LED(波長940nm)を使っても良いです。筆者手元にはTCRT5000Lしか無かったので、流用しました。
レイアウト図に従ってブレッドボード上に構築した赤外線リモコン化328duinoは以下の様になりました。
最初のレイアウト図と見比べてみてください。
過去に作った赤外線リモコン送信プログラム(test_ir_send.ino)は、38kHzデューティ1/3のキャリアPWM発生に16ビットタイマ(TC1)を使っていました。詳細設計(回路)の通り、キャリアPWM発生に使うのは8ビットタイマ(TC2)となるので、修正する必要があります。
単に、38kHzデューティ1/3のPWMを出力すれば良いだけですので、以下の初期設定でTC2への変更は完了します。
// TC2 setting for 38kHz 1/3 duty PWM
// OC2B pwm output, pwm phase correct OCR2A mode 5, fcpu/1
TCCR2A=0x20 + 0x01;
TCCR2B=0x08 + 0x01;
OCR2A=F_CPU/2/38000 - 1; // 38kHz
OCR2B=0; // duty, 0=stop pwm
注意点としては、16ビット → 8ビットタイマになったことで、周期を決めるOCR2Aレジスタの値が万一、8ビットの範囲を超えてしまうと、カウントクロック値などを変更する必要があるので、以下の様なコードを挟んでおいて、コンパイルエラーに落とすようにしておきます。
#if F_CPU/2/38000 - 1>255
#error "F_CPU too fast. You need change count clock of TC2."
#endif
タイマ変更以外は、過去に作ったプログラム(test_ir_send.ino)からの流用で行けます。
U/Iとして追加するタクトスイッチのハンドリングについては、新たに作り込む必要があります。
タクトスイッチの配置は、ミニブレッドボードの空き地に当てはめただけなので、328duino側の端子機能とは無関係に使用すべき端子が決まっています。
スイッチの検出には割り込みを使うのが便利なのですが、コアライブラリのattachInterrupt()で使える割り込みは2本しかありません。
丁度、タクトスイッチの一方には、attachInterrupt()が対応可能なD2(PD2/INT0)端子が割り当たっていたのですが、2個目のタクトスイッチには、割り込み信号INTxの割り当てが無いD16(PC2/PCINT10)が割り当たってしまいました。
そこで今回は、何故かわかりませんがコアライブラリでサポートの無い、端子変化割り込みPCINTを使ってみようと思います。
こちらのUNO端子表を眺めてみると、UNOのほとんどの端子にPCINTxと言う端子機能が割り当たっていることが解ります。これらすべて、端子変化割り込みに設定することが出来るので、UNOの場合、ほとんどの端子で外部から割り込みをかけることが可能となっているのです。
ただし、attachInterrupt()と異なって、立ち上がりエッジ、立ち下がりエッジ、両エッジ、……等のイベントを選択することが出来ず、両エッジ(端子変化)割り込みだけが実現可能です。
PCINT割り込みはコアライブラリにサポートが無いので、自分でATmega328のI/Oレジスタを直接叩いて設定します。
制御に使うI/Oレジスタは以下表の通りです。
レジスタ名 | アドレス | 備考 |
PCIFR | 0x003b | 割り込み状態(フラグ)レジスタ |
PCICR | 0x0068 | 割り込み制御(イネーブル)レジスタ |
PCMSK0 | 0x006b | 割り込みマスクレジスタ:PCINT0〜7 |
PCMSK1 | 0x006c | 割り込みマスクレジスタ:PCINT8〜14 |
PCMSK2 | 0x006d | 割り込みマスクレジスタ:PCINT16〜24 |
今回の割り込み端子はD2(PCINT18、スイッチSW1)とD16(PCINT10、スイッチSW2)なので、setup()関数内の初期設定は以下の様になります。
// interrupt
// pin_SW1=PCINT18, pin_SW2=PCINT10
PCMSK2=0x04; // PCINT18 enable
PCMSK1=0x04; // PCINT10 enable
PCIFR=0x07; // interrupt flag clear
PCICR=0x06; // PCINT2 & PCINT1 enable
幸いなことに、割り込み要因もPCINT1_vectとPCINT2_vectに分かれているので、各ISR(Interrupt Service Routine)内で、各タクトスイッチの処理を分離して実行できます。
ISR (PCINT1_vect)
{
// PCINT1 interrupt service routine
:
: (省略) スイッチSW2(PCINT10)用検出処理
:
}
ISR (PCINT2_vect)
{
// PCINT2 interrupt service routine
:
: (省略) スイッチSW1(PCINT18)用検出処理
:
}
そうか💡。もし、2つのPCINTx割り込みを使う時、割り込み要因PCINTx_vectが一致してしまうと、2端子の端子変化割り込みは同じISRに分岐してしまうのだけど、その時、どちらのPCINT割り込みが起きたのか?知る手段が無いのですね。
その場合はだいぶプログラムに制約が出来て、使い難くなります。恐らくそれがコアライブラリでサポートが無い理由なのでしょう。……今回は、たまたま割り込み要因が異なる2端子だったので、うまく活用できたわけです。良かった。
前の節で決めた様に、2個のボタンと4つのモードで計8種類の赤外線リモコン信号を送信できるようなプログラムにします。
「#4-3 赤外線リモコン(送信)」の記事における、回路とプログラムを使って、エミュレートしたいリモコンの赤外線リモコン信号を取り込むと、シリアルモニタに以下の様なC言語のコードが表示されます。
実験サンプル:【 】内はシリアルモニタからの送信データ
【captc,200000】
CAPT
PROGMEM const uint16 irsd_data[ ]=
{
2242, 1127, 140, 144, 139, 138, 146, 138,
137, 148, 138, 137, 146, 138, 137, 146,
140, 139, 144, 428, 133, 428, 131, 430,
138, 429, 132, 427, 132, 429, 140, 427,
132, 429, 132, 427, 140, 138, 137, 430,
139, 138, 138, 146, 139, 144, 140, 427,
132, 139, 144, 140, 138, 429, 138, 137,
140, 430, 137, 428, 133, 427, 132, 152,
126, 435, 132, 0
};
END
この配列記述部分を丸ごとコピーして、ir_remocon.inoに貼り付けます。この時、配列変数名は解り易くユニークな名前に変えてください。変更した配列変数名を以下の様に配列ir_patn[8]に登録したら終了です。
const uint16 *ir_patn[8]=
{
// mode 1
ir_sel_b1, // hdmi b1
ir_sel_b4, // hdmi b4
// mode 2
ir_rgz_pow, // regza power
ir_fan_pow, // fan power
// mode 3
ir_lgt_on, // light on
ir_lgt_off, // light off
// mode 4
NULL,
NULL
};
ir_patn[8]は8個の赤外線リモコン信号を保持する配列変数で、上から順に、
の、8つのデータを保持しています。各モードの各ボタン押下で送信すべき赤外線リモコン信号はこの配列で指定されます。赤外線リモコン信号が登録されない場合(NULL)、そのモードでボタンを押しても、何もしません。必要な信号だけを設定することが出来ます。
まず、完成したプログラムをリンクしておきます。
完成プログラム:ir_remocon.ino
デバッグ用にシリアルモニタが活きているのですが、実用上は特に使いません。ミニブレッドボード上に構築した2つのボタンを使って操作します。以下、実験中の様子です。
実験サンプル:ブレッドボード上の2ボタン(SW1,SW2)で操作
LED: 起動後、モード1を表す青色が点灯している。
【SW1を長押し】
LED: モード選択状態に入って、RGB LEDが消灯する。
【SW2を短押し】
LED: モード2に遷移して、赤色点灯に変わる。
ちゃんと、モードが変わって、LEDの表示色が変化するのを確かめてみました。その他、モード遷移とLED表示色の関係は前回表にまとめてあります。
なお、ATmega328の内蔵プルアップ抵抗で光らせたRGB LEDですが、予想通り暗いです、赤と青は。それに比べて緑の何と明るいことか!その性で、緑単色と、緑+赤=黄色、緑+青=水色、緑+赤+青=白色が全て同じ緑にしか見えません(1^−^;)。緑と他の色を組み合わせるのは、RGB LEDでは無理みたいです。
もし、どうしてもR,G,Bの組み合わせで8状態を表示したい場合は、RGB LEDではなくて、単体のLED 3本を接続して表示した方が良いと思います。
実験サンプル:ブレッドボード上の2ボタンで操作
【SW1を短押し】
IR送信: モード2のSW1に割り当てた赤外線リモコン信号が送信されるかどうか試してみました。
IR LEDは、肉眼では光っているかどうか見えないので、1)可視光LEDを繋いでデバッグする、2)IR LEDに差し替えたら、スマホなどCMOSセンサカメラを通してIR発光を確認する、等の手順でデバッグします。
以下雑感です。
以上の様に、基本的にはモバイルバッテリや乾電池ボックスからの給電を前提とするアプリケーションなのだけど、さすがにeneloop(1.2V)×2本だと、表示用LEDが厳しいです。IR LEDはそこそこ飛んでいるみたいなので、もったいない。
実用的には、eneloop×3〜4本(※)または、5V給電可能なモバイルバッテリなどを使うのが正解みたいです。
※アルカリ乾電池(1.5V)を使う場合は、×3本≒4.5Vですね。
今回のお買い物は、以下の通りです。
赤外線リモコン化328duinoを実用化するに当たっては、以前購入したATmega328P-PU(単体チップ)をリモコンアプリケーション専用として使う必要があるため、普段使い用に2個ほど追加購入しました。タクトスイッチは、ブレッドボードやピンソケットで何かと使い易い、2端子で足が長めのタイプを購入しておきました(下図)。
更に、電源スイッチとして使用可能なスライドスイッチ(下図)も購入して準備万端で臨みました。
写真でわかるかどうか、背景の方眼が約1cm□ですので、とても小さいです。ブレッドボードの連続する3穴に挿さります(ピンピッチ=2.54mm)。
今回お世話になったショップはaitendoと言うお店でした。最低購入金額が1,000円以上、1回の配送料が490円と、正直ちょっと使い難いお店なのですが、色々と細かい部品を購入するのに、品ぞろえがニーズに合っていたのでこのお店を選びました。
1個20円とか言う細かい部品を売ってくれているのですが、そういう部品を色々組み合わせて1,000円超えるのは結構大変でした。ちなみに、今回購入した部品の中で一番高額だったのは上に挙げたATmega328P-PU、253円(税込み)でしたとさ(1^^;)っ。
わっ!一番高額だったのは配送料490円だわ。他のショップみたいに、メール便とかで200円くらいにしてくれると、もう少し使い易くなるのだけど……。
HDMIセレクタのリモコンが怪しくなって以来、「作ろう、作ろう」と思いながら放置していました。ようやく必要な部品類を揃えて、ここまでこぎ着けました。ミニブレッドボードに載りきるくらいな回路なので、将来的には5×7[cm]程度の万能基板に実装することを考えています。暫く試作(ブレッドボード)のまま使ってみて、対策点などが見つかればフィードバックしようと思います。
なにより、リモコンがまだ何とか反応する間(完全に壊れる前)に、赤外線リモコン信号を取り込みしておく、これが重要です。