TQ210——S5PV210串口通信
1、 串行与并行通信
串行通道中,有效信息被编码后串行的从单根传输线上传送出去。譬如发送ASCII字符’a’,我们可以将’a’对应的ASCII码97(0x61)分作8个bit在传输线上顺序依次传输。
与串行传输相对应的是并行传输,并行传输一般有多根并列的传输线可以同时传输多个bit数据。譬如8线的并行传输,可以同时传输8个比特。因此在线速相同的情况下并行传输效率更高,但同时布线成本更高。
串行硬件接口简单,只需要 3 根线:一是底线,二是发送,三是接收。
UART 使用标准的 TTL/CMOS 逻辑电平(0~5V)来表示数据,用 1 表示高 电平,用 0 表示低电平,为了增强数据的抗干扰能力、提高传输距离,通常将 TTL/CMOS 逻辑电平转换成 RS232 逻辑电平,一般用 1 表示(-3~12V),用 0 表 示 (3~12V)
(1)、帧:因为串行通信只有一根传输线,单位时间内只能传输一个比特位,因此要对传输信息进行编码发送。以常用的ASCII编码为例,则传输一个字符的ASCII编码称为一帧。一帧信息包括起始位、数据位、校验位、停止位等部分。
(2)起始位:一帧的开始标志。
(3)数据位:帧内有效数据,长度可配置为6、7、或8bit,一般ASCII编码下均使用8bit数据位。
(4)校验位:可选择奇/偶/无校验
(5)停止位:一帧的停止标志。停止位后传输线变为空闲状态,一直到下一帧的起始位
(6)波特率:串行传输中传输线上每秒钟传输的bit数称为波特率(Baudrate),波特率越大传输速率越快,同时传输对误差和干扰越敏感越容易出错。
2、 S5PV210 —— UART(4个独立的异步串行IO)
S5PV210的UART支持查询模式(Polling)、中断模式(Interrupt)、DMA模式三种模式
Polling模式可以用来实现stdio
中断模式中,CPU可以在不影响主任务的情况下,使用中断响应处理串行通信,因此可以用来实现高效的串行发送和接收。如果开启FIFO模式,效率将更高。
DMA模式中使用DMA通道来传输信息,适合批量数据的串行传输需要。
S5PV210的UART模块有两个可选时钟源:PCLKor SCLK_UART。我们大多数选择 PCLK 作为波特 率发生器的时钟源输入。
3、 寄存器
(1) ULCON
ULCON中主要设置串行通信的数据格式,一般会设置为0x03(普通模式、无校验、1停止位、8数据位)。
UCON中主要设置Tx/Rx中断类型(必须设置为Level,而不能是Pulse,这是S5PV210本身决定的)以及Rx/TxMode(如果不使用DMA,则一般会选择Interruptrequest or polling mode),所以UCON的常规赋值为0x305。
(2)UFCON
UFCON中设置Rx/TxFIFO enable/disable,用来使能或禁止FIFO模式
如果使能FIFO模式,则同时需要设置各串口的发送与接收FIFO的触发等级
注意接收时,RxFIFO中的实际值大于RxFIFO的触发等级时会触发RxInterrupt;而发送时,TxFIFO中的实际值小于TxFIFO时即会触发Tx Interrupt。因此一般会先填充TxFIFO然后再使用Tx Interrupt
(3)STATUS寄存器
UTRSTATn寄存器中记录发送(FIFO)空与接收ready状态标志位。我们在发送前必须检查发送空标志位,待其为1时才可发送;接收前必须检查接收好状态位,待其为1时才可去读接收字符。
UERSTATn寄存器记录通信过程中的错误,如帧错误、奇偶校验错误、覆盖错误等。
UFSTATn记录FIFO模式下的相关状态标志。
(4)发送/接受缓冲器
UART0~3各有一个发送缓冲寄存器和一个接收缓冲寄存器
UART发送时,将要发送的数据(一般为1Byte)放入UTXHn即可
UART接收时,从URXHn读取接收到的数据
(5)波特率设置寄存器
S5PV210的UART波特率设置寄存器与S3C2440等有很大不同,除UBRDIVn外增加了UDIVSLOTn寄存器作为精细调节以减小误差。
对于UBRDIVn:
DIV_VAL = (PCLK / (bps x 16)) −1
or DIV_VAL = (SCLK_UART / (bps x 16)) − 1
对于UDIVSLOTn:
DIV_VAL = UBRDIVn + (num of 1's inUDIVSLOTn)/16
比如配置波特率为 115200bps,时钟源选择 PCLK=66MHz
DIV_VAL = (66000000/(115200 x 16))-1 =35.8 - 1 = 34.8
UBRDIV0 = 34 ( DIV_VAL 的整数部分)
(num of 1's in UDIVSLOTn)/16 = 0.8 ( DIV_VAL 的小数部分)
(num of 1's in UDIVSLOTn) = 12
UDIVSLOT0 = 0xDDDD (查表)
(6)UARTINTERRUPT1
当一个UARTinterrupt来临时,会在UINTSP寄存器中产生一个相应bit的挂起标志。同时若UINTM中相应bit设置为中断允许,则会在UINTP中产生一个中断挂起。
(7)UARTINTERRUPT2
UART使用的中断共有4个,其中常用的是接收中断RXD。串口模块使能RXDInterrupt时,当串口接收到信息时则会触发中断,CPU响应中断并从URXHn读出数据。中断机制的引入,使CPU具有了一边执行主程序、一边进行串口通信接收的宏观上的并行能力。
4、串口编程操作步骤如下:
(1)、配置时钟,选择时钟源
(2)、配置 ULCONn 寄存器:设置数据位、停止位、 校验位、模式
(3)、配置 UCONn 寄存器:设置数据接收和发送模式、时钟源
(4)、设置 UFCONn:启用或静止 FIFO
(5)、配置 UBRDIVn 和 UDIVSLOTn:计算波特率
(6)、发送数据:等待发送器为空,将要发送的 8 位数据赋给发送缓存寄存器 UTXHn
(7)、接收数据:等待接收缓冲区有数据可读,从接收缓存寄存器 URXHn 中取出数据
.global _start
_start:
bl uart_init /* 串口初始化*/
bl main /* 跳转到C函数去执行*/
halt:
b halt
#define GPA0CON *((volatile unsigned int *)0xE0200000)
#define ULCON0 *((volatile unsigned int*)0xE2900000)
#define UCON0 *((volatile unsigned int*)0xE2900004)
#define UFCON0 *((volatile unsigned int*)0xE2900008)
#define UTRSTAT0 *((volatile unsigned int *)0xE2900010)
#define UTXH0 *((volatileunsigned int *)0xE2900020)
#define URXH0 *((volatile unsigned int*)0xE2900024)
#define UBRDIV0 *((volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 *((volatile unsigned int*)0xE290002C)
/* UART0初始化*/
void uart_init()
{
/*
** 配置GPA0_0为UART_0_RXD
** 配置GPA0_1为UART_0_TXD
*/
GPA0CON &= ~0xFF;
GPA0CON |= 0x22;
/* 8-bits/One stop bit/No parity/Normalmode operation */
ULCON0 = 0x3 | (0 << 2) | (0<< 3) | (0 << 6);
/* Interrupt request or polling mode/Normaltransmit/Normal operation/PCLK/*/
UCON0 = 1 | (1 << 2) | (0 <<10);
/* 静止FIFO*/
UFCON0 = 0;
/*
** 波特率计算:115200bps
** PCLK = 66MHz
** DIV_VAL = (66000000/(115200 x 16))-1 =35.8 - 1 = 34.8
** UBRDIV0 = 34(DIV_VAL的整数部分)
** (num of 1's in UDIVSLOTn)/16 = 0.8
** (num of 1's in UDIVSLOTn) = 12
** UDIVSLOT0 = 0xDDDD (查表)
*/
UBRDIV0 = 34;
UDIVSLOT0 = 0xDDDD;
}
static void uart_send_byte(unsigned char byte)
{
while (!(UTRSTAT0 & (1 << 2))); /* 等待发送缓冲区为空*/
UTXH0 = byte; /* 发送一字节数据*/
}
static unsigned char uart_recv_byte()
{
while (!(UTRSTAT0 & 1)); /* 等待接收缓冲区有数据可读*/
return URXH0; /* 接收一字节数据*/
}
void putchar(int c)
{
uart_send_byte(c);
/* 如果只写'\n',只是换行,而不会跳到下一行开头*/
if (c == '\n')
uart_send_byte('\r');
}
int getchar()
{
int c;
c = uart_recv_byte();
return c;
}
void puts(char *str)
{
char *p = str;
while (*p)
putchar(*p++);
putchar('\n');
}
#define GPC0CON *((volatile unsigned int *)0xE0200060)
#define GPC0DAT *((volatile unsigned int*)0xE0200064)
int main()
{
int c;
GPC0CON &= ~(0xFF << 12);
GPC0CON |= 0x11 << 12; // 配置GPC0_3和GPC0_4为输出
GPC0DAT &= ~(0x3 << 3); // 熄灭LED1和LED2
puts("UART Test in S5PV210");
puts("1.LED1 Toggle");
puts("2.LED2 Toggle");
puts("Please select 1 or 2 to Togglethe LED");
while (1)
{
c = getchar(); // 从串口终端获取一个字符
putchar(c); // 回显
putchar('\r');
if (c == '1')
GPC0DAT ^= 1 << 3; // 改变LED1的状态
else if (c == '2')
GPC0DAT ^= 1 << 4; // 改变LED2的状态
}
return 0;
}