创建好“ds18b20.c”和“ds18b20.h”文件,把下面代码添加进各自的文件。


1.ds18b20.c代码

我们在宋老师的代码基础上添加了温度转换函数,原理在上一讲已经讲解清楚了。

#include <reg52.h>
#include <ds18b20.h>
#include <intrins.h>
  
unsigned char temp_i=0;//定义全局变量temp_i用来帮助液晶屏灵活显示

/* 软件延时函数,延时时间(t*10)us */
void DelayX10us(unsigned char t)
{
    do {
           _nop_();
           _nop_();
           _nop_();
           _nop_();
           _nop_();
           _nop_();
           _nop_();
           _nop_();
    } while (--t);
}

/* 复位总线,获取存在脉冲,以启动一次读写操作 */
unsigned char Get18B20Ack()
{
    unsigned char ack;
     
    EA = 0;           //禁止总中断
    IO_18B20 = 0;     //产生500us复位脉冲
    DelayX10us(50);
    IO_18B20 = 1;
    DelayX10us(6);    //延时60us
    ack = IO_18B20;   //读取存在脉冲
    while(!IO_18B20); //等待存在脉冲结束
    EA = 1;           //重新使能总中断
     
    return ack;
}

/* 向DS18B20写入一个字节,dat-待写入字节 */
void Write18B20(unsigned char dat)
{
    unsigned char mask;
     
    EA = 0;                             //禁止总中断
    for (mask=0x01; mask!=0; mask<<=1)  //低位在先,依次移出8个bit
    {
        IO_18B20 = 0;                   //产生2us低电平脉冲
        _nop_();
        _nop_();
        if ((mask&dat) == 0)            //输出该bit值
            IO_18B20 = 0;
        else
            IO_18B20 = 1;
        DelayX10us(6);                  //延时60us
        IO_18B20 = 1;                   //拉高通信引脚
    }
    EA = 1;                             //重新使能总中断
}

/* 从DS18B20读取一个字节,返回值-读到的字节 */
unsigned char Read18B20()
{
    unsigned char dat;
    unsigned char mask;
     
    EA = 0;                             //禁止总中断
    for (mask=0x01; mask!=0; mask<<=1)  //低位在先,依次采集8个bit
    {
        IO_18B20 = 0;                   //产生2us低电平脉冲
        _nop_();
        _nop_();
        IO_18B20 = 1;                   //结束低电平脉冲,等待18B20输出数据
        _nop_();                        //延时2us
        _nop_();
        if (!IO_18B20)                  //读取通信引脚上的值
            dat &= ~mask;
        else
            dat |= mask;
        DelayX10us(6);                  //再延时60us
    }
    EA = 1;                             //重新使能总中断
   
    return dat;
}

/* 启动一次18B20温度转换,返回值-表示是否启动成功 */
unsigned char Start18B20()
{
    unsigned char ack;
     
    ack = Get18B20Ack();   //执行总线复位,并获取18B20应答
    if (ack == 0)          //如18B20正确应答,则启动一次转换
    {
        Write18B20(0xCC);  //跳过ROM操作
        Write18B20(0x44);  //启动一次温度转换
    }
    return !ack;           //ack==0表示操作成功,所以返回值对其取非,返回值就是1了
}

/* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */
unsigned char Get18B20Temp(int *temp)
{
    unsigned char ack;
    unsigned char LSB, MSB;                     //16bit温度值的低字节和高字节
     
    ack = Get18B20Ack();                        //执行总线复位,并获取18B20应答
    if (ack == 0)                               //如18B20正确应答,则读取温度值
    {
        Write18B20(0xCC);                       //跳过ROM操作
        Write18B20(0xBE);                       //发送读命令
        LSB = Read18B20();                      //读温度值的低字节
        MSB = Read18B20();                      //读温度值的高字节
        *temp = ((unsigned int)MSB << 8) + LSB; //合成为16bit整型数
    }
    return !ack;                                //ack==0表示操作成功,所以返回值对其取非,返回值就是1了
}

/* 温度转换 */
unsigned char TEMP_CONV(unsigned int *temp, unsigned char *str)
{
    unsigned char res;
    float  temp_float;
   
    res = Get18B20Temp(temp);  //读取当前温度,传入的参数是指针类型
    if (res)                   //读取成功时,进行温度转换
    {
        if( (*temp>>11)==0 )   //温度大于等于0度     
        {   
            temp_float=( (float)(*temp) ) *0.0625*10.0;  //*temp就是没有转换时的16位那个变量,然后再把实际温度值再乘以10倍
            *temp=(unsigned int)temp_float;              //得到16位整型的数值
            str[0]='0'+( (*temp/1000)%10 );              //当温度大于等于100度时需要显示百位数
            str[1]='0'+( (*temp/100)%10 );               //当温度大于等于10度时需要显示十位数
            str[2]='0'+( (*temp/10)%10 );                //当温度大于等于1度时需要显示个位数
            str[3]='.';
            str[4]='0'+( (*temp)%10 );                   //温度必须显示小数点后的一位

            if(str[0]=='0')temp_i++;
            if(str[1]=='0')temp_i++;

            return 1;//读取温度成功,返回值一律为1
        } 

        else if( (*temp>>11)>0 )//温度小于0度     
        {
            *temp=(*temp)&0x07FF;                       //清除掉高5位使其变为0
            *temp=2048-(*temp);                         //此时的temp为补码     
            temp_float=( (float)(*temp) )*0.0625*10.0;  //实际温度值再乘以10倍
            *temp=(unsigned int)temp_float;             //得到16位整型的数值

            str[0]='-';                                 //添加负数的符号
            str[1]='0'+( (*temp/100)%10 );              //当温度在-10度以下时需要显示十位数
            str[2]='0'+( (*temp/10)%10 );               //温度必须显示个位数,哪怕是0,比如“-0.5”
            str[3]='.';
            str[4]='0'+( (*temp)%10 );                  //温度必须显示小数点后的一位

            if(str[1]=='0')
            { 
                str[1]='-';
                temp_i=1;//实际温度大于-10.0度的时候,假如是-5.4度,那么“LcdShowStr(0, 0, str+temp_i);”就是显示“-5.4”,小数点就是在第3个显示格上显示
                         //实际温度小于等于-10.0度的时候,假如是-12.6度,str[1]不等于‘0’,
                         //这样temp_i是等于0的,那么“LcdShowStr(0, 0, str+temp_i);”显示“-12.6”,小数点就是在第4个显示格上显示  
            }

            return 1;    //读取温度成功,返回值一律为1
        } 
    } 
    
    return 0;//读取温度不成功,返回值为0
}

 

2.ds18b20.h代码

#ifndef __DS18B20_H__
#define __DS18B20_H__
  
sbit IO_18B20 = P3^2;                                            //DS18B20通信引脚
extern unsigned char temp_i;                                     //在main.c中要用该变量需要进行全局变量声明
  
void DelayX10us(unsigned char t);                                //软件延时函数,延时时间(t*10)us
unsigned char Get18B20Ack();                                     //复位总线,获取存在脉冲,以启动一次读写操作
void Write18B20(unsigned char dat);                              //向DS18B20写入一个字节,dat-待写入字节
unsigned char Read18B20();                                       //从DS18B20读取一个字节,返回值-读到的字节
unsigned char Start18B20();                                      //启动一次18B20温度转换,返回值-表示是否启动成功
unsigned char Get18B20Temp(int *temp);                           //读取DS18B20转换的温度值,返回值-表示是否读取成功
unsigned char TEMP_CONV(unsigned int *temp, unsigned char *str); //温度转换
 
#endif


温度传感器11


3.main.c测试代码

#include <reg52.h>
#include <function.h>//详见第六章第8讲
#include <timer.h>   //详见第八章第11讲
#include <lcd.h>     //详见第十一章第3讲
#include <ds18b20.h>

u8 flag1s=0;
void main()
{
    u16 temp;
    u8 str[10];
    EA = 1;   
    Start18B20();        //启动DS18B20
    InitLcd1602();       //初始化液晶
    TIM0_Init(10000,11); //定时10ms,11是微调使定时更精确

    while (1)
    {
        if (flag1s)      //每秒更新一次温度
        {
            flag1s = 0;
       
            if( TEMP_CONV(&temp,str)==1 )     //返回值为1代表读取温度成功
            {
                LcdShowStr(0, 0, str+temp_i); //显示到液晶屏上
                temp_i=0;
            }
            else                              //读取失败时,提示错误信息
            {
                LcdShowStr(0, 0, "error!");
            }
            Start18B20();                     //必须要重新启动下一次转换         
        }
    }
}
  
void TIM0_IRQHandler() interrupt 1
{ 
    static u8 tmr1s = 0;
   
    TH0 = T0RH;        //重新加载重载值
    TL0 = T0RL;
   
    tmr1s++;
    if (tmr1s >= 100)  //定时1s
    {
        tmr1s = 0;
        flag1s = 1;
    }
}

笔者亲自把开发板放进冰箱进行冷冻10分钟,拿出来上电时,确实可以把0度以下的温度测试出来并完整的显示在液晶屏上。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)