前言
本文将介绍UART通信协议。
说明:学习嵌入式五大通信协议之一的UART通信协议。
什么是UART通信协议
UART 通用异步收发器(Universal Asynchronous Receiver Transmitter),是一种通用的串行、异步通讯总线,有两条数据线,可实现全双工的发送和接收。
在嵌入式系统中常用于主机与辅助设备之间的通信。(在嵌入式领域中使用最多的通信协议)
通信基础概念
并行和串行
总线的作用:在计算机不同传递数据或信息。
总线是导线,通过传递高低电平的信号来实现信息的传递。高电平代表1,低电平代表0。
并行通信:总线在传递数据的过程中可以通过多根数据线把一个数据的多个位一次性发送。
串行通信:总线在传递数据的过程中可以通过一根数据线把一个数据依次发送。
并行通信优点在于快速,缺点是线比较多可能布线有一定难度,线之间存在干扰,小数据量下比较浪费。
串行通讯优点在于稳定,缺点是速度慢,数据大了传输的时间长。
嵌入式中串行总线用的多。
单工和双工
单工通信:有一方是发送器,另一方是接收器。通信时只能由发送器发送数据给接收器。单向的
双工通信:双方可以发送数据也可以接收数据,但其中半双工里不能双方同时发送数据,只能一方发送一方接收。
波特率
波特率用于描述UART通信时的通信速度,其单位为bps(bit per second)即每秒钟传送的bit的数量。
UART帧格式 - UART发送数据的格式
起始位占1位,数据位(要发送的数据,5-8位,先发低位,后发高位位),校验位占1位(可有可无),停止位占1/1.5或2位
当不发送数据时,规定数据线上的状态必须是高电平。
起始位的电平为低电平。
通常来说数据位发送8位(一个字节),但5-8位都可以。为什么不能发很多位是为了避免累计误差,由于双方时钟可能存在误差,会导致结果漏掉其中的一两位,发5-8位可以减小累计误差的影响,不至于影响一整个数据。
检验位为奇偶校验,统计数据中1的个数,如果是偶数就把校验位置为1,反之为0。
停止位的电平为高电平。
01和0011的波形是一样的,但是通过波特率能区分0和1的个数。所以双方的波特率必须一致,要事先约定好。
UART硬件连接
TXD发送数据,RXD接收数据
UART控制器
一般情况下处理器中都会集成UART控制器,使用UART进行通信的时候只需要对其内部的相关寄存器进行设置即可。
引脚功能设置
设置引脚功能的实质是让引脚在芯片内部连接到某一个对应的控制器
串口控制器包含:
波特率产生器(Baud-rate generator):控制串口发送数据或者接收数据的速度(需要时钟源来做精确控制,保证波特率的精准)
发送器(Transmitter):控制发送数据
接收器(Receiver):控制接收数据
控制单元(Control unit):控制整个串口控制器的工作逻辑
主频是CPU的工作频率,串口控制器有自己的工作频率。
工作逻辑
发送器和接收器都包含一个队列(FIFO)和一个移位器,队列用来存放数据,移位器用来吧数据传出去。发送的数据首先写到发送的缓冲区(队列),再把要发送的数据包括起始位,校验位和停止位拷贝到移位器,移位器通过右移把数据一位一位的移动到发送的引脚并以电平信号发送出去。
CPU通过芯片内部总线读取接收队列中的数据;向发送队列写入数据;读写控制器,通过控制寄存器控制整个串口的控制逻辑
UART寄存器
不同的寄存器是在自己的基地址的基础上加上需实现功能的偏移量
单块开发板可以用回环模式(Loop-back Mode)查看是否串口是否设置正确,就是自己发送的数据,自己接收。
CPU和外围硬件的 交互模式 有三种轮询模式,中断模式,DMA模式。
轮询模式
数据从外面的芯片发过来,接收器接收了,CPU想要调用这个数据,CPU跑一段自己的程序就查看一下队列中有没有数据,没有就继续跑一段自己的程序再查看一下队列中有没有数据,直到队列中有数据,CPU读取数据。
中断模式
一旦队列接收到数据串口控制器就给CPU发送一个中断信号,此时CPU再去读数据
DMA模式
直接存储器访问。队列接收到数据后串口控制器直接访问存储器把数据放进去。
程序思路
1、定义接收引脚和发送引脚
要先把低八位清零再设置
2、设置帧格式
3、设置数据交互模式
4、设置波特率
5、设置循环将发送的数据写入发送寄存器中
问:为什么只写发送程序电脑接收到的和发送的数据不一样?
因为在没有限制的情况下CPU往寄存器中写数据的速度和寄存器往外发数据的速度是不一样的。
解决方法:判断发送器是否是空闲的,空闲了CPU再发。(芯片内部有只读寄存器能判断)
题外话:在SecureCRT中看到的显示内容,其实是开发板返回的内容,看到显示的内容和输入的内容一致只是开发板返回了相同的内容。
输入输出重定向
如何让串口发送字符串
首先需要把要发送字符串的首地址传到函数中,再通过发送函数把字符串的每一个字一个一个发出去。
举例
1 | void UART_Send_Str(char * pstr) |
输出重定向
之前电脑安装了C库所以printf输出的内容通过显卡输出到显示屏上;在开发板没有安装C库之前在程序中自定义了printf函数,所以printf输出的内容通过开发板发送到UART上。
这就是输出重定向。主要用于系统移植