请各位大神看一下我的仿真电路应该没问题吧 仿真的这个计算器按键不起作用 请各位大神帮我分析一下原因
- #include <reg51.h>
- #define LCD1602_DB P0
- sbit LCD1602_RS = P1^0;
- sbit LCD1602_RW = P1^1;
- sbit LCD1602_E = P1^5;
- void LcdWaitReady() //等待液晶准备好
- {
- unsigned char sta;
- LCD1602_DB = 0xFF;
- LCD1602_RS = 0;
- LCD1602_RW = 1;
- do
- {
- LCD1602_E = 1;
- sta = LCD1602_DB; //读取状态字
- LCD1602_E = 0;
- } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
- }
- void LcdWriteCmd(unsigned char cmd) //写入命令函数
- {
- LcdWaitReady();
- LCD1602_RS = 0;
- LCD1602_RW = 0;
- LCD1602_DB = cmd;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- void LcdWriteDat(unsigned char dat) //写入数据函数
- {
- LcdWaitReady();
- LCD1602_RS = 1;
- LCD1602_RW = 0;
- LCD1602_DB = dat;
- LCD1602_E = 1;
- LCD1602_E = 0;
- }
- void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str) //显示字符串,屏幕起始坐标(x,y),字符串指针str
- {
- unsigned char addr;
- //由输入的显示坐标计算显示RAM的地址
- if (y == 0)
- addr = 0x00 + x; //第一行字符地址从0x00起始
- else
- addr = 0x40 + x; //第二行字符地址从0x40起始
- //由起始显示RAM地址连续写入字符串
- LcdWriteCmd(addr | 0x80); //写入起始地址
- while (*str != '\0') //连续写入字符串数据,直到检测到结束符
- {
- LcdWriteDat(*str);
- str++;
- }
- }
- void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len) //区域清除,清除从(x,y)坐标起始的len个字符位
- {
- unsigned char addr;
- //由输入的显示坐标计算显示RAM的地址
- if (y == 0)
- addr = 0x00 + x; //第一行字符地址从0x00起始
- else
- addr = 0x40 + x; //第二行字符地址从0x40起始
- //由起始显示RAM地址连续写入字符串
- LcdWriteCmd(addr | 0x80); //写入起始地址
- while (len--) //连续写入空格
- {
- LcdWriteDat(' ');
- }
- }
- void LcdFullClear()
- {
- LcdWriteCmd(0x01); //清屏
- }
- void LcdInit() //液晶初始化函数
- {
- LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
- LcdWriteCmd(0x0C); //显示器开,光标关闭
- LcdWriteCmd(0x06); //文字不动,地址自动+1
- LcdWriteCmd(0x01); //清屏
- LcdShowStr(15, 1, "0");
- }
- /***********************keyboard.c文件程序源代码************************/
- #include <reg51.h>
- sbit KEY_IN_1 = P2^4; //矩阵按键的扫描输入引脚1
- sbit KEY_IN_2 = P2^5; //矩阵按键的扫描输入引脚2
- sbit KEY_IN_3 = P2^6; //矩阵按键的扫描输入引脚3
- sbit KEY_IN_4 = P2^7; //矩阵按键的扫描输入引脚4
- sbit KEY_OUT_1 = P2^3; //矩阵按键的扫描输出引脚1
- sbit KEY_OUT_2 = P2^2; //矩阵按键的扫描输出引脚2
- sbit KEY_OUT_3 = P2^1; //矩阵按键的扫描输出引脚3
- sbit KEY_OUT_4 = P2^0; //矩阵按键的扫描输出引脚4
- const unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到PC标准键盘键码的映射表
- { '1', '2', '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
- { '4', '5', '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
- { '7', '8', '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
- { '0', 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键
- };
- unsigned char pdata KeySta[4][4] = { //全部矩阵按键的当前状态
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1}
- };
- unsigned char step = 0; //操作步骤
- unsigned char oprt = 0; //运算类型
- signed long num1 = 0; //操作数1
- signed long num2 = 0; //操作数2
- signed long result = 0; //运算结果
- extern void LcdFullClear();
- extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
- extern void LcdAreaClear(unsigned char x, unsigned char y, unsigned char len);
- unsigned char NumToString(unsigned char *str, signed long num) //整型数转换为字符串,字符串指针str,待转换数num,返回值为字符串长度
- {
- unsigned char i, len;
- unsigned char buf[12];
- if (num < 0) //如果为负数,则首先输出符号到指针上,并取其绝对值
- {
- *str = '-';
- str++;
- num = -num;
- }
- i = 0; //先转换为低位在前的十进制数组
- do {
- buf[ i] = num % 10;
- num /= 10;
- i++;
- } while (num > 0);
- len = i; //i最后的值就是有效字符的个数
- while (i > 0) //然后将数组值转换为ASCII码反向拷贝到接收指针上
- {
- i--;
- *str = buf[ i] + '0';
- str++;
- }
- return len; //返回转换后的字符串长度
- }
- void ShowOprt(unsigned char y, unsigned char type) //显示运算符,显示位置y,运算符类型type
- {
- switch (type)
- {
- case 0: LcdShowStr(0, y, "+"); break;
- case 1: LcdShowStr(0, y, "-"); break;
- case 2: LcdShowStr(0, y, "*"); break;
- case 3: LcdShowStr(0, y, "/"); break;
- default: break;
- }
- }
- void Reset() //计算器复位函数
- {
- num1 = 0;
- num2 = 0;
- step = 0;
- LcdFullClear();
- }
- void NumKeyAction(unsigned char n) //数字键动作函数,按键输入的数值n
- {
- unsigned char len;
- unsigned char str[12];
- if (step > 1) //如计算已完成,则重新开始新的计算
- {
- Reset();
- }
- if (step == 0) //输入第一操作数
- {
- num1 = num1*10 + n; //输入数值累加到原操作数上
- len = NumToString(str, num1); //新数值转换为字符串
- LcdShowStr(16-len, 1, str); //显示到液晶第二行上
- }
- else //输入第二操作数
- {
- num2 = num2*10 + n;
- len = NumToString(str, num2);
- LcdShowStr(16-len, 1, str);
- }
- }
- void OprtKeyAction(unsigned char type) //运算符按键动作函数,运算符类型type
- {
- unsigned char len;
- unsigned char str[12];
- if (step == 0) //第二操作数尚未输入时响应,即不支持连续操作
- {
- len = NumToString(str, num1); //第一操作数转换为字符串
- LcdAreaClear(0, 0, 16-len); //清除第一行左边的字符位
- LcdShowStr(16-len, 0, str); //字符串靠右显示在第一行
- ShowOprt(1, type); //在第二行显示操作符
- LcdAreaClear(1, 1, 14); //清除第二行中间的字符位
- LcdShowStr(15, 1, "0"); //在第二行最右端显示0
- oprt = type; //记录操作类型
- step = 1;
- }
- }
- void GetResult() //计算结果
- {
- unsigned char len;
- unsigned char str[12];
- if (step == 1) //第二操作数已输入时才执行计算
- {
- step = 2;
- switch (oprt) //根据运算符类型计算结果,未考虑溢出问题
- {
- case 0: result = num1 + num2; break;
- case 1: result = num1 - num2; break;
- case 2: result = num1 * num2; break;
- case 3: result = num1 / num2; break;
- default: break;
- }
- len = NumToString(str, num2); //原第二操作数和运算符显示在第一行
- ShowOprt(0, oprt);
- LcdAreaClear(1, 0, 16-1-len);
- LcdShowStr(16-len, 0, str);
- len = NumToString(str, result); //计算结果和等号显示在第二行
- LcdShowStr(0, 1, "=");
- LcdAreaClear(1, 1, 16-1-len);
- LcdShowStr(16-len, 1, str);
- }
- }
- void KeyAction(unsigned char keycode) //按键动作函数,根据键码执行相应动作
- {
- if ((keycode>='0') && (keycode<='9')) //显示输入的字符
- {
- NumKeyAction(keycode - '0');
- }
- else if (keycode == 0x26) //向上键,+
- {
- OprtKeyAction(0);
- }
- else if (keycode == 0x28) //向下键,-
- {
- OprtKeyAction(1);
- }
- else if (keycode == 0x25) //向左键,*
- {
- OprtKeyAction(2);
- }
- else if (keycode == 0x27) //向右键,÷
- {
- OprtKeyAction(3);
- }
- else if (keycode == 0x0D) //回车键,计算结果
- {
- GetResult();
- }
- else if (keycode == 0x1B) //Esc键,清除
- {
- Reset();
- LcdShowStr(15, 1, "0");
- }
- }
- void KeyDrive() //按键动作驱动函数
- {
- unsigned char i, j;
- static unsigned char pdata backup[4][4] = { //按键值备份,保存前一次的值
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1},
- {1, 1, 1, 1}
- };
- for (i=0; i<4; i++) //循环扫描4*4的矩阵按键
- {
- for (j=0; j<4; j++)
- {
- if (backup[ i][j] != KeySta[ i][j]) //检测按键动作
- {
- if (backup[ i][j] != 0) //按键按下时执行动作
- {
- KeyAction(KeyCodeMap[ i][j]); //调用按键动作函数
- }
- backup[ i][j] = KeySta[ i][j];
- }
- }
- }
- }
- void KeyScan() //按键扫描函数
- {
- unsigned char i;
- static unsigned char keyout = 0; //矩阵按键扫描输出计数器
- static unsigned char keybuf[4][4] = { //按键扫描缓冲区,保存一段时间内的扫描值
- {0xFF, 0xFF, 0xFF, 0xFF},
- {0xFF, 0xFF, 0xFF, 0xFF},
- {0xFF, 0xFF, 0xFF, 0xFF},
- {0xFF, 0xFF, 0xFF, 0xFF}
- };
- //将一行的4个按键值移入缓冲区
- keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
- keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
- keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
- keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
- //消抖后更新按键状态
- for (i=0; i<4; i++) //每行4个按键,所以循环4次
- {
- if ((keybuf[keyout][ i] & 0x0F) == 0x00)
- { //连续4次扫描值为0,即16ms(4*4ms)内都只检测到按下状态时,可认为按键已按下
- KeySta[keyout][ i] = 0;
- }
- else if ((keybuf[keyout][ i] & 0x0F) == 0x0F)
- { //连续4次扫描值为1,即16ms(4*4ms)内都只检测到弹起状态时,可认为按键已弹起
- KeySta[keyout][ i] = 1;
- }
- }
- //执行下一次的扫描输出
- keyout++;
- keyout &= 0x03;
- switch (keyout)
- {
- case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
- case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
- case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
- case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
- default: break;
- }
- }
- /*************************main.c文件程序源代码**************************/
- #include <reg51.h>
- void ConfigTimer0(unsigned int ms);
- extern void KeyScan();
- extern void KeyDrive();
- extern void LcdInit();
- unsigned char T0RH = 0; //T0重载值的高字节
- unsigned char T0RL = 0; //T0重载值的低字节
- void main(void)
- {
- EA = 1; //开总中断
- ConfigTimer0(1); //配置T0定时1ms
- LcdInit(); //初始化液晶
- while(1)
- {
- KeyDrive();
- }
- }
- void ConfigTimer0(unsigned int ms) //T0配置函数
- {
- unsigned long tmp;
- tmp = 11059200 / 12; //定时器计数频率
- tmp = (tmp * ms) / 1000; //计算所需的计数值
- tmp = 65536 - tmp; //计算定时器重载值
- tmp = tmp + 18; //修正中断响应延时造成的误差
- T0RH = (unsigned char)(tmp >> 8); //定时器重载值拆分为高低字节
- T0RL = (unsigned char)tmp;
- TMOD &= 0xF0; //清零T0的控制位
- TMOD |= 0x01; //配置T0为模式1
- TH0 = T0RH; //加载T0重载值
- TL0 = T0RL;
- ET0 = 1; //使能T0中断
- TR0 = 1; //启动T0
- }
- void InterruptTimer0() interrupt 1 //T0中断服务函数
- {
- TH0 = T0RH; //定时器重新加载重载值
- TL0 = T0RL;
- KeyScan(); //按键扫描
- }
复制代码
|