标签:  函数  解析  驱动  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中的0位GPIOA位置1。事实是,我们不管用什么程序结构,都是为了达到这个目的。最直接的,最熟练的方式是采用C语言的赋值语句:

RCGC2 |= 0x00000001;

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

图2 RCGC2中GPIOA位置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_INDEX在Sysctl.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所示的寄存器RCGC2的0位GPIOA,被成功的置1了。

3、        总结

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

LM3S1138驱动函数SysCtlPeripheralEnable解析相关推荐

  1. 【NanoPi T2】 7.uboot gmac网卡驱动(3) - 驱动源码解析

    1.mac控制器,phy芯片,rgmii协议 2.寄存器介绍 3.驱动源码解析 4.nanopi t2 移植rtl8211e网卡驱动(首发) 驱动注册入口 驱动的注册分两个部分,一个部分是静态编译的时 ...

  2. Python文本变量与函数的解析执行,增强自动化测试数据驱动

    关注我,每天分享软件测试技术干货.面试经验,想要领取测试资料.进入软件测试学习交流群的可以直接私信我哦~~ 我们在使用Python进行自动化测试或者测试脚本开发时,通常会在代码中融入数据驱动设计,以便 ...

  3. 展锐平台的camera sensor驱动代码设计解析(2)

    展锐平台的camera sensor驱动代码设计解析(1) 展锐平台的camera sensor驱动代码设计解析(2) 展锐平台的camera sensor驱动代码设计解析(3) Camera驱动的基 ...

  4. 2008年12月13日上海USB驱动开发深度解析讲座PPT

    讲座PPT:宋宝华2008年12月13日上海USB驱动开发深度解析讲座PPT [url]http://www.linuxdriver.cn/200812/20081213172619_836.rar[ ...

  5. 语言中拟合函数 计算aic_Go语言函数深度解析(中)

    上回函数深度解析给大家聊了一些函数的基本知识,不知道还有没有人记得,不记得赶紧回去复习! 他们是 go语言中函数的基本原理 单/多个同/不同类型参数 单/多个同/不同类型返回值 值传递,引用传递 函数 ...

  6. c语言的point函数,C语言中friend友元函数详细解析

    C语言中friend友元函数详细解析 友元函数是可以直接访问类的私有成员的非成员函数.它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字frien ...

  7. php 立即执行函数,关于javascrip的立即执行函数的解析

    这篇文章主要介绍了关于javascrip的t立即执行函数的解析,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 概念: 立即执行函数顾名思义就是函数定义好之后立即执行. 函数表达式方式: ...

  8. C++函数重载解析细节

    Copyright(C)<C++从入门到精通-经典完整版> 函数重载解析细节 函数重载解析过程有三个步骤这些步骤可以总结如下: 1 确定为该调用而考虑的候选函数以及函数调用中的实参表属性 ...

  9. Python中sort和sorted函数代码解析

    Python中sort和sorted函数代码解析 本文研究的主要是Python中sort和sorted函数的相关内容,具体如下. 一.sort函数 sort函数是序列的内部函数 函数原型: L.sor ...

最新文章

  1. SpringBoot系列二:搭建自己的第一个SpringBoot程序
  2. xp工作组计算机打不开,XP“网上邻居”、“查看工作组计算机”打不开及无法访问局...
  3. 【1024程序员节】都有什么?现场亲历者告诉你...
  4. 我对新版CCNP考试的一点想法
  5. 统一返回的json时间格式
  6. 转载:关于爱情、伴侣、承诺、人生、
  7. 学习笔记10-C语言-小项目-五子棋
  8. 网关 Apache APISIX 在 360 基础运维平台项目中的实践
  9. 从零开始搭建公司后台技术栈,这套架构绝了...
  10. xshell5免费版本下载
  11. 小小串联电阻,大大的作用
  12. 健脾和胃,养生食疗——山药枸杞鲫鱼汤了解一下
  13. Python开发——做一个简单的【表白墙】网站
  14. php公众号向多个用户推送消息,如何实现微信公众号给指定互动用户推送多次消息?...
  15. java知识串讲_java基础之Java知识串讲
  16. 实验项目:用IMAIL构建企业邮件服务器
  17. 每日新闻:百度云宣布边缘计算开源,发布智能边缘开源平台;英特尔和华为成功完成SA架构的5G互操作性测试;优信淘宝打造二手车供应链...
  18. 大数据分析工具Power BI(十):制作可视化图表的报表类型
  19. 软件测试系列之单元测试 (转载)
  20. c语言实现伽罗华域乘法器,[2018年最新整理]伽罗瓦域GF(2^128)乘法器的设计.doc

热门文章

  1. lua c语言混合编程入门
  2. 【蓝桥杯】8皇后·改
  3. nyoj985带通配符的数
  4. 返回结果集的存储过程实例及调用
  5. Thinkphp的知识内容
  6. mapReducer第一个例子WordCount
  7. pycharm中报错:ImportError: No module named 'skimage'
  8. CUBRID学习笔记 42 Hierarchical QuerySQL层级查询
  9. Linux Kernel 3.8.8/3.4.41/3.0.74 发布
  10. ThinkPHP采用模块和操作