#include<reg52.h>
#include"iic.h"
#include"onewire.h"
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
void read_temp();
void display_temp();
sfr P4=0xc0;//矩阵按键P4^4替换P3^7,P4^2替换P3^6
sfr AUXR=0x80;//
sbit h3=P3^2;//矩阵键盘第三行
sbit h4=P3^3;//矩阵键盘第四行
sbit l1=P4^4;//矩阵键盘第1列
sbit l2=P4^2;//矩阵键盘第2列
sbit led1=P0^0;//定义led灯
sbit led2=P0^1;
sbit led3=P0^2;
sbit led4=P0^3;
uint dac=325;//dac输出界面下的dac变量
uint lsb,msb;//温度寄存器的低8位和高8位
uint temp=0; //温度显示界面下温度变量
uint state0=0; //按键s4状态变量
uint state1=0;//dac输出模式状态
uint canshu=25;//温度参数界面初始值
uchar table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
uchar point_table[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
void keyscan();
//**************温度读取函数中的延时函数*******
//因为在读取温度时候延时也得不断扫描数码管
void Delayms(uint i) //@12.000MHz
{
//i=i*12;
while(i--)
{
display_temp(); //在温度读取函数中的延时需要包括显示函数
}
}
//********************************
//******普通延时函数**************
void delaysmg(uint i)
{
while(i--);
}
//********************************
//*******译码器选通函数***********
void hc138(uint z)
{
switch(z)
{
case 4:P2=P2&0x1f|0x80;break;
case 5:P2=P2&0x1f|0xa0;break;
case 6:P2=P2&0x1f|0xc0;break;
case 7:P2=P2&0x1f|0xe0;break;
case 0:P2=P2&0x1f|0x00;break;
}
}
//********************************
//******初始化外设函数************
void initsystem()
{
P0=0xff;
hc138(4);
hc138(0); //关闭LED
P0=0x00;
hc138(5); //关闭蜂鸣器
hc138(0);
}
//********************************
//********数码管选通函数***********
void selectsmg(uint we,uint du)
{
P0=0x01<<we; //P0送入位选值,然后打开138译码器选通位选
hc138(6); //138选通数码管位选
hc138(0); //然后关闭138译码器选通
P0=du; //P0送入段码值,打开段选
hc138(7); //138译码器选通段选
delaysmg(500); //延时一段时间,因为后面要把它给关闭,长一点的话亮一点
P0=0xff; //然后关闭数码管段码显示
hc138(0); //然后关闭138译码器选通
}
//*********************************
void delaysmg1(uint z)
{
while(z--);
}
//******D/A转换 写进去的数字 函数 iic通讯******
void write_dac(uint dat)
{
IIC_Start();
IIC_SendByte(0x90); //pcf8591写操作地址
//IIC_WaitAck(); //可不用写等待应答
IIC_SendByte(0x40); //选择dac输出通道,第6位为1打开D/A
//IIC_WaitAck();
IIC_SendByte(dat); //选择要从单片机给pcf8591芯片的数字量
//IIC_WaitAck();
IIC_Ack(0); //iic总线产生非应答
IIC_Stop(); //停止通讯
}
//**********************************
//*******读取环境温度函数-单总线通讯*******
void read_temp()
{
init_ds18b20(); //单片机告诉dsb20要用它了,初始化一下
Write_DS18B20(0xcc);//跳过ROM指令,意思只用1个ds18b20,不用再选地址了
Write_DS18B20(0x44);//开始温度转换
//Delayms(2); //延时,延时长短决定读取温度间隔缝隙
init_ds18b20(); //再次初始化
Write_DS18B20(0xcc); //跳过ROM指令
Write_DS18B20(0xbe); //读取高速暂存器
lsb=Read_DS18B20(); //读取低8位
msb=Read_DS18B20(); //读取高8位
//要求保留两位小数
temp=(msb<<8)|lsb;//此时temp16位包括整数和小数部分
temp=temp>>4;//此时整数部分右移4位,消除了小数部分,且乘了分辨率0.0625
temp=temp*100;//整数部分扩大100倍,现在可看作是10进制数
temp=temp+(lsb&0x0f)*6.25;//整数部分加上(小数部分乘以分辨率和100倍)
//最后temp现在是一个4位数的整数
}
//************************************
//**********显示温度函数---4位数显示*************
void display_temp()
{
selectsmg(0,0xc6);//显示c
delaysmg(200);
selectsmg(1,0xff);
delaysmg(200);
selectsmg(2,0xff);
delaysmg(200);
selectsmg(3,0xff);
delaysmg(200);
selectsmg(4,table[temp/1000]);//四位数温度处理
delaysmg(200);
selectsmg(5,point_table[temp/100%10]);
delaysmg(200);
selectsmg(6,table[temp/10%10]);
delaysmg(200);
selectsmg(7,table[temp%10]);
delaysmg(200);
}
//****************************
//********参数设置函数--两位数显示*************
void display_set()
{
selectsmg(0,0x8c);//显示p
delaysmg1(200);
selectsmg(1,0xff);
delaysmg1(200);
selectsmg(2,0xff);
delaysmg1(200);
selectsmg(3,0xff);
delaysmg1(200);
selectsmg(4,0xff);
delaysmg1(200);
selectsmg(5,0xff);
delaysmg1(200);
selectsmg(6,table[canshu/10]);
delaysmg1(200);
selectsmg(7,table[canshu%10]);
delaysmg1(200);
}
//**********************
//********dac输出显示---3位数显示*********
void display_dac()
{
selectsmg(0,0x08);
delaysmg1(200);
selectsmg(1,0xff);
delaysmg(200);
selectsmg(2,0xff);
delaysmg(200);
selectsmg(3,0xff);
delaysmg(200);
selectsmg(4,0xff);
delaysmg(200);
selectsmg(5,point_table[dac/100]);
delaysmg(200);
selectsmg(6,table[dac/10%10]);
delaysmg(200);
selectsmg(7,table[dac%10]);
delaysmg(200);
}
//*********************************
//******按键处理函数**********************
void keyscan()
{
//2*2矩阵键盘:
//s4按键按下
l1=l2=1; //第一二列为1
h3=1;h4=0; //第三行为1,第4行为0
if(l1==0)//如果s4按下
{
if(state0==0)//如果state0为0,即处于温度显示界面
{
state0=1; //第一次按下变为1
while(l1==0)
{
display_temp(); //防止按键按下时,打断温度显示函数
}
}
//
else if(state0==1)
{
state0=2; //改变s4控制的状态量
while(l1==0)
{
display_set(); //防止按键按下时,打断设置显示函数
}
}
//
else //state0为2时
{
state0=0; //改变s4控制的状态量
while(l1==0)
{
display_dac(); //防止按键按下时,打断dac显示函数
}
}
}
//
if(state0==1) //在参数设置状态下,按下s8才有效
{
h3=h4=1;
l1=1;l2=0;//按下s8
if(h4==0)
{
canshu--;
}
while(h4==0)
{
display_set(); //松手检测,没有的话会一次按下多次加
}
//
h3=h4=1;
l1=1;l2=0;//按下s9
if(h3==0)
{
canshu++;
}
while(h3==0)
{
display_set(); //松手检测,没有的话会一次按下多次加
}
}
//
h3=h4=1;
l1=0;l2=1;//s5按下
if(h3==0)
{
if(state1==0)
{
state1=1;
while(h3==0)
{
if(state0==0)
{
display_temp();
}
else if(state0==1)
{
display_set();
}
else if(state0==2)
{
display_dac();
}
}
}
//
else if(state1==1)
{
state1=0;
while(h3==0)
{
if(state0==0)
{
display_temp();
}
else if(state0==1)
{
display_set();
}
else if(state0==2)
{
display_dac();
}
}
}
}
}
//**************LED显示函数*****************
void led()//不同显示状态下led亮位置不同
{
if(state0==0) //显示温度界面时
{
P0=0xff; //防止数码管和led冲突,没有的话led会受到数码管影响
led2=0; //led2亮
hc138(4);
hc138(0);
}
else if(state0==1) //参数设置界面时
{
P0=0xff; //防止数码管和led冲突,没有的话led会受到数码管影响
led3=0;
hc138(4);
hc138(0);
}
else //dac输出界面时
{
P0=0xff; //防止数码管和led冲突,没有的话led会受到数码管影响
led4=0;
hc138(4);
hc138(0);
}
if(state1==0) //在dac输出格式模式1下
{
led1=0; //led1亮
hc138(4);
hc138(0);
}
else //在dac输出格式模式2下
{
led1=1; //led1灭
hc138(4);
hc138(0);
}
}
//*****************************
//因为读取出来的温度处理成为了四位数,也就是千位数,和正常比扩大了100倍
//所以在处理dac中,与温度相比的数值也应该扩大100倍
//******dac数模输出模式1***********
void dac_moshi1()
{
if(temp<(canshu*100)) //环境温度小于设定参数
{
dac=0*100; //赋值给dac数码管显示函数变量
write_dac(0); //dac写入数字量0 ---对应模拟量0v
}
else if(temp>(canshu*100)) //环境温度大于设定参数
{
dac=5*100; //赋值给dac显示变量
write_dac(5*51); //dac写入255,因为pcf8591是8位精度的,所以模拟量5v对应数字量255
}
}
//****************************************************//
//**********dac数模转换模式2****************
void dac_moshi2()
{
if(temp<=20*100) //环境温度小于20度时
{
dac=1*100;//dac显示界面为1
write_dac(1.0*51);//写入pcf8591的数字量为51,对应模拟量DA引脚输出1v
}
else if(temp>=40*100) //环境温度大于40度时
{
dac=4*100; //dac显示界面为4
write_dac(4.0*51); //写入pcf8591数字量为4*51
}
else //环境温度大于20小于40时
//注意由于我们读出的温度是扩大了100倍的,所以在一次函数中temp要除100
dac=(3.0/20.0*((float)temp/100)-2)*100; //赋值给数码管显示dac输出相对应的电压
write_dac((3.0/20.0*((float)temp/100)-2)*51); //将数字量写入pcf8591,使其转化为相对应模拟量
//注意被除数要写成浮点型,这样在除的时候才会有小数
}
//************************************
//************************************
void main()
{
initsystem(); //初始化系统
while(1)
{
keyscan();//循环扫描按键
led();
if(state0==0) //state0处于状态0,即数码管处于温度显示界面
{
read_temp(); //读取环境温度
display_temp(); //显示环境温度
}
else if(state0==1) //state0处于1状态,即数码管处于参数设置界面
{
display_set(); //显示参数设置
}
else //state0处于2状态,即数码管处于dac输出界面
{
display_dac(); //显示dac输出
}
///
if(state1==0) //state1处于0状态,即dac输出处于模式1
{
dac_moshi1(); //dac模式1设置
}
else if(state1==1) //state1处于1状态,即dac输出处于模式2
{
dac_moshi2(); //dac模式2设置
}
}
}
//led和数码管冲突--粗暴解决方法,直接在显示时将P0=0xff
//按键s4从温度显示界面转到参数设置界面非常不灵敏(按很多下才可以转换)解决方法:温度读取函数中延时写小一点。