文章目录
前言
对于51单片机的串口通信学习进行一个总结,把自己的经验分享给大家,在学习过程中遇到了很多困难和不理解的地方,通过多次实践,对串口通信有了一个大概的了解,希望对大家能够有帮助。
一、串行通信口的功能以及串行通信口的结构及原理
1.1 串行通信口的功能
首先来了解什么是数据通信?
数据通信就是指单片机与单片机之间或者单片机和其他设备之间的信息交换
而数据通信又分为串行通信和并行通信
-
并行通信:数据的各位同时进行发送或接收的通信方式。优点是速率高。缺点是需要的传输线多,成本高,只适合近距离的数据通信。
-
串行通信:一位一位的按顺序的进行发送或接收的通信方式。优点是需要的传输线少,成本低。缺点是传输的速率慢,适合远距离的数据通信。
从上述来看,串行口的功能,就是通过串行口连接的数据传输线大大减少,可以进行远距离的数据通信。
1.2 51单片机串口的结构
串口的结构如下图所示,
通过定时器T1计时,由T1产生溢出率,作为波特率发生器。
-
两个数据缓冲器,SBUF。分别是发送数据的发送寄存器,读取数据的接收寄存器。
串口写入时,写入的是发送寄存器,即数据向发送寄存器SBUF写入。
向串口读时,读出的是接收寄存器,即数据由接收寄存器SBUF读出。 -
定时器1产生波特率, 串口一般使用定时器1,模式2,八位自动重装模式,来产生溢出率,从而产生波特率。而且在配置定时器相关的寄存器时不用配置定时器中断,只是使用定时器1来产生波特率的功能。
-
移位寄存器,在接受控制器的控制下,将输入的数据逐位移入接收SBUF。
-
串行控制寄存器SCON,SCON的功能是控制串行通信口的工作方式以及工作的状态。
二、串行通信口的控制寄存器
2.1 串行控制寄存器SCON
SCON寄存器用来控制串行通信的工作方式及反映串行口的工作状态。SCON的地址是98H,可位寻址。如下图所示。
通过配置SCON寄存器来对串行通信的工作模式进行控制。
说说几个常用的位
- SM0和SM1,组合确定串行口的模式,不同的模式下波特率不同,功能也不同,下面会详细介绍不同模式下的波特率该如何计算。
- REN,允许/禁止串行接收控制位。 由软件置位REN,即REN=1为允许串行接收状态,可启动串行口接收数据。
- TI,发送中断请求标志位。主机通过串行口发送数据发送完成后,TI会置1,向串口申请中断,如果TI为1 就表示SUBF的数据已经发送完成了,要手动将TI位清零。(即SBUF=Data)
- RI,接收中断请求标志位。从机接收到主机通过串行口接收到的数据后,RI会置1,即会串口申请中断,如果RI为1 就表示数据已经通过SBUF读取完成了,要手动将RI位清零。(即Data=SBUF)
2.2 电源控制寄存器PCON
这里我们只需要知道SMOD是啥就可以了。
- SMOD位,就是波特率是否加倍,若SMOD=0,波特率不变。SMOD=1,波特率加倍。SMOD默认为0。
2.3 配置寄存器
配置SCON寄存器
看,上图就是SCON寄存器的位图,下面以串口通信的模式1来举例。
模式1,那么
SM0和SM1就要配置成 0 1,注意这里SM0和SM1决定串口通信的模式,感谢评论区@nggtryy 指出
REN,可置1,置1就是允许串行通信口接收数据,当不需要接收数据的时候可以置0。
后面的几位直接不看,一般用不到
TI和RI,也默认为0,当接收数据或发送数据的时候,会由硬件置1,继而触发中断,这时要由软件置1。
这样SCON寄存器就配置好了。
这样我的SCON配置如下
SCON=0x40; //方式1 REN置1/0 允许/禁止数据接收的控制位
配置PCON寄存器
我们只需要明确一点,我的波特率是否需要加倍,如果加倍,那么PCON的第一位SMOD就要置1。
这样我的PCON就配置好了,如加倍
PCON |= 0x80; //波特率加倍
配置中断
注意,定时器1,不用开中断,即ET1=0,因为定时器的功能只是产生波特率。
下图为串口和中断的连接图,较为直观,也比较好理解
下方代码块为中断服务子函数模块
// 串口中断函数模板
void Uart_Routine() interrupt 4
{
if(RI==1)
{
RI=0;
}
if(TI==1)
{
TI=0;
}
}
2.4 实验
单片机向pc发送数据
代码如下:
UART.c
因为是单片机向PC发送数据,不用接收数据,所以这里的SCON寄存器的REN位未置1,而模式1,SM1和SM2就要配置成 0 1。
#include <REGX52.H>
/**
* @brief 串口初始化
* @param 无
* @retval 无
*/
void Uart_Init() //4800bps@11.0592MHz
{
SCON=0x40; //方式1 REN置1 允许/禁止数据接收的控制位
PCON |= 0x80; //波特率加倍
//配置定时器1 模式二 8位自动重装
TMOD&=0x0F;
TMOD|=0x20; //模式二
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1=0; //不用开启中断 定时器在这里的作用是产生波特率
TR1=1; //启动定时器1
}
/**
* @brief 串口发送一位字节数据
* @param Byte 要发送的字节数据
* @retval 无
*/
void Uart_SendByte(unsigned char Byte)
{
SBUF=Byte; //向缓存器中写入内容
while(TI==0); //发送中断标志位 如果检测到了寄存器的TI位 如果为0 就表示数据未发送完成 反复执行本条语句检测TI位
//如果TI为1 就表示SUBF的数据已经发送完成了 马上执行下条语句将TI位清零
TI=0;
}
main.c
这里,调用UART模块里的串口发送一位字节数据的函数,向PC发送递增的数据。
#include <REGX52.H>
#include "Delay.h"
#include "Uart.h"
unsigned char Sec;
void main()
{
Uart_Init();
while(1)
{
Sec++;
Uart_SendByte(Sec);
Delay(50);
}
}
PC向单片机发送数据,单片机接收数据
UART.c
这里,因为单片机要接收自己发送到PC里的数据,简而言之就是MCU->PC,然后PC->MCU
#include <REGX52.H>
/**
* @brief 串口初始化
* @param 无
* @retval 无
*/
void Uart_Init() //4800bps@11.0592MHz
{
SCON=0x50; //方式1 REN置1 允许/禁止数据接收的控制位 允许接收 REN置1
PCON |= 0x80; //波特率加倍
//配置定时器1 模式二 8位自动重装
TMOD&=0x0F;
TMOD|=0x20; //模式二
TL1 = 0xF4; //设定定时初值
TH1 = 0xF4; //设定定时器重装值
ET1=0; //不用开启中断 定时器在这里的作用是产生波特率
TR1=1; //启动定时器1
//接收数据完成后就触发中断
ES=1;
EA=1;
}
/**
* @brief 串口发送一位字节数据
* @param Byte 要发送的字节数据
* @retval 无
*/
void Uart_SendByte(unsigned char Byte)
{
SBUF=Byte; //向缓存器中写入内容
while(TI==0); //发送中断标志位 如果检测到了寄存器的TI位 如果为0 就表示数据未发送完成 反复执行本条语句检测TI位
//如果TI为1 就表示SUBF的数据已经发送完成了 马上执行下条语句将TI位清零
TI=0;
}
/* 串口中断函数模板
void Uart_Routine() interrupt 4
{
if(RI==1) //如果接收到数据 接收完成后 产生中断 如果是接收中断
//发送数据也会产生中断
{
P2=SBUF; //从SUBF缓存器中读取数据
Uart_SendByte(SBUF);
RI=0;
}
}
*/
main.c
如果接收到数据 接收完成后 产生中断 ,单片机把SBUF缓存里的数据读取出来了,因为检测到了接收中断,即RI=1,在主函数写了串口中断函数,检测是否接收中断,把接收到的数据,以LED灯的形式显示出来。
#include <REGX52.H>
#include "Delay.h"
#include "Uart.h"
unsigned char Sec;
void main()
{
Uart_Init();
while(1)
{
}
}
void Uart_Routine() interrupt 4
{
if(RI==1) //如果接收到数据 接收完成后 产生中断 如果是接收中断
//发送数据也会产生中断
{
P2=SBUF; //从SUBF缓存器中读取数据
Uart_SendByte(SBUF);
RI=0;
}
}
2.5 实验现象
单片机向PC发送数据
可以看到单片机向PC发送递增的数据,直到关闭串口,数据停止发送。
PC向单片机发送数据,单片机接收数据
可以看到在PC上发送0xF0,LED灯以相应的状态亮起,串口数据发送成功。
三、总结
首先要保证双机通信时,配置的波特率相同
然后串口通信主要要明确S**CON中的RN,SM0和SM1模式位的选择,以及RI和TI和理解。
在配置定时器1的时候,一定不要开定时器中断,即ET1=0,因为定时器的功能只是产生波特率。
然后定时器1产生的波特率该如何计算,计时器的初始值应该如何计算,将会在下一篇文章中详细说明。
希望对你有帮助,行文仓促,还请谅解,欢迎指错。