基于单片机的计算器设计
(仿真+程序+原理图+设计报告)
功能介绍
具体功能:
1.采用LCD1602液晶显示;
2.用4*4矩阵按键输入,共16个按键;
3.能够计算四位数加减乘除,除法运算结果保留小数点后4位;
4.可以按键打开/关闭蜂鸣器+LED声光效果;
演示视频:
基于单片机的计算器设计—四位数运算,声光提示
添加图片注释,不超过 140 字(可选)
程序
/***微信公众号:木子单片机********/
/***************51单片机简易计算器设计************
按键介绍:
| 7 | 8 | 9 | + |
- - - - - - - - - - - - -
| 4 | 5 | 6 | - |
- - - - - - - - - - - - -
| 1 | 2 | 3 | * |
- - - - - - - - - - - - -
| 0 | = | C | / |
*/
//操作说明:
//按第一个数,再按'+-*/',再按第二个数,最后按'='显示出结果,然后按C清屏
//加最大9999+9999=19998
//减最大9999-0 =9999
//乘最大9999*9999=99980001
//除 1/9=0.1111 保留小数点后4位
#include <REGX52.H>
#define uint unsigned int
#define uchar unsigned char
/******************LCD1602**********************/
#define LCD_RS P2_4 //定义LCD_RS端口
#define LCD_RW P2_5 //定义LCD_RW端口
#define LCD_EN P2_6 //定义LCD_EN端口
#define LCD_DA P0 //定义LCD_DA端口(数据端)
#define KEY P3 //定义矩阵按键为P3口
#define Speak P2_3 //定义蜂鸣器端口
#define LED P2_2 //定义指示灯
#define KEY_1 P2_0 //定义按键为P1.7口(开/关 蜂鸣器、LED灯)
#define LED1 P1 //定义流水指示灯
/**************定义显示数据数组*************/
uchar code table[]= {" "};
/****************定义动画数组***************/
uchar code table1[]={" Welcome to "};
uchar code table2[]={" 51 MCU Counter "};
/*************定义流水灯显示数组************/
uchar code table3[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//从低位往高位(流水)
uchar code table4[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//从高位往低位(流水)
uchar code table5[]={0x7e,0xbd,0xdb,0xe7};//从两边往中间(流水)
uchar code table6[]={0xe7,0xdb,0xbd,0x7e};//从中间往两边(流水)
/***************定义运算数据***************/
long int data_a=0; //第一个数
long int data_b=0; //第二个数
long int data_c=0; //计算结果
uchar dispaly[10]; //显示缓冲
uchar i,j; //定义i,j
uchar flag=1; //定义开/关 蜂鸣器、LED灯标志位,初始化为1
uchar n=1; //定义n为1(默认 8位LED显示流水状态1)
uchar k=0; //定义k
uchar num=0; //定义时间基数初始值为0
uchar key=0xff; //键值初始化
uchar a=0; //第1个数 可以按1-4次(在主函数中体现)
uchar b=5; //第2个数 可以按1-4次(在主函数中体现)
uchar x=0; //定义x=0
/***************延时函数*********************/
void delay(uint time) //延时函数
{
uint i,j;
for(i=time;i>0;i--)
for(j=110;j>0;j--);
}
/***************蜂鸣器响 延时函数************/
void delay_fmq(uint time) //蜂鸣器响 延时函数
{
uint x,y;
for(x=time;x>0;x--)
for(y=5;y>0;y--);
}
/************LED灯闪烁并且蜂鸣器响***********/
void spk() //LED灯闪烁并且蜂鸣器响
{
LED=0; //LED亮
for(j=0;j<100;j++) //利用for语句延时
{
Speak=0; //蜂鸣器为低电平
delay_fmq(5); //延时
Speak=1; //蜂鸣器为高电平
delay_fmq(5); //延时
}
LED=1; //LED灭
}
/***************1602液晶写指令*****************/
void write_com(uchar com) //1602液晶写指令(参照LCD1602时序图)
{
LCD_RS=0; //写指令
LCD_RW=0; //写液晶
LCD_DA=com; //写入指令com
delay(2); //延时
LCD_EN=1; //使能1602
delay(2); //延时
LCD_EN=0; //使能1602
}
/****************1602液晶写数据***************/
void write_date(uchar date) //1602液晶写数据(参照LCD1602时序图)
{
LCD_RS=1; //写数据
LCD_RW=0; //写液晶
LCD_DA=date; //写入数据date
delay(2); //延时
LCD_EN=1; //使能1602
delay(2); //延时
LCD_EN=0; //使能1602
}
/****************指定x,y写入字符函数*********/
void W_lcd(unsigned char x,unsigned char y,unsigned char Data)
{
if(y==0) //如果y=0
{
write_com(0x80 + x); //第一行
}
else //否则y=1
{
write_com(0xc0 + x); //第二行
}
write_date(Data); //写入数据
}
/****************指定x,y写入字符串函数********/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if(y==0) //如果y=0
{
write_com(0x80 + x); //第一行
}
else //否则y=1
{
write_com(0xC0 + x); //第二行
}
while(*s)
{
write_date(*s); //写入数据
s++; //s++
}
}
/*****************初始化液晶******************/
void LCD_init() //初始化液晶
{
write_com(0x38); //8 位总线,双行显示,5X7 的点阵字符
write_com(0x0c); //开显示,无光标,光标不闪烁
write_com(0x01); //清屏
}
/*****************开机动画********************/
void cartoon() //开机动画
{
spk(); //蜂鸣器响,LED亮
write_com(0x80); //写指令 准备显示第1行第1个
for(i=0;i<16;i++) //for语句循环16次
{
write_date(table1[i]); //写数据" Welcome to "
delay(100); //延时一下(为了数字、字母一个一个显示)
}
write_com(0xc0); //写指令 准备显示第2行第1个
for(i=0;i<16;i++) //for语句循环16次
{
write_date(table2[i]); //写数据" 51 MCU Counter "
delay(100); //延时一下(为了数字、字母一个一个显示)
}
spk(); //蜂鸣器响,LED亮
delay(200);
}
/*************反转法键盘扫描******************/
short keycheckdown() //反转法键盘扫描
{
short temp1,temp2,temp,a=0xff;
KEY=0xf0; //1111 0000 //输入行值(或列值)
delay(20); //延时
temp1=KEY; //**** 0000 //读列值(或行值)
KEY=0xff;
delay(20); //延时
KEY=0x0f; //0000 1111 //输入列值(或行值)
delay(20); //延时
temp2=KEY; //0000 **** //读行值(或列值)
KEY=0xff;
temp=(temp1&0xf0)|(temp2&0xf); //将两次读入数据组合
switch(temp) //通过读入数据组合判断按键位置
{
/************第一行按键 7 8 9 + *********/
case 0xee :a=7; //按键 7
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xed :a=8; //按键8
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xeb :a=9; //按键9
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xe7 :a=0x0a; //按键 +
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
/************第二行按键 4 5 6 - *********/
case 0xde :a=4; //按键 4
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xdd :a=5; //按键 5
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xdb :a=6; //按键 6
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xd7 :a=0x0b; //按键 -
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
/************第三行按键 1 2 3 * *********/
case 0xbe :a=1; //按键 1
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xbd :a=2; //按键 2
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xbb :a=3; //按键 3
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0xb7 :a=0x0c; //按键 *
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
/************第四行按键 0 = C / *********/
case 0x7e :a=0; //按键 0
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0x7d :a=0x0e; //按键 =
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0x7b :a=0x0f; //按键 C
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
case 0x77 :a=0x0d; //按键 /
if(flag==1) //若flag为1,则蜂鸣器响,LED亮
{
spk();
}
break; //返回
default :a=0xff; //没有键按下
}
return a; //返回按键值
}
/******************显示数据a******************/
void display_a() //显示数据a
{
dispaly[3]=data_a%10000/1000; //千位
dispaly[2]=data_a%1000/100; //百位
dispaly[1]=data_a%100/10; //十位
dispaly[0]=data_a%10; //个位
write_com(0x80+0); //显示数据a
if(data_a>999)
{
write_date('0'+dispaly[3]); //显示千位
}
if(data_a>99)
{
write_date('0'+dispaly[2]); //显示百位
}
if(data_a>9)
{
write_date('0'+dispaly[1]); //显示十位
}
write_date('0'+dispaly[0]); //显示个位
}
/******************显示数据b******************/
void display_b() //显示数据b
{
write_com(0x80+7); //第一行
dispaly[3]=data_b%10000/1000; //千位
dispaly[2]=data_b%1000/100; //百位
dispaly[1]=data_b%100/10; //十位
dispaly[0]=data_b%10; //个位
if(data_b>999)
{
write_date('0'+dispaly[3]); //显示千位
}
if(data_b>99)
{
write_date('0'+dispaly[2]); //显示百位
}
if(data_b>9)
{
write_date('0'+dispaly[1]); //显示十位
}
write_date('0'+dispaly[0]); //显示个位
}
/******************计算结果*******************/
void display_c(x)
{
if(data_c<100000000&&data_c>-1) //溢出时显示错误
{
dispaly[8]=data_c%1000000000/100000000; //万万
dispaly[7]=data_c%100000000/10000000; //千万
dispaly[6]=data_c%10000000/1000000; //百万
dispaly[5]=data_c%1000000/100000; //十万
dispaly[4]=data_c%100000/10000; //万
dispaly[3]=data_c%10000/1000; //千
dispaly[2]=data_c%1000/100; //百
dispaly[1]=data_c%100/10; //十
dispaly[0]=data_c%10; //个
write_com(0x80+0x40+7); //第二行
if(x==4)
{
if(data_c>99999999)
{
write_date('0'+dispaly[8]); //显示万万
}
if(data_c>9999999)
{
write_date('0'+dispaly[7]); //显示千万
}
if(data_c>999999)
{
write_date('0'+dispaly[6]); //显示百万
}
if(data_c>99999)
{
write_date('0'+dispaly[5]); //显示十万
}
write_date('0'+dispaly[4]); //万
write_date('.');
write_date('0'+dispaly[3]); //千
write_date('0'+dispaly[2]); //百
write_date('0'+dispaly[1]); //十
write_date('0'+dispaly[0]); //个
}
else
{
if(data_c>99999999)
{
write_date('0'+dispaly[8]); //显示万万
}
if(data_c>9999999)
{
write_date('0'+dispaly[7]); //千万
}
if(data_c>999999)
{
write_date('0'+dispaly[6]); //百万
}
if(data_c>99999)
{
write_date('0'+dispaly[5]); //十万
}
if(data_c>9999)
{
write_date('0'+dispaly[4]); //万
}
if(data_c>999)
{
write_date('0'+dispaly[3]); //千
}
if(data_c>99)
{
write_date('0'+dispaly[2]); //百
}
if(data_c>9)
{
write_date('0'+dispaly[1]); //十
}
write_date('0'+dispaly[0]); //个
}
}
else //溢出时显示错误
{
write_com(0x80+0x40+11); //第二行
write_date('E'); //显示 E
write_date('r'); //显示 R
write_date('r'); //显示 R
write_date('o'); //显示 O
write_date('r'); //显示 R
}
}
/****************加减乘除运算*****************/
void eql(uchar yunsuan) //加减乘除运算
{
switch(yunsuan) //功能键选择
{
case 1: data_c=data_a+data_b; //加法
break; //返回
case 2: if(data_a>=data_b) //若a>b
{
data_c=data_a-data_b; //减法
}
else //否则a<b
{
data_c=data_b-data_a; //减法
W_lcd(5,1,'-'); //带上负号
}
break; //返回
case 3: data_c=(data_a*data_b); //乘法
break; //返回
case 4: if(data_b==0) //如果被除数为0
{
LCD_Write_String(1,1,"Error");//除法 溢出时显示错误
}
else
{
data_c=(data_a*10000)/data_b;//除法
}
break; //返回
case 0: break; //不做运算 返回
}
}
/****************定时器0初始化****************/
void Time0_init() //定时器0初始化
{
TMOD=0x01; //定时器0为方式1
TL0=(65536-50000)%256;
TH0=(65536-50000)/256; //12M晶振 定时时间50ms
ET0=1; //开定时器0中断
EA=1; //开总中断
TR0=1; //开启定时器0
}
/****************开/关 指示函数***************/
void K_G_ZS() //开/关 指示函数
{
if(KEY_1==0) //若按键按下
{
delay(20); //延时去抖
if(KEY_1==0) //再次确认按键是否按下
{
flag=!flag; //flag取反
} //flag=0关闭蜂鸣器响和LED亮。flag=1开启蜂鸣器响和LED亮
if(flag==1) //若flag为1
{
spk(); //蜂鸣器响,LED亮
TR0=1; //开启定时器0
}
else
{
TL0=(65536-50000)%256;
TH0=(65536-50000)/256;//12M晶振 定时时间50ms
TR0=0; //关闭定时器0
num=0; //时间基数清0
n++; //n加1
k=0; //k清0
if(n==6) //若n=5
{
n=1; //n置1 (n的范围是1 2 3 4 5)
}
LED1=0xff; //流水指示灯关闭
}
while(KEY_1==0); //等待按键松开
}
}
/*************定时器0中断服务函数**************/
void Time0() interrupt 1 //定时器0中断服务函数
{
TH0=(65536-50000)/256; //重新载装初值,设置50ms中断一次
TL0=(65536-50000)%256;
num++; //时间基数加1
while(num==5)
{
num=0; //num清0
if(n==1) //如果n为状态1
{
LED1=table3[k]; //8位LED 从上往下流水状态
k++;
if(k==8) {k=0;}
}
else if(n==2) //如果n为状态2
{
LED1=table4[k]; //8位LED 从下往上流水状态
k++;
if(k==8) {k=0;}
}
else if(n==3) //如果n为状态3
{
LED1=table5[k]; //8位LED 从两边往中间流水状态
k++;
if(k==4) {k=0;}
}
else if(n==4) //如果n为状态4
{
LED1=table6[k]; //8位LED 从中间往两边流水状态
k++;
if(k==4) {k=0;}
}
else //如果n为状态5
{
LED1=table5[k]; //8位LED 从两边往中间,再从中间往两边流水状态
k++;
if(k==8) {k=0;}
}
}
}
硬件设计
使用元器件:
单片机:STC89C52;
(注意:单片机是通用的,无论51还是52、无论stc还是at都一样,引脚功能都一样。程序也是一样的。)
自锁开关; 电阻10K;
电解电容10uF;瓷片电容30P;
电阻470(贴片);晶振12M;
排针40P;排座(母)40P;
LCD1602(自备);DIP40;
LED(红发红);蜂鸣器;
电阻4.7K;三极管8550;
按键;可调电阻103;
9*15万能板;
导线:若干;
添加图片注释,不超过 140 字(可选)
流程图:
添加图片注释,不超过 140 字(可选)
设计资料
01仿真图
本设计使用proteus7.8和proteus8.9两个版本设计,向下兼容,无需担心!具体如图!
添加图片注释,不超过 140 字(可选)
02原理图
本系统原理图采用Altium Designer19设计,具体如图!
添加图片注释,不超过 140 字(可选)
03程序
本设计使用软件keil5版本编程设计!具体如图!
添加图片注释,不超过 140 字(可选)
04设计报告
九千字设计报告,具体如下!
添加图片注释,不超过 140 字(可选)
05设计资料
资料获取请关注同名公众号,全部资料包括程序(含注释)、仿真源文件 、AD原理图、设计报告、流程图、实物图、元件清单等。具体内容如下,全网最全!!
添加图片注释,不超过 140 字(可选)
资料获取请观看前面演示视频!
点赞分享一起学习成长。