安德鲁

菜农M0助学板之GPIO按键之边沿检测小练(寄存器操作方式)

0
阅读(4942)

现象解释:
  KED1双边沿控制LED1:按下亮松开灭
  KEY2下降沿控制LED2 第一次按下即亮,第二次按下灭
  KEY2上升沿控制LED3:第一次松开即亮,第二次松开即灭


这个比较简单,直接贴出代码,大家可以验证一下。


main.c

#include "nuc1xx.h"
#include "DrvSYS.h"
/**********************************************************
* 宏定义
**********************************************************/
typedef enum{NO=0, YES=!NO}BOOL_T;
/* GPIO宏定义参考 */
// GPIO功能寄存器宏定义
//typedef struct
//{
//    __IO uint32_t PMD0:2;
//    __IO uint32_t PMD1:2;
//    __IO uint32_t PMD2:2;
//    __IO uint32_t PMD3:2;
//    __IO uint32_t PMD4:2;
//    __IO uint32_t PMD5:2;
//    __IO uint32_t PMD6:2;
//    __IO uint32_t PMD7:2;
//    __IO uint32_t PMD8:2;
//    __IO uint32_t PMD9:2;
//    __IO uint32_t PMD10:2;
//    __IO uint32_t PMD11:2;
//    __IO uint32_t PMD12:2;
//    __IO uint32_t PMD13:2;
//    __IO uint32_t PMD14:2;
//    __IO uint32_t PMD15:2;
//} GPIO_PMD_T;
// PMD[1:0] = 0; // 输入
//            1; // 输出
//            2; // 开漏
//            3; // 准双向
// GPIO输出寄存器宏定义
//typedef __IO uint32_t GPIO_DOUT_T;
// GPIO输入寄存器宏定义
//typedef __IO uint32_t GPIO_PIN_T;
/* 设置所用GPIO */
// GPIO方向
#define BEEP_OE     GPIOB->PMD.PMD10 = 1 // BEEP输出
#define KEY_IE      (*(uint32_t *)&GPIOB->PMD) &= 0x0FFFFFFF 
//#define KEY_IE      GPIOB->PMD.PMD15 = 0; \
//                    GPIOB->PMD.PMD14 = 0   
#define LED_OE      (*(uint32_t *)&GPIOA->PMD) |= 0x00000150
//#define LED_OE      GPIOA->PMD.PMD2 = 1; \
//                    GPIOA->PMD.PMD3 = 1; \
//                    GPIOA->PMD.PMD4 = 1;     
// GPIOIO电平
#define Close_BEEP  GPIOB->DOUT&=~(1<<10)
#define Open_BEEP   GPIOB->DOUT|=1<<10  
#define Close_LED1  GPIOA->DOUT|=1<<2
#define Open_LED1   GPIOA->DOUT&=~(1<<2)
#define Close_LED2  GPIOA->DOUT|=1<<3
#define Open_LED2   GPIOA->DOUT&=~(1<<3)
#define Close_LED3  GPIOA->DOUT|=1<<4
#define Open_LED3   GPIOA->DOUT&=~(1<<4)
#define Read_KEY1   (GPIOB->PIN>>15)&0x1
#define Read_KEY2   (GPIOB->PIN>>14)&0x1      
/**********************************************************
* 函数及变量申明
**********************************************************/
void MAIN_INIT(void);
void TMR0_IRQHandler(void) __irq;
BOOL_T Flag_tmr0_20ms = NO;
/**********************************************************
* 系统上电初始化
**********************************************************/
void MAIN_INIT(void)
{
    UNLOCKREG();
    {   /* 配置系统时钟 */
        SYSCLK->PWRCON.XTL12M_EN = 1; //  设定12M外部晶振
        DrvSYS_Delay(5000); // 等待时钟就绪
        DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入
        DrvSYS_Open(50000000); // 打开50MHz
    }      
    {   /* 配置GPIO */
        BEEP_OE; Close_BEEP;
        LED_OE; Close_LED1; Close_LED2;
        KEY_IE;         
    }
    {   /* 配置TMR0 */
        NVIC_DisableIRQ(TMR0_IRQn);
        // 第一步 使能和选择定时器时钟源及使能定时器模块          
        SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源 
        SYSCLK->APBCLK.TMR0_EN =1;  // 使能定时器0
        TIMER0->TCSR.CEN = 1;       // 使能定时器模块
        // 第二步 选择操作模式    
        TIMER0->TCSR.MODE = 1; // 选择周期模式
        TIMER0->TCSR.CRST = 1; // 清加1计数器    
        // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP)
        TIMER0->TCSR.PRESCALE = 11; // 12分频
        TIMER0->TCMPR = 5000; // 12M/12/20000, 20ms
        // 第四步 使能中断
        TIMER0->TISR.TIF = 1; // 清中断  
        TIMER0->TCSR.IE = 1; // 使能中断 
        NVIC_EnableIRQ(TMR0_IRQn);  // 使能TMR0中断
        // 第五步 使能定时器模块
        TIMER0->TCSR.CRST = 1; // 复位向上计数器
        TIMER0->TCSR.CEN = 1; // 使能TMR0
        //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值
    }
    LOCKREG();
}
/**********************************************************
* TMR0 ISR
**********************************************************/
void TMR0_IRQHandler(void) __irq
{   // 注意:ISR内必须清中断
    TIMER0->TISR.TIF = 1; // 清中断  

    Flag_tmr0_20ms = YES;   
}
/**********************************************************
* 主函数
**********************************************************/
int main(void)
{
    uint8_t KEY_OldVal[2] = {1,1}; // 按键之旧值,次态值
    uint8_t KEY_NewVal[2] = {1,1}; // 按键之新值,现态值
    uint8_t LED_Val[3] = {1,1,1}; // LED值
    MAIN_INIT(); // 上电初始化系统

    while(1) {
        if(Flag_tmr0_20ms != NO) {
            Flag_tmr0_20ms = NO;

            KEY_NewVal[0] = Read_KEY2;
            KEY_NewVal[1] = Read_KEY1;

            /* KEY2双边沿控制LED1 */
            if(KEY_NewVal[0] ^ KEY_OldVal[0]) {
                // 先锁存旧值
                KEY_OldVal[0] = KEY_NewVal[0]; 
                // 双边沿采样
                LED_Val[0] = KEY_OldVal[0];                             
            }

            /* KEY1单边沿控制LED2、LED3 */
            if(KEY_NewVal[1] ^ KEY_OldVal[1]) {
                // 下降沿控制LED2
                if(KEY_OldVal[1]/* && (~KEY_NewVal[1])*/) 
                    LED_Val[1] = LED_Val[1] ? 0 : 1; 
                // 上升沿采样LED3 
                if(/*(~KEY_OldVal[1]) && */KEY_NewVal[1]) 
                    LED_Val[2] = LED_Val[2] ? 0 : 1;
                // 后锁存旧值
                KEY_OldVal[1] = KEY_NewVal[1];                              
            }


            /* 响应双边沿控制 */
            // 按下亮松开灭
            if(!LED_Val[0]) Open_LED1;
            else Close_LED1;
            
            /* 响应下降沿控制 */
            // 第一次按下即亮,第二次按下灭
            if(!LED_Val[1]) Open_LED2;
            else Close_LED2;  
            
            /* 响应上升沿控制 */
            // 第一次松开即亮,第二次松开即灭
            if(!LED_Val[2]) Open_LED3;
            else Close_LED3;           
           }

        if(0) break; // 跳出大循环
    }
    return 0;
}

下面给出Multisim之硬件仿真图示范


双边沿检测


上升沿检测


下降沿检测