目次PIC回路集LEDフラッシャー2


LEDフラッシャー2 ソフト処理説明



処理概要

今回のソフトウェアではメイン処理でLEDの点灯制御を行い、割り込み処理で点灯するLEDの位置を変えています。
メイン処理のLED点灯制御では4段階の明るさ制御を行っています。
左の図は1つのLEDについて時間とともに変化する点灯状況を表しています。赤い部分はLEDが点灯している時間を示しています。あるLEDに対する明るさ制御データが 11 の場合、全てのプロセスでLEDが点灯します。制御データが 10 の場合、プロセス1と3でLEDが点灯し、プロセス2と4では点灯しません。01 の場合にはプロセス1のみ点灯するという具合です。00 の場合には点灯しません。
各プロセスでは led_data0 および led_data1 を読み、8つあるLEDのそれぞれの点灯制御データをチェックし、該当時間にLEDを点灯させるか、消灯させるかを判断しています。点灯制御データに対してどのプロセスで点灯させるか消灯させるかは led01、led10、led11 に格納される明るさ制御データが使われます。プロセス数を多くすれば、LEDの明るさをもう少し細かく制御できるのですが、1プロセスで8つのLED点灯制御を行い、4プロセスで32回点灯制御を行うことになります。プログラムメモリの関係で4プロセスとしました。実際のプロセスの進行は処理の都合でプロセス4から行われます。プロセス1まで行くと、再びプロセス4が行われるというように繰り返されます。
この処理はLEDの点灯制御をするだけで点灯するLEDの位置は変わりません。LEDの点灯位置を変えるのは割り込み処理で行われます。
割り込み処理はTMR0を使用して50ミリ秒毎に発生させています。割り込み処理では主に点灯するLEDの位置を変える処理を行っています。点灯の移動パターンは3種類にしています。両側から交差するようにLEDの光が移動するパターン0、右から左へ移動するパターン1、左から右へ移動するパターン2の3種類です。初期化ではパターン0にしています。
割り込みが行われると、まず、スイッチの状態をチェックしています。押されたスイッチによってパターンが変わります。各LEDの移動時間は100ミリ秒にしました。この時間は speed というデータを変えることにより50ミリ秒単位で変えることができます。speed データを変えることにより割り込み回数に対する移動処理の回数が変わります。今回は speed に 1 を設定しているので2回に1回になり100ミリ秒です。speed が 0 だと50ミリ秒、1 だと100ミリ秒、2 だと150ミリ秒という具合です。
LED点灯移動処理ではパターン毎に登録されている10個の制御データを100ミリ秒間隔で led_data0 および led_data1 にコピーしています。
以上のような処理により尾を引いて流れるようなLEDフラッシャーができあがっています。


環境設定
        list            p=pic16f84a
        include         p16f84a.inc
        __config _hs_osc & _wdt_off & _pwrte_on & _cp_off
        errorlevel      -302    ;Eliminate bank warning
listこのコマンドは本来アッセンブラ結果のリストを出力する際のパラメータを指定するコマンドです。そのコマンドのパラメータの一つにプロセッサ名称を指定するようになっています。逆にプロセッサ名称を指定するために list コマンドを使う必要があります。
includeこのコマンドは標準的なラベルの値が定義されているファイルの読み込み指示をするコマンドです。 p16f84a.inc ではPIC16F84Aで標準的に使用されるラベルが定義されています。incファイルを使わずにソースリストで直接ラベルを定義することもできます。 include コマンドを使う場合にはラベルを使用する行より前に書かれている必要があります。 list の次に書くのが無難です。参考に p16f84a.inc をテキスト形式にしたファイルを掲載します。 p16f84a.txt 拡張子を .inc に変えれば inc ファイルとして使えます。
__config このコマンドはコンフィグレーションワードの内容を指定するコマンドです。 使用するパラメータの文字は inc ファイルの最後のほうに書かれています。今回のリストでは「発振器はHSタイプ」、「ウォッチドッグタイマーは使用しない」、「パワーオンリセットを使用する」、「コピープロテクトは使用しない」としています。各パラメータの間は & でつなぎます。
アンダーバー ( _ ) は2つ連続することに注意してください。
errorlevel PIC16F84AではRAMメモリの制御にバンクという方式が使われています。バンク指定を間違えるとメモリの制御が正常に行われません。それを警告するためにバンク1のメモリを操作する際に警告メッセージ302が出されます。このメッセージは処理が正常でも出されます。ですから、正常の場合には煩わしいメッセージです。 errorlevel でこのメッセージの表示を止めることができます。ソースコードを書いた最初の段階ではこのコマンドは使用しない方がよいかもしれません。バンク切り替えに間違いが無いことを確認してからメッセージを止めた方が無難です。
このコマンドを使わなくても p16f84a.inc の中の OPTION_REG から EECON2 までの5つのラベル定義の2桁目の 8 を 0 に変えればメッセージは出なくなります。



ラベル定義
;****************  Label Definition  ********************
        cblock  h'0c'
データエリアにはCBLOCK疑似命令を使用して0Chから自動割付を行っています。割付の終了にはENDCを使います。
以下に今回使用しているデータエリアの意味(使用目的)を示します。

ラベル
用  途
loop_cnt:LEDの明るさを4段階で制御するためのプロセス処理に使用するカウンタです。
led_data0:LEDの明るさ制御データの上位側を格納するエリアです。
led_data1:LEDの明るさ制御データの下位側を格納するエリアです。
led01:LEDの明るさ「やや消灯」の制御データを格納するエリアです。
led10:LEDの明るさ「やや点灯」の制御データを格納するエリアです。
led11:LEDの明るさ「点灯」の制御データを格納するエリアです。
port_work:8つのLED用の各ビットの状態を一時格納するエリアです。
counter:LEDの移動処理の時間を制御するカウンタです。
ptn_save:LEDの点灯パターンを格納するエリアです。
ptn_counter:LEDの明るさ制御データの位置を指定するカウンタです。
w_save:割り込み処理時にWレジスタの元の内容を待避するためのエリアです。
s_save:割り込み処理時にSTATUSレジスタの元の内容を待避するためのエリアです。

他のラベル定義としては以下のようなラベルがあります。
ラベル
用  途
ra0/ra1/ra2:スイッチを接続するRAポートの番号です。
speed:LED点灯の移動速度を設定します。50ミリ秒単位。
led1:「やや消灯」のLED点灯時間を設定します。
led2:「やや点灯」のLED点灯時間を設定します。
led3:「点灯」のLED点灯時間を設定します。

;*************  Pattern Data Definition  ****************
; 1 bright  1 less bright  0 almost dim  0 dim
; 1         0              1             0
各LEDの点灯パターンの設定です。2バイトが対になっています。各ビットはLEDに対応し、上側と下側を組み合わせて使っています。あるビットで上側が 1 で下側が 0 の場合、10 ということで、「やや点灯」を表します。
3パターン分を定義しています。


プログラム開始
;****************  Program Start  ***********************
電源投入/リセット時はプログラムメモリアドレスは h'0000' からスタートします。また、割り込み処理のスタート番地は h'0004' です。


初期化処理
;****************  Initial Process  *********************
    電源投入後の初期化処理として以下の処理を行います。
    ポートAのモードを設定
    ポートAには点灯パターン切り替え用スイッチが接続されます。それに対応する0、1、2の3ポートを入力モードに設定しています。
    ポートBのモードを設定

    ポートBにはLEDが接続されます。全ポートを出力ポートに設定しています。
    ポートBのプルアップ機能を無効(RPBU=1)に設定

    今回の回路の場合、ポートBにはLEDが接続されるのでプルアップは必要ありません。
    プリスケーラ設定

    今回使用しているPICの発振器は4MHzです。4MHzの周期は0.25マイクロ秒ですので、命令実行サイクルはその4倍の1マイクロ秒になります。
    TMR0としては50ミリ秒でタイムアウトさせたいのですが、プリスケーラを使わないと最大でも256マイクロ秒しか設定できません。256倍のプリスケーラを設定すれば、最大約65ミリ秒まで設定することができます。プリスケーラをTMR0で使用(PSA=0)、また、プリスケーラ値を256に設定(PS=1:256) としています。
    ワークエリアの初期化

    明るさ制御データエリアをクリア
    LED点灯時間データエリアを設定
    LED点灯状態一時格納エリアをクリア
    移動速度データを設定
    点灯パターンを設定
    点灯位置カウンタをクリア
    TMR0設定

    50ミリ秒のタイマー値を設定。(256-50ms/0.256ms = 61) TMR0はカウントアップタイマーで255から0になるときにタイムアウトが発生します。
    割り込み条件設定

    TMR0のタイムアウト割り込みを可能とします。また、割り込みを可能とするため、GIEも 1 に設定します。



LED点灯制御処理
;****************  LED control Process ******************
LEDの点灯制御は4回に分けて行います。各回でLEDの明るさ指定に応じてLEDの点灯制御を実施します。一番明るく点灯させる場合には4回とも点灯させます。やや明るく点灯させる場合には4回のうちの2回だけ点灯させます。やや暗く点灯させる場合には4回のうち1回だけ点灯させます。消灯の場合には4回とも点灯させません。各回はプロセスという処理名称を付けています。プロセス4→プロセス3→プロセス2→プロセス1→プロセス4というようにサイクリックに処理が行われます。処理プロセスは loop_cnt の値により決まります。
;****************** bit0 for process1 *******************
各プロセスでは各LED毎の処理が実行されます。各LEDは8ビットのメモリの1ビット毎に対応させています。ビット0がLED1、ビット1がLED2という具合です。1つのプロセスではビット0からビット7まで連続に処理されます。
led_data0 と led_data1 にはLEDの明るさを指定するデータが設定されています。このデータの設定は後に説明する割り込み処理で行われます。led_data0 が上位側のビット、led_data1 が下位側のビットを表しています。led_data0 のビット0が"1"、led_data1 のビット0が"1"の場合、 11 ということで「点灯」という指定になります。このデータが 10 の場合は「やや明るい」という指定、 01 の場合は「やや暗い」、 00 の場合は「消灯」という指定です。
各プロセスでLEDを点灯するか消灯するかは led11、led10、led01 で指定されるデータによります。 00 の場合には消灯なので、特にデータは設けていません。 11 も点灯なので、データを設けなくても良いのですが、明るさを制御することもなるかもしれないと思い、データを設けています。例えば led10 には 11111010 というデータが設定されています。今回の処理では4プロセスしか設けていないので、上位の4ビットは使用していません。下位の4ビットは各プロセスに対応しています。ビット0はプロセス1用、ビット1はプロセス2用という具合です。各ビットの意味は1が消灯、0が点灯を意味しています。ですから、明るさ指定が 10 の場合、プロセス1と3で点灯、プロセス2と4では消灯というように制御されます。
led_data0 と led_data1 のデータにより各ビットが処理され、LEDが点灯( 0 )か消灯( 1)かの判断の結果は port_work の対応するビットに設定されます。全てのビットの処理が完了し、プロセスが終了する時点で port_work のデータは PortB に書き込まれます。この時点で各LEDは点灯または消灯の状態になります。
この説明のアニメーションではプロセスを500ミリ秒毎に変化させていますが、実際の1プロセスの処理は約100マイクロ秒で終了します。1秒間に10,000回の速さなので、LEDが点滅している状態は目では見えません。点灯の明るさが違って見えるだけです。
LEDの明るさは電流の大きさに比例しません。少ししか電流が流れなくてもLEDは点灯をはじめます。また、人間の目で感じる明るさ(感明度)は輝度の1/3乗に比例するそうです。輝度が小さいときは明るさの変化を敏感に感じ取りますが、ある程度の明るさになると輝度が変化してもそれほど変化を感じないということです。今回の場合、「点灯」と「やや消灯」では輝度に4倍の差がありますが、見た目にはそれほどの差を感じません。
明るさに差を付ける方法としてプロセスを増やす方法が考えられます。プロセス4自体をさらに4回繰り返すと「点灯」と「やや消灯」の差が8倍になるので明るさの差をはっきりさせることも可能と思います。今回はメモリがほとんどないので試していません。


割り込み処理
;**********  Interruption process beginning *************
TMR0 が50ミリ秒をカウントしてタイムアウトすると処理の割り込みが発生します。割り込みが発生するとプログラムメモリの0004番地に実行アドレスが移動します。今回の処理では0004番地に割り込み処理への goto 命令を書いてあるので、処理は割り込み処理にジャンプしてきます。
割り込み処理で最初に行うことはWレジスタとSTATUSレジスタの内容をセーブすることです。これらのレジスタは割り込み前の処理でも使用しており、割り込み処理が終わった後、割り込み前と同じ値になっていないと元の処理が正常に動作しなくなります。
次にタイマー割り込みであるかをチェックしています。今回の処理では割り込みとしてはTMR0のタイムアウトしか使用していないので、あまり意味はありません。もし、タイムアウト以外の割り込みの場合、異常動作ですから、初期化処理にジャンプするようにしています。
;*****  Timer interruption process (50ms interval) ******
TMR0のタイムアウト割り込みを確認したあと、タイムアウトの割り込み表示を消しています。これを消さないと、割り込み処理を終了させたあと、すぐに割り込みが発生してしまいます。タイムアウトするとTMR0の内容は0になっているので、50ミリ秒の値を設定します。
;******************  Key Scan Process *******************
LEDの点灯パターンを変更するスイッチが押されているかをチェックします。スイッチが押されていると、スイッチが接続されている PortA のビットが 0 になります。どのビットが 0 になっているかをチェックすると押されたスイッチを知ることができます。スイッチが押された場合、スイッチに対応するパターン番号を ptn_save に記録します。
Key Scan 処理の最後ではパターン処理を実行するかどうかの判断をしています。割り込みはTMR0により50ミリ秒毎に発生します。パターン処理の実行判断は counter の値により行っています。 counter から1を引いて0になったらパターン処理を実行します。すなわち、counter の値が2の場合、2回の割り込みに対して1回パターン処理を実行することになります。この場合、パターン処理は100ミリ秒間隔で行われることになります。 counter の値が3の場合は150ミリ秒間隔ということになります。 counter の設定値は speed という定義で指定しています。 speed が1の場合、counter は2に設定され、パターン処理は100ミリ秒間隔になります。
;*****************  Pattern Process *********************
パターン処理の先頭では counter の値を再設定しています。これを行わないと、パターン処理の実行間隔が一定になりません。
そのあと、 ptn_save に記録されているパターン番号をチェックし、該当するパターン処理にジャンプします。
;*********************  Pattren 0 ***********************

各パターン処理の先頭では自分のパターン番号と ptn_save の番号が一致しているかをチェックしています。一致している場合には前回のパターン処理の継続であり、led_data0 と led_data1に設定するデータが前回設定した次のデータを設定します。一致していない場合には該当するパターンの先頭のデータを led_data0 と led_data1 に設定します。
パターン処理では Pattern Data Definition で定義されたデータを順番に led_data0 と led_data1 に設定します。speed が 1 の場合には100ミリ秒間隔で設定します。
パターンデータを led_data0 と led_data1 に設定したあと、パターンカウンタ ( ptn_counter )をカウントアップして割り込み終了処理にジャンプします。
アニメーションではパターンデータの設定を200ミリ秒としています。100ミリ秒にもできますが、少し速すぎて動作が分かり難いので少し遅くしました。実際にもこのくらいの速度で良いかもしれません。その場合には speed を 3 にすればデータ設定間隔が200ミリ秒になります。



割り込み終了処理
;************  END of interruption process **************
割り込み終了処理ではSTATUSレジスタとWレジスタを割り込み前の状態に戻します。最初にSTATUSレジスタを戻し、次にWレジスタを戻します。ここで注意しなければいけない点はWレジスタを戻す方法です。 movf または movfw 命令を使うと 復元するWレジスタの内容によってSTATUSレジスタの内容が変化してしまいます。そこでSTATUSレジスタを変化させないために swapf 命令を2回使ってWレジスタを元に戻しています。
retfie 命令で割り込み処理が終了します。