串口是一种应用十分广泛的通讯接口,是串行通信最纯粹的表现形式,即数据结构是由一串一串的数据流构成的,通常是8位一串地进行传递,每一位都有先后顺序,并不是同时到达目的地址的,因此串口的成本低,容易使用,线路简单,但速率较慢,可以实现两个设备的简单通信。与之对应的是并口,以8位为例,并行通信是8位数据同时发出,同步达到,理论上速率是穿行的8倍,但成本较高,应用场景较少。
单片机的可以时单片机与单片机,单片机与电脑,单片机与各种各样的设备进行互相通信,极大地提升了单片机裸机的扩展性,增强了其硬件实力。
15.1 UART
51单片机内部自带UART(Universal Asynchronous Receiver Transmitter,通用异步收发器),可实现单片机的串口通信。
STC89C52系列单片机内部集成有一个功能很强的全双工串行通信口,与传统8051单片机的串口完全兼容。设有2个互相独立的接收、发送缓冲器,可以同时发送和接收数据。发送缓冲器只能写入而不能读出,接收缓冲器只能读出而不能写入,,因而两个缓冲器可以共用一个地址码(99H)。两个缓冲器统称串行通信特殊功能寄存器 SBUF 。
15.1.1电平协议:
值得一提的是,串行通信分为很多种电平协议,例如TTL,RS232,RS485等,不做任何处理的话它们相互之间是无法正常通信的。电平标准就是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系。单片机一般都是TTL协议,电脑端大多都使用的是USB接口,TTL转USB协议就需要一颗ch340芯片,不过51的开发板已经装好了,所以我们只需要插上USB线就可以实现单片机和电脑端的串口通信了。
串口常用的电平标准有如下三种:
TTL电平:+5V表示1,0V表示0
RS232电平:-3~-15V表示1,+3~+15V表示0
RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)
15.1.2引脚定义 :
TXD :(Transmit Exchange Data) 数据发送
RXD:(Receive Exchange Data) 数据接收
严格意义上来讲串口通信只有这两根线组成,通信原理也非常简单,就是一发一收,发送和接收分别独占一根数据线,可以兼顾发送和接收的工作,所以串口为全双工。由于没有第三根时钟线来统一传输的时间基准,所以串口为异步通信方式,双方必须约定好波特率,否则数据就会出现不可预知的错误。
15.1.3工作模式:
STC89C52只有一个UART,总共有四种工作模式:
- 模式0:同步移位寄存器
- 模式1:8位UART,波特率可变(常用)
- 模式2:9位UART,波特率固定
- 模式3:9位UART,波特率可变
常用的只有模式1,其他3种模式小编到现在还没遇到过。
15.2 UART参数、时序、及其工作原理
STC89C52系列单片机串行口对应的硬件部分对应的管脚是P3.0/RxD和P3.1/TxD。
15.2.1参数:
波特率:串口通信的速率(发送和接收各数据位的间隔时间)
校验位:用于数据验证(奇校验/偶校验)
停止位:用于数据帧间隔
15.2.2时序:
一般情况我们都会选则模式1,此模式为8位UART格式,一帧数据为10位,1位起始位,8位数据位,1位停止位,波特率可变,即可根据需要进行设置。
模式2和模式3为9位数据格式,一帧数据总共11位,在停止位后紧接一位校验位,用以检验这帧数据的可靠性。
这两种方法均采用低位先行的原则,即从一帧数据的右端开始逐一发送,比如1010 1010,就是从最低位 “0” 开始发,接着发第二位的 “1” 接着 “0” 、“1”、“0”......经过一些硬件电路的处理后,接收端收到的数据仍然是“1010 1010”。
下面是手册里给的时序图:
15.2.3 寄存器
/**********************************
函数名: Uart_Init
传递参数: 无
返回参数: 无
函数作用: 串口的初始化函数
**********************************/
void Uart_Init(void)
{
SCON=0x50; //配置串口为模式 1
/* 用定时器1 */
// PCON |= 0X80; //控制波特率加倍
// TMOD|=0x20; //配置 T1 为模式 2
// TL1 = TH1 = 256-11059200/12/16/9600;//配置波特率为9600
// TR1=1; //启动 T1
/* 用定时器2*/
RCLK = 1; //串口接受时钟由定时器2提供
TCLK = 1; //串口发送时钟由定时器2提供
TH2 = RCAP2H = (65536 - 11059200/32/115200)/256; //设置波特率 : 115200
TL2 = RCAP2L = (65536 - 11059200/32/115200)%256; //设置波特率
TR2 = 1; //启动 定时器2
/*********/
ES=1; //打开串口中断
EA=1; //打开总中断
}
SCON=0x50;
这样设置是确认串口的工作方式,将串口设置在 8 位UART,波特率可变。
使用定时器 2
使用定时器 1 和定时器 2 本质上是考虑到定时器2有专用的波特率发生器,可以发出我们指定的波特率,我们也建议使用这样的方式,使用定时器1的就是使用定时器1的溢出率,来产生波特率,值得注意的是定时器1产生的波特率本身速度是不够的,所以使用了波特率加倍的寄存器。
RCLK = 1; //串口接受时钟由定时器2提供
TCLK = 1; //串口发送时钟由定时器2提供
这两个主要是串口的接收时钟是由谁产生的并且确认定时器2的模式是波特率发生器。
TH2 = RCAP2H = (65536 - 11059200/32/115200)/256; //设置波特率 : 115200
TL2 = RCAP2L = (65536 - 11059200/32/115200)%256; //设置波特率
这样就可以确认波特率了。后面就是开启串口中断以及开启总中断。
使用定时器 1
PCON |= 0X80; //控制波特率加倍
TMOD|=0x20; //配置 T1 为模式 2
首先第一个是配置波特率加倍,也就是使得SMOD=1
第二个是设置定时器1的工作模式位 8位自动重装载模式。
TL1 = TH1 = 256-11059200/12/16/9600;//配置波特率为9600
我们在串口通讯模式1下,可以看出波特率的计算公式。
SBUF
STC89C52 系 列 单 片 机 的 串 行 口 缓 冲 寄 存 器 ( SBUF ) 的 地 址 是 99H , 实 际 是 2 个 缓 冲 器 , 写 SBUF 的 操 作 完 成 待 发 送 数 据 的 加 载 , 读 SBUF 的 操 作 可 获 得 己 接 收 到 的 数 据 。 两 个 操 作 分 别 对 应 两 个 不 同 的 寄 存 器 , 1 个 是 只 写 寄 存 器 , 1 个 是 只 读 寄 存 器 。
串 行 通 道 内 设 有 数 据 寄 存 器 。 在 所 有 的 串 行 通 信 方 式 中 , 在 写 入 SBUF 信 号 的 控 制 下 , 把 数 据 装 入 相 同 的 9 位 移 位 寄 存 器 , 前 面 8 位 为 数 据 字 节 , 其 最 低 位 为 移 位 寄 存 器 的 输 出 位 。 根 据 不 同 的 工 作 方 式 会 自 动 将 “ 1 ” 或 TB8 的 值 装 入 移 位 寄 存 器 的 第 9 位 , 并 进 行 发 送 。
串 行 通 道 的 接 收 寄 存 器 是 一 个 输 入 移 位 寄 存 器 。 在 方 式 0 时 它 的 字 长 为 8 位 , 其 他 方 式 时 为 9 位 。 当 一 帧 接 收 完 毕 , 移 位 寄 存 器 中 的 数 据 字 节 装 入 串 行 数 据 缓 冲 器 SBUF 中 , 其 第 9 位 则 装 入 SCON 寄 存 器 中 的 RB8 位 。 如 果 由 于 SM2 使 得 己 接 收 到 的 数 据 无 效 时 , RB8 和 SBUF 中 内 容 不 变 。
由 于 接 收 通 道 内 设 有 输 入 移 位 寄 存 器 和 SBUF 缓 冲 器 , 从 而 能 使 一 帧 接 收 完 将 数 据 由 移 位 寄 存 器 装 入 SBUF 后 , 可 立 即 开 始 接 收 下 一 帧 信 息 , 主 机 应 在 该 帧 接 收 结 束 前 从 SBUF 缓 冲 器 中 将 数 据 取 走 , 否 则 前 一 帧 数 据 将 丢 失 。 SBUF 以 并 行 方 式 送 往 内 部 数 据 总 线 。
TI
发 送 中 断 请 求 标 志 位 。 在 方 式 0 , 当 串 行 发 送 数 据 第 8 位 结 束 时 , 由 内 部 硬 件 自 动 置 位 , 即 TI=I , 向 主 机 请 求 中 断 , 响 应 中 断 后 必 须 用 软 件 复 位 , 即 TI=OO 在 其 他 方 式 中 , 则 在 停 止 位 开 始 发 送 时 由 内 部 硬 件 置 位 , 必 须 用 软 件 复 位 。
RI
接 收 中 断 请 求 标 志 位 。 在 方 式 0 , 当 串 行 接 收 到 第 8 位 结 束 时 由 内 部 硬 件 自 动 置 位 RI =1 , 向 主 机请求中断 , 响应中断 后 必 须 用 软 件 复 位 , 即 RI=0。 在 其 他 方 式 中 , 串 行 接 收 到 停 止 位 的 中 间 时 刻 由 内 部 硬 件 置 位 , 即 RI=1 ( 例 外 情 况 见 SM2 说 明 ) , 必 须 由 软 件 复 位 , 即 RI=0。
15.2.4 串口的代码书写
/**********************************
包含头文件
**********************************/
#include "uart.h"
/**********************************
函数名: Uart_Init
传递参数: 无
返回参数: 无
函数作用: 串口的初始化函数
**********************************/
void Uart_Init(void)
{
SCON=0x50; //配置串口为模式 1
/* 用定时器1 */
// PCON |= 0X80;
// TMOD|=0x20; //配置 T1 为模式 2
// TL1 = TH1 = 256-11059200/12/16/9600;//配置波特率为9600
// TR1=1; //启动 T1
/* 用定时器2*/
RCLK = 1; //串口接受时钟由定时器2提供
TCLK = 1; //串口发送时钟由定时器2提供
TH2 = RCAP2H = (65536 - 11059200/32/115200)/256; //设置波特率 : 115200
TL2 = RCAP2L = (65536 - 11059200/32/115200)%256; //设置波特率
TR2 = 1; //启动 定时器2
/*********/
ES=1; //打开串口中断
EA=1; //打开总中断
}
/****
*******串口发送一个字节函数
*****/
void Uart_Sent_Char(uchar date)
{
SBUF=date;
while(!TI);
TI=0;
}
/****
*******串口发送字符串函数
*****/
void Uart_Sent_Str(uchar *date)
{
while(*date != '\0')
{
Uart_Sent_Char(*date);
date++;
}
}
/****
******* 串口中断服务程序函数
*****/
void ser() interrupt 4
{
if(RI)
{
RI = 0;
Uart_Sent_Char(SBUF);
}
}