本帖最后由 LBQ691477940 于 2011-1-10 21:05 编辑  
 
 
非原创3个普通IO识别22个按键试验,感觉很实用故与大伙一起分享  实验用89S51作试验,
 电路接线就是P1.2,P1.3,P1.4接键盘,P1.0接显示器。 
 /*==================================================================*  
*                   3个IO接识别22键测试程序                         *  
*       ------------------------------------------------            *  
*       MCU:     AT89C2051                                          *  
*       OSC:     12M cysytel                                        *  
*       程序设计:Cowboy                                             *  
*       程序版本:V1.0                                               *  
*==================================================================*/  
 
#include <reg52.h>  
 
//================== IO口线连接 ==================  
sbit Bus          = P1^0;  
sbit IO_a         = P1^4;  
sbit IO_b         = P1^3;  
sbit IO_c         = P1^2;  
    
//================== 变量声明 ====================  
unsigned char Disp_buf[3];  
unsigned char Dig;  
unsigned char Key_count;  
unsigned char bdata Key_state;      
sbit KB0 = Key_state^0;  
sbit KB1 = Key_state^1;  
sbit KB2 = Key_state^2;  
sbit KB3 = Key_state^3;  
sbit KB4 = Key_state^4;  
sbit KB5 = Key_state^5;  
 
//================== 表格数据 ====================  
code unsigned char LED_font[24]=  
{  
        0x84,0x9f,0xa2,0x8a,0x99,0xc8,0xc0,0x9e,0x80, //012345678  
        0x88,0x90,0xc1,0xe4,0x83,0xe0,0xf0,0xff,0xfb, //9abcdef -  
};  
 
code unsigned char Key_tab[64]=     //键码映射表  
{//  0  1  2  3  4  5  6  7  8  9     
        22, 0, 2, 0, 0, 0, 0, 0, 4, 0, //0  
         0, 0, 0, 0, 0,18, 0, 0, 0, 0, //1X  
         0, 0, 0, 0, 0, 0, 3,14, 0, 0, //2X  
        20,10, 6, 0, 0, 0, 0, 0, 1,19, //3X  
         0, 5, 0, 0, 0,15, 0,11, 0, 0, //4X  
         0,17, 0, 0,13, 8, 0,21, 0, 9, //5X  
        16,12, 7, 0                    //6X  
};  
 
//=============== 检测按键 =================  
void Key_scan()  
{     
    unsigned char i;  
    Key_count --;                        //扫描次序  
    Key_count &= 3;  
    switch (Key_count)                //按次序处理  
    {  
        case 2:                                //第一轮扫描  
        KB0 = IO_b;   
        KB1 = IO_c;   
        IO_a = 1;  
        IO_b = 0;  
        break;  
      
        case 1:                                //每二轮扫描  
        KB2 = IO_c;  
        KB3 = IO_a;  
        IO_b = 1;  
        IO_c = 0;  
        break;  
      
        case 0:                                //每三轮扫描  
        KB4 = IO_a;  
        KB5 = IO_b;  
        IO_c = 1;  
        break;  
      
        default:                        //每四轮扫描  
        if (!IO_a) KB0 = 0;  
        if (!IO_b) KB2 = 0;  
        if (!IO_c) KB4 = 0;  
        IO_a = 0;  
 
        //======更新显示缓冲区=======  
        i = Key_tab[Key_state];  
        if (i == 0)  
        {  
            Disp_buf[2] = 0x11;                //显示三横  
            Disp_buf[1] = 0x11;  
            Disp_buf[0] = 0x11;  
        }  
        else  
        {  
            Disp_buf[2] = 0x0c;     //字符"C"  
            Disp_buf[1] = i / 10;   //键码十位  
            Disp_buf[0] = B;于      //键码个位  
        }  
        Key_state = 0;  
    }  
}      
 
      
/*===================================================================  
                    ONE WIRE 显示总线驱动程序         
===================================================================*/  
 
//=============== 发送一位 =================  
void Send_bit(bit Dat)      
{      
    unsigned char i = 3;  
    if (!Dat) Bus = 0;  
    else  
    {  
        Bus = 0;  
        Bus = 1;  
    }  
    while(--i);                 //延时8us      
    Bus = 1;  
}      
 
//=============== 总线驱动 =================  
void Bus_drive()  
{  
    unsigned char i = 0;  
    unsigned char Sdat;  
    Send_bit(1);                        //Bit6消隐  
    do Bus = 1; while(--i);             //延时768us  
    do Bus = 0; while(--i);             //延时768us  
    Bus = 1;  
    Sdat = LED_font[Disp_buf[Dig++]];   //获取显示数据  
    Send_bit(Sdat & 0x01);              //发送位0          
    Send_bit(Sdat & 0x02);              //发送位1          
    Send_bit(Sdat & 0x04);              //发送位2          
    Send_bit(Sdat & 0x08);              //发送位3          
    Send_bit(Sdat & 0x10);              //发送位4          
    Send_bit(Sdat & 0x20);              //发送位5          
    Send_bit(Dig  & 0x01);              //发送位选1          
    Send_bit(Dig  & 0x02);              //发送位选2  
    while(--i);                         //延时512us  
    Send_bit(Sdat & 0x40);              //发送位6  
    for (i = 7;i> 0;i--) Send_bit(1);  //位6移至Dout  
    if (Dig == 3) Dig = 0;  
}       
          
/*===================================================================  
                    延时 5ms 程序         
===================================================================*/  
void Delay_5ms()          
{      
    while(!TF1);      
    TF1 = 0;      
    TH1 = (- 5000) / 256;  
    TL1 = (- 5000) % 256;  
}      
 
/*===================================================================  
                        主程序         
===================================================================*/  
void main()  
{  
    TMOD = 0x10;            //定时器1,16位模式  
    TCON = 0xc0;            //TR1=1;TF1=1;  
    while(1)                //主循环  
    {  
        Bus_drive();        //显示总线驱动   
        Key_scan();         //检测按键  
        Delay_5ms();        //延时5MS      
    }  
}作为试验目的,没有接按键,只焊了个键盘框架,用镊子短路相应的节点来当按键。图中接二极管阵列的三根线是3个IO,单独的一根是地线。MCU发送串行数据给HC595驱动数码管作键码显示。 对于这种方式的按键识别方法,很多朋友担心编程会很复杂,其实仔细分析后也很简单.比如上面例子,其本的思路是依次把三个IO拉低,然后记录另外两个IO的状态,最后三个IO都不下拉,再记录一次,就可得出的结果.对于按下不同的按键,就有不同的结果.如果只扫18键,那么最后一次扫描可以省掉,即扫描三次即可.实际应用时5MS的扫描间隔可以用定时中断来实现,这样就只占用很少的MCU时间.   |