分类: LINUX
2015-01-22 00:25:19
原文地址:裸机系列-UART串口 作者:kine1314
开始前,线路处于空闲状态,送出连续“1”。传送开始时首先发一个“0”作为起始位,
然后出现在通信线桑的时字符的二进制编码数据。
每个字符的数据位长可以约定为:5 位、6 位、7 位或 8 位,一般采用 ASCII 编码,后
面时奇偶校验位,根据约定,用奇偶校验位将所传的字符中为“1”的位数凑成奇数个或偶
数个。也可以约定不要奇偶校验,这样就取消奇偶校验位。
最后时表示停止位的“1”信号,这个停止位可以约定连续 1 位、1.5 位或 2 位的时间宽
度。
至此一个字符传送完毕,线路又进入空闲,持续为“1”。经过一段随机的时间后,下一
个字符开始传送。
传输速率
每一个数据位的宽度定于发送波特率的倒数。微机异步串行通信中,常用的波特率为110、150、300、600、1200、2400、4800、9600、57600、115200 等。
电气特性
要完成基本的通信功能,实际上值需要 RXD、TXD 和 GND 即可,但由于 RS-232-C标准所定义的高、低电平信号于 S3C2410A 系统的 LVTTL 电路定义的高、低电平信号完全不同,LVTTL 的标准逻辑“1”对应 2-3.3V,标准逻辑“0”对应 0-0.4V,而 RS-232-C
标准采用负逻辑方式,逻辑“1”对应-5――15V,标准逻辑“0”对应+5-+15V。显然
两者间要进行通信,必须经过电平的转换,转换芯片采用 max232
串口通信
当通信距离较近时,只需使用少数几根信号线就可实现数据通信。最简单的情况只需三
根线(发送线、接收线、信号地线)便可实现全双工异步串行通信。
无 Modem 时,最大通信距离按如下方式计算:RS-232C 标准规定,当误码率小于 4%
时,要求导线的电容值应小于 2500PF。对于普通导线,其电容值约为 170PF/M。则允许距
离 L=2500PF/(170PF/M)=15M
这一距离的计算,是偏于保守的,实际应用中,当使用 9600bps,普通双绞屏蔽线时,
距离可达 30~35 米。
零 Modem 的最简连线(3 线制)
下图是零 MODEM 方式的最简单连接(即三线连接),图中的 2 号线与 3 号线交叉连接
是因为在直连方式时,把通信双方都当作数据终端设备看待,双方都可发也可收。在这种方
式下,通信双方的任何一方,只要请求发送 RTS 有效和数据终端准备好 DTR 有效就能开始
发送和接收。
(1)RTS 与 CTS 互联:只要请求发送,立即得到允许
(2)DTR 与 DSR 互联:只要本端准备好,认为本端立即可以接收(DSR、数传机准备好)
S3C2410A 的 UART 介绍
S3C2410A 的 UART(通用异步串行接口)单元提供了三个独立的异步串行 I/O 端口,
每个都可以在中断和 DMA 两种模式下进行,他们支持的最高波特率是 115.2Kbps。每个
UART 通道包含 2 个 16 位 FIFO 分别提供个接收和发送。
S3C2410A 的 UART 可以进行以下参数的设置:可编程的波特率,红外收/发模式,1 或
2 个停止位,5 位、6 位、7 位或 8 位数据宽度和奇偶校验.
每个 UART 包含波特率发生器、接收器,发送器和控制单元。波特里发生器以 MCLK
为时钟源。发送器和接收器包含 16 字节 FIFO 寄存器和移位寄存器,当发送数据的时候,
数据先写道 FIFO,然后拷贝到发送移位寄存器,然后从数据输出端口(TxDn)依次被移位
输出。被接收的数据也同样从接收端口(RxDn)移位输入到移位寄存器,然后拷贝到 FIFO
中。
数据发送操作
数据发送帧的格式是可编程的,它包含一个开始位,5 到 8 个数据位,一个可选的奇偶位和一个或两个停止位,这些可以通过线性控制器(UCONn)来设置。发送器也能够产生发送中止条件。中止条件迫使串口输出保持在逻辑 0 状态,这种状态保持一个传输帧的时间长度。通常在一帧传输数据完整地传输完之后,在通过这个全 0 的状态将中止信号发送给对方。中止信号发送之后,传送数据连续放到 FIFO 中(在不使用 FIFO 模式下,将被放到输出保持寄存器)。
数据接收操作
与数据发送一样,数据接收的帧也是可以编程的,它包含一个开始位,5 到 8 个数据,
一个可选的奇偶位和一位到两位停止位,他们是通过线性控制器(ULCONn)来设置的。接
收器能够检测溢出错误,奇偶检验错误,帧错误和中止状况,每种情况下都将会产生一个错
误标志置位。
波特率发生器
每个 UART 的波特率发生器为传输提供了串行移位时钟。波特率产生器的时钟源可以
从 S3C2410A 的内部系统时钟或 UCLK 中来选择。波特率由时钟源(PCLK 或 UCLK)16分频和 UART 波特率除数寄存器(UBRDIn)指定的 16 位除数决定。UBRDIn 的置可以按
照下式确定:
UBRDIn=(int)(PCLK/(bps*16))-1
除数的范围为 1 到(16**2-1)。例如,如果波特率为 115200bps 且 PCLK 或 UCLK 为
50MHz,则 UBRDIn 为:
UBRDIn =(int)(50000000/(115200×16))-1
=(int)(27.1)-1
=26
S3C2410 的 UART 相关寄存器
ULCONn――串口线性控制寄存器
线性控制寄存器主要用来规定传输帧的格式。下面表格所进哦线性控制寄存器的地址和
位定义
下面我们就给出如何用s3c2440来实现非FIFO的UART通信。要实现某种通信,就必须遵循该通信协议。UART的协议包括传输数据的位数,停止位的位数,以及是否进行奇偶校验,这些设置是利用ULCONn寄存器完成的。另一个很重要的地方就是设置波特率。s3c2440波特率的时钟源有三个:PCLK、FCLK/n和UEXTCLK。时钟源的选择是由UCONn的第10位和第11位来完成的。波特率的具体计算公式为:
时钟源频率÷(波特率×16)-1
这个计算结果很可能是小数,把该小数取最接近的整数,放入寄存器UBRDIVn中就完成了波特率的设置。如我们选择波特率的时钟源为PCLK,它为50MHz,我们设置的波特率为115.2kHz,通过上式计算的结果为26.13,取整后得到26,那么我们把26放入UBRDIVn中即可。由于我们没有使用FIFO和MODEM,所以可以不用设置FIFO控制寄存器UFCONn和MODEM控制寄存器UMCONn。通过以上寄存器的设置,UART就可以正常传输数据。
接收到的数据是放到接收缓存器URXHn中,要发送数据时,是把数据放入发送缓存器UTXHn中。由于UART是通过字节方式传输数据的,因此要区分是大端模式还是小端模式,也就是说这两个寄存器在这两种模式下,所在的地址是不同。为了了解当前数据传输的各种状态,还需要一些状态寄存器。传输状态寄存器UTRSTATn非常有用,它的第0位可以用来判断接受缓存器内是否有可接收的数据,第1位和第2位可以用来判断发送缓存器中是否为空,为空时可以发送数据。由于在这里我们不进行传输数据时错误的判断,因此错误状态寄存器UERSTATn不需要,FIFO状态寄存器UFSTATn和MODEM状态寄存器UMSTATn在这里也不需要。
我们给出UART通信的两种方法:查询和中断。为了验证程序,使用任一款的串行通信软件来实现PC和s3c2440之间的通信即可。
首先给出的是查询程序。它是在主程序的循环体内不断查询UART端口,当有数据来时,就接收数据,并再通过UART发送该数据。然后根据所接收数据的不同,分别执行不同的内容,如点亮、熄灭LED,蜂鸣器响、或不响。在这里,我们每次只完成一个字节的传输。
#define rGPBCON (*(volatile unsigned *)0x56000010) //Port B control
#define rGPBDAT (*(volatile unsigned *)0x56000014) //Port B data
#define rGPBUP (*(volatile unsigned *)0x56000018) //Pull-up control B
#define rGPHCON (*(volatile unsigned *)0x56000070) //Port H control
#define rGPHUP (*(volatile unsigned *)0x56000078) //Pull-up control H
#define rULCON0 (*(volatile unsigned *)0x50000000) //UART 0 Line control
#define rUCON0 (*(volatile unsigned *)0x50000004) //UART 0 Control
#define rUFCON0 (*(volatile unsigned *)0x50000008) //UART 0 FIFO control
#define rUMCON0 (*(volatile unsigned *)0x5000000c) //UART 0 Modem control
#define rUTRSTAT0 (*(volatile unsigned *)0x50000010) //UART 0 Tx/Rx status
#define rUERSTAT0 (*(volatile unsigned *)0x50000014) //UART 0 Rx error status
#define rUFSTAT0 (*(volatile unsigned *)0x50000018) //UART 0 FIFO status
#define rUMSTAT0 (*(volatile unsigned *)0x5000001c) //UART 0 Modem status
#define rUBRDIV0 (*(volatile unsigned *)0x50000028) //UART 0 Baud rate divisor
//little endian
#define rUTXH0 (*(volatile unsigned char *)0x50000020) //UART 0 Transmission Hold
#define rURXH0 (*(volatile unsigned char *)0x50000024) //UART 0 Receive buffer
void Main(void)
{
char ch;
rGPBCON = 0x015551;
rGPBUP = 0x7ff;
rGPBDAT = 0x1e0;
rGPHCON = 0x00faaa; //使用UART0功能
rGPHUP = 0x7ff;
rULCON0 = 0x3; //设置UART0无奇偶校验,一位停止位,8位数据
rUCON0 = 0x245; //PCLK为时钟源,接收和发送数据为查询或中断方式
rUFCON0 = 0; //
rUMCON0 = 0; //
rUBRDIV0 = 26; //设置波特率,PCLK为50MHz,波特率为115.2kHz
while(!(rUTRSTAT0 & 0x2)); //等待并判断发送缓存是否为空
rUTXH0 = 0xaa; //是空,则发送0xAA字节
while(1)
{
while(!(rUTRSTAT0 & 0x1)); //等待并判断接收缓存是否准备好
ch = rURXH0; //接收一个字节数据
while(!(rUTRSTAT0 & 0x2)); //等待并判断发送缓存是否为空
rUTXH0 = ch; //发送一个字节数据
switch(ch) //根据所接收数据的不同,执行不同的程序
{
case 0x11: //灭LED
rGPBDAT |= 0x1e0;
break;
case 0x22: //亮LED
rGPBDAT &= 0x1f;
break;
case 0x33: //蜂鸣器不响
rGPBDAT &= 0x1e0;
break;
case 0x44: //蜂鸣器响
rGPBDAT |= 0x1;
break;
default: //LED灭,蜂鸣器不响
rGPBDAT = 0x1e0;
break;
}
}
}
下面是UART中断程序,它要比查询复杂一些,因为涉及到了中断处理,并且UART发送数据和接收数据是一个中断源。主程序循环体内不执行任何程序,都在UART中断程序内执行。当接收到0x55字节数据时,亮两个LED,当接收到其他数据时,发送该字节,并在发送部分执行亮4个LED程序。
#define _ISR_STARTADDRESS 0x33ffff00
#define pISR_UART0 (*(unsigned *)(_ISR_STARTADDRESS+0x90))
#define U32 unsigned int
#define rGPBCON (*(volatile unsigned *)0x56000010) //Port B control
#define rGPBDAT (*(volatile unsigned *)0x56000014) //Port B data
#define rGPBUP (*(volatile unsigned *)0x56000018) //Pull-up control B
#define rGPHCON (*(volatile unsigned *)0x56000070) //Port H control
//#define rGPHDAT (*(volatile unsigned *)0x56000074) //Port H data
#define rGPHUP (*(volatile unsigned *)0x56000078) //Pull-up control H
#define rULCON0 (*(volatile unsigned *)0x50000000) //UART 0 Line control
#define rUCON0 (*(volatile unsigned *)0x50000004) //UART 0 Control
#define rUFCON0 (*(volatile unsigned *)0x50000008) //UART 0 FIFO control
#define rUMCON0 (*(volatile unsigned *)0x5000000c) //UART 0 Modem control
#define rUTRSTAT0 (*(volatile unsigned *)0x50000010) //UART 0 Tx/Rx status
#define rUERSTAT0 (*(volatile unsigned *)0x50000014) //UART 0 Rx error status
#define rUFSTAT0 (*(volatile unsigned *)0x50000018) //UART 0 FIFO status
#define rUMSTAT0 (*(volatile unsigned *)0x5000001c) //UART 0 Modem status
#define rUBRDIV0 (*(volatile unsigned *)0x50000028) //UART 0 Baud rate divisor
//little endian
#define rUTXH0 (*(volatile unsigned char *)0x50000020) //UART 0 Transmission Hold
#define rURXH0 (*(volatile unsigned char *)0x50000024) //UART 0 Receive buffer
#define rSRCPND (*(volatile unsigned *)0x4a000000) //Interrupt request status
#define rINTMSK (*(volatile unsigned *)0x4a000008) //Interrupt mask control
#define rINTPND (*(volatile unsigned *)0x4a000010) //Interrupt request status
#define rSUBSRCPND (*(volatile unsigned *)0x4a000018) //Sub source pending
#define rINTSUBMSK (*(volatile unsigned *)0x4a00001c) //Interrupt sub mask
void __irq uartISP(void)
{
char ch;
rSUBSRCPND |= 0x3;
rSRCPND = 0x1<<28;
rINTPND = 0x1<<28;
if(rUTRSTAT0 & 1) //接收数据处理部分
{
ch = rURXH0; //接收字节数据
if(ch==0x55)
rGPBDAT = ~0x61; //亮两个LED
else
rUTXH0 = ch; //发送字节数据
}
else //发送数据处理部分
{
rGPBDAT = ~0x1e1; //亮4个LED
}
}
void Main(void)
{
rGPBCON = 0x015551;
rGPBUP = 0x7ff;
rGPBDAT = 0x1e0;
rGPHCON = 0x00faaa;
rGPHUP = 0x7ff;
rULCON0 = 0x3;
rUCON0 = 0x5;
rUFCON0 = 0;
rUMCON0 = 0;
rUBRDIV0 = 26;
rSRCPND = 0x1<<28;
rSUBSRCPND = 0x3;
rINTPND = 0x1<<28;
rINTSUBMSK = ~(0x3); //打开UART0发送和接收中断屏蔽
rINTMSK = ~(0x1<<28); //打开UART0中断屏蔽
pISR_UART0 = (U32)uartISP;
while(1)
{
}
}
最后还要强调几点关于非FIFO模式下UART中断的一些注意事项:
1. 对于s3c2440来说,接收数据是被动的,发送数据是主动的,因此一般来说,接收数据用中断方式,发送数据用查询方式较好;
2. 在中断方式下,当接收到数据时,尽管可能该数据无用,但也一定要读取它,否则下次再接收数据时,不会再引起中断,因为接收数据缓存器被上次接收到的数据所霸占,只要没有读取它,它就永远在那里;
3. 由于UART中断涉及到SUBSRCPND寄存器,因此在中断处理程序中不仅要清SRCPND寄存器,还要清SUBSRCPND寄存器,它们的顺序一定是先清SUBSRCPND寄存器,再清SRCPND寄存器,否则就会引起一个中断两次响应的问题。因为是否中断由SRCPND寄存器决定,而SRCPND寄存器的相关状态位由SUBSRCPND寄存器决定,如果先清SRCPND寄存器,而还没有清SUBSRCPND寄存器的话,SRCPND寄存器的相关位还是会被置1,而一旦被置1,则一定还会引起中断。