LM3S1138驱动函数SysCtlPeripheralEnable解析
1、??????? 引言
在LM3S1138的使用過程中,如果要使用外設(shè),如本文所述的GPIO_A端口時,就得先使能此外設(shè)在RCGCx寄存器中的對應(yīng)位。至于為什么使用外設(shè)時要打開其相應(yīng)的RCGCx寄存器中的對應(yīng)位,此處先不講,我也先不懂。
LM3S系列芯片因為自帶了豐富的驅(qū)動庫程序,所以編程變得方便了很多。但對于我一個入門級選手來說,我得先懂得其驅(qū)動庫程序的組織結(jié)構(gòu),爾后才能把Luminary的驅(qū)動庫為我所用。我有一個簡單的愿望就是,我使用Luminary的驅(qū)動庫的水平,能達(dá)到這個庫仿佛是我寫的一樣。本文正是在此愿望水平還很強烈時草草擬出的。
本文內(nèi)容,很單一,只是說明一個我在使用LM3S1138芯片時,為了把PA1引腳設(shè)置為通用的IO引腳,且能對其進行軟件上的置位與復(fù)位所作的前期準(zhǔn)備工作中的一部分。這一部分工作的核心就是把RCGC2寄存器中的GPIOA位置1,這個核心也就是本文的全部內(nèi)容了。
2、??????? 正文
我們先給出LM3S1138中的RCGC2寄存器結(jié)構(gòu),如圖1所示。
??
圖1 RCGC2寄存器的結(jié)構(gòu)圖
看到這個圖之后,我們知道自己所做的工作即是把RCGC2中的0位GPIOA位置1。事實是,我們不管用什么程序結(jié)構(gòu),都是為了達(dá)到這個目的。最直接的,最熟練的方式是采用C語言的賦值語句:
?
RCGC2 |= 0x00000001;
?
接下來,我們順著Luminary的驅(qū)動庫程序的流程,來看一下,上述目的是怎么個實現(xiàn)過程。首現(xiàn)我們先將Luminary驅(qū)動庫程序?qū)?/span>RCGC2中的GPIOA位置1的程序流程圖羅列出來,如圖2所示。
圖2 RCGC2中GPIOA位置1的程序結(jié)構(gòu)圖
圖2所示的程序流程圖中的函數(shù)原型:
?
extern void SysCtlPeripheralEnable(unsigned long ulPeripheral);
?
在Sysctl.h中聲明,在Sysctl.c中定義,其作用是置位對應(yīng)外設(shè)在RCGC2中的控制位,使能此外設(shè)。
程序流程圖是簡單的,程序的執(zhí)行過程是復(fù)雜的,當(dāng)然復(fù)雜是因為我的初學(xué),不懂的太多。接下來,我們要探討的是SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA)這個函數(shù)的執(zhí)行細(xì)節(jié)。為了夠細(xì)節(jié),我們直接將Luminary的源碼羅列在下面,以達(dá)到給篇幅注水的目的。
?
//*****************************************************************************
//
//! 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);
}
?
?
這個函數(shù)基本上就做了兩件事情,一件是采用斷言:
?
ASSERT(SysCtlPeripheralValid(ulPeripheral));
?
檢查形參的合法性,若形參不合法,ASSERT(條件)里面的邏輯值為假。程序在編譯階段是要報錯的。斷言的使用,目前不是很熟悉,不多講了。
斷言對形參進行判斷之后,參數(shù)合法,接著,就指著這個參數(shù)來進行一系列的寄存器操作了。其操作語句為:
?
??? //
??? // Enable this peripheral.
??? //
??? HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=
??????? SYSCTL_PERIPH_MASK(ulPeripheral);
?
關(guān)于這條語句的注釋是,這條語句用專業(yè)的驅(qū)動庫把一條簡單的
?
RCGC2 |= 0x00000001;
?
賦值語句進行了一點點小小的復(fù)雜化。下面,我們就把這個語句,給拆明白了,如果我能把這條語給講明白了,那真得覺得算是我的一點點小小的造化。首先,我們就
?
??? HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=
??????? SYSCTL_PERIPH_MASK(ulPeripheral);
?
這條賦值語句的左邊是如何解析出RCCG2來進行說明,然后,我們就這條賦值語句的右邊是如何解析出0x00000001來再進行說明。這條賦值語句的左邊是:
?
HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])
?
HWREG它是一個帶參數(shù)的宏,它的參數(shù)是一個數(shù)組名為g_pulRCGCRegs的元素,這個元素在數(shù)組中的序號是SYSCTL_PERIPH_INDEX(ulPeripheral),我查過,SYSCTL_SYSCTL_PERIPH_INDEX在Sysctl.c中有定義,是一個帶參數(shù)的宏。完整的定義是:
?
//*****************************************************************************
//
// 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 這個形參所對應(yīng)的實參是:SYSCTL_PERIPH_GPIOA。對上述左邊的表達(dá)式,比較看好的執(zhí)行結(jié)果是:
RCGC2
?
我們順著HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)])的執(zhí)行過程進行解析。
1)? 給出ulPeripheral所對應(yīng)的實參為:
SYSCTL_PERIPH_GPIOA,
這個實參是個代表32位二進制數(shù)的宏,在Sysctl.h定義,
?
#define SYSCTL_PERIPH_GPIOA???? 0x20000001? // GPIO A
?
2) 執(zhí)行SYSCTL_PERIPH_INDEX(ulPeripheral)
也就是執(zhí)行
(((a) >> 28) & 0xf)
代入實參SYSCTL_PERIPH_GPIOA(0x20000001)之后的情況是:
(((SYSCTL_PERIPH_GPIOA) >> 28) & 0xf)
(((0x20000001) >> 28) & 0xf)
這個值我們心算一下,可以得出,等于十進制數(shù)2。
?
3) 執(zhí)行g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)],也就是執(zhí)行
?
g_pulRCGCRegs[2]
?
g_pulRCGCRegs[]是一個數(shù)組,在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]對應(yīng)的元素即是:
?
SYSCTL_RCGC2
?
這也是個宏,在Hw_sysctl.h中定義,其具體的定義形式為:
?
#define SYSCTL_SCGC2??????????? 0x400FE118? // Sleep-mode clock gating reg 2
?
這對應(yīng)的數(shù)值0x400FE118,即對應(yīng)著RCGC2對應(yīng)的地址,如圖1所示的寄存器結(jié)構(gòu)的左上腳的說明部分,如圖3所示。
?
圖3 RCGC2的地址說明
4) 執(zhí)行HWREG(0x400FE118),這一句語翻譯成標(biāo)準(zhǔn)的C語言之后,應(yīng)該是:
?
*((volatile unsigned long *)0x400FE108),
?
HWREG()這個宏在Hw_types.h文件中有定義,具本定義為:
?
#define HWREG(x)??? (*((volatile unsigned long *)(x)))
?
在TI網(wǎng)站上,你可以下載一個spmu019c.pdf,在第7頁與第9頁,也會告訴你,在Luminary完整的驅(qū)動庫文件(名為:SW-LM3S-5961.exe,這個是今年5月份的時候的叫法)中,你解壓完之后的/inc目錄下,你可以找到lm3s1138.h這個頭文件,這頭文件是為直接寄存器訪問的編程而制做的頭文件,在這個頭文件中,你也可以找到如下的宏定義。
?
#define SYSCTL_RCGC2_R????????? (*((volatile unsigned long *)0x400FE108))????????????????????
?
表達(dá)式*((volatile unsigned long *)0x400FE108)的作用是:
先用(volatile unsigned long *)0x400FE108強制轉(zhuǎn)換,將0x400FE108變成一個地址,然后再用*((volatile unsigned long *)0x400FE108)將這個地址,變成實實在在的一個沒有名稱的變量,你可以往里賦值了。
?
寫到這里,請大家清醒的意識到,我們只是干完了
?
??? HWREG(g_pulRCGCRegs[SYSCTL_PERIPH_INDEX(ulPeripheral)]) |=
??????? SYSCTL_PERIPH_MASK(ulPeripheral);
?
這條語句的左半部分,這條賦值語句的右邊是:
?
SYSCTL_PERIPH_MASK(ulPeripheral)
?
它是一個帶參數(shù)的宏,它的參數(shù)是ulPeripheral,對應(yīng)的實參是SYSCTL_PERIPH_GPIOA,
這個帶參數(shù)的宏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,前面已經(jīng)講過是個代表32位二進制數(shù)的宏,在Sysctl.h定義,
?
#define SYSCTL_PERIPH_GPIOA???? 0x20000001? // GPIO A
?
SYSCTL_PERIPH_MASK(ulPeripheral)
?
中的ulPeripheral 這個形參所對應(yīng)的實參是:SYSCTL_PERIPH_GPIOA,這條語句比較看好的執(zhí)行結(jié)果是:
0x00000001
我們順著SYSCTL_PERIPH_MASK(ulPeripheral)的執(zhí)行過程進行解析。
?
1)????? 給出ulPeripheral所對應(yīng)的實參為:
SYSCTL_PERIPH_GPIOA,
這個實參是個代表32位二進制數(shù)的宏,在Sysctl.h定義,
?
#define SYSCTL_PERIPH_GPIOA???? 0x20000001? // GPIO A
?
2) ?SYSCTL_PERIPH_MASK (ulPeripheral),也就是執(zhí)行
?
(((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了,結(jié)合式子的左右部分得出的完整的語句是:
?
*((volatile unsigned long *)0x400FE108) |= 0x00000001;
?
圖1所示的寄存器RCGC2的0位GPIOA,被成功的置1了。
?
3、??????? 總結(jié)
夏天很熱,上述文字寫得也不冷靜,許多暖昧不清的地方可能還沒被我意識到,許多應(yīng)該加以說明的地方,我可能草草了事。像每個引用的文件,其作用,沒有被說明;像優(yōu)秀的變量命名方式?jīng)]有被表揚;究其原因,我覺得是我入門的太淺,不能對文中所述的內(nèi)容,做以全局的把握和說明。
?
?總結(jié)
以上是生活随笔為你收集整理的LM3S1138驱动函数SysCtlPeripheralEnable解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring 多数据源-实现
- 下一篇: 【项目管理】用LoC衡量程序员的工作效率