51实验思考题目<2-4>

很长时间没有更新那必然是有原因的,比如憋大招。
本次将剩下所有实验的思考题全部发布
实验二
自行设计实验线路并编制程序:要求在无外部中断时最右一位发光二极管点亮。请编程实现:当外部中断0请求中断时发光二极管左移;当外部中断1请求中断时发光二极管闪烁。
电路图
代码如下:

#include<reg52.h>
sbit LSA=P1^5;					
sbit LSB=P1^6;
sbit LSC=P1^7;
void EXint_Init(void);
void delayms(unsigned int);
void led1();		        
void led2();				
void main()
{
	EXint_Init();	
	LSA=0;
	LSB=0;
	LSC=0;						
	while(1)					
	{      
		P0 = 0xfe;					 
	}                         
}
void EXint_Init()              
{
	IT0=1;                       
	IT1=0;                      
	EA=1;                        
	EX0=1;                       
	EX1=1;                      
}
void delayms(unsigned int xms)
{
	unsigned int i,j;
	for(i = xms;i > 0;i--)
		for (j = 110;j > 0;j--);
}
void EXINT0() interrupt 0     	
{
	led1();					
}
void EXINT1() interrupt 2       
{
	led2();					
}
void led1() 				 
{	
	    int i;
	    P0=0xfe;
for(i=0;i<8;i++)
			{
		delayms(500);
		P0=P0<<1|0x01;
			}
}
void led2() 		
{	
		P0 = 0xfe;	
	    delayms(500);
	    P0 = 0xff;	
	    delayms(500);
}

思考题
1.51 单片机的中断系统由哪几个特殊功能寄存器组成?
2.中断函数与函数调用有何不同?
3.如果 INT0 和 INT1 的优先级都设置成 0,但我们知道中断号越小,优先级越高,即 INT0 的自然优先级高于 INT1,请修改程序,先按下 KEY1,在 LED 的闪烁过程中按下 KEY0,看能否打断 LED的闪烁?原因是什么?
1.中断允许寄存器IE、定时器/计时器T0和T1控制寄存器TCON、定时器/计时器T2控制寄存器T2CON、串口控制寄存器SCON
2.子函数的调用时间是已知的,而中断是时时刻刻都有发生的可能;也就是说子函数的发生时间是程序员自己设定好的,而中断是与工作环境有关的。详细的解释请点击以下超链接:
中断函数与子函数的不同
3.由于上边的程序没有设置优先级,所以不需要修改代码。当先按下 KEY1,在 LED 的闪烁过程中按下 KEY0,发现LED闪烁被打断。原因是因为T0的优先级高于T1,所以T1的中断任务能被T0的中断任务打断。
实验三(上)
编程实现8段数码管的动态扫描显示,要求4个数码管从左到右分别显示“1”、“2”、“3”、“4”、及“A”、“b”、“C”、“d”。
在这里插入图片描述
代码如下:

#include <reg52.h>
sbit LSA=P1^5; 
sbit LSB=P1^6; 
sbit LSC=P1^7;
unsigned char code smgduan[]=
{0xf9,0xa4,0xb0,0x99,0x88,0x83,0xc6,0xa1};
//共阳极数码管显示1234 ABCD
void delayms(unsigned int xms);
void smgscan(unsigned char wei,unsigned char display);
void main()
{
	unsigned int  i,j;
	i=500;
	while(i--)
	{
		for (j=0;j<4;j++)
		{
		smgscan(j,smgduan[j]);    //数码管显示
		delayms(1);         //间隔1ms扫描	
		smgscan(j,0xff);
		LSA=1;LSB=1;LSC=1;       //消隐
		}
	}
	i=500;
	while(i--)
	{
		for (j=0;j<4;j++)
		{
		smgscan(j,smgduan[j+4]);  //数码管显示
		delayms(1);         //间隔1ms扫描
		smgscan(j,0xff);
LSA=1;LSB=1;LSC=1;       //消隐
		}
	}
}
void smgscan(unsigned char wei,unsigned char display)
 {
 unsigned char i;
 for(i=0;i<=wei;i++)
    {		
switch(wei)	     //位选
		{
		case(0):
          LSA=0;LSB=0;LSC=1; break;//显示第0位
		case(1):
		  LSA=1;LSB=1;LSC=0; break;//显示第1位
		case(2):
		  LSA=0;LSB=1;LSC=0; break;//显示第2位
		case(3):
		  LSA=1;LSB=0;LSC=0; break;//显示第3位			}
		P0=display;//发送段码
    }	
 }
 void delayms(unsigned int xms)
{
	unsigned int i,j;
	for(i = xms;i>0;i--)
	for(j = 110;j>0;j--);
}

思考题
1.何谓数码管的动态显示,其原理是什么?
2.若要实现秒表从 0.0s 秒到 9.9s 循环显示,请写出实现的思路或代码?
1.动态显示驱动:数码管动态显示接口是单片zhi机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是那个数码管会显示出字形,取决于单片机对位选通COM端电路的控制(以上代码中switch函数部分),所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
2.首先我们用定时器产生10ms的定时函数,然后用一个变量记录发生中断的次数,中断发生10次则为100ms=0.1s,此程序可以看作0-99倒计时,但是十位和个位之间有一个点,所以可以定义两个数组,一个用来显示秒,另一个为毫秒。
代码如下:

#include <reg52.h>
sbit LSA=P1^5;  			
sbit LSB=P1^6; 			
sbit LSC=P1^7;
unsigned int cnt=0;
signed char sec;
signed char sec_all=99;
signed char count=0;
unsigned char m[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x078,0x00,0x10};
unsigned char mm[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0x0f8,0x80,0x90};
void timer10ms()
{
	TMOD = 0x00; 		         //清空T0控制位
	TMOD = 0x01; 		         //设置T0模式为01
	TL0 = -9216%256;            //计数值装载
	TH0 = -9216/256;
	ET0 = 1;                     //使能T0中断
}
void Timer0() interrupt 1
{
	TH0 = -9216/256; 	
	TL0 = -9216%256;
	cnt++; 		                //中断次数加一
	if (cnt >= 10)              //中断50次=1s
	{
		cnt = 0;
		sec--; 
		if (sec < 0)            //当显示数值到0时,闪烁
		{
			sec = sec_all;
		}
	}
}
void ledscan(unsigned char wei,unsigned char display)
{
    unsigned char i;
    for(i=0;i<=wei;i++)
    {	
	P0=display;	
	switch(wei)	                  //位选
	{
		case(0):
		LSA=1;LSB=0;LSC=0; break; //显示第0位
		case(1):
		LSA=0;LSB=1;LSC=0; break; //显示第1位
		case(2):
		LSA=1;LSB=1;LSC=0; break; //显示第2位
		case(3):
		LSA=0;LSB=0;LSC=1; break; //显示第3位
	}
	P0=0xff;LSA=1;LSB=1;LSC=1;//消隐	                  			
    }	
}
void main(void)
{
	LSA = 0;
    LSB = 0;
    LSC = 0;
	cnt = 0;                 		
	sec = sec_all;                		
	timer10ms();	
	EA=1;                                   
	IT0=0; 
	EX0=1;    	
    TR0=1;	
    while(1)
    {
		ledscan(0,mm[sec%10]);   //显示个位
		ledscan(1,m[sec/10]);   //显示十位	
    }
}

实验三(下)59秒倒计时
编写实验程序,实现59秒倒计时功能,初始状态数码管最后两位显示数字“59”,要求定时器0每10ms产生一次中断,在定时中断服务程序里进行计数,计数满一秒,数码管显示相应减1,倒计时结束时,数码管显示“00”,并以周期1秒进行闪烁;其中一个按键实现复位,即按下后马上回到显示“59”,另一个按键实现暂停和启动功能,即按一下后暂停,再按一下后启动。
代码如下:

#include <reg52.h>
sbit LSA=P1^5;  			
sbit LSB=P1^6; 			
sbit LSC=P1^7;
unsigned int cnt=0;
signed char sec;
signed char sec_all=59;   //计数器初值
signed char count=0;
unsigned char smgduan[]={0xc0,0xf9,0xa4,0xb0,
0x99,0x92,0x82,0x0f8,0x80,0x90};
void timer10ms()
{
	TMOD = 0x00; 		     //清空T0控制位
	TMOD = 0x01; 		     //设置T0模式为01
	TL0 = -9216%256;            //计数值装载
	TH0 = -9216/256;
	ET0 = 1;                     //使能T0中断
}
void EXINT0() interrupt 0     	
{
	count++;				
}
void Timer0() interrupt 1
{
TH0 = -9216/256; 	
	TL0 = -9216%256;
	cnt++; 		                //中断次数加一
	if (cnt >= 100)              //中断100次=1s
	{
		cnt = 0;
		sec--; 
		if(sec<0)      //当显示数值到0时,闪烁
		{
			sec = 0;
		}
	}
}
void ledscan(unsigned char wei,unsigned char display)
{
unsigned char i;
for(i=0;i<=wei;i++)
    {	
P0=display;	                   //显示
	switch(wei)	                  //位选
{
		case(0):
		LSA=1;LSB=0;LSC=0; break;  //显示第0位
		case(1):
		LSA=0;LSB=1;LSC=0; break; //显示第1位
		case(2):
		LSA=1;LSB=1;LSC=0; break; //显示第2位
		case(3):
		LSA=0;LSB=0;LSC=1; break; //显示第3位
	}
	P0=0xff;LSA=1;LSB=1;LSC=1;   //消隐	               
    }	
}
void main(void)
{
	LSA = 0;
    LSB = 0;
    LSC = 0;
	cnt = 0;                 		
	sec = sec_all;                		
	timer10ms();
EA=1;                                   
	IT0=0; 
	EX0=1;	  				
    TR0=1; 
    while(1)
    {
	if(count%2==0)  //按键控制定时器开启和关闭
		TR0 = 1;
	else
		TR0 = 0;
	if((cnt>50)&&(sec==0))
	{
		ledscan(0,0xff);    	//显示十位
		ledscan(1,0xff);    	//显示个位	
	}
	else
	{
ledscan(0,smgduan[sec%10]); //显示十位
		ledscan(1,smgduan[sec/10]); //显示个位   
	}  
    }
}

实验四:对4×4矩阵式键盘电路的键值进行编码,编程实现在LCD液晶显示器上显示每个按键的“0-F”序号
在这里插入图片描述
代码如下:

#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
//LCD使能初始化
sbit lcden=P1^2;
sbit lcdrs=P1^0;
sbit rw=P1^1;
//矩阵键盘IO初始化
sbit KEY_IN_1=P2^4; 
sbit KEY_IN_2=P2^5; 
sbit KEY_IN_3=P2^6;  
sbit KEY_IN_4=P2^7;  
sbit KEY_OUT_1=P2^0;  
sbit KEY_OUT_2=P2^1;  
sbit KEY_OUT_3=P2^2;  
sbit KEY_OUT_4=P2^3;  
//LED使能初始化
sbit LSA=P1^5;  
sbit LSB=P1^6;  
sbit LSC=P1^7;  
 //变量定义
unsigned char disBuf=0;
uchar table1[] = "Welcome To CSLG!";
uchar table2[] = "0123456789ABCDEF";
uchar table3[] = "                ";
uchar num; 
const unsigned char KeyCodeMap[4][4] = { 
	//矩阵按键到标准码的映射表
    { '0',  '1',  '2', '3' },  //0、1、2、3
    { '4',  '5',  '6', '7' },  //4、5、6、7
    { '8',  '9',  'a', 'b' }, //8、9、a、b
    { 'c',  'd',  'e', 'f' } // c、d、E、F
}; 
unsigned char KeySta[4][4] = {  
	//全部矩阵按键的当前状态
     {1, 1, 1, 1}, 
     {1, 1, 1, 1},  
     {1, 1, 1, 1},  
     {1, 1, 1, 1}
};
void KeyScan();
void KeyDriver();
void KeyAction(unsigned char keycode); 
void KeyDriver()
{
    unsigned char i, j;
    static unsigned char backup[4][4] = {  
		//按键值的临时储存区
        {1, 1, 1, 1},  
        {1, 1, 1, 1},  
        {1, 1, 1, 1}, 
        {1, 1, 1, 1}
};   
	 //连续4次扫描值为0,即在4*4ms内都是按下状态,可认为按键稳定按下
    for (i=0; i<4; i++)  
    {
        for (j=0; j<4; j++)
        {
            if (backup[i][j] != KeySta[i][j])
				//判断按键是否摁下    
            {
                if (backup[i][j] != 0) //按键按下          
                {
                    KeyAction(KeyCodeMap[i][j]);  //数码管显示
                }
                backup[i][j] = KeySta[i][j];   
				//将前一次的按键值储存在临时储存区
            }
        }
    }
}
//按键扫描
void KeyScan()
{
    unsigned char i;
    static unsigned char keyout = 0;   
    static unsigned char keybuf[4][4] = {
		//矩阵案件扫描缓冲区
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF}
    };
	//将键值移入缓冲区
    keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
    keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
    keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
    keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
    //消抖
    for (i=0; i<4; i++)  
    {
        if ((keybuf[keyout][i] & 0x0F) == 0x00)
        {   //连续4次扫描值为0,即在4*4ms内都是按下状态,可认为按键稳定按下
            KeySta[keyout][i] = 0;
        }
        else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
        {   //连续4次扫描值为1,即在4*4ms内都是弹起状态,可认为按键稳定弹起
            KeySta[keyout][i] = 1;
        }
    }  
    keyout++; //标志位递加       
    keyout &= 0x03;  //到4归0
    switch (keyout)  //根据标志位,拉高当前输出引脚,并将下一引脚拉低
    {
        case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
        case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
        case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
        case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
        default: break;
    }
}
 //LCD显示
void KeyAction(unsigned char keycode)
{	
	disBuf = keycode;	
}
 //延时函数
void delay(uint z) {
    uint x, y;
    for(x = z; x > 0; x--)
        for(y = 110; y > 0; y--);
}
//写数据功能使能
void write_com(uchar com) {
    lcdrs = 0;
    P0 = com;
    delay(5);
    lcden = 1;
    delay(5);
    lcden = 0;
}
 //写数据
void write_data(uchar date) {
    lcdrs = 1;
    P0 = date;
    delay(5);
    lcden = 1;
    delay(5);
    lcden = 0;
}
 //初始化
void init() {	
    rw = 0;
    lcden = 0;
    write_com(0x38);
    write_com(0x0e);
    write_com(0x06);
    write_com(0x01);
}
void main() {
    init();
    write_com( 0x00 | 0x80 );
    for(num = 0; num < 16; num++) {
        write_data(table1[num]);//显示第一行
        delay(100);
    }
    write_com( 0x40 | 0x80 );
    for(num = 0; num < 16; num++) {
        write_data(table2[num]);//显示第二行
        delay(100);
    }
    write_com( 0x40 | 0x80 );
    for(num = 0; num < 16; num++) {
        write_data(table3[num]);//按键输入显示第二行
        delay(100);
    }    
    while(1)
    {
        KeyScan();
        KeyDriver();
        write_com( 0x40 | 0x80 );
        write_data(disBuf);
    }
}

思考题:

  1. LCD 显示改为数码管显示(选做);
  2. 若要实现数字键 0~9 按下后,直接在 LCD 上显示数字,如何修改程序?
    1.代码如下:
#include <reg52.h>

/* IO引脚分配定义 */
sbit KEY_IN_1  = P2^4;  //矩阵按键的扫描输入引脚1
sbit KEY_IN_2  = P2^5;  //矩阵按键的扫描输入引脚2
sbit KEY_IN_3  = P2^6;  //矩阵按键的扫描输入引脚3
sbit KEY_IN_4  = P2^7;  //矩阵按键的扫描输入引脚4
sbit KEY_OUT_1 = P2^0;  //矩阵按键的扫描输出引脚1
sbit KEY_OUT_2 = P2^1;  //矩阵按键的扫描输出引脚2
sbit KEY_OUT_3 = P2^2;  //矩阵按键的扫描输出引脚3
sbit KEY_OUT_4 = P2^3;  //矩阵按键的扫描输出引脚4

sbit LSA = P1^5;  //LED位选译码地址引脚A
sbit LSB = P1^6;  //LED位选译码地址引脚B
sbit LSC = P1^7;  //LED位选译码地址引脚C

unsigned char disBuf,disBuf1=0;
unsigned char code smgduan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void ConfigTimer0();
void KeyAction(unsigned char);

const unsigned char code KeyCodeMap[4][4] = {  //矩阵按键到标准键码的映射表
    { '1',  '2',  '3', 0x41 },  //数字键1、数字键2、数字键3、向上键
    { '4',  '5',  '6', 0x42 },  //数字键4、数字键5、数字键6、向左键
    { '7',  '8',  '9', 0x43 },  //数字键7、数字键8、数字键9、向下键
    { '0',  0x45, 0x46 , 0x44 }   //数字键0、ESC键、  回车键、 向右键
};

unsigned char pdata KeySta[4][4] = {  //全部矩阵按键的当前状态
    {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1} };

/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{
    unsigned char i, j;
    static unsigned char pdata backup[4][4] = {  //按键值备份,保存前一次的值
        {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1}
    };
    
    for (i=0; i<4; i++)  //循环检测4*4的矩阵按键
    {
        for (j=0; j<4; j++)
        {
            if (backup[i][j] != KeySta[i][j])    //检测按键动作
            {
                if (backup[i][j] != 0)           //按键按下时执行动作
                {
                    KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
                }
                backup[i][j] = KeySta[i][j];     //刷新前一次的备份值
            }
        }
    }
}
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
void KeyScan()
{
    unsigned char i;
    static unsigned char keyout = 0;   //矩阵按键扫描输出索引
    static unsigned char keybuf[4][4] = {  //矩阵按键扫描缓冲区
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF}
    };

    //将一行的4个按键值移入缓冲区
    keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
    keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
    keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
    keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
    //消抖后更新按键状态
    for (i=0; i<4; i++)  //每行4个按键,所以循环4次
    {
        if ((keybuf[keyout][i] & 0x0F) == 0x00)
        {   //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
            KeySta[keyout][i] = 0;
        }
        else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
        {   //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
            KeySta[keyout][i] = 1;
        }
    }
    //执行下一次的扫描输出
    keyout++;        //输出索引递增
    keyout &= 0x03;  //索引值加到4即归零
    switch (keyout)  //根据索引值,释放当前输出引脚,拉低下次的输出引脚
    {
        case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
        case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
        case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
        case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
        default: break;
    }
}
/*主函数*/
void main()
{
    EA = 1; 
	//选中数码管0进行显示
  	LSA = 1;
   	LSB = 0;
   	LSC = 0;
	ConfigTimer0();//定时器0定时时间为1ms
	disBuf = 0;
	P0 = smgduan[disBuf];
    while (1)
    {
		KeyDriver();  //按键扫描程序
		    P0 = smgduan[disBuf];  
	}
}

//*定时器0中断程序*/
void Timer0() interrupt 1 
{
	KeyScan();   //按键扫描
}
/* 配置并启动T0 */
void ConfigTimer0()
{
    TH0 = -921/256;  			//初值寄存器赋值
    TL0 = -921%256;
    TMOD  = 0x02;   //配置T0为模式2
    ET0 = 1;        //使能T0中断
    TR0 = 1;        //关闭T0,当检测到零点后才启动
}
/*按键处理程序,上下键增加、减少亮度,向右键进行模式切换*/
void KeyAction(unsigned char keycode)
{	 
	if(keycode >= 0x30 && keycode <= 0x3f)
	{
		disBuf = keycode - 0x30;	
	}
	else
	{
		disBuf=keycode-0x40+9;
	}
}	

2.代码如下:

#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
//LCD使能初始化
sbit lcden=P1^2;
sbit lcdrs=P1^0;
sbit rw=P1^1;
//矩阵键盘IO初始化
sbit KEY_IN_1=P2^4; 
sbit KEY_IN_2=P2^5; 
sbit KEY_IN_3=P2^6;  
sbit KEY_IN_4=P2^7;  
sbit KEY_OUT_1=P2^0;  
sbit KEY_OUT_2=P2^1;  
sbit KEY_OUT_3=P2^2;  
sbit KEY_OUT_4=P2^3;
const unsigned char KeyCodeMap[4][4] = { 
	//矩阵按键到标准码的映射表
    { '0',  '1',  '2', '3' },  //0、1、2、3
    { '4',  '5',  '6', '7' },  //4、5、6、7
    { '8',  '9',  'a', 'b' }, //8、9、a、b
    { 'c',  'd',  'e', 'f' } // c、d、E、F
}; 
unsigned char KeySta[4][4] = {  
	//全部矩阵按键的当前状态
     {1, 1, 1, 1}, 
     {1, 1, 1, 1},  
     {1, 1, 1, 1},  
     {1, 1, 1, 1}
};
void delay(uint z) {
    uint x, y;
    for(x = z; x > 0; x--)
        for(y = 110; y > 0; y--);
}
//写数据功能使能
void write_com(uchar com) {
    lcdrs = 0;
    P0 = com;
    delay(5);
    lcden = 1;
    delay(5);
    lcden = 0;
}
 //写数据
void write_data(uchar date) {
    lcdrs = 1;
    P0 = date;
    delay(5);
    lcden = 1;
    delay(5);
    lcden = 0;
}
 //初始化
void init() {	
    rw = 0;
    lcden = 0;
    write_com(0x38);
    write_com(0x0e);
    write_com(0x06);
    write_com(0x01);
}
void KeyDriver()
{
    unsigned char i, j;
    static unsigned char backup[4][4] = {  
		//按键值的临时储存区
        {1, 1, 1, 1},  
        {1, 1, 1, 1},  
        {1, 1, 1, 1}, 
        {1, 1, 1, 1}};   
	 //连续4次扫描值为0,即在4*4ms内都是按下状态,可认为按键稳定按下
    for (i=0; i<4; i++)  
    {
        for (j=0; j<4; j++)
        {
            if (backup[i][j] != KeySta[i][j])
				//判断按键是否摁下    
            {
                if (backup[i][j] != 0) //按键按下      				
                {  
                    write_com( 0x40 | 0x80 );
					write_data(KeyCodeMap[i][j]);
					delay(100);
				}
                backup[i][j] = KeySta[i][j];   
				//将前一次的按键值储存在临时储存区
            }
        }
    }
}
//按键扫描
void KeyScan()
{
    unsigned char i;
    static unsigned char keyout = 0;   
    static unsigned char keybuf[4][4] = {
		//矩阵案件扫描缓冲区
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF}
    };
	//将键值移入缓冲区
    keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
    keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
    keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
    keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
    //消抖
    for (i=0; i<4; i++)  
    {
        if ((keybuf[keyout][i] & 0x0F) == 0x00)
        {   //连续4次扫描值为0,即在4*4ms内都是按下状态,可认为按键稳定按下
            KeySta[keyout][i] = 0;
        }
        else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
        {   //连续4次扫描值为1,即在4*4ms内都是弹起状态,可认为按键稳定弹起
            KeySta[keyout][i] = 1;
        }
    }  
    keyout++; //标志位递加       
    keyout &= 0x03;  //到4归0
    switch (keyout)  //根据标志位,拉高当前输出引脚,并将下一引脚拉低
    {
        case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
        case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
        case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
        case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
        default: break;
    }
}
void main() {
    init();
	while(1)
    {
        KeyScan();
        KeyDriver();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值