找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3206|回复: 1
打印 上一主题 下一主题
收起左侧

单片机计算器出现问题

[复制链接]
跳转到指定楼层
楼主
请各位大神看一下我的仿真电路应该没问题吧  仿真的这个计算器按键不起作用 请各位大神帮我分析一下原因
  1. #include <reg51.h>

  2. #define LCD1602_DB   P0

  3. sbit LCD1602_RS = P1^0;
  4. sbit LCD1602_RW = P1^1;
  5. sbit LCD1602_E  = P1^5;

  6. void LcdWaitReady()  //等待液晶准备好
  7. {
  8.     unsigned char sta;

  9.     LCD1602_DB = 0xFF;
  10.     LCD1602_RS = 0;
  11.     LCD1602_RW = 1;
  12.     do
  13.     {
  14.         LCD1602_E = 1;
  15.         sta = LCD1602_DB; //读取状态字
  16.         LCD1602_E = 0;
  17.     } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
  18. }
  19. void LcdWriteCmd(unsigned char cmd)  //写入命令函数
  20. {
  21.     LcdWaitReady();
  22.     LCD1602_RS = 0;
  23. LCD1602_RW = 0;
  24. LCD1602_DB = cmd;
  25.     LCD1602_E  = 1;
  26.     LCD1602_E  = 0;
  27. }
  28. void LcdWriteDat(unsigned char dat)  //写入数据函数
  29. {
  30.     LcdWaitReady();
  31.     LCD1602_RS = 1;
  32.     LCD1602_RW = 0;
  33.     LCD1602_DB = dat;
  34.     LCD1602_E  = 1;
  35.     LCD1602_E  = 0;
  36. }
  37. void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str)  //显示字符串,屏幕起始坐标(x,y),字符串指针str
  38. {
  39.     unsigned char addr;

  40.     //由输入的显示坐标计算显示RAM的地址
  41.     if (y == 0)
  42.         addr = 0x00 + x; //第一行字符地址从0x00起始
  43.     else
  44.         addr = 0x40 + x; //第二行字符地址从0x40起始

  45.     //由起始显示RAM地址连续写入字符串
  46.     LcdWriteCmd(addr | 0x80); //写入起始地址
  47.     while (*str != '\0')      //连续写入字符串数据,直到检测到结束符
  48.     {
  49.         LcdWriteDat(*str);
  50.         str++;
  51.     }
  52. }
  53. void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len)  //区域清除,清除从(x,y)坐标起始的len个字符位
  54. {
  55.     unsigned char addr;

  56.     //由输入的显示坐标计算显示RAM的地址
  57.     if (y == 0)
  58.         addr = 0x00 + x; //第一行字符地址从0x00起始
  59.     else
  60.         addr = 0x40 + x; //第二行字符地址从0x40起始

  61.     //由起始显示RAM地址连续写入字符串
  62.     LcdWriteCmd(addr | 0x80); //写入起始地址
  63.     while (len--)             //连续写入空格
  64.     {
  65.         LcdWriteDat(' ');
  66.     }
  67. }
  68. void LcdFullClear()
  69. {
  70.     LcdWriteCmd(0x01);  //清屏
  71. }
  72. void LcdInit()  //液晶初始化函数
  73. {
  74.     LcdWriteCmd(0x38);  //16*2显示,5*7点阵,8位数据接口
  75.     LcdWriteCmd(0x0C);  //显示器开,光标关闭
  76.     LcdWriteCmd(0x06);  //文字不动,地址自动+1
  77.     LcdWriteCmd(0x01);  //清屏
  78.     LcdShowStr(15, 1, "0");
  79. }

  80. /***********************keyboard.c文件程序源代码************************/

  81. #include <reg51.h>

  82. sbit KEY_IN_1  = P2^4;  //矩阵按键的扫描输入引脚1
  83. sbit KEY_IN_2  = P2^5;  //矩阵按键的扫描输入引脚2
  84. sbit KEY_IN_3  = P2^6;  //矩阵按键的扫描输入引脚3
  85. sbit KEY_IN_4  = P2^7;  //矩阵按键的扫描输入引脚4
  86. sbit KEY_OUT_1 = P2^3;  //矩阵按键的扫描输出引脚1
  87. sbit KEY_OUT_2 = P2^2;  //矩阵按键的扫描输出引脚2
  88. sbit KEY_OUT_3 = P2^1;  //矩阵按键的扫描输出引脚3
  89. sbit KEY_OUT_4 = P2^0;  //矩阵按键的扫描输出引脚4

  90. const unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到PC标准键盘键码的映射表
  91.     { '1',  '2',  '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
  92.     { '4',  '5',  '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
  93.     { '7',  '8',  '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
  94.     { '0', 0x1B, 0x0D, 0x27 }  //数字键0、ESC键、  回车键、 向右键
  95. };
  96. unsigned char pdata KeySta[4][4] = {  //全部矩阵按键的当前状态
  97.     {1, 1, 1, 1},
  98.     {1, 1, 1, 1},
  99.     {1, 1, 1, 1},
  100.     {1, 1, 1, 1}
  101. };
  102. unsigned char step = 0;  //操作步骤
  103. unsigned char oprt = 0;  //运算类型
  104. signed long num1 = 0;    //操作数1
  105. signed long num2 = 0;    //操作数2
  106. signed long result = 0;  //运算结果

  107. extern void LcdFullClear();
  108. extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
  109. extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);

  110. unsigned char NumToString(unsigned char *str, signed long num) //整型数转换为字符串,字符串指针str,待转换数num,返回值为字符串长度
  111. {
  112.     unsigned char i, len;
  113.     unsigned char buf[12];

  114.     if (num < 0)  //如果为负数,则首先输出符号到指针上,并取其绝对值
  115.     {
  116.         *str = '-';
  117.         str++;
  118.         num = -num;
  119.     }
  120.     i = 0;        //先转换为低位在前的十进制数组
  121.     do {
  122.         buf[ i] = num % 10;
  123.         num /= 10;
  124.         i++;
  125.     } while (num > 0);
  126.     len = i;       //i最后的值就是有效字符的个数
  127.     while (i > 0)  //然后将数组值转换为ASCII码反向拷贝到接收指针上
  128.     {
  129.         i--;
  130.         *str = buf[ i] + '0';
  131.         str++;
  132.     }

  133.     return len;   //返回转换后的字符串长度
  134. }
  135. void ShowOprt(unsigned char y, unsigned char type) //显示运算符,显示位置y,运算符类型type
  136. {
  137.     switch (type)
  138.     {
  139.         case 0: LcdShowStr(0, y, "+"); break;
  140.         case 1: LcdShowStr(0, y, "-"); break;
  141.         case 2: LcdShowStr(0, y, "*"); break;
  142.         case 3: LcdShowStr(0, y, "/"); break;
  143.         default: break;
  144.     }
  145. }
  146. void Reset()  //计算器复位函数
  147. {
  148.     num1 = 0;
  149.     num2 = 0;
  150.     step = 0;
  151.     LcdFullClear();
  152. }
  153. void NumKeyAction(unsigned char n) //数字键动作函数,按键输入的数值n
  154. {
  155.     unsigned char len;
  156.     unsigned char str[12];

  157.     if (step > 1)  //如计算已完成,则重新开始新的计算
  158.     {
  159.         Reset();
  160.     }
  161.     if (step == 0)  //输入第一操作数
  162.     {
  163.         num1 = num1*10 + n;           //输入数值累加到原操作数上
  164.         len = NumToString(str, num1); //新数值转换为字符串
  165.         LcdShowStr(16-len, 1, str);   //显示到液晶第二行上
  166.     }
  167.     else            //输入第二操作数
  168.     {
  169.         num2 = num2*10 + n;
  170.         len = NumToString(str, num2);
  171.         LcdShowStr(16-len, 1, str);
  172.     }
  173. }
  174. void OprtKeyAction(unsigned char type) //运算符按键动作函数,运算符类型type
  175. {
  176.     unsigned char len;
  177.     unsigned char str[12];

  178.     if (step == 0)  //第二操作数尚未输入时响应,即不支持连续操作
  179.     {
  180.         len = NumToString(str, num1); //第一操作数转换为字符串
  181.         LcdAreaClear(0, 0, 16-len);   //清除第一行左边的字符位
  182.         LcdShowStr(16-len, 0, str);   //字符串靠右显示在第一行
  183.         ShowOprt(1, type);            //在第二行显示操作符
  184.         LcdAreaClear(1, 1, 14);       //清除第二行中间的字符位
  185.         LcdShowStr(15, 1, "0");       //在第二行最右端显示0
  186.         oprt = type;                  //记录操作类型
  187.         step = 1;
  188.     }
  189. }
  190. void GetResult() //计算结果
  191. {
  192.     unsigned char len;
  193.     unsigned char str[12];

  194.     if (step == 1) //第二操作数已输入时才执行计算
  195.     {
  196.         step = 2;
  197.         switch (oprt)  //根据运算符类型计算结果,未考虑溢出问题
  198.         {
  199.             case 0: result = num1 + num2; break;
  200.             case 1: result = num1 - num2; break;
  201.             case 2: result = num1 * num2; break;
  202.             case 3: result = num1 / num2; break;
  203.             default: break;
  204.         }
  205.         len = NumToString(str, num2);   //原第二操作数和运算符显示在第一行
  206.         ShowOprt(0, oprt);
  207.         LcdAreaClear(1, 0, 16-1-len);
  208.         LcdShowStr(16-len, 0, str);
  209.         len = NumToString(str, result); //计算结果和等号显示在第二行
  210.         LcdShowStr(0, 1, "=");
  211.         LcdAreaClear(1, 1, 16-1-len);
  212.         LcdShowStr(16-len, 1, str);
  213.     }
  214. }

  215. void KeyAction(unsigned char keycode)  //按键动作函数,根据键码执行相应动作
  216. {
  217.     if  ((keycode>='0') && (keycode<='9'))  //显示输入的字符
  218.     {
  219.         NumKeyAction(keycode - '0');
  220.     }
  221.     else if (keycode == 0x26)  //向上键,+
  222.     {
  223.         OprtKeyAction(0);
  224.     }
  225.     else if (keycode == 0x28)  //向下键,-
  226.     {
  227.         OprtKeyAction(1);
  228.     }
  229.     else if (keycode == 0x25)  //向左键,*
  230.     {
  231.         OprtKeyAction(2);
  232.     }
  233.     else if (keycode == 0x27)  //向右键,÷
  234.     {
  235.         OprtKeyAction(3);
  236.     }
  237.     else if (keycode == 0x0D)  //回车键,计算结果
  238.     {
  239.         GetResult();
  240.     }
  241.     else if (keycode == 0x1B)  //Esc键,清除
  242.     {
  243.         Reset();
  244.         LcdShowStr(15, 1, "0");
  245.     }
  246. }
  247. void KeyDrive()  //按键动作驱动函数
  248. {
  249.     unsigned char i, j;
  250.     static unsigned char pdata backup[4][4] = {  //按键值备份,保存前一次的值
  251.         {1, 1, 1, 1},
  252.         {1, 1, 1, 1},
  253.         {1, 1, 1, 1},
  254.         {1, 1, 1, 1}
  255.     };

  256.     for (i=0; i<4; i++)  //循环扫描4*4的矩阵按键
  257.     {
  258.         for (j=0; j<4; j++)
  259.         {
  260.             if (backup[ i][j] != KeySta[ i][j])  //检测按键动作
  261.             {
  262.                 if (backup[ i][j] != 0)  //按键按下时执行动作
  263.                 {
  264.                     KeyAction(KeyCodeMap[ i][j]);  //调用按键动作函数
  265.                 }
  266.                 backup[ i][j] = KeySta[ i][j];
  267.             }
  268.         }
  269.     }
  270. }
  271. void KeyScan()  //按键扫描函数
  272. {
  273.     unsigned char i;
  274.     static unsigned char keyout = 0;  //矩阵按键扫描输出计数器
  275.     static unsigned char keybuf[4][4] = {  //按键扫描缓冲区,保存一段时间内的扫描值
  276.         {0xFF, 0xFF, 0xFF, 0xFF},
  277.         {0xFF, 0xFF, 0xFF, 0xFF},
  278.         {0xFF, 0xFF, 0xFF, 0xFF},
  279.         {0xFF, 0xFF, 0xFF, 0xFF}
  280.     };

  281.     //将一行的4个按键值移入缓冲区
  282.     keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
  283.     keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
  284.     keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
  285.     keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;

  286.     //消抖后更新按键状态
  287.     for (i=0; i<4; i++)  //每行4个按键,所以循环4次
  288.     {
  289.         if ((keybuf[keyout][ i] & 0x0F) == 0x00)
  290.         {   //连续4次扫描值为0,即16ms(4*4ms)内都只检测到按下状态时,可认为按键已按下
  291.             KeySta[keyout][ i] = 0;
  292.         }
  293.         else if ((keybuf[keyout][ i] & 0x0F) == 0x0F)
  294.         {   //连续4次扫描值为1,即16ms(4*4ms)内都只检测到弹起状态时,可认为按键已弹起
  295.             KeySta[keyout][ i] = 1;
  296.         }
  297.     }

  298.     //执行下一次的扫描输出
  299.     keyout++;
  300.     keyout &= 0x03;
  301.     switch (keyout)
  302.     {
  303.         case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
  304.         case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
  305.         case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
  306.         case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
  307.         default: break;
  308.     }
  309. }

  310. /*************************main.c文件程序源代码**************************/

  311. #include <reg51.h>

  312. void ConfigTimer0(unsigned int ms);
  313. extern void KeyScan();
  314. extern void KeyDrive();
  315. extern void LcdInit();

  316. unsigned char T0RH = 0;  //T0重载值的高字节
  317. unsigned char T0RL = 0;  //T0重载值的低字节

  318. void main(void)
  319. {
  320.     EA = 1;          //开总中断
  321.     ConfigTimer0(1); //配置T0定时1ms
  322.     LcdInit();       //初始化液晶

  323.     while(1)
  324.     {
  325.         KeyDrive();
  326.     }
  327. }

  328. void ConfigTimer0(unsigned int ms)  //T0配置函数
  329. {
  330.     unsigned long tmp;

  331.     tmp = 11059200 / 12;      //定时器计数频率
  332.     tmp = (tmp * ms) / 1000;  //计算所需的计数值
  333.     tmp = 65536 - tmp;        //计算定时器重载值
  334.     tmp = tmp + 18;           //修正中断响应延时造成的误差

  335.     T0RH = (unsigned char)(tmp >> 8);  //定时器重载值拆分为高低字节
  336.     T0RL = (unsigned char)tmp;
  337.     TMOD &= 0xF0;   //清零T0的控制位
  338.     TMOD |= 0x01;   //配置T0为模式1
  339.     TH0 = T0RH;     //加载T0重载值
  340.     TL0 = T0RL;
  341.     ET0 = 1;        //使能T0中断
  342.     TR0 = 1;        //启动T0
  343. }

  344. void InterruptTimer0() interrupt 1  //T0中断服务函数
  345. {
  346.     TH0 = T0RH;  //定时器重新加载重载值
  347.     TL0 = T0RL;
  348.     KeyScan();   //按键扫描
  349. }
复制代码


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:82765 发表于 2017-8-16 15:25 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表