【.Net Micro Framework PortingKit - 08】GPIO驱动

简介:

要点亮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相关的寄存器描述:

 
  1. struct CortexM3_GPIO  
  2.  
  3. {  
  4.  
  5.   static const UINT32 c_Base = 0x40010800;  
  6.  
  7.   static const UINT32 A = 0;  
  8.  
  9.   static const UINT32 B = 1;  
  10.  
  11.   static const UINT32 C = 2;  
  12.  
  13.   static const UINT32 D = 3;  
  14.  
  15.   static const UINT32 E = 4;  
  16.  
  17.   static const UINT32 F = 5;   
  18.  
  19.   static const UINT32 G = 6;   
  20.  
  21.   static const UINT32 GPIO_Mode_NULL = 0x00;  
  22.  
  23.   static const UINT32 GPIO_Mode_Speed_10MHz = 0x01;  
  24.  
  25.   static const UINT32 GPIO_Mode_Speed_2MHz = 0x02;  
  26.  
  27.   static const UINT32 GPIO_Mode_Speed_50MHz = 0x03;  
  28.  
  29.   static const UINT32 GPIO_Mode_IN_FLOATING = 0x04;  
  30.  
  31.     /****/ volatile UINT32   CRL;  //配置寄存器  
  32.  
  33.   /****/ volatile UINT32   CRH;  
  34.  
  35.   /****/ volatile UINT32   IDR;  //数据寄存器  
  36.  
  37.   /****/ volatile UINT32   ODR;  
  38.  
  39.   /****/ volatile UINT32   BSRR; //置位复位寄存器  
  40.  
  41.   /****/ volatile UINT32   BRR;  //复位寄存器  
  42.  
  43.   /****/ volatile UINT32   LCKR; //锁定寄存器  
  44.  
  45. };  
  46.  
  47. struct CortexM3_EXTI  
  48.  
  49. {  
  50.  
  51.   static const UINT32 c_Base = 0x40010400;  
  52.  
  53.   /****/ volatile UINT32  IMR;  
  54.  
  55.   /****/ volatile UINT32  EMR;  
  56.  
  57.   /****/ volatile UINT32  RTSR;  
  58.  
  59.   /****/ volatile UINT32  FTSR;  
  60.  
  61.   /****/ volatile UINT32  SWIER;  
  62.  
  63.   /****/ volatile UINT32  PR;  
  64.  
  65. };  
  66.  
  67. struct CortexM3_AFIO  
  68.  
  69. {  
  70.  
  71.   static const UINT32 c_Base = 0x40010000;    
  72.  
  73.   /****/ volatile UINT32 EVCR;  
  74.  
  75.   /****/ volatile UINT32 MAPR;  
  76.  
  77.   /****/ volatile UINT32 EXTICR[4];  
  78.  
  79. };  
  80.  

由于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 中断函数的具体实现。

 
  1. BOOL GPIO_Driver::Initialize()  
  2.  
  3. {  
  4.  
  5.          CortexM3_AFIO &AFIO = CortexM3::AFIO();  
  6.  
  7.     for(int i=0;i<4;i++)   
  8.  
  9.     {  
  10.  
  11.        AFIO.EXTICR[i]=0x0000;  
  12.  
  13.     }          
  14.  
  15. CortexM3_EXTI &EXTI= CortexM3::EXTI();      
  16.  
  17.     EXTI.IMR = 0x00000000;  
  18.  
  19.     EXTI.EMR = 0x00000000;  
  20.  
  21.     EXTI.RTSR = 0x00000000;   
  22.  
  23.     EXTI.FTSR = 0x00000000;   
  24.  
  25.     EXTI.PR = 0x0007FFFF;  
  26.  
  27.     //NVIC   
  28.  
  29.     if(!CPU_INTC_ActivateInterruptEx(CortexM3_NVIC::c_IRQ_Index_EXTI0,(UINT32)(void *)EXTI_IRQHandler ))   return FALSE;  
  30.  
  31.          //略  
  32.  
  33.     return TRUE;  
  34.  
  35. }  
  36.  

其中比较重要的是CPU_INTC_ActivateInterruptEx函数,它可动态设置c_IRQ_Index_EXTI0中断所对应的中断函数的入口地址。

 
  1. void GPIO_Driver::EnableOutputPin(GPIO_PIN pin, BOOL initialState)  
  2.  
  3. {  
  4.  
  5.     ASSERT(pin < c_MaxPins);          
  6.  
  7.     UINT32 port = PinToPort(pin);   
  8.  
  9.          UINT32 bit = PinToBit(pin);  
  10.  
  11.     UINT32 pos = (bit % 8)<<2;  
  12.  
  13.          CortexM3_GPIO &GPIO= CortexM3::GPIO(port);      
  14.  
  15.     //通用推挽输出模式  
  16.  
  17.     if(bit<8)  
  18.  
  19.     {          
  20.  
  21.        GPIO.CRL = (GPIO.CRL & ~(0x0F << pos)) | (CortexM3_GPIO::GPIO_Mode_Speed_50MHz << pos);  
  22.  
  23.     }  
  24.  
  25.          else 
  26.  
  27.          {  
  28.  
  29.             GPIO.CRH = (GPIO.CRH & ~(0x0F << pos)) | (CortexM3_GPIO::GPIO_Mode_Speed_50MHz << pos);  
  30.  
  31.          }  
  32.  
  33.    
  34.  
  35.     //初值  
  36.  
  37.     if(initialState) GPIO.BSRR = 0x1 << bit;  
  38.  
  39.     else GPIO.BRR = 0x1 << bit;  
  40.  
  41. }  
  42.  

输出默认为通用推挽输出模式,你也可以根据实际需要进行必要的调整。

 
  1. BOOL GPIO_Driver::EnableInputPin(GPIO_PIN pin, BOOL GlitchFilterEnable, GPIO_INTERRUPT_SERVICE_ROUTINE ISR, void *pinIsrParam, GPIO_INT_EDGE intEdge, GPIO_RESISTOR resistorState)  
  2.  
  3. {  
  4.  
  5.     ASSERT(pin < c_MaxPins);          
  6.  
  7.     UINT32 port = PinToPort(pin);   
  8.  
  9.          UINT32 bit = PinToBit(pin);  
  10.  
  11.     UINT32 pos = (bit % 8)<<2;  
  12.  
  13.          CortexM3_GPIO &GPIOCortexM3::GPIO(port);      
  14.  
  15.     //浮空输入  
  16.  
  17.     if(bit<8)  
  18.  
  19.     {          
  20.  
  21.        GPIO.CRL = (GPIO.CRL & ~(0x0F << pos))| (CortexM3_GPIO::GPIO_Mode_IN_FLOATING << pos);  
  22.  
  23.     }  
  24.  
  25.          else  
  26.  
  27.          {  
  28.  
  29.             GPIO.CRH = (GPIO.CRH & ~(0x0F << pos))| (CortexM3_GPIO::GPIO_Mode_IN_FLOATING << pos);  
  30.  
  31.          }  
  32.  
  33.     //中断输入源配置(AFIO)  
  34.  
  35.     CortexM3_AFIO &AFIO = CortexM3::AFIO();  
  36.  
  37.     AFIO.EXTICR[bit >> 2] &= ~(0x0F << (0x04 * (bit & 0x03)));  
  38.  
  39.     AFIO.EXTICR[bit >> 2] |= port << (0x04 * (bit & 0x03));  
  40.  
  41.          CortexM3_EXTI &EXTI=CortexM3::EXTI();  
  42.  
  43.     if(ISR)  
  44.  
  45.     {  
  46.  
  47.         switch(intEdge)  
  48.  
  49.         {  
  50.  
  51.             case GPIO_INT_NONE:   //无中断  
  52.  
  53.                 EXTI.IMR &= ~(0x1<<bit);                                      
  54.  
  55.                 return FALSE;  
  56.  
  57.             case GPIO_INT_EDGE_LOW:   //下降沿中断  
  58.  
  59.             case GPIO_INT_LEVEL_LOW:  
  60.  
  61.                 EXTI.IMR |= 0x1<<bit;       
  62.  
  63.                                      EXTI.FTSR |= 0x1<<bit;        //下降沿有效  
  64.  
  65.                                      EXTI.RTSR &= ~(0x1<<bit);  //上升沿无效  
  66.  
  67.                 break;  
  68.  
  69.             //略  
  70.  
  71.             default:  
  72.  
  73.                 ASSERT(0);  
  74.  
  75.                 return FALSE;  
  76.  
  77.         }  
  78.  
  79.     }                   
  80.  
  81.                      
  82.  
  83.     return TRUE;  
  84.  
  85. }  
  86.  

GPIO输入的实现比较繁琐一些,可以根据需要仅把端口配置成输入模式,而不配置相应的中断参数。这样可以通过不断扫描的方式获得输入信号。

 
  1. void GPIO_Driver::ISR(void *Param)  
  2.  
  3. {  
  4.  
  5.          CortexM3_EXTI &EXTI=CortexM3::EXTI();  
  6.  
  7.     UINT32 interruptsActive = EXTI.PR;  
  8.  
  9.     UINT32 bitMask  = 0x1, bitIndex = 0;  
  10.  
  11.          while(interruptsActive)  
  12.  
  13.     {  
  14.  
  15.         while((interruptsActive & bitMask) == 0)  
  16.  
  17.         {  
  18.  
  19.             bitMask  <<= 1;  
  20.  
  21.             ++bitIndex;  
  22.  
  23.         }  
  24.  
  25.         CortexM3_AFIO &AFIO = CortexM3::AFIO();  
  26.  
  27.         UINT32 port = (AFIO.EXTICR[bitIndex >> 2]>>(0x04 * (bitIndex & 0x03))) & 0xF;  
  28.  
  29.         GPIO_PIN pin = BitToPin( bitIndex, port);  
  30.  
  31.         PIN_ISR_DESCRIPTOR& pinIsr = g_GPIO_Driver.m_PinIsr[ pin ];  
  32.  
  33.         pinIsr.Fire( (void*)&pinIsr );  
  34.  
  35.         interruptsActive ^= bitMask;  
  36.  
  37.                    EXTI.PR |= bitMask;  
  38.  
  39.     }   
  40.  
  41. }  
  42.  

在中断函数中,根据相关寄存器的值来判断哪一个(或同时哪一些)GPIO发生的中断,并由此执行业已配置好的异步中断处理函数。

好了,写完了GPIO驱动程序,我们就可以漂漂亮亮的在NativeSample中写我们的测试程序了:

 
  1. void ISR( GPIO_PIN Pin, BOOL PinState, void* Param )  
  2.  
  3. {  
  4.  
  5.     if(PinState)    // released, up  
  6.  
  7.     {  
  8.  
  9.       CPU_GPIO_SetPinState(GPIO_Driver::PF7,0x0);  
  10.  
  11.     }  
  12.  
  13.     else            // pressed, down  
  14.  
  15.     {  
  16.  
  17.        CPU_GPIO_SetPinState(GPIO_Driver::PF7,0x1);  
  18.  
  19.     }  
  20.  
  21. }  
  22.  
  23.    
  24.  
  25. void ApplicationEntryPoint()  
  26.  
  27. {     
  28.  
  29.     //LED D1 D2 D3 D4     
  30.  
  31.     CPU_GPIO_EnableOutputPin(GPIO_Driver::PF7,FALSE);  
  32.  
  33.          CPU_GPIO_EnableOutputPin(GPIO_Driver::PF8,FALSE);  
  34.  
  35.    
  36.  
  37.          //user按钮 = 0x1  
  38.  
  39.          CPU_GPIO_EnableInputPin(GPIO_Driver::PG8,FALSE,ISR,GPIO_INT_EDGE_BOTH,RESISTOR_PULLDOWN);                      
  40.  
  41.     while(TRUE)       
  42.  
  43.     {     
  44.  
  45.            CPU_GPIO_SetPinState(GPIO_Driver::PF8,!CPU_GPIO_GetPinState(GPIO_Driver::PF8));  
  46.  
  47.            Events_WaitForEvents( 0, 1000 );         
  48.  
  49.          }  
  50.  
  51. }  
  52.  

上面的程序比我们最初在《调试初步:点亮LED灯》中提到的代码清爽多了,把程序下载到开发板上运行,你会发现D3 LED灯以一秒为周期不断地闪烁,而D2 LED灯则在user按钮按下时才亮,放开时则灭。

只要细心 + 耐心,其实嵌入式开发还是比较容易的,并且功能实现那一刻喜悦的“强度”,是作为PC平台软件开发者所难以企及的

 










本文转自yefanqiu51CTO博客,原文链接:http://blog.51cto.com/yfsoft/321228,如需转载请自行联系原作者

相关文章
.Net Micro Framework研究—Digi开发板初探
写的比较基础全面,由于我们北航的研发团队先研究了Digi的开发板,所以直到今天Digi开发板才到我的手上,我的《Micro Framework研究》系列文章以后也会陆续推出
734 0
.Net Micro Framework研究—IO读写
试验平台:Digi MF开发板
459 0
.Net Micro Framework研究—串口操作
试验平台:Digi MF开发板,Digi提供的示例中包含了串口的示例程序
576 0
|
网络协议
.Net Micro Framework研究—TCP/IP通信
关于网络通信方面,Digi提供了两个程序,一个是TCP Server运行在Digi的开发板上,一个是TCP Client程序,运行在PC上,通过网络,上位机很容易控制Digi开发的IO信号
664 0
.Net Micro Framework研究—模拟器改造
由于Digi提供的开发板没有LCD显示屏,所以有关绘图方面的操作,只好在模拟器上进行了。
562 0
|
Windows
.Net Micro Framework研究—中文显示
微软示例程序中,仅支持两种字体(small.tinyfnt和NinaB.tinyfnt),并不支持中文。
594 0
.Net Micro Framework研究—绘图
目前在VS2005的环境里,还不支持.Net Micro Framework界面的所见即所得绘制,界面制作有三种方式,一是窗体直接绘图,二是Panel+形状对象、三是窗体+控件。第一种做法让人觉得又回到了DOS时代,回到了SCREEN 12的16色的世界里。
497 0
.Net Micro Framework研究—Shapes命名空间
在Microsoft.SPOT.Presentation.Shapes命名空间下,包含几个形状对象,主要有Ellipse、Line、Polygon、Rectangle,同样也只有Rectangle实现的最好,其他形状都不支持填充色,虽然每个对象都有Fill属性。
656 0
.Net Micro Framework研究—窗体控件
目前版本的MF对TCP协议栈支持也并不完善(对串口也谈不上完善,毕竟不支持奇偶校验、停止位设置),Digi的以太网口是加入了自己的处理方案,明年二月份微软将要发布的MF V3.0版,就已经完全支持TCP了,到时候MF最理想的应用也许就是通信转换了。
518 0
.Net Micro Framework研究—应用实例
在前几篇关于.Net Micro Framework的研究文章中,我对它的绘图功能实不敢恭维,不过微软的MF开发人员很聪明,对位图方面的功能实现的就比较完善,这样做起图形应用来就不至于捉襟见肘了。前段时间用.Net Compact Framework实现了一个奥运场馆查询
547 0