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

[复制链接]
 楼主| tear086 发表于 2011-5-5 07:52 | 显示全部楼层 |阅读模式
现象解释:
  KED1双边沿控制LED1:按下亮松开灭
  KEY2下降沿控制LED2 第一次按下即亮,第二次按下灭
  KEY2上升沿控制LED3:第一次松开即亮,第二次松开即灭

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

main.c
  1. #include "nuc1xx.h"
  2. #include "DrvSYS.h"
  3. /**********************************************************
  4. * 宏定义
  5. **********************************************************/
  6. typedef enum{NO=0, YES=!NO}BOOL_T;
  7. /* GPIO宏定义参考 */
  8. // GPIO功能寄存器宏定义
  9. //typedef struct
  10. //{
  11. //    __IO uint32_t PMD0:2;
  12. //    __IO uint32_t PMD1:2;
  13. //    __IO uint32_t PMD2:2;
  14. //    __IO uint32_t PMD3:2;
  15. //    __IO uint32_t PMD4:2;
  16. //    __IO uint32_t PMD5:2;
  17. //    __IO uint32_t PMD6:2;
  18. //    __IO uint32_t PMD7:2;
  19. //    __IO uint32_t PMD8:2;
  20. //    __IO uint32_t PMD9:2;
  21. //    __IO uint32_t PMD10:2;
  22. //    __IO uint32_t PMD11:2;
  23. //    __IO uint32_t PMD12:2;
  24. //    __IO uint32_t PMD13:2;
  25. //    __IO uint32_t PMD14:2;
  26. //    __IO uint32_t PMD15:2;
  27. //} GPIO_PMD_T;
  28. // PMD[1:0] = 0; // 输入
  29. //            1; // 输出
  30. //            2; // 开漏
  31. //            3; // 准双向
  32. // GPIO输出寄存器宏定义
  33. //typedef __IO uint32_t GPIO_DOUT_T;
  34. // GPIO输入寄存器宏定义
  35. //typedef __IO uint32_t GPIO_PIN_T;
  36. /* 设置所用GPIO */
  37. // GPIO方向
  38. #define BEEP_OE     GPIOB->PMD.PMD10 = 1 // BEEP输出
  39. #define KEY_IE      (*(uint32_t *)&GPIOB->PMD) &= 0x0FFFFFFF
  40. //#define KEY_IE      GPIOB->PMD.PMD15 = 0; \
  41. //                    GPIOB->PMD.PMD14 = 0   
  42. #define LED_OE      (*(uint32_t *)&GPIOA->PMD) |= 0x00000150
  43. //#define LED_OE      GPIOA->PMD.PMD2 = 1; \
  44. //                                    GPIOA->PMD.PMD3 = 1; \
  45. //                    GPIOA->PMD.PMD4 = 1;     
  46. // GPIOIO电平
  47. #define Close_BEEP  GPIOB->DOUT&=~(1<<10)
  48. #define Open_BEEP   GPIOB->DOUT|=1<<10  
  49. #define Close_LED1  GPIOA->DOUT|=1<<2
  50. #define Open_LED1   GPIOA->DOUT&=~(1<<2)
  51. #define Close_LED2  GPIOA->DOUT|=1<<3
  52. #define Open_LED2   GPIOA->DOUT&=~(1<<3)
  53. #define Close_LED3  GPIOA->DOUT|=1<<4
  54. #define Open_LED3   GPIOA->DOUT&=~(1<<4)
  55. #define Read_KEY1   (GPIOB->PIN>>15)&0x1
  56. #define Read_KEY2   (GPIOB->PIN>>14)&0x1      
  57. /**********************************************************
  58. * 函数及变量申明
  59. **********************************************************/
  60. void MAIN_INIT(void);
  61. void TMR0_IRQHandler(void) __irq;
  62. BOOL_T Flag_tmr0_20ms = NO;
  63. /**********************************************************
  64. * 系统上电初始化
  65. **********************************************************/
  66. void MAIN_INIT(void)
  67. {
  68.         UNLOCKREG();
  69.         {   /* 配置系统时钟 */
  70.                 SYSCLK->PWRCON.XTL12M_EN = 1; //  设定12M外部晶振
  71.                 DrvSYS_Delay(5000); // 等待时钟就绪
  72.                 DrvSYS_SelectPLLSource(E_SYS_EXTERNAL_12M); // 选择12MHz为PLL输入
  73.                 DrvSYS_Open(50000000); // 打开50MHz
  74.         }      
  75.         {   /* 配置GPIO */
  76.         BEEP_OE; Close_BEEP;
  77.         LED_OE; Close_LED1; Close_LED2;
  78.         KEY_IE;                
  79.         }
  80.         {   /* 配置TMR0 */
  81.                 NVIC_DisableIRQ(TMR0_IRQn);
  82.                 // 第一步 使能和选择定时器时钟源及使能定时器模块         
  83.                 SYSCLK->CLKSEL1.TMR0_S = 0; // 选择12Mhz作为定时器时钟源
  84.                 SYSCLK->APBCLK.TMR0_EN =1;  // 使能定时器0
  85.                 TIMER0->TCSR.CEN = 1;       // 使能定时器模块
  86.                 // 第二步 选择操作模式   
  87.                 TIMER0->TCSR.MODE = 1; // 选择周期模式
  88.                 TIMER0->TCSR.CRST = 1; // 清加1计数器   
  89.                 // 第三步 输出时钟周期 = 定时器时钟源周期*(8位预分频因子 + 1) * (24位比较因子TCMP)
  90.                 TIMER0->TCSR.PRESCALE = 11; // 12分频
  91.                 TIMER0->TCMPR = 5000; // 12M/12/20000, 20ms
  92.                 // 第四步 使能中断
  93.                 TIMER0->TISR.TIF = 1; // 清中断  
  94.                 TIMER0->TCSR.IE = 1; // 使能中断
  95.                 NVIC_EnableIRQ(TMR0_IRQn);  // 使能TMR0中断
  96.                 // 第五步 使能定时器模块
  97.                 TIMER0->TCSR.CRST = 1; // 复位向上计数器
  98.                 TIMER0->TCSR.CEN = 1; // 使能TMR0
  99.                 //TIMER0->TCSR.TDR_EN=1; // 无需读取加1计数器值
  100.         }
  101.         LOCKREG();
  102. }
  103. /**********************************************************
  104. * TMR0 ISR
  105. **********************************************************/
  106. void TMR0_IRQHandler(void) __irq
  107. {   // 注意:ISR内必须清中断
  108.         TIMER0->TISR.TIF = 1; // 清中断  

  109.         Flag_tmr0_20ms = YES;   
  110. }
  111. /**********************************************************
  112. * 主函数
  113. **********************************************************/
  114. int main(void)
  115. {
  116.     uint8_t KEY_OldVal[2] = {1,1}; // 按键之旧值,次态值
  117.     uint8_t KEY_NewVal[2] = {1,1}; // 按键之新值,现态值
  118.     uint8_t LED_Val[3] = {1,1,1}; // LED值
  119.         MAIN_INIT(); // 上电初始化系统

  120.         while(1) {
  121.                 if(Flag_tmr0_20ms != NO) {
  122.                         Flag_tmr0_20ms = NO;

  123.             KEY_NewVal[0] = Read_KEY2;
  124.             KEY_NewVal[1] = Read_KEY1;

  125.             /* KEY2双边沿控制LED1 */
  126.             if(KEY_NewVal[0] ^ KEY_OldVal[0]) {
  127.                 // 先锁存旧值
  128.                 KEY_OldVal[0] = KEY_NewVal[0];
  129.                 // 双边沿采样
  130.                 LED_Val[0] = KEY_OldVal[0];                             
  131.             }

  132.             /* KEY1单边沿控制LED2、LED3 */
  133.             if(KEY_NewVal[1] ^ KEY_OldVal[1]) {
  134.                 // 下降沿控制LED2
  135.                 if(KEY_OldVal[1]/* && (~KEY_NewVal[1])*/)
  136.                     LED_Val[1] = LED_Val[1] ? 0 : 1;
  137.                 // 上升沿采样LED3
  138.                 if(/*(~KEY_OldVal[1]) && */KEY_NewVal[1])
  139.                     LED_Val[2] = LED_Val[2] ? 0 : 1;
  140.                 // 后锁存旧值
  141.                 KEY_OldVal[1] = KEY_NewVal[1];                              
  142.             }


  143.             /* 响应双边沿控制 */
  144.             // 按下亮松开灭
  145.             if(!LED_Val[0]) Open_LED1;
  146.             else Close_LED1;
  147.             
  148.             /* 响应下降沿控制 */
  149.             // 第一次按下即亮,第二次按下灭
  150.             if(!LED_Val[1]) Open_LED2;
  151.             else Close_LED2;  
  152.             
  153.             /* 响应上升沿控制 */
  154.             // 第一次松开即亮,第二次松开即灭
  155.             if(!LED_Val[2]) Open_LED3;
  156.             else Close_LED3;           
  157.                    }

  158.                 if(0) break; // 跳出大循环
  159.         }
  160.         return 0;
  161. }




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| tear086 发表于 2011-5-5 07:59 | 显示全部楼层
下面给出Multisim之硬件仿真图示范


双边沿检测


上升沿检测


下降沿检测

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
hotpower 发表于 2011-5-5 09:39 | 显示全部楼层
旧值新值感觉不如计数器简洁。
例如只测试压键事件:
计数器初始化为0,定时到,若压键,计数加一,否则直接减为零。
加到2认为有键压下。

加减注意要先判定越界处理。
如大于等于2不加。

若再判断长压键或键释放要稍复杂些。
 楼主| tear086 发表于 2011-5-5 10:58 | 显示全部楼层
哈哈,俺看到边沿检测,就想起了D触发器。
ytfdhb 发表于 2011-8-24 12:56 | 显示全部楼层
学习了^_^
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:個人博客:yuphone.cnblogs.com 聯絡方式:張亞峰 15013013265@qq.com

0

主题

120

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部
个人签名:個人博客:yuphone.cnblogs.com 聯絡方式:張亞峰 15013013265@qq.com

0

主题

120

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部