我们需要在串口通信前告诉CPU相应的内容,所以我们要在主程序初始化位置配置好寄存器:
以上寄存器中,在此次程序中(单片机向电脑发送数据)我们只需要配置SCON和PCON即可,不需要开启中断,所以不需要配置中断部分;
SCON和PCON的格式如下:
以上这些都不需要记忆,只是为了让你清楚如何去配置。
如果我们要根据配置模式1,其各数据位的操作如图所示:
转化为16进制就是0x40;
其中,TI、RI和SMOD在串口模式图中的位置如下图所示:
也就是说SMOD如果给1(打到1的位置)就是波特率不加倍,如果给0就是波特率➗2,波特率减半。其中SBUF不需要配置,TI和RI分别控制是否去中断系统。
其实这些东西不需要我们一个一个去根据手册去配置出来,STC-ISP会给我们生成程序,我们可以直接照搬:
可以看到:我们这里的定时器选择的是8位自动重载,而我们之前定时器时钟部分我们选择的是16位定时器,两者的区别是:
16位定时器有65535个数据位,我们在使用的时候需要赋予初值,比较浪费时间;而8位自动重载定时器把16位拆成2部分,两部分分别(也就是8位)只有255个数据位,但我们使用时不需要赋予初值,它会自动赋初值,不需要程序进行操作;
这里为什么选择波特率为4800而不是9600?可以看到上面生成的代码是存在一定的误差的,如果把波特率设置为960会发现这个误差值增加,这个误差可能会导致数据错误甚至无法正常工作。但4800的波特率还是会有百分之6.99的误差率,这时候我们需要让开启波特率倍速,让误差大大减小:
代码复制过来后,我们需要将红色圈起部分删除,因为高系列单片机可能可以进行选择,但89C82系列根本没得选。
注意:这里为什么禁止定时器1中断?
在定时器时钟部分时,定时器中断是指到点了就中断,而在串口发送数据这里,定时器起到的作用是控制单片机的波特率,使单片机的波特率一致,我们需要用到的是串口中断,而非定时器中断,串口中断是指接收到数据了就中断。而我们这个程序是单片机向电脑发送数据,所以也用不到串口中断。接下来的电脑通过串口控制LED会用到串口中断。
配置完成后,我们开始发送数据:首先定义一个发送字节的函数,往Byte的值赋予SBUF。
这里通过判断TI是否等于0的一个循环,是为了检测数据是否发送完成。也就是说发送数据前TI本身就为0,如果没发送完成就一直在循环里面,直到8位数据全部发送完成后,硬件自动置1,TI = 1,跳出循环,去中断系统,然后这时候我们需要软件置0,让它能够再次重新判断下一个数据发送是否完成。
然后我们烧录一下看看现象:
可以看到电脑成功接受到数据AA
这里有几点需要注意:(1)串口选择应该要与我们是扫描串口一致(2)电脑波特率要和单片机的波特率保持一致,均为4800(3)我这里选择了编程完成后自动打开串口,程序下载完成之后,需要按下单片机的复位键(晶振旁边的红色小按键)才能接受到,否则会出现串口打开失败或者接受区空白的情况。
还有一种情况:同样的数据我放入循环当中,使它循环发送,就会出现明明发送的是AA,但显示的却是35的情况:
如果把发送的数据放入循环当中,循环发送,就可能会出现数据接受错误的情况。这是因为发送速率太快加上有误差,这时候我们只需要在循环后面加上一个延时就能解决这个问题,加上延时给它一个缓冲的时间,如果误差率大的话,为了不出差,延时的时间也应该延长一些。
1、串口向电脑发送数据的完整代码如下:
#include <REGX52.H>
#include "DELAY.H"
void Uart_Init(void)
{
PCON |= 0x80;
SCON = 0x40;
TMOD &= 0x0F;
TMOD |= 0x20;
TL1 = 0xF3;
TH1 = 0xF3;
ET1 = 0;
TR1 = 1;
}
void Uart_SendByte(unsigned char Byte)
{
SBUF = Byte;
while(TI == 0){};
TI = 1;
}
void main()
{
Uart_Init();
while(1)
{
Uart_SendByte(0xAA);
Delay(10);
}
}
2、串口向电脑逐一发送数据代码如下:(前面已模块化)
#include <REGX52.H>
#include "DELAY.H"
#include "Uart.H"
unsigned char Sec;
void main()
{
Uart_Init();
while(1)
{
Uart_SendByte(Sec);
Sec++;
Delay(1000);
}
}
3、电脑通过串口控制LED灯
EA(Enable All):
CPU的总中断允许控制位,从上面的中断电路图可以看出,当EA = 1时,开关闭合,线路接通,CPU开放中断,反之EA = 0,CPU屏蔽所有中断申请,各中断源首先受EA控制,其次还受各中断源自己的中断允许控制位控制
上面的程序有说到:如果串口只需要发送数据(只发不收),那么我们不需要开启定时器中断以及串口中断,现在我们想要实现电脑通过串口控制LED(即发又收),那么我们需要开启串口中断,也就是当有数据传输给串口的时候,就会进入中断,读取数据。如果没有串口中断,我们需要时时刻刻准备着,因为不知道什么时候电脑会传输数据过来。
为什么有数据进来了就会进入中断呢?可以看到上图中只要TI = 1或者RI = 1时,我们只需要把ES和EA控制好,这条电路就能连通就会进入中断。
所以我们需要在前面程序的基础上,对串口中断进行配置:
串口中断配置好后,我们需要写串口中断函数:
这里我们检测一下,看看是否能够进入中断,如果进入中断了,LED将会被点亮:
这里我们在发送缓冲区随便发送一个数据f0,使数据传输给串口,这样才能触发中断,实施中断里面的代码,使灯全部点亮。
想要实现电脑通过串口控制LED,我们只需要让P3 = SBUF即可:
这里为什么要加一个判断if呢?因为TI = 1和RI = 1 占用同一个通道,也就是说触发指令的也可能是TI而不是RI 。这时候我们在发送缓冲区发送相应的数据,就能直接控制LED灯,如果发00,那就是全部灯亮,发f0就是只有一半亮。
注意!选择的LED的I/O口不要与UART的两个引脚相互冲突!否则就会出现错误的现象!
串口助手里面的文本和EX模式
文本模式就是显示的是所对应的ASCII码 ;EX模式显示的就是二进制或者十六进制。
电脑通过串口控制LED灯的完整代码如下:
#include <REGX52.H>
#include "Delay.h"
#include "Uart.h"
void main()
{
Uart_Init();
while(1)
{
}
}
void Uart_Routine() interrupt 4
{
if(RI == 1)
{
P0 = SBUF;
Uart_SendByte(SBUF);
RI = 0;
}
}
其中模块化程序如下:
#include <REGX52.H>
/**
* @brief 串口初始化
* @param 无
* @retval 无
*/
void Uart_Init(void) //4800bps@12.000MHz
{
PCON |= 0x80; //使能波特率倍速位SMOD
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF3; //设置定时初始值
TH1 = 0xF3; //设置定时重载值
ET1 = 0; //禁止定时器中断
TR1 = 1; //定时器1开始计时
EA = 1;
ES = 1;
}
/**
* @brief 串口要发送的一个字节数据
* @param Byte一个要发送的字节数据
* @retval 无
*/
void Uart_SendByte(unsigned char Byte)
{
SBUF = Byte;
while(TI == 0){};
TI = 0;
}
#ifndef ___Uart_H__
#define ___Uart_H__
void Uart_Init(void);
void Uart_SendByte(unsigned char Byte);
#endif