Renesas Synergy™

FAQ 1006587 : マイコンでのキー・スイッチ入力

マイコンの入力としてキー・スイッチは基本的なものの一つです。ここでは、マイコンでのキー・スイッチの取り込みについて触れます。

[はじめに]
マイコンでキー・スイッチの取り込みには入力ポートを用います。基本的な構成の例を下図に示します。この例で、キー・スイッチが押されていないとき(offのとき)には入力ポートP70端子はハイ・レベルとなり、キー・スイッチが押されると(onのとき)P70端子はロウ・レベルとなります。従って、プログラムによりP7を読み出して、ビット0が1ならキー・スイッチは押されておらず、0ならば押されていることになります。



[チャタリング対策]
原理的には以上のようになりますが、一般にメカニカルな部品であるスイッチにはチャタリングがあるため、このままでは実用になりません。そこで、時間間隔をつけてポートを読み出します(サンプリング)。このサンプリングのイメージを下図に示します。ここで丸はポートを読み出す(サンプリングする)タイミングで、定周期で読み出すことを示しています。サンプリングするタイミングはチャタリングの発生する時間よりも長くとります。部品のばらつきや劣化を考慮すると、長めに設定する必要があります。サンプリングを行なうことで、簡単に信号のエンベロープを得ることができ、チャタリング対策ができます。ただし、これだけでは、サンプリングのタイミングでノイズがあった場合にノイズを信号と誤解する可能性があります。ノイズ対策も行なうには2回以上連続して同じ状態をサンプリングしたら正しい値とします。具体的なプログラムは[チャタリング防止処理]を参照してください。



[キー・リターン割り込み/キー割り込み](78K0/Kx2)
以上のキー・スイッチからのデータ取り込みは定周期でポートを確認することで実現していました。実際のアプリケーションでは、電力消費を抑えるために、通常はスタンバイ状態で、キーが押されると動作状態に復帰するような例があります。このような場合に、割り込みを用いるのですが、通常のエッジ検出による割り込みとは別に、このような用途のために準備された割り込みがあります。それが、キー・リターン割り込みです。
上記の例で示したように、キー・スイッチは通常はハイ・レベルで、キーが押されるとロウ・レベルになります。キー・リターン割り込みはこのロウ・レベルを検出して割り込みを発生させます。通常のエッジ検出による割り込みとの違いは、KR0~KR7の8本の入力信号のどれか一つでもロウ・レベルになったことが検出できることです。
キー・リターン割り込みでキーが押されたことを検出して、スタンバイ解除し、CPUを動作させます。その後、ポート(P7)を読み出して、押されたキーを確認します。78K0/Kx2のキー・リターン割り込みにはビット単位でのマスク機能があるので、通常のキー入力用とスタンバイ解除もできるキーを組み合わせて構成することができます。例えば、下記の構成では8個のキー・スイッチを使用し、マイコンに内蔵されたプルアップ抵抗を利用してプルアップしてあります。また、P70(KR0)~P73(KR3)に接続した#1~#4はキー・リターン割り込みを使用し、#5~#8は使用しない(KRMでマスクする)ものとします。この構成では#1~#4のキーを押すと、キー・リターン割り込みによりスタンバイは解除されますが、スタンバイ状態で#5~#8のキーを押してもキー・リターン割り込みが発生しないため、スタンバイは解除されません。このようなキーの使い分けも可能です。



[通常の割り込みによるキー検出]
キー・リターン割り込みによるキーの検出を行なえない場合には通常のエッジ検出割り込み入力を用います。ただし、エッジ検出割り込みの数は限られているので、使い方に工夫が必要です。具体的な例を下図に示します。この例で、キー・スキャンに用いるPx0~Px7はN-chオープン・ドレイン出力ポートを使用します。通常のCMOS出力のポートを使用する際にはダイオードを図の向きに挿入して、出力ポートから電流が流れ出さないようにします(「こんな使い方が」も参照)。
この方式では、キー・スキャン用の出力ポートのどれか一つにロウ・レベルを出力し、そのときのINTPn端子を監視することで、キーの状態を検出できます。下図の例で、#2のキーが押されていたとすると、このキーに接続されたPx1がロウ・レベルのときだけINTPn端子はロウ・レベル、それ以外の出力がロウ・レベルの場合にはINTPn端子はハイ・レベルとなります。これで、どのキーが押されたかが判断できます。キーの確認を1つずつ行なうため、キーの判定には最悪8回のスキャンを行なう必要があります。



この方式で、キー・リターン割り込みのように指定した複数のキーのどれかが押されたときに割り込みを発生することを考えます。たとえば、#1~#3のキーが押されたらINTPnを発生させるには、対応したPx0~Px3にロウ・レベルを出力します。割り込みが発生したら、改めてPx0からキー・スキャンを行い押されたキーを確認します。

 

こんな使い方が
N-chオープン・ドレイン出力はないが、ダイオードも入れられない場合にはどうにもできないのでしょうか?
実は方法があります。ポートの出力ラッチには全て0をセットしておき、ロウ・レベルを出力したいポートだけをPMレジスタで出力ポートに、その他を入力ポートに設定します。そうすることで、擬似的にN-chオープン・ドレイン出力と同じにできます。また、この方法はラインをハイ・レベルに立ち上げることもできるので、スキャン時間を短くすることが可能です。


[キー・マトリクス]
使用するキーの数が少ない場合には、キーを直接ポートに接続するだけで済みます。しかし、キーが多くなると、キーの数だけポートを用いるのは効率的ではありません。そこで、出力ポートと組み合わせて、キー・マトリクスを構成することになります。下図に4×4のマトリクスの例を示します。入力ポートにおいては、内蔵抵抗でプルアップしているものとします。また、出力ポートはN-chオープン・ドレイン出力とします。

 

ちょっと注意
もし、#1と#5が同時に押されていたとすると、ライン1とライン2が#1と#5のキー・スイッチを介して接続された状態となります。このとき、出力ポートが通常のCMOS出力であれば、ハイ・レベルの出力とロウ・レベルの出力がぶつかることになり、デバイスにダメージを与えたり、キー・スイッチに定格以上の電流が流れたりします。このような問題を避けるには、キー・スキャンに用いる出力ポートはN-chオープン・ドレイン出力にする必要があります。「こんな使い方が」のようなCMOS出力ポートで入出力を切り替える方法も考えられます。
さらに、#1と#5に加えて、#6が同時に押されていた場合には、別の問題が発生します。ライン1をスキャンしているとき、#1と#5を通してライン2もロウ・レベルとなり、この状態でライン2に接続された#6が押されているとI2入力もロウ・レベルになってしまいます。つまり、押していないはずの#2まで押されたと間違われます。これは、#5において本来とは逆方向に電流が流れたためです。対策としてはダイオードを追加して電流を1方向だけにすることがあげられます。(右下の図参照)
ダイオードを追加することで、出力ポートとして、N-chオープン・ドレイン出力の必要性はなくなりますが、キー・スイッチごとにダイオードが必要になるので、コストにも影響します。
このため、他のキーと同時に押すキーにだけダイオードを付ける場合もあります。
以上の話は、メカニカルなスイッチでオン抵抗が小さい場合です。配線やスイッチのオン抵抗をある程度大きくすると、オン/オフの信号ではなく、抵抗分割された電圧を入力することになります。こうすることで、上記の3つを同時に押した例でも3つのスイッチを経由するとロウ・レベルに下がりきれないので、誤動作を避けることができます。


[スキャン]
キー・マトリクスを構成した場合には、出力ポートを使って、スキャン・ラインを順番に一ラインずつロウ・レベルにして入力ポートから読むことで、どのキーが押されたかを判定します。上記のキー・マトリクスを使用した場合のキー・スキャン例を示します。
プログラム例は[キー・スキャンでのチャタリング防止処理]を参照してください。



ライン1をロウ・レベルにした(スキャンした)ときにI1の入力がロウになっていると、#1が押されていることになります。同様にライン2のスキャン中にI1の入力がロウになると、#5が押されていることになります。このように、スキャンラインと入力の組み合わせで押されたキーを判断します。

 

ちょっと注意2
オープン・ドレイン出力の場合には、スキャンが完了して出力をハイ・レベルにしても、直ちに入力ポートの状態がハイ・レベルにはなりません。入力ポートをハイ・レベルに引き上げるのはプルアップ抵抗の働きです。従って、入力のラインの容量成分を抵抗で充電することになりますので、信号の立ち上がりに遅れが発生します。そのため、スキャンの間隔が短いと、ライン1のスキャンで#1のキーが押されてロウ・レベルになった入力がライン2のスキャンまで残ってしまい、押してもいない#5があたかも押されているように判断される場合があります。そこで、キー・スキャンとチェックのタイミングを下記の例のようにシフトします。スキャンのトリガとなるタイマ割り込みで前回のスキャンの結果をチェックし、その後、次のキー・スキャンを開始して、処理を終えます。これにより、スキャンの間隔分だけ信号変化の時間に余裕がもてます。
これ以外に、「こんな使い方が」で触れた、通常のCMOS出力でロウ・レベルを出力しないときは入力ポート設定する方法では、ロウ・レベルを出力してキーを確認したあとで、出力をハイ・レベルにするとラインの立ち上がり時間を短くすることができます。入出力の制御に加えて出力データも制御するので、手間はかかりますが、スキャン間隔を短くすることができます。


[チャタリング防止処理]
チャタリング防止処理を行なうためのプログラム例を2つ示します。どちらも78K0/Kx2のP7をキー入力に用い、インターバル・タイマの定周期割り込みの中で8ビットのキー入力の状態を確認することでチャタリングやノイズを除去しています。他のデバイスで利用するにはポートをP7からデバイスに合わせて変更してください。また、8ビット以下の場合にはキー・データの読み込みの後で使用していないビットをマスクしてください。このプログラムは定周期に実行されるタイマ割り込み処理の中に組み込んで使用してください。

[第1のプログラム例]
(1)プログラムの概要
このプログラムは2回のスキャン結果を比較して、その結果に応じて
①変化したキーに対応するビットは前回の有効キーのデータをそのまま利用する。
②前回のキーと変化がないビットは今回のキー・スキャン結果を利用する。
と言った処理を行います。このように2回以上同じ値が連続しないとキー・スキャン結果を取り込まないので、たまたまサンプリングされたノイズを取り除くことができます。複数のキーがランダムに押される場合を考慮して、変化の判定は条件分岐ではなく、ビットごとの演算を行なって処理します。

(2)使用するデータ領域
このプログラムで得られた有効なキー入力がKEYDATAに入ります。KEYDATAは結果の出力用と前回までの有効なキー入力のデータとしてプログラムで参照します。また、前回のキー・データを保持しておくため作業用としてOLDDATAを用います。

(3)処理手順

読み込んだキー・データは次のスキャンで使用するためにデータ領域(OLDDATA)に保存し、さらにOLDDATAに保存されていた前回のスキャン結果と比較(XOR演算)することで、キー・データの変化を検出します。
XORの結果の論理を反転することで、変化したビットを0にし、それとスキャン結果のAND演算を行ないます。これで、変化したビットは0に、変化しなかったビットはスキャン結果が入ることになります。
得られた変化がなかったキー・データをセーブし、変化したビットのデータとデータ領域(KEYDATA)に保存されていた前回の有効なキー入力のAND演算を行ないます。これで、キー・データが変化しなかったビットは0に、変化しているビットは前回のデータがはいります。
以上の演算で得られた2つの結果をOR演算することで最終的な結果が得られますから、これをデータ領域(KEYDATA)に保存します。


(4)プログラム例


[第2のプログラム例]
このプログラムではキーの変化(押されたキーとはなされたキーの情報)も情報として得ることができます。このため、第1の例に変化検出処理を追加してあります。新たなキーが押されたら(キー入力データが0になったら)PUSHKEY、はなされたらRELKEYの該当するビットがセットされるので、変化した情報を知ることができます。なお、この情報は保存されず、スキャン毎に更新されてしまいます。

 

ちょっと注意3
2つのプログラムは5行目までで2回のキー・スキャン・データの変化したビットは1になり、変化がないビットは0になったデータがAとXレジスタに入ります。ここまでは同じで、次の2命令が異なっています。しかし得られる結果は同じになります。
変化がないビットに対してはOLDDATAのビットを利用する(キーを取り込む)ために変化したビット(AやXで1のビット)はクリアする必要があります。そこで、最初のプログラム例ではAレジスタのデータの補数をとり、OLDDATAとの論理積をとることで変化したビットをクリアしています。2番目の例では、AレジスタとOLDDATAとの論理和をとることで変化のあったOLDDATAのビットを1にし、さらにXレジスタと排他的論理和をとることでクリアしています。この2つの演算の関係については下の式を参照ください。


[キー・スキャンでのチャタリング防止処理]
(1)プログラムの概要
キー・マトリクスの例で示された、4×4の16キーを対象として考えます。P7の上位4ビットでスキャンを行い、下位4ビットで読み込みを行います。(P7は通常のCMOS出力ですので、スキャンラインからポート出力にだけ電流が流れるようにポート出力にダイオードを入れるか、「ちょっと注意」に掲載されたように各キーにダイオードを入れるものとします。)
使用するポートの構成やデバイスによって読み込むポートや、スキャンするポートを変更してください。

(2)プログラムの概略動作
このプログラムはキー・リターン割り込みとタイマ(タイマ51)の二つの割り込みを利用しています。最初はキー・スキャンは行なわず、ライン1を選択して、#1~#4のキーのどれかが押されるまで、キー・リターン割り込みを用いて待ちます。#1~#4のキーのどれかが押されて、キー・リターン割り込みが発生したら、タイマを起動し、キー・スキャンを開始します。キー・スキャンを開始すると、約10ms周期で発生するタイマ割り込みで

ライン1→ライン2→ライン3→ライン4→ライン1・・・・・・

とスキャンを行ない、4回のタイマ割り込みで全てのラインのスキャンを完了します。スキャンした結果は、ライン1から順にKEYDATA1~KEYDATA4に格納されます。

(3)使用するデータ領域
基本的な処理は前に説明したチャタリング防止処理の例1と同じです。スキャン毎に使用するデータ領域を以下のように定義しておき、全て0FHで初期化します。ここでは、HLレジスタを用いてアクセスしますので、SADDR領域に配置する必要はありません。また、スキャンラインを指定するために変数をひとつ(SCANLINE)準備します。



(4)初期化プログラム部
プログラムの処理としては定周期に実行するためにインターバル・タイマでの割り込みを使用します。そのため、初期化部分と実際の処理部分に分けて説明します。
この処理を、デバイス全体の初期化ルーチンに組み込んで使用してください。
この初期化部分では、ポートのモード、タイマやキー・リターン割り込みの設定を行います。初期状態ではキー・スキャンは行なわず、ライン1(P77に対応)の#1~#4を押すと、キー・リターン割り込みが発生するように設定します。タイマは初期設定だけを行ない、停止状態にしておきます。



(5)キー・スキャン関係の初期化処理
ここで、スキャンポートの設定、変数初期化、キー・リターンやタイマの初期化処理を行ないます。キー・リターンやタイマ機能はまだ起動しません。



(6)キー・リターン割り込みによるキー・スキャンの起動処理
キー・リターン割り込みが発生して、この処理が動作すると、キー・スキャンのタイマを起動します。また、以降はキー・リターン割り込みを禁止します。



(7)タイマ割り込み処理
定周期に発生するタイマ割り込みの処理の中からキー・スキャン処理をサブルーチンとして呼び出します。



(8)キー・スキャン処理
ここは、定周期タイマ割り込みで起動されて、実際にキー・スキャンを行ないます。この処理は大きく3つの部分に分けられます。
最初に、スキャンした結果を保存するためのポインターを計算しています。できるだけ入力パラメータを少なくし、さらに条件分岐を使わないで済ませたいので、計算で必要な情報を得ています。
次に、実際にポートからスキャンデータを読み込み、チャタリングやノイズ対策を行ないます。このような厳密な処理が必要なく、単にサンプリングによるチャタリング対策だけで済ませる場合にはこの部分はポートからスキャンデータを読み出してデータ領域に保存するだけでも結構です。
最後に、次のキー・スキャンのための準備を行ないます。スキャンラインを次のラインに変更し、新しいラインをスキャンするためのデータをポートに出力します。

 

ちょっと注意4
スキャンラインの更新処理では、スキャンラインが4本、つまり0~3の2ビットで表すことができることを利用しています。+1して、下位の2ビット以外をマスクすることで新しいスキャンラインを得ることができます。
また、実際にポートをスキャンするデータについては、1ビットだけ0にしたデータを用いますが、上位4ビットしか使用していないので、単に右回転するだけでは不十分です。ライン4までスキャンした段階でポートのデータは1110XXXとなっています。このまま右回転してもビット7を0にできません。そこで、このプログラムでは、ライン1、つまりスキャンラインが0のときだけキャリーをクリアし、それ以外ではキャリーをセットしておくことでキャリーを含めて右回転することで、ライン1のときにビット7を0にしています。このような処理を行なうことで、条件分岐命令を使用することなく処理しています。
他にご質問がございましたら、リクエストを送信してください