目录
DS18B20温度测量
实验目的
学习DS18B20的操作。
实验内容
学习使用单总线技术,通过它来实现STM32和外部温度传感器(DS18B20)的通信,并把从温度传感器得到的温度数据通过LCD1602显示出来。
实验说明
1. DS18B20简介
DS18B20是由DALLAS半导体公司推出的一种的“一线总线”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。一线总线结构具有简洁且经济的特点,可使用户轻松地组建传感器网络,从而为测量系统的构建引入全新概念,测量温度范围为-55~+125℃,精度为±0.5℃。现场温度直接以“一线总线”的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~12位的数字值读数方式。它工作在3~5.5V的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在EEPROM中,掉电后依然保存。ROM中的64位序列号是出厂前被光记好的,它可以看作是该DS18B20的地址序列码,每个DS18B20的64位序列号均不相同。64位ROM的排列是:前8位是产品家族码,接着48位是DS18B20的序列号,最后8位是前面56位的循环冗余校验码(CRC=X8+X5+X4+1)。ROM作用是使每一个DS18B20都各不相同,这样就可实现一根总线上挂接多个。所有的单总线器件要求采用严格的信号时序,以保证数据的完整性。DS18B20共有6种信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1。所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。这里我们简单介绍这几个信号的时序:
1) 复位脉冲和应答脉冲
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少 480us,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时15~ 60us,并进入接收模式(Rx)。接着DS18B20拉低总线60~240us,以产生低电平应答脉冲, 若为低电平,再延时480us。
2) 写时序
写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线。写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
3) 读时序
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。
在了解了单总线时序之后,我们来看看DS18B20的典型温度读取过程,DS18B20的典型温度读取过程为:复位→发SKIPROM命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送SKIPROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束。
DS18B20的介绍就到这里,更详细的介绍,请大家参考DS18B20的手册。
2. 硬件电路
3. DS18B20的初始化
1) 在进行初始化之前,要先进行IO口的初始化。
/************************************************************ * 函 数 名 : ds18b20_init * 函数功能 : IO端口时钟初始化函数 * 输 入 : 无 * 输 出 : 无 ************************************************************/ void ds18b20_init() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin=dq; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIO_ds18b20,&GPIO_InitStructure); }
管脚定义是在其头文件内:
// PB8 <-- 第一行 #define dq (GPIO_Pin_8) #define GPIO_ds18b20 GPIOB #define ds18b20_dq_H GPIO_SetBits(GPIO_ds18b20,dq) #define ds18b20_dq_L GPIO_ResetBits(GPIO_ds18b20,dq)
2) DS18B20的初始化
主机首先发出一个480–960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。
若无低电平出现一直都是高电平说明总线上无器件应答。作为从器件的DS18B20在一上电后就一直在检测总线上是否有480–960微秒的低电平出现,如果有,在总线转为高电平后等待15–60微秒后将总线电平拉低60–240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
3) 初始化时序图
4) 例程代码
/***************************************** * 函 数 名 : ds18b20init * 函数功能 : DS18B20初始化时序 * 输 入 : 无 * 输 出 : 无 *****************************************/ void ds18b20init() { DQOUTINT(); //输出 ds18b20_dq_L; delay_us(480); //延时480微妙 ds18b20_dq_H; delay_us(480); //延时480微妙 }
4. DS18B20读操作
主机发出各种操作命令都是向DS18B20写0和写1组成的命令字节,接收数据时也是从DS18B20读取0或1的过程。因此首先要搞清主机是如何进行写0、写1、读0和读1的。写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则将总线置为低电平,若主机想写1,则将总线置为高电平,持续时间最少60微秒直至写周期结束,然后释放总线为高电平至少1微秒给总线恢复。而DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
1) 读操作时序图
2) 读操作程序
/*********************************************************** * 函 数 名 : DS18b20rd * 函数功能 : DS18B20读数据时序 * 输 入 : 无 * 输 出 : value ***********************************************************/ u8 DS18b20rd() { u8 i=0,value=0; for(i=0;i<8;i++) { value>>=1; DQOUTINT(); //输出 ds18b20_dq_L; //拉低 delay_us(4); //延时4微妙 ds18b20_dq_H; delay_us(10); //延时10微妙 DQININT(); //输入配置 if(GPIO_ReadInputDataBit(GPIO_ds18b20,dq)==1) { value|=0x80; //读数据从低位开始 } delay_us(45); //延时45微妙 } return value; }
5. DS18B20写操作
对于读数据操作时序也分为读0时序和读1时序两个过程。读周期是从主机把单总线拉低1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。作为从机DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60微秒才能完成。
1) 写操作时序图
2) 写操作程序
/***************************************************** * 函 数 名 : ds18b20wr * 函数功能 : DS18B20写数据时序 * 输 入 : dat * 输 出 : 无 *****************************************************/ void ds18b20wr(u8 dat) { u8 i=0; DQOUTINT();//输出 for(i=0;i<8;i++) { ds18b20_dq_L; // 拉低 delay_us(15); //延时15微妙 if((dat&0x01)==1) { ds18b20_dq_H; } else { ds18b20_dq_L; } delay_us(60); //延时60微妙 ds18b20_dq_H; dat>>=1; //准备下一位数据的发送 } }
6. 读取温度操作
1)读取温度操作
DS18B20经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。所以当我们只想简单的读取温度值的时候,只用读取暂存器中的第0和第1个字节就可以了。读取一次D18B20温度的操作步骤如下:
a) 初始化DS18B20 b) 跳过ROM操作(ROM里面可以读取DS18B20的地址、型号,还有配置分辨率等,我们只使用一个DS18B20,所以不用读取地址型号,配置直接使用默认的12位分辨率就好了。) c) 发送温度转换命令。 d) 跳过ROM操作 e) 发送读取温度命令 f) 读取温度值。
补码介绍
- 正数的补码是正数本身
- 负数的补码是原码取反,然后再加1
DS18B20存储的温度值是以补码的形式存储的,所以读出来的温度值是实际温度值的补码,要把的转换为原码。
- 正温度的话,原码就是补码本身,所以在12位分辨率下,温度的计算公式为 $$\text{温度值}=\text{读取值}\times 0.0625$$
- 负温度的话,原码是补码减1再取反,所以在12位分辨率下,计算公式为 $$\text{温度值}=-(\text{读取值减1再取反})\times 0.0625$$
读取温度程序函数
/************************************************** * 函 数 名 : readtemp * 函数功能 : DS18B2温度寄存器配置,温度读取 * 输 入 : 无 * 输 出 : value **************************************************/ double readtemp() // 读取温度内需要复位的 { u16 temp; u8 a,b; double value; ds18b20init(); //初始化 ds18b20wr(0xcc); //发送忽略ROM指令 ds18b20wr(0x44); //发送温度转换指令 delay_ms(800); ds18b20init(); //初始化 ds18b20wr(0xcc); //发送忽略ROM指令 ds18b20wr(0xbe); //发读暂存器指令 a=DS18b20rd(); //温度的低八位 b=DS18b20rd(); //温度的高八位 temp=b; temp=(temp<<8)+a; if((temp&0xf800)==0xf800) { temp=(~temp)+1; value=temp*(-0.0625); } else { value=temp*0.0625; } return value; }
7. 例程主函数
int main() { u16 wd; double temp; LCD1602_GPIO_Init(); LCD1602_Init(); ds18b20_init(); // DS18B20初始化 while(1) { temp=readtemp(); // 读取温度 wd=temp*100; // 显示二位小数 LCD1602_MoveToPosition(0,0); LCD1602_DisplayString("wendu"); LCD1602_MoveToPosition(1,0); LCD1602_DisplayOneCharOnAddr(0,9,wd/1000+0x30); // 显示十位 LCD1602_DisplayOneCharOnAddr(0,10,wd%1000/100+0x30); // 显示个位 LCD1602_DisplayOneCharOnAddr(0,11,'.'); // 显示. LCD1602_DisplayOneCharOnAddr(0,12,wd%1000%100/10+0x30); // 显示小数1位 LCD1602_DisplayOneCharOnAddr(0,13,wd%1000%100%10+0x30); // 显示小数2位 LCD1602_DisplayOneCharOnAddr(0,14,0xdf); // ℃ LCD1602_DisplayOneCharOnAddr(0,15,'C'); // 显示C } }
主程序的效果是,读取到的温度值通过LCD1602上显示出来。
实验步骤
1. 使用1根1P杜邦线连接核心板PB8与“两路温度传感器”单元JP35(WD1)相连。 2. 使用1根8P杜邦线连接核心板PA0~PA7与“LCD1602液晶”单元JP33(D0~D7)。 3. 使用3根1P线连接PA8~PA11与“LCD1602液晶”单元JP31(RS,RW,EN)。 4. 在KEIL软件中调试运行“DS18B20温度测量”工程文件。 5. 实验现象:在液晶上显示“wendu xx.xx℃”。
实验任务
1. 设置温度显示范围为小数点后4位; 2. 改变测量温度的分辨率为10位,即最小刻度变化为0.25°C,此时可以观察到温度显示的最后两位始终为0; 3. 设置温度超过某一阈值即报警(蜂鸣器响),注意接线。
- 任务1.c
int main() { u32 wd; // 这里要改为 u32,因为u16最多表示2^16=65536 double temp; LCD1602_GPIO_Init(); LCD1602_Init(); // printf_init(); //printf初始化 ds18b20_init(); //DS18B20初始化 while(1) { temp=readtemp(); //读取温度 wd=temp*10000; //显示四位小数 LCD1602_MoveToPosition(0,0); LCD1602_DisplayString("Temperature:"); LCD1602_MoveToPosition(1,0); LCD1602_DisplayOneCharOnAddr(1,7,wd/100000+0x30);//显示十位 LCD1602_DisplayOneCharOnAddr(1,8,wd%100000/10000+0x30);//显示个位 LCD1602_DisplayOneCharOnAddr(1,9,'.');//显示. 用ox2e效果一样 LCD1602_DisplayOneCharOnAddr(1,10,wd%100000%10000/1000+0x30);//显示小数1位 LCD1602_DisplayOneCharOnAddr(1,11,wd%100000%10000%1000/100+0x30);//显示小数2位 LCD1602_DisplayOneCharOnAddr(1,12,wd%100000%10000%1000%100/10+0x30);//显示小数3位 LCD1602_DisplayOneCharOnAddr(1,13,wd%100000%10000%1000%100%10+0x30);//显示小数4位 LCD1602_DisplayOneCharOnAddr(1,14,0xdf); //显示°,对照LCD1602的字符表查询 LCD1602_DisplayOneCharOnAddr(1,15,'C');//显示C //printf("当前温度为:%0.4lf ℃\r\n",temp); }