平台:smart210
CPU:S5PV210
目标:通过官方文档【S5PV210_UM_REV1.1.pdf】,获取UART设置的相关信息,进一步学习UART编程
1.通过搜索UART,在P853找到该芯片的串口功能介绍
1. 摘取关键点,我们能够知道,210提供了4个UART接口,支持中断模式或者DMA(直接存储器访问)模式,每个UART包含有两个FIFO缓冲区(读与写),当然每个UART通道所支持的最大FIFO缓冲区也是有限制的,其中UART0支持256字节,UART1支持64字节,UART2与UART3各支持16字节的缓冲区。
2. 然后就是串口常见的波特率、停止位、校验位、帧宽、等设置,其中波特率来源于PCLK或者SCLK_UART,如图右框图部分
要想获得SLCK_UART,就得先通过某个CLK_SRC寄存器选择时钟信号源,然后再通过某个CLK_DIV寄存器设置分频得到。 当然我们也不需要这样,直接使用默认的PCLK作为串口输入时钟就行了
由于默认是PCLK,而且由官方文档得知UART工作在PSYS domain下,则推理得出UART的工作时钟应该是PCLK_PSYS,在我上一篇文章中已经讲解了子时钟源的设置步骤,照着那个思路去做,PCLK_PSYS就能设置(官方推荐值66Mhz)。只要得到PCLK_PSYS时钟,再设置一些寄存器就能设置波特率了。
3. S5PV210的UART所具备的功能
可知,普通的Rxd与Txd可以用来基于DMA或中断模式的使用,而各自配合上RTS与CTS,将具备自动流量控制功能。由于我们一般把UART用在RS232通讯上,所以一些多余的功能暂时用不到。
4.以UART0为例,介绍各个寄存器
基本常用的那几个有ULCON0、UCON0、UFCON0、UMCON0、UTXH0、URXH0、UTRSTAT0等
其中,ULCON0用来配置一些参数,如 普通/红外模式,奇偶校验,停止位,帧宽。
UCON0用来配置UART0的功能,包括接收/发送模式(中断或轮询/DMA),在一帧的时间内发送中断信号,回环模式,中断相关的一系列设置,DMA设置、波特率设置等
UFCON0是有关FIFO的一系列设置,首位是使能位,接下来是清空缓冲区,FIFO触发中断的阀值设置等。
UMCON0是跟AFC(自动流量控制)相关的,感觉上和FIFO的设置有点像,应该很少会用到这个功能,但是关掉是必须的。
UTRSTAT0的第0位用来查询接收缓冲区是否非空(0为空),第1位用来查询发送缓冲区是否空(0为非空),第2位是同时查询发送移位寄存器与发送缓冲区是否空(0为非空)
如果使用了FIFO,设置了FIFO触发中断阀值为0时,可以使用UTRSTAT0来查询缓冲区。
如果使用了FIFO,设置了FIFO触发中断阀值不为0时,则需要查询UFSTAT0而不是UTRSTAT0。
5. 特别讲解之比特率设置的方法
UBRDIV0(16位)与UDIVSLOT0(16位)是与UCLK相关的寄存器,进一步决定了baud rate(波特率)的值(单位是bps),在有了PCLK_PSYS与目标波特率的情况下,怎么设置UBRDIV0与UDIVSLOT0呢?
这里引进了一个中间变量DIV_VAL,这个中间变量与PCLK_PSYS、bps有这样的函数关系:DIV_VAL=(PCLK_PSYS/(bps*16))-1
同时DIV_VAL与UBRDIV0与UDIVSLOT0也有这样的函数关系:DIV_VAL=UBRDIV0+光棍数/16 (光棍数是指UDIVSLOT0这个16位数在二进制形式下,1的数目,三星好神奇!)
举个小例子吧,假设我现在好不容易有了PCLK_PSYS=66Mhz,那么假设我需要115200bps,那么DIV_VAL=66000000/(115200*16)-1=34.81左右,对应过来就是UBRDIV0=34,光棍数=12.96约为13吧,
那么UDIVSLOT0=b0001 1111 1111 1111 =0x1fff了,数一数,是不是13根光棍呢?
再举一个例子,假设现在的PCLK_PSYS=66.5Mhz,需要115200bps,则DIV_VAL=35.079左右,对应过来UBRDIV0=35,光棍数1.26左右,约为1,那么UDIVSLOT0=0x1。
刚又测试成功,PCLK_PSYS=66.5Mhz,需要9600,算得UBRDIV0=431,UDIVSLOT0=0xefff。
6. 程序实例分析(这个是准备被调用的uart.c,main就一个很简单的getc()然后putc()而已)
#define GPA0CON ( *((volatile unsigned long *)0xE0200000) ) #define GPA1CON ( *((volatile unsigned long *)0xE0200020) ) // UART相关寄存器 #define ULCON0 ( *((volatile unsigned long *)0xE2900000) ) #define UCON0 ( *((volatile unsigned long *)0xE2900004) ) #define UFCON0 ( *((volatile unsigned long *)0xE2900008) ) #define UMCON0 ( *((volatile unsigned long *)0xE290000C) ) #define UTRSTAT0 ( *((volatile unsigned long *)0xE2900010) ) #define UERSTAT0 ( *((volatile unsigned long *)0xE2900014) ) #define UFSTAT0 ( *((volatile unsigned long *)0xE2900018) ) #define UMSTAT0 ( *((volatile unsigned long *)0xE290001C) ) #define UTXH0 ( *((volatile unsigned long *)0xE2900020) ) #define URXH0 ( *((volatile unsigned long *)0xE2900024) ) #define UBRDIV0 ( *((volatile unsigned long *)0xE2900028) ) #define UDIVSLOT0 ( *((volatile unsigned long *)0xE290002C) ) #define UINTP ( *((volatile unsigned long *)0xE2900030) ) #define UINTSP ( *((volatile unsigned long *)0xE2900034) ) #define UINTM ( *((volatile unsigned long *)0xE2900038) ) //上面提到的波特率分频值 #define UART_UBRDIV_VAL 35 #define UART_UDIVSLOT_VAL 0x1 void uart_init() { GPA0CON = 0x22222222;//配置这些GPIO作为串口通讯用 GPA1CON = 0x2222; UFCON0 = 0x1;//FIFO启动,中断阀值设为0, UMCON0 = 0x0;//流控制就不用了 ULCON0 = 0x3;//0000_0011 no parity,no stop bit,normal,8bits UCON0 = 0x5;//读写均采用中断或者轮询模式,由于还关了中断相关设置,则只能用轮询模式了 UBRDIV0 = UART_UBRDIV_VAL; UDIVSLOT0 = UART_UDIVSLOT_VAL; } char getc(void) { while (!(UTRSTAT0 & (1<<0)));//由于使用了FIFO但是阀值为0,所以可以使用UTRSTAT来查询,接收区状态为1即非空,!(1&1)=0,跳出轮询以读取缓冲区数据 return URXH0; } void putc(char c) { while (!(UTRSTAT0 & (1<<2)));//发送区与发送移位寄存器空的时候UTRSTAT0的bit2为1,!(1&1)=0,跳出轮询以发送数据到缓冲区 UTXH0 = c; }