#include "my51.h" #include "smg.h" #include "ds18b20.h" void main() //测试 ,6位数码管显示温度值 { u8 i=0; u16 temp=0; while(1) { temp=ds18b20_readTemperaData(); for(i=0;i<100;i++) { displaySMG(ds18b20_processTempData(temp)); } } }
#ifndef _DS18B20_H #define _DS18B20_H #include "my51.h" #include "smg.h" extern u8 smgWela[7]; //数码管位选数据 sbit DQ=P2^2; //总线定义 bool ds18b20_init(); //初始化函数 u8* ds18b20_processTempData(u16 temp);//将temp数据处理成数码管可显示数据 u16 ds18b20_readTemperaData(); //读温度 u8 ds18b20_readByte() ; //读一个字节 void ds18b20_writeByte(u8 dat); //mcu向18b20写一个字节 #endif
#include "ds18b20.h" /****************************************************************** 当主机总线t0时刻从高拉至低电平时就产生写时间隙 从to 时刻开始的1us之后,15us之前将所需写的位送到总线上 DSl820 在t0后的15-60us 对总线采样若低电平写入的位是0 ,若高电平写入的位是1 连续写2 个位之间的间隙应大于1us 写1,总时间大于60us,在t0开始延时1us就可以写1,15us之后ic来采样,采样时间最大45us 写0,总时间是60~120us,15~60us是ic在采样,120以外就没必要了,mcu总得释放总线吧 不管写1还是写0,大于60us的话,ic肯定已经采样完成了,那mcu就可以释放了 *******************************************************************/ void ds18b20_writeByte(u8 dat) //mcu向ic写一个字节 { u8 i; u8 tmep=dat; for(i=0;i<8;i++) { DQ=0; //产生读写时序的起始信号 _nop_(); //要求至少1us的延时 DQ=dat & 0x01; //对总线赋值,从最低位开始写起 delayXus(10);//延时74us,写0在60~120us之间释放,写1的话大于60us均可释放 DQ=1; //释放总线,为下一次mcu送数据做准备, dat>>=1; //有效数据移动到最低位,2次写数据间隙至少需1us } } /************************************************************************** 下降沿产生读时序 整个读时序必须至少有60us的持续时间,相邻两个读时序必须要有至少1us的恢复时间 DS18B20在读时序产生1us后输出数据到总线上,也有可能需要2~3个微秒,但不会更多 而要求主机释放总线和采样总线等动作要在15μs内完成,那么让mcu采样的最佳时机 是读时序产生后的5~13us之间,在15~60us这段时间是18b20的私有时间,它会在这段 时间内的任意时刻释放总线,是不稳定期,我们不要让mcu在这段时间里对总线操作 *******************************************************/ u8 ds18b20_readByte() //mcu读一个字节 { u8 i,value=0; for(i=0;i<8;i++) { DQ=0; //起始信号 value>>=1; //顺便延时3~4个机器周期 DQ=1; //mcu释放总线 _nop_();_nop_();_nop_(); //再延时3.3us if(DQ) { value|=0x80;//保存高电平数据,低电平的话不用保存,移位后默认是0 } delayXus(8); //延时60.76us } return value; } u16 ds18b20_readTemperaData() //读取温度值 { u16 temp=0; if(ds18b20_init()) { ds18b20_writeByte(0xcc); //写指令:跳过rom检测 ds18b20_writeByte(0x44); //写指令:温度转换 //delayms(750);// 转换延时需要750ms以上,我们不等它 //首次转换未完成时,得到的初始化数据是85度,处理一下就可以了 //温度转换电路是硬件独立的,不会阻塞初始化功能 if(ds18b20_init()) { ds18b20_writeByte(0xcc); //写指令:跳过检测rom ds18b20_writeByte(0xbe); //写指令:读取温度值 temp=ds18b20_readByte(); //先读低8位数据 temp|=(u16)ds18b20_readByte()<<8; //再读高8位数据,然后合并 temp&= 0x0FFF; //高4位数据反正没用上,我们用来存放错误码 } else { led5=0; //调试代码 temp=0x2000; //错误码,初始化失败 } } else { led6=0; //调试代码 temp=0x1000; //错误码,初始化失败,可能器件损坏 } return temp; } bool ds18b20_init() //初始化 { u8 checkState=0; DQ=1; //总线初始状态 _nop_();_nop_(); DQ=0; //mcu先将总线拉低 delayXus(80); //延时530us,要求480us~960us的低电平信号 //当ic接受到此复位信号后会回发一个存在信号 //mcu若要接收此存在信号则先要释放总线,让ic控制该总线 //当mcu释放总线后的15~60us之后,ic才向总线发一个低电平信号 //该信号存在时间为60~240us DQ=1; //mcu释放总线 delayXus(10); //mcu释放15~60us以上,(8+6*10)*1.085=73us, //这时DS18B20已经拉低信号,大约60~240us应答保持时间, checkState=DQ; //在这段60~240us时间内,mcu采样是否有器件响应,0表示有响应 delayXus(70); //延时464us,加上之前的73us,共537us //虽然ic在拉低电平60~240us之后,会释放总线,但整个时间至少480us //故我们共用时537us,这样是为了不影响后续的操作 if(checkState) //checkstate为0说明有器件响应,为1无器件响应 { return FALSE; } return TRUE; //初始化成功 } u8* ds18b20_processTempData(u16 temp) //返回数码管可直接显示的数据指针 { u8 i=0; if(0x0550==temp) //如果初始化温度数据是85度的话 { led7=0; //亮灯报警,调试 smgWela[5]=18; //当温度是85度,第6个数码管显示负号 return smgWela; //一般刚上电时能看到这个负号 } if(1==(temp&0x0800)) //检测第11位是否为1,为1是负温度 { temp&=0x07ff; //只取第0~10共11个位 temp=(~temp+1) & 0x07ff;//将补码还原 smgWela[0]=18; //第一个数码管显示18号元素,即负号 } else { smgWela[0]=dark; //正温度的话这个数码管就不要显示了 } temp=(u16)(temp*6.25); //精度的1000倍,我们将小数点另外叠加显示 if(temp>=10000) { smgWela[1]=1; //第二个数码管显示1,是百位上,100度以上啊 } else { smgWela[1]=dark; //百位上是0的话不要显示这个0 } smgWela[2]=temp%10000/1000; //第三个数码管 十位 smgWela[3]=temp%1000/100; //第四个数码管 个位叠加小数点 smgWela[4]=temp%100/10; //第五个数码管 smgWela[5]=temp%10; //第六个数码管 smgWela[6]=0xf7; //第4个数码管叠加小数点 return smgWela; //返回数组 }
#ifndef _51SMG_H_ #define _51SMG_H_ #include <reg52.h> #include "mytype.h" sbit dula =P2^6; //段选锁存器控制 控制笔段 sbit wela =P2^7; //位选锁存器控制 控制位置 #define dark 0x11//在段中,0x11是第17号元素,0x00是低电平,数码管不亮,即table[17] #define dotDark 0xff//小数点全暗 void displaySMG(u8* pWela); //数码管显示函数,参数是数组指针 #endif
#include "smg.h" #include "my51.h" static u8 code table[]= { //0~F外加小数点和空输出的数码管编码 0x3f , 0x06 , 0x5b , 0x4f , // 0 1 2 3 0x66 , 0x6d , 0x7d , 0x07 , // 4 5 6 7 0x7f , 0x6f , 0x77 , 0x7c , // 8 9 A B 0x39 , 0x5e , 0x79 , 0x71 , // C D E F 0x80 , 0x00 ,0x40 // . 空 负号 空为第17号元素 }; /* 由于此表只能一次显示一个小数点,故已注释掉,仅供查询 例如想要第一个和第六个数码管小数点同时点亮, 则执行 pWela->dot = 0xfe & 0xdf 即可 u8 code dotTable[]={ //小数点位置,某一位置0时,小数点亮 0xff , //全暗 0xfe , 0xfd , 0xfb , //1 2 3 0xf7 , 0xef , 0xdf //4 5 6 };*/ u8 data smgWela[7]={0,0,0,0,0,0,0}; //第一位到第六位,最后一个是小数点位置控制 //P0口的数码管位选控制锁存器只用了低6位,我们保留高2位的数据,留作它用 void displaySMG(u8* pWela) { u8 i=0; //控制6位数码管显示函数,不显示的位用参数dark u8 preState=P0|0x3f; //保存高2位状态,其中最高位是ADC0804的片选信号 wela=0;dula=0;_nop_();//先锁定数据,防止吴亮及位选锁存器高2位数据被改变 P0=0; //由于数码管是共阴极的,阳极送低电平,灯不亮 dula=1;_nop_(); dula=0; //段选数据清空并锁定 P0=preState; //共阴极数码管是阴极置高不亮,低6位置1,高2位保留 wela=1;_nop_(); //注:wela和dula上电默认为1 wela=0; //位选锁定,初始保留高2位的数据,低6位置高不亮 for(i=0;i<6;i++) //显示6位数码管 { P0=table[pWela[i]]|(((1<<i) & pWela[6])?0x00:0x80); dula=1;_nop_(); //送段数据,叠加小数点的显示,0x00点亮小数点 dula=0; P0=preState&~(1<<i); //不影响高2位数据,低6位是数码管位选,低电平有效 wela=1; _nop_(); //送位选号 wela=0; delayms(1); //稍作延时,让灯管亮起来 { //消除叠影及误亮,阴极置1不亮,低6位置1,高2位保留并锁定 P0=preState; wela=1; _nop_(); wela=0; } } }
#ifndef _MY51_H #define _MY51_H #include <reg52.h> //#include <math.h> #include <intrins.h> #include "mytype.h" #define high 1 //高电平 #define low 0 //低电平 #define led P1 //灯总线控制 sbit led0=P1^0; //8个led灯,阴极送低电平点亮 sbit led1=P1^1; sbit led2=P1^2; sbit led3=P1^3; sbit led4=P1^4; sbit led5=P1^5; sbit led6=P1^6; sbit led7=P1^7; sbit ledLock=P2^5; //led锁存的状态,0锁定 ,1不锁定 sbit beep=P2^3; //蜂鸣器 void delayms(u16 ms); void delayXus(u8 us); //函数执行(8+6x)个机器周期, 即t=(8+6x)*1.085 ///////////////////////////////////////////////////////////////////////////// #endif
#include "MY51.h" void delayms(u16 ms) //毫秒级软延时函数 { u16 i,j; for(i=ms;i>0;i--) { for(j=113;j>0;j--) {} } } /**************************************************************** 若使用12分频模式的mcu,晶振频率为11059200Hz 则每个机器周期用时12/11059200=1.085微秒 keil4编译,在默认的8级优化方式下 参数us=0时,函数执行9个机器周期,即t=9*1.085=9.77 us 参数us!=0时,函数执行(8+6x)个机器周期, 即t=(8+6x)*1.085 us *****************************************************************/ void delayXus(u8 us) //微秒级软延时函数 { while(us) //这种写法有利于减小us=0时的机器周期 { us--; } }