ステップ8
キーマトリックススキャン
■用意するもの
・簡易キーマトリックス 1つ
■キーマトリックススキャンとは?
キーマトリックススキャンとは本来、複数のキースイッチの状態を調べるのであれば、スイッチの数だけ入出力ポートが必要になります。しかし、キーボードのような大量のキーを使うものは入出力ポートが不足してしまいます。このような時に使われるのがキーマトリックススキャンです。 キーマトリックススキャンを使うことで入出力ポートを節約することができます。
■キーマトリックススキャンの仕組み
今回はわかりやすく説明するために3×3のスイッチにピックアップして説明していきます。
例えばOUT 1をL、OUT2~3をHにし、一番左の列のスイッチが押されているとIN1~IN3にLレベル、押されていないとHレベルが 入力され、スイッチの状態を読み取ることができます。 この時に真ん中の列のスイッチが押されてOUT2がHレベルなので、真ん中のスイッチの状態が IN1~IN3に入ることはありません。
■ キーマトリックススキャンの回路図
■キーマトリックススキャンのプログラム
では、実際に押された英数字がパソコン上に表示させるプログラムを書いてみよう。
●main.c
#include <stdio.h>
#include "iodefine.h"
#include "intrinsics.h"
#include "sci.h"
#ifdef CPPAPP
//Initialize global constructors
extern "C" void __main()
{
static int initialized;
if (! initialized)
{
typedef void (*pfunc) ();
extern pfunc __ctors[];
extern pfunc __ctors_end[];
pfunc *p;
initialized = 1;
for (p = __ctors_end; p > __ctors; )
(*--p) ();
}
}
#endif
unsigned char sw_prev[4]; // SWの直前のOn/Off状態
unsigned char buff[16];
unsigned char scan_cnt;
void initIO(void){
unsigned char i;
P1.DDR = 0xff;
P2.DDR = 0x00; // キー入力
P3.DDR = 0xff;
P4.DDR = 0xff;
P5.DDR = 0xff;
P6.DDR = 0xf0;
P8.DDR = 0xff;
P9.DDR = 0xff;
PA.DDR = 0xff;
PB.DDR = 0xff;
P2.PCR.BYTE = 0xff; // 下位4ビット:プルアップ有効
P1.DR.BYTE = 0xff;
for(i = 0; i < 4; i++){
P1.DR.BYTE = ~(1 << i);
sw_prev[i] = 0x0f & P2.DR.BYTE;
P1.DR.BYTE = 0xff;
}
scan_cnt = 0;
}
void initITU(void){
set_imask_ccr(1); // 割込み禁止
ITU0.TCR.BIT.CCLR = 1; // GRAコンペアマッチ-->TCNTクリア
ITU0.TCR.BIT.CKEG = 0; // 立ち上がりエッジでカウント
ITU0.TCR.BIT.TPSC = 2; // 分周比 0:φ 1:φ/2 2:φ/4 3:φ/8
ITU0.TCNT = 0;
ITU0.GRA = 62499; // 1/100秒でコンペアマッチ
ITU0.TSR.BIT.IMFA = 0; // フラグクリア
ITU0.TIER.BYTE = 1; // コンペアマッチA-->割込み
ITU.TSTR.BYTE = 1; // ITU0 START
set_imask_ccr(0); // 割込み許可
}
int main(void)
{
initIO();
initITU();
initSCI();
while (1) {
}
return 0;
}
// 左下が(0,0)
void keyTyped(int row, int col){
unsigned char code;
code = 4 * row + col;
sprintf(buff, "code = %X\n", code);
sci_puts(buff);
}
int keyScan(void){
unsigned char sw, sw_on;
int i, j, cnt;
cnt = 0;
for(i = 0; i < 4; i++){
P1.DR.BYTE = ~(1 << i);
sw = 0x0f & P2.DR.BYTE; // i行目をスキャン
P1.DR.BYTE = 0xff;
sw_on = (sw_prev[i] ^ sw) & ~sw; // 押されたスイッチを1
for(j = 0; j < 4; j++){
if (sw_on & (0x08 >> j)){
// i行 j列のキーが Off→On
cnt++;
keyTyped(i, j);
}
}
sw_prev[i] = sw; // i行目の状態を記憶しておく
}
return cnt;
}
/*
* 1/100秒 = 10ms間隔でコールされる。
*/
void onTimer(void){
scan_cnt++;
if (scan_cnt >= 4){
scan_cnt = 0;
keyScan();
}
}
●sci.c
#include "iodefine.h"
#include "sci.h"
void sci_putc(unsigned char ch){
while (SCI1.SSR.BIT.TDRE == 0)
;
SCI1.TDR = ch;
SCI1.SSR.BIT.TDRE = 0;
}
void sci_puts(unsigned char *s){
while (*s){
if (*s == '\n')
sci_putc('\r');
sci_putc(*s++);
}
}
unsigned char sci_read(void){
unsigned char c;
if ( (SCI1.SSR.BYTE & 0x78) == 0 )
return '\0'; // 入力なし
if (SCI1.SSR.BIT.RDRF == 1){
c = SCI1.RDR;
SCI1.SSR.BIT.RDRF = 0;
return c;
}
SCI1.SSR.BYTE &= 0x87; // エラーフラグクリア
return -1;
}
void initSCI(void){
SCI1.SCR.BIT.TE = 0; // 送信無効
SCI1.SCR.BIT.RE = 0; // 受信無効
SCI1.SCR.BIT.CKE = 0; // クロックソース:内部クロック
SCI1.SMR.BYTE = 0; // 調歩, キャラクタ8, パリティナシ, ストップ1, クロックφ
SCI1.BRR = 80; // 9600baud
SCI1.SSR.BYTE &= 0x87; // フラグクリア
SCI1.SCR.BIT.TE = 1; // 送信有効
SCI1.SCR.BIT.RE = 1; // 受信有効
}
●sci.h
#ifndef SCI_H_
#define SCI_H_
void initSCI(void);
void sci_putc(unsigned char ch);
void sci_puts(unsigned char *s);
unsigned char sci_read(void);
#endif /* SCI_H_ */
●inthandler.c
#include "inthandler.h"
#include "iodefine.h"
void INT_IMIA0(void) {
ITU0.TSR.BIT.IMFA = 0; // フラグクリア
onTimer();
}
■実際の完成写真