Renesas Synergy™

FAQ 1006589 : LEDのダイナミック表示方法(78K0/Kx2,78K0S/Kx1+)

[はじめに]
多くの情報を表示する場合には一般にダイナミック表示を行ないます。FAQの「ダイナミック動作とスタティック(Dynamic Operation and Static)」も参照ください。
ここでは、FAQの「LEDのドライブ方法」の「LEDのマトリクス接続の例」に掲載された回路を対象にしたダイナミック表示方法について説明します。
LEDは以下の表に示すポート構成によりグループを選択、その中から対象を選択します。

 
[表示のしかた]
ここではL1~L4、L5~L8、L9~L12、L13~L16の4つのLEDから構成された4つのグループに分けて表示することにします。 P44~P47で点灯したいLEDのグループに対応したポートに0それ以外は1に設定、表示したいLEDをP40~P43で指定することで表示を行ないま す。下図の表示のしかたに示すようにP44から順にP45、P46、P47、またP44と0を設定してポートからロウレベルを出力します。これに合わせ て、P40~P43に対応したLEDの点灯パターンを出力します。

 
表示は2.5m秒で切り替え、10m秒(100Hz)で全てのLEDの点灯を完了するものとします。このために、システム・クロックとして高速内蔵発振器 の8MHzを1/2分周した4MHzを使用します。これを1/64分周したクロックで8ビット・タイマを駆動し、1/156分周することで約400Hzの インターバル割り込みを発生させます。このインターバル割り込みの中で表示を切り替えます。

[プログラム例1]
ここではアセンブラ言語で記述したプログラム例を示します。C言語記述についてはプログラム例2で説明します。
表示の指定は、点灯するLEDに対応した2つ(LEDSTATUS1とLEDSTATUS2)の8ビットの変数(LEDステータス)を準備して、そこに表 示データを設定しておきます。タイマ割り込み処理では、点灯したいLEDに対応したビットから4個のLEDに対応したデータを抽出します。そのデータと表 示グループの指定データを組み合わせてポート4に出力することでLEDを点灯します。これを4回繰り返すことで完全な表示となります。

・表示データの作成
LEDステータスの情報を4つのグループに分けて点灯するため、何回目の表示かの情報を元にLEDステータスから表示データパターンを抽出します。ここで は、どのグループかの情報をXレジスタ、どのLEDかの情報をAレジスタで作成し、2つを合成して表示データを作ります。処理時間を短くするために、グ ループを示す情報と、その中の点灯するLEDの情報を並行して処理し、最後に結合します。下記のプログラム例では、主にXレジスタでグループの選択情報、 Aレジスタでグループ中の点灯するLEDの情報を処理しています。

DATWORK         DSEG    SADDR
NEXTDATA: DS 1 ; 次の表示データ格納用
LEDSTATUS1: DS 1 ; LED1~8の点灯情報(1で点灯)
LEDSTATUS2: DS 1 ; LED9~16の点灯情報(1で点灯)
COUNT: DS 1 ; 何回目の表示か(0-3)

GETLED:
MOV A,LEDSTATUS1 ; LED1~8のステータスを読み出す
MOV X,#11101111b ; LED1~4の選択信号
BF COUNT.1,$NEXT1 ; LED1~8の表示なら次へ
MOV A,LEDSTATUS2 ; そうでなければLED9~16を
MOV X,#10111111b ; LED9~12の選択信号
NEXT1:
BF COUNT.0,$NEXT2 ; LED1~4/9~12なら分岐
ROR A,1 ; 有効LEDビットを下位4ビットに
ROR A,1
ROR A,1
ROR A,1
XCH A,X ; グループ選択信号をAレジスタへ
ROL A,1 ; 選択信号を次のグループに
XCH A,X ; A:LEDステータス、X:グループ情報
NEXT2:
OR A,#11110000b ; グループ情報をマスク
AND A,X ; 表示データを合成
MOV NEXTDATA,A ; 次の表示データを保存
RET

・データの表示(タイマ割り込み処理)
2.5m秒ごとの割り込みでLEDをグループごとに点灯させます。表示するためのデータ生成は上記のサブルーチンを用いています。
ここでは、まず表示を消したあとで、前回計算しておいたデータをポートに出力することで表示を行ないます。その後、次のグループの選択し、その表示データを準備します。
前回計算しておいたデータを使用するのはできるだけ時間のばらつきの影響をなくすためです。

TMH1INT:
PUSH AX
MOV A,NEXTDATA ; 表示データ情報を読み出す。
MOV P4,#11110000b ; LEDを全て消灯
MOV P4,A ; 新しいデータを表示
INC COUNT ; 次のグループを選択
MOV A,#00000011b ; 表示グループ数のマスク
AND A,COUNT ; 全ての桁が終了していれば
MOV COUNT,A ; 次のグループをセット
CALL !GETLED ; 次の表示データを作成

ここに他に必要な処理を記述する

POP AX
DUMMY: RETI ; 割り込み処理終了

・初期化処理
初期化処理では、スタックポインタの設定、電源の立ち上がり待ち、メモリや使用する内蔵周辺のハードウェアを初期化します。その後にタイマを起動し、割り込みのマスクを解除し、その後は他の処理を行ないます。タイマ割り込みが発生すると、LED表示を開始します。
なお、リセット・ベクタや割り込みベクタは以下のようにしておきます。ここで、使用しない割り込みには何もしないでRETI命令で戻るためのダミーのベクタを設定しておきます。

RSTVECT CSEG    AT      0
DW START ;00:リセットスタート処理のベクタ
DW DUMMY ;02:
DW DUMMY ;04:
DW DUMMY ;06:INTLVI
DW DUMMY ;08:INTP0
DW DUMMY ;0A:INTP1
DW TMH1INT ;0C:INTTMH1
DW DUMMY ;0E:INTTM000
DW DUMMY ;10:INTTM010
DW DUMMY ;12:INTAD
CSEG
START: MOVW AX,#STACKP
MOVW SP,AX
CALL !POWERON ; 電源の立ち上がりを待つ
CALL !INITIO ; ハードの初期化
CALL !INITRAM ; RAMの初期化


SET1 TMHE1 ; タイマをスタート
CLR1 TMMKH1 ; タイマ割り込みマスク解除
EI

ここには他にやりたい処理を記述する。


;
; 電源の立ち上がり待ち(2.7V以上になるのを待つ)
;
POWERON:
DI
MOV LVIS,#00000111b ; VLI=2.7V
MOV LVIM,#10000000b ; 低電圧検出開始
MOV PCC,#00 ; CPU clock is fx/4
MOV WDTM,#01110000b ; WDTを停止
SET1 LSRSTOP ; 低速発振器停止
MOV A,#50
PONLOOP:
DEC A
BNZ $PONLOOP ; 0.2m秒待つ

PONLOOP2:
BT LVIF,$PONLOOP2 ; 電圧が2.7V以上まで待つ
MOV PPCC,#01 ; CPU clock is fx/2
RET
;
; RAMの初期化
;
INITRAM:
MOV COUNT,#3
MOV LEDSTATUS1,#0
MOV LEDSTATUS2,#0
MOV NEXTDATA,#11110000b
RET
;
; タイマH1やポート4等ハードウェアの初期設定
;
INITIO:
MOV P4,#11110000b ; 初期値(LED全消灯)
MOV PM4,#00 ; ポート4は全て出力に
MOV TMHMD1,#00110000b ; タイマモード設定
; |||++----- インターバルタイマモード
; +++------- カウントクロックfXP/64
MOV CMP01,#155 ; インターバル時間を設定
MOV IF0,#00 ; 割り込みクリア

ここには他で必要な初期化処理を記述する

RET


[プログラム例2]
ここではC言語で記述したLED表示プログラム例について説明します。この例では、表示パターンを約1秒ごとに+1するような処理も追加しています。

・宣言部分
このプログラムでは内蔵周辺I/Oの操作以外に、割り込み制御を行なうので、プログラムの先頭で以下のような#pragma指令の宣言を行ないます。割り込み処理を行なう関数としてはGETLEDをプロトタイプ宣言しています。

コーヒーブレーク

78Kシリーズ用のC言語では標準的なC言語に対して組み込み用途で使用するために機能が拡張されています。この拡張機能を使用するためには宣言が必要となります。主な宣言としては、ここで使用している内蔵周辺I/Oをキーワードとして使用するための「#pragma SFR」、CPUのもついくつかの命令をC言語で使用するための「#pragma EI」や「#pragma NOP」等の指定、CPUのベクタ割り込み(ここではINTTMH1)と実際の処理関数(ここではGETLED)を結びつけるための「#pragma INTERRUPT INTTMH1 GETLED」があります。


それ以外ではハードウェアの初期化を行なうためのhdwinitもプロトタイプ宣言しています。
変数としてはLEDSTATUSが点灯するLEDの情報で、ここでは16ビットの変数として処理しています。SECTIMERは1秒の時間をカウントするために使用します。NEXTDATAやCOUNTはアセンブラ記述のプログラムと同じ意味で使用しています。

#pragma SFR
#pragma EI
#pragma DI
#pragma NOP
#pragma INTERRUPT INTTMH1 GETLED

unsigned int LEDSTATUS,SECTIMER;
unsigned char NEXTDATA,COUNT;

__interrupt void GETLED(void);
void hdwinit(void);


・初期化処理部
78KのC言語では内蔵周辺I/Oの初期化処理をプログラム本体の実行開始前に実行できるようになっています。そのために、専用の関数(hdwinit)が予約されています。早目に実行させたい内容はhdwinit関数に記述します。

コーヒーブレーク

78KのAll Flashマイコンでは、CPUが起動するとオプションバイトの確認によるハードウェア構成の決定が行なわれます。その後にリセット・ベクタ(0番地と1番地)に格納されているアドレスから実行がスタートします。アセンブラ記述のプログラムでは、リセット・ベクタもプログラムとして記述しますが、C言語記述の場合にはスタートアップ・ルーチンがスタートします。スタートアップ・ルーチンについてはFAQの「最初に実行する内容について」を参照してください。
スタートアップ・ルーチンがスタックポインターの設定やCのプログラムを実行するために必要な処理を行ないます。その中にhdwinit関数の呼び出しが含まれています。一連の処理が完了したら、スタートアップ・ルーチンはC言語で記述されたmain関数を呼び出すことで、C言語記述のプログラムを実行します。


以下に示すhdwinit関数では、ウォッチドッグタイマを停止した後で、低電圧検出機能を用いて、電源電圧の立ち上がりを待っています。低電圧検出機能は起動してから0.2m秒待たないと、正常な結果が得られないので、そこはソフトウェア・タイマで待ちます。電源電圧が2.7V以上になったら、CPUの動作クロックを4MHzに設定して使用するポートやタイマ等の初期化を行っています。

void    hdwinit(void){
unsigned char work1;

LVIS = 0b00000111; /* select VLI=2.7V */
LVIM = 0b10000000; /* Start VLI detection */
PCC = 0x00; /* CPU clock is fx/4 */
WDTM = 0b01110000; /* Stop WDT */
LSRSTOP = 1; /* Stop Low speed OSC */

/*
wait for 0.2m秒
*/
for (work1 = 0; work1 < 10; work1++){
NOP();
}

while ( LVIF ){
NOP();
} /* wait VDD > 2.7V */

PPCC = 0b00000001; /* CPU clock is fx/2 */
P4 = 0b11110000; /* Clear All LED */
PM4 = 0x00; /* Port 4 is Output */

TMHMD1 = 0b00110000; /* set interval timer mode */
CMP01 = 155; /* Interval = 2.5m秒 */
IF0 = 0x00;
}


・本体処理部
main関数では、各種の変数に初期値を設定します。その後は、タイマを起動して、1秒の時間をカウントする変数(SECTIMER)が0になるのを待ちます。1秒経過したら、表示データ(LEDSTATUS)の値を+1します。変数SECTIMERはmain関数と割り込み関数の両方から書き込まれるので、本来は排他制御を行なうことが考えられます。しかし、ここでは、割り込み間隔(2.5m秒)以内に処理は完了してしまうことから排他制御(この場合には割り込みの禁止/許可)は行いません。

void    main(void){
COUNT = 3;
NEXTDATA = 0b1111000;
LEDSTATUS = 1;
SECTIMER = 400;
TMHE1 = 1;
TMMKH1 = 0;
EI();
while(1){
while(SECTIMER){
NOP();
}
SECTIMER = 400;
LEDSTATUS++;
}
}

・割り込み処理部
タイマH1による2.5m秒ごとの割り込みにより起動され、実行されます。そのため、関数の頭で割り込み処理関数であることを示す「__interrupt」が宣言されています。これ以外は通常の引数や戻り値をもたない関数と同じです。
処理の殆どは表示のためのポートに出力するデータパターンの生成です。これは2つの処理から構成されています。一つは、表示するグループに対応したグループの選択信号を生成です。これはswitch文により、変数COUNTに応じた値をNEXTDADAにセットすることにより実現しています。2つ目は、表示データの抽出です。これは表示したいデータ(LEDSTATUS)を必要な回数だけ右シフトして、下位4ビット以外はマスクすることで実現しています。このようにして得られた2つのデータを結合して、次の表示のためのデータパターンとします。
その他の処理としては、最初に前回作成したデータパターンを実際にポートに出力や、次回のための表示グループの更新や1秒を得るためのカウントです。

__interrupt     void    GETLED(void){
unsigned int work1;
unsigned char work2;
P4 = 0b11110000; /* Clear LED */
P4 = NEXTDATA; /* Turn on specified LED */
work1 = COUNT; /* Get Display Group Number */
switch(work1){
case 0:
NEXTDATA = 0b11100000;
break;
case 1:
NEXTDATA = 0b11010000;
break;
case 2:
NEXTDATA = 0b10110000;
break;
case 3:
NEXTDATA = 0b01110000;
}
work1 *= 4;
work2 = LEDSTATUS >> work1;
work2 &= 0b00001111;
work1 = (unsigned char) work2;
NEXTDATA |= work1;
COUNT = (COUNT+1) & 0b00000011;
SECTIMER--;
}

適用製品
78K ファミリ
他にご質問がございましたら、リクエストを送信してください