SPI、IIC 和 UART 是最常用的三种串行总线,这三种总线在 s3c2440 中都被集成了。在
这里我们主要介绍 UART,另两个总线在后面的文章中给出。
UART(Universal Asynchronous Receiver/Transmitter,通用异步接收/发送装置)用于
异步通信,可以实现全双工发送和接收。它不仅可以实现不同嵌入式系统之间的通信,还可
以实现与 PC 之间的通信。
s3c2440 提供了三个 UART 端口,它们都可以通过查询、中断和 DMA 方式传输数
据,而且每个 UART 都分别有一个 64 个字节的接收 FIFO 和一个 64 个字节的发送 FIFO。
在这里,我们只给出非 FIFO 模式,即传输数据不利用 FIFO 缓存,一个字节一个字节地传
输。
下面我们就给出如何用 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;
//LED 灭,蜂鸣器不响
rGPBDAT = 0x1e0;
break;
default:
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)
//接收数据处理部分
{
}
else
{
ch = rURXH0;
if(ch==0x55)
//接收字节数据
rGPBDAT = ~0x61;
//亮两个 LED
else
rUTXH0 = ch;
//发送字节数据
//发送数据处理部分
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;