目录
首先分为根据要实现的功能来选择硬件和软件:
硬件部分
软件部分
输入部分:
计算部分:
连续计算:
源代码示例:
主函数:
键盘输入:
LCD1602显示:
蜂鸣器:
延时函数:
首先分为根据要实现的功能来选择硬件和软件:
首先我们要实现的功能有:多位显示,小数计算,连续计算,符号按错修改,,
硬件部分
用到LCD1602显示屏,矩阵键盘,蜂鸣器(可有可无哈)
这里这里不用数码管的原因是,数码管需要扫描,而且对于加减乘除这些符号不是很好显示,转而利用的是相对简单的LCD1602的子函数来显示
矩阵键盘的模块就相对比较简单一些:原理图如下:
软件部分
输入部分:
很显然要有输入两个或多个需要运算的数字。输入操作:每输入一个数字,之前的数字都要往前进行移位的操作。而对于小数部分而言,就是每输入一位数字,数字后面依次显示。(有的同学可能不习惯看输入数字时前面的0000,,可以用计数溢出变量,有几位就显示几位用多重if语句)
计数溢出变量:就是统计输入数字的位数从而保证输入的数字总长度不会超出屏幕,在达到最大长度时就限制显示函数从而达到效果
计算部分:
其次要设计加减乘除各各运算子程序,但是我们初次设计,所以计算的位数比较少,所以将变量与变量相作用的结果用第三个变量储存起来,然后在进行显示就就行了。
输入数可能为整数也可能为小数,那么以乘法为例那么计算的可能的情况有:
整数x整数
小数x整数
整数x小数
小数x小数
为了方便表示,这里我设计的是将一个小数分为整数部分和小数部分来储存;也就是说例如一个2位的小数:number=整数部分+(小数部分*0.01)如果我们只想对整数进行计算的话,那(小数部分*0.01)为0也不会影响到后面的计算
在输入完一个数的整数部分,按运算符号后下一步就是第二个数的输入。但是如果按下的是小数点号,那么对运算符号的输入就要延后,下一步就要对小数点后的数字进行输入,之后按下运算符号,才是对第二个数的输入。在按下每一个按键的过程中,我们还可以结合蜂鸣器,每按一下响一次。确保我们是确实按下了这个按键的
连续计算:
还有就是进行多个数字的计算,我们日常使用计算器的时候可能不止是对2个数字之间进行计算,这多个数字之间可能涉及到加,减,乘,除等多种运算。
一般这样我是以2个数字之间的计算为基础吧,将第一次输入的2个数字的所计算的结果储存到第一个数字中,第二个数字清零(计数溢出变量也要清除,否则计数溢出变量到达指定值以后就不能对第二个数进行输入了)。
图示如下:
文章来源:https://www.toymoban.com/news/detail-490915.html
还要将要运算的符号标记,在输入第一个数的函数中检测到这个标记就跳出直接进入第二个数的输入(因为我们已将之前运算的结果存入第一个数的变量所以只对第二个数进行输入就行了)文章来源地址https://www.toymoban.com/news/detail-490915.html
怎么一说,思路还是比较清晰的
源代码示例:
主函数:
#include <REGX52.H>
#include "LCD1602.h"
#include "delay.h"
#include "jianpan.h"
#include "Buzzer.h"
/*
number1为第一次输入的按键
number2为第二次输入的按键
number3为第三次输入的按键
number4为第四次输入的按键
number为计算结果
password1和password3构成number1,password2和password4构成number2
count1,count2,count3,count4为避免输入的数字溢出的计数变量
计算的数字可能是:
整数与整数
整数与小数
小数与整数
小数与小数
按键说明:
键盘上的S1到S9分别是1到9,然后S10是0
S11为小数点,S12在第一次个数输入的时候为清除键,在第二个数输入后为等于键
S13到S16分别为加,减,乘,除。
*/
float number,password1,password2;
unsigned long number1,number2,number3,number4,password3,password4;
unsigned char symbol,count1,count2,count3,count4;
void main(){
while(1){
LCD_Init();
LCD_ShowString(1,1,"Calculator x4.0");
LCD_ShowString(2,1,"Wellcome");
password3=0;
count2=0;
while(1){
if(symbol==13||symbol==14||symbol==15||symbol==16)
{break;}
number1=jianpan();
if(number1){
Buzzer_Time(100);
if(number1==12){
LCD_ShowString(2,1," ");
count1=0;
password1=0;
LCD_ShowNum(2,1,password1,8);
}
if(number1<=10){
if(count1<8){//最多输入8位,避免溢出
password1=10*password1;//这里两行是关键,将输入的单个数字依次移位
password1=number1%10+password1;
if(count1>=0)
LCD_ShowNum(2,8,password1,1);
LCD_ShowString(2,1," ");
if(count1>=1)
LCD_ShowNum(2,7,(unsigned long)(password1/10)%10,1);
LCD_ShowString(2,1," ");
if(count1>=2)
LCD_ShowNum(2,6,(unsigned long)(password1/100)%10,1);
LCD_ShowString(2,1," ");
if(count1>=3)
LCD_ShowNum(2,5,(unsigned long)(password1/1000)%10,1);
LCD_ShowString(2,1," ");
if(count1>=4)
LCD_ShowNum(2,4,(unsigned long)(password1/10000)%10,1);
LCD_ShowString(2,1," ");
if(count1>=5)
LCD_ShowNum(2,3,(unsigned long)(password1/100000)%10,1);
LCD_ShowString(2,1," ");
if(count1>=6)
LCD_ShowNum(2,2,(unsigned long)(password1/1000000)%10,1);
LCD_ShowString(2,1," ");
if(count1>=7)
LCD_ShowNum(2,1,(unsigned long)(password1/10000000)%10,1);
}
count1++;
}
if(number1==11)//不用switch,case可以提高容错率,多次改变符号的选择
break;
if(number1==13){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'+');
symbol=number1;
break;
}
if(number1==14){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'-');
symbol=number1;
break;
}
if(number1==15){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'*');
symbol=number1;
break;
}
if(number1==16){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'/');
symbol=number1;
break;
}
}
}
while(1){
if(symbol==13){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'+');
break;
}
if(symbol==14){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'-');
break;
}
if(symbol==15){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'*');
break;
}
if(symbol==16){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'/');
break;
}
number2=jianpan();
if(number1==11){//如果上次输入的是小数点,那么进入此循环,否则不会进入(第一个数为整数时)
LCD_ShowChar(2,9,'.');
if(number2){
Buzzer_Time(100);
if(number2<=10){
if(count2<3){//最多输入3位,避免溢出
password3=10*password3;//这里两行是关键,将输入的单个数字依次移位
password3=number2%10+password3;
if(count2>=0)
{
LCD_ShowNum(2,10,password3,1);
}
if(count2>=1)
{
LCD_ShowNum(2,10,(password3/10)%10,1);
LCD_ShowNum(2,11,password3,1);
}
if(count2>=2)
{
LCD_ShowNum(2,12,password3,1);
LCD_ShowNum(2,11,(password3/10)%10,1);
LCD_ShowNum(2,10,(password3/100)%10,1);
}
// LCD_ShowNum(2,10,password3,1);
// LCD_ShowNum(2,11,(password3/10)%10,1);
// LCD_ShowNum(2,12,(password3/100)%10,1);
}
count2++;
}
if(number2==13){//符号显示
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'+');
symbol=number2;
break;
}
if(number2==14){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'-');
symbol=number2;
break;
}
if(number2==15){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'*');
symbol=number2;
break;
}
if(number2==16){
LCD_ShowString(2,1," ");
LCD_ShowChar(2,1,'/');
symbol=number2;
break;
}
}
}
else
break;
}
password2=0;//(这里要注意一点是在进行连续运算的时候,第二次输入的数字要清零,不然的话到到后来第三次输入的数字是在第二次输入的基础上输入的)
password4=0;
count3=0;//(这里要注意一点是在进行连续运算的时候,计数变量要清零,不然的话到到后来可能输入不了数字**)
count4=0;
while(1){
number3=jianpan();
if(number3){
Buzzer_Time(100);
if(number3<=10){
if(count3<8){//最多输入8位,避免溢出
password2=10*password2;//这里两行是关键,将输入的单个数字依次移位
password2=number3%10+password2;
if(count3>=0)
LCD_ShowNum(2,8,password2,1);
LCD_ShowString(2,1," ");
if(count3>=1)
LCD_ShowNum(2,7,(unsigned long)(password2/10)%10,1);
LCD_ShowString(2,1," ");
if(count3>=2)
LCD_ShowNum(2,6,(unsigned long)(password2/100)%10,1);
LCD_ShowString(2,1," ");
if(count3>=3)
LCD_ShowNum(2,5,(unsigned long)(password2/1000)%10,1);
LCD_ShowString(2,1," ");
if(count3>=4)
LCD_ShowNum(2,4,(unsigned long)(password2/10000)%10,1);
LCD_ShowString(2,1," ");
if(count3>=5)
LCD_ShowNum(2,3,(unsigned long)(password2/100000)%10,1);
LCD_ShowString(2,1," ");
if(count3>=6)
LCD_ShowNum(2,2,(unsigned long)(password2/1000000)%10,1);
LCD_ShowString(2,1," ");
if(count3>=7)
LCD_ShowNum(2,1,(unsigned long)(password2/10000000)%10,1);
}
count3++;
}
if(number3==11){break;}
if(number3==12){
if(count2==1) password3=password3*100;//利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
if(count2==2) password3=password3*10;
switch (symbol){//整数(小数)x小数
case 13: number=(password1+password3*0.001)+(password2+password4*0.001); break;
case 14: number=(password1+password3*0.001)-(password2+password4*0.001); break;
case 15: number=(password1+password3*0.001)*(password2+password4*0.001); break;
case 16: number=(password1+password3*0.001)/(password2+password4*0.001); break;
}
LCD_ShowString(2,1,"result:");
LCD_ShowNum(2,8,number,1);
LCD_ShowNum(2,7,(unsigned long)(number/10)%10,1);
LCD_ShowNum(2,6,(unsigned long)(number/100)%10,1);
LCD_ShowNum(2,5,(unsigned long)(number/1000)%10,1);
LCD_ShowNum(2,4,(unsigned long)(number/10000)%10,1);
LCD_ShowNum(2,3,(unsigned long)(number/100000)%10,1);
LCD_ShowNum(2,2,(unsigned long)(number/1000000)%10,1);
LCD_ShowNum(2,1,(unsigned long)(number/10000000)%10,1);
LCD_ShowChar(2,9,'.');
LCD_ShowNum(2,10,(unsigned long)(number*1000)%1000,3);
}
if(number3==13||number3==14||number3==15||number3==16)
{
if(count2==1) password3=password3*100;//连续运算中利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
if(count2==2) password3=password3*10;
switch (symbol){//整数(小数)x小数
case 13: number=(password1+password3*0.001)+(password2+password4*0.001); break;
case 14: number=(password1+password3*0.001)-(password2+password4*0.001); break;
case 15: number=(password1+password3*0.001)*(password2+password4*0.001); break;
case 16: number=(password1+password3*0.001)/(password2+password4*0.001); break;
}
password1=number;
symbol=number3;
number=0;
break;//结束小循环,重新进行第一步的输入操作
}
}
}
while(1)
{
if(number3==13||number3==14||number3==15||number3==16)
{break;}
number4=jianpan();
if(number3==11){
LCD_ShowChar(2,9,'.');
if(number4){
Buzzer_Time(100);
if(number4<=10){
if(count4<3){//最多输入3位,避免溢出
password4=10*password4;//这里两行是关键,将输入的单个数字依次移位
password4=number4%10+password4;
if(count4>=0)
{
LCD_ShowNum(2,10,password4,1);
}
if(count4>=1)
{
LCD_ShowNum(2,10,(password4/10)%10,1);
LCD_ShowNum(2,11,password4,1);
}
if(count4>=2)
{
LCD_ShowNum(2,12,password4,1);
LCD_ShowNum(2,11,(password4/10)%10,1);
LCD_ShowNum(2,10,(password4/100)%10,1);
}
}
count4++;
}
if(number4==12){
if(count2==1) password3=password3*100;//利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
if(count2==2) password3=password3*10;
if(count4==1) password4=password4*100;
if(count4==2) password4=password4*10;
switch (symbol){//整数(小数)x小数
case 13: number=(password1+password3*0.001)+(password2+password4*0.001); break;
case 14: number=(password1+password3*0.001)-(password2+password4*0.001); break;
case 15: number=(password1+password3*0.001)*(password2+password4*0.001); break;
case 16: number=(password1+password3*0.001)/(password2+password4*0.001); break;
}
LCD_ShowString(2,1,"result:");
LCD_ShowNum(2,8,number,1);
LCD_ShowNum(2,7,(unsigned long)(number/10)%10,1);
LCD_ShowNum(2,6,(unsigned long)(number/100)%10,1);
LCD_ShowNum(2,5,(unsigned long)(number/1000)%10,1);
LCD_ShowNum(2,4,(unsigned long)(number/10000)%10,1);
LCD_ShowNum(2,3,(unsigned long)(number/100000)%10,1);
LCD_ShowNum(2,2,(unsigned long)(number/1000000)%10,1);
LCD_ShowNum(2,1,(unsigned long)(number/10000000)%10,1);
LCD_ShowChar(2,9,'.');
LCD_ShowNum(2,10,(unsigned long)(number*1000)%1000,3);
}
if(number4==13||number4==14||number4==15||number4==16)
{
if(count2==1) password3=password3*100;//连续运算中利用溢出变量,实现一位,二位小数的数据和三位小数的数据统一
if(count2==2) password3=password3*10;
if(count4==1) password4=password4*100;
if(count4==2) password4=password4*10;
switch (symbol){//整数(小数)x小数
case 13: number=(password1+password3*0.001)+(password2+password4*0.001); break;
case 14: number=(password1+password3*0.001)-(password2+password4*0.001); break;
case 15: number=(password1+password3*0.001)*(password2+password4*0.001); break;
case 16: number=(password1+password3*0.001)/(password2+password4*0.001); break;
}
password1=number;
symbol=number4;
number=0;
break;//结束小循环,重新进行第一步的输入操作
}
}
}
}
}
}
键盘输入:
#include <REGX52.H>
#include "delay.h"
/**
* @brief 矩阵键盘
* @param 无
* @retval 按下按键时进入if()和while()中,松开手的一瞬间,返回值生成,并返回原函数
* 无按键按下时,返回初始值0(为什么主函数要加if?就是用来检测是否按下按键,只有按下才进入循环)
*/
unsigned int jianpan(){
unsigned int number=0;
while(1){
P1=0xFF;//在每一列前面都应该有,否则会出现反复按一个数时,出现和这个数一行的别的数
P1_3=0;
if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=1;}
if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=5;}
if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=9;}
if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=13;}
P1=0xFF;
P1_2=0;
if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=2;}
if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=6;}
if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=10;}
if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=14;}
P1=0xFF;
P1_1=0;
if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=3;}
if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=7;}
if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=11;}
if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=15;}
P1=0xFF;
P1_0=0;
if(P1_7==0){delay(20);while(P1_7==0);delay(20);number=4;}
if(P1_6==0){delay(20);while(P1_6==0);delay(20);number=8;}
if(P1_5==0){delay(20);while(P1_5==0);delay(20);number=12;}
if(P1_4==0){delay(20);while(P1_4==0);delay(20);number=16;}
return number;
}
}
LCD1602显示:
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
蜂鸣器:
#include <REGX52.H>
#include <INTRINS.H>
//蜂鸣器端口:
sbit Buzzer=P2^5;
/**
* @brief 蜂鸣器私有延时函数,延时500us
* @param 无
* @retval 无
*/
void Buzzer_Delay500us() //@12.000MHz
{
unsigned char i;
_nop_();
i = 247;
while (--i);
}
/**
* @brief 蜂鸣器发声
* @param ms 发声的时长,范围:0~32767
* @retval 无
*/
void Buzzer_Time(unsigned int ms)
{
unsigned int i;
for(i=0;i<ms*2;i++)//2x500us=1ms
{
Buzzer=!Buzzer;
Buzzer_Delay500us();//发声频率1000Hz,一次循环用时0.5ms,故29行的ms要*2
}//f=1/T,一个周期为2x500us=1ms(一个波峰和一个波谷),f=1/(1x10^-3)=1000Hz
}
延时函数:
void delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms){
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
xms--;
}
}
到了这里,关于单片机实现简易计算器功能,附有解析与代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!