分类: 嵌入式
2011-07-14 16:12:44
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,蜂鸣器响、或不响。在这里,我们每次只完成一个字节的传输。
//======================================================================
// 工程名称: UART.mcp
// 功能描述: 通过超级终端完成PC和S3C2440的数据传输,
// 利用超级终端输入需要发送的字符
// 读取接到的数字控制LED灯的亮灭
// IDE环境: ADS v1.2 TX2440A
// 组成文件:
// 硬件连接: 无
// 维护记录: Lzy 2011-7-14 V1.0
//======================================================================
#include "2440addr.h"
#include "string.h"
#include "def.h"
#include "2440lib.h"
#include "uart.h"
#define LED1ON 0xFE
#define LED2ON 0xFD
#define LED3ON 0xFB
#define LED4ON 0xF7
#define LEDOFF 0xFF
int Main(void)
{
char num;
memcpy((unsigned char *)0x0,(unsigned char *)0x30000000,0x1000);
/*初始化系统时钟*/
SetSysFclk(FCLK_400M); //设置系统时钟 400M
ChangeClockDivider(2, 1); //设置分频 1:4:8
CalcBusClk(); //计算总线频
rGPFCON = (rGPFCON | 0xFFFF) & 0x55; //GPF0--GPF3设置为output
rGPFUP = rGPFUP & 0xFFF0; //使能GPF上拉电阻
rGPFDAT = 0xFF; //GPF低4位初始化为1
Uart_Select(0); //选择串口
Uart_Init(0,115200); //选择时钟源(0->PCLL)和设置波特率
Uart_Printf("\nUART TEST\n");
while(1)
{
Uart_Printf("\n\n灯号(1-4)> "); //发送命令提示行
num = Uart_Getch(); //接收数据
switch(num)
{
case '1':
Uart_Printf("LED1 ON!\n");
rGPFDAT = LED1ON; //点亮LED1
break;
case '2':
Uart_Printf("LED2 ON!\n");
rGPFDAT = LED2ON; //点亮LED2
break;
case '3':
Uart_Printf("LED3 ON!\n");
rGPFDAT = LED3ON; //点亮LED3
break;
case '4':
Uart_Printf("LED4 ON!\n");
rGPFDAT = LED4ON; //点亮LED4
break;
default:
Uart_Printf("LED OFF!\n");
rGPFDAT = LEDOFF;
break;
}
}
}
//=============================================================
// 文件名称: uart.c
// 功能描述: UART相关函数
// 维护记录: 2009-8-14 V1.0
//=============================================================
#include
#include
#include
#include
#include
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
extern unsigned int PCLK;
static int UartNum=0;
char *string;
void Uart_Select(int ch)
{
UartNum = ch;
}
//====================================================
// 语法格式:void Uart_Init(int whichuart, int baud)
// 功能描述: 对Uart进行初始化,以所需要的波特率为输入参数
// 入口参数: UART端口号 波特率
// 出口参数: 无
//======================================================================
void Uart_Init(int pclk, int baud)
{
if(pclk == 0)
pclk = PCLK;
if(UartNum==0) //判断是否使用UART0
{
rGPHCON = rGPHCON & (~(0xffff)); //UART0: RXD0<==>GPH3 TXD0<==>GPH2
rGPHCON = rGPHCON | (0xaaa0) ; //设置GPH端口为UART口
rGPHUP = 0x0; //使能上拉功能
rUFCON0=0x0; // 不使用FIFO
rUMCON0=0x0; //不使用自动流控制
rULCON0=0x3; //不采用红外线传输模式,无奇偶校验位,1个停止位,8个数据位
rUCON0=0x245; //发送中断为电平方式,接收中断为边沿方式,禁止超时中断,允许产生错误状态中断,禁止回送模式,禁止中止信号,传输模式为中断请求模式,接收模式也为中断请求模式。
rUBRDIV0=( (int)(pclk/16./baud+0.5) -1 ); //根据波特率计算UBRDIV0的值
Delay(10);
}
else if(UartNum==1)
{
rGPHCON = rGPHCON & (~(0xffff)) ; //UART1: RXD1<==>GPH5 TXD1<==>GPH4
rGPHCON = rGPHCON | (0xaaa0) ; //设置GPH端口为UART口
rGPHUP = 0x0; // 使能上拉功能
rUFCON1=0x0;
rUMCON1=0x0;
rULCON1=0x3;
rUCON1=0x245;
rUBRDIV1=((int)(pclk/(baud*16))-1);
Delay(10);
}
else if(UartNum==2)
{
rGPHCON = rGPHCON & (~(0xffff)) ; //UART1: RXD2<==>GPH7 TXD2<==>GPH6
rGPHCON = rGPHCON | (0xaaa0) ; //设置GPH端口为UART口
rGPHUP = 0x0; // 使能上拉功能
rUFCON2=0x0;
rUMCON2=0x0;
rULCON2=0x3;
rUCON2=0x245;
rUBRDIV2=((int)(pclk/(baud*16))-1);
Delay(10);
}
}
//====================================================
// 语法格式:void Uart_TxEmpty(int ch)
// 功能描述:
// 入口参数: 串口号
// 出口参数: 无
//====================================================================
void Uart_TxEmpty(int ch)
{
if(ch==0)
while(!(rUTRSTAT0 & 0x4)); //等待发送缓冲区为空
else if(ch==1)
while(!(rUTRSTAT1 & 0x4));
else if(ch==2)
while(!(rUTRSTAT2 & 0x4));
}
//====================================================
// 语法格式:void Uart_SendByte(char ch)
// 功能描述: 发送字节数据
// 入口参数: 发送的字节数据
// 出口参数: 无
//====================================================================
void Uart_SendByte(char ch)
{
if (UartNum == 0)
{
if(ch=='\n')
{
while(!(rUTRSTAT0 & 0x2)); //等待,直到发送缓冲区为空
// Delay(10); //超级中断的响应速度较慢
WrUTXH0('\r'); //发送回车符
}
while(!(rUTRSTAT0 & 0x2)); //等待,直到发送缓冲区为空
// Delay(10);
WrUTXH0(ch); //发送字符
}
else if (UartNum == 1)
{
if(ch=='\n')
{
while(!(rUTRSTAT1 & 0x2)); //等待,直到发送缓冲区为空
// Delay(10); //等待
rUTXH1='\r';
}
while(!(rUTRSTAT1 & 0x2)); //Wait until THR is empty.
// Delay(10);
WrUTXH1(ch);
}
else if (UartNum == 2)
{
if(ch=='\n')
{
while(!(rUTRSTAT2 & 0x2)); //等待,直到发送缓冲区为空
// Delay(10); //等待
rUTXH2='\r';
}
while(!(rUTRSTAT2 & 0x2)); //Wait until THR is empty.
// Delay(10);
WrUTXH2(ch);
}
}
//====================================================
// 语法格式:char Uart_ReceiveByte(void)
// 功能描述: 接收字节数据
// 入口参数: 无
// 出口参数: 接收的字节数据
//====================================================================
char Uart_ReceiveByte(void)
{
if(UartNum==0)
{
while(!(rUTRSTAT0 & 0x1)); //等待接收数据
return RdURXH0();
}
else if(UartNum==1)
{
while(!(rUTRSTAT1 & 0x1)); //等待接收数据
return RdURXH1();
}
else if(UartNum==2)
{
while(!(rUTRSTAT2 & 0x1));
return RdURXH2();
}
return 0;
}
//====================================================
// 语法格式:char Uart_Getch(void)
// 功能描述: 接收一个字符,和Uart_ReceiveByte一样
// 入口参数: 无
// 出口参数: 接收的字节数据
//====================================================================
char Uart_Getch(void)
{
if(UartNum==0)
{
while(!(rUTRSTAT0 & 0x1));
return RdURXH0();
}
else if(UartNum==1)
{
while(!(rUTRSTAT1 & 0x1));
return RdURXH1();
}
else if(UartNum==2)
{
while(!(rUTRSTAT2 & 0x1));
return RdURXH2();
}
return 0 ;
}
//====================================================
// 语法格式:char Uart_GetKey(void)
// 功能描述: 得到键值,和Uart_ReceiveByte一样
// 入口参数: 无
// 出口参数: 接收的字节数据
//====================================================================
char Uart_GetKey(void)
{
if(UartNum==0)
{
if(rUTRSTAT0 & 0x1) //Receive data ready
return RdURXH0();
else
return 0;
}
else if(UartNum==1)
{
if(rUTRSTAT1 & 0x1) //Receive data ready
return RdURXH1();
else
return 0;
}
else if(UartNum==2)
{
if(rUTRSTAT2 & 0x1) //Receive data ready
return RdURXH2();
else
return 0;
}
return 0 ;
}
//====================================================
// 语法格式:void Uart_Send (char *str)
// 功能描述: 发送字符串
// 入口参数: 字符串指针
// 出口参数: 无
//====================================================================
void Uart_Send (char *str)
{
while (*str)
Uart_SendByte(*str++);
}
//====================================================
// 语法格式:void Uart_receive(char *string)
// 功能描述: 接收字符串
// 入口参数: 字符串指针
// 出口参数: 无
//===================================================================
void Uart_receive(char *string)
{
char *string2 ;
char c;
string2 = string;
while((c = Uart_ReceiveByte())!='\r')
{
if(c=='\b')
{
if( (int)string2 < (int)string )
{
printf("\b \b");
string--;
}
}
else
{
*string++ = c;
Uart_SendByte(c);
}
}
*string='\0';
Uart_SendByte('\n');
}
//====================================================
// 语法格式:int Uart_GetIntNum(void)
// 功能描述: 得到整型数
// 入口参数: 无
// 出口参数: 整型数
//====================================================================
int Uart_GetIntNum(void)
{
char str[30];
char *string = str;
int base = 10;
int minus = 0;
int result = 0;
int lastIndex;
int i;
Uart_receive(string);
if(string[0]=='-')
{
minus = 1;
string++;
}
if(string[0]=='0' && (string[1]=='x' || string[1]=='X'))
{
base = 16;
string += 2;
}
lastIndex = strlen(string) - 1;
if(lastIndex<0)
return -1;
if(string[lastIndex]=='h' || string[lastIndex]=='H' )
{
base = 16;
string[lastIndex] = 0;
lastIndex--;
}
if(base==10)
{
result = atoi(string);
result = minus ? (-1*result):result;
}
else
{
for(i=0;i<=lastIndex;i++)
{
if(isalpha(string[i]))
{
if(isupper(string[i]))
result = (result<<4) + string[i] - 'A' + 10;
else
result = (result<<4) + string[i] - 'a' + 10;
}
else
result = (result<<4) + string[i] - '0';
}
result = minus ? (-1*result):result;
}
return result;
}
int Uart_GetIntNum_GJ(void)
{
char string[16] ;
char *p_string = string ;
char c;
int i = 0 ;
int data = 0 ;
while( ( c = Uart_Getch()) != '\r' )
{
if(c=='\b') p_string--;
else *p_string++=c;
Uart_SendByte( c ) ;
}
*p_string = '\0';
i = 0 ;
while( string[i] != '\0' )
{
data = data * 10 ;
if( string[i]<'0'||string[i]>'9' )
return -1 ;
data = data + ( string[i]-'0' ) ;
i++ ;
}
return data ;
}
//====================================================
// 语法格式:void Uart_Printf(char *fmt,...)
// 功能描述: 按格式输出字符串
// 入口参数: printf格式的字符串
// 出口参数: 无
//===================================================================
void Uart_Printf(char *fmt,...)
{
va_list ap; //指向参数的指针
char string[256];
va_start(ap,fmt); //初始化变量刚定义的VA_LIST变量;
vsprintf(string,fmt,ap);//送格式化输出到串中
Uart_Send(string);
va_end(ap); //结束可变参数的获取
}
//=============================================================
// 文件名称: uart.h
// 功能描述: UART相关函数
// 维护记录: 2009-8-14 V1.0
//=============================================================
#ifndef __UART_H_
#define __UART_H_
void Uart_Select(int ch);
void Uart_Init(int whichuart, int baud);
void Uart_TxEmpty(int ch);
void Uart_SendByte(char ch);
char Uart_ReceiveByte(void);
char Uart_Getch(void);
char Uart_GetKey(void);
int Uart_GetIntNum(void);
int Uart_GetIntNum_GJ(void);
void Uart_Send(char *str);
void Uart_receive(char *string);
void Uart_Printf(char *fmt,...);
#endif //__UART_H_
va_list 详解 VA_LIST 是在C语言中解决变参问题的一组宏
他有这么几个成员:
1) va_list型变量:
#ifdef _M_ALPHA
typedef struct {
char *a0; /* pointer to first homed integer argument */
int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif
2)_INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
3)VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
4)VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型):
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
5)VA_END宏,清空va_list可变参数列表:
#define va_end(ap) ( ap = (va_list)0 )
VA_LIST的用法:
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
(2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;
(3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
(4)最后用VA_END宏结束可变参数的获取。
使用VA_LIST应该注意的问题:
(1)可变参数的类型和个数完全由程序代码控制,它并不能智能地识别不同参数的个数和类型;
(2)如果我们不需要一一详解每个参数,只需要将可变列表拷贝至某个缓冲,可用vsprintf函数;
(3)因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码;
小结:可变参数的函数原理其实很简单,而VA系列是以宏定义来定义的,实现跟堆栈相关。我们写一个可变参数的C函数时,有利也有弊,所 以在不必要的场合,我们无需用到可变参数,如果在C++里,我们应该利用C++多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。
va_list 详解:http://hi.baidu.com/kang_liang/blog/item/168c9059a9a1ca2d2934f05f.html
串口程序代码 uart.rar