(1)实验平台:
普中 51-Ai8051 开发板https://item.taobao.com/item.htm?abbucket=17&id=1026052331067(2)资料下载 :普中科技-各型号产品资料下载链接
生活中, 温度测量应用随处可见, 小到数字温度计, 大到工业设备温度测量等。 本章来学习使用 AI8051 控制 DS18B20 数字温度传感器, 实时读取环境温度。本章分为如下几部分内容:
26.1 实验介绍
26.1.1 实验简介
26.1.2 实验目的
26.2 硬件设计
26.3 软件设计
26.3.1 创建和配置工程
26.3.1.1 GPIO 配置
26.3.1.2 配置 UART1
26.3.1.3 生成工程
26.3.2 添加用户驱动代码
26.3.2.1 ds18b20.c 文件
26.3.2.2 main.c 文件
26.4 实验现象
26.1 实验介绍
26.1.1 实验简介
相信没有电子爱好者不知道 DS18B20 的, DS18B20 是常用的数字温度传感器, 其输出的是数字信号, 具有体积小, 硬件开销低, 抗干扰能力强, 精度高,多点组网的特点。 DS18B20 数字温度传感器接线方便, 封装成后可应用于多种场合, 如管道式, 螺纹式, 磁铁吸附式, 不锈钢封装式, 型号多种多样。
主要根据应用场合的不同而改变其外观。 封装后的 DS18B20 可用于电缆沟测温, 高炉水循环测温, 锅炉测温, 机房测温, 农业大棚测温, 洁净室测温, 弹药库测温等各种非极限温度场合。 耐磨耐碰, 体积小, 使用方便, 封装形式多样,适用于各种狭小空间设备数字测温和控制领域。
温度范围-55℃~+125℃, 在-10~+85℃时精度为±0.5℃; 电压范围: 3.0~ 5.5V; DS18B20 支持多点组网功能, 多个 DS18B20 可以并联在唯一的三线上, 实现组网多点测温。
DS18B20 不同封装外观实物如下图所示:
从 DS18B20 外观图可以看到, 当我们正对传感器切面(传感器型号字符那一面) 时, 传感器的管脚顺序是从左到右排列。 管脚 1 为 GND, 管脚 2 为数据 DQ,管脚 3 为 VDD。 如果把传感器插反, 那么电源将短路, 传感器就会发烫, 很容易损坏, 所以一定要注意传感器方向, 通常我们在开发板上都会标出传感器的凸起出, 所以只需要把传感器凸起的方向对着开发板凸起方向插入即可。
DS18B20 内部结构如下图所示:
ROM 中的 64 位序列号是出厂前被光刻好的, 它可以看作是该 DS18B20 的地址序列号。 64 位光刻 ROM 的排列是: 开始 8 位(28H) 是产品类型标号, 接着的48 位是该 DS18B20 自身的序列号, 最后 8 位是前面 56 位的循环冗余校验码。 光刻 ROM 的作用是使每一个 DS18B20 都各不相同, 这样就可以实现一根总线上挂接多个 DS18B20 的目的。
DS18B20 温度传感器的内部存储器包括一个高速的暂存器 RAM 和一个非易失性的可电擦除的 EEPROM,后者存放高温度和低温度触发器 TH、 TL 和配置寄存器。
配置寄存器是配置不同的位数来确定温度和数字的转化, 配置寄存器结构如下:
低五位一直都是"1", TM 是测试模式位, 用于设置 DS18B20 在工作模式还是在测试模式。 在 DS18B20 出厂时该位被设置为 0, 用户不需要去改动。 R1 和 R0用来设置 DS18B20 的精度(分辨率) , 可设置为 9, 10, 11 或 12 位, 对应的分辨率温度是 0.5℃, 0.25℃, 0.125℃和 0.0625℃。 R0 和 R1 配置如下图:
在上状态下默认的精度是 12 位, 即 R0=1、 R1=1。高速暂存存储器由 9 个字节组成, 其分配如下:
当温度转换命令(44H) 发布后, 经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。 存储的两个字节, 高字节的前 5 位是符号位 S, 单片机可通过单线接口读到该数据, 读取时低位在前, 高位在后, 数据格式如下:
如果测得的温度大于 0, 这 5 位为‘0’ , 只要将测到的数值乘以 0.0625(默认精度是 12 位) 即可得到实际温度; 如果温度小于 0, 这 5 位为‘1’ , 测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。 温度与数据对应关系如下:
比如我们要计算+85 度, 数据输出十六进制是 0X0550, 因为高字节的高 5位为 0, 表明检测的温度是正温度, 0X0550 对应的十进制为 1360, 将这个值乘以 12 位精度 0.0625, 所以可以得到+85 度。
知道了怎么计算温度, 接下来我们就来看看如何读取温度数据, 由于 DS18B20是单总线器件, 所有的单总线器件都要求采用严格的信号时序, 以保证数据的完整性。 DS18B20 时序包括如下几种: 初始化时序、 写(0 和 1) 时序、 读(0 和1) 时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。 这里我们简单介绍这几个信号的时序:
(1) 初始化时序
单总线上的所有通信都是以初始化序列开始。 主机输出低电平, 保持低电平时间至少 480us(该时间的时间范围可以从 480 到 960 微妙) , 以产生复位脉冲。接着主机释放总线, 外部的上拉电阻将单总线拉高, 延时 15~60 us, 并进入接收模式。 接着 DS18B20 拉低总线 60~240 us, 以产生低电平应答脉冲, 若为低电平, 还要做延时, 其延时的时间从外部上拉电阻将单总线拉高算起最少要 480微妙。 初始化时序图如下:
(2) 写时序
写时序包括写 0 时序和写 1 时序。 所有写时序至少需要 60us, 且在 2 次独立的写时序之间至少需要 1us 的恢复时间, 两种写时序均起始于主机拉低总线。 写 1 时序: 主机输出低电平, 延时 2us, 然后释放总线, 延时 60us。 写 0时序: 主机输出低电平, 延时 60us, 然后释放总线, 延时 2us。 写时序图如下:
(3) 读时序
单总线器件仅在主机发出读时序时, 才向主机传输数据, 所以, 在主机发出读数据命令后, 必须马上产生读时序, 以便从机能够传输数据。 所有读时序至少需要 60us, 且在 2 次独立的读时序之间至少需要 1us 的恢复时间。 每个读时序都由主机发起, 至少拉低总线 1us。 主机在读时序期间必须释放总线, 并且在时序起始后的 15us 之内采样总线状态。 读时序图如下:
典型的读时序过程为: 主机输出低电平延时 2us, 然后主机转入输入模式延时 12us, 然后读取单总线当前的电平, 然后延时 50us。
在了解了单总线时序之后, 我们来看看 DS18B20 的典型温度读取过程, DS18B20 的典型温度读取过程为: 复位→发 SKIP ROM 命令(0XCC) →发开始转换命令(0X44) →延时→复位→发送 SKIP ROM 命令(0XCC) →发读存储器命令(0XBE) →连续读出两个字节数据(即温度)→结束。
到这里我们就介绍完了 DS18B20 , 如需更详细的介绍, 请大家参考“6--芯片资料” 《DS18B20 中文》 数据手册。
26.1.2 实验目的
数码管实时显示温度数值。
26.2 硬件设计
本实验使用到硬件资源如下:
(1) DS18B20 温度传感器
(2) 数码管模块
(3) 串口 1
相关电路在前面章节已经介绍过, 此处省略。 DS18B20 模块电路图如下图所示:
从电路图中可以看到, 该接口可兼容 DS18B20 温度传感器和 DHT11 温湿度传感器, 它们都是单总线接口。 单总线接口连接在芯片的 P47 管脚上, 并且接了一个 10K 的上拉电阻。 通过 IO 管脚模拟单总线时序与 DS18B20 温度传感器通信。一定要注意温度传感器的方向, 在接口处我们已经用丝印画了一个凸起, 所以只需要将温度传感器对应插入即可。 DS18B20 温度传感器接口如下图所示:
26.3 软件设计
26.3.1 创建和配置工程
按照前面章节内容创建一份新工程, 并命名为 23-ds18b20, 如下图所示:
26.3.1.1 GPIO 配置
使能端口和时钟, 数码管端口配置可参考前面章节, 将 UART1 管脚配置为 P30和 P31, P47 管脚设置为准双向口模式等。 如下图所示:
26.3.1.2 配置 UART1
在 UART1 的参数设置界面中, 可选择“与 printf 函数关联” , 这样就可以非常方便的使用 printf 输出调试结果信息。 如下图所示:
26.3.1.3 生成工程
配置完成后, 按下代码生成按钮, 自动创建工程, 系统开始生成初始化代码。生成工程文件目录如下图所示:
在工程文件夹内新建一个 APP 文件夹, 用于存储用户编写的外设驱动, 方便后期工程管理和程序移植。 在 APP 文件夹下将数码管驱动复制进来即可。 在 APP文件夹下新建 ds18b20 文件夹, 并在该文件夹内新建 ds18b20.c 和 ds18b20.h,用于保存 DS18B20 相关驱动。
然后在导出的工程中添加 APP 组, 并将 xx.c 导入到工程组内, 最后添加头文件路径, 否则编译将报错。 如下:
26.3.2 添加用户驱动代码
26.3.2.1 ds18b20.c 文件
#include "ds18b20.h" /******************************************************************************* * 函 数 名 : DS18B20_Reset * 函数功能 : 复位DS18B20 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void DS18B20_Reset(void) { DS18B20_DQ=0; //拉低DQ delay_us(750); //拉低750us DS18B20_DQ=1; //DQ=1 delay_us(15); //15US } /******************************************************************************* * 函 数 名 : DS18B20_Check * 函数功能 : 检测DS18B20是否存在 * 输 入 : 无 * 输 出 : 1:未检测到DS18B20的存在,0:存在 *******************************************************************************/ u8 DS18B20_Check(void) { u8 retry=0; while (DS18B20_DQ&&retry<200) { retry++; delay_us(1); }; if(retry>=200)return 1; else retry=0; while (!DS18B20_DQ&&retry<240) { retry++; delay_us(1); }; if(retry>=240)return 1; return 0; } /******************************************************************************* * 函 数 名 : DS18B20_Read_Bit * 函数功能 : 从DS18B20读取一个位 * 输 入 : 无 * 输 出 : 1/0 *******************************************************************************/ u8 DS18B20_Read_Bit(void) // read one bit { u8 dat; DS18B20_DQ=0; delay_us(2); DS18B20_DQ=1; delay_us(12); if(DS18B20_DQ)dat=1; else dat=0; delay_us(50); return dat; } /******************************************************************************* * 函 数 名 : DS18B20_Read_Byte * 函数功能 : 从DS18B20读取一个字节 * 输 入 : 无 * 输 出 : 一个字节数据 *******************************************************************************/ u8 DS18B20_Read_Byte(void) // read one byte { u8 i,j,dat; dat=0; for (i=0;i<8;i++) { j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat; } /******************************************************************************* * 函 数 名 : DS18B20_Write_Byte * 函数功能 : 写一个字节到DS18B20 * 输 入 : dat:要写入的字节 * 输 出 : 无 *******************************************************************************/ void DS18B20_Write_Byte(u8 dat) { u8 j; u8 testb; for (j=1;j<=8;j++) { testb=dat&0x01; dat=dat>>1; if (testb) { DS18B20_DQ=0;// Write 1 delay_us(2); DS18B20_DQ=1; delay_us(60); } else { DS18B20_DQ=0;// Write 0 delay_us(60); DS18B20_DQ=1; delay_us(2); } } } /******************************************************************************* * 函 数 名 : DS18B20_Start * 函数功能 : 开始温度转换 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void DS18B20_Start(void)// ds1820 start convert { DS18B20_Reset(); DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert } /******************************************************************************* * 函 数 名 : DS18B20_Init * 函数功能 : 初始化DS18B20的IO口 DQ 同时检测DS的存在 * 输 入 : 无 * 输 出 : 1:不存在,0:存在 *******************************************************************************/ u8 DS18B20_Init(void) { DS18B20_Reset(); return DS18B20_Check(); } /******************************************************************************* * 函 数 名 : DS18B20_GetTemperture * 函数功能 : 从ds18b20得到温度值 * 输 入 : 无 * 输 出 : 温度数据 *******************************************************************************/ float DS18B20_GetTemperture(void) { u16 temp; u8 a,b; float value; DS18B20_Start(); // ds1820 start convert DS18B20_Reset(); DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0xbe);// convert a=DS18B20_Read_Byte(); // LSB b=DS18B20_Read_Byte(); // MSB temp=b; temp=(temp<<8)+a; if((temp&0xf800)==0xf800) { temp=(~temp)+1; value=temp*(-0.0625); } else { value=temp*0.0625; } return value; }DS18B20_Init 函数: 该函数带有一个返回值, 如果返回值为 1 表示 DS18B20初始化失败, 返回值为 0 表示初始化成功。 函数返回值其实就是通过调用DS18B20_Check 函数获得, 此函数用来检测 DS18B20 是否存在。 初始化函数内还调用了 DS18B20_Reset 函数, 这两个函数其实就是根据前面介绍的初始化时序编写。
DS18B20_GetTemperture 函数: 该函数首先调用了 DS18B20_Start 函数用来开始 DS18B20 的温度转换。 最终将 2 个字节的温度数据读取出来, 判断最高字节的高 5 位是否为 0, 如果为 0 表明读取的温度值为正温度, 直接乘以 0.0625 即可, 否则为负温度, 需取反后加 1 再乘以 0.0625
26.3.2.2 main.c 文件
//<<AICUBE_USER_HEADER_REMARK_BEGIN>> /* 深圳市普中科技有限公司(PRECHIN 普中) * 在线视频:https://space.bilibili.com/2146492485/video 官网:www.prechin.cn * 实验名称:DS18B20温度传感器实验 * * 接线说明:参考电路图 * * 实验现象:程序下载成功后,数码管显示温度数值 * * 注意事项: * */ //<<AICUBE_USER_HEADER_REMARK_END>> #include "config.h" //默认已包含stdio.h、intrins.h、ai_usb.h等头文件 //<<AICUBE_USER_INCLUDE_BEGIN>> // 在此添加用户头文件包含 #include "smg.h" #include "ds18b20.h" //<<AICUBE_USER_INCLUDE_END>> //<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>> // 在此添加用户全局变量定义、用户宏定义以及函数声明 //<<AICUBE_USER_GLOBAL_DEFINE_END>> //////////////////////////////////////// // 项目主函数 // 入口参数: 无 // 函数返回: 无 //////////////////////////////////////// void main(void) { //<<AICUBE_USER_MAIN_INITIAL_BEGIN>> // 在此添加用户主函数初始化代码 u8 i=0; u8 buf[4]; int temper; //<<AICUBE_USER_MAIN_INITIAL_END>> SYS_Init(); printf("Hello World !\n"); //<<AICUBE_USER_MAIN_CODE_BEGIN>> // 在此添加主函数中运行一次的用户代码 while(DS18B20_Init()) { printf("DS18B20检测失败,请插好!\r\n"); delay_ms(500); } printf("DS18B20检测成功!\r\n"); //<<AICUBE_USER_MAIN_CODE_END>> while (1) { //<<AICUBE_USER_MAIN_LOOP_BEGIN>> // 在此添加主函数中用户主循环代码 i++; if(i%50==0) { temper=DS18B20_GetTemperture()*10; if(temper<0) { printf("检测的温度为:-"); } else { printf("检测的温度为: "); } printf("%.1f°C\r\n",(float)temper/10); } buf[0]=gsmg_code[temper/100]; buf[1]=gsmg_code[temper%100/10]|0x80; buf[2]=gsmg_code[temper%100%10]; buf[3]=0x39; SMG_Display(buf,5); delay_ms(10); //<<AICUBE_USER_MAIN_LOOP_END>> } }主函数相对比较简单, 初始化相关外设, 然后调用我们前面编写的DS18B20_Init 函数用来检测 DS18B20 温度传感器是否成功, 若初始化成功, 打印输出“DS18B20 检测成功!” 提示信息, 否则一直循环打印输出“DS18B20 检测失败, 请插好!” , 初始化成功后进入 while 循环, 间隔 500ms 读取一次温度,并将温度打印输出, 将温度数据显示在数码管上。
26.4 实验现象
将程序编译下载到目标板运行, 打开串口助手“\5--开发工具\5-串口调试助手\串口调试助手(丁丁) \sscom5.13.1.exe” , 实验现象: 数码管实时显示温度数值。