关键词:温度传感器,DS18B20
调试环境:系统使用了 RT-Thread V4.0.2 ,编译器:RT-Thread Studio
说明:本笔记原始记录是在2020年完成, RT-Thread Studio 已经更新过多版,界面上可能对不上。
文档介绍:文档介绍使用 RT-Thread Studio 创建的 RT-Thread V4.0.2 RTOS 工程,并在工程中添加 DS18B20 的驱动软件包,使用软件包读取 DS18B20 的温度。
目录
1.利用系统配置功能,添加 DS18B20 的软件包
添加软件包
添加后选择保存,编译,看是否有错误
回到编译器,点击编译,增加的源码文件夹会自动出现在项目的目录里面
2.DS18B20源码修改
ds18b20_sample.c ,文件修改 IC 对应的IO;
找到如下代码,修改即可
/* Modify this pin according to the actual wiring situation */
#define DS18B20_DATA_PIN GET_PIN(A, 12)
修改后编译,可能会出现头文件包含的问题。
由于我在项目中有个总的头文件,如下,所以新建了个头文件 ds18b20_sample.h ,里面只有个线程的声明。
#include "user_cfg.h"
//#include <stdlib.h>
//#include <rtthread.h>
//#include "sensor.h"
//#include "sensor_dallas_ds18b20.h"
/* Modify this pin according to the actual wiring situation */
#define DS18B20_DATA_PIN GET_PIN(A, 12)
如果出现找不到头文件的情况,按如下流程设置,右键点击项目,选择属性
3.调试DS18B20源码
DS18B20 源码修改 , ds18b20_sample.c
修改源码后,通过串口看收到的温度数据始终是 0 ;
通过逻辑分析仪器抓了 IC 的数据管脚的数据,发现IC有发出正确的温度数据,但是单片机没能准确读取到数据。
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-07-24 WillianChan the first version
*/
#include "user_cfg.h"
//#include <stdlib.h>
//#include <rtthread.h>
//#include "sensor.h"
//#include "sensor_dallas_ds18b20.h"
/* Modify this pin according to the actual wiring situation */
#define DS18B20_DATA_PIN GET_PIN(A, 12) /* YLTEST IC于MCU连接的IO 配置 */
static void read_temp_entry(void *parameter)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data sensor_data;
rt_size_t res;
dev = rt_device_find(parameter);
if (dev == RT_NULL)
{
rt_kprintf("Can't find device:%s\n", parameter);
return;
}
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device failed!\n");
return;
}
rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100);
while (1)
{
res = rt_device_read(dev, 0, &sensor_data, 1);
if (res != 1)
{
rt_kprintf("read data failed!size is %d\n", res);
rt_device_close(dev);
return;
}
else
{
if (sensor_data.data.temp >= 0)
{
rt_kprintf("temp:%3d.%dC, timestamp:%5d\n",
sensor_data.data.temp / 10,
sensor_data.data.temp % 10,
sensor_data.timestamp);
}
else
{
rt_kprintf("temp:-%2d.%dC, timestamp:%5d\n",
abs(sensor_data.data.temp / 10),
abs(sensor_data.data.temp % 10),
sensor_data.timestamp);
}
}
rt_thread_mdelay(2000); /* YLTEST 调整了传感器的读取间隔为 2秒 */
}
}
int ds18b20_read_temp_sample(void)
{
rt_thread_t ds18b20_thread;
ds18b20_thread = rt_thread_create("18b20tem",
read_temp_entry,
"temp_ds18b20",
1024,
10, /* YLTEST 调整了线程的优先级*/
50); /* YLTEST 调整了时间片大小 */
if (ds18b20_thread != RT_NULL)
{
rt_thread_startup(ds18b20_thread);
}
return RT_EOK;
}
//INIT_APP_EXPORT(ds18b20_read_temp_sample); /* YLTEST线程自动初始化代码注销,改成了在main函数添加线程运行代码。*/
static int rt_hw_ds18b20_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.user_data = (void *)DS18B20_DATA_PIN;
rt_hw_ds18b20_init("ds18b20", &cfg);
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_ds18b20_port);
代码跟踪调试
调试发现读取温度的函数进入了这个文件 sensor_dallas_ds18b20.c
如下代码是读取温度传感器发出温度数据的函数,调试发现 存储温度的变量 TL,TH 读出的数据始终是 255;
int32_t ds18b20_get_temperature(rt_base_t pin)
{
uint8_t TL, TH;
int32_t tem;
ds18b20_start(pin);
ds18b20_init(pin);
ds18b20_write_byte(pin, 0xcc);
ds18b20_write_byte(pin, 0xbe);
TL = ds18b20_read_byte(pin); /* LSB first */
TH = ds18b20_read_byte(pin);
if (TH > 7)
{
TH =~ TH;
TL =~ TL;
tem = TH;
tem <<= 8;
tem += TL;
tem = (int32_t)(tem * 0.0625 * 10 + 0.5);
return -tem;
}
else
{
tem = TH;
tem <<= 8;
tem += TL;
tem = (int32_t)(tem * 0.0625 * 10 + 0.5);
return tem;
}
}
进一步跟进如下函数
TL = ds18b20_read_byte(pin); /* LSB first */
这个函数是 读取 温度传感器发出的温度数据,读 1 个字节的函数。
调试发现,读字节函数中变量 dat 数据也每次都是 255 ,所以问题还在函数内部调用的函数
j = ds18b20_read_bit(pin); 出了问题
static uint8_t ds18b20_read_byte(rt_base_t pin)
{
uint8_t i, j, dat;
dat = 0;
for (i = 1; i <= 8; i++)
{
j = ds18b20_read_bit(pin);
dat = (j << 7) | (dat >> 1);
}
return dat;
}
如下标记 //YLTEST 的代码是我修改了时间的部分,按这些数据修改后,函数就能够准确读到温度传感器的温度数据。
static uint8_t ds18b20_read_bit(rt_base_t pin)
{
uint8_t data;
rt_pin_mode(pin, PIN_MODE_OUTPUT);
rt_pin_write(pin, PIN_LOW);
rt_hw_us_delay(1);//YLTEST
rt_pin_write(pin, PIN_HIGH);
rt_pin_mode(pin, PIN_MODE_INPUT);
/* maybe 12us, maybe 5us, whatever...I have no idea */
rt_hw_us_delay(2);//YLTEST
if(rt_pin_read(pin))
data = 1;
else
data = 0;
rt_hw_us_delay(30);//YLTEST
return data;
}
4.调试总结
1:解决问题主要还是 逻辑分析仪 帮了大忙,逻辑分析仪抓到了数据,并按 1 WIRE 准确解析了协议,再根据 IC 的协议文档,看明白了协议格式和数据的含义,通过手工算了 温度数据,发现温度是准确的。这也缩小了问题点的范围,让我能够准确的知道问题就是读取温度数据这部分出的问题。如下图箭头指的部分。
2:IC 规格书中读数据的时序介绍
3:网络上查资料,关于 1-wire 协议的讲解
解析单总线协议(1-wire)_zhengqijun_的博客-CSDN博客
4:逻辑分析仪抓包的时间分析
通过下图发现,源码的时序设置已经超过了 IC 的时序要求,所以导致MCU没能按准确的时序读到数据。按前面代码中的方式修改时序后,MCU读取数据正常。
5.问题分析
1:软件包出现这样的时序问题,有可能是我的开发板使用的是内部晶振,导致实际延时偏慢。
2:目前没有验证是不是和晶振时钟配置相关。后续有空,再调整一下单片机时钟看看。
3:经过测试发现,上次测试的代码是用的系统内部默认时钟,改成外部8M晶振,72M主频后,再用原来的时间设置,读取数据正常。 2020/06/13 21:56