DSP

多路温度采集的实现(原理图+程序源代码)

2019-07-13 19:45发布

实验原理图: 实验源代码:   /**************************************************************************************/ /*单片机采用STC89C52,晶振为12MHz。P1.0与DS18B20的数据端相连*/ /*P0为数码管的字段码口,P2口为位选码口*/ /*P3.2与按键0相连,P3.3与按键1相连*/ /*MAXNUM是单总线上最大可扫描DS18B20个数*/ /*flag为温度的正负号标志单元,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值*/ /*温度测量范围为(-55.0~99.9摄氏度)。*/ /*变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位*/ /*num记录当前单总线上DS18B20的个数*/ /*z是按键标志位,z为1时表明有按键按下*/ /*a,b分别是按键0和按键1的记录变量*/ /*m是轮换显示各DS18B20温度值和序列号的全局标志变量*/ /*二维数组ID用于记录各DS18B20的ROM序列号*/ /*数组disbuffer是LCD显示缓存数组*/ /*数组RomID_temp用于匹配DS18B20时临时记录要匹配DS18B20的序列号*/ /*共用体temp用于存放从DS18B20读入的数据*/ /**************************************************************************************/ #include #include #define uchar unsigned char #define uint unsigned int #define MAXNUM 4 //宏定义单总线上最大可扫描DS18B20个数 sbit DS=P2^2; //用P1.0口作为各DS18B20与单片机的I/O口 sbit Key0=P3^2; //P3.2用作按键0的输入,采用外部中断方式获取按键信号 sbit Key1=P3^3; //P3.3用作按键1的输入,采用外部中断方式获取按键信号 sbit dula=P2^6; sbit wela=P2^7; //unsigned char code chocode[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef}; union{ //定义共用体temp用于存放从DS18B20读入的数据,Keil C51采用大端格式,c[1]是高地址. uchar c[2]; //其中存放读取温度数值的低字节;c[0]是低地址,其中存放读取温度数值的高字节. uint x; //这样根据大端格式的要求,即数据的高字节存储在低地址中,数据的低字节存放在高地址中, }temp; //x便刚好是读出的温度数值. uchar idata flag; //温度的正负号标志,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值 uint cc,xs; //变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位 uchar idata disbuffer[6]; //LCD显示缓存数组 uchar idata ID[4][8]={0}; //用于记录各DS18B20的ROM序列号 uchar idata RomID_temp[8]; //匹配DS18B20时临时记录要匹配DS18B20的序列号 uchar m=0; //m是轮换显示各DS18B20温度值和序列号的全局标志变量 uchar num=0; //num记录当前单总线上DS18B20的个数 uchar z=0; //z是按键标志位,z为1时表明有按键按下 uchar a=0; //a是按键0的记录变量 uchar b=0; //b是按键1的记录变量 void delay(uint i) //延时i*9.62us { uint j; for(j=i;j>0;j--); } void delay_ms(uchar i) //延时(j*2+1+2)*i+5 个机器周期 { uchar j; //12MHz时,延时 0.5*i ms do{j=248; do{j--;}while(j); i--; }while(i); } void delay_2us(uchar i) //延时 2*i+5 us { while(--i); } uchar DS_init(void) //18B20复位,初始化函数 { uchar presence; DS=0; delay_2us(250); //根据DS18B20的复位时序.先把总线拉低555us DS=1; delay_2us(30); //再释放总线,65us后读取DS18B20发出的信号 presence=DS; delay_2us(250); //如果复位成功,则presence的值为0;否则为1 return (presence); //返回0则初始化成功,否则失败 } uchar read_byte(void) //读1字节 { uchar i,j,dat=0; for(i=1;i<=8;i++) //作8个循环,读出的8位组成一个字节 {DS=0; _nop_(); //先将总线拉低1us, DS=1; delay_2us(2); //再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值 j=DS; delay_2us(30); //一位读完后,延迟65us后读下一位 dat=(j<<7)|(dat>>1); //读出的数据最低位在一个字节的最低位,这样刚好一个字节在DAT里 } return(dat); } uchar read_2bit(void) //读2位 { uchar i=0,j=0; DS=0; _nop_(); //先将总线拉低1us, DS=1; delay_2us(2); //再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值 j=DS; delay_2us(30); //一位读完后,延迟65us后读下一位 DS=0; _nop_(); DS=1; delay_2us(2); i=DS; delay_2us(30); i=j*2+i; //将读出的两位放到变量i中,其中第一个读出的位处于i的第1位;而第二个读出的位处于i的第0位 return(i); } void write_byte(uchar dat) //写1字节 { uchar i; for(i=0;i<8;i++) //作8个循环,写入的8位组成一个字节 {DS=0; //先将总线拉低 DS = dat&0x01; //向总线上放入要写的值 delay_2us(50); //延迟105us,以使DS18B20能采样到要写入的值 DS = 1; //释放总线,准备写入下一位 dat>>=1; //将要写的下一位移到dat的最低位 } } void write_bit(bit dat) //写1位 { DS=0; //先将总线拉低 DS=dat; //向总线上放入要写的值 delay_2us(50); //延迟105us,以使DS18B20能采样到要写入的值 DS = 1; //释放总线 } void display_ROMID(void) //定义显示序列号的函数 { uchar i,p,t,k; uint q; uchar disbuffer_rom[8]; uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf}; //共阳极的字段码 uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef}; //位选码表 z=0; //z归零,表明没有按键按下 for(q=0;q<500;q++) //显示各DS18B20的序号 { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出 break; t=chocode[7]; //取当前的位选码 P2=t; //送出位选码 t=codevalue[m+10]; //查得显示字符的字段码 P0=t; //送出字段码 delay(100); } P2=0xFF; //关断LCD一段时间,产生闪屏效果 for(q=0;q<500;q++) { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出 break; delay(100); } for(k=0;k<8;k=k+4) //依次显示序列号的低32位或高32位 { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出 break; //接下来将序列号的低32位或高32位值取出放入disbuffer_rom存储 disbuffer_rom[0]=(ID[m][k]&0x0F); disbuffer_rom[1]=((ID[m][k]&0xF0)>>4); disbuffer_rom[2]=(ID[m][k+1]&0x0F); disbuffer_rom[3]=((ID[m][k+1]&0xF0)>>4); disbuffer_rom[4]=(ID[m][k+2]&0x0F); disbuffer_rom[5]=((ID[m][k+2]&0xF0)>>4); disbuffer_rom[6]=(ID[m][k+3]&0x0F); disbuffer_rom[7]=((ID[m][k+3]&0xF0)>>4); for(q=0;q<250;q++) //显示序列号的低32位或高32位 { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出 break; for (i=0;i<8;i++) { if(z==1) break; t=chocode[i]; //取当前的位选码 P2=t; //送出位选码 p=disbuffer_rom[i]; //取当前显示的字符 t=codevalue[p]; //查得显示字符的字段码 P0=t; //送出字段码 delay(40); } } P2=0xFF; //显示完一轮,灯灭 for(q=0;q<500;q++) { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出 break; delay(100); } } } void display_error(void) //定义显示总线上没有DS18B20时的函数 { uchar i,p,t; uint q; uchar disbuffer_temp[8]={0,2,8,1,16,0x0f,0x0f,0}; //显示内容为"OFF 1820" uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf}; //共阳极的字段码 uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef}; //位选码表 for(q=0;q<250;q++) { for (i=0;i<8;i++) { t=chocode[i]; //取当前的位选码 P2=t; //送出位选码 p=disbuffer_temp[i]; //取当前显示的字符 t=codevalue[p]; //查得显示字符的字段码 P0=t; //送出字段码 delay(40); } } } uchar search_rom(void) //遍历搜索单线上所连的所有18b20的序列号 { uchar k,l=0,chongtuwei,m,n,a; uchar _00web[MAXNUM]={0}; do { DS_init(); //复位单总线上的所有DS18B20 write_byte(0xf0); //单片机发布搜索命令 for(m=0;m<8;m++) { uchar s=0; //s用来记录本次循环得到的1个字节(8位)序列号 for(n=0;n<8;n++) { k=read_2bit(); //读第m*8+n位的原码和反码,保存在k中 k=k&0x03; //屏蔽掉k中其它位的干扰,为下一步判断作准备 s>>=1; //s右移一位,即把上一次循环得到的位值右移一位, //这样执行完一次n为变量的循环,便可得到一个字节的ROM号 if(k==0x01) //k为01,表明读到的数据为0,即所有器件在这一位都为0,所以向总线上写0 //同时对s的值不进行操作,即这位的序列号记为0 { write_bit (0); } else if(k==0x02)//k为02,表明读到的数据为1,即所有器件在这一位都为1,所以向总线上写1 { s=s|0x80; //记录下此位的值,即s的最高位置1 write_bit (1); } else if(k==0x00) { chongtuwei=m*8+n+1; //记录下这个冲突位发生的位置;之所以加1是为了让_00web数组中的第一位保持0不变, //便于判断搜索循环是否结束; if(chongtuwei>_00web[l])//如果冲突位比标志00位的位高,即发现了新的冲突位,那么这位写0 { write_bit (0); _00web[++l]=chongtuwei; //依次记录位比冲突标志位高的冲突位在_00web数组中 } else if(chongtuwei<_00web[l]) //如果冲突位比标志00位的位低,那么把ID中这位所在的字节右移n位, { //从而得到这位先前已经写过的值,如果为0,说明这位先前写的是0,那么继续写0, // 如果这位先前写的是1,那么继续写1 a=(ID[num-1][m]>>n)&0x01; s=s|(a<<7); //记录下此位的值 write_bit(a); } else if(chongtuwei==_00web[l])//如果冲突位就是标志00位,那么s的最高位置1,即这位记为1,同时向总线上写1; //之所以不写0,是因为前面已经写过0,再写0,就得不到遍历的效果. { s=s|0x80; write_bit (1); l=l-1; //改变标志00位的位置,即向前推一个00位,并且是往低位方向推 } } else if(k==0x03) //k为03,表明总线上没有DS18B20,函数结束,同时返回零值 {return(0);} } ID[num][m]=s; } num++; //DS18B20的个数加1 }while((_00web[l]!=0)&&(num0xf8) {flag=1;temp.x=~temp.x+1;} //如果为负,则符号标志置1,计算温度值,注意c[0]中放的是读取温度值的高字节 //还要注意读出的数值一共是12位 cc=temp.x/16; //计算出温度值的整数部分,这个语句相当于数值乘0.0625再取整数部分 xs=temp.x&0x0f; //取温度值小数部分的第一位 xs=xs*10; //这两条语句相当于乘0.625,得小数位的第一位,注意不是乘0.0625 xs=xs/16; } void display(void) //定义温度底层显示函数 { uchar codevalue[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40};//共阴极的字段码 uchar chocode[]={0xdf,0xef,0xf7,0xfb,0xfd,0xfe,};//位选码表 uchar i=0,p,t; disbuffer[4]=0x10; //让LCD的第5位全灭,即什么也不显示,注意是16进制表示 disbuffer[5]=0x0A+m; //LCD的第6位显示DS18B20序列号,以字母A,B,C,D,E,F来给示,所以加上0x0a if (flag==1)disbuffer[3]=0x11;//判断是否显示负号 else disbuffer[3]=0x10; disbuffer[0]=xs; //放入小数位显示的数值 disbuffer[1]=(cc%10);//放入个数要显示的数值 disbuffer[2]=(cc/10);//放入十位要显示的数值 for (i=0;i<6;i++) { /* t=chocode[i]; //取当前的位选码 P2=t; //送出位选码 p=disbuffer[i]; //取当前显示的字符 t=codevalue[p]; //查得显示字符的字段码 if (i==1) t=t+0x80; //个位比较特殊,因为有小数点,所以要加上0x80 P0=t; //送出字段码 delay(40); */ t=codevalue[disbuffer[i]]; if (i==1) t=t+0x80; dula=1; P0=t; dula=0; P0=0xff; wela=1; P0=chocode[i]; wela=0; delay(45); } } void diplay_final(void) //定义温度上层显示函数 { uint q,r; z=0; for(q=0;q<8;q++) //将要显示DS18B20的温度值的序列号放入数组RomID_temp中 { RomID_temp[q]=ID[m][q]; } P0=0xFF; if(a==0) //如果按键0的标志变量a=0,即进行闪烁显示 { for(q=0;q<3;q++) //闪烁空隙仍在读取温度 { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出 break; flag=0; Read_Temperature_rom(); // 读取双字节温度 Temperature_cov(); //温度转换 for(r=0;r<15;r++) delay(1000); } } for(q=0;q<5;q++) //读取温度并显示 { if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出 break; flag=0; Read_Temperature_rom(); // 读取双字节温度 Temperature_cov(); //温度转换 for(r=0;r<100;r++) display(); //显示温度 } } void key_0() interrupt 0 //外部中断0的中断处理函数 { delay(1200); //延时消抖 if(Key0==0) { if(a==0) //a只可能取0值或1值 a=1; else a=0; b=0; //同时一旦0键按下就将b归零 z=1; //将有按键按下标志位z置位 } } void key_1() interrupt 2 //外部中断1的中断处理函数 { delay(1200); //延时消抖 if(Key1==0) { if(a==0) //只有a等于0时才让b的值在0或1之间转变 { if(b==0) b=1; else b=0; } z=1; //将有按键按下标志位z置位 if(a==1) //当a=1时进入固定显示一个DS18B20的温度值的状态 { m++; //按一次1键m值加1,即在不同的DS18B20切换显示 if(m>=num) //当然,m的值不能超过或等于总线上挂接的DS18B20的数目 m=0; } } } void main() //主函数 { uchar p,i; delay(10); p=search_rom(); //执行搜索DS18B20算法,同时返回搜索情况,p值为0,表明总线上没有DS18B20,p值为1表明搜索到DS18B20; if(p==0) //如果总线上没有DS18B20,显示"OFF 1820" while(1) display_error(); EA=1; //开中断 EX0=1; //允许外部中断0 IT0=1; //外部中断0为边沿触发方式 EX1=1; //允许外部中断1 IT1=1; //外部中断1为边沿触发方式 while(1) { if(a==0&&b==0) //按键0和1的状态变量值都为0,此时轮换显示各DS18B20的温度值 { diplay_final(); //显示DS18B20的温度值 if(m 由于最近比较忙,所有过多的芯片简介就没介绍,希望大家理解。