LM3S1138驱动函数SysCtlPeripheralEnable解析
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解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring 多数据源-实现
- 下一篇: 【项目管理】用LoC衡量程序员的工作效率