一, 通信的概念
任何通信都要有信息传输载体,或者是有线的或者是无线的。
(1),通信双方事先需要约定好信息的表示方法和解析方法,做到一致,否则信息不能有效传递。
(2)同步通信:发送方和接收方按照同一个时钟节拍工作就叫同步(一般需要发送方给接收方发送信息同时发送时钟信号,接收方根据发送方给它的时钟信号来安排自己的节奏。)
(3)异步通信:发送方和接收方没有统一的时钟节拍、而各自按照自己的节拍工作就叫异步。(在双方通信的频率不固定时(有时3ms收发一次,有时3天才收发一次)不适合使用同步通信,而适合异步通信。异步通信时接收方不必一直在意发送方,发送方需要发送信息时会首先给接收方一个信息开始的起始信号,接收方接收到起始信号后就认为后面紧跟着的就是有效信息,才会开始注意接收信息,直到收到发送方发过来的结束标志。)
(4) 电平信号的传输线中有一个参考电平线(一般是GND),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。
(5) 差分信号的传输线中没有参考电平,所有都是信号线。然后1和0的表达靠信号线之间的电压差。
总结:电平信号容易受干扰, 差分信号不容易受干扰。
(6)并行接口:有多根数据传输和接收线, 数据并行输出/输入(8个bit在每一个bit在每一个数据线中在一个信号下统一输出/输入, 8个bit被统一时间发送), 效率较高, 但消耗成本过大。
(7) 串行接口:只用一根数据线就能完成数据的发送和接收,把8个bit数据一位一位的发送或接收, 效率较低, 成本较低, 用起来比较方便。所以现在一般都用串行接口。
(8) 单工: 接收和发送数据只能是单方向的,即在一根数据线中只能发或收。
(9)双工:接送和发送数据是双方向的,同时能发也能收。
(10)自动流控(AFC:Auto flow control):流控的目的是让串口通信非常可靠,在发送方速率比接收方快的时候流控可以保证发送和接收不会漏掉东西。(就是在发送时要判断对方是否接收完和对放是否接收)但现在一般都不用流控了
(11)RS232电平:-3V~-15V表示1;+3~+15V表示0 ;适合干扰大、距离远的情况
(12)TTL电平:TTL电平则是+5V表示1,0V表示0; 电压范围小,适合距离近且干扰小的情况(一般用在电路板内部两个芯片之间)
(13)串口通信的特点:异步、电平信号、串行
二, 什么是串口通讯?
串行通讯是指仅用一根接收线和一根发送线就能将数据以位(二进制位)进行传输的一种通讯方式。
典型的串口通信使用3根线完成,分别是地线(GND)、发送(Tx)、接收(Rx)。
由于串口通信是异步的,所以端口能够在一根线上发送数据同时在另一根线上接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶的校验。对于两个需要进行串口通信的端口,这些参数必须匹配,这也是能够实现串口通讯的前提。
(1)串口通信时,收发是一个周期一个周期进行的,没周期传输n个二进制位。这一个周期就叫做一个通信单元,一个通信单元是由:起始位+数据位+奇偶校验位+停止位组成的。
(2)波特率(bandrate),指的是串口通信的速率,也就是串口通信时每秒钟可以传输多少个二进制位。譬如每秒种可以传输9600个二进制位(传输一个二进制位需要的时间是1/9600秒,也就是104us),波特率就是9600;( 通信双方必须事先设定相同的波特率这样才能成功通信, 不能随意设置。)
(3)起始位: 起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。(一般是固定的)
(4)数据位: 数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定。传输数据时先传送字符的低位,后传送字符的高位。
(5)奇偶校验位: 奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位;如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位。
(6)停止位: 停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。
(7)空闲位: 空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。
三, s5pv210数据手册串口解析
1, 看懂s5pv210框图: 框图中包含时钟总线(APB), 发送区(包含FIFO, 发送缓冲区, 移位区, TxDn数据线), 接收区(包含FIFO, 发送缓冲区, 移位区, RxDn数据线), 一个控制单元, Buad-rate Generator(对时钟信号进行分频)
(1) 根据时钟框图,PSYS域部分, uart是挂在PSYS域的AXI 133MHz时钟下的 低频时钟(PCLK_PSYS)66MHz总线下。
(2) transmitter由发送缓冲区和发送移位器构成。我们要发送信息时,首先将信息进行编码(一般用ASCII码)成二进制流,然后将一帧数据(一般是8位)写入发送缓冲区(从这里以后程序就不用管了,剩下的发送部分是硬件自动的),发送移位器会自动从发送缓冲区中读取一帧数据,然后自动移位(移位的目的是将一帧数据的各个位分别拿出来)将其发送到Tx通信线上。
(3)receiver由接收缓冲区和接收移位器构成。当有人通过串口线向我发送信息时,信息通过Rx通信线进入我的接收移位器,然后接收移位器自动移位将该二进制位保存入我的接收缓冲区,接收完一帧数据后receiver会产生一个中断给CPU,CPU收到中断后即可知道receiver接收满了一帧数据,就会来读取这帧数据。
(4)串口控制器中有一个波特率发生器,作用是产生串口发送/接收的节拍时钟。波特率发生器其实就是个时钟分频器,它的工作需要源时钟(APB总线来),然后内部将源时钟进行分频(软件设置寄存器来配置)得到目标时钟,然后再用这个目标时钟产生波特率(硬件自动的)。
四, FIFO模式及其作用
(1) 典型的串口设计,发送/接收缓冲区只有1字节,每次发送/接收只能处理1帧数据。这样在单片机中没什么问题,但是到复杂SoC中(一般有操作系统的)就会有问题,会导致效率低下,因为CPU需要不断切换上下文。
(2) 解决方案就是想办法扩展串口控制器的发送/接收缓冲区,譬如将发送/接收缓冲器设置为64字节,CPU一次过来直接给发送缓冲区64字节的待发送数据,然后transmitter慢慢发,发完再找CPU再要64字节。但是串口控制器本来的发送/接收缓冲区是固定的1字节长度的,所以做了个变相的扩展,就是FIFO。
(3) FIFO就是first in first out,先进先出。fifo其实是一种数据结构,这里这个大的缓冲区叫FIFO是因为这个缓冲区的工作方式类似于FIFO这种数据结构。
五, DMA模式及其作用
(1) DMA direct memory access,直接内存访问。DMA本来是DSP中的一种技术,DMA技术的核心就是在交换数据时不需要CPU参与,模块可以自己完成。
(2) DMA模式要解决的问题和上面FIFO模式是同一个问题,就是串口发送/接收要频繁的折腾CPU造成CPU反复切换上下文导致系统效率低下。
(3) 传统的串口工作方式(无FIFO无DMA)效率是最低的,适合低端单片机;高端单片机上CPU事物繁忙所以都需要串口能够自己完成大量数据发送/接收。这时候就需要FIFO或者DMA模式。FIFO模式是一种轻量级的解决方案,DMA模式适合大量数据迸发式的发送/接收时。
六, IrDA模式及其用法
(1)IrDA其实就是红外,红外就是红外线通信(电视机、空调遥控器就是红外通信的)。
(2)红外通信的原理是发送方固定间隔时间向接收方发送红外信号(表示1或0)或者不发送红外信号(表示0或者1),接收方每隔固定时间去判断有无红外线信号来接收1和0.
(3)分析可知,红外通信和串口通信非常像,都是每隔固定时间发送1或者0(判断1或0的物理方式不同)给接收方来通信。因此210就利用串口通信来实现了红外发送和接收。
(4)210的某个串口支持IrDA模式,开启红外模式后,我们只需要向串口写数据,这些数据就会以红外光的方式向外发射出去(当然是需要一些外部硬件支持的),然后接收方接收这些红外数据即可解码得到我们的发送信息。
七, 程序流程
(1)整个串口通信相关程序包含2部分:uart_init负责初始化串口,uart_putc负责发送一个字节
串口控制器初始化关键步骤
(1)初始化串口的Tx和Rx引脚所对应的GPIO(查原理图可知Rx和Rx分别对应GPA0_1和GPA0_0)
(2)GPA0CON(0xE0200000),bit[3:0] = 0b0010 bit[7:4] = 0b0010
(3)初始化这几个关键寄存器UCON0 ULCON0 UMCON0 UFCON0 UBRDIV0 UDIVSLOT0
主要的几个寄存器
(1)ULCON0 = 0x3 // 0校验位、8数据位、1停止位
(2)UCON = 0x5 // 发送和接收都是polling mode
(3)UMCON0 = 0x0 // 禁止modem、afc
(4)UFCON0 = 0x0 // 禁止FIFO模式
(5)UBRDIV0和UDIVSLOT0和波特率有关,要根据公式去算的
代码
#define GPA0CON 0xE0200000
#define ULCON0 0xE2900000
#define UCON0 0xE2900004
#define UFCON0 0xE2900008
#define UMCON0 0xE290000C
#define UTRSTAT0 0xE2900010
#define UERSTAT0 0xE2900014
#define UMSTAT0 0xE290001C
#define UTXH0 0xE2900020
#define URXH0 0xE2900024
#define UBRDIV0 0xE2900028
#define UDIVSLOT0 0xE290002C
#define rGPA0CON *((volatile unsigned int*)GPA0CON )
#define rULCON0 *((volatile unsigned int*)ULCON0 )
#define rUCON0 *((volatile unsigned int*)UCON0 )
#define rUFCON0 *((volatile unsigned int*)UFCON0 )
#define rUMCON0 *((volatile unsigned int*)UMCON0 )
#define rUTRSTAT0 *((volatile unsigned int*)UTRSTAT0 )
#define rUERSTAT0 *((volatile unsigned int*)UERSTAT0 )
#define rUMSTAT0 *((volatile unsigned int*UMSTAT0 )
#define rUTXH0 *((volatile unsigned int*)UTXH0 )
#define rURXH0 *((volatile unsigned int*)URXH0 )
#define rUBRDIV0 *((volatile unsigned int*)UBRDIV0 )
#define rUDIVSLOT0 *((volatile unsigned int*)UDIVSLOT0 )
//串口初始化程序
void uart_init(void)
{
// 初始化Tx, Rx对应的GPIO引脚GPA0CON_0 GPA0CON_1 为0010(即设置为uart模式)
rGPA0CON &= ~(0xff << 0); //把 GPA0CON 寄存器的bit0 到bit 7清0
rGPA0CON |= 0x22;
//几个关键寄存器的设置
rULCON0 = 0x3; //设置uart线路控制寄存器,根据数据手册,这里设置禁用红外模式,无奇偶校验位, 发送一帧一个停止位, 每帧发送或接收的数据为8bit
rUCON0 = 0x5; //设置uart控制寄存器,根据数据手册,这里设置接收模式为轮询模式或中断请求01, 设置传输模式为中断请求或轮询模式01,时钟选择为PCLK 中断模式不启用,所以其他位全设置为0
rUMCON0 = 0; //设置uart的调制解调器, 禁用流控
rUFCON0 = 0; //禁用FIFO
//波特率设置 DIV_VAL = (PCLK / (bps x 16))-1
// 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
while (!(rUTRSTAT0 & (1 << 1))); //Tx/Rx状态寄存器, 判断Tx, Rx缓冲区的情况
rUTXH0 = c; //UART传输缓冲寄存器 要发的数据直接丢到这个寄存器里
}
//串口接收函数, 轮询方式, 接收一个字节
char uart_getc(void)
{
while (!(rUTRSTAT0 & (1 << 0))); //与发送同理
return (rURXH0 & 0x0f);
}
void delay();
void main(void)
{
uart_init(); //串口初始化程序
while (1)
{
uart_putc('a'); //串口发送函数
delay();
}
}
void delay(void)
{
volatile unsigned int i = 9000;
while (i--);
}
参考朱老师物联网大讲堂
s5pv210官方手册
凡亿教育