LM3S1138驱动函数SysCtlPeripheralEnable解析

来源:互联网 发布:java nginx文件上传 编辑:程序博客网 时间:2024/06/03 14:02
标签:  函数  解析  驱动  2010-06-10 14:46

1、        引言

LM3S1138的使用过程中,如果要使用外设,如本文所述的GPIO_A端口时,就得先使能此外设在RCGCx寄存器中的对应位。至于为什么使用外设时要打开其相应的RCGCx寄存器中的对应位,此处先不讲,我也先不懂。

LM3S系列芯片因为自带了丰富的驱动库程序,所以编程变得方便了很多。但对于我一个入门级选手来说,我得先懂得其驱动库程序的组织结构,尔后才能把Luminary的驱动库为我所用。我有一个简单的愿望就是,我使用Luminary的驱动库的水平,能达到这个库仿佛是我写的一样。本文正是在此愿望水平还很强烈时草草拟出的。

本文内容,很单一,只是说明一个我在使用LM3S1138芯片时,为了把PA1引脚设置为通用的IO引脚,且能对其进行软件上的置位与复位所作的前期准备工作中的一部分。这一部分工作的核心就是把RCGC2寄存器中的GPIOA位置1,这个核心也就是本文的全部内容了。

2、        正文

我们先给出LM3S1138中的RCGC2寄存器结构,如图1所示。

 

 

1 RCGC2寄存器的结构图

看到这个图之后,我们知道自己所做的工作即是把RCGC2中的0GPIOA位置1。事实是,我们不管用什么程序结构,都是为了达到这个目的。最直接的,最熟练的方式是采用C语言的赋值语句:

 

RCGC2 |= 0x00000001;

 

接下来,我们顺着Luminary的驱动库程序的流程,来看一下,上述目的是怎么个实现过程。首现我们先将Luminary驱动库程序将RCGC2中的GPIOA位置1的程序流程图罗列出来,如图2所示。

2 RCGC2GPIOA位置1的程序结构图

2所示的程序流程图中的函数原型:

 

extern void SysCtlPeripheralEnable(unsigned long ulPeripheral);

 

Sysctl.h中声明,在Sysctl.c中定义,其作用是置位对应外设在RCGC2中的控制位,使能此外设。

程序流程图是简单的,程序的执行过程是复杂的,当然复杂是因为我的初学,不懂的太多。接下来,我们要探讨的是SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA)这个函数的执行细节。为了够细节,我们直接将Luminary的源码罗列在下面,以达到给篇幅注水的目的。

 

//*****************************************************************************

//

//! Enables a peripheral.

//!

//! /param ulPeripheral is the peripheral to enable.

//!

//! Peripherals are enabled with this function.  At power-up, all peripherals

//! are disabled; they must be enabled in order to operate or respond to

//! register reads/writes.

//!

//! The /e ulPeripheral parameter must be only one of the following values:

//! /b SYSCTL_PERIPH_ADC, /b SYSCTL_PERIPH_CAN0, /b SYSCTL_PERIPH_CAN1,

//! /b SYSCTL_PERIPH_CAN2, /b SYSCTL_PERIPH_COMP0, /b SYSCTL_PERIPH_COMP1,

//! /b SYSCTL_PERIPH_COMP2, /b SYSCTL_PERIPH_ETH, /b SYSCTL_PERIPH_GPIOA,

//! /b SYSCTL_PERIPH_GPIOB, /b SYSCTL_PERIPH_GPIOC, /b SYSCTL_PERIPH_GPIOD,

//! /b SYSCTL_PERIPH_GPIOE, /b SYSCTL_PERIPH_GPIOF, /b SYSCTL_PERIPH_GPIOG,

//! /bSYSCTL_PERIPH_GPIOH, /bSYSCTL_PERIPH_HIBERNATE, /b SYSCTL_PERIPH_I2C0,

//! /b SYSCTL_PERIPH_I2C1, /b SYSCTL_PERIPH_PWM, /b SYSCTL_PERIPH_QEI0,

//! /b SYSCTL_PERIPH_QEI1, /b SYSCTL_PERIPH_SSI0, /b SYSCTL_PERIPH_SSI1,

//! /b SYSCTL_PERIPH_TIMER0, /b SYSCTL_PERIPH_TIMER1, /b SYSCTL_PERIPH_TIMER2,

//! /b SYSCTL_PERIPH_TIMER3, /b SYSCTL_PERIPH_UART0, /b SYSCTL_PERIPH_UART1,

//! /b SYSCTL_PERIPH_UART2, /b SYSCTL_PERIPH_UDMA, /b SYSCTL_PERIPH_USB0, or

//! /b SYSCTL_PERIPH_WDOG.

//!

//! /note It takes five clock cycles after the write to enable a peripheral

//! before the the peripheral is actually enabled.  During this time, attempts

//! to access the peripheral will result in a bus fault.  Care should be taken

//! to ensure that the peripheral is not accessed during this brief time

//! period.

//!

//! /return None.

//

//*****************************************************************************

void

SysCtlPeripheralEnable(unsigned long ulPeripheral)

{

    //

    // Check the arguments.

    //

    ASSERT(SysCtlPeripheralValid(ulPeripheral));

 

    //

    // Enable this peripheral.

    //

    HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=

        SYSCTL_PERIPH_MASK(ulPeripheral);

}

 

 

这个函数基本上就做了两件事情,一件是采用断言:

 

ASSERT(SysCtlPeripheralValid(ulPeripheral));

 

检查形参的合法性,若形参不合法,ASSERT(条件)里面的逻辑值为假。程序在编译阶段是要报错的。断言的使用,目前不是很熟悉,不多讲了。

断言对形参进行判断之后,参数合法,接着,就指着这个参数来进行一系列的寄存器操作了。其操作语句为:

 

    //

    // Enable this peripheral.

    //

    HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=

        SYSCTL_PERIPH_MASK(ulPeripheral);

 

关于这条语句的注释是,这条语句用专业的驱动库把一条简单的

 

RCGC2 |= 0x00000001;

 

赋值语句进行了一点点小小的复杂化。下面,我们就把这个语句,给拆明白了,如果我能把这条语给讲明白了,那真得觉得算是我的一点点小小的造化。首先,我们就

 

    HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=

        SYSCTL_PERIPH_MASK(ulPeripheral);

 

这条赋值语句的左边是如何解析出RCCG2来进行说明,然后,我们就这条赋值语句的右边是如何解析出0x00000001来再进行说明。这条赋值语句的左边是:

 

HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])

 

HWREG它是一个带参数的宏,它的参数是一个数组名为g_pulRCGCRegs的元素,这个元素在数组中的序号是SYSCTL_PERIPH_INDEX(ulPeripheral),我查过,SYSCTL_SYSCTL_PERIPH_INDEXSysctl.c中有定义,是一个带参数的宏。完整的定义是:

 

//*****************************************************************************

//

// This macro extracts the array index out of the peripheral number.

//

//*****************************************************************************

#define SYSCTL_PERIPH_INDEX(a)  (((a) >> 28) & 0xf)

 

 

HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])

 

其中ulPeripheral 这个形参所对应的实参是:SYSCTL_PERIPH_GPIOA。对上述左边的表达式,比较看好的执行结果是:

RCGC2

 

我们顺着HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])的执行过程进行解析。

1)  给出ulPeripheral所对应的实参为:

SYSCTL_PERIPH_GPIOA

这个实参是个代表32位二进制数的宏,在Sysctl.h定义,

 

#define SYSCTL_PERIPH_GPIOA     0x20000001  // GPIO A

 

2) 执行SYSCTL_PERIPH_INDEX(ulPeripheral)

也就是执行

(((a) >> 28) & 0xf)

代入实参SYSCTL_PERIPH_GPIOA(0x20000001)之后的情况是:

(((SYSCTL_PERIPH_GPIOA) >> 28) & 0xf)

(((0x20000001) >> 28) & 0xf)

这个值我们心算一下,可以得出,等于十进制数2

 

3) 执行g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)],也就是执行

 

g_pulRCGCRegs[2]

 

g_pulRCGCRegs[]是一个数组,在Sysctl.c中定义,其具体的定义形式为:

//*****************************************************************************

//

// An array that maps the "peripheral set" number (which is stored in the upper

// nibble of the SYSCTL_PERIPH_* defines) to the SYSCTL_RCGC? register that

// controls the run-mode enable for that peripheral.

//

//*****************************************************************************

static const unsigned long g_pulRCGCRegs[] =

{

    SYSCTL_RCGC0,

    SYSCTL_RCGC1,

    SYSCTL_RCGC2

};

 

可以看到g_pulRCGCRegs[2]对应的元素即是:

 

SYSCTL_RCGC2

 

这也是个宏,在Hw_sysctl.h中定义,其具体的定义形式为:

 

#define SYSCTL_SCGC2            0x400FE118  // Sleep-mode clock gating reg 2

 

这对应的数值0x400FE118,即对应着RCGC2对应的地址,如图1所示的寄存器结构的左上脚的说明部分,如图3所示。

 

3 RCGC2的地址说明

4) 执行HWREG(0x400FE118),这一句语翻译成标准的C语言之后,应该是:

 

*((volatile unsigned long *)0x400FE108)

 

HWREG()这个宏在Hw_types.h文件中有定义,具本定义为:

 

#define HWREG(x)    (*((volatile unsigned long *)(x)))

 

TI网站上,你可以下载一个spmu019c.pdf,在第7页与第9页,也会告诉你,在Luminary完整的驱动库文件(名为:SW-LM3S-5961.exe,这个是今年5月份的时候的叫法)中,你解压完之后的/inc目录下,你可以找到lm3s1138.h这个头文件,这头文件是为直接寄存器访问的编程而制做的头文件,在这个头文件中,你也可以找到如下的宏定义。

 

#define SYSCTL_RCGC2_R          (*((volatile unsigned long *)0x400FE108))                    

 

表达式*((volatile unsigned long *)0x400FE108)的作用是:

先用(volatile unsigned long *)0x400FE108强制转换,将0x400FE108变成一个地址,然后再用*((volatile unsigned long *)0x400FE108)将这个地址,变成实实在在的一个没有名称的变量,你可以往里赋值了。

 

写到这里,请大家清醒的意识到,我们只是干完了

 

    HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=

        SYSCTL_PERIPH_MASK(ulPeripheral);

 

这条语句的左半部分,这条赋值语句的右边是:

 

SYSCTL_PERIPH_MASK(ulPeripheral)

 

它是一个带参数的宏,它的参数是ulPeripheral,对应的实参是SYSCTL_PERIPH_GPIOA

这个带参数的宏SYSCTL_PERIPH_MASK()Sysctl.c中有定义,完整的定义是:

 

//*****************************************************************************

//

// This macro constructs the peripheral bit mask from the peripheral number.

//

//*****************************************************************************

#define SYSCTL_PERIPH_MASK(a)   (((a) & 0xffff) << (((a) & 0x001f0000) >> 16))

 

实参ulPeripheral,前面已经讲过是个代表32位二进制数的宏,在Sysctl.h定义,

 

#define SYSCTL_PERIPH_GPIOA     0x20000001  // GPIO A

 

SYSCTL_PERIPH_MASK(ulPeripheral)

 

中的ulPeripheral 这个形参所对应的实参是:SYSCTL_PERIPH_GPIOA,这条语句比较看好的执行结果是:

0x00000001

我们顺着SYSCTL_PERIPH_MASK(ulPeripheral)的执行过程进行解析。

 

1)      给出ulPeripheral所对应的实参为:

SYSCTL_PERIPH_GPIOA

这个实参是个代表32位二进制数的宏,在Sysctl.h定义,

 

#define SYSCTL_PERIPH_GPIOA     0x20000001  // GPIO A

 

2)  SYSCTL_PERIPH_MASK (ulPeripheral),也就是执行

 

(((a) & 0xffff) << (((a) & 0x001f0000) >> 16))

代入实参SYSCTL_PERIPH_GPIOA(0x20000001)之后的情况是:

(((SYSCTL_PERIPH_GPIOA) & 0xffff) << (((SYSCTL_PERIPH_GPIOA) & 0x001f0000) >> 16))

(((0x20000001) & 0xffff) << (((0x20000001) & 0x001f0000) >> 16))

0x00000001 << (0>> 16)

0x00000001 << 0

0x00000001

 

好了,你可以把这个1赋给RCGC2了,结合式子的左右部分得出的完整的语句是:

 

*((volatile unsigned long *)0x400FE108) |= 0x00000001;

 

1所示的寄存器RCGC20GPIOA,被成功的置1了。

 

3、        总结

夏天很热,上述文字写得也不冷静,许多暖昧不清的地方可能还没被我意识到,许多应该加以说明的地方,我可能草草了事。像每个引用的文件,其作用,没有被说明;像优秀的变量命名方式没有被表扬;究其原因,我觉得是我入门的太浅,不能对文中所述的内容,做以全局的把握和说明。

 

 
原创粉丝点击