zoukankan      html  css  js  c++  java
  • 【.Net Micro Framework PortingKit 08】GPIO驱动

    要点亮LED灯或获得输入IO的状态应该是比较容易的,打开端口时钟,然后读写相关的GPIO寄存器就可以了,但是要实现一个输入中断,就要费些周折了。

    STM32Cortex-M3)的芯片,要实现一个GPIO中断一般需要如下几步:

    1、 配置时钟控制器寄存器(RCC)的APB2RSTR,确保对应的GPIOA ~ GPIOG时钟使能。

    2、 GPIO寄存器的CRL(或CRH)要设置正确的输入模式,如浮空输入模式(对接收IO中断来说,当然要设置成输入模式)。

    3、 要通过AFIO寄存器配置中断的输入来源,对STM32芯片来说,具有19EXTI中断线,其中3路分别连接PVD输出、RTC闹钟事件及USB唤醒事件,剩下的对GPIOA ~ GPIOG 7*16=112IO点来说,同时只能配置16IO输入中断。

    4、 接下来要配置EXIT寄存器,根据需要来配置是上升沿触发中断、还是下降沿触发中断或两者都触发。

    5、 而后比较重要的是, 要配置NVICSETENA寄存器,让对应的EXTI0EXTI1EXTI2EXTI3EXTI4EXTI9_5EXTI15_10中断位使能,此外还要配置各中断的优先级(前提是中断优先级分组寄存器已配置完毕)。

    6、 最后我们要设置中断向量表(该向量表要重定位到内存中,以便于动态修改),在EXTI0EXTI1EXTI2EXTI3EXTI4EXTI9_5EXTI15_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的架构来说,要实现如下几个接口:

    1CPU_GPIO_Initialize

    2CPU_GPIO_Uninitialize

    3CPU_GPIO_Attributes

    4CPU_GPIO_DisablePin

    5CPU_GPIO_EnableOutputPin

    6CPU_GPIO_EnableInputPin

    7CPU_GPIO_EnableInputPin2

    8CPU_GPIO_GetPinState

    9CPU_GPIO_SetPinState

    10CPU_GPIO_GetPinCount

    11CPU_GPIO_GetPinsMap

    12CPU_GPIO_GetSupportedResistorModes

    13CPU_GPIO_GetSupportedInterruptModes

    14CPU_GPIO_PinIsBusy

    15CPU_GPIO_ReservePin

    16CPU_GPIO_GetDebounce

    17CPU_GPIO_SetDebounce

    考虑到难易程度和篇幅,我们只介绍CPU_GPIO_InitializeCPU_GPIO_EnableOutputPin CPU_GPIO_EnableInputPinEXTI_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;

        //NVIC

        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平台软件开发者所难以企及的。
  • 相关阅读:
    BZOJ3875 AHOI2014/JSOI2014骑士游戏(动态规划)
    Contest 9
    Contest 8
    Codeforces Round#514 Div.2 翻车记
    Contest 7
    安徽师大附中%你赛day6 T3 Hamsters [POI2010]CHO-Hamsters 解题报告
    2018.8 安徽师大附中培训游记
    安徽师大附中%你赛day5 T3 树上行走 解题报告
    安徽师大附中%你赛day4T2 演讲解题报告
    安徽师大附中%你赛day4T1 金字塔 解题报告
  • 原文地址:https://www.cnblogs.com/yefanqiu/p/1652827.html
Copyright © 2011-2022 走看看