要點亮LED燈或獲得輸入IO的狀態應該是比較容易的,打開端口時鐘,然后讀寫相關的GPIO寄存器就可以了,但是要實現一個輸入中斷,就要費些周折了。
對STM32(Cortex-M3)的芯片,要實現一個GPIO中斷一般需要如下幾步:
1、? 配置時鐘控制器寄存器(RCC)的APB2RSTR,確保對應的GPIOA ~ GPIOG時鐘使能。
2、? 對GPIO寄存器的CRL(或CRH)要設置正確的輸入模式,如浮空輸入模式(對接收IO中斷來說,當然要設置成輸入模式)。
3、? 要通過AFIO寄存器配置中斷的輸入來源,對STM32芯片來說,具有19路EXTI中斷線,其中3路分別連接PVD輸出、RTC鬧鐘事件及USB喚醒事件,剩下的對GPIOA ~ GPIOG 7*16=112個IO點來說,同時只能配置16路IO輸入中斷。
4、? 接下來要配置EXIT寄存器,根據需要來配置是上升沿觸發中斷、還是下降沿觸發中斷或兩者都觸發。
5、? 而后比較重要的是, 要配置NVIC的SETENA寄存器,讓對應的EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5或EXTI15_10中斷位使能,此外還要配置各中斷的優先級(前提是中斷優先級分組寄存器已配置完畢)。
6、? 最后我們要設置中斷向量表(該向量表要重定位到內存中,以便于動態修改),在EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5或EXTI15_10對應的位置,放入我們的中斷函數的入口地址。
?
和PC平臺程序開發不同,基本上你每做一步,都可以很直觀的看到你的進展和成果,但對嵌入式開發來說,如果上述幾步有任何一個環節出了問題,你的進展都是零,有時候你會花上一天的時間去反復核實每個寄存器的值是否正確,以期獲得你希望的結果。所以說嵌入式開發是驚喜的型的,要么成,要么不成,一線之隔!
接下來我們說一下GPIO實現的詳細步驟,首先在CortexM3.h頭文件中添加GPIO相關的寄存器描述:
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相關的代碼我們已經在《NVIC中斷處理》說過了,這里就不重復了。
對.Net Micro Framework的架構來說,要實現如下幾個接口:
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 中斷函數的具體實現。
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函數,它可動態設置c_IRQ_Index_EXTI0中斷所對應的中斷函數的入口地址。
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; ??} ?? 輸出默認為通用推挽輸出模式,你也可以根據實際需要進行必要的調整。
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);??//上升沿無效 ??????????????????break; ??????????????//略 ??????????????default: ??????????????????ASSERT(0); ??????????????????return?FALSE; ??????????} ??????}????????????????? ????????????????????? ??????return?TRUE; ??} ?? GPIO輸入的實現比較繁瑣一些,可以根據需要僅把端口配置成輸入模式,而不配置相應的中斷參數。這樣可以通過不斷掃描的方式獲得輸入信號。
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; ??????}? ??} ?? 在中斷函數中,根據相關寄存器的值來判斷哪一個(或同時哪一些)GPIO發生的中斷,并由此執行業已配置好的異步中斷處理函數。
好了,寫完了GPIO驅動程序,我們就可以漂漂亮亮的在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?);??????? ???????????} ??} ?? 上面的程序比我們最初在《調試初步:點亮LED燈》中提到的代碼清爽多了,把程序下載到開發板上運行,你會發現D3 LED燈以一秒為周期不斷地閃爍,而D2 LED燈則在user按鈕按下時才亮,放開時則滅。
只要細心 + 耐心,其實嵌入式開發還是比較容易的,并且功能實現那一刻喜悅的“強度”,是作為PC平臺軟件開發者所難以企及的
?
轉載于:https://blog.51cto.com/yfsoft/321228
總結
以上是生活随笔為你收集整理的【.Net Micro Framework PortingKit - 08】GPIO驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。