出处:http://www.cnblogs.com/nibuyaoni/p/5732526.html
(1)先来说说串口通信的特点。
串行通信(UART)的全称是:Universal asynchronous receiver/transmitter(通用异步接收和发送)
1.异步通信
异步通信的特点:(1)发送方和接收方是工作在两个不同的时钟频率上的,也就是接收方有自己工作时的时钟频率,发送方也有自己工作时的时钟频率。(2)在发送数据的时候,所发送的字符的时间间隔是可以任意的。因为它是按照字符为单位进行发送的,每一个字符开头都有一个开始位,结束时候有一个结束标志位。因此,接收方就知道什么时候开始接收信息,什么时候接收完信息了。
和异步通信不同的有同步通信:(1)通信双方必须工作在同一个时钟频率上。所以发送方在通信前就要发送一个时钟信号给接收方,让接收方调整自己的时钟频率,来让通信双方步伐一致。(2)通信数据必须连续发送,中间不可以有间隔。如果中间没有信息发送,就以空字符来填充。这样,就可以快速地发送多个数据。
2.电平信号
电平信号:一根信号线和一根地线,信号线上发送高电平就为1,低电平就为0.
电平信号比较常用的可以分为TTL电平信号和RS232电平信号。
TTL(Transistor-Transistor Logic)电平信号:用在UART上和一些单片机上,+5V表示1,0V表示0
RS232电平:用在电脑上,-15V到-3V表示1,3V到15V表示0.
差分信号:有两根信号线,两个信号线上的信号振幅相同,相位相反。抗干扰性强的原因就是:噪声信号在两条信号线上的差值为0.
3.串行接口
串行接口:一条数据线加一条地线(或者两条数据线加一条地线:一条接收,一条发送),数据在数据线上是一位(bit)一位(bit)的传输过去的,这就是串行传输。
并行接口:8条数据线,数据在每一条数据线也是一位一位传输的,8条数据线和一条地线,一次就可以传8位(一个字节)。
4.全双工通信
全双工通信:可以同时接收和发送数据。
半双工通信:可以接收或者发送数据,但是同一时间内,只能接收或者只能发送。
单工通信:只能接收或者只能发送数据。
(2)串行通信细节部分
1.波特率
波特率就是每一秒传输的bit位的个数,例如115200波特率,就是一秒钟传输115200个bit位,折合就是15KB每秒左右。通信的之前要设定好波特率,其实就是时钟频率,例如115200波特率就是115200Hz。
2.起始位,数据位,奇偶校验位和停止位
我们的一个通信单元就是:起始位+数据位+奇偶校验位+停止位
起始位:告诉对方开始通信。数据位:要传输的数据(有5-8位可选)。奇偶校验位:查看数据是否出错。停止位(有1,1.5,2个停止位可选):告诉对方一个通信单元结束。
(3)S5PV210的串口支持的功能简介
我们可以看到S5PV210中有四个UART接口:ch0,ch1,ch2,ch3.四个接口都支持红外线接收和DMA或者中断控制。
还有:ch0有256个字节的FIFO(First In First Out,先入先出,缓存区),Ch1有64个字节,ch2和ch3有16个字节。
还支持自动流控功能,和握手发送或者接收功能。
(4)S5PV210的串口的工作流程简介
首先,我们的串口是工作在外围设备总线上的。
发送数据:在FIFO模式下,把我们的要发送出去的数据放在发送缓存寄存器(Transmit Buffer Register)上,发送缓存寄存器就会自动(硬件自动,不需要我们插手了)把一个字节的数据复制到发送移位器(Transmit Shifter)上,开始发送的时候,发送移位器就会自动(硬件自动)地把一位(bit)一位数据放到发送数据线上(TXDn),当发送完之后,发送缓存寄存器再给一字节数据发送移位器,直到发送缓存寄存器内的数据发送完了,这时候我们只需要向发送缓存寄存器放数据,它就又开始自动工作了。
接收数据:在FIFO模式下,开始接收的时候,接收移位器(Receive Shifter)就会自动(硬件自动)地从接收数据线上(RXDn)一位(bit)一位数据地接收,当接收移位器接满了一个字节的时候,接收缓存寄存器(Receive Buffer Register)就会自动复制这一字节数据到接收缓存寄存器上,然后接收移位器继续接收数据,直到装满了整个接收缓存寄存器,这时候我们只需要取走接收缓存寄存器内的数据,它就又开始自动工作了。
总结: 要发送数据,就向发送缓存寄存器(Transmit Buffer Register)上丢数据就可以了。
要接收数据,就读取接收缓存寄存器(Receive Buffer Register)就可以了。
FIFO模式和NON-FIFO模式的区别:FIFO模式:整个缓存寄存器都可以使用。Non-FIFO模式:只可以使用缓存寄存器的一个字节,这时候CPU必须时刻看着缓存寄存器才不会遗漏数据。
(5)S5PV210的串口中断模式,DMA模式和红外线模式简介
1.串口的中断模式,也就是什么情况下会触发中断
FIFO模式下:
接收数据:(1)Rx FIFO内的数据多于你设置的那个水平的时候(可以不是满的,也就是设置是快满的时候)(2)超时(超过发三个字符的时间就算超时,不过你也可以自己设置超时的时间)
发送数据:(1)Tx FIFO内的数据少于你设置的那个水平的时候(可以不是全空的时候,也就是可以设置是快空的时候)
错误中断:有四种
Non-FIFO模式下:
接收数据:满的时候;发送数据:空的时候;(因为它只有一个字节)
2.DMA模式
DMA(直接内存存储)其实就是一大块内存,和FIFO的功能一样,不过比FIFO的内存空间大很多。你把数据放在DMA上,它就可以代替CPU,当FIFO空的时候,就向FIFO放数据。
3.红外线模式
开启红外线模式,我们只需要向串口写数据,这些数据就会以红外线的方式传输信息给对方。
(5)S5PV210的串口的时钟简介
时钟来源是PCLK_PSYS:66.7MHz,也可以在clock controller那里设置为SCLK_UART。然后经过UBDIV和UDIVSLOT分频,得到最终的UCLK时钟。
计算方式:这里我们设置波特率是115200
DIV_VAL = (66.7MHz/(115200x16))-1=36.08-1=35.19
然后,UBRDIVn = 35. x/16 = 0.19 -> x = 3.04 然后查询下表,可知:UDIVSLOTn = 0x0888;
(6)查看原理图,看串口接在哪个GPIO处
上图可知:我们的串口接在了GPA0_0和GPA0_1处(我们的代码只是设置了UART0)
(7)设置GPIO为UART模式。
只要往GPA0CON[0]和GPA0CON[1]写0x2就可以了,选择0010
(8)关于串口的关键寄存器的设置:ULCON0,UCON0,UMCON0,UFCON0,UTRSTAT0,UTXH0,URXH0七个寄存器
1.ULCON0:设置数据位长度,停止位,奇偶校验位,正常模式/红外线模式。我们设置的值是0x3
2.UCON0:选择DMA模式,中断模式/轮询模式,红外线模式;选择时钟源。我们设置的值是0x5,选择中断或者轮询模式。
3.UFCON:选择FIFO模式的,我们设置的值是0,不使用FIFO模式。
4.UMCON:选择中断模式,自动流控,FIFO中断水平这些的,我们设置为0,不使用中断,使用轮询方式。
5.UTRSTAT0:通过查看这个寄存器,我们可以知道接收是否接收完和发送是否发送完的,如果第一位是1表示已经接收完,第二位是1表示已经发送完。
6.UTXH0:发送缓存寄存器,要发送什么数据,就往这个寄存器写值就可以了。
7.URXH0:接收缓存寄存器,读取这个寄存器,就可以接收到别人通过串口发过来的信息了
(9)发上完整的代码
#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); //接收寄存器中只有八位是有效的,其他的都是无效的,所以在这里位与清零
}