DSP

串口通信初始化

2019-07-13 19:43发布

出处: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); //接收寄存器中只有八位是有效的,其他的都是无效的,所以在这里位与清零 }