全部博文(395)
分类: 嵌入式
2011-06-06 22:27:25
以下是s3c2440中的uart实验,以下是主题程序Main.c中的内容,我做了注释和里面关键代码的深入解析,呵呵(可以再ads上测试一下)
/*************************************************
s3c2440,串口实验
**************************************************/
#define GLOBAL_CLK 1
#include
#include
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
#include "mmu.h"
#include "profile.h"
#include "memtest.h"
static void cal_cpu_bus_clk(void);
void Set_Clk(void);
void beep_init(void);
void beep_run(void);
/*************************************************
Function name: delay
Parameter : times
Description : 延时函数
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
void delay(int times)
{
int i,j;
for(i=0;i
for(j=0;j<400;j++);
}
/*************************************************
Function name: Main
Parameter : void
Description : 主功能函数,实现了串口的收发功能
首先想串口发送十次“hello world”,
然后从键盘输入R,则蜂鸣器连续响5
次。
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
void Main(void)
{
int i;
int Scom=0;
Set_Clk();
beep_init();
/*设置波特率、数据位、停止位、校验位*/
Uart_Init(0,115200);
Uart_Select(Scom);
for(i=0;i<10;i++)
Uart_Printf("\nHello World!\n");
while(1)
{
while(Uart_GetKey()=='r')
{
for(i=0;i<10;i++)
beep_run();
Uart_Printf("\nBeep Quit!\n");
}
}
}
/*************************************************
Function name: Set_Clk()
Parameter : void
Description : 设置CPU的时钟频率
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
void Set_Clk(void)
{
int i;
U8 key;
U32 mpll_val = 0 ;
i = 2 ; //don't use 100M!
//boot_params.cpu_clk.val = 3;
switch ( i ) {
case 0: //200
key = 12;
mpll_val = (92<<12)|(4<<4)|(1);
break;
case 1: //300
key = 13;
mpll_val = (67<<12)|(1<<4)|(1);
break;
case 2: //400
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
case 3: //440!!!
key = 14;
mpll_val = (102<<12)|(1<<4)|(1);
break;
default:
key = 14;
mpll_val = (92<<12)|(1<<4)|(1);
break;
}
//init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); //set the register--rMPLLCON
ChangeClockDivider(key, 12); //the result of rCLKDIVN [0:1:0:1] 3-0 bit
cal_cpu_bus_clk(); //HCLK=100M PCLK=50M
}
/*************************************************
Function name: cal_cpu_bus_clk
Parameter : void
Description : 设置PCLK\HCLK\FCLK的频率
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
static void cal_cpu_bus_clk(void)
{
static U32 cpu_freq;
static U32 UPLL;
U32 val;
U8 m, p, s;
val = rMPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1< //FCLK=400M FIN=12000000
val = rCLKDIVN;
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
}
/*************************************************
Function name: beep_init()
Parameter : void
Description : 初始化蜂鸣器
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
void beep_init(void)
{
rGPBCON &= ~(0x3<<0);
rGPBCON |= (0x1<<0);
}
/*************************************************
Function name: beep_run()
Parameter : void
Description : 运行蜂鸣器
Return : void
Argument : void
Autor & date : Daniel
**************************************************/
void beep_run(void)
{
rGPBDAT |= (0x1<<0);
delay(5000);
rGPBDAT &= (0x0<<0);
delay(5000);
}
上面的程序相当的漂亮,下面解析一下里面几个函数的原型,及运行原理
1.Uart_Init/*设置波特率、数据位、停止位、校验位*/
第一个参数,就是设置时钟,此时传入参数0,就是默认使用pclk了,而第二个就是波特率了,arm中的寄存器还真是特殊,这个还有波特率寄存器,呵呵,以下函数中各个寄存器请参照 韦东山 的那本书,上面有讲解,不过每一本讲解裸机的都有(或者数据手册)。。。。
void Uart_Init(int pclk,int baud)
{
int i;
if(pclk == 0)
pclk = PCLK;
rUFCON0 = 0x0; //UART channel 0 FIFO control register, FIFO disable
rUFCON1 = 0x0; //UART channel 1 FIFO control register, FIFO disable
rUFCON2 = 0x0; //UART channel 2 FIFO control register, FIFO disable
rUMCON0 = 0x0; //UART chaneel 0 MODEM control register, AFC disable
rUMCON1 = 0x0; //UART chaneel 1 MODEM control register, AFC disable
//UART0
rULCON0 = 0x3; //Line control register : Normal,No parity,1 stop,8 bits
// [10] [9] [8] [7] [6] [5] [4] [3:2] [1:0]
// Clock Sel, Tx Int, Rx Int, Rx Time Out, Rx err, Loop-back, Send break, Transmit Mode, Receive Mode
// 0 1 0 , 0 1 0 0 , 01 01
// PCLK Level Pulse Disable Generate Normal Normal Interrupt or Polling
rUCON0 = 0x245; // Control register
rUBRDIV0=( (int)(pclk/16./baud+0.5) -1 ); //Baud rate divisior register 0
//UART1
rULCON1 = 0x3;
rUCON1 = 0x245;
rUBRDIV1=( (int)(pclk/16./baud+0.5) -1 );
//UART2
rULCON2 = 0x3;
rUCON2 = 0x245;
rUBRDIV2=( (int)(pclk/16./baud+0.5) -1 );
for(i=0;i<100;i++); //这个是为了是设定稳定下来
}
2.Uart_Select(Scom);选择com口,从scom可以看出,选择了0口,
void Uart_Select(int ch)
{
whichUart = ch;//这个whichUart是全局变量,下面你会看到会用到它的,呵呵
}
3.下面这个函数非常重要Uart_Printf(),是个可变参数函数,关于这个方面的知识请网上搜索,我的博文中也有相应的介绍,你可以参考一下
void Uart_Printf(char *fmt,...)
{
va_list ap;
char string[256];
va_start(ap,fmt);
vsprintf(string,fmt,ap);
Uart_SendString(string);//这句非常重要,这一句就把你要发送的字符串给发出去了
va_end(ap);
}
关于Uart_SendString(string)函数,下面接着跟踪
void Uart_SendString(char *pt)
{
while(*pt)
Uart_SendByte(*pt++);
}
可以看到,上面的函数中uart_SendByte功能是发送单个字符
void Uart_SendByte(int data)
{
if(whichUart==0)
{
if(data=='\n')
{
while(!(rUTRSTAT0 & 0x2));
// Delay(1); //because the slow response of hyper_terminal
WrUTXH0('\r'); //这个\r不理解,为什么,也期待解释
}
while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty.
// Delay(1);
WrUTXH0(data);//关于WrUTXH0这个最重要,下面在介绍
}
else if(whichUart==1)
{
if(data=='\n')
{
while(!(rUTRSTAT1 & 0x2));
//Delay(1); //because the slow response of hyper_terminal
rUTXH1 = '\r';
}
while(!(rUTRSTAT1 & 0x2)); //Wait until THR is empty.
//Delay(1);
rUTXH1 = data;
}
else if(whichUart==2)
{
if(data=='\n')
{
while(!(rUTRSTAT2 & 0x2));
//Delay(1); //because the slow response of hyper_terminal
rUTXH2 = '\r';
}
while(!(rUTRSTAT2 & 0x2)); //Wait until THR is empty.
//Delay(1);
rUTXH2 = data;
}
}
看到了吧,上面的那个whichUart,用到了吧,再看这其中的关键词:WrUTXH0
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
看到了么,也许大家不明白,为啥会弄这个宏呢?怎么还弄这么长的一个数,这个是地址,意思就是把这个ch字符放到这个500000020地址里面,我们再来看看这个地址是啥?
#define UTXH0 (0x50000020) //Byte_access address by DMA
看到了么?是UTXH0,呵呵,这个是啥呀?如果你看看书,或者手册的话,这个就是发送缓冲寄存器,呵呵,就是cpu发数据发送到这里面,它会自己发送,呵呵,终于理解了吧。我也是终于理解了,
也真是难为了这些arm的开发者,很经典,并且也很费解,为什么要弄这么麻烦呢?。。。
也是为了是程序员更容易操作吧。。。。
好了,这个最重要的解释完了
4.Uart_GetKey(),这个函数如下,基本上和3中的一些思想一样
char Uart_GetKey(void)
{
if(whichUart==0)
{
if(rUTRSTAT0 & 0x1) //Receive data ready
return RdURXH0();
else
return 0;
}
else if(whichUart==1)
{
if(rUTRSTAT1 & 0x1) //Receive data ready
return RdURXH1();
else
return 0;
}
else if(whichUart==2)
{
if(rUTRSTAT2 & 0x1) //Receive data ready
return RdURXH2();
else
return 0;
}
return 0 ;
}
看RdURXH0这个,如下:
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
#define URXH0 (0x50000024)
有了3步的知识,你理解了吧,就是接受寄存器中的内容了,呵呵,
这样的话总的代码也就解释完了,呵呵
下面粘贴操作步骤图片(如果你看到的话,相信你能看懂,呵呵):
看到了,串口打印出了hello world字符串,说明程序第一步执行成功
当输入r字符是,蜂鸣器响了,并且串口打印出了beep quit字样,呵呵,
就写到这吧