电路如图,下面的例子用4个io实现16个按键的方案。
原理:
AVR单片机内部的IO上拉电阻大约在36K欧姆,初始化设置4列IO为输入,并启动内部上拉电阻。
当没有任何按键按下时,任何一个IO的输入电压为:
Vin =(1000K+2K)/(1000 K+2K+36K)×Vcc= 0.965×Vcc。
这个电压对于单片机来说算是高电平。
当其中一个按键按下后,该列的输出电压变成:
Vin =2K /(2K+36K)×Vcc= 0.052×Vcc。
这个电压对于单片机来说算是低电平。
单片机可以启动按键电平改变中断,或者扫描检测IO。当该IO由高变成低后,则说明该列有一个按键按下,单片机记录PC口的PIN值。
然后单片机取消内部的上拉电阻,并将变低的列输出高电平,输出管脚高电平为Vcc,其负载电阻为2K,与该2K电阻连接的1M电阻将另外一路输入变成高电平,其余为低电平。再次读取PC口的PIN值。根据2次读取得值来判断按键.
下面举一个具体的例子,说明一个按键的读取。
1、 初始化,4个IO设置成为输入,带内部上拉电阻。
2、 如按键9被按下,则PC1管脚变成低电平,单片机读取PC口,键值为1101。
3、 单片机取消内部上拉,并将PC1设置成为输出高电平,如果按键9仍然按下,则与按键9相连的2K电阻R3输出为高电平,与2K连接的1M电阻R7输出也为高,其他口由于1M和2K电阻的下拉则输出为低,单片机读取PC口,键值为0110。
4、 将键值1左移4位和键值2相加:键值为11010110,等于0xd6。
5、 根据键值表查到按键的相应键值。
键值表如下:
按键名称 键值 按键名称 键值
1 01111001 0x79 7 01111100 0x7c
2 10110101 0xb5 8 10110100 0xb4
3 11010011 0xd3 9 11010110 0xd6
a 11100001 0xe1 c 11100101 0xe5
4 01111010 0x7a * 01111000 0x78
5 10110110 0xb6 0 10111100 0xbc
6 11010010 0xd2 # 11011010 0xda
b 11100011 0xe3 d 11101001 0xe9
没有按键 11110000 0xf0
程序如下:
//winavr20060421
//last edit 2006/11/12
//copyright by zsmbj
#define KeyDir DDRC
#define KeyPort PORTC
#define KeyVal PINC
#define NoKey 0xff
unsigned char PROGMEM KEYTAB2[17][2]={
{0xf0,0xff},
{0x79,'1'},{0xb5,'2'},{0xd3,'3'},{0xe1,'a'},
{0x7a,'4'},{0xb6,'5'},{0xd2,'6'},{0xe3,'b'},
{0x7c,'7'},{0xb4,'8'},{0xd6,'9'},{0xe5,'c'},
{0x78,'*'},{0xbc,'0'},{0xda,'#'},{0xe9,'d'}
};
unsigned char GetPortkey2(void)
{
unsigned char i,getkey;
unsigned char temp_getkey = NoKey ;
KeyPort |= 0x0f; //key input with pullup
KeyDir &= 0xf0; //PC low 4 bit input
asm("nop");
asm("nop");
getkey = KeyVal & 0x0f; //get 4 bit
KeyPort &= 0xf0; //key input without pullup
KeyDir |= ~getkey; //
asm("nop");
asm("nop");
getkey <<= 4; //
getkey += KeyVal & 0x0f; //get 4 bit
KeyPort |= 0x0f; //key input with pullup
KeyDir &= 0xf0; //PC low 4 bit input
for(i=0;i<17;i++) //scan key value
{
if( getkey == pgm_read_byte(KEYTAB2[0]) )
{
temp_getkey= pgm_read_byte(KEYTAB2[1]); //get key value
break;
}
}
return temp_getkey;
}