串口是一种应用十分广泛的通讯接口,是串行通信最纯粹的表现形式,即数据结构是由一串一串的数据流构成的,通常是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);
  }
}

发表回复

后才能评论