基于51单片机实验报告计算器.docx
《基于51单片机实验报告计算器.docx》由会员分享,可在线阅读,更多相关《基于51单片机实验报告计算器.docx(24页珍藏版)》请在冰点文库上搜索。
基于51单片机实验报告计算器
基于51单片机实验报告(计算器)
一.计算器模块
1.功能介绍
利用8051单片机硬件资源和常用外围电路如LCD1602,七段数码管,时钟(DS1302)温度传感器(18B20)等实现一个能做简单四则运算,并具有时钟显示,温度显示附加功能的计算器。
2.设计方案
利用STC89C52为内核的单片机,PC机。
四则运算利用4*4矩阵键盘实现从0—9和运算符号的输入,并将操作过程和结果显示在LCD1602上。
时钟显示和温度显示,可以利用DS1302产生年份,月份,日期,星期,时,分,秒的数据,并将数据送往LCD1602显示,同样可以利用单片机开发板上面集成的DS18B20温度传感器来测试周围环境的温度,将获取的温度通过在LCD1602来显示。
系统设计框图
3.具体实现代码
计算器四则运算部分主要分为键盘扫描的键值读取,判断运算符号实现乘除优先级计算,LCD1602显示。
✧键盘扫描常用的有行扫描法,线反转法,此处我们用行扫描法,可以更明了读取键值。
unsignedchartemp;
key=null;
//第一行按键
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!
=0xf0)
{
delay(10);//延时软件去抖动
temp=P3;
temp=temp&0xf0;
if(temp!
=0xf0)//确认有键按下
{
temp=P3;
switch(temp)
{
case0xee:
key='D';//读键值
break;
case0xde:
key=0;
break;
case0xbe:
key='=';
break;
case0x7e:
key='/';
break;
}
flag++;
}
}
✧读完按键值之后我们需要读取运算的数字与运算符号,通过判断键值为数字则通过nun=nun*10+key,计算出数字,判断键值为运算符号则读出数字和键值。
flag=0;
addr++;
while(flag==0)
{
scan();
}
if(flag>0)//如果有数据从键盘输入
{//则关闭数码管的显示
latch=0;
}
if(key>=0&&key<=9)//如果有数字输入则连续读取数字
{
display_lcd(key);
num=num*10+key;
key=null;
read();
}
elseif(key=='+'||key=='-'||key=='/'||key=='*'||key=='=')
{
display_lcd(key);//碰到运算符则同时读取数字和运算符
sym=key;
key=null;
}
else
{
display_lcd(key);
}
}
uintread_num()//读取数字
{
uinttemp=num;
num=0;
returntemp;
}
ucharread_sym()//读取运算符
{
uchartemp=sym;
sym=null;
returntemp;
}
✧读取完键值之后,对运算符进行判断如果第二位运算符为乘除则在读取一次数字和运算符,并将第二个操作数与第三个操作数相计算并赋给第二个操作数,将第三个运算符赋给第二个运算符,并调用开始计算两个操作符的函数继续判断第二个操作符,直到第二个运算符为“=”为止,即可实现四则运算的优先级运算。
if(sym2=='=')//简单运算如果第二个运算符为“=”
{
switch(sym1)
{
case'+':
result=num1+num2;delay(500);display_result(result);//直接计算并在LCD上显示
break;
case'-':
result=num1-num2;delay(500);display_result(result);
✧如果第二个操作符不是“=”,则需先先计算一步,并将计算结果,和读取的第三个键值赋给第二组的数字和运算符,继续循环判断。
read();
temp=sym2;
if(sym1=='*')//第一个操作符为乘除则先计算前两个数字
{
calculate(num1*num2,temp,read_num(),read_sym());
}
elseif(sym1=='/')
{
calculate(num1/num2,temp,read_num(),read_sym());
}
elseif(sym2=='*')//第二个操作符为乘除先计算后面两个数字
{
calculate(num1,sym1,num2*read_num(),read_sym());
}
elseif(sym2=='/')
{
calculate(num1,sym1,num2/read_num(),read_sym());
........................
✧LCD显示结果
LCD显示主要是控制三个端口lcden=0;lcdrs=0;lcdrw=0;分别为使能端,控制向LCD读写,控制向LCD写数据和命令。
voidinit_lcd()
{
lcdrw=0;
write_command(0x38);//
write_command(0x0f);//
write_command(0x06);//
write_command(0x01);//
write_command(0x80);//
}
二.万年历模块
1.功能描述
主要依靠时钟芯片DS1302来实现,通过读取函数将芯片中的计时数据读取出来,并显示在LCD上,再通过写入数据函数将初始值赋予时钟芯片,最后的设置部分是靠单片机上的独立按键以及写入数据函数来实现的。
图3.1DS1302引脚图
使用DS1302时,要对其引脚和寄存器进行特殊设置,以实现所需功能。
引脚设置后面会提及,这里不做说明,主要讲一下程序的编写。
2.代码实现:
.
DS1302的的工作过程中包过读写一个字节数据等过程,具体代码实现如下:
✧向DS1302中写入数据,利用RTInputByte函数向DS1302中一字节一字节的写入数据,RTInputByte函数具体是根据要输入的数据的每一位的1或0的情况,来控制IO口来进行高低电平的变化,从而实现一字节数据的输入。
而对于DS1302而言,输入的数据前8位为地址,后8位为输入的地址。
voidW1302(ucharucAddr,ucharucDa)
{
T_RST=0;
T_CLK=0;
T_RST=1;
RTInputByte(ucAddr);/*地址,命令*/
RTInputByte(ucDa);/*写1Byte数据*/
T_CLK=1;
T_RST=0;
}
✧从DS1302中读取数据,原理基本同上
ucharR1302(ucharucAddr)
{
ucharucData;
T_RST=0;
T_CLK=0;
T_RST=1;
RTInputByte(ucAddr);/*地址,命令*/
ucData=RTOutputByte();/*读1Byte数据*/
T_CLK=1;
T_RST=0;
return(ucData);
}
✧按键设置部分的实现,利用单片机上的独立按键来实现:
voidkeyscan()
{
if(s1==0)
{
lcd_delay(10);
if(s1==0)
{
time_data_buff[1]++;
if(time_data_buff[1]==0x5a)
{
time_data_buff[1]=0x00;
}
while(!
s1);
if(time_data_buff[1]==0x0a)
{
time_data_buff[1]=0x10;
}
if(time_data_buff[1]==0x1a)
{
time_data_buff[1]=0x20;
}
if(time_data_buff[1]==0x2a)
{
time_data_buff[1]=0x30;
}
if(time_data_buff[1]==0x3a)
{
time_data_buff[1]=0x40;
}
if(time_data_buff[1]==0x4a)
{
time_data_buff[1]=0x50;
}
Set1302(time_data_buff);
}
}
if(s2==0)
{
lcd_delay(10);
if(s2==0)
{
time_data_buff[2]++;
if(time_data_buff[2]==0x24)
{
time_data_buff[2]=0x00;
}
if(time_data_buff[2]==0x0a)
{
time_data_buff[2]=0x10;
}
if(time_data_buff[2]==0x1a)
{
time_data_buff[2]=0x20;
}
while(!
s2);
Set1302(time_data_buff);
}
}
if(s3==0)
{
lcd_delay(10);
if(s3==0)
{
time_data_buff[5]++;
if(time_data_buff[5]==0x07)
{
time_data_buff[5]=0x00;
}
}
while(!
s3);
Set1302(time_data_buff);
}
if(s4==0)
{
lcd_delay(10);
if(s4==0)
{
time_data_buff[3]++;
if(time_data_buff[3]==0x32)
{
time_data_buff[3]=0x01;
}
if(time_data_buff[3]==0x0a)
{
time_data_buff[3]=0x10;
}
if(time_data_buff[3]==0x1a)
{
time_data_buff[3]=0x20;
}
if(time_data_buff[3]==0x2a)
{
time_data_buff[3]=0x30;
}
}
while(!
s4);
Set1302(time_data_buff);
}
}
三.温度测量模块
1.功能描述:
该模块主要利用温度传感器DS18b20测量温度并将温度显示在LCD上。
1.DS18b20芯片介绍:
①独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。
②测温范围-55℃~+125℃,固有测温误差(注意,不是分辨率,这里之前是错误的)1℃。
③支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只能并联8个,实现多点测温,如果数量过多,会使供电电源电压过低,从而造成信号传输的不稳定。
④工作电源:
3.0~5.5V/DC(可以数据线寄生电源)
⑤在使用中不需要任何外围元件
⑥测量结果以9~12位数字量方式串行传送
下图展示18b20时序图(非常重要!
)
2.具体代码实现:
/*温度测量*/
/*----------------------------------------------*/
/************************************************/
/*头文件*/
/************************************************/
#include
#include//有_nop_指令
/************************************************/
/*端口定义*/
/************************************************/
sbitRS=P2^4;//1602控制端
sbitRW=P2^5;
sbitEN=P2^6;
sbitDQ=P1^3;//18b20总线引脚
/************************************************/
/*函数声明*/
/************************************************/
/************************************************/
/*延时函数*/
/************************************************/
voidDelay4us()//延时4us
{
;
}
voidDelay(unsignedcharj)//一个循环延时15us
{
unsignedchari;
while(j--)
{
i=5;
while(--i);
}
}
/************************************************/
/*18b20初始化函数*/
/************************************************/
bitInit_18b20()
{
bitdat;
DQ=1;//DQ复位
Delay4us();
DQ=0;//拉低总线
Delay(35);//延时525us
DQ=1;//拉高总线
Delay
(2);//延时30us
dat=DQ;//读取返回值(为0时有18b20存在,为1时没有)
Delay
(2);
return(dat);
}
/************************************************/
/*18b20写入数据函数*/
/************************************************/
voidWriteData_18b20(unsignedchardat)//写8位数据
{
unsignedchari;
for(i=0;i<8;i++)
{
DQ=0;//拉低总线
DQ=dat&0x01;//将dat的最低位赋给总线
Delay(4);//延时60us
DQ=1;//拉高总线准备写下一个数据
dat>>=1;//数据右移一位
}
}
/************************************************/
/*18b20读数据*/
/************************************************/
unsignedcharReadData_18b20()
{
unsignedchari,dat=0;
for(i=0;i<8;i++)
{
DQ=0;//拉低总线
dat>>=1;//数据右移一位
DQ=1;//拉高总线准备读取数据
if(DQ)//判断若是1.将数据赋给变量最高位
dat=dat|0x80;
Delay(4);
}
return(dat);
}
/************************************************/
/*读取温度值函数*/
/************************************************/
unsignedintReadTemp()
{
unsignedchari=0;//低八位温度数据
unsignedcharj=0;//高八位温度数据
unsignedintk=0;//总16位温度数据
Init_18b20();//初始化
WriteData_18b20(0xcc);//跳过序列号操作
WriteData_18b20(0x44);//开启温度转换
Delay(200);//延时3ms
Init_18b20();//初始化
WriteData_18b20(0xcc);
WriteData_18b20(0xbe);//读取18b20寄存器
i=ReadData_18b20();//低八位
j=ReadData_18b20();//高八位
k=j;
k<<=8;
k=k+i;
return(k);
}
/************************************************/
/*us延时*/
/************************************************/
voidDelayus(unsignedchart)
{
while(--t);
}
/************************************************/
/*ms延时*/
/************************************************/
voidDelayms(unsignedchart)
{
while(--t)
{
Delayus(245);
Delayus(245);
}
}
/************************************************/
/*1602读忙*/
/************************************************/
bitBusy_1602()
{
P0=0xff;
RS=0;
RW=1;
EN=1;
return(bit)(P0&0x80);
}
/************************************************/
/*1602写入*/
/************************************************/
voidWrite_1602(biti,unsignedcharj)//参数i为0写指令,为1写数据。
参数j为数据
{
while(Busy_1602())//忙时等待
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
RS=i;
RW=0;
EN=1;
P0=j;
_nop_();
EN=0;
}
/************************************************/
/*1602清屏*/
/************************************************/
voidClear_1602()
{
Write_1602(0,0x01);
Delayms(5);
}
/************************************************/
/*1602字符显示*/
/************************************************/
voidShowByte_1602(unsignedcharx,unsignedchary,unsignedchardat)//x为列,y为行,dat为数据
{
if(y==0)
{
Write_1602(0,(0x80+x));//第一行
}
else
{
Write_1602(0,(0xc0+x));//第二行
}
Write_1602(1,dat);//写入数据
}
/************************************************/
/*1602字符串显示*/
/************************************************/
voidShowString_1602(unsignedcharx,unsignedchary,unsignedchar*dat)
{
if(y==0)
Write_1602(0,(0x80+x));
else
Write_1602(0,(0xc0+x));
while(*dat)
{
Write_1602(1,*dat);
dat++;
}
}
/************************************************/
/*1602初始化*/
/************************************************/
voidInit_1602()
{
Write_1602(0,0x38);//显示模式设置
Delayms(5);
Write_1602(0,0x38);
Delayms(5);
Write_1602(0,0x38);
Delayms(5);
Write_1602(0,0x38);
Write_1602(0,0x08);//只开显示
Write_1602(0,0x01);//显示清屏
Write_1602(0,0x06);//地