1.串口
1.1基本概念
1.1.1串口的简介
- STC89C52有两个引脚是专门用来做UART(通用异步收发传输器)串行通信的,一个是P3.0,另一个是P3.1,它们还分别有另外的名字叫作RXD和TXD,由它们组成的通信接口就叫作串行接口,简称串口。
(1)GND表示接地,TXD是串行发送引脚,RXD是串行接收引脚。
(2)两个单片机之间要通信,首先电源基准得一样,所以要把两个单片机的GND相互连接起来,然后单片机1的TXD引脚接到单片机2的RXD引脚上,即此路为单片机1发送而单片机2接收的通道,单片机1的RXD引脚接到单片机2的TXD引脚上,即此路为单片机2发送而单片机1接收的通道。
- 串行和并行接口
(1)串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度:
(2) 并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位、16位、32位等数据一起传输。
- 异步和同步模式
(1)同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为"位间隔的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保特字符同步关系。
(2) 异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程,为使双方的收发协调,通信双方应该各自约定通信速率;
(3)异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有"位间隔"的整数倍的关系,但同一字符内的各位之间的距离均为"位间隔"的整数倍。
- 单工/半双工/全双工通信
(1)单工通信:数据只能沿一个方向传输
(2)半双工通信:数据可以沿两个方向传输,但需要分时进行
(3)全双工通信:数据可以同时进行双向传输
- 波特率和数据传送
(1)波特率 就是发送二进制数据位的速率 , 习惯上用 baud 表示 , 即发送一位二进制数据的持续时间
=1/baud 。
(2) 在 UART 通信的时候,一个字节是 8 位 , 规定当没有通信信号发生时 , 通信线 路保持高电平,当要发送数据之前, 先发一位 0 表示起始位 ,然后发送 8 位数据位,数据位是先低后高 的顺序 , 数据位发完后再发一位 1 表示停止位 ,这样本来要发送一个字节的 8 位数据 , 而实际上一共发送 了 10 位,多出来的两位其中一位起始位,一位停止位。而接收方呢 , 原本一直保持的高电平,一旦检测 到了一位低电平 , 那就知道了要开始准备接收数据了,接收到 8 位数据位后 , 然后检测到停止位,再准备下一个数据的接收,
1.1.2串口的框图和相关寄存器
- 我这里使用江协科大的资料(非常完美)
数据的接收和发送的流程
(1)**串口数据的接收:**外部数据通过RXD输入,先进入移位寄存器,在进入SUBF(接受缓冲器);当一帧数据全部进入SUBF后,串口发出中断请求,通知CPU读取数据,CPU执行完读取SUBF中的数据,将其送入其他存储单元。同时接受端可接受下一帧数据。
(2)串口数据的发送: CPU将数据通过内部数据总线传送的移位寄存器,经过控制门逐位的把数据通过引脚TXD发送出去。在发送时,由于CPU主动进行操作,在发送外后,中断发出中断请求,可发送下一帧数据。
- 串行口控制寄存器SCON
- 电源控制寄存器PCON
- 串行接口数据缓冲器SBUF
- 从机地址控制寄存器SADEN和SADDR
- 与串行口中断相关的寄存器IE和IPH、IP
1.1.3串口的工作模式1(8位自动重置模式)
- 波特率的设置
(1) SMOD=0
1.2代码
1.2.1Stc-ISP的代码
1.2.2串口初始化代码和发送数据函数(无中断)
cpp
//串口初始化代码
void UARTInit()
{
SCON=0x50; //使用公式方式1,8位UART波特率可变,并且允许接收。0101 0000
TMOD=0x20; //使用定时器1,使用定时器模式。0010 0000
TH1=256-3; //设置初值3,设置定时器1的高位,晶振为11.0592.波特率为9600
TL1=256-3; //设置初值3,设置定时器1的低位,晶振为11.0592.波特率为9600
EA=1; //开启总开关
ES=1; //开启串口中断开关
ET1 = 1; //开启定时器1中断
TR1=1; //启动定时器1
}
void send_data()
{
SBUF=num; //将要发送的数据在串口上显示出来
while(TI!=1); //判断是否发送完成,发送完成之后TI等于1
TI=0; //允许下次发送
}
UARTInit();
while(1)
{
Delay_ms(100);
send_data();
num++;
}
1.2.3 使用中断用户自主发送和接收
cpp
//串口初始化代码
void UARTInit()
{
SCON=0x50; //使用公式方式1,8位UART波特率可变,并且允许接收。0101 0000
TMOD=0x20; //使用定时器1,使用定时器模式。0010 0000
TH1=256-3; //设置初值3,设置定时器1的高位,晶振为11.0592.波特率为9600
TL1=256-3; //设置初值3,设置定时器1的低位,晶振为11.0592.波特率为9600
EA=1; //开启总开关
ES=1; //开启串口中断开关
// ET1 = 1; //开启定时器1中断
TR1=1; //启动定时器1
}
void send_data()
{
SBUF=resdata; //将要发送的数据在串口上显示出来
while(TI!=1); //判断是否发送完成,发送完成之后TI等于1
TI=0; //允许下次发送
}
//当RI=1时表示数据接收完成,此时表示可以传送下一次的数据
//用户将数据写入到SBUF,SBUF缓冲区中的数据传输到resdata,
//此时调用send_data函数将用户写入的数据传输到串口上显示
void Exit_UART() interrupt 4
{
if(RI==1)//判断数据是否接受完成
{
resdata=SBUF;//
RI=0;//允许接收下次数据
flag=1;
send_data();
}
}
1.3proteus的仿真
1.3.1器件的选取和总体的仿真图
(1)虚拟终端
(2) 此时没有采用中断num进行自增操作
(3)此时使用中断,用户发送串口接收
2.矩阵按键
2.1基本概念
2.1.1矩阵按键的简介
- 在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式
- 采用逐行或逐列的"扫描",就可以读出任何位置按键的状态
- 扫描信号
(1)行线的主要功能是向列线发送扫描信号。当某一行线被设置为低电平时,与之相交的列线会被检测(若相对应的按键被按下,列线被检测为低电平)以判断是否有按键被按下。
- 确定按键行
(1)通过依次将每一行线设置为低电平(可以逐行扫描键盘)。当检测到列线上有低电平时,可以确定被按下的按键位于当前选中的行上。若列线上无低电平,那么将此行线设置为高电平,下一行设置为低电平,进行新一轮按键检测。
- 逐行扫描原理
(1)以上图为例当要按下H0行的按键时,可以设置H0的电平为低电平,此时设置P1的输出电平为0xEF,当第一行第一列按键按下时,此时H0为低电平,则连接H0的L0也为低电平,此时P1输出的电平为0xEE,因此可以判断按键。
2.2proteus仿真
2.2.1总体仿真
2.3代码
cpp
//矩阵按键扫描
void Scan_KEY()
{
KEY_PORT=0xEF; //扫描第一行1110 1111
if(KEY_PORT!=0xEF)//第一行已经存在按键按下
{
Delay_ms(5);
switch(KEY_PORT)//判断那个按键按下
{
case 0XEE: num=0;break;//此时按键第一行第一列被按下,此时为1110 1110
case 0XED: num=1;break;//此时按键第一行第二列被按下,此时为1110 1101
case 0XEB: num=2;break;//此时按键第一行第三列被按下,此时为1110 1011
case 0XE7: num=3;break;//此时按键第一行第四列被按下,此时为1110 0111
}
}
while(KEY_PORT!=0xEF);//当按键抬起时跳出循环,防止重复执行if语句
KEY_PORT=0xDF; //扫描第二行1101 1111
if(KEY_PORT!=0xDF) //此时第二行已经有按键按下
{
Delay_ms(5);
switch(KEY_PORT)//判断那个按键按下
{
case 0XDE: num=4;break;//此时按键第二行第一列被按下,此时为1101 1110
case 0XDD: num=5;break;//此时按键第二行第二列被按下,此时为1101 1101
case 0XDB: num=6;break;//此时按键第二行第三列被按下,此时为1101 1011
case 0XD7: num=7;break;//此时按键第二行第四列被按下,此时为1101 0111
}
}
while(KEY_PORT!=0xDF);//当按键抬起时跳出循环,防止重复执行if语句
KEY_PORT=0xBF; //扫描第三行1011 1111
if(KEY_PORT!=0xDF) //此时第三行已经有按键按下
{
Delay_ms(5);
switch(KEY_PORT)//判断那个按键按下
{
case 0XBE: num=8;break; //此时按键第三行第一列被按下,此时为1011 1110
case 0XBD: num=9;break; //此时按键第三行第二列被按下,此时为1011 1101
case 0XBB: num=10;break;//此时按键第三行第三列被按下,此时为1011 1011
case 0XB7: num=11;break;//此时按键第三行第四列被按下,此时为1011 0111
}
}
while(KEY_PORT!=0xBF);//当按键抬起时跳出循环,防止重复执行if语句
KEY_PORT=0x7F; //扫描第四行0111 1111
if(KEY_PORT!=0xDF) //此时第四行已经有按键按下
{
Delay_ms(5);
switch(KEY_PORT)//判断那个按键按下
{
case 0X7E: num=12;break; //此时按键第四行第一列被按下,此时为0111 1110
case 0X7D: num=13;break; //此时按键第四行第二列被按下,此时为0111 1101
case 0X7B: num=14;break; //此时按键第四行第三列被按下,此时为0111 1011
case 0X77: num=15;break; //此时按键第四行第四列被按下,此时为0111 0111
}
}
while(KEY_PORT!=0x7F);//当按键抬起时跳出循环,防止重复执行if语句
}