入門その4「PICでLEDをフルカラーで光らせる」春だからはじめる工作教室(2/2 ページ)

» 2005年05月19日 17時01分 公開
[小林哲雄,ITmedia]
前のページへ 1|2       

フルカラーなんだから色もつけたい

 せっかく動作用の回路にフルカラーのLEDを実装しているのに、今回の練習で使っているプログラムは、やっていることはRGBすべてを点灯するか消灯する、要するに白黒するだけのプログラムだった。これではフルカラーLEDの立つ瀬がないので、せめて「色をつけてピカピカ」ぐらいしてあげたい。では、どうすればいいだろうか?

 「乱数で色を決定する」といいたいところだが、「コンピュータで乱数を作る」というテーマだけで論文が1つ書けてしまうぐらい厄介なシロモノ(プログラムの説明も面倒)で、これだけで工作教室夏春分合わせても足りないぐらい。

 実のところ、点滅のサイクルを早くしてRGBを順番に光らせるだけで、それなりの感じになる。つまりGPIOのGP0からGP2の3ビット分の点灯(オン=1)と消灯(オフ=0)を、3ピット分まとめた数値「1」(ビットでいうと001)から「7」(同じく111)で光らせるとよい。3ビットだから「0」(同000)もあるでしょ?、と言われそうなので補足しておくが、「0」では黒、つまり光らないので、これが入ると周期的に値を変更しているのがバレてしまう。

 メインルーチンを書き換えて、まっとうなプログラムにすると以下のようになるだろう。LED用のワークレジスタを用意して、それをインクリメントし、「8」だったら「1」に戻す。先のプログラムを改変するとこうなる。以下、最初のテストプログラムから変更部分のみ記載する。

LEDDAT EQU 23H ; LED用ワーク
ORG 0
main
BSF STATUS,RP0 ; Bank 1 へ切替
CLRF TRISIO
CLRF ANSEL
CALL 3FFh ; 内蔵オシレーター調整
MOVWF OSCCAL
BCF STATUS,RP0 ; Bank 0 へ戻る
CLRF LEDDAT ; LEDDATを0初期化
CLRF GPIO ; GPIOを0初期化
MAINLP
MOVF LEDDAT , W ; LEDデータを+1する
ADDLW 01
ANDLW 07H
BTFSC STATUS , Z ; データが8なら1にする(黒なし)
ADDLW 01
MOVWF LEDDAT
MOVWF GPIO ; PORTへLEDDATのデータを出力
CALL TIME1N
GOTO MAINLP

 初期化ルーチンに2行追加してあるが、これが内蔵オシレータの補正値を使うためのプログラムだ(詳しくはデータシートのセクション9.2.5.1とEXAMPLE9-1を参照)。

 メインルーチンではちょっと変なテクニックを使っている。それが「8」だったら「1」に戻すというところで、実際には必要な下位3ビットだけ「AND」を行い、その結果が「0」ならZフラグが立つので、これをBTFSC命令でチェックし、「0」ならさらに+1、つまり「1」になるわけだ。

 PICの命令はともかくシンプルだが、こんな妙なところもあるので注意したい。さて、ここまでできたらシミュレータでGPIOをモニタしながらanimation実行するとGPIOの値が「1」から「7」の間で書き換わっているのが分かるだろう。

プログラムの最適化をしてみよう

 今回のサンプルのように小さいプログラムには不要なのだが、一応例題として最適化ということも考えてみたい。

 PICの命令は種類が少ないので、実行の効率が上がるように作り変えるのはなかなか難しい。そのなかでもPICプログラミングで「壁」となるのが「RAMが少ない」という点だ。RAMと言ってもワークレジスタなので、ちょっと複雑なことをさせるとワークレジスタ不足に陥るのだ。

 プログラムをジーっと眺めているとLEDのデータをワークレジスタで用意しているが、別にGPIOというシステムレジスタに書き込んでも問題ないし、レジスタ間のコピー命令がいらなくなるので、その分シンプルに仕上がる。

main
CLRF GPIO ; GPIOを0初期化
MAINLP
MOVF GPIO , W ; LEDデータを+1する
ADDLW 01
ANDLW 07H
BTFSC STATUS , Z ; データが8なら1にする(黒なし)
ADDLW 01
MOVWF GPIO ; PORTへWregのデータを出力
CALL TIME1N
GOTO MAINLP

 PIC用命令のちょっとヘンな点として、汎用レジスタを1つ足す(もしくは引く)という命令があるのにアキュムレータにはこの命令がないことが挙げられる。

 だが、任意の数を足すADDLW命令があり、これで「1を足す」とすればいい。実行時間も命令数も同じなので実用上の問題はない。

 プログラミングの世界では手作業での最適化というのは効率は上がるが、実際にはさまざまな条件を考えなければならず、もし業務として取り組む場合は非効率になる。

 しかし「個人的な勉強」ならば、さまざまなケースを想定して試してみることも重要だ。より効率のよい(これには「命令数を減らす」というのと、「実行時間を減らす」という2つの方向性がある)コードを可能にする方法を考えてみるとよいだろう。

 さて、この路線に沿って、サンプルプログラムの最適化をもうちょっと考えると

MAINLP
INCFSZ GPIO
GOTO MAINLP1
GOTO MAINLP
MAINLP1
CALL TIME1N
GOTO MAINLP

 と、条件分岐つきインクリメント命令INCFSZを使うことで命令数を半減できるかもしれない。GP3が入力専門で読み出しが常に0になることを利用してみる、と期待したが、デバッガで見るとダメであることが判明(フラグ判定はインクリメントしたデータを利用するので当たり前なのだが)。

 ここで、動作が確認できたら、時間待ちのサブルーチン(今まで使ってきたサンプルプログラムからコピペする)を加えて完成だ。ただし、実際にLEDを光らせてみると分かるが、待ち時間が1秒程度だと周期的に光っているのがばれてしまう。この改善のために、短い時間待ちループにするとよいだろう。

 時間待ちループのパラメータを以下に用意しておくので、適宜変更して使ってみよう。表の見方だが、左が待ち時間で真ん中の3つがCOUNT1、2、3のそれぞれに与える初期値、最後はルーチン終了の前に入れるNOPの数だ。これらの値はこの3重ループでの最適値なので、そのまま流用するとよい。

COUNT123NOP
1000msA78F81
500ms23BD250
300ms2947210
100ms1A114A1
時間待ちループ用のパラメーター一覧

第1フェーズはこれにて終了

 実は「ボーっと光らせる」とか、「ランダムで光らせる」など色々なパターンを考えていたのだが4回連載という話なのでここでいったん終了する。PICのプログラムとしては初歩の初歩だが、実際にモノを作らなくてもシミュレータで試行錯誤するだけでも遊べるというのが理解していただけると幸いだ。

 要望が多ければ別の題材をテーマにもう1歩深く突っ込んだPIC講座をお届けできるだろう。「こんなものを作ってほしい」という要望があればぜひ編集部までアイディアを寄せてほしい(メカトロで遊ぼう!は企画中でテストプログラムも書きだしていたりする)。

前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

最新トピックスPR

過去記事カレンダー