[程序源码] 菜农的零耗时按键例程已成绝响,还好我有备份

[复制链接]
1606|2
 楼主| xyz549040622 发表于 2017-7-22 08:56 | 显示全部楼层 |阅读模式
  1. /*---------------------------------------------------------
  2.         零耗时键盘各种事件及消抖处理模板裸奔程序详解
  3. 雁塔菜农HotPower   2006.12.22 于菜地www.HotPower.21ic.org/
  4. ---------------------------------------------------------*/
  5. #include <LPC213xdef.h>//包含213x自定义结构指针写法头文件
  6. //注意:可将LPC213XDEF.H拷贝到Keil\ARM\INC\PHILIPS目录下
  7. /*---------------------------------------------------------
  8.         时钟参数定义
  9. ---------------------------------------------------------*/
  10. #define Fosc 11059200
  11. #define Fcclk (Fosc * 5)
  12. #define Fcco (Fcclk * 5)
  13. #define Fpclk (Fcclk / 5) * 1

  14. /*---------------------------------------------------------
  15.         键盘接口定义
  16. ---------------------------------------------------------*/
  17. #define KEYPORT   P1//KEY在P1口
  18. #define KEY0      P1_16//
  19. #define KEY1      P1_17//
  20. #define KEY2      P1_18//
  21. #define KEY3      P1_19//


  22. /*---------------------------------------------------------
  23.         申请系统变量
  24. ---------------------------------------------------------*/
  25. volatile signed int KeyPressCount[4];//申请压键20mS计数器数组
  26. volatile signed int KeyDblCount[4];//申请键值计数器数组

  27. /*---------------------------------------------------------
  28.         系统函数声明
  29. ---------------------------------------------------------*/
  30. void SystemInit(void);//系统初始化
  31. void VicInit(void);//向量中断初始化
  32. void PortInit(void);//IO初始化
  33. void Timer0Init(void);//定时器初始化
  34. void KeyInit(void);//键盘初始化
  35. void VicIntSetup(void);//向量中断设置
  36. void KeyCommandExec(unsigned int, unsigned int);//执行键盘命令
  37. void IRQ_Timer0 (void) __irq;//定时器0中断


  38. /*---------------------------------------------------------
  39.         键盘事件处理函数声明
  40. ---------------------------------------------------------*/
  41. void Key00(void);//放键事件
  42. void Key01(void);
  43. void Key02(void);
  44. void Key03(void);

  45. void Key10(void);//短压键事件
  46. void Key11(void);
  47. void Key12(void);
  48. void Key13(void);

  49. void Key20(void);//双击键事件
  50. void Key21(void);
  51. void Key22(void);
  52. void Key23(void);

  53. void Key30(void);//长键事件
  54. void Key31(void);
  55. void Key32(void);
  56. void Key33(void);

  57. void Key0_1(void);//组合键事件
  58. void Key1_2(void);
  59. void Key2_3(void);
  60. void Key3_0(void);


  61. /*----------------------------------------------------------------------
  62. “零耗时键盘”介绍:

  63.     “零耗时”并非不耗时。它主要是将原本需要延时消除键盘抖动的时间转化为
  64. 对定时器的计数来替代。这样就可将节约的时间用于对其他事件的处理。
  65.     “零耗时”键盘程序的编写很简单,首先要做到:
  66. 1.用总键盘个数除消除键盘抖动的时间20mS.本例有4个键,即20mS/4=5mS.
  67.   所以,定时器0中断时间常数应该定义为5mS.
  68. 2.设置1个压键20mS计数器数组KeyPressCount[]。用于对各键盘的压键次数计数。
  69.   由于全部键盘扫描需要20mS,故KeyPressCount[]内的值为20mS的倍数。
  70. 3.设置1个键扫描位置计数器KeyCount,用于记录当前键扫描的位置。
  71.   注意键扫描函数KeyScan()每次只扫描1个键(本例即为IRQ_Timer0())。
  72. 4.设置1个键扫描键值计数器数组KeyDblCount[],用于记录键值以处理双击状态。
  73.   本例主要讲解“零耗时”键盘程序的编写,一般不主张在MCU系统下用双击键。
  74.   多建议采用长压键来替代双击键。

  75. 特别注意:
  76.   “零耗时”键盘程序属于“扫而不描”类型,即每次只扫描1个键而不管其他键
  77. 的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。
  78.    键扫描位置计数器KeyCount的值就是键盘扫描结果的键值。故也改进了经典的
  79. 键扫描函数KeyScan()需要逐次扫描的缺点
  80.   “零耗时”键盘程序只区分键释放,单击键,双击键 和长压键4种基本事件。
  81. 区分只简单地判别KeyPressCount[]的个数即可。
  82. 1.当无键压下且KeyPressCount[]减到0时,可判别为键释放事件发生。
  83. 2.当有键压下且KeyPressCount[]=2时,可认为键已经经过20mS消抖处理,
  84.   即单击键事件发生。
  85.   如果需要双击键处理,则需要附加KeyDblCount[]双击键计数器数组。
  86. 3.当有键压下且KeyPressCount[]=3*50时,即3*50*20mS=3S时,认为3S长压键事件发生。

  87.   对“零耗时键盘”的个人应用总结:
  88. 在MCU的裸奔中,“零耗时键盘”很容易构成一个基于时间片小型的操作系统。
  89. 它可以“并行地”处理多个键盘事件及任务。
  90. 它的节拍不是OS常用的10mS,而是20mS消抖时间的1/N份。
  91. 由于20mS也做为视觉暂留的时间基准,故在常用的LED+KEY系统中裸奔表现很不错。
  92. 如果每个任务都能保证在20mS/N内完成,那么后台程序可以废除,即主程序只是个
  93. 死循环。这在低功耗系统中应用很广。
  94. ----------------------------------------------------------------------*/


  95. /*----------------------------------------------------------------------
  96.        定时器0中断作为键盘扫描和键盘消抖处理及命令执行
  97. ----------------------------------------------------------------------*/
  98. void IRQ_Timer0 (void) __irq
  99. {
  100. const static unsigned int KeyTab[4] = {//定义FLASH数组
  101.   KEY0, KEY1, KEY2, KEY3
  102. };
  103. static unsigned int KeyCount = 0;
  104. unsigned int i;
  105.   KeyCount &= 0x03;//只有4个键KEY0~KEY3(注意每次只扫描1个键)
  106.   if (KEYPORT->IOPIN & (1 << KeyTab[KeyCount])) {//高电平压键无效
  107.     if (KeyPressCount[KeyCount] > 0) {
  108.           KeyPressCount[KeyCount] = -2;//键释放也需消除键盘抖动至少20mS
  109.         }
  110.         else if (KeyPressCount[KeyCount] < 0) {
  111.           KeyPressCount[KeyCount] ++;
  112.           if (KeyPressCount[KeyCount] == 0) {//键释放
  113.         KeyCommandExec(0, KeyCount);//键释放
  114.           }
  115.         }
  116.   }
  117.   else {
  118.     KeyPressCount[KeyCount] ++;//
  119.     if (KeyPressCount[KeyCount] == 2) {//单击键刚满20mS
  120.           if (KeyDblCount[KeyCount] != KeyCount) {
  121.         KeyCommandExec(1, KeyCount);//单击压键
  122.                 for (i = 0; i < 4; i ++ ) {
  123.                   if (i == KeyCount) {
  124.                     KeyDblCount[i] = KeyCount;//设置单击标志
  125.                   }
  126.                   else {
  127.                     KeyDblCount[i] = -1;//摧毁其他键单击标志
  128.                   }
  129.                 }
  130.           }
  131.           else {
  132.         KeyCommandExec(2, KeyCount);//双击压键
  133.                 for (i = 0; i < 4; i ++ ) {
  134.                   if (i == KeyCount) {
  135.                     KeyDblCount[i] = 0x80 + KeyCount;//设置双击标志
  136.                   }
  137.                   else {
  138.                     KeyDblCount[i] = -1;//摧毁其他键双击标志
  139.                   }
  140.                 }
  141.           }
  142.         }
  143.         else if (KeyPressCount[KeyCount] >= 3 * 50) {//3S长压键
  144.       KeyCommandExec(3, KeyCount);//长压键
  145.           KeyDblCount[KeyCount] = -1;//清除单击压键
  146.           KeyPressCount[KeyCount] = 3;//避开单击键以实现多次长压键事件处理
  147.         }
  148.   }
  149.   KeyCount ++;
  150.   T0->TIMER_IR = 0x01;                //清除中断标志
  151.   VIC->VectAddr = 0x00;                //通知VIC中断处理结束
  152. }

  153. /*----------------------------------------------------------------------
  154.      系统初始化函数
  155. ----------------------------------------------------------------------*/
  156. void SystemInit(void)
  157. {
  158.   VicInit();
  159.   PortInit();
  160.   KeyInit();
  161.   Timer0Init();
  162.   VicIntSetup();
  163. }

  164. /*----------------------------------------------------------------------
  165.      向量中断初始化函数
  166. ----------------------------------------------------------------------*/
  167. void VicInit(void)
  168. {
  169. volatile unsigned int start;
  170.   VIC->IntEnable  = 0;//关闭全部中断
  171.   VIC->SoftIntClr = 0xffffffff;//清除所有软中断标志
  172.   VIC->IntSelect  = 0;//全部中断为IRQ中断或默认中断
  173.   for (start = 0; start < 10000; start ++);//系统延时等待接口稳定
  174. }

  175. /*----------------------------------------------------------------------
  176.      IO初始化函数
  177. ----------------------------------------------------------------------*/
  178. void PortInit(void)
  179. {
  180.   PINSEL->PIN_SEL0 = 0x00000000;//设置管脚连接GPIO
  181.   PINSEL->PIN_SEL1 = 0x00000000;//设置管脚连接GPIO
  182.   P0->IODIR        = 0x00000000;//设置P0口为输入
  183.   P1->IODIR        = 0x00000000;//设置P1口为输入
  184. }

  185. /*----------------------------------------------------------------------
  186.      定时器初始化函数
  187. ----------------------------------------------------------------------*/
  188. void Timer0Init(void)
  189. {
  190.   T0->TIMER_TC   = 0;                        //时器设置为0
  191.   T0->TIMER_PR   = 0;                        //时钟不分频
  192.   T0->TIMER_MCR  = 0x03;                //设置T0MR0匹配后复位T0TC,并产生中断标志
  193.   T0->TIMER_MR0  = Fpclk / 200;        //5mS定时
  194.   T0->TIMER_TCR  = 0x01;                //启动定时器
  195.   T0->TIMER_IR   = 0x01;                //清除中断标志
  196. }

  197. /*----------------------------------------------------------------------
  198.      向量中断设置函数
  199. ----------------------------------------------------------------------*/
  200. void VicIntSetup(void)
  201. {
  202.         /* 设置定时器0中断IRQ */
  203.   VIC->VectCntls[0] = VICIntSel_Enable | VICIntSel_Time0;//设置定时器0中断通道分配最高优先级
  204.   VIC->VectAddrs[0] = (unsigned int)IRQ_Timer0;//设置中断服务程序地址
  205.   VIC->IntEnable |= (1 << VICIntSel_Time0);//使能定时器0中断
  206. }


  207. /*----------------------------------------------------------------------
  208.      键盘初始化函数
  209. ----------------------------------------------------------------------*/
  210. void KeyInit(void)
  211. {
  212. unsigned int i;
  213.   KEYPORT->IODIR &= ~((1 << KEY0) | (1 << KEY1) | (1 << KEY2) | (1 << KEY3));// 设置KEY控制口为输入       
  214.   for(i = 0; i < 4;i ++) {//4个键KEY0~KEY3
  215.     KeyPressCount[i] = 0;//清除压键20mS计数器数组,默认无键压下
  216.     KeyDblCount[i] = -1;//清除单击压键
  217.   }
  218. }


  219. /*----------------------------------------------------------------------
  220.      执行键盘命令函数
  221. ----------------------------------------------------------------------*/
  222. void KeyCommandExec(unsigned int CommMode, unsigned int CommTask)
  223. {
  224. typedef void (* PV)(void);//函数指针
  225. const static PV KeyCommandArray[4][4] = {//二维函数数组指针阵列表(散转命令地址表)
  226.   {Key00, Key01, Key02, Key03},
  227.   {Key10, Key11, Key12, Key13},
  228.   {Key20, Key21, Key22, Key23},
  229.   {Key30, Key31, Key32, Key33}
  230. };
  231. PV func;//声明函数指针
  232.   func = KeyCommandArray[CommMode][CommTask];//从FLASH中取出键盘放事件处理表
  233.   func();//运行KeyX0()~KeyX3()
  234. }


  235. int main(void)
  236. {
  237.   SystemInit();//系统初始化
  238.   while(1) {
  239.     POWER->P_CON = 1;//待机
  240.         __nop();
  241.     __nop();
  242.     __nop();
  243.   }
  244. }



  245. void Key00(void)//键释放事件
  246. {
  247.   if (KeyDblCount[0] == 0x80) {
  248. //在此添加双击键释放事件处理
  249.         __nop();
  250.   }
  251.   else {
  252. //在此添加单击键释放事件处理
  253.         __nop();
  254.   }
  255. }
  256. void Key01(void)
  257. {
  258. //在此添加释放事件处理(不管单双击键)
  259.   __nop();
  260. }
  261. void Key02(void)
  262. {
  263. }
  264. void Key03(void)
  265. {
  266. }

  267. void Key10(void)//单击键事件
  268. {
  269.   if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
  270.     Key0_1();
  271.   }
  272.   else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件
  273.     Key3_0();
  274.   }
  275. }
  276. void Key11(void)
  277. {
  278.   if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
  279.     Key0_1();
  280.   }
  281.   else if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件
  282.     Key1_2();
  283.   }
  284. }
  285. void Key12(void)
  286. {
  287.   if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
  288.     Key1_2();
  289.   }
  290.   else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件
  291.     Key2_3();
  292.   }
  293. }
  294. void Key13(void)
  295. {
  296.   if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件
  297.     Key2_3();
  298.   }
  299.   else if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
  300.     Key3_0();
  301.   }
  302. }

  303. void Key20(void)//双击键事件
  304. {
  305. }
  306. void Key21(void)
  307. {
  308. }
  309. void Key22(void)
  310. {
  311. }
  312. void Key23(void)
  313. {
  314. }

  315. void Key30(void)//长压键事件
  316. {
  317. }
  318. void Key31(void)
  319. {
  320. }
  321. void Key32(void)
  322. {
  323. }
  324. void Key33(void)
  325. {
  326. }

  327. /*----------------------------------------------------------------------
  328. “零耗时键盘”对组合键的处理方法:
  329. “零耗时键盘”的一个重要的方法是对键盘“扫而不描”。即每次只扫描1个键而不
  330. 管其他键的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。
  331.         所谓”组合键”即为在1个键压下后再发生其他键压下的键盘事件。最简单的是
  332. 有序组合键即有压键顺序的组合键。
  333.     例先压KEY0再压KEY1,这样我们就可只对单击键事件Key11()做出相应处理即可。
  334. void Key11(void)
  335. {
  336.   if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
  337.     Key0_1();//KEY0KEY1组合键事件
  338.   }
  339. }
  340. KeyPressCount[0]内存放的是KEY0的20mS压键次数。若>=2表示KEY0已经压下。
  341. 如果是无序组合键,则如例中示例在Key10()中也要做相应处理。
  342. void Key10(void)//单击键事件
  343. {
  344.   if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
  345.     Key0_1();//KEY0KEY1组合键事件
  346.   }
  347. }
  348. 零耗时键盘”对组合键的处理方法看上去“很烦琐”,但却带来了更大的灵活性。
  349. 而且也减轻了键扫描程序的负担。
  350. ----------------------------------------------------------------------*/

  351. void Key0_1(void)//组合键事件KEY0KEY1
  352. {
  353. }
  354. void Key1_2(void)//KEY1KEY2
  355. {
  356. }
  357. void Key2_3(void)//KEY2KEY3
  358. {
  359. }
  360. void Key3_0(void)//KEY3KEY0
  361. {
  362. }


junyee 发表于 2017-7-25 21:41 来自手机 | 显示全部楼层
还是太反锁了,跟 0 关系不大……
 楼主| xyz549040622 发表于 2017-7-25 21:52 | 显示全部楼层
junyee 发表于 2017-7-25 21:41
还是太反锁了,跟 0 关系不大……

0只是说不占用其他时间。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群: 嵌入式系统arm初学者 224636155←← +→→点击-->小 i 精品课全集,21ic公开课~~←←→→点击-->小 i 精品课全集,给你全方位的技能策划~~←←

2841

主题

19330

帖子

110

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