Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2242130
  • 博文数量: 395
  • 博客积分: 10994
  • 博客等级: 上将
  • 技术积分: 5586
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-17 19:49
文章存档

2014年(1)

2013年(10)

2012年(74)

2011年(303)

2010年(7)

分类: 嵌入式

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字样,呵呵,

就写到这吧

阅读(3149) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~