[51单片机] 有关51单片机程序函数使用的疑问

[复制链接]
294|3
 楼主 | 2019-5-14 23:52 | 显示全部楼层 |阅读模式
  1. #include<reg52.h>

  2. sbit KEY_IN_1 = P2^4;
  3. sbit KEY_IN_2 = P2^5;
  4. sbit KEY_IN_3 = P2^6;
  5. sbit KEY_IN_4 = P2^7;
  6. sbit KEY_OUT_1 = P2^3;
  7. sbit KEY_OUT_2 = P2^2;
  8. sbit KEY_OUT_3 = P2^1;
  9. sbit KEY_OUT_4 = P2^0;
  10. signed long beats = 0;
  11. unsigned char KeySta[4][4] = {
  12.         {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
  13.         };

  14. unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表
  15.     { 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、向上键
  16.     { 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、向左键
  17.     { 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、向下键
  18.     { 0x30, 0x1B, 0x0D, 0x27 }  //数字键0、ESC键、  回车键、 向右键
  19.         };
  20. void  KeyDriver();
  21. void main()
  22. {
  23.     EA = 1;       //使能总中断
  24.     TMOD = 0x01;  //设置T0为模式1
  25.     TH0  = 0xFC;  //为T0赋初值0xFC67,定时1ms
  26.     TL0  = 0x67;
  27.     ET0  = 1;     //使能T0中断
  28.     TR0  = 1;     //启动T0
  29.        
  30.     while (1)
  31.     {
  32.         KeyDriver();   //调用按键驱动函数
  33.     }
  34. }

  35. void StartMotor(signed long angle)
  36. {
  37.         EA = 0;
  38.         beats = (angle*4076)/360;
  39.         EA = 1;
  40. }
  41. void StopMotor()
  42. {
  43.         EA = 0;
  44.         beats = 0;
  45.         EA = 1;
  46. }
  47. void KeyAction(unsigned char keycode)
  48. {
  49.         static bit dirMotor = 0;
  50.        
  51.         if((keycode >= 0x30) &&(keycode <= 0x39))
  52.         {
  53.                 if(dirMotor == 0)
  54.                 {
  55.                         StartMotor(360 * (keycode - 0x30));
  56.                 }
  57.                 else
  58.                 {
  59.                         StartMotor(-360 * (keycode - 0x30));       
  60.                 }
  61.         }
  62.        
  63.         else if(keycode == 0x26)
  64.         {
  65.                 dirMotor = 0;
  66.         }
  67.         else if(keycode == 0x28)
  68.         {
  69.                 dirMotor = 1;
  70.         }
  71.         else if(keycode == 0x25)
  72.         {
  73.                 StartMotor(90);
  74.         }
  75.         else if(keycode == 0x27)
  76.         {
  77.                 StartMotor(-90);
  78.         }


  79.         else if(keycode == 0x1B)
  80.         {
  81.                 StopMotor();        
  82.         }       
  83.          
  84. }
  85. void  KeyDriver()
  86. {
  87.         unsigned char i, j;
  88.         static        unsigned char backup [4][4] = {
  89.         {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}
  90.         };

  91.         for(i=0; i<4; i++)
  92.                 {
  93.                         for(j=0; j<4; j++)
  94.                         {
  95.                                 if(backup[i][j] != KeySta[i][j])
  96.                                 {
  97.                                         if(backup[i][j] == 0)
  98.                                         {
  99.                                                 KeyAction(KeyCodeMap[i][j]);
  100.                                         }
  101.                                         backup[i][j] = KeySta[i][j];
  102.                                 }
  103.                         }       
  104.                 }

  105.        
  106. }

  107. /* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
  108. void KeyScan()
  109. {
  110.     unsigned char i;
  111.     static unsigned char keyout = 0;   //矩阵按键扫描输出索引
  112.     static unsigned char keybuf[4][4] = {  //矩阵按键扫描缓冲区
  113.         {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF},
  114.         {0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF}
  115.     };

  116.     //将一行的4个按键值移入缓冲区
  117.     keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
  118.     keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
  119.     keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
  120.     keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
  121.     //消抖后更新按键状态
  122.     for (i=0; i<4; i++)  //每行4个按键,所以循环4次
  123.     {
  124.         if ((keybuf[keyout][i] & 0x0F) == 0x00)
  125.         {   //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下
  126.             KeySta[keyout][i] = 0;
  127.         }
  128.         else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
  129.         {   //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起
  130.             KeySta[keyout][i] = 1;
  131.         }
  132.     }
  133.     //执行下一次的扫描输出
  134.     keyout++;                //输出索引递增
  135.     keyout = keyout & 0x03;  //索引值加到4即归零
  136.     switch (keyout)          //根据索引,释放当前输出引脚,拉低下次的输出引脚
  137.     {
  138.         case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
  139.         case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
  140.         case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
  141.         case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
  142.         default: break;
  143.     }
  144. }

  145. void TurnMotor()
  146. {
  147.         unsigned char tmp;
  148.         static unsigned char index = 0;
  149.         unsigned char code BeatCode[8] = {
  150.         0x0E, 0x0C, 0x0D, 0x09, 0x0B, 0x03, 0x07, 0x06};

  151.         if(beats != 0)
  152.         {
  153.                 if(beats > 0)
  154.                 {
  155.                         index++;
  156.                         index = index & 0x07;
  157.                         beats--;
  158.                 }
  159.                 else
  160.                 {
  161.                         index--;
  162.                         index = index & 0x07;
  163.                         beats++;
  164.                 }
  165.                 tmp = P1;
  166.                 tmp = tmp & 0xF0;
  167.                 tmp = tmp | BeatCode[index];                         
  168.                 P1 = tmp;
  169.         }
  170.         else
  171.         {
  172.                 P1 = P1 | 0x0F;
  173.         }
  174. }
  175. /* T0中断服务函数,用于数码管显示扫描与按键扫描 */
  176. void InterruptTimer0() interrupt 1
  177. {
  178.         static bit div = 0;

  179.         TH0 = 0xFC;
  180.         TL0 = 0x67;

  181.         KeyScan();

  182.         div = ~ div;
  183.         if(div == 1)
  184.         {
  185.                 TurnMotor();
  186.         }


  187. }
复制代码
  1. #include

  2. sbit ADDR0 = P1^0;

  3. sbit ADDR1 = P1^1;

  4. sbit ADDR2 = P1^2;

  5. sbit ADDR3 = P1^3;

  6. sbit ENLED = P1^4;

  7. unsigned char code LedChar[] = {  //数码管显示字符转换表

  8.     0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,

  9.     0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E

  10. };

  11. unsigned char LedBuff[6] = {  //数码管显示缓冲区,初值 0xFF 确保启动时都不亮

  12.     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF

  13. };

  14. void SecondCount();

  15. void LedRefresh();

  16. void main(){

  17.     ENLED = 0;  //使能 U3,选择控制数码管

  18.     ADDR3 = 1;  //因为需要动态改变 ADDR0-2 的值,所以不需要再初始化了

  19.     TMOD = 0x01;  //设置 T0 为模式 1

  20.     TH0 = 0xFC;  //为 T0 赋初值 0xFC67,定时 1ms

  21.     TL0 = 0x67;

  22.     TR0 = 1;  //启动 T0

  23.     while (1){

  24.         if (TF0 == 1){  //判断 T0 是否溢出

  25.             TF0 = 0;  //T0 溢出后,清零中断标志

  26.             TH0 = 0xFC;  //并重新赋初值

  27.             TL0 = 0x67;

  28.             SecondCount();  //调用秒计数函数

  29.             LedRefresh();  //调用显示刷新函数

  30.         }

  31.     }

  32. }

  33. /* 秒计数函数,每秒进行一次秒数+1,并转换为数码管显示字符 */

  34. void SecondCount(){

  35.     static unsigned int cnt = 0;  //记录 T0 中断次数

  36.     static unsigned long sec = 0;  //记录经过的秒数

  37.     cnt++;  //计数值自加 1

  38.     if (cnt >= 1000){  //判断 T0 溢出是否达到 1000 次

  39.         cnt = 0;  //达到 1000 次后计数值清零

  40.         sec++;  //秒计数自加 1

  41.       

  42.         LedBuff[0] = LedChar[sec%10];

  43.         LedBuff[1] = LedChar[sec/10%10];

  44.         LedBuff[2] = LedChar[sec/100%10];

  45.         LedBuff[3] = LedChar[sec/1000%10];

  46.         LedBuff[4] = LedChar[sec/10000%10];

  47.         LedBuff[5] = LedChar[sec/100000%10];

  48.     }

  49. }

  50. /* 数码管动态扫描刷新函数 */

  51. void LedRefresh(){

  52.     static unsigned char i = 0; //动态扫描的索引

  53.     switch (i){

  54.         case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;

  55.         case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;

  56.         case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;

  57.         case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;

  58.         case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;

  59.         case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;

  60.         default: break;

  61.     }

  62. }
复制代码
这里有两个程序,一个是在main()里只调用了一个KeyDriver()函数,另一个是在main()调用SecondCount()和LedRefresh()函数,我想问一下为什么第一个程序没有像第二个程序那样子写直接在main里面调用函数,而是在函数里面调用函数。我应该怎么去理解第一个程序,难道要跟着他的函数思路跳来跳去?例如第一个是KeyDriver函数,然后到backup--->Keysta--->KeyCodeMap---->然后呢?我没找到去哪个函数了。这样子怎么办,各位是如何处理这样子的问题的?我应该怎么理解这样子的程序?如果跟着函数跳来跳去的话,代码量一多起来的话脑袋都绕晕了还怎么理解程序?

使用特权

评论回复
| 2019-5-15 21:43 | 显示全部楼层
要理解这种程序,你应该要建立一种模块化思想。
例如程序一的按键处理。
1、硬件扫描,就是读取io状态。这个跟单片机有关,不可移植。
第一个程序,把它弄到定时器中断里,每1ms扫描一次。就是这个keyscan
2、在硬件扫描的基础上建立,软按键,例如处理“按下”,“释放“,”长按“,”短按“,”双击“.这部分代码属于中间层。是可以用在别的单片机上的。
第一个程序,把这些也跟定时器中断搞在一起了,但是软件分层思想他有了。以我的软件水平,可以剥离硬件。
3、在软按键的基础上,是不是还有个按键处理。就是要执行命令。
就是这个keyaction
当然你也可以写道一起,但是在下个项目中,你可能还得写一遍。作为程序员,应该程序越写越少。每个项目都有一定的积累。
基于这个思想
按键处理分为:
1、硬件驱动。平台有关。
2、中间耦合层,强化处理。平台无关,项目无关。
3、事务处理。项目有关。
那么通过这个项目,你得到了一个中间耦合层,下次你还用到键盘处理的时候,你就有积累了。
另外,你这个程序1代码,水平还很低。相信你能超越他的。

使用特权

评论回复
| 2019-5-17 14:38 | 显示全部楼层
Keysta//由中断调用KeyScan()产生
backup//Keysta的备份。假如Keysta发生变化,0->1不处理只备份。1->0先处理,再备份。
KeyCodeMap//如何处理

使用特权

评论回复
| 2019-5-17 14:42 | 显示全部楼层
0->1不处理只备份。可以理解为按键抬起。
1->0先处理,再备份。可以理解为按键按下。

在51下,用一个字节表示一个位,太浪费了。

使用特权

评论回复
扫描二维码,随时随地手机跟帖
*滑动验证:
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

关闭

热门推荐上一条 /7 下一条

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