要點亮LED燈或獲得輸入IO的狀態(tài)應(yīng)該是比較容易的,打開端口時鐘,然后讀寫相關(guān)的GPIO寄存器就可以了,但是要實現(xiàn)一個輸入中斷,就要費些周折了。
對STM32(Cortex-M3)的芯片,要實現(xiàn)一個GPIO中斷一般需要如下幾步:
1、? 配置時鐘控制器寄存器(RCC)的APB2RSTR,確保對應(yīng)的GPIOA ~ GPIOG時鐘使能。
2、? 對GPIO寄存器的CRL(或CRH)要設(shè)置正確的輸入模式,如浮空輸入模式(對接收IO中斷來說,當(dāng)然要設(shè)置成輸入模式)。
3、? 要通過AFIO寄存器配置中斷的輸入來源,對STM32芯片來說,具有19路EXTI中斷線,其中3路分別連接PVD輸出、RTC鬧鐘事件及USB喚醒事件,剩下的對GPIOA ~ GPIOG 7*16=112個IO點來說,同時只能配置16路IO輸入中斷。
4、? 接下來要配置EXIT寄存器,根據(jù)需要來配置是上升沿觸發(fā)中斷、還是下降沿觸發(fā)中斷或兩者都觸發(fā)。
5、? 而后比較重要的是, 要配置NVIC的SETENA寄存器,讓對應(yīng)的EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5或EXTI15_10中斷位使能,此外還要配置各中斷的優(yōu)先級(前提是中斷優(yōu)先級分組寄存器已配置完畢)。
6、? 最后我們要設(shè)置中斷向量表(該向量表要重定位到內(nèi)存中,以便于動態(tài)修改),在EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5或EXTI15_10對應(yīng)的位置,放入我們的中斷函數(shù)的入口地址。
?
和PC平臺程序開發(fā)不同,基本上你每做一步,都可以很直觀的看到你的進展和成果,但對嵌入式開發(fā)來說,如果上述幾步有任何一個環(huán)節(jié)出了問題,你的進展都是零,有時候你會花上一天的時間去反復(fù)核實每個寄存器的值是否正確,以期獲得你希望的結(jié)果。所以說嵌入式開發(fā)是驚喜的型的,要么成,要么不成,一線之隔!
接下來我們說一下GPIO實現(xiàn)的詳細步驟,首先在CortexM3.h頭文件中添加GPIO相關(guān)的寄存器描述:
struct?CortexM3_GPIO ??{ ????static?const?UINT32?c_Base?=?0x40010800; ????static?const?UINT32?A?=?0; ????static?const?UINT32?B?=?1; ????static?const?UINT32?C?=?2; ????static?const?UINT32?D?=?3; ????static?const?UINT32?E?=?4; ????static?const?UINT32?F?=?5;? ????static?const?UINT32?G?=?6;? ????static?const?UINT32?GPIO_Mode_NULL?=?0x00; ????static?const?UINT32?GPIO_Mode_Speed_10MHz?=?0x01; ????static?const?UINT32?GPIO_Mode_Speed_2MHz?=?0x02; ????static?const?UINT32?GPIO_Mode_Speed_50MHz?=?0x03; ????static?const?UINT32?GPIO_Mode_IN_FLOATING?=?0x04; ???????volatile?UINT32???CRL;???????volatile?UINT32???CRH; ?????volatile?UINT32???IDR;???????volatile?UINT32???ODR; ?????volatile?UINT32???BSRR;??????volatile?UINT32???BRR;???????volatile?UINT32???LCKR;???}; ??struct?CortexM3_EXTI ??{ ????static?const?UINT32?c_Base?=?0x40010400; ?????volatile?UINT32??IMR; ?????volatile?UINT32??EMR; ?????volatile?UINT32??RTSR; ?????volatile?UINT32??FTSR; ?????volatile?UINT32??SWIER; ?????volatile?UINT32??PR; ??}; ??struct?CortexM3_AFIO ??{ ????static?const?UINT32?c_Base?=?0x40010000;?? ?????volatile?UINT32?EVCR; ?????volatile?UINT32?MAPR; ?????volatile?UINT32?EXTICR[4]; ??}; ?? 由于NVIC相關(guān)的代碼我們已經(jīng)在《NVIC中斷處理》說過了,這里就不重復(fù)了。
對.Net Micro Framework的架構(gòu)來說,要實現(xiàn)如下幾個接口:
1、CPU_GPIO_Initialize
2、CPU_GPIO_Uninitialize
3、CPU_GPIO_Attributes
4、CPU_GPIO_DisablePin
5、CPU_GPIO_EnableOutputPin
6、CPU_GPIO_EnableInputPin
7、CPU_GPIO_EnableInputPin2
8、CPU_GPIO_GetPinState
9、CPU_GPIO_SetPinState
10、CPU_GPIO_GetPinCount
11、CPU_GPIO_GetPinsMap
12、CPU_GPIO_GetSupportedResistorModes
13、CPU_GPIO_GetSupportedInterruptModes
14、CPU_GPIO_PinIsBusy
15、CPU_GPIO_ReservePin
16、CPU_GPIO_GetDebounce
17、CPU_GPIO_SetDebounce
考慮到難易程度和篇幅,我們只介紹CPU_GPIO_Initialize、CPU_GPIO_EnableOutputPin、 CPU_GPIO_EnableInputPin和EXTI_IRQHandler 中斷函數(shù)的具體實現(xiàn)。
BOOL?GPIO_Driver::Initialize() ??{ ???????????CortexM3_AFIO?&AFIO?=?CortexM3::AFIO(); ??????for(int?i=0;i<4;i++)? ??????{ ?????????AFIO.EXTICR[i]=0x0000; ??????}???????? ??CortexM3_EXTI?&EXTI=?CortexM3::EXTI();???? ??????EXTI.IMR?=?0x00000000; ??????EXTI.EMR?=?0x00000000; ??????EXTI.RTSR?=?0x00000000;? ??????EXTI.FTSR?=?0x00000000;? ??????EXTI.PR?=?0x0007FFFF; ????????????if(!CPU_INTC_ActivateInterruptEx(CortexM3_NVIC::c_IRQ_Index_EXTI0,(UINT32)(void?*)EXTI_IRQHandler?))???return?FALSE; ?????????????????return?TRUE; ??} ?? 其中比較重要的是CPU_INTC_ActivateInterruptEx函數(shù),它可動態(tài)設(shè)置c_IRQ_Index_EXTI0中斷所對應(yīng)的中斷函數(shù)的入口地址。
void?GPIO_Driver::EnableOutputPin(GPIO_PIN?pin,?BOOL?initialState) ??{ ??????ASSERT(pin?<?c_MaxPins);???????? ??????UINT32?port?=?PinToPort(pin);? ???????????UINT32?bit?=?PinToBit(pin); ??????UINT32?pos?=?(bit?%?8)<<2; ???????????CortexM3_GPIO?&GPIO=?CortexM3::GPIO(port);???? ????????????if(bit<8) ??????{???????? ?????????GPIO.CRL?=?(GPIO.CRL?&?~(0x0F?<<?pos))?|?(CortexM3_GPIO::GPIO_Mode_Speed_50MHz?<<?pos); ??????} ???????????else???????????{ ??????????????GPIO.CRH?=?(GPIO.CRH?&?~(0x0F?<<?pos))?|?(CortexM3_GPIO::GPIO_Mode_Speed_50MHz?<<?pos); ???????????} ??? ????????????if(initialState)?GPIO.BSRR?=?0x1?<<?bit; ??????else?GPIO.BRR?=?0x1?<<?bit; ??} ?? 輸出默認為通用推挽輸出模式,你也可以根據(jù)實際需要進行必要的調(diào)整。
BOOL?GPIO_Driver::EnableInputPin(GPIO_PIN?pin,?BOOL?GlitchFilterEnable,?GPIO_INTERRUPT_SERVICE_ROUTINE?ISR,?void?*pinIsrParam,?GPIO_INT_EDGE?intEdge,?GPIO_RESISTOR?resistorState) ??{ ??????ASSERT(pin?<?c_MaxPins);???????? ??????UINT32?port?=?PinToPort(pin);? ???????????UINT32?bit?=?PinToBit(pin); ??????UINT32?pos?=?(bit?%?8)<<2; ???????????CortexM3_GPIO?&GPIO=?CortexM3::GPIO(port);???? ??????//浮空輸入 ??????if(bit<8) ??????{???????? ?????????GPIO.CRL?=?(GPIO.CRL?&?~(0x0F?<<?pos))|?(CortexM3_GPIO::GPIO_Mode_IN_FLOATING?<<?pos); ??????} ???????????else ???????????{ ??????????????GPIO.CRH?=?(GPIO.CRH?&?~(0x0F?<<?pos))|?(CortexM3_GPIO::GPIO_Mode_IN_FLOATING?<<?pos); ???????????} ??????//中斷輸入源配置(AFIO) ??????CortexM3_AFIO?&AFIO?=?CortexM3::AFIO(); ??????AFIO.EXTICR[bit?>>?2]?&=?~(0x0F?<<?(0x04?*?(bit?&?0x03))); ??????AFIO.EXTICR[bit?>>?2]?|=?port?<<?(0x04?*?(bit?&?0x03)); ???????????CortexM3_EXTI?&EXTI=CortexM3::EXTI(); ??????if(ISR) ??????{ ??????????switch(intEdge) ??????????{ ??????????????case?GPIO_INT_NONE:???//無中斷 ??????????????????EXTI.IMR?&=?~(0x1<<bit);???????????????????????????????????? ??????????????????return?FALSE; ??????????????case?GPIO_INT_EDGE_LOW:???//下降沿中斷 ??????????????case?GPIO_INT_LEVEL_LOW: ??????????????????EXTI.IMR?|=?0x1<<bit;????? ???????????????????????????????????????EXTI.FTSR?|=?0x1<<bit;????????//下降沿有效 ???????????????????????????????????????EXTI.RTSR?&=?~(0x1<<bit);??//上升沿?zé)o效 ??????????????????break; ??????????????//略 ??????????????default: ??????????????????ASSERT(0); ??????????????????return?FALSE; ??????????} ??????}????????????????? ????????????????????? ??????return?TRUE; ??} ?? GPIO輸入的實現(xiàn)比較繁瑣一些,可以根據(jù)需要僅把端口配置成輸入模式,而不配置相應(yīng)的中斷參數(shù)。這樣可以通過不斷掃描的方式獲得輸入信號。
void?GPIO_Driver::ISR(void?*Param) ??{ ???????????CortexM3_EXTI?&EXTI=CortexM3::EXTI(); ??????UINT32?interruptsActive?=?EXTI.PR; ??????UINT32?bitMask??=?0x1,?bitIndex?=?0; ???????????while(interruptsActive) ??????{ ??????????while((interruptsActive?&?bitMask)?==?0) ??????????{ ??????????????bitMask??<<=?1; ??????????????++bitIndex; ??????????} ??????????CortexM3_AFIO?&AFIO?=?CortexM3::AFIO(); ??????????UINT32?port?=?(AFIO.EXTICR[bitIndex?>>?2]>>(0x04?*?(bitIndex?&?0x03)))?&?0xF; ??????????GPIO_PIN?pin?=?BitToPin(?bitIndex,?port); ??????????PIN_ISR_DESCRIPTOR&?pinIsr?=?g_GPIO_Driver.m_PinIsr[?pin?]; ??????????pinIsr.Fire(?(void*)&pinIsr?); ??????????interruptsActive?^=?bitMask; ?????????????????????EXTI.PR?|=?bitMask; ??????}? ??} ?? 在中斷函數(shù)中,根據(jù)相關(guān)寄存器的值來判斷哪一個(或同時哪一些)GPIO發(fā)生的中斷,并由此執(zhí)行業(yè)已配置好的異步中斷處理函數(shù)。
好了,寫完了GPIO驅(qū)動程序,我們就可以漂漂亮亮的在NativeSample中寫我們的測試程序了:
void?ISR(?GPIO_PIN?Pin,?BOOL?PinState,?void*?Param?) ??{ ??????if(PinState)????//?released,?up ??????{ ????????CPU_GPIO_SetPinState(GPIO_Driver::PF7,0x0); ??????} ??????else????????????//?pressed,?down ??????{ ?????????CPU_GPIO_SetPinState(GPIO_Driver::PF7,0x1); ??????} ??} ??? ??void?ApplicationEntryPoint() ??{??? ??????//LED?D1?D2?D3?D4??? ??????CPU_GPIO_EnableOutputPin(GPIO_Driver::PF7,FALSE); ???????????CPU_GPIO_EnableOutputPin(GPIO_Driver::PF8,FALSE); ??? ???????????//user按鈕?=?0x1 ???????????CPU_GPIO_EnableInputPin(GPIO_Driver::PG8,FALSE,ISR,GPIO_INT_EDGE_BOTH,RESISTOR_PULLDOWN);???????????????????? ??????while(TRUE)????? ??????{??? ?????????????CPU_GPIO_SetPinState(GPIO_Driver::PF8,!CPU_GPIO_GetPinState(GPIO_Driver::PF8)); ?????????????Events_WaitForEvents(?0,?1000?);??????? ???????????} ??} ?? 上面的程序比我們最初在《調(diào)試初步:點亮LED燈》中提到的代碼清爽多了,把程序下載到開發(fā)板上運行,你會發(fā)現(xiàn)D3 LED燈以一秒為周期不斷地閃爍,而D2 LED燈則在user按鈕按下時才亮,放開時則滅。
只要細心 + 耐心,其實嵌入式開發(fā)還是比較容易的,并且功能實現(xiàn)那一刻喜悅的“強度”,是作為PC平臺軟件開發(fā)者所難以企及的
?
轉(zhuǎn)載于:https://blog.51cto.com/yfsoft/321228
總結(jié)
以上是生活随笔為你收集整理的【.Net Micro Framework PortingKit - 08】GPIO驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。