/******************************************************/
基于MCS-51的简易计算器
/******************************************************/
整体规划(1课时)
1、用protues进行原理图绘制
2、用keil软件进行代码编写与调试
3、Keil + protues进行仿真实验
4、处理器采用Atmel公司的AT89C51,晶振频率12MHz,源代码用C语言编写
5、此项目可以分为3个模块:LCD1602显示部分、按键扫描部分、核心计算部分
LCD1602模块(3课时)
1、阅读LCD1602手册,明白每一个引脚的用途,按照手册连接原理图
2、仔细阅读1602控制指令,编写LCD1602控制程序
指令1:清显示,指令码01H,光标复位到地址00H位置
指令2:光标复位,光标返回到地址00H
指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效
指令4:显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁
指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示5x7的点阵字符,高电平时显示5x10的点阵字符
指令7:字符发生器RAM地址设置
指令8:DDRAM地址设置
指令9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
指令10:写数据
指令11:读数据
LCD1602控制程序
点击(此处)折叠或打开
- #include reg52.h>
- #include lcd_1602.h>
- #define uchar unsigned char
-
- float power(int num, int p);
- int powerr(int num, int p);
-
- //RS 高电平选择数据 低电平选择命令/地址
- //R/W 高电平读 低电平写
-
- uchar code cmd[] = {
- 0x01, //清屏
- 0x02, //光标返回00h
- 0x06, //开显示,无光标,不闪烁
- 0x38 //8位总线,双行显示,5*7阵列
- };
-
- void delay_1ms();
- void delay_10us();
-
- int lcd_1602_busy() //读忙信号
- {
- uchar tmp;
- P0 = 0xff; //将P0口设置为输出
- LCD_1602_RW = 1;
- LCD_1602_RS = 0;
-
- LCD_1602_EN = 1;
- delay_10us();
- tmp = P0;
- LCD_1602_EN = 0;
-
- return (bit)(0x80 & tmp);
-
- }
-
- void lcd_1602_write_cmd(uchar cmd) //发送命令\地址
- {
- while(lcd_1602_busy());
-
- LCD_1602_RW = 0;
- LCD_1602_RS = 0;
- P0 = cmd;
-
- LCD_1602_EN = 1;
- // P0 = cmd;
- delay_10us();
- LCD_1602_EN = 0;
- }
-
- void lcd_1602_write_data(uchar dat) //发送数据
- {
- while(lcd_1602_busy());
-
- LCD_1602_RW = 0;
- LCD_1602_RS = 1;
- P0 = dat;
-
- LCD_1602_EN = 1;
- // P0 = dat;
- delay_10us();
- LCD_1602_EN = 0;
- }
-
- void lcd_1602_init() //lcd初始化
- {
- lcd_1602_write_cmd(0x01);
- lcd_1602_write_cmd(0x0c);
- lcd_1602_write_cmd(0x38);
- lcd_1602_write_cmd(0x01);
- delay_1ms();
- }
-
- void lcd_1602_write_char(uchar x, uchar y, uchar dat)//写字符
- {
- if(x == 0)
- {
- lcd_1602_write_cmd(0x80 + y%16);
- lcd_1602_write_data(dat);
- }
- else
- {
- lcd_1602_write_cmd(0xc0 + y%16);
- lcd_1602_write_data(dat);
- }
- }
-
- void lcd_1602_write_string(uchar x, uchar y, uchar *string)//写字符串
- {
- while(*string != '\0')
- {
- lcd_1602_write_char(x, y++, *(string++));
- }
- }
-
- void lcd_1602_write_num(uchar x, uchar y, float num)//写数字
- {
- int i;
- int nu = (int)num;
-
- char string[5];
- for(i=0; i4; i++)
- {
- string[i] = nu/powerr(10, 3-i) + '0';
- nu = nu % powerr(10, 3-i);
- if(nu 10)
- string[i+1] = nu + '0';
- }
- string[4] = '\0';
- lcd_1602_write_string(x, y, string);
- }
-
- int powerr(int num, int p) //求num的p次方
- {
- int sum = 1;
-
- while(p)
- {
- sum = sum*num;
- p--;
- }
-
- return sum;
- }
按键模块
1、阅读课本第10章按键扫描方法
2、绘制按键原理图
3、编写按键扫描程序
点击(此处)折叠或打开
- /**************************************************
- 函数功能:按键扫描
- 实现4*4键盘扫描,记录下每一个按键
- *****************************************************/
- int keyscan()
- {
-
- P3=0xfe;
- temp=P3;
- temp = temp & 0xf0;
- if(temp!=0xf0)
- {
- delay(10);
- if(temp!=0xf0)
- {
- temp=P3;
- switch(temp)
- {
- case 0xee:
- key = '1';
- break;
-
- case 0xde:
- key = '2';
- break;
-
- case 0xbe:
- key = '3';
- break;
-
- case 0x7e:
- key = '+';
- break;
- }
- while(temp!=0xf0)
- {
- temp=P3;
- temp=temp&0xf0;
- beep=0;
- }
- beep=1;
- return 1;
- }
- }
- P3=0xfd;
- temp=P3;
- temp=temp&0xf0;
- if(temp!=0xf0)
- {
- delay(10);
- if(temp!=0xf0)
- {
- temp=P3;
- switch(temp)
- {
- case 0xed:
- key='4';
- break;
-
- case 0xdd:
- key='5';
- break;
-
- case 0xbd:
- key='6';
- break;
-
- case 0x7d:
- key='-';
- break;
- }
- while(temp!=0xf0)
- {
- temp=P3;
- temp=temp&0xf0;
- beep=0;
- }
- beep=1;
- return 1;
- }
- }
- P3=0xfb;
- temp=P3;
- temp=temp&0xf0;
- if(temp!=0xf0)
- {
- delay(10);
- if(temp!=0xf0)
- {
- temp=P3;
- switch(temp)
- {
- case 0xeb:
- key='7';
- break;
-
- case 0xdb:
- key='8';
- break;
-
- case 0xbb:
- key='9';
- break;
-
- case 0x7b:
- key='*';
- break;
- }
- while(temp!=0xf0)
- {
- temp=P3;
- temp=temp&0xf0;
- beep=0;
- }
- beep=1;
- return 1;
- }
- }
- P3=0xf7;
- temp=P3;
- temp=temp&0xf0;
- if(temp!=0xf0)
- {
- delay(10);
- if(temp!=0xf0)
- {
- temp=P3;
- switch(temp)
- {
- case 0xe7:
- key = '0';
- lcd_1602_write_data(key);
- break;
-
- case 0xd7:
- key='.';
- lcd_1602_write_data(key);
- break;
-
- case 0xb7:
- key='=';
- break;
-
- case 0x77:
- key='/';
- break;
- }
- while(temp!=0xf0)
- {
- temp=P3;
- temp=temp&0xf0;
- beep=0;
- }
- beep=1;
- return 1;
- }
- }
- return 0;
- }
核心计算模块
1、测试LCD1602显示程序是否正确
2、测试按键扫描程序是否正确
3、在Linux环境下调试计算程序。
首先将扫描到的按键存储在数组中,然后从数组提取操作数和操作码,调用计算子程序进行计算,最后输入计算结果。
以上过程需要先在Linux环境下模拟,因此需要少许Linux知识。
点击(此处)折叠或打开
- #include reg51.h>
- #include stdio.h>
- #include calculate.h>
-
- extern uchar key;
-
- int f = 0;
- int str_len = 0;
- int operand_num = 0;
- int operator_num = 0;
- float end = 0.0;
- int a = 0, b = 0;
-
- char xdata input[100];
- float xdata operand[10]={0};
- char xdata operator[10];
-
- void get_operand(char *string);
- void str_int(char *str, float num[]);
-
- float power(int num, int p);
- float powe(int p);
- float operat();
-
- void cmg88()//关数码管,点阵函数
- {
- DU=1;
- P0=0X00;
- DU=0;
- }
-
- #if 0
- void main()
- {
- cmg88();
- lcd_1602_init();
- delay_1ms();
- lcd_1602_write_char(0, 13, '0');
-
- while(1)
- {
- while(!keyscan());
-
- if((key >= '0' && key = '9') || key == '.') //显示扫描到的数字,不显示运算符号
- lcd_1602_write_char(0, str_len, key);
-
- input[str_len] = key;
- str_len ++;
- if((key > '9' || key '0') && key != '.') //扫描到+-/*
- {
- get_operand(input); //获取操作数
- operator[operator_num] = key; //操作码保存在数组中
- operator_num ++;
- f++;
-
- if(f>1)
- {
- operat(); //扫描到2个以上操作码,进行运算
- lcd_1602_write_num(1, 0, end); //显示得到的结果
- }
- }
- }
- }
-
- /*****************************************************************
- 函数功能:获取操作数,并将小数点过滤
- ******************************************************************/
- void get_operand(char *string)
- {
- int i;
- char str[20] = {' '};
- for(i=0; istr_len; i++)
- {
- if((string[i] > '9' || string[i] '0') && string[i] != '.')
- {
- str[i] = '\0';
- lcd_1602_write_cmd(0x01);
- break;
- }
- str[i] = string[i];
- }
- str_int(str, operand);
- str_len = 0;
- operand_num ++;
- }
- /*****************************************************************
- 函数功能:字符串转浮点数
- LCD1602只能显示字符,按键扫描到的也是字符
- 所有的字符都必须转换为数字彩能参加运算
- ******************************************************************/
- void str_int(char *str, float num[])
- {
- int i = 0, len_i = 0, len_f= 1, j = 0;
- while(1)
- {
- if(str[i] == '.')
- break;
- if(str[i] == '\0')
- break;
- i++;
- }
- len_i = i;
- while(len_i)
- {
- num[operand_num] += (str[len_i-1]-'0')*power(10, j);
- j++;
- len_i--;
- }
- j = 1;
- while(str[i] == '.')
- i++;
-
- while(str[i] != '\0')
- {
- num[operand_num] += (str[i]-'0')*powe(j);
- j++;
- i++;
- }
- }
- /*****************************************************************
- 函数功能:求一个数的n次方
- ******************************************************************/
- float power(int num, int p)
- {
- float sum = 1;
-
- while(p)
- {
- sum = sum*num;
- p--;
- }
-
- return sum;
- }
- /*****************************************************************
- 函数功能:求10 的-n次方
- ******************************************************************/
- float powe(int p)
- {
- float sum = 1;
- while(p)
- {
- sum = sum/10;
- p--;
- }
-
- return sum;
- }
-
- /*****************************************************************
- 函数功能:计算过程
- ******************************************************************/
- float operat()
- {
- if(b == 0)
- {
- switch (operator[a])
- {
- case '+':
- end = operand[b] + operand[b+1];
- break;
- case '-':
- end = operand[b] - operand[b+1];
- break;
- case '*':
- end = operand[b] * operand[b+1];
- break;
- case '/':
- end = operand[b] / operand[b+1];
- break;
- default:
- break;
- }
- b = 2;
- a++;
-
- return end;
- }
- if(b != 0 )
- {
- switch (operator[a])
- {
- case '+':
- end = end + operand[b];
- break;
- case '-':
- end = end - operand[b];
- break;
- case '*':
- end = end * operand[b];
- break;
- case '/':
- end = end / operand[b];
- break;
- default:
- break;
- }
- a++;
- b++;
-
- return end;
- }
- }
- #endif