前面一章节我们介绍了温度的计算方法和读取温度流程,初始化了DS18B20器件,我们这样接着前面一章节继续讲解。
- DS18B20的ROM的操作
ROM的操作就涉及到发送数据和接收数据。对于写入和读取一位的时序我们看下图1。
图1 DS18B20的写入和读1bit数据时序
图1中第一个时序是,写入时序,我们可以看到写一次数据不管0还是1时间都是必须在60us-120us,并且不管写0还是1都是以拉低总线为开始,前15us为总线拉低状态,代表开始,后面 45us为写入状态。那么我们就知道写时序如下:拉低总线—>延迟15us以内—>拉低信号到60us为写入0(拉高信号为写入1)。
写入1字节代码如下:
void DS18B20_WriteByte(unsigned char byte_data) { unsigned char i; unsigned char wait_send_bit; DS18B20_Mode_Out_PP(); for( i=0; i<8; i++ ) { wait_send_bit = byte_data & 0x01; byte_data = byte_data>>1; /* 写0和写1的时间至少要大于60us */ if (wait_send_bit) { DS18B20_GPIO_L; ds18b20_delay(1) ; //1us < 延时 < 15us DS18B20_GPIO_H; ds18b20_delay(6) ; } else { DS18B20_GPIO_L; ds18b20_delay(7) ; DS18B20_GPIO_H; ds18b20_delay(1) ; } } }
这段代码if语句内实现的1bit写入,for循环实现了8bit写入,对于DS18B20不论读取还是写入,数据的传输都以LSB为先,所以么一次对于待发送的byte_data数据,我们都是先取出最低位数据,然后,判断后发出,在把byte_data左移1bit,等待发送下一位。这样就直接实现了1byte数据的发送。
在看图1,读取的时序如下:拉低总线1~15us代表起始–>切换成输入状态–>读取总线状态–>延迟满足60us间隔条件即可。
代码如下:
unsigned char DS18B20_ReadBit(void) { unsigned char bit_data; DS18B20_Mode_Out_PP(); DS18B20_GPIO_L; /* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */ ds18b20_delay(1) ; DS18B20_Mode_IPU(); /* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */ if(DS18B20_GPIO_ReadData == 1 ) { bit_data = 1; } else { bit_data = 0; } ds18b20_delay(5) ; return bit_data; }
这是读取1bit函数,为了方便我们循环8次 就可以实现1字节的读取,
代码如下:
unsigned char DS18B20_ReadByte(void) { unsigned char i; unsigned char bit_data = 0; unsigned char byte_dat = 0; for(i=0; i<8; i++) { bit_data = DS18B20_ReadBit(); byte_dat = (byte_dat) | (bit_data<<i); } return byte_dat; }
这个比较简单,读取到的数据需要移位组成1字节。
有了写入和读取函数,我们在看匹配rom,我们看到其命令值为0x55,对于这个命令器件在读取温度之前,为了匹配读取哪一个器件的温度值,总线上只有1个器件时,可以选择使用跳过匹配rom命令,从而不匹配进行读取。但是为了考虑方便后续设计,我们采用匹配后读取。那么我们知道需要匹配ID,才可以。
那么DS18B20的ID由64bit数据组成,包括8bit的CRC、48bit序列号、8bit产品编码组成。我们先通过读取ID后才能匹配ROM,总线上只有1个器件时,可以用命令0x33来读取ID,多个则不能使用该命令,需要换成0xf0命令进行算法计算出。这里只说明1个器件情况下,
代码如下
void DS18B20_ReadId (unsigned char *id ) { unsigned char i; DS18B20_WriteByte(0x33); //读取序列号 for ( i = 0; i < 8; i ++ ) { *id = DS18B20_ReadByte(); id++; } }
发送0x33命令后,连续读取8个字节数据放到指针中。
然后就可以实现ROM的匹配,命令0x55,
代码如下:
void DS18B20_MatchRom (unsigned char *id) { unsigned char i; DS18B20_Rst(); DS18B20_WriteByte(0X55); /* 匹配 ROM */ for(i=0;i<8;i++) { DS18B20_WriteByte (*id); id++; } }
读取到的ID放在buf中,然后在通过buf发送出去。就完成对应ID的器件的匹配,后续的操作就只针对这个ID的器件来进行了。完成了匹配后,我们就可以执行该器件的温度转换指令,发送命令0x44(DS18B20_WriteByte(0X44)),最后读取温度,然后转换即可。
代码如下:
float DS18B20_GetTemp_for_MatchRom (unsigned char *id ) { unsigned char temp_msb; unsigned char temp_lsb; short temp_msblsb; float ds18b20_temp; DS18B20_MatchRom (id); //匹配ROM DS18B20_WriteByte(0X44); /* 开始转换 */ DS18B20_MatchRom (id); //匹配ROM DS18B20_WriteByte(0XBE); /* 读温度值 */ temp_lsb = DS18B20_ReadByte(); temp_msb = DS18B20_ReadByte(); temp_msblsb = (temp_msb<<8) | temp_lsb; if( temp_msblsb < 0 ) /* 负温度 */ ds18b20_temp = (~temp_msblsb+1) * 0.0625; else ds18b20_temp = temp_msblsb * 0.0625; return ds18b20_temp; }
代码流程如下,匹配rom—发送温度转换命令0x44–匹配rom—读取温度—读取两个字节的数据—温度转换。首先,两个匹配rom的原因是每一次命令发出,都需要重新匹配一次ROM;为什么读取两个字节数据我们看下下图2
图2 DS18B20 内部存储单元
从图2我们看到DS18B20内部有9个字节的存储单元,第一个和第二个字节存放温度LSB和MSB数据,后面存放的温度告警的两个字节,然后配置转换分辨率的寄存器和4个不关紧要寄存器。因此我们先读取2次就是前面2个寄存器的数据,刚好是温度LSB和MSB的数据。然后我们把LSB和MSB进行组合,组成1个short型数据,然后进行判断数据的正温度还是负温度。在这里强调的对于C语言定义为有符号位数据时,最后为就是符号位,因此我们只需要判断其是否小于0即可,不需要判断符号位就知道我们读取的是正还是负的数据了。这样就完成了读DS18B20的温度。
但是我们在手册看到,每一次数据读取都是以初始化(复位)开始,因此在调用读取温度的时候,我们需要3个函数的组成才可以完成一次温度的读取:复位—读取ID—读取温度;
例如在Main内我们初始化后,我们先调用DS18B20_Rst(); 然后在调用DS18B20_ReadId (ID_buf),最后在执行DS18B20_GetTemp_for_MatchRom (ID_buf),就可以返回温度值了。当然为了简便我们可以把这三个函数封装成一个函数来调用也是可以的。到这里DS18B20的温度读取就学习完了。下一次我们将选择一个章节专门分享延迟函数的种类以及实现方法。
原创文章,作者:小峰,如若转载,请注明出处:http://www.wfblog.com/archives/447.html