一、基本知識
1. 按鍵分類與輸入原理
按鍵按照結構原理科分為兩類,一類是觸點式開關按鍵,如機械式開關、導電橡膠式開關燈;另一類是無觸點式開關按鍵,如電氣式按鍵,磁感應按鍵等。前者造價低,后者壽命長。目前,微機系統中最常見的是觸點式開關按鍵。
在單片機應用系統中,除了復位按鍵有專門的復位電路及專一的復位功能外,其他按鍵都是以開關狀態來設置控制功能或輸入數據的。當所設置的功能鍵或數字鍵按下時,計算機應用系統應完成該按鍵所設定的功能,鍵信息輸入時與軟件結構密切相關的過程。
對于一組鍵或一個鍵盤,總有一個接口電路與CPU相連。CPU可以采用查詢或中斷方式了解有無將按鍵輸入,并檢查是哪一個按鍵按下,將該鍵號送入累加器,然后通過跳轉指令轉入執行該鍵的功能程序,執行完成后再返回主程序。
2. 按鍵結構與特點
微機鍵盤通常使用機械觸點式按鍵開關,其主要功能式把機械上的通斷轉換為電氣上的邏輯關系。也就是說,它能提供標準的TTL邏輯電平,以便于通用數字系統的邏輯電平相容。機械式按鍵再按下或釋放時,由于機械彈性作用的影響,通常伴隨有一定的時間觸點機械抖動,然后其觸點才穩定下來。
其抖動過程如圖1所示,抖動時間的長短與開關的機械特性有關,一般為5-10ms。在觸點抖動期間檢測按鍵的通與斷,可能導致判斷出錯,即按鍵一次按下或釋放錯誤的被認為是多次操作,這種情況是不允許出現的。為了克服按鍵觸點機械抖動所致的檢測誤判,必須采取消抖措施。按鍵較少時,可采用硬件消抖;按鍵較多式,采用軟件消抖。
3. 獨立按鍵與矩陣鍵盤
(1)獨立按鍵
單片機控制系統中,如果只需要幾個功能鍵,此時,可采用獨立式按鍵結構。
獨立按鍵式直接用I/O口線構成的單個按鍵電路,其特點式每個按鍵單獨占用一根I/O口線,每個按鍵的工作不會影響其他I/O口線的狀態。獨立按鍵的典型應用如圖所示。獨立式按鍵電路配置靈活,軟件結構簡單,但每個按鍵必須占用一個I/O口線,因此,在按鍵較多時,I/O口線浪費較大,不宜采用。獨立按鍵如圖2所示。
獨立按鍵的軟件常采用查詢式結構。先逐位查詢與I/O口線的輸入狀態,如某一根I/O口線輸入為低電平,則可確認該I/O口線所對應的按鍵已按下,然后,再轉向該鍵的功能處理程序。
(2) 關于上拉電阻
單片機按鍵一般通過配備上拉電阻來實現輸入端高低電平的切換。
4條輸入線接到單片機的IO口上,當按鍵K1按下時,+5V通過電阻R1然后再通過按鍵K1最終進入GND形成一條通路,那么這條線路的全部電壓都加到了R1這個電阻上,KeyIn1這個引腳就是個低電平。當松開按鍵后,線路斷開,就不會有電流通過,那么KeyIn1和+5V就應該是等電位,是一個高電平。我們就可以通過KeyIn1這個IO口的高低電平來判斷是否有按鍵按下。
三、獨立按鍵實例編程
1.說明
以普中科技51單片機開發板為例
圖4為獨立按鍵電路圖 8個按鍵分別對應JP5的八個引腳,所有按鍵統一接地,按鍵之間互不影響,JP5中包含上拉電阻。當按鍵松開時,對應引腳輸入1;當按鍵按下時,對應引腳輸入0。
圖5為流水燈電路圖 8個LED燈接地共陰,當引腳輸出1時,LED燈亮;當引腳輸出0時,LED燈滅。
2.代碼實現
(1) 無消抖的8個引腳控制8個LED燈
#include <reg51.h>
#define Key P0 //P0接獨立按鍵電路引腳
#define Led P2 //P2接LED流水燈電路引腳
int main()
{
unsigned char i;
P2=0x00; //初始化流水燈全滅
while(1)
{
//動態掃描八個按鍵
for(i=0;i<8;i++)
{
if( 0 == (Key&(1<<i)) ) Led|=1<<i; //按鍵按下
else Led&=~(1<<i); //按鍵彈起
}
}
return 0;
}
(2) 通過按鍵控制單個數碼管計數并作消抖處理
具體要求:
- 數碼管初始化為0,按下按鍵增加對應的數,例如按下Key1則增加1,按下Key2則增加2
- Key8用于清零
- 超出9時,要做越界處理
- 消抖
- 按鍵抬起檢測 按一次只加一次
//printNum.h頭文件
#define Led P2 //P2口控制單個數碼管
#define state 1 //此處是共陽數碼管 所以置1
void printNum(int i)
{
//0123456789AbCDEF
unsigned char num[16]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
if(state==1) Led=num[i]; //Common yang
else Led=~num[i]; //Common yin
}
void delay_ms(unsigned int i)
{
unsigned int temp=i*100;
while(temp--) ;
}
#include <reg51.h>
#include "printNum.h"
#define Key P0
int main()
{
unsigned char i;
unsigned char count=0;
while(1)
{
printNum(count);
for(i=0;i<7;i++) //動態檢測8個按鍵
{
if( 0==(Key&(1<<i)) ) //判斷按鍵是否按下
{
delay_ms(150); //消抖
if( 0==(Key&(1<<i)) )
count+=i+1; //累加上對應的數
if(count>9) count%=10; //防止越界
printNum(count); //實時更新數字
while( !(Key&(1<<i)) ) ; //按鍵抬起檢測
}
}
if( 0 == (Key&(1<<7))) count=0; //最后一個鍵用于清零
}
}
(3)通過按鍵控制多個數碼管計數
具體要求:
- 功能:數碼管初始化為0,按下按鍵增加對應的數,例如按下Key1則增加1,按下Key2則增加2……但是按下Key8需清零
- 使用38譯碼器對COM口進行控制 節省I/O口
- P1的三個引腳控制38譯碼器,其他引腳上的值不允許被改動
- P2控制數碼管段碼端的給值
- P0控制檢測按鍵的輸入
- 按鍵要消抖 抬起要檢測 邊界要檢測
#define Led P2
#define state 0
void printNum(int i)
{
//0123456789AbCDEF
unsigned char num[16]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
if(state==1) Led=num[i]; //Common yang
else Led=~num[i]; //Common yin
}
void delay_ms(unsigned int i)
{
unsigned int temp=i*100;
while(temp--) ;
}
#include <reg51.h>
#include "printNum.h"
#define Key P0
#define LED_PLACE P1
unsigned char screenNum[8]={0,0,0,0,0,0,0,0};
int main()
{
unsigned char i;
unsigned char j;
unsigned long count=0;
unsigned long temp=0;
while(1)
{
LED_PLACE &= 0xf8; //Clear PLACE.0-2
if(count>99999999) count=0; //deal with the range
temp=count;
for(i=0;i<8;i++) //transfer long to arr
{
screenNum[7-i]=temp%10;
temp/=10;
}
for(i=0;i<8;i++) //give nums to screen
{
printNum(screenNum[i]);
j=100;
while(j--) ;
Led = 0x0; //remove the double image
LED_PLACE+=1; //control the place
}
for(i=0;i<7;i++) //scan the press keys
{
if( 0==(Key&(1<<i)) )
{
delay_ms(150);
if( 0==(Key&(1<<i)) )
count+=i+1;
while( !(Key&(1<<i)) ) ;
}
}
if( 0 == (Key&(1<<7))) count=0; //key8 to clear all
}
}
不足:按下按鍵時,數碼管全部熄滅,這是由于掉進按鍵檢測的死循環中,無法掃描動態數碼管。改進方法,等待學習中斷和定時器。
更新:
(3) 通過中斷來控制按鈕增加數碼管顯示
連線方式:
- P2接J12控制動態數碼管段碼端
- P1.0-2接J6-三八譯碼器 控制動態數碼管COM端
- P3.2接JP5-K1 INT0控制按鍵1
#define Led P2
#define state 0
void printNum(int i)
{
//0123456789AbCDEF
unsigned char num[16]={0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
if(state==1) Led=num[i]; //Common yang
else Led=~num[i]; //Common yin
}
void delay_ms(unsigned int i)
{
unsigned int temp=i*100;
while(temp--) ;
}
#include <reg51.h>
#include "printNum.h"
#define LED_PLACE P1
unsigned char screenNum[8]={0,0,0,0,0,0,0,0};
unsigned long count=0;
void exint0() interrupt 0 // P3.2
{
count++;
}
void initDevice()
{
IT0=1;
EX0=1;
EA=1;
}
int main()
{
unsigned char i;
unsigned char j;
unsigned long temp=0;
initDevice();
while(1)
{
LED_PLACE &= 0xf8; //Clear PLACE.0-2
if(count>99999999) count=0; //deal with the range of dital
temp=count;
for(i=0;i<8;i++) //transfer long to arr
{
screenNum[7-i]=temp%10;
temp/=10;
}
for(i=0;i<8;i++) //give nums to screen
{
printNum(screenNum[i]);
j=100;
while(j--) ;
Led = 0x0; //remove the double image
LED_PLACE+=1; //control the place
}
}
}
參考資料:
http://www.51hei.com/bbs/dpj-19896-1.html ——單片機論壇
http://blog.csdn.net/fanyuqa/article/details/48036529 ——CSDN fanyuqa博客