一、串口通信的基础
1、串口通信(UART)的全称是:Universal Asynchronous Receiver /Transmitter(通用异步接收和发送)
2、异步通信和同步通信:
(1)、异步通信:
发送方和接收方是工作在两个不同的时钟频率上的,也就是接收方有自己工作时的时钟频率,发
送方也有自己的工作的时钟频率。
在发送数据的时候,所发送的字符的时间间隔是可以任意的。因为它是按照字符为单位进行发送
的,每一个字符都有一个起始位,结束时有一个结束标志位。因此,接收方就知道什么时候开始
接收信息,什么时候接收完信息了。
(2)、同步通信:
通信双方必须工作在同一个时钟频率上。所以发送方在通信前就要发送一个时钟信号给接收方,让
接收方调整自己的时钟频率,来让双方通信频率一致。
通信数据必须连续发送,中间不能有间隔。如果中间没有信息发送,就用空字符来填充。这样就可
以快速地发送多个数据。
3、电平信号
TTL:用在UART和一些单片机上+5v表示1,0v表示0。
RS232:用在电脑上+15v到-3v表示1,3v到15v表示0。
差分信号:有两根信号线,两个信号线上的信号振幅相同,相位相反。抗干扰性强:噪声信号在两条线上的
差值为零。
4、串行接口和并行接口
串行接口:一条数据线加一条地线(或者两条数据线加一条地线:一条接收,一条发送),数据在数据线上
是一位一位的传输过去的,这就是串行传输。
并行接口:8条数据线,数据在每一条数据线也是一位一位传输的,8条数据线和一条地线,一次就可以传8
位。
5、全双工通信
全双工通信:可以同时接收和发送数据
半双工通信:可以接收或者发送数据,但是同一时间内只能接收或者发送
单工通信:只能接收或者只能发送数据
6、波特率和起始位,数据位,奇偶校验位和停止位
我们的一个通信单元就是:起始位+数据位+奇偶校验位+停止位
起始位:告诉对方开始通信。
数据位:要传输的数据(5-8位可选)。
奇偶校验位:查看数据是否出错。
停止位:告诉对方一个通信单元接收。
二、S5pv210串口通信的具体介绍
1、 我们可以看到S5pv210中有四个UART接口:ch0,ch1,ch2,ch3.四个接口都支持红外接收和DMA或者中断控制
ch0有256个字节的FIFO,ch1有64个字节,ch2和ch3有16个字节。
还支持自动流控,和握手发送或者接收功能。
2、 S5pv210的串口工作流程简介
发送数据:
在FIFO模式下,把我们的要发送的数据放在发送缓存寄存器上,发送缓存寄存器就会自动把一个字节
的数据复制到发送移位器上,开始发送的时候,发送移位寄存器就会自动把一位数据放到发送数据线上,
当发送完之后发送缓存寄存器再给一字节数据给发送移位器,直到发送缓存寄存器的数据发送完了,这时
候我们只需要发送缓存寄存器放数据,它就又开始工作了。
接收数据:
在FIFO模式下,开始接收的时候,接收移位器就会自动地从接收数据线上一位一位数据地接收,当接收
移位寄存器接满了一个字节的时候,接收缓存寄存器就会复制这一字节的数据到接收缓存寄存器上,然后接
收移位器继续接收数据,直到装满了整个接受缓存寄存器,这时候我们只需要取走接收缓存寄存器内的数据
它就又开始工作了。
总结:发送数据只需要向发送缓存寄存器上丢数据就可以了,接收数据,就读取接收缓存寄存器就可以了。
FIFO和NON-FIFO模式的区别:FIFO模式下整个缓存寄存器都可以使用。
NON-FIFO模式:只可以使用缓存寄存器的一个字节,这时候cpu必须时刻看着缓存寄存器才不会遗漏数据。
3、S5pv210的串口中断,DMA模式和红外线模式简介
(1)、串口中断模式,也就是什么情况下会触发中断
FIFO模式下:
接收数据:Rx FIFO内的数据多于你设置的那个水平的时候(可以不是满的,也就是设置快满的时候)
超时(超过发三个字符的时间就算超时,不过你也可以自己设置超时的时间)
发送数据:Tx FIFO内的数据少于你设置的那个水平的时候(可以不是全空,也就是快空的时候)
错误:有4种
NON-FIFO模式:
接收数据:满的时候;
发送数据:空的时候(因为它只有一个字节)
DMA模式:
DMA(直接内存存储)其实就是一大块内存,和FIFO的功能一样,不过FIFO的内存空间大很多。你把数据
放在DMA上,它就可以代替CPU,当FIFO空的时候,就向FIFO放数据。
红外线模式:
开启红外线模式,我们只需要向串口写数据,这些数据就会以红外线的方式传输信息给对方。
4、S5pv210的串口时钟简介
时钟来源是PCLK_PSYS:66.7MHZ,也可以在clock controller那里设置为SCLK_UART。然后经过UBDIV和UDIVSLOT
分频,得到最终的UCLK时钟。
计算方式:这里我们设置波特率为115200
DIV_VAL=(66.7MHZ/(115200*16))- 1 = 36.08-1=35.19
然后:UBRDIVn = 35 x/16 = 0.19 得 x = 3.04
然后查询上表可知UDIVSLOTn = 0x0888;
5、查询原理图,看串口接在哪个GPIO上
上图可知:我们的串口接在了GPA0_0和GPA0_1处(我们的代码只是设置了UART0)
6、设置GPIO为UART模式
只要往GPA0CON[0]和GPA0CON[1]写0x2就可以了,选择0010
7、UCON0:选择DMA模式,中断模式/轮询模式,红外线模式;选择时钟源。我们设置的值是0x5,选择
中断或者轮询模式
(1)、ULCON0:设置数据位长度,停止位,奇偶校验位,正常模式/红外模式。我们设置的值是0x3
(2)、选择DMA模式,中断模式/轮询模式,红外线模式;选择时钟源。我们设置的值是0x5,选择
中断或者轮询模式。
(3)、UFCON:选择FIFO模式的,我们设置的值是0,不使用FIFO模式
(4)、UMCON:选择中断模式,自动流控,FIFO中断水平这些的,我们设置为0,不使用中断,使用轮询方式。
(5)、UTRSTAT0:通过查看这个寄存器,我们可以知道接收是否接收完和发送是否发送完的,如果第一位是1表
示已经接收完,第二位是1表示已经发送完。
(6)、UTXH0:发送缓存寄存器,要发送什么数据,就往这个寄存器写值就可以了。
(7)、URXH0:接收缓存寄存器,读取这个寄存器,就可以接收到别人通过串口发过来的信息了。
三、完整代码
#define GPA0CON 0xE0200000
#define ULCON0 0xE2900000
#define UCON0 0xE2900004
#define UFCON0 0xE2900008
#define UMCON0 0xE290000C
#define UBRDIV0 0xE2900028
#define UDIVSLOT0 0xE290002C
#define UTRSTAT0 0xE2900010
#define UTXH0 0xE2900020
#define URXH0 0xE2900024
#define rGPAOCON (*(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 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)
{
//第一步:首先就是设置GPA0,设置这里是为了打开端口,只有打开端口,信息才会发送出去嘛
//写上左移还是比较好的,虽然这次只是第一位开始:0~7
//位操作就是先把那几位清零,然后赋值就ok了
rGPAOCON &= (~(0xff<<0)); //这里少了一个分号,导致了30行出了错误
rGPAOCON |= 0x00000022; //这里不可以写成这样:0b00100010bit[3:0] = 0b0010 bit[7:4] = 0b0010
//第二步:设置关键的寄存器,这些都是在数据手册中看的,在笔记中有。
//这里是初始化UART控制器
rULCON0 = 0x3;
rUCON0 = 0x5;
rUFCON0 = 0;
rUMCON0 = 0;
//第三步:设置波特率,计算波特率
//这里的那个频率可以取66.7MHz,也是可以取66MHz
//DIV_VAL = (PCLK / (bps x 16)) ?1
//DIV_VAL = (66000000 / (115200 x 16)) - 1 算出来是34,余数是0.80729
//rUBRDIV0 = 34;
//(num of 1's in UDIVSLOTn)/16 = 0.7 0.8 可以算出是12.8
//rUDIVSLOT0 = 0xdfdd;
//当是66700000时候,算出来就是35.1870 0.2算出来是3,之后在下面查表,就看到相应的值了
//这样就初始化串口了
rUBRDIV0 = 34;
rUDIVSLOT0 = 0xdfdd;
}
//发送一个字节
void uart_putc(char c)
{
//发送字符的时候,要注意CPU比发送缓存区的速度要快很多的,所以我们要看发送缓存区是否已经发送完数据了
//之后再给数据它
//我们这里0就是非空,1就是空了,所以要等1的时候才跳出循环,而要跳出循环需要是0的时候才可以,所以
//加上了!
while(!(rUTRSTAT0 & (1<<1))); //!是逻辑运算符,如果是真(非0)就变为假(0)
rUTXH0 = c;
}
//接收一个字节
char uart_getc(void)
{
//只是接收一个字节
while(!(rUTRSTAT0 & (1<<0)));
return (rURXH0 & 0xff); //接收寄存器中只有八位是有效的,其他的都是无效的,所以在这里位与清零
}
欢迎各位指出不足之处