标题:
单片机计算器出现问题
[打印本页]
作者:
星际漫游者
时间:
2017-8-16 13:49
标题:
单片机计算器出现问题
请各位大神看一下我的仿真电路应该没问题吧 仿真的这个计算器按键不起作用 请各位大神帮我分析一下原因
E80TDANI[9QLKA(2G8EE.png
(27.88 KB, 下载次数: 61)
下载附件
2017-8-16 13:48 上传
#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(); //按键扫描
}
复制代码
作者:
cjjcjj1
时间:
2017-8-16 15:25
提示:
作者被禁止或删除 内容自动屏蔽
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1