|
本帖最后由 liuyy 于 2015-1-11 14:55 编辑
1、效果图
2、矩阵键盘按键
3、代码
4、小结(矩阵键盘外部中断的小细节)
参考资料:实时时钟DS1302资料:http://www.51hei.com/bbs/dpj-22465-1.html
效果图
小时
日期
星期与温度
矩阵键盘按键
完整程序源代码下载:http://www.51hei.com/f/shizhongcx.rar
代码:
#include <reg51.h>
//管脚声明
sbit dm = P2^0; //段码
sbit wm = P2^1; //位码
sbit st = P2^2; //使能(RST)
sbit da = P2^3; //i/o管脚(数据管脚)(i/o)
sbit cl = P2^4; //时钟管脚(CLK)
sbit DQ = P2^5; //定义总线的I/O管脚
sbit ZD = P3^2;
//下面是一些需要提前声明的函数(起始可以不用)
void DelayUs2x(unsigned char t); //u延时
void DelayMs(unsigned char t); //m延时
void x_sj(); //原始数据还原
void d_sj(); //数码管显示切换函数
void sjse(); //设置时间年月日函数
void szjia(unsigned char *ls,unsigned char j,unsigned char i,bit l); //数值加或减函数
void sjqh(unsigned char *ls,unsigned char i,unsigned char j,unsigned char k); //设置,数码管缓存写入函数
void ntb(); //年同步函数
unsigned char jpsm(); //矩阵键盘反转扫描
void jpjm(unsigned char i); //矩阵键盘设置函数
void sm(); //数码管扫描显示函数
void smgql(); //数码管缓存清零函数
void csh_hc(); //切换初始化数码管缓存函数(这个函数的作用就是在切换时清理数码管的缓存)
void sjse(); //设置时间年月日函数
//下面是ds1302时钟芯片的低层函数
unsigned char d_1302(unsigned char addr,unsigned char l); //读取8个bit(参数1:地址,参数2:启用BCD处理)
void x_1302(unsigned char addr,unsigned char q,unsigned char l); //写入8个bit(参数1:地址,参数2:数据,参数3:启用BCD处理)
//下面是ds18b20温度传感器的函数
bit d18b20_qs(); //18b20 起始
void d18b20_x(unsigned char dat); //写 8 位 数 据
unsigned char d18b20_d(); //读 8 位 数 据
unsigned int wd(); //读取温度函数
void zh(unsigned int i); //数码管显示缓存写入函数
//下面的常量是一些寄存器的地址
#define addr_m 0x80 //秒
#define addr_f 0x82 //分
#define addr_x 0x84 //小时
#define addr_r 0x86 //日期
#define addr_y 0x88 //月
#define addr_xq 0x8a //星期
#define addr_n 0x8c //年
#define addr_xbh 0x8e //写保护
#define addr_ram 0xc0 //RAM 开始地址(这款芯片有31个8位的RAM起始地址是C0)
#define addr_ram2 0xc2
//下面的是一些用到的全局变量
unsigned char sz; //状态标志位
unsigned char hc[8]; //数码管缓存
unsigned char DM[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值0~9
unsigned char WM[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //位码。显示的位置
unsigned char sjhc[8]={0,0,15,23,12,1,13,20}; //秒,分,小时,日期,月,星期,低位年,高位年
//unsigned char addr[]={0x80,0x82,0x84,0x86,0x88,0x8c,0x8a}; //秒,分,小时,日期,月,年,星期
unsigned char zt[5]; //小时,分钟,秒 与 年,月,日的数据临时变量
unsigned char shijian[8]; //全局临时变量(用来干什么都可以理论上可以节省声明数组的时间)
void csh_wbzd_0() //初始化外部中断0
{
IT0=0; //外部中断0下降沿触发
EX0=1; //启用外部中断0
EA=1; //打开总中断
}
void wbzd_0() interrupt 0 //外部中断服务函数
{
if(!(sz & 0x80))
sz = sz |0x80; //有按键按下,把按键标志位 置 1
}
void DelayUs2x(unsigned char t) //延时
{
while(--t);
}
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延时1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
bit d18b20_qs() //18b20 起始
{
bit dat;
DQ = 1; //DQ复位
DQ = 0; //拉低总线
DelayUs2x(175);
DQ = 1; //拉高总线
DelayUs2x(45); //这里延时大概 45us
dat = DQ; //读取返回值(0:有18b20存在 1:是没有)
DelayUs2x(20); //这里延时大概 20us
return dat; //返回数值
}
void d18b20_x(unsigned char dat) //写 8 位 数 据
{
unsigned char i;
for(i=0;i<8;i++) //8位计数器
{
DQ = 0; //拉低总线
DQ = dat & 0x01; //取最低位赋值给总线
DelayUs2x(45); //延时45us
DQ = 1; //拉过总线准备写下一个数据(或者总线复位)
dat >>= 1; //数据右移一位
}
}
unsigned char d18b20_d() //读 8 位 数 据
{
unsigned char i,dat=0;
for(i=0;i<8;i++) //8位计数器
{
DQ = 0; //拉低总线
dat >>= 1; //数据右移一位
DQ = 1; //拉过总线(准备读取数据)
if(DQ) //判断是否是 1 如果是就把数据赋值给变量的高位
dat |= 0x80;
DelayUs2x(25); //这里延时大概 25us
}
return dat; //返回读取到数据数据
}
unsigned int wd() //读取温度函数
{
unsigned char i = 0; //低8位数据
unsigned char j = 0; //高8位数据
unsigned int k = 0; //无符号16整形用来存储读回来的 16位温度数据(j和i组合后的数据)
sm();
d18b20_qs(); //初始化
d18b20_x(0xCC); //跳过序列号的操作(因为18b20在总线上可以挂很多个,这个序列号和网卡MAC地址类似)
d18b20_x(0x44); //开启温度转换
sm();
sm();
sm();
d18b20_qs(); //初始化
d18b20_x(0xCC); //跳过序列号的操作(因为18b20在总线上可以挂很多个,这个序列号和网卡MAC地址类似)
sm();
d18b20_x(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
sm();
i = d18b20_d(); //读取低8位
j = d18b20_d(); //读取高8位
sm();
k = j;
k <<= 8;
k = k + i;
sm();
return k; //返回读取到的16位数据
}
void zh(unsigned int i) //数码管显示缓存写入函数
{
unsigned char x,z;
x = i & 0x0f; //取出小数
i >>=4;
z = i & 0xff; //取出整数
switch(x) //小数位写人显示缓存
{
case 0: hc[7]=DM[0];break;
case 1: hc[7]=DM[1];break;
case 2: hc[7]=DM[1];break;
case 3: hc[7]=DM[2];break;
case 4: hc[7]=DM[3];break;
case 5: hc[7]=DM[3];break;
case 6: hc[7]=DM[4];break;
case 7: hc[7]=DM[4];break;
case 8: hc[7]=DM[5];break;
case 9: hc[7]=DM[6];break;
case 10: hc[7]=DM[6];break;
case 11: hc[7]=DM[7];break;
case 12: hc[7]=DM[8];break;
case 13: hc[7]=DM[8];break;
case 14: hc[7]=DM[9];break;
case 15: hc[7]=DM[9];break;
}
x = z/10; //取出十位
hc[5]=DM[x];//十位缓存写入
x = z%10; //取出个位
hc[6] = DM[x] | 0x80;//个位缓存写入
}
/*下面这个函数是ds1302的最低层函数也是最重要的*/
void x_1302(unsigned char addr,unsigned char q,unsigned char l) //写入8个bit(参数1:地址,参数2:数据,参数3:启用BCD处理)
{
unsigned char i;
if(l) //BCD处理(处理成BCD格式)
{
i=q/10;
q=q%10;
q=q+i*16;
}
addr = addr & 0xFE; //最低位置零
cl = 0; //拉低时钟
da = 0; //复位数据
st = 1; //使能芯片。写入开始
for(i=0;i<8;i++) //写入地址
{
addr >>= 1;
da = CY;
cl = 1;
cl = 0;
}
for(i=0;i<8;i++) //写入数据
{
q >>= 1;
da = CY;
cl = 1;
cl = 0;
}
st = 0; //写入结束
}
/*下面这个函数是ds1302的最低层函数也是最重要的*/
unsigned char d_1302(unsigned char addr,unsigned char l) //读取8个bit(参数1:地址,参数2:启用BCD处理)
{
unsigned char i,dat;
addr = addr | 0x01;//最低位置高
cl = 0; //拉低时钟
da = 0; //复位数据
st = 1; //使能芯片,读取开始
for(i=0;i<8;i++) //写入地址
{
addr >>= 1;
da = CY;
cl = 1;
cl = 0;
}
for(i=0;i<8;i++) //读取数据
{
dat >>= 1;
if(da)
dat = dat | 0x80;
cl = 1;
cl = 0;
}
st = 0; //读取结束
if(l) //BCD处理(还原成10进制)
{
i = dat / 16;
dat = dat % 16;
dat = dat +i*10;
}
return dat;
}
/*以下函数就是反转扫描的精华*/
unsigned char jpsm() //矩阵键盘反转扫描
{
unsigned char i=0; //用于接收按键数值
P1 = 0x0f; //检测低4位
if(0x0f != P1) //检测按键是否按下
{
DelayMs(10); //去抖
if(0x0f != P1) //在次判断按键是否真的按下而不是其他干扰
{
i = P1; //把低4位赋值给i
P1 = 0xf0; //检测高4位
//DelayUs2x(5); //稍微延时。。这条语句可以去掉
i = i | P1; //把低4位和高4位组合成完整的按键数值
while( 0xf0 != P1 ) //检测按键是否松开
{
sm();
}
P1 = 0xf0; //这里是一个狠重要的经验(以为在检测低4位时赋值了0x0f,就是这个0x0f导致外部中断被无限的触发)
sz = sz & 0x7f;
return i; //返回按键数值
}
}
P1 = 0xf0; //这里是一个狠重要的经验(以为在检测低4位时赋值了0x0f,就是这个0x0f导致外部中断被无限的触发)
sz = sz & 0x7f;
return 0; //按键没有按下返回0
}
void sm() //数码管扫描显示
{
unsigned char i;
for(i=0;i<8;i++)
{
P0 = 0; //消影
dm = 1;
dm = 0;
P0 = WM[ i]; //写入位码
wm = 1;
wm = 0;
P0 = hc[ i]; //写入段码
dm = 1;
dm = 0;
DelayUs2x(10);
}
P0 = 0; //清空段码数据
dm = 1;
dm = 0;
}
void jpjm(unsigned char i) //矩阵键盘设置函数
{
switch(i)
{
case 0xe7: //矩阵键盘按键 (切换)
{
switch((sz & 0x03))
{
case 0x00: //如果当前是时间模式则切换到日期模式模式
{
ntb();
sz++; //把模式设置成日期格式
csh_hc(); //清理数码管缓存
break;
}
case 0x01: //如果当前是日期模式则切换到时间模式
{
sz++; //把模式设置成时间格式
csh_hc(); //清理数码管缓存
break;
}
case 0x02:
{
sz = sz & 0xfc; //把模式设置成时间格式
csh_hc();
break;
}
}
break;
}
case 0xdb:
{
sjse(); //调用设置函数
csh_hc(); //初始化数码管显示缓存
break;
}
case 0xb7: //矩阵键盘 。还原原始时间
{
x_sj();
csh_hc();
break;
}
}
}
void x_sj() //原始数据还原
{
unsigned char i,j=addr_m;
x_1302(addr_xbh,0,0); //关闭写保护
for(i=0;i<7;i++)
{
x_1302(j,sjhc[ i],1);
j += 2;
}
x_1302(addr_ram,sjhc[7],1); //把高2位年写入DS1302的RAM的C0地址中
x_1302(addr_ram2,sjhc[6],1);
x_1302(addr_xbh,0x80,0); //打开写保护
}
void d_sj() //数码管显示切换函数
{
unsigned char j;
switch((sz & 0x03))
{
case 0: //显示时,分,秒
{
if((j = d_1302(addr_m,1)) != zt[2]) //读取秒然后判断时间 这样来提升效率
{
zt[2] = j; //记录秒的临时数值
hc[6] = DM[zt[2]/10]; //写入秒的十位数据到数码管缓存
hc[7] = DM[zt[2]%10]; //写入秒的个位数据到数码管缓存
if(zt[2] == 0) //判断秒是否是0,如果是0条件就成立就读取分钟
{
zt[1] = d_1302(addr_f,1); //记录分钟的临时数值
hc[3] = DM[zt[1]/10]; //写入分钟的十位数据到数码管缓存
hc[4] = DM[zt[1]%10]; //写入分钟的个位数据到数码管缓存
if(zt[1] == 0) //判断时钟是否是0,如果是0条件成立就读取小时
{
zt[0] = d_1302(addr_x,1); //记录小时的临时数值
ntb(); //每小时同步一下年
hc[0] = DM[zt[0]/10]; //写入小时的十位数据到数码管缓存
hc[1] = DM[zt[0]%10]; //写入小时的个位数据到数码管缓存
}
}
}
break;
}
case 1: //显示年,月,日
{
if((j = d_1302(addr_r,1)) != zt[2]) //小时变化后读取月年
{
csh_hc();
}
break;
}
case 2: //显示周与温度
{
csh_hc();
break;
}
}
}
void csh_hc() //切换初始化数码管缓存函数(这个函数的作用就是在切换时清理数码管的缓存)
{
switch((sz & 0x03))
{
case 0x00:
{
zt[2] = d_1302(addr_m,1); //读取秒
zt[1] = d_1302(addr_f,1); //读取分钟
zt[0] = d_1302(addr_x,1); //读取小时
hc[7] = DM[zt[2]%10]; //写入秒的个位
hc[6] = DM[zt[2]/10]; //写入秒的十位
hc[5] = 0x40; // -
hc[4] = DM[zt[1]%10]; //写入分的个位
hc[3] = DM[zt[1]/10]; //写入分的十位
hc[2] = 0x40; // -
hc[1] = DM[zt[0]%10]; //写入时的个位
hc[0] = DM[zt[0]/10]; //写入时的十位
break;
}
case 0x01:
{
zt[3] = d_1302(addr_ram,1); //读取年的高2位(千位和百位)
zt[2] = d_1302(addr_r,1); //读取日
zt[1] = d_1302(addr_y,1); //读取月
zt[0] = d_1302(addr_n,1); //读取年的低2位(个位和十位)
hc[7] = DM[zt[2]%10]; //写入日的个位
hc[6] = DM[zt[2]/10]; //写入日的十位
hc[5] = 0x80 | DM[zt[1]%10]; //写入月的个位 + "."
hc[4] = DM[zt[1]/10]; //写入月的十位
hc[3] = 0x80 | DM[zt[0]%10]; //写入年的个位 + "."
hc[2] = DM[zt[0]/10]; //写入年的十位
hc[1] = DM[zt[3]%10]; //写入年的百位
hc[0] = DM[zt[3]/10]; //写入年的千位
break;
}
case 0x02:
{
zh(wd());
zt[3] = d_1302(addr_xq,1);
hc[0] = 0x40;
hc[1] = DM[zt[3]];
hc[2] = 0x40;
hc[3] = 0;
hc[4] = 0;
break;
}
}
}
void smgql() //数码管缓存清零函数
{
unsigned char i;
for(i=0;i<8;i++)
hc[ i] = 0;
}
void szjia(unsigned char *ls,unsigned char j,unsigned char i,bit l) //数值加或减函数
//参数1:数据,参数2:传送要设置的位置,参数3:传送模式,参数4:(0是:减,1是:加)
{
switch((sz & 0x03)) //区分时间,日期,周
{
case 0: //时间(从switch1:分支1)
{
switch(j) //区分 秒,分钟,小时
{
case 1: //秒
{
if(l) //判断是加还是减
{
ls[0]++;
if( 60 <= ls[0] )
ls[0] = 0;
}
else
{
if( 0 >= ls[0])
ls[0]=59;
else
ls[0]--;
}
sjqh(ls,6,0,1);
break;
}
case 2: //分钟
{
if(l) //判断是加还是减
{
ls[1]++;
if( 60 <= ls[1] )
ls[1] = 0;
}
else
{
if( 0 >= ls[1])
ls[1]=59;
else
ls[1]--;
}
sjqh(ls,3,1,1);
break;
}
case 3: //小时
{
if(l) //判断是加还是减
{
ls[2]++;
if( 24 <= ls[2] )
ls[2] = 0;
}
else
{
if(0 >= ls[2])
ls[2]=23;
else
ls[2]--;
}
sjqh(ls,0,2,1);
break;
}
}
break;
}
case 1: //日期(从switch2:分支1)
{
switch(j) //区分 日期,月份,年
{
case 1: //日期
{
if(l) //判断是加还是减
{
ls[3]++;
if( i <= ls [3]) //这里的i放在右移的按键里面处理,
ls[3] = 1;
}
else
{
if(1 >= ls[3])
{
ls[3]= i-1;
}
else
{
ls[3]--;
}
}
sjqh(ls,6,3,1);
break;
}
case 2: //月份
{
if(l)
{
ls[4]++;
if(13 <= ls[4])
ls[4] = 1;
}
else
{
if(1 >= ls[4])
ls[4] = 12;
else
ls[4]--;
}
sjqh(ls,4,4,1);
break;
}
case 3: //年
{
if(l)
{
ls[6]++;
if( 100 <= ls[6])
{
ls[6] = 0;
ls[7]++;
if( 100 <= ls[7] )
ls[7] = 0;
}
}
else
{
if( 0 >= ls[6] )
{
ls[6] = 99;
ls[7]--;
if( 0 >= ls[7] )
{
ls[7] = 99;
}
}
else
ls[6]--;
}
sjqh(ls,0,7,2);
break;
}
}
break;
}
case 2: //周(从switch3:分支3)
{
if(l)
{
ls[5]++;
if( 8 <= ls[5] )
{
ls[5] = 1;
}
}
else
{
if( 1 >= ls[5])
ls[5] = 7;
else
ls[5]--;
}
sjqh(ls,0,0,10);
break;
}
}
}
void sjqh(unsigned char *ls,unsigned char i,unsigned char j,unsigned char k) //设置,数码管缓存写入函数
//参数1:数据,参数2:缓存开始地址(0~7),参数3:写入缓存的数据,参数4:写入数据的个数
{
if(k<10)
for(;k>0&&i<8&&j<8;k--)
{
hc[ i]= DM[ls[j]/10];
i++;
if(i<8)
hc[ i]= DM[ls[j]%10];
j--;i++;
}
else
{
hc[0]=0x40;
hc[1]=DM[ls[5]];
hc[2]=0x40;
}
}
void sjse() //设置时间年月日函数
{
unsigned char ls[8],i,j=0x80;
unsigned int n; //保存年(计算闰年要用到)
for(i=0;i<7;i++) //读取寄存器的所有数值
{
shijian[ i]=d_1302(j,1); //读取数据
ls[ i] = shijian[ i]; //赋值给临时设置数组
j += 2; //地址指针移动到下一个地址
}
shijian[7]=d_1302(addr_ram,1); //读取高2位年。地址是0xc0
ls[7] = shijian[7];//赋值给临时设置数组
j = 3; //初始化位置
smgql(); //数码管缓存清零
switch((sz & 0x03))
{
case 0:sjqh(ls,0,2,1);break; //把小时写入数码管缓存
case 1:sjqh(ls,0,7,2);break; //把年写入缓存
case 2:sjqh(ls,0,0,10); //星期
}
while(i)
{
if((sz & 0x80)) //这里是判断是否有按键按下。
{
sz = sz & 0x7f; //按键标志位置0
switch (jpsm()) //按下的按键区分(主switch)
{
case 0xdd: //6 数值加(主switch:分支1)
{
szjia(ls,j,i,1); //写入数码管显示缓存
break;
}
case 0xeb: //* 左移(主switch:分支2)
{
smgql(); //数码管缓存清零
if( 1 < j )
j--;
else
j=3;
switch((sz & 0x03))
{
case 0: //时间左移
{
switch(j) //区分移动标志的数值
{
case 1: //移动标志1是6~7数码管显示
{
sjqh(ls,6,0,1); //写入数码管显示缓存
break;
}
case 2:
{
sjqh(ls,3,1,1); //写入数码管显示缓存
break;
}
case 3:
{
sjqh(ls,0,2,1); //写入数码管显示缓存
break;
}
}
break;
}
case 1: //日期左移 >>
{
//标志1
switch(j)
{
case 1:
{
switch(ls[4]) //设置每个月天数的上限
{
case 2:
{
n = ls[7];
n = n*100+ls[6];
if( n % 400 ==0 ||((n%4) ==0 && (n%100))) //能被400整除与能被4整除且不能被100整除
//四年一闰,百年不闰,四百年再闰
i = 29;
else
i = 28;
break;
}
case 4: i=30;break;
case 6: i=30;break;
case 9: i=30;break;
case 11: i=30;break;
default : i = 31;
}
i++;
sjqh(ls,6,3,1); //写入数码管显示缓存
break;
}
case 2:
{
sjqh(ls,4,4,1); //写入数码管显示缓存
break;
}
case 3:
{
sjqh(ls,0,7,2); //写入数码管显示缓存
break;
}
}
break;
}
case 2: sjqh(ls,0,0,10);break;
}
break;
}
case 0xdb: //9 设置或者确定(主switch:分支3)
{
j=0x80;
x_1302(addr_xbh,0,0);
for(i=0;i<7;i++)
{
if( shijian[ i] != ls[ i] )
{
x_1302(j,ls[ i],1);
}
j += 2;
}
x_1302(addr_ram,ls[7],1);
x_1302(addr_ram2,ls[6],1);
x_1302(addr_xbh,0x80,0);
i=0;
break;
}
case 0xbb: //8 右移(主switch:分支4) <<
{
smgql(); //数码管缓存清零
if(j>=3)
j=1;
else
j++;
switch( (sz & 0x03) )
{
case 0: //时间
{
switch(j)
{
case 1: sjqh(ls,6,0,1);break;
case 2: sjqh(ls,3,1,1);break;
case 3: sjqh(ls,0,2,1);break;
}
break;
}
case 1: //日期
{
switch(j)
{
case 1: sjqh(ls,6,3,1);break;
case 2: sjqh(ls,4,4,1);break;
case 3: sjqh(ls,0,7,2);break;
}
break;
}
case 2: sjqh(ls,0,0,10);break;
}
break;
}
case 0xd7: //= 数值减(主switch:分支5)
{
szjia(ls,j,i,0);
break;
}
case 0xe7: //设置切换
{
j=3;
smgql(); //数码管缓存清零
if((sz & 0x03) == 2)
sz = sz & 0xfc;
else
sz++;
switch((sz & 0x03))
{
case 0:sjqh(ls,0,2,1);break;
case 1:sjqh(ls,0,7,2);break;
case 2:sjqh(ls,0,0,10);break;
}
break;
}
case 0xb7: i=0;break; //.取消设置
}
}
sm(); //扫描显示数码管
}
}
void ntb() //年同步函数
{
shijian[5] = d_1302(addr_n,1);
shijian[6] = d_1302(addr_ram2,1);
if(shijian[6] != shijian[5])
{
if( shijian[6] == 99)
{
shijian[7] = d_1302(addr_ram,1);
if(shijian[7] != 99)
shijian[7]++;
else
shijian[7]=0;
x_1302(addr_xbh,0,0);
x_1302(addr_ram2,0,1);
x_1302(addr_ram,shijian[7],1);
x_1302(addr_xbh,0x80,0);
}
else
{
x_1302(addr_xbh,0,0);
x_1302(addr_ram2,shijian[5],1);
x_1302(addr_xbh,0x80,0);
}
}
}
void main() //主函数
{
sz = 0;
csh_hc();
csh_wbzd_0(); //外部中断初始化
ntb();
P1 = 0xf0; //这个是用于检测矩阵键盘是否按下的(外部中断需要的)
while(1)
{
d_sj();
if((sz&0x80)) //这里是判断是否有按键按下。
/*注:这里加这个判断是因为键盘解码后需要执行的函数打乱主函数中断 d_sj()
函数造成数据有时无法写入DS1302芯片*/
{
jpjm(jpsm());
}
sm(); //扫描显示数码管
}
}
代码中关于ds18b20部分原理详见:http://www.51hei.com/bbs/dpj-22807-1.html
注:代码写的狠垃圾。。根本没有算法可言而且BUG很多 。。
没有改的BUG有。在设置日期时存在日期可以设置成 2月31号的错误
矩阵键盘的外部中断问题
1,如果矩阵键盘检测按键完毕后 ,如果P1口的数值是 0x0f就会导致外部中断
无限触发。
这里P1口的数值要结合电路图来判断。 |
|