DS18B20驱动(单总线协议)

        DS18B20是单片机一个可以做到测量温度的模块。在CT107D型号(stc15系列)单片机中使用单总线协议通讯。这里总结针对于在代码实现的经验。     

DS18B20的特点有以下几种:

                                                                         1-采用单总线(1-Wire)协议.

        2-拥有独立64位ID

        3-多点测温应用

        4-无需外部元件

        5-可以由数据线供电

        6-测量温度范围:-55℃~+125℃

        7-在-10℃~+85℃区间内有±0.5℃的误差

        8-可以选择9~12位温度分辨率(二进制数)

        9-转换12位温度分辨率最多需要750ms

DS18B20电路图

  • GND:电源地线                                                
  • DQ:数字信号输入/输出端。
  • VDD:外接供电电源输入端。
  • ROM:只读存储器。
  • SCRATCHPAD:暂存器。
  • MEMORY CONTROL LOGICl:存储器控制逻辑

        这里着重看右半部分的工作,只读存储器(ROM)跟暂存器(SCRATCHPAD)依靠存储器控制逻辑(MEMORY CONTROL LOGICl)来控制双方的数据交换。只读存储器使用单总线协议用一个串口跟外界完成数据交换。暂存器则需要(从上到下)与温度传感器,警告触发寄存器,配置寄存器,2字节用户寄存器,8位循环冗余校验产生器交换数据。

DS18B20初始化

          通过DS18B20来实现测温,一定要通过单总线协议实现对芯片的读写。首先使用之前我们要确定DS18B20的存在。这个过程也能叫做初始化DS18B20(一般而言单总线协议遇到同型号的多个单元要多确定地址,但是CT107D只有一个DS18B20,所以不考虑)。

        

    线型注释

  • 黑粗线:总线控制器拉低电平 
  • 灰粗线:DS18B20拉低电平
  • 黑细线:上拉电阻拉高电平      

         根据以上时序图,我将整个初始化DS18B20的过程总括为几个步骤:总线(DQ)首先发送复位脉冲,将电平拉低。在至少480微秒之后,上拉电平释放总线,上拉电阻会在15到60微秒区间将电位拉高。在上拉电阻开始上拉电平后的60到240微秒区间内DS18B20会主动将电平拉低——也就是说,如果DS18B20不存在或者异常,那么电平在这个区间内保持高电平。度过这个区间之后就要重新上拉电平。

        总结这个历程,我们就是要判断第一次上拉电平释放总线后的60到240微秒区间读取电平,读到低电平,则DS18B20存在,反之DS18B20异常或者不存在。

        下面是DS18B20初始化的代码操作。首先补充这里的单总线协议由单片机的P14口完成,其次延时函数可以通过stc-isp来生成。

bit DQ = P1^4;

bit DS18B20_Init(void)
{
    bit flag;         //接收电平数据

    DQ = 1;           //先将电平置为高电平
    Delay_10us(5);    
    DQ = 0;           //从这里历程正式开始,将电平拉低
    Delay_10us(55);   //延时550us,至少要大于480us
    DQ = 1;           //第一次上拉电平
    Delay_10us(10);   //延时100us,电阻要最多60us才能上拉
    flag = DQ;        //读取DQ状态
    Delay_10us(5);    //防止短时间多次操作产生错误,结束历程要延时一会
    
    return flag;     //返回0则初始化成功,返回1则失败
}

DS18B20写操作

        从上面的总括我们可以看到,DS18B20功能还是比较强大的,给我们有多个模式来选择(给我们添了又一道麻烦),这就需要我们通过写操作来配置。

         电平的操作手段也是一样的,这里就不过多赘述。写操作的本质上就是向DS18B20一位一位写入“1”和“0“。那么写操作的核心就是如何向DS18B20写入“1”和“0”。看时序图可以知道:拉低电平之后历程开始,此后如果60到120微秒内保持低电平,那么DS18B20将会视作写入”0“。那么在1到无穷大时间内保持高电平就是会被视作写入1。看到下面的DS18B20的采样历程,实际上将高或低电平覆盖DS18B20的采样历程就是会被视作写入一个”1“或”0“。

        总结就是拉低电平之后,要写入1就立马拉高电平,写入0就在60到120微秒区间内拉高电平就行了。由于我们通常是一个字节一个字节进行写操作,那将历程执行八次就行了。下面是代码操作。

bit DQ = P1^4;

void DS18B20_Write(unsigned char item)
{
    unsigned char i = 0;
    for(i;i<8;i++)
    {    
        DQ = 0;           //拉低电平
        DQ = item&0x01;   //读取输入数据的最低一位
        Delay_10us(8);    //延时80us,至少60us
        DQ = 1;           //拉高电平
        item >>= 1;       //输入数据右移一位
    }
    Delay_10us(5);        //每次操作完成要间隔一会
}

        

DS18B20读操作

        

           看上图的时序,读操作中,拉低电平之后1微秒以内就可以拉高电平释放总线。也就是拉低电平之后就可以直接执行拉高电平,这时候DS18B20就会视作执行读操作。当我们拉高电平释放总线是,如果DS18B20要输出“0”,那么会保持一段时间的低电平在60微秒以内,输出“1”就会直接拉高电平。手册推荐的采样器采样时间是拉高电平释放总线15微秒的区间,所以微秒释放总线之后就可以直接读取总线状态。

           总结就是,拉低电平就直接拉高电平释放总线,并读取总线状态。下面是代码操作。

bit DQ = P1^4;

unsigned char DS18B20_Read(void)
{
    unsigned char i = 0;
    unsigned char dat = 0x00;    //存储读取的数据
    
    for(i;i<8;i++)    
    {
        DQ = 0;
        dat >>= 1; //读取的数据左移,最高位此时是0
        DQ = 1;
        if(DQ)     //如果读出“1”,就将dat最高位置为1,是“0”就不操作
        {
            DQ |= 0x80;
        }
        Delay_10us(5);    //延时一会
    }
    return dat;
}

DS18B20暂存器

        

        从上往下看,暂存器分为9个区域。Byte0和1存储温度数据,这部分是与温度传感器交换数据的区域,Byte0存储低位数据,Byte1存储高位数据;Byte2和3存储警报触发值,这部分与警报触发寄存器交换数据,同样的Byte2存储低位数据,Byte3存储高位数据;Byte4存储配置寄存器数据,与配置寄存器交换数据,这里的数据决定温度精度; Byte4、5、6存储保留位与用户寄存器交换数据。Byte8则是与冗余校验发生器交换数据。

        下面着重看到温度存储区域。

        可以看出,LS BTYE是Btye0,MS BYTE是Byte1。数据存储方式如图所示,温度都是以2进制数,不同位数存储,从-4位一直到6位。它们的最大值就是大约85℃。另外高位的s是符号(sign),看到表格,全为0则位为正数,全为1则是负数。

DS18B20指令

        DS18B20的操作指令已经设计了,只需要按照手册用写函数输入对应指令就行。

         跳过寄存器指令(0xcc) :前文有提到操作DS18B20需要定位设备位置,但是由于单片机上只有一个DS18B20,那么我们就不需要定位,直接跳过这个流程。 

    

         写暂存器(0x4e)               读暂存器(0xbe):也就是读操作

         启动温度转换(0x44):启动后DS18B20会将测出来的温度转换成数据(16进制)

DS18B20代码操作

        综合上面的板块我们就可以完成读取温度的操作了,我们先看看这个读DS18B20温度参数的过程。

float fRead_Temperature()
{
	unsigned char low,high;
	float temp;
	DS18B20_Init();
	DS18B20_Write(0xCC);        //跳过ROM
	DS18B20_Write(0x44);        //启动温度转换
	DS18B20_Init();
	DS18B20_Write(0xCC);        //跳过ROM
	DS18B20_Write(0xBE);        //获取暂存器数据
	low = DS18B20_Read();		//获取低8位数据
	high = DS18B20_Read();		//获取高8位数据
	temp = (high<<8|low)*0.0625;
	return temp;
}

        这里看到读和写是两次操作,写了就要重新初始化才能读。读取的数值也要注意,DS18B20测出来的数据是4位16进制数,需要两个u8类型的变量或者一个u32数据来存储(因为我们写的读数据函数一次读一个字节,所以这里读两次)。乘以0.0625是为了加上小数点,将2进制数转化位10进制数。

        最后配合计时器数码管等加入主函数,将数值显示在数码管上。

        

#include <STC15F2K60S2.H>
#include "intrins.h"
#include "System.h"
#include "onewire.h"
#include "Device.h"
#include "Timer.h"

void vDevice_Ctrl(unsigned char p0,unsigned char p2)
{
	P0 = p0;
	P2 = P2&0x1f|p2;	//与运算0x1f是为了保证iic通讯的正常
	P2 &= 0x1f;
}

void vSystem_Init()    
{
	vDevice_Ctrl(0x00,0xa0);
	vDevice_Ctrl(0xff,0x80);
	vDevice_Ctrl(0xff,0xe0);
}

float temperature;
unsigned long temp;
unsigned char cnt;
unsigned char smg_ctrl[]={0x01,0x02,0x04,0x08,0x10,0x20,0x040,0x80};
unsigned char smg_code[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char smg_buf[8];

void smg_process()
{
	smg_buf[2] = smg_code[temp/100000];
	smg_buf[3] = smg_code[temp/10000%10]|0x80;
	smg_buf[4] = smg_code[temp/1000%10];
	smg_buf[5] = smg_code[temp/100%10];
	smg_buf[6] = smg_code[temp/10%10];
	smg_buf[7] = smg_code[temp%10];
}

void smg_display()
{
	static unsigned char i;
	vDevice_Ctrl(~smg_buf[i],0xe0);
	vDevice_Ctrl(smg_ctrl[i],0xc0);
	i++;
	if(i == 8) i = 0;
}

void vTimer2_Init()
{
	AUXR |= 0x04;			//定时器时钟1T模式
	T2L = 0x20;				//设置定时初始值
	T2H = 0xD1;				//设置定时初始值
	AUXR |= 0x10;			//定时器2开始计时
	IE2 |= 0x04;			//定时器2中断
	EA = 1;						//开启总中断
}

void main()
{
	vSystem_Init();
	vTimer2_Init();
	while(1)
	{
		if(cnt >= 100)
		{
			temperature = fRead_Temperature();
			temp = temperature*10000;
			cnt = 0;
		}
		smg_process();
	}
}

void Timer2_IRC() interrupt 12
{
	cnt++;
	smg_display();
}

 

 下载到板子的现象就是如图

         至此,本篇结束,如有纰漏,希望读者斧正。

以下是DS18B20驱动代码的示例: ```c #include <OneWire.h> // 定义引脚 #define ONE_WIRE_BUS 2 // 初始化 OneWire 对象 OneWire oneWire(ONE_WIRE_BUS); // DS18B20 的地址 byte addr[8]; void setup() { Serial.begin(9600); // 搜索 DS18B20 的地址 if (!oneWire.search(addr)) { Serial.println("No DS18B20 detected."); while (1); } // 判断 DS18B20 的地址是否正确 if (OneWire::crc8(addr, 7) != addr[7]) { Serial.println("Invalid DS18B20 address."); while (1); } // 输出 DS18B20 的地址 Serial.print("Found DS18B20 with address: "); for (byte i = 0; i < 8; i++) { Serial.print(addr[i], HEX); Serial.print(" "); } Serial.println(); // 启动温度转换 oneWire.reset(); oneWire.select(addr); oneWire.write(0x44); // 发送启动转换命令 } void loop() { // 等待温度转换完成 delay(1000); // 读取温度值 oneWire.reset(); oneWire.select(addr); oneWire.write(0xBE); // 发送读取温度命令 // 读取温度数据 byte data[9]; for (byte i = 0; i < 9; i++) { data[i] = oneWire.read(); } // 判断温度数据是否正确 if (OneWire::crc8(data, 8) != data[8]) { Serial.println("Invalid temperature data."); return; } // 计算温度值 int16_t raw = (data[1] << 8) | data[0]; float temp = (float)raw / 16.0; // 输出温度值 Serial.print("Temperature: "); Serial.print(temp); Serial.println(" C"); } ``` 以上代码使用了 `OneWire` 库来控制 DS18B20 温度传感器。首先,代码搜索 DS18B20 的地址,然后发送启动转换命令,等待温度转换完成,发送读取温度命令,读取温度数据,并计算出温度值。最后,代码输出温度值到串口。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值