单片机STC15双机通信&矩阵键盘&异步串行通信&Proteus
●题目
●原理图
1、CPU
STC15W4K32S4系列
STC15W4K32S4系列内含多种具体型号的单片机。下图摘自数据手册。
!!!
Proteus的网络标号不区分大小写
乙机为区别甲机,乙机的标号前端加了下划线_
2、矩阵键盘
●矩阵键盘并没有按照 示意图接限流电阻(图中300欧姆的电阻,可能是限流,不理解该处电阻的作用);也未接上拉电阻(感觉没必要)。
●示意图中矩阵键盘的扫描接口接的是P0口,后经DL指点P0有BUG,测试见文末,改为P6口。
●四路与门是在调试矩阵键盘时,为中断扫描所连接的;在这个实验中矩阵键盘的接法为查询扫描&多行扫描。
附:在Proteus的库中有已经封装好的矩阵键盘模块。
关于矩阵键盘可详见: 矩阵键盘.
3、显示部分
●LED、数码管
●4个LED采用灌电流接法,经300欧姆排阻接VCC。
●使用了4个一位八段共阴数码管。
●LED对应按键值0—15,亮为1,灭为0,以8421BCD码显示按键值。
●数码管显示十进制0—15。
●数码管驱动电路(74HC573锁存器)
●数码管采用静态驱动,为节省IO口资源外接了锁存器。
●程序
代码为完整代码,关于双机通信单独的例子以及课本例程测试,可见 双机通信.
#include<STC15.H>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define KeyBus P6 //定义矩阵键盘接口
uchar Key_res =0;
uchar i=0;
uchar temp=0XFF; //串行发送临时传递参数
uchar temp1=0XFF; //串行接收临时传递参数
uchar busy; //正忙标志(数据正在传输)
code uchar A[10]={0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F}; //无点,数码管驱动数组
void GPIO(void); //IO口初始化函数
void Delay5ms(); //@11.0592MHz
void UartInit(void); //[email protected] 串行口1初始化函数
uchar Key16_Scan(void);//矩阵键盘扫描函数子程序
void Key16_Function(void); //矩阵键盘服务(功能)子函数
void SendData(void); //发送串口数据 (串行发送采用查询方式)
void main(void)
{
GPIO(); //IO口初始化函数
UartInit(); //串行口1初始化函数
ES=1; //允许串行口1中断
EA=1; //总中断允许控制位
while(1)
{
Key_res =0;
i=Key16_Scan(); //矩阵键盘扫描函数子程序
switch(i)
{
case 1: temp=0X00;break;
case 2: temp=0X01;break;
case 3: temp=0X02;break;
case 4: temp=0X03;break;
case 5: temp=0X04;break;
case 6: temp=0X05;break;
case 7: temp=0X06;break;
case 8: temp=0X07;break;
case 9: temp=0X08;break;
case 10: temp=0X09;break;
case 11: temp=0X0A;break;
case 12: temp=0X0B;break;
case 13: temp=0X0C;break;
case 14: temp=0X0D;break;
case 15: temp=0X0E;break;
case 16: temp=0X0F;break;
default: break;
}
SendData(); //发送串口数据
}
}
//-----------------------------------IO口初始化函数-----------------------------//
void GPIO(void)
{
P0M1=0;
P0M0=0;
P1M1=0;
P1M0=0;
P2M1=0;
P2M0=0;
P3M1=0;
P3M0=0;
P4M1=0;
P4M0=0;
P5M1=0;
P5M0=0;
}
//------------------------------------软件延时---------------------------------------//
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
//-------------------------------------串行口1初始化函数-----------------------------//
void UartInit(void) //[email protected]
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器1时钟为Fosc,即1T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设定定时器1为16位自动重装方式
TL1 = 0xE0; //设定定时初值
TH1 = 0xFE; //设定定时初值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
//---------------------------------------矩阵键盘扫描函数子程序-------------------------//
uchar Key16_Scan(void)
{
uchar X_temp = 0,Y_temp = 0;
uchar Key_down = 0;
KeyBus = 0XFF;
KeyBus = 0X0F; //高4位置0,低4位置1,此时有按键按下时,低四位的某一位会被拉低,由此定位按下的按键在第几列
Y_temp = KeyBus&0X0F;
if(Y_temp!=0X0F) //如果检测到某列有按键按下(有按键按下时,低四位会有一位被拉低)
{
Delay5ms(); //按键消抖(不知道有无必要)
KeyBus = 0X0F;
Y_temp = KeyBus&0X0F;//将KeyBus低四位的按键信息赋值给Y_temp
if(Y_temp!=0X0F)
{
KeyBus = 0X0F;
Y_temp = KeyBus&0X0F; //将KeyBus低四位的按键信息赋值给Y_temp,反应列信息
KeyBus = 0XF0;
X_temp = KeyBus&0XF0; //将KeyBus高四位的按键信息赋值给X_temp,反应行信息
if(Key_down == 0) //按键抬起检测,
{
switch(Y_temp) //判断列
{
case 0X07: Key_res=1; break;
case 0X0B: Key_res=2; break;
case 0X0D: Key_res=3; break;
case 0X0E: Key_res=4; break;
default: return 0; //无按键按下,返回0
}
switch(X_temp) //判断行
{
case 0X70: return Key_res+12;break;
case 0XB0: return Key_res+ 8;break;
case 0XD0: return Key_res+ 4;break;
case 0XE0: return Key_res; break;
default: return 0; //无按键按下,返回0
}
}
}
}
else
Key_down = 0; //按键被松开
if(Key_res)
Key_down = 1; //标志按键被按下,防止重入
return Key_res;
}
//-----------------------------------矩阵键盘服务(功能)子函数-------------------//
void Key16_Function(void)
{
if(i != 0) //按键按下
{
switch(i)
{
case 1: P1=0XFE;P2=0XFF;break;
case 2: P1=0XFD;P2=0XFF;break;
case 3: P1=0XFB;P2=0XFF;break;
case 4: P1=0XF7;P2=0XFF;break;
case 5: P1=0XEF;P2=0XFF;break;
case 6: P1=0XDF;P2=0XFF;break;
case 7: P1=0XBF;P2=0XFF;break;
case 8: P1=0X7F;P2=0XFF;break;
case 9: P1=0XFF;P2=0XFE;break;
case 10: P1=0XFF;P2=0XFD;break;
case 11: P1=0XFF;P2=0XFB;break;
case 12: P1=0XFF;P2=0XF7;break;
case 13: P1=0XFF;P2=0XEF;break;
case 14: P1=0XFF;P2=0XDF;break;
case 15: P1=0XFF;P2=0XBF;break;
case 16: P1=0XFF;P2=0X7F;break;
default:P1=0X00;P2=0X00;break;
}
}
}
//------------------------串行接收中断函数 (串行接收采用中断方式)---------------------//
void uart_isr() interrupt 4
{
if(RI)
{
RI=0; //清除RI位
temp1=SBUF;
switch(temp1)
{
case 0x00:P4=0XFF;P2=0X01;P7=A[0];P2=0X02;P7=A[0];break;
case 0x01:P4=0XFE;P2=0X01;P7=A[0];P2=0X02;P7=A[1];break;
case 0x02:P4=0XFD;P2=0X01;P7=A[0];P2=0X02;P7=A[2];break;
case 0x03:P4=0XFC;P2=0X01;P7=A[0];P2=0X02;P7=A[3];break;
case 0x04:P4=0XFB;P2=0X01;P7=A[0];P2=0X02;P7=A[4];break;
case 0x05:P4=0XFA;P2=0X01;P7=A[0];P2=0X02;P7=A[5];break;
case 0x06:P4=0XF9;P2=0X01;P7=A[0];P2=0X02;P7=A[6];break;
case 0x07:P4=0XF8;P2=0X01;P7=A[0];P2=0X02;P7=A[7];break;
case 0x08:P4=0XF7;P2=0X01;P7=A[0];P2=0X02;P7=A[8];break;
case 0x09:P4=0XF6;P2=0X01;P7=A[0];P2=0X02;P7=A[9];break;
case 0x0A:P4=0XF5;P2=0X01;P7=A[1];P2=0X02;P7=A[0];break;
case 0x0B:P4=0XF4;P2=0X01;P7=A[1];P2=0X02;P7=A[1];break;
case 0x0C:P4=0XF3;P2=0X01;P7=A[1];P2=0X02;P7=A[2];break;
case 0x0D:P4=0XF2;P2=0X01;P7=A[1];P2=0X02;P7=A[3];break;
case 0x0E:P4=0XF1;P2=0X01;P7=A[1];P2=0X02;P7=A[4];break;
case 0x0F:P4=0XF0;P2=0X01;P7=A[1];P2=0X02;P7=A[5];break;
default:P4=0XFF;break;
}
}
if(TI)
{
TI = 0; //清除TI位
busy = 0; //清零正忙标志
}
}
//-----------------------发送串口数据(串行发送采用查询方式)-------------------------------//
void SendData(void)
{
while(busy); //等待前面的数据发送完成
busy = 1; //将正忙标志置1,
SBUF = temp; //串行发送
}
●P0口BUG
刚开始按照示意图使用P0口作为矩阵键盘的检测扫描接口,并没有实现功能。过程中发现P0口部分IO口不会被正常拉低、拉高,而是产生短路。尝试之后发现,只有P01和P03会和其它P0端的IO口发生短路,其他IO口正常。
(具体分析ing)
正常拉低:
发生短路:
外接上拉电阻后,P01,P03的短路情况得到改变,能够被正常拉低。但是P04和P06此时却没有了电平指示(既不是高电平,低电平,也不是高阻态或者短路)