ARM裸机的知识点总结---------7、串口通信

Author: 想文艺一点的程序员
自动化专业 工科男
再坚持一点,再自律一点
CSDN@想文艺一点的程序员
来自朱有鹏嵌入式的学习笔记

一、通信的基本概念

1、电子通讯(大)概念

(1)同步通信和异步通信

(1)、同步和异步的区别:首先很多地方都有同步和异步的概念,简单来说就是发送方和接收方按照同一个时钟节拍工作就叫同步,发送方和接收方没有统一的时钟节拍、而各自按照自己的节拍工作就叫异步。

(2)、同步通信中,通信双方按照统一节拍工作,所以配合很好;一般需要发送方给接收方发送信息同时发送时钟信号,接收方根据发送方给它的时钟信号来安排自己的节奏。同步通信用在通信双方信息交换频率固定,或者经常通信时。

(3)、异步通信又叫异步通知。在双方通信的频率不固定时(有时3ms收发一次,有时3天才收发一次)不适合使用同步通信,而适合异步通信。异步通信时接收方不必一直在意发送方,发送方需要发送信息时会首先给接收方一个信息开始的起始信号,接收方接收到起始信号后就认为后面紧跟着的就是有效信息,才会开始注意接收信息,直到收到发送方发过来的结束标志

(2 电平信号和差分信号

(1)、电平信号和差分信号是用来描述通信线路传输方式的。也就是说如何在通信线路上表达 1 和 0 .

(2)、电平信号的传输线中有一个参考电平线(一般是GND),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。

(3)、差分信号的传输线中没有参考电平,所有都是信号线。然后1和0的表达靠信号线之间的电压差。

总结1:
电平信号的2根通信线之间的电平差异容易受到干扰,传输容易失败
差分信号不容易受到干扰因此传输质量比较稳定,现代通信一般都使用差分信号,电平信号几乎没有了。

总结2:看起来似乎相同根数的通信线下,电平信号要比差分信号要快;但是实际还是差分信号快,因为差分信号抗干扰能力强,因此1个发送周期更短。

(3)并行接口和串行接口

(1)、串行、并行主要是考虑通信线的根数,就是发送方和接收方同时可以传递的信息量的多少
(2)、譬如在电平信号下,1根参考电平线+1根信号线可以传递1位二进制;如果我们有3根线(2根信号线+1根参考线)就可以同时发送2位二进制;如果想同时发送8位二进制就需要9根线。
(3)、在差分信号下,2根线(彼此差分)可以同时发送1位二进制;如果需要同时发送8位二进制,需要16根线。

总结:听起来似乎并行接口比串行接口要快(串行接口一次只能发送1位二进制,而并行接口一次可以发送多位二进制)要更优秀;但是实际上串行接口才是王道,用的比较广。因为更省信号线,而且对传输线的要求更低、成本更低;而且串行时可以通过提高通信速度来提高总体通信性能,不一定非得要并行。

其实这么多年发展,最终胜出的是:异步、串行、差分,譬如USB和网络通信。

2、串口通讯(小)概念

(1)串口通信的特点:异步、电平信号、串行

(1)、异步:串口通信的发送方和接收方之间是没有统一的时钟信号的。

(2)、电平信号:串口通信出现的时间较早,速率较低,传输的距离较近,所以干扰还不太明显,因此当时使用了电平信号传输。后期出现的传输协议都改成差分信号传输了。

(3)、串行通信:串口通信每次同时只能传输1个二进制位

(2)RS232电平和TTL电平

1) TTL是Transistor-Transistor Logic,即晶体管-晶体管,它是计算机处理器控制的设备内部各部分之间通信的标准技术
电平信号应用广泛,是因为其数据表示采用二进制规定,+5V等价于逻辑”1”,0V等价于逻辑”0”。
数字电路中,由TTL电子元器件组成电路的电平是个电压范围,规定:
输出高电平>=2.4V,输出低电平<=0.4V;
输入高电平>=2.0V,输入低电平<=0.8V。

在这里插入图片描述

2)RS232电平是串口的一个标准。(USB,DB9接口线上传输的就是 RS232 电平)

在TXD和RXD数据线上:
  (1)逻辑1为 -3 ~ -15V 的电压 (负电压)
  (2)逻辑0为 3 ~ 15V的电压 (正电压)
在这里插入图片描述

(3)TTL 电平(cpu) 和 RS232电平(usb 或者 db9) 不可以直接进行通讯。必须利用芯片来进行转换。
在这里插入图片描述

(3)单工通信和双工通信

1)单工就是单方向,双工就是双方同时收发,同时只能但方向但是方向可以改变叫半双工

(2)如果只能A发B收则单工,A发B收或者B发A收(两个方向不能同时)叫半双工,A发B收同时B发A收叫全双工。

(4)三根通信线:Rx Tx GND

(1)串口通信线最少需要2根(GND和信号线),可以实现单工通信,也可以使用3根通信线(Tx、Rx、GND)来实现全双工

(2)一般开发板都会引出SoC上串口引脚直接输出的TTL电平的串口(X210开发板没有),
类似于51单片机,32单片机的引脚就会直接引出。(所以我们可以直接利用引脚,和 ttl 转usb 的下载器来进行下载代码a)

插座用插针式插座,每个串口引出的都有3个线(Tx、Rx、GND),可以用这些插座直接连接外部的TTL电平的串口设备。

在这里插入图片描述
各个引脚定义:我们只是用 RX TX 和 GND。剩下的6根都与 流控 相关。
在这里插入图片描述

(5)波特率

(1)波特率(bandrate),指的是串口通信的速率,也就是串口通信时每秒钟可以传输多少个二进制位。譬如每秒种可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600.

(2)串口通信的波特率不能随意设定,而应该在一些值中去选择。一般最常见的波特率是9600或者115200(低端单片机如51常用9600,高端单片机和嵌入式SoC一般用115200).

(3)为什么波特率不可以随便指定?
主要是因为:第一,通信双方必须事先设定相同的波特率这样才能成功通信,如果发送方和接收方按照不同的波特率通信则根本收不到,因此波特率最好是大家熟知的而不是随意指定的。
第二,常用的波特率经过长久发展,就形成了共识,大家常用就是9600或者115200.

(6)起始位、数据位、奇偶校验位、停止位 (一个周期)

必须建立这样一个概念:

串口通信时,收发是一个周期一个周期进行的,每周期传输n个二进制位
一个周期就叫做一个通信单元,一个通信单元是由:起始位+数据位+奇偶校验位+停止位组成的。

常见的有1位停止位,1.5位停止位,2位停止位等。99%情况下都是用1位停止位。

  • 起始位表示发送方要开始发送一个通信单元;
  • 数据位是一个通信单元中发送的有效信息位
  • 奇偶校验位是用来校验数据位,以防止数据位出错的;
  • 停止位是发送方用来表示本通信单元结束标志的。

(1)起始位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。

(2)数据位是本次通信真正要发送的有效数据,串口通信一次发送多少位有效数据是可以设定的(一般可选的有6、7、8、9,99%情况下我们都是选择8位数据位。因为我们一般通过串口发送的文字信息都是ASCII码编码的,而ASCII码中一个字符刚好编码为8位。

(3)奇偶校验位是用来给数据位进行奇偶校验(把待校验的有效数据逐个位的加起来,总和为奇数奇偶校验位就为1,总和为偶数奇偶校验位就为0)的,可以在一定程度上防止位反转。

(4)停止位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。

(7)信息以二进制流的方式在信道上传输

理解:在一根线上的同一时间,2个设备两端对这一根线的感知是一样的。
比如:发送方将电压拉高, 接收方就可以立马感知。(从而实现了数据的传输)

(1)每位数据的持续时间:
串口通信的发送方每隔一定时间(时间固定为1/波特率,单位是秒)将有效信息(1或者0)放到通信线上去,逐个二进制位的进行发送。

(2)起始时间由读到起始位标志开始,间隔时间由波特率决定
接收方通过定时读取通信线上的电平高低来区分发送给我的是1还是0。
依次读取数据位、奇偶校验位、停止位,停止位就表示这一个通信单元(帧)结束,然后中间是不定长短的非通信时间(发送方有可能紧接着就发送第二帧,也可能半天都不发第二帧,这就叫异步通信),下来就是第二帧·····

总结:
第一,波特率非常重要,波特率错了整个通信就乱套了;
数据位、奇偶校验位、停止位也很重要,否则可能认不清数据。
第三,通过串口不管发数字、还是文本还是命令还是什么,都要先对发送内容进行编码,编码成二进制再进行逐个位的发送。

(3)串口发送的一般都是字符,一般都是ASCII码编码后的字符,所以一般设置数据位都是8,方便刚好一帧发送1个字符。

二、s5pv210的串口通讯(数据手册)

1、S5PV210串行通信接口详解

(1)S5PV210的串口控制器工作原理框图

在这里插入图片描述

(1)整个串口控制器包含transmitter和receiver两部分,两部分功能彼此独立,transmitter负责210向外部发送信息,receiver负责从外部接收信息到210内部。

(2)总线角度来讲,串口控制器是接在APB总线上的。对我们编程有影响的是:将来计算串口控制器的源时钟时是以APB总线来计算的。

(3)transmitter由发送缓冲区和发送移位器构成。我们要发送信息时,首先将信息进行编码(一般用ASCII码)成二进制流,然后将一帧数据(一般是8位)写入发送缓冲区(从这里以后程序就不用管了,剩下的发送部分是硬件自动的),发送移位器会自动从发送缓冲区中读取一帧数据,然后自动移位(移位的目的是将一帧数据的各个位分别拿出来)将其发送到Tx通信线上。

(4)receiver由接收缓冲区和接收移位器构成。当有人通过串口线向我发送信息时,信息通过Rx通信线进入我的接收移位器,然后接收移位器自动移位将该二进制位保存入我的接收缓冲区,接收完一帧数据后receiver会产生一个中断给CPU,CPU收到中断后即可知道receiver接收满了一帧数据,就会来读取这帧数据。

总结:发送缓冲区和接收缓冲区是关键。发送移位器和接收移位器的工作都是自动的,不用编程控制的,
所以我们写串口的代码就是:
首先初始化(初始化的实质是读写寄存器)好串口控制器(包括发送控制器和接收控制器),然后要发送信息时直接写入发送缓冲区,要接收信息时直接去接收缓冲区读取即可。

可见,串口底层的工作(譬如怎么移位的、譬如起始位怎么定义的、譬如TTL电平还是RS232电平等)对程序员是隐藏的,程序员不用去管。软件工程师对串口操作的接口就是发送/接收缓冲区(实质就是寄存器,操作方式就是读写内存)

(5)串口控制器中有一个波特率发生器,作用是产生串口发送/接收的节拍时钟。波特率发生器其实就是个时钟分频器,它的工作需要源时钟(APB总线来),然后内部将源时钟进行分频(软件设置寄存器来配置)得到目标时钟,然后再用这个目标时钟产生波特率(硬件自动的)。

(2)串行通信与中断的关系

(1)串口通信分为发送/接收2部分。
发送方一般不需要(也可以使用)中断即可完成发送,接收方必须(一般来说必须,也可以轮询方式接收)使用中断来接收。

(2)发送方可以选择使用中断,也可以选择不使用中断。

使用中断的工作情景是:发送方先设置好中断并绑定一个中断处理程序,然后发送方丢一帧数据给transmitter,transmitter发送耗费一段时间来发送这一帧数据,**这段时间内发送方CPU可以去做别的事情,等transmitter发送完成后会产生一个TXD中断,**该中断会导致事先绑定的中断处理程序执行,在中断处理程序中CPU会切换回来继续给transmitter放一帧数据,然后CPU切换离开;

不使用中断的工作情景是:发送方事先禁止TXD中断(当然也不需要给相应的中断处理程序了),发送方CPU给一帧数据到transmitter,然后transmitter耗费一段时间来发送这帧数据,这段时间CPU在这等着(CPU没有切换去做别的事情),待发送方发送完成后CPU再给它一帧数据继续发送直到所有数据发完

CPU是怎么知道transmitter已经发送完了?

原来是有个状态寄存器,状态寄存器中有一个位叫发送缓冲区空标志,transmitter发送完成(发送缓冲区空了)就会给这个标志位置位,CPU就是通过不断查询这个标志位为1还是0来指导发送是否已经完成的。

(3)因为串口通信是异步的,异步的意思就是说发送方占主导权。
也就是说发送方随时想发就能发,但是接收方只有时刻等待才不会丢失数据。
所以这个差异就导致发送方可以不用中断,而接收方不得不使用中断模式。

(3)210串行通信接口的时钟设计

(1)串口通信为什么需要时钟?因为串口通信需要一个固定的波特率,所以transmitter和receiver都需要一个时钟信号。

(2)时钟信号从哪里来?源时钟信号是外部APB总线(PCLK_PSYS,66MHz)提供给串口模块的(这就是为什么我们说串口是挂在APB总线上的),然后进到串口控制器内部后给波特率发生器(实质上是一个分频器),在波特率发生器中进行分频,分频后得到一个低频时钟,这个时钟就是给transmitter和receiver使用的。

(3)串口通信中时钟的设置主要看寄存器设置。重点的有:寄存器源设置(为串口控制器选择源时钟,一般选择为PCLK_PSYS,也可以是SCLK_UART),还有波特率发生器的2个寄存器。

(4)波特率发生器有2个重要寄存器:UBRDIVn和UDIVSLOTn,其中UBRDIVn是主要的设置波特率的寄存器,UDIVSLOTn是用来辅助设置的,目的是为了校准波特率的。

2、串口通讯的扩展模式

(1)FIFO(first in first out)模式及其作用

(1)典型的串口设计,发送/接收缓冲区只有1字节,每次发送/接收只能处理1帧数据。这样在单片机中没什么问题,但是到复杂SoC中(一般有操作系统的)就会有问题,会导致效率低下,因为CPU需要不断切换上下文。

(2)解决方案就是想办法扩展串口控制器的发送/接收缓冲区,譬如将发送/接收缓冲器设置为64字节,CPU一次过来直接给发送缓冲区64字节的待发送数据,然后transmitter慢慢发,发完再找CPU再要64字节。
但是串口控制器本来的发送/接收缓冲区是固定的1字节长度的,所以做了个变相的扩展,就是FIFO。

(3)FIFO就是first in first out,先进先出。fifo其实是一种数据结构,这里这个大的缓冲区叫FIFO是因为这个缓冲区的工作方式类似于FIFO这种数据结构。

(2)DMA(direct memory access)模式及其作用

(1)DMA direct memory access,直接内存访问。DMA本来是DSP中的一种技术,DMA技术的核心就是在交换数据时不需要CPU参与,模块可以自己完成。

(2)DMA模式要解决的问题和上面FIFO模式是同一个问题,就是串口发送/接收要频繁的折腾CPU造成CPU反复切换上下文导致系统效率低下。

(3)传统的串口工作方式(无FIFO无DMA)效率是最低的,适合低端单片机;高端单片机上CPU事物繁忙所以都需要串口能够自己完成大量数据发送/接收。这时候就需要FIFO或者DMA模式。FIFO模式是一种轻量级的解决方案,DMA模式适合大量数据迸发式的发送/接收时。

(3)IrDA(红外)模式及其用法

(1)IrDA其实就是红外,红外就是红外线通信(电视机、空调遥控器就是红外通信的)。

(2)红外通信的原理是发送方固定间隔时间向接收方发送红外信号(表示1或0)或者不发送红外信号(表示0或者1),接收方每隔固定时间去判断有无红外线信号来接收1和0.

(3)分析可知,红外通信和串口通信非常像,都是每隔固定时间发送1或者0(判断1或0的物理方式不同)给接收方来通信。因此210就利用串口通信来实现了红外发送和接收。

(4)210的某个串口支持IrDA模式,开启红外模式后,我们只需要向串口写数据,这些数据就会以红外光的方式向外发射出去(当然是需要一些外部硬件支持的),然后接收方接收这些红外数据即可解码得到我们的发送信息。

3、S5PV210串行通信编程实战

整个程序流程
整个串口通信相关程序包含2部分:
uart_init负责初始化串口
uart_putc负责发送一个字节
char uart_getc负责接收一个字节

(1)串口控制器初始化关键步骤

1)初始化串口的Tx和Rx引脚所对应的GPIO(查原理图可知Rx和Rx分别对应GPA0_1和GPA0_0)将其设置为串口模式
GPA0CON(0xE0200000),bit[3:0] = 0b0010 bit[7:4] = 0b0010 设置GPIO引脚为串口输出

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(2)关键寄存器(U 各种 con (control))的初始化
(1)ULCON0 = 0x3 // 0校验位、8数据位、1停止位
(2)UCON = 0x5 // 发送和接收都是polling mode
(3)UMCON0 = 0x0 // 禁止modem、afc
(4)UFCON0 = 0x0 // 禁止FIFO模式
(5)UBRDIV0和UDIVSLOT0和波特率有关,要根据公式去算的

(3)波特率的计算
在这里插入图片描述
(1)第一步,用PCLK_PSYS和目标波特率去计算DIV_VAL: DIV_VAL = (PCLK / (bps x 16)) -1
(2)第二步,UBRDIV0寄存器中写入DIV_VAL的整数部分
(3)第三步,用小数部分*16得到1个个数,查表得uBDIVSLOT0寄存器的设置值
在这里插入图片描述

波特率设置	DIV_VAL = (PCLK / (bps x 16))-1        (bps指的是我们要设置的目标波特率)
 PCLK_PSYS用66MHz算		余数0.8
rUBRDIV0 = 34;	
rUDIVSLOT0 = 0xdfdd;
	
PCLK_PSYS用66.7MHz算		余数0.18
DIV_VAL = (66700000/(115200*16)-1) = 35.18
	rUBRDIV0 = 35;
 (rUDIVSLOT中的1的个数)/16=上一步计算的余数=0.18
(rUDIVSLOT中的1的个数 = 16*0.18= 2.88 = 3
	rUDIVSLOT0 = 0x0888;		 31,查官方推荐表得到这个数字

(3)具体代码的实现

1.添加uart.c实现了串口的初始化,串口的发送函数,串口的接收函数;

#define GPA0CON		0xE0200000
#define UCON0 		0xE2900004
#define ULCON0 		0xE2900000
#define UMCON0 		0xE290000C
#define UFCON0 		0xE2900008
#define UBRDIV0 	0xE2900028
#define UDIVSLOT0	0xE290002C
#define UTRSTAT0	0xE2900010
#define UTXH0		0xE2900020	
#define URXH0		0xE2900024	

#define rGPA0CON	(*(volatile unsigned int *)GPA0CON)
#define rUCON0		(*(volatile unsigned int *)UCON0)
#define rULCON0		(*(volatile unsigned int *)ULCON0)
#define rUMCON0		(*(volatile unsigned int *)UMCON0)
#define rUFCON0		(*(volatile unsigned int *)UFCON0)
#define rUBRDIV0	(*(volatile unsigned int *)UBRDIV0)
#define rUDIVSLOT0	(*(volatile unsigned int *)UDIVSLOT0)
#define rUTRSTAT0		(*(volatile unsigned int *)UTRSTAT0)
#define rUTXH0		(*(volatile unsigned int *)UTXH0)
#define rURXH0		(*(volatile unsigned int *)URXH0)

// 串口初始化程序
void uart_init(void)
{
	// 初始化Tx Rx对应的GPIO引脚
	rGPA0CON &= ~(0xff<<0);			// 把寄存器的bit0~7全部清零
	rGPA0CON |= 0x00000022;			// 0b0010, Rx Tx
	
	// 几个关键寄存器的设置
	rULCON0 = 0x3;
	rUCON0 = 0x5;
	rUMCON0 = 0;
	rUFCON0 = 0;
	
	// 波特率设置	DIV_VAL = (PCLK / (bps x 16))-1        (bps指的是我们要设置的目标波特率)
	// PCLK_PSYS用66MHz算		余数0.8
	//rUBRDIV0 = 34;	
	//rUDIVSLOT0 = 0xdfdd;
	
	// PCLK_PSYS用66.7MHz算		余数0.18
	// DIV_VAL = (66700000/(115200*16)-1) = 35.18
	rUBRDIV0 = 35;
	// (rUDIVSLOT中的1的个数)/16=上一步计算的余数=0.18
	// (rUDIVSLOT中的1的个数 = 16*0.18= 2.88 = 3
	rUDIVSLOT0 = 0x0888;		// 3个1,查官方推荐表得到这个数字
}


// 串口发送程序,发送一个字节
void uart_putc(char c)
{                  	
	// 串口发送一个字符,其实就是把一个字节丢到发送缓冲区中去
	// 因为串口控制器发送1个字节的速度远远低于CPU的速度,所以CPU发送1个字节前必须
	// 确认串口控制器当前缓冲区是空的(意思就是串口已经发完了上一个字节)
	// 如果缓冲区非空则位为0,此时应该循环,直到位为1
	while (!(rUTRSTAT0 & (1<<1)));
	rUTXH0 = c;
}

// 串口接收程序,轮询方式,接收一个字节
char uart_getc(void)
{
	while (!(rUTRSTAT0 & (1<<0)));
	return (rURXH0 & 0x0f);
}

2.添加main.c(这里的main.c不是像单片机的主函数,而是作为之后调用其他文件提供了基础)在main.c里面调用串口有关函数;

void main(void)
{
	uart_init();
	
	while(1)
	{
		uart_putc('a');
		delay();
	}
}

3.修改makefile,在依赖中添加uart.o ,main.o

uart.bin: start.o led.o clock.o uart.o main.o
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页