串口名字表示采用的通信方式为串行而不是并行。那么串行与并行的区别是啥呢?很简单,串行就是同一时刻只能传输一个bit,而并行则可以同时传输多个bits。乍一看,仿佛并行通信速度更快,而且并行的信号线越多,传输数据快。CPU的总线就是使用并行通信的,总线带宽也从8一路飙升到了目前的64。然而这是在CPU的内部,对于设备间通信而言,并行通信意味着需要更多的信号连线,更复杂的控制逻辑,设备成本飙升。所以目前外围设备通信中,串行通信才是主流。如USB就是串行通信的代表,IDE的硬盘接口也被串行的SATA取代。
串口,顾名思义,采用的串行通信,一次发送一个bit位,干净利索。
UART是Universal Asynchronous Receiver&Transmitter的缩写,意思是统一异步通信。这里的关键点是异步通信。那就说明还存在另一种通信方式,叫做“同步通信”。这两者到底啥区别呢?
具体看参考这篇文章。总结来说:
串口通信有自己工业标准,目前常用的标准有两种:一是RS232,二是RS485。他们并不兼容,各自规定了自己的电气标准,通信规范。参考这篇文章。
简单来说:
由于RS232接口标准出现较早,难免有不足之处,主要有以下四点:
(1) 接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL 电平不兼容故需使用电平转换电路方能与TTL电路连接。
(2) 传输速率较低,在异步传输时,波特率为20Kbps。
(3) 接口使用一根信号线和一根信号返回线而构成共地的传输形式,容易产生共模干扰,所以抗噪声干扰性弱。
(4) 传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右。
针对RS232接口的不足,于是就不断出现了一些新的接口标准,RS-485就是其中之一,它具有以下特点:
(1) RS-485的电气特性:逻辑“1”以两线间的电压差为+(2-6) V表示;逻辑“0”以两线间的电压差为-(2-6)V表示。接口信号电平比RS-232降低了,就不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL 电路连接。
(2) RS-485的数据最高传输速率为10Mbps 。
(3) RS-485接口是采用平衡驱动器和差分接收器的组合,抗共模干能力增强,即抗噪声干扰性好。
(4) RS-485接口的最大传输距离标准值为4000英尺,实际上可达3000米,另外RS-232接口在总线上只允许连接1个收发器,即单站能力。而RS-485接口在总线上是允许连接多达128个收发器。即具有多站能力,这样用户可以利用单一的RS-485接口方便地建立起设备网络。
S3C2440片内集成了UART控制器,支持3个独立的串口。TQ2440开发板对其中的0号串口引脚连接了电平转换电路把TTL电平转换成了RS232标准的电平,并搭配了标准的DB9接头。这样就可以直接和PC的DB9接口通过串口线连接调试了。
串口通信需要时钟来驱动,S3C2440提供了三种时钟来源供UART控制器使用,分别是PCLK,UEXTCLK,FCLK/n。具体使用哪个时钟源通过UCONn寄存器来设置。
波特率以时钟源的频率为基础,按照固定的公式计算得出。
UBRDIVn = (时钟源频率 / (波特率X16) -1
设置波特率的寄存器为UBRDIVn。
我们的实验中,选择PCLK作为时钟源,频率为50MHz,如果要想得到115200Hz的波特率,那么就需要设置
UBRDIVn = 50,000,000 / 115200*16 -1 = 26
这是串口通信的三要素,收发两端必须一致才能正确通信。通过ULCONn寄存器来设置。详见数据手册。
需要发送数据时
(1)把数据写入UTXHn寄存器,写完成导致:
* 发送完成状态清0;
* UART硬件自动把其中的数据串行化发送出去;
* 发送完毕UTRSTATn的发送完成状态置1。
(2)循环检测发送完毕状态位,等待发送完毕退出。
接收数据也很简单
当接收到数据时,会把数据反串行化后放入URXHn寄存器中,并设置URTSTATn寄存器来通知。
(1)循环检测UTRSTATn的接收完成状态,直到其为1
(2)读取URXHn寄存器,取出收到的字节,读完成会导致:
uart.c
#include "2440addr.h"
void uart_init(void)
{
rGPHCON &= ~(3<<4 | 3<<6);
rGPHCON |= 2<<4 | 2<<6;
rGPHUP = 0x00;
rULCON0 &= ~(0x3); /* 8 bits */
rULCON0 |= 0x3;
rULCON0 &= ~(1<<2); /* 1 stopbit */
rULCON0 &= ~(1<<5); /* no parity */
rULCON0 &= ~(1<<6); /* normal (not infrared) */
rUCON0 &= ~(3); /* open send mode */
rUCON0 |= 0x1;
rUCON0 &= ~(3<<2); /* open recv mode */
rUCON0 |= 1<<2;
rUCON0 &= ~(3<<10); /* PCLK */
rUBRDIV0 = 26; /* baud rate = 115200 */
}
void uart_set_baud(int baud)
{
if (baud == 115200 ) {
rUBRDIV0 = 26;
} else if(baud == 9600 ) {
rUBRDIV0 = 325;
} else {
}
}
void uart_set_stopbits(int n)
{
if (n < 1 || n > 2) return;
if (n == 1) {
rULCON0 &= ~(1<<2);
}else{
rULCON0 |= 0x4;
}
}
/* 0 - no, 1 - odd, 2 - even */
void uart_set_parity(int how)
{
if (how < 0 || how >2) return;
rULCON0 &= ~(0x7 << 3);
if (how == 0) {
rULCON0 |= (0 << 3);
} else if (how == 1) {
rULCON0 |= (4 << 3);
} else {
rULCON0 |= (5 << 3);
}
}
/* n=5,6,7,8 */
void uart_set_databits(int n)
{
if (n<5 || n>8) return;
rULCON0 &= ~3;
switch (n) {
case 5:
rULCON0 |= 0;
break;
case 6:
rULCON0 |= 1;
break;
case 7:
rULCON0 |= 2;
break;
case 8:
rULCON0 |= 3;
break;
default:
break;
}
}
/* send a byte and wait until sent. */
void uart_send_byte(unsigned char c)
{
rUTXH0 = c;
while ( 0 == (rUTRSTAT0 & (1<<2)) );
}
unsigned char uart_recv_byte()
{
while ( 0 == (rUTRSTAT0 & 1)) ;
return rURXH0;
}
void uart_send_str(char* msg)
{
while (*msg) {
uart_send_byte(*msg++);
}
}
这里我们原样模拟了标准C库的函数。
注意对C字符串换行符的处理,需要转换成终端换行符序列0x0A0D。
int putchar(int c)
{
if(c == '\n') {
uart_send_byte(0x0A);
uart_send_byte(0x0D);
} else {
uart_send_byte((unsigned char)c);
}
return 0;
}
int puts(const char* str)
{
while(*str){
putchar(*str++);
}
return 0;
}
io_init(0);
puts("Hello, World!\n");
标签v0.6。