菜农M0助学板之GPIO按键之边沿检测小练(寄存器操作方式)
0赞
发表于 5/16/2011 8:54:51 PM
阅读(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之硬件仿真图示范
双边沿检测
上升沿检测
下降沿检测