51单片机(八)串口通信
本节主要介绍学习串口通信的相关知识,包括串口基础知识介绍、本节目标等;并利用两个小实验来进行练习,分别是串口向电脑发送数据以及电脑通过串口控制LED。
❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。
☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋。
🌙专栏目标:实现从零基础入门51单片机和STM32单片机,力求在玩好单片机的同时,能够了解一些计算机的基本概念,了解电路及其元器件的基本理论等。⭐️ 专栏主要内容: 主要学习51单片机的功能、各个模块、单片机的外设、驱动等,最终玩好单片机和单片机的外设,全程手敲代码,实现我们所要实现的功能。
🌴 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
💑专栏主页:http://t.csdn.cn/HCD8v
本学习过程参考:https://space.bilibili.com/383400717
单片机安装软件、各种资料以及源码的路径:
https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng
提取码:gdzf
本节主要介绍学习串口通信的相关知识,包括串口基础知识介绍、本节目标等;并利用两个小实验来进行练习,分别是串口向电脑发送数据以及电脑通过串口控制LED。
文章目录
一、串口介绍和本节目标
1.1 串口介绍
本节主要介绍51单片机和个人电脑之间,是如何使用串口进行通信的。
串口的硬件电路
电平标准
- 我们单片机使用的就是TTL电平;(缺点:只能传输10米之内)
- CAN总线以及USB使用的是RS485电平,通过两根线的压差来区分,也就是查分信号;而不是用电源正负极来区分电压。(最大传送距离可以达到千米以上)
接口及引脚定义:
常见的通信接口
相关术语
-
全双工方式,在A和B双方之间存在两根通信线,通信双方使用这两根线,A在向B发送数据的同时,B也可以通过另一个线向A发数据;
-
半双工方式,再A和B双方之间只存在一根通信线,利用这根线,通信双方可以互相发送数据,分时复用,也就是说同一时间只能有一方发送,另一方接收;
-
同步和异步比较,同步会比异步多一根时钟线,用来实现同步的功能。
下面介绍一下51单片机里的UART。
51单片机里的UART
STC89C52里只集成了一个UART
上图是其电路图,上图中的TXD和RXD就分别连接到单片机上的RXD和TXD;也就是下图的11和10口:
这些其实单片机开发板已经帮我们焊好了,所以将USB插到电脑上就可以进行通信了。
串口参数和时序图
检验位:用于检验发送数据的正确性;一般的方法有,奇偶校验(校验发送的数据和接收的数据里的1的个数是奇数还是偶数);
串口模式图
上图解释了在串口内部,数据是如何进行收发的;
上图虚线右边的部分都是单片机内部的电路;
其中最左边的双向箭头表示单片机的总线;T1溢出率部分是时钟;
除此之外还有两个寄存器SBUF(串口数据缓存寄存器),这两个寄存器都叫SBUR,且拥有相同的地址;上面的为写SBUF,MCU向该寄存器写入数据,并通过TXD发送出去;下面的为读SBUF,通过RXD接收数据,然后MCU读取该寄存器中的数据。
串口和中断系统
串口相关寄存器
1.2 本节目标
1.2.1 目标1:单片机通过串口向个人电脑发送数据
具体的为,单片机每隔1s,向电脑发送一次递增的数据;如下图所示:
1.2.2 目标2:电脑通过串口控制LED
电脑通过串口向单片机发送数据,进而控制LED的状态,例如当发送1时,第一个LED点亮
当发送0时,第一个LED熄灭,同时单片机会将收到的信息原封不动发回给电脑
当发送十进制的55时(对应的16进制是0101 0101),所以LED等也按照0101 0101的方式点亮;
当发送十六进制的aa给单片机时,也会按照对应的顺序点亮LED,如下图所示:
二、串口向电脑发送数据
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\8-1 串口向电脑发送数据
具体代码:
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"
unsigned char Sec;
void main()
{
UART_Init(); //串口初始化
while(1)
{
UART_SendByte(Sec); //串口发送一个字节
Sec++; //Sec自增
Delay(1000); //延时1秒
}
}
UART.c代码如下:
#include <REGX52.H>
/**
* @brief 串口初始化,4800bps@12.000MHz
* @param 无
* @retval 无
*/
void UART_Init()
{
SCON=0x40;
PCON |= 0x80;
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
/**
* @brief 串口发送一个字节数据
* @param Byte 要发送的一个字节数据
* @retval 无
*/
void UART_SendByte(unsigned char Byte)
{
SBUF=Byte;
while(TI==0);
TI=0;
}
代码解释:
UART_Init函数其实就是在对串口相关的寄存器进行初始化配置,也就是对以下八个寄存器进行配置,下面一一解释各个寄存器的配置。
在UART_Init函数中,各个寄存器为什么会进行以上设置呢?
SCON
首先是SCON寄存器的配置,代码中写的是SCON=0x40;
,关于SCON的设置,我们看下手册的内容,如下图所示,SCON由B0-B7 等8位构成,那么每一位分别设置成0还是1,怎么确认呢?
在上面的内容中有写我们最常用的就是8位UART模式,所以SM0(B7)设置为0,SM1(B6)设置为1;
从上图可知,
SM2是方式2和方式3用的,我们使用的方式1,所以不用管这个值,直接给0即可。
REN负责控制是否允许接收操作,我们此次是单片机向电脑发送数据,所以不涉及到接收,给0即可;
TB8和RB8也是方式2和方式3用的,我们使用的方式1,所以不用管这个值,直接给0即可;
对于TI和RI,上图手册中描述的有些难以理解,通俗点解释就是,参考串口模式图,其实就是图中的TI和RI:
TI和RI都是标志位,当TI等于1时,表示已经发送完成;当RI等于1时表示已经接收完成;当检测到发送完成时,TI必须手动置为0;当检测到接收完成时,RI必须手动置为0;
所以总结一下,在初始化时,SCON的配置如下:
从高到低以此为B7=0,B6=1,B5=0,B4=0,B3=0,B2=0,B1=0,B0=0;
即SCON=0100 0000
,也就是SCON=0x40
;
SBUF
SBUF是串口数据缓存,在初始化的时候不需要配置。
PCON
SMOD对应串口模式图中位置如下:
SMOD控制波特率是否加倍,当SMOD=0时,表示波特率除以2;当SMOD=1时,表示不除以2,也就相当于加倍。
SMOD0表示帧错误检查,如果不需要,配置0即可。
因为是初始化,所以以下几个都可以不用配置。
IE
不配置
IPH
不配置
IP
不配置
SADEN
不配置
SADDR
不配置
至此,串口部分就配置好了,剩下还需要配置定时器的初始化,结合上一节,上一节我们使用的是定时器0,但是串口部分需要定时器1,即将TMOD的高四位设置为1,低四位为0;TMOD &= 0x0F
表示先把高四位清空,以便后面对其进行设置。
另外,在串口模式,定时器要使用双八位自动模式,
即M1、M0要设置为1、0,即TMOD |= 0x20
:
另外TH1和TL1涉及到波特率的设置,如下图所示:
TL1和TH1的值的设置,我们可以使用STC-ISP里带的波特率计算器
生成工具来生成代码,如下进行配置:
然后拿出其中的TL1和TH1的配置即可。
所以代码如下:
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
所以整体的UART_Init函数代码就写好了:
void UART_Init()
{
SCON=0x40;
PCON |= 0x80;
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
初始化函数写好后,下面就是串口向电脑发送数据,写一个发送数据的函数,如下:
/**
* @brief 串口发送一个字节数据
* @param Byte 要发送的一个字节数据
* @retval 无
*/
void UART_SendByte(unsigned char Byte)
{
SBUF=Byte;
while(TI==0);
TI=0;
}
即将Byte赋值给SBUF即可,(SBUF在左边,即给他赋值,此时SBUF就默认是写寄存器了。
前面提到,发送完成之后,TI被置为1,需要我们软件手动置为0,所以当检测到TI为0时,说明发送完了,手动TI=0
;
然后将代码写好编译后,下载到单片机,打开STC-ISP软件中的串口助手,点击打开串口,按下单片机的复位键,即可发现单片机在向电脑从0开始递增发数据,即目标1的效果;(这里需要注意,因为我们代码中写的波特率是4800,所以串口助手中一定要选择波特率为4800,即约定好波特率,否则数据会发送错误,如下图进行设置)
三、电脑通过串口控制LED
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\8-2 电脑通过串口控制LED
具体代码如下:
#include <REGX52.H>
#include "Delay.h"
#include "UART.h"
void main()
{
UART_Init(); //串口初始化
while(1)
{
}
}
void UART_Routine() interrupt 4
{
if(RI==1) //如果接收标志位为1,接收到了数据
{
P2=~SBUF; //读取数据,取反后输出到LED
UART_SendByte(SBUF); //将受到的数据发回串口
RI=0; //接收标志位清0
}
}
这里需要注意的是,接收函数设置为了中断触发模式,即void UART_Routine() interrupt 4
;
为什么是 interrupt 4
呢?因为 interrupt 4表示串口的中断函数:
所以相较于上一节,需要多配置中断模式;如下,初始化时,EA=1
,表示启动所有中断;ES=1
,表示启动串口中断:
/**
* @brief 串口初始化,4800bps@12.000MHz
* @param 无
* @retval 无
*/
void UART_Init()
{
SCON=0x50;
PCON |= 0x80;
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xF3; //设定定时初值
TH1 = 0xF3; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;
ES=1;
}
在中断处理函数中:
void UART_Routine() interrupt 4
{
if(RI==1) //如果接收标志位为1,接收到了数据
{
P2=~SBUF; //读取数据,取反后输出到LED
UART_SendByte(SBUF); //将受到的数据发回串口
RI=0; //接收标志位清0
}
}
RI==1
表示如果是接收到了信息,因为RI是接收的标志位,当RI等于1时,说明已经成功接收到了数据(判断RI=1的目的是一定要保证这是接收数据触发的中断,因为发送数据也会触发该中断,从以下串口和中断系统图中可以看出,发送和接收是共用同一个中断的,所以一定要进行判断)。
下一步P2=~SBUF;
表示将接收到的数据赋值给P2,此时SBUF在右边,说明是读取SBUF的值,此时SBUF就表示读SBUF
了。后面RI=0,是表示读取到数据后,需要软件手动将RI置为0;
其他的代码,跟第二节的代码基本一致。不再详细讲解。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)