原理:利用數組分壓+AD采集;
優點:一個IO口可以做成多個按鍵,節省IO口(矩陣鍵盤在>4時優點才能體現出來);可備用作為AD基准輸入。
缺點:不能做成組合按鍵(或者電阻要精確選擇);且離IO口越近優先級越高。按鍵的識別收到精度的影響(消兜:抖動時間幾毫秒到幾十毫秒,所以連續讀4次(每次8ms)直到讀到值都相同。按鍵的識別是靠AD值的容差范圍而非具體的AD值來識別)。基准電壓的獲得(IO或TL431)
參考http://www.ednchina.com/ART_46350_11_0_OA_6f4d5e96.HTM
http://blog.sina.com.cn/s/blog_7a9b7c4c0100sohh.html
http://wenku.baidu.com/link?url=-vUPz14ryQnsrXNIJdfbOn1qw1JsJqIFRG9VUhxbaGjy80GEzZz8judHw1WRubzAsb-KOUzGfZQ-zVpOKu2PVH-SvRerysWsd-F_kTzivwS
--------------------------矩陣按鍵程序---------------------------------------------
矩陣鍵盤是否接上拉電阻:網友說法對於哪些弱上拉驅動能力弱的准3態IO的需要加,為了抗干擾也需要加;否則就不需要加。
/**************************************************************
*按鍵的鍵值分布圖:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
***************************************************************/
/**************************************************************
* 功能:P3外接4×4按鍵, 按照查表法讀出鍵值,使用時需要添加延時消抖動
* 返回:按鍵值0~15/如無鍵按下, 返回16
***************************************************************/
/*uchar keyscan(void)
{
uchar code K_Tab[4][4] = {
0xee, 0xde, 0xbe, 0x7e, //掃描碼為0xfe時(僅第一行為0)第一行4列4個按鍵可能按下時的值
0xed, 0xdd, 0xbd, 0x7d, //掃描碼為0xfd時(僅第二行為0)第二行4列4個按鍵可能按下時的值
0xeb, 0xdb, 0xbb, 0x7b,
0xe7, 0xd7, 0xb7, 0x77};
uchar temp1 = 0xfe, temp2, i, j;
for(i = 0; i < 4; i++)
{ //掃描低四位
P3 = temp1; //輸出一行0
temp2 = P3; //馬上就讀入
if((temp2 & 0xf0) != 0xf0) //如果有鍵按下
{
for(j = 0; j < 4; j++) //就掃描高四位
if(temp2 == K_Tab[i][j]) //查表
return i * 4 + j; //查到了就返回按鍵的數值
}
else temp1 = _crol_(temp1, 1);//非51的循環移位處理:(uchar)~(1<<i)
}
return 16; //沒有查到,返回按鍵松開的代碼
} */ //呵呵,實質性的語句不過9行,就是這么簡練!
/**************************************************************
* 功能: 按照反轉計算法讀出鍵值(IO必須是雙向的或能切換),不用循環結構
* 輸出:按鍵值0~15/如無鍵按下, 返回16
***************************************************************/
uchar keyscan(void)
{
uchar temH, temL,hang,lie;
uchar key_value;
P3 = 0xf0;
if(P3 != 0xf0)
{
delay(5); //消抖
if(P3 != 0xf0) //的確是有按鍵被按下
{
P3 = 0xf0; temH = P3; //低四位先輸出0;讀入,高四位含有按鍵信息
P3 = 0x0f; temL = P3; //然后反轉輸出0;讀入,低四位含有按鍵信息
switch(temH) {
case 0xe0: lie = 0; break;
case 0xd0: lie = 1; break;
case 0xb0: lie = 2; break;
case 0x70: lie = 3; break;
default: lie = 16;//按下的不是上述按鍵,就當是沒有按鍵
}
switch(temL)
{
case 0x0e: hang = 0; break;
case 0x0d: hang = 1; break;
case 0x0b: hang = 2; break;
case 0x07: hang = 3; break;;
default: hang = 16;//按下的不是上述按鍵,就當是沒有按鍵
}
retrun (hang -1)*4 + lie;
}
}
}
定時器的消抖法(裸奔程序):初始化keyvalue=一個不可能存在的值。
1當有按鍵按下時(keyolder),啟動定時器定時10MS(systick),然后退出;
2定時到后置位Flag_10MS,並讀按鍵值keynew
3if((Flag_10MS)&&(keyoder==keynew)) keyvalue=keynew;
4鍵值使用:
if(keyvalue!=一個不可能存在的值)
{
....
}
獨立按鍵的經典算法:可以簡潔的識別長按和短按,對於彈式按鍵和鎖式按鍵都適合(鎖式相當於長按)
PB0表示第一個按鍵
長按(2s):ReadData = 0x01;Trg = 0x00;Cont = 0x01;
短按:ReadData = 0x01;Trg = 0x01;Cont = 0x01;
無按鍵或彈起:ReadData = 0x00;Trg = 0x00;Cont = 0x00;
http://blog.csdn.net/caiyunfreedom/article/details/6543256#comments
核心算法:
unsigned char Trg;
unsigned char Cont;
void KeyRead( void )/*每20MS調用一次*/
{
unsigned char ReadData = PINB^0xff; // 1
Trg = ReadData & (ReadData ^ Cont); // 2
Cont = ReadData; // 3
}
http://www.cnblogs.com/UPUPDay2152/p/9673886.html
分享一個mini板按鍵KEY0單擊、雙擊(連續返回2個相同的值)、長按和串口實現燈的控制狀態
http://www.openedv.com/forum.php?mod=viewthread&tid=274729&extra=
unsigned char key_driver(void)
{
static unsigned char key_state = key_state_0, key_time = 0;
unsigned char key_press, key_return = N_key;
key_press = KEY0; // 讀按鍵IO電平
switch (key_state)
{
case key_state_0: // 按鍵初始態
if (!key_press) key_state = key_state_1; // 鍵被按下,狀態轉換到按鍵消抖和確認狀態
break;
case key_state_1: // 按鍵消抖和確認狀態
if (!key_press) //按鍵仍然處於按下
{
key_time = 0;
key_state = key_state_2; //,消抖完成,狀態轉換到按下鍵時間的計時
}
else
key_state = key_state_0; //按鍵已抬起,轉換到按鍵初始態。此處完成和實現軟件消抖,此時
break; //按鍵的按下和釋放都在此消抖
case key_state_2:
if(key_press)
{
key_return = S_key; // 此時按鍵的釋放,說明是產生一次短操作,回送S_key
key_state = key_state_0; // 轉換到按鍵的初始態
}
else if (++key_time >= 100) // 繼續按下,計時10ms
{
key_return = L_key; // 按下時間>1000ms,此按鍵為長按操作,返回長按操作
key_state = key_state_3; // 轉換到等待按鍵釋放狀態
}
break;
case key_state_3: // 等待按鍵釋放狀態,此狀態只返回無按鍵事件
if (key_press) key_state = key_state_0; //按鍵已釋放,已轉換到按鍵初始態
break;
}
return key_return;
}
/*=============
在定時器10ms中斷中設立標志,在主程序中查詢標志,到了就執行該操作(即10ms執行一次)
===============*/
unsigned char key_read(void)
{
static unsigned char key_m = key_state_0, key_time_1 = 0;
unsigned char key_return = N_key,key_temp;
key_temp = key_driver();
switch(key_m)
{
case key_state_0:
if (key_temp == S_key )
{
key_time_1 = 0; // 第一次單擊,不返回,到下個轉態判斷后面是否出現雙擊
key_m = key_state_1;
}
else
key_return = key_temp; // 對於無鍵、長按,返回原事件
break;
case key_state_1:
if (key_temp == S_key) // 又一次單擊(間隔時間<500ms)
{
key_return = D_key; // 返回雙擊鍵事件,回初始狀態
key_m = key_state_0;
}
else
{ // 這里500ms內肯定讀到的都是無鍵事件,
if(++key_time_1 >= 50) //因為長按>1000ms,低層返回都是無按鍵
{
key_return = S_key; // 500ms內沒有再次出現單鍵事件,返回上一次的單鍵
key_m = key_state_0; // 返回初始狀態
}
}
break;
}
return key_return;
}
思考:外中斷按鍵的消抖。