LM3S API函数解读范例

来源:互联网 发布:淘宝售后时间 编辑:程序博客网 时间:2024/06/04 19:52

jtagWait()函数解读

1.通常在主函数的开始都要调用jtagWait()函数,如工程模版的主函数,

int main(void)

{     

 jtagWait();                                         //  防止JTAG失效,重要!

    clockInit();                                         //  时钟初始化:晶振,6MHz

for (;;)

{

}

}

 

2.关于函数的解释,先要找到函数的定义,可以在函数的调用处指向函数名后右键选择“Go to definition of …”。

函数jtagWait()systemInit.c文件中,其功能是防止JTAG接口失效。函数定义:

void jtagWait(void)

{

    SysCtlPeriEnable(KEY_PERIPH);                 //  使能KEY所在的GPIO端口

    GPIOPinTypeIn(KEY_PORT, KEY_PIN);           //  设置KEY所在管脚为输入

 

    if (GPIOPinRead(KEY_PORT, KEY_PIN) == 0x00) //  若复位时按下KEY,则进入

    {

        for (;;);                                    //  死循环,以等待JTAG连接

    }

    SysCtlPeriDisable(KEY_PERIPH);                //  禁止KEY所在的GPIO端口

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  第一个函数SysCtlPeriEnable()的功能是使能外围设备,原名是SysCtlPeripheralEnable(),用#define替换是为了使用的方便,该函数在sysctl.c文件中,可以先加入该文件以便使用“Go to definition of …”找到其定义。其定义为:

Void SysCtlPeripheralEnable(unsigned long ulPeripheral)

{

    ASSERT(SysCtlPeripheralValid(ulPeripheral));                // Check the arguments.

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

        SYSCTL_PERIPH_MASK(ulPeripheral);                // Enable this peripheral.

}

该函数中的ASSERT()是一个宏,其定义在debug.h中,其作用是计算括号中的表达式,为0则程序报告错误并终止执行,非0则继续向下执行。函数SysCtlPeripheralValid()用于判断传入参数的有效性,即判断是否为该函数可以处理的外围设备,是则返回真,否则返回假,在调用中传入参数KEY_PERIPH,其实为SYSCTL_PERIPH_GPIOG,其值为0x20000040,该值不是寄存器的地址,暂时不清楚该值的意义。

HWREG()又是定义的一个宏,在文件hw_types.h中,其定义为:

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

其中(volatile unsigned long *)(x)是将x强制转换为无符号长整型的指针,*((volatile unsigned long *)(x))是引用该指针所指向的存储单元,例如HWREG(0x20000000)=1;就是向地址为0x20000000的存储单元写入1

传入HWREG ()的参数为g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]

首先看g_pulRCGCRegs[],这是一个静态只读的无符号长整型数组,其定义为:

static const unsigned long g_pulSCGCRegs[] =

{

    SYSCTL_SCGC0,              //0x400FE110

    SYSCTL_SCGC1,              //0x400FE114

    SYSCTL_SCGC2              //0x400FE118

};

这其实是三个睡眠模式时钟选通控制寄存器,每个位控制一个给定接口、功能、或单元的时钟使能,如果置位,则对应的单元接收时钟并运行,否则,对应的单元不使用时钟并禁止(节能),这些位的复位状态都为0(不使用时钟),即所有功能单元都禁止,应用所需的端口需通过软件来使能。分了三个寄存器来管理所有接口、功能、或单元的时钟。SYSCTL_SCGC0=0x400FE110 表示寄存器的地址。

再看SYSCTL_PERIPH_INDEX(ulPeripheral),此处的ulPeripheral=KEY_PERIPH =SYSCTL_PERIPH_GPIOG=0x20000040SYSCTL_PERIPH_INDEX()的定义为:

#define SYSCTL_PERIPH_INDEX(a)  (((a) >> 28) & 0xf),此处计算结果SYSCTL_PERIPH_INDEX(0x20000040)=2,而g_pulSCGCRegs[2]正好是睡眠模式时钟选通控制寄存器2 (SCGC2),正好是控制GPIOAGPIOH的时钟使能。因此不难猜想SYSCTL_PERIPH_INDEX(a)是为了选择寄存器SCGC0SCGC1SCGC2中的一个。

因此HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])意思就是选中寄存器SCGC2,整句的意思是要改写该寄存器的值。于是继续看后半句SYSCTL_PERIPH_MASK(ulPeripheral),当然 |= 是复合赋值a|=b相当于a=a|b

#define SYSCTL_PERIPH_MASK(a)   (((a) & 0xffff) << (((a) & 0x001f0000) >> 16)),该句比较复杂,暂时不知道为什么要这样写,但此处计算结果SYSCTL_PERIPH_MASK(0x20000040)=0x0040。因此HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=

        SYSCTL_PERIPH_MASK(ulPeripheral);     

一句的目的是将寄存器SCGC2的第6GPIOG1而使能GPIOG端口。另外,再看一下SCGC2的定义如下:

7

6

5

4

3

2

1

0

GPIOH

GPIOG

GPIOF

GPIOE

GPIOD

GPIOC

GPIOB

GPIOA

 

 

这就不难理解为什么有如下定义

#define SYSCTL_PERIPH_GPIOA     0x20000001  // GPIO A

#define SYSCTL_PERIPH_GPIOB     0x20000002  // GPIO B

#define SYSCTL_PERIPH_GPIOC     0x20000004  // GPIO C

#define SYSCTL_PERIPH_GPIOD     0x20000008  // GPIO D

#define SYSCTL_PERIPH_GPIOE     0x20000010  // GPIO E

#define SYSCTL_PERIPH_GPIOF     0x20000020  // GPIO F

#define SYSCTL_PERIPH_GPIOG     0x20000040  // GPIO G 本例中使用

#define SYSCTL_PERIPH_GPIOH     0x20000080  // GPIO H

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  第二个函数GPIOPinTypeIn()其实为GPIOPinTypeGPIOInput(),该函数在gpio.c文件中,其定义为:

Void GPIOPinTypeGPIOInput(unsigned long ulPort, unsigned char ucPins)

{  

ASSERT(GPIOBaseValid(ulPort));  // Check the arguments.

    GPIODirModeSet(ulPort, ucPins, GPIO_DIR_MODE_IN);    // Make the pin(s) be inputs.

    GPIOPadConfigSet(ulPort, ucPins, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);

            // Set the pad(s) for standard push-pull operation.

}

调用该函数时传入的参数为ulPort =GPIO_PORTG_BASE= 0x40026000ucPins =GPIO_PIN_5= 0x00000020

第一句有关ASSERT()宏的应用如前所述,不再赘述。

现在来看第二句GPIODirModeSet(ulPort, ucPins, GPIO_DIR_MODE_IN);其中新的参数GPIO_DIR_MODE_IN =0x00000000,该函数的目的是要将GPIOG的第5个管脚置为输入。该函数也在gpio.c文件中,其定义为:

Void GPIODirModeSet(unsigned long ulPort, unsigned char ucPins, unsigned long ulPinIO)

{

    ASSERT(GPIOBaseValid(ulPort));

    ASSERT((ulPinIO == GPIO_DIR_MODE_IN) || (ulPinIO == GPIO_DIR_MODE_OUT) ||

           (ulPinIO == GPIO_DIR_MODE_HW));              // Check the arguments.

    HWREG(ulPort + GPIO_O_DIR) = ((ulPinIO & 1) ?

                                  (HWREG(ulPort + GPIO_O_DIR) | ucPins) :

                                  (HWREG(ulPort + GPIO_O_DIR) & ~(ucPins)));

    HWREG(ulPort + GPIO_O_AFSEL) = ((ulPinIO & 2) ?

                                    (HWREG(ulPort + GPIO_O_AFSEL) | ucPins) :

                                    (HWREG(ulPort + GPIO_O_AFSEL) &

                                     ~(ucPins)));     // Set the pin direction and mode.

}

同上,前两句不再赘述,第三句赋值表达式的左边为HWREG(ulPort + GPIO_O_DIR),此处ulPort= =GPIO_PORTG_BASE= 0x40026000,是GPIOG块的基地址,每个块都有相同的GPIO寄存器,因此可以用BASE+OFFSET的方式寻址各个块的寄存器,GPIO_O_DIR= 0x00000400,表明GPIO方向寄存器GPIODIR的偏移量为0x400,因此HWREG(ulPort + GPIO_O_DIR)寻址到了GPIOG的寄存器GPIODIR,该寄存器是数据方向寄存器,GPIODIR 寄存器中设为1的位将相应的管脚配置成输出,而设为0的位将相应的管脚配置成输入,所有位在复位时都会被清零,即默认情况下所有GPIO管脚都是输入。赋值号右边是一个条件表达式,条件为(ulPinIO & 1)ulPinIO为传入的第三个参数,为0则置为输入管脚,为1则置为输出管脚,此处传入GPIO_DIR_MODE_IN =0x00000000,因此条件为假,所以会将(HWREG(ulPort + GPIO_O_DIR) & ~(ucPins))的值写入寄存器GPIODIR,下面分析如何实现,HWREG(ulPort + GPIO_O_DIR)取出GPIODIR的值,此处的ucPins =GPIO_PIN_5= 0x00000020,取反后只有第5位为0,其余位为1,与原值相与后把第5为清0,然后送回寄存器。

第四句与第三句形式一样,不再详细分析,只简单说明其作用,HWREG(ulPort + GPIO_O_AFSEL)为选中GPIO 备用功能选择(GPIOAFSEL)寄存器,向该寄存器中的任意位写“1表示选择该GPIO线路所对应的硬件控制(功能)。由于所有的位都在复位时都会清零,因此在默认的情况下,并无GPIO线被设为硬件控制(功能)。而参数ulPinIO有三种选择,如下:

#define GPIO_DIR_MODE_IN        0x00000000  // Pin is a GPIO input

#define GPIO_DIR_MODE_OUT       0x00000001  // Pin is a GPIO output

#define GPIO_DIR_MODE_HW        0x00000002  // Pin is a peripheral function

因此ulPinIO=2时则将GPIOAFSEL寄存器的相应位置1,表示选择其硬件控制功能。本例中第5位清0

  第三个函数为GPIOPinRead(),猜想其功能为读取某端口某管脚的状态,该函数也在gpio.c中,其定义为:

Long  GPIOPinRead(unsigned long ulPort, unsigned char ucPins)

{

    ASSERT(GPIOBaseValid(ulPort));

    return(HWREG(ulPort + (GPIO_O_DATA + (ucPins << 2))));  // Return the pin value(s).

}

其中HWREG(ulPort + (GPIO_O_DATA + (ucPins << 2)))这句很重要,体现了一种利用地址线屏蔽而实现可对任意位操作的机制,将地址总线的位[9:2]用作屏蔽位,在读操作过程中,如果与数据位相关联的地址位被设为1,那么读取该值,如果与数据位相关联的地址位被设为0,那么不管它的实际值是什么,都将该值读作0。下面结合本例解释,将各个参数的值代入为HWREG(0x40026000 + (0x000 + (0x00000020 << 2))),结果为HWREG(0x40026080)

ADDR[9:2]

9

8

7

6

5

4

3

2

1

0

0x080

0

0

1

0

0

0

0

0

0

0

GPIODATA

7

6

5

4

3

2

1

0

 

 

如上表所示,GPIODATA寄存器中除第5位外,其余都被屏蔽,可以只读出第5位的状态。

  第四个函数SysCtlPeriDisable()的原型为void SysCtlPeripheralDisable(unsigned long ulPeripheral),在sysctl.c文件中,其定义为:

Void  SysCtlPeripheralDisable(unsigned long ulPeripheral)

{

    ASSERT(SysCtlPeripheralValid(ulPeripheral));

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

        ~SYSCTL_PERIPH_MASK(ulPeripheral);    // Disable this peripheral.

}

其形式和SysCtlPeripheralEnable()相似,不再赘述。

原创粉丝点击