【AT-START-M412测评】+ ③驱动DS18B20温度采集
本帖最后由 yinwuqing110 于 2025-6-19 21:28 编辑温度传感器有多种,采用IIC通讯的方式的比较多,而在IO口资源有限的单片机设计中,使用DS18B20的仍然比较常见。之所以DS18B20不会退出市场,不少教学开发板上仍保留它的一席之地,是因为它的驱动方式只需要一根线,也就是一个IO口即能完成数据的精准采集,而且灵敏度不输IIC通讯方式的温敏传感器。“一线式”通讯方式,资源占用少,但驱动起来其实比IIC通讯要求更苛刻,尤其是时序上的要求。今天,咱们使用AT-START-M412驱动DS18B20模块,温度值显示在LCD屏上,并通过串口打印相关信息。当用手握DS18B20模块给传感器加温,采集温度瞬间增加;挪开手,则采集的温度值明显下降。
首先来认识一下DS18B20模块,实物图如下:
由上可知,除了给模块正常上电外,只需一个DQ引脚,完成数字信号的输入输出,读写时序需要严格按照官方给定的数据手册来。
DS18B20可以测量-55℃至+125℃的温度范围,通常采用外部供电方式,并在数据线上并联一个4.7k的上拉电阻,以增强数据的抗干扰能力。
为了高效得驱动DS18B20,先来阅读一下数据手册。
其中比较重要的是读写时序,该数据手册中提供了关于驱动DS18B20的读写时序图,如下图所示:
解读控制时序:
①、初始化时序:与DS18B20的所有通信都由初始化时序开始,该时序包括从主设备发出的复位脉冲及从DS18B20响应的存在脉冲。当DS18B20响应复位信号的存在脉冲后,表明其在总线上并且已经准备好进行操作。
②、读时序:读时序包括读1时段和读0时段。主设备通过读1时段来读取DS18B20中的逻辑1,通过读0时段来读取逻辑0。每个读时段最小必须有60us的持续时间,并且独立的读时段间至少有1us的恢复时间。
③、写时序:写时序包括写1时段和写0时段。主设备通过写1时段向DS18B20中写入逻辑1,通过写0时段写入逻辑0。每个写时段最小必须有60us的持续时间,并且独立的写时段间至少有1us的恢复时间。
解读读取温度流程:
①、对DS18B20进行复位操作。
②、发送开始转换指令(指令值:0x44)。
③、再次进行复位操作。
④、等待DS18B20应答。
⑤、发送读取温度指令。
⑥、读取16位的数据(有效位最大为12位)。
经过上述了解,接下来,咱们编写相关的驱动代码,源码展示见如下。
DS18B20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "at32m412_416_board.h"
/************************** DS18B20 连接引脚定义********************************/
#define DS18B20_DQ_GPIO_CLK CRM_GPIOB_PERIPH_CLOCK
#define DS18B20_DQ_GPIO_PORT GPIOB
#define DS18B20_DQ_GPIO_PIN GPIO_PINS_12
/************************** DS18B20 函数宏定义********************************/
#define DS18B20_DQ_0 gpio_bits_reset(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
#define DS18B20_DQ_1 gpio_bits_set(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
#define DS18B20_DQ_IN() gpio_input_data_bit_read(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
/************************** DS18B20 函数声明 ********************************/
uint8_t DS18B20_Init(void);
void DS18B20_ReadId( uint8_t * ds18b20_id);
float DS18B20_GetTemp_SkipRom(void);
float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id);
#endif DS18B20.c
#include "at32m412_416_board.h"
#include "at32m412_416_clock.h"
#include "DS18B20.h"
/**
* 函数功能: DS18B20 初始化函数
* 输入参数: 无
* 返 回 值: 0:初始化成功,检测到传感器,1:初始化失败
* 说 明:无
*/
uint8_t DS18B20_Init(void)
{
DS18B20_GPIO_Config ();
DS18B20_DQ_1;
DS18B20_Rst();
return DS18B20_Presence ();
}
/**
* 函数功能: 配置DS18B20用到的I/O口
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_GPIO_Config(void)
{
gpio_init_type gpio_init_structure;
crm_periph_clock_enable(DS18B20_DQ_GPIO_CLK, TRUE);
gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;
gpio_init_structure.gpio_out_type= GPIO_OUTPUT_PUSH_PULL;
gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(DS18B20_DQ_GPIO_PORT , &gpio_init_structure);
}
/**
* 函数功能: 使DS18B20-DATA引脚变为输入模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_Mode_IPU(void)
{
gpio_init_type gpio_init_structure;
gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;
gpio_init_structure.gpio_pull= GPIO_PULL_NONE;
gpio_init_structure.gpio_mode = GPIO_MODE_INPUT;
gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
}
/**
* 函数功能: 使DS18B20-DATA引脚变为输出模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_Mode_Out_PP(void)
{
gpio_init_type gpio_init_structure;
gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;
gpio_init_structure.gpio_out_type= GPIO_OUTPUT_PUSH_PULL;
gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
}
/**
* 函数功能: 主机给从机发送复位脉冲
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_Rst(void)
{
DS18B20_Mode_Out_PP();
DS18B20_DQ_0;
delay_us(750);
DS18B20_DQ_1;
delay_us(15);
}
/**
* 函数功能: 检测从机给主机返回的存在脉冲
* 输入参数: 无
* 返 回 值: 0:成功,1:失败
* 说 明:无
*/
static uint8_t DS18B20_Presence(void)
{
uint8_t pulse_time = 0;
DS18B20_Mode_IPU();
while( DS18B20_DQ_IN() && pulse_time<100 )
{
pulse_time++;
delay_us(1);
}
if( pulse_time >=100 )
return 1;
else
pulse_time = 0;
while( !DS18B20_DQ_IN() && pulse_time<240 )
{
pulse_time++;
delay_us(1);
}
if( pulse_time >=240 )
return 1;
else
return 0;
}
/**
* 函数功能: 从DS18B20读取一个bit
* 输入参数: 无
* 返 回 值: 读取到的数据
* 说 明:无
*/
static uint8_t DS18B20_ReadBit(void)
{
uint8_t dat;
DS18B20_Mode_Out_PP();
DS18B20_DQ_0;
delay_us(10);
DS18B20_Mode_IPU();
if( DS18B20_DQ_IN() == SET )
dat = 1;
else
dat = 0;
delay_us(45);
return dat;
}
/**
* 函数功能: 从DS18B20读一个字节,低位先行
* 输入参数: 无
* 返 回 值: 读到的数据
* 说 明:无
*/
static uint8_t DS18B20_ReadByte(void)
{
uint8_t i, j, dat = 0;
for(i=0; i<8; i++)
{
j = DS18B20_ReadBit();
dat = (dat) | (j<<i);
}
return dat;
}
/**
* 函数功能: 写一个字节到DS18B20,低位先行
* 输入参数: dat:待写入数据
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_WriteByte(uint8_t dat)
{
uint8_t i, testb;
DS18B20_Mode_Out_PP();
for( i=0; i<8; i++ )
{
testb = dat&0x01;
dat = dat>>1;
/* 写0和写1的时间至少要大于60us */
if (testb)
{
DS18B20_DQ_0;
/* 1us < 这个延时 < 15us */
delay_us(8);
DS18B20_DQ_1;
delay_us(58);
}
else
{
DS18B20_DQ_0;
/* 60us < Tx 0 < 120us */
delay_us(70);
DS18B20_DQ_1;
/* 1us < Trec(恢复时间) < 无限大*/
delay_us(2);
}
}
}
/**
* 函数功能: 跳过匹配 DS18B20 ROM
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_SkipRom ( void )
{
DS18B20_Rst();
DS18B20_Presence();
DS18B20_WriteByte(0XCC); /* 跳过 ROM */
}
/**
* 函数功能: 执行匹配 DS18B20 ROM
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DS18B20_MatchRom ( void )
{
DS18B20_Rst();
DS18B20_Presence();
DS18B20_WriteByte(0X55);
}
/**
* 函数功能: 在跳过匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: 无
* 返 回 值: 温度值
* 说 明:无
*/
float DS18B20_GetTemp_SkipRom ( void )
{
uint8_t tpmsb, tplsb;
short s_tem;
float f_tem;
DS18B20_SkipRom ();
DS18B20_WriteByte(0X44); /* 开始转换 */
DS18B20_SkipRom ();
DS18B20_WriteByte(0XBE); /* 读温度值 */
tplsb = DS18B20_ReadByte();
tpmsb = DS18B20_ReadByte();
s_tem = tpmsb<<8;
s_tem = s_tem | tplsb;
if( s_tem < 0 ) /* 负温度 */
f_tem = (~s_tem+1) * 0.0625;
else
f_tem = s_tem * 0.0625;
return f_tem;
}
/**
* 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: ds18b20_id:用于存放 DS18B20 序列号的数组的首地址
* 返 回 值: 无
* 说 明:无
*/
void DS18B20_ReadId ( uint8_t * ds18b20_id )
{
uint8_t uc;
DS18B20_WriteByte(0x33); //读取序列号
for ( uc = 0; uc < 8; uc ++ )
ds18b20_id [ uc ] = DS18B20_ReadByte();
}
/**
* 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
* 输入参数: ds18b20_id:存放 DS18B20 序列号的数组的首地址
* 返 回 值: 温度值
* 说 明:无
*/
float DS18B20_GetTemp_MatchRom ( uint8_t * ds18b20_id )
{
uint8_t tpmsb, tplsb, i;
short s_tem;
float f_tem;
DS18B20_MatchRom (); //匹配ROM
for(i=0;i<8;i++)
DS18B20_WriteByte ( ds18b20_id [ i ] );
DS18B20_WriteByte(0X44); //开始转换
DS18B20_MatchRom (); //匹配ROM
for(i=0;i<8;i++)
DS18B20_WriteByte ( ds18b20_id [ i ] );
DS18B20_WriteByte(0XBE); //读温度值
tplsb = DS18B20_ReadByte();
tpmsb = DS18B20_ReadByte();
s_tem = tpmsb<<8;
s_tem = s_tem | tplsb;
if( s_tem < 0 ) /* 负温度 */
f_tem = (~s_tem+1) * 0.0625;
else
f_tem = s_tem * 0.0625;
return f_tem;
}
main.c
/* includes */
#include "at32m412_416_board.h"
#include "at32m412_416_clock.h"
#include "lcd_st7735.h"
#include "DS18B20.h"
int main(void)
{
uint8_t i, DS18B20ID;
char str;
float temperature;
system_clock_config();
at32_board_init();
uart_print_init(115200);
LcdInit();
printf("DS18B20温度传感器信息读取\n");
LcdFill(0,0,128,160,BLACK);
LcdShowString(2,24,"DS18B20",RED, BLACK,16);
LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
LcdShow16x16Hz(18, 48, 15, YELLOW, BLACK);
LcdShow16x16Hz(34, 48, 16, YELLOW, BLACK);
LcdShow16x16Hz(50, 48, 17, YELLOW, BLACK);
LcdShow16x16Hz(66, 48, 18, YELLOW, BLACK);
delay_ms(2000);
while(DS18B20_Init())
{
printf("DS18B20温度传感器不存在\n");
LcdFill(0,0,128,160,BLACK);
LcdShowString(2,24,"DS18B20",RED, BLACK,16);
LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
LcdShow16x16Hz(18, 48, 19, YELLOW, BLACK);
LcdShow16x16Hz(34, 48, 20, YELLOW, BLACK);
LcdShow16x16Hz(50, 48, 21, YELLOW, BLACK);
delay_ms(1000);
}
printf("检测到DS18B20温度传感器,并初始化成功\n");
LcdFill(0,0,128,160,BLACK);
LcdShow16x16Hz(2, 24, 22, YELLOW, BLACK);
LcdShow16x16Hz(18, 24, 23, YELLOW, BLACK);
LcdShow16x16Hz(34, 24, 24, YELLOW, BLACK);
LcdShowString(50,24,"DS18B20",RED, BLACK,16);
LcdShow16x16Hz(108, 24, 10, YELLOW, BLACK);
LcdShow16x16Hz(2, 48, 11, YELLOW, BLACK);
LcdShow16x16Hz(18, 48, 12, YELLOW, BLACK);
LcdShow16x16Hz(34, 48, 13, YELLOW, BLACK);
LcdShow16x16Hz(50, 48, 14, YELLOW, BLACK);
LcdShowString(66,48,",",YELLOW, BLACK,16);
LcdShow16x16Hz(74, 48, 25, YELLOW, BLACK);
LcdShow16x16Hz(90, 48, 26, YELLOW, BLACK);
LcdShow16x16Hz(106, 48, 27, YELLOW, BLACK);
LcdShow16x16Hz(2, 72, 28, YELLOW, BLACK);
LcdShow16x16Hz(18, 72, 29, YELLOW, BLACK);
LcdShow16x16Hz(34, 72, 30, YELLOW, BLACK);
DS18B20_ReadId(DS18B20ID);
printf("DS18B20的序列号是: 0x");
for ( i = 0; i < 8; i ++ )
printf ( "%.2X", DS18B20ID);
printf("\n");
sprintf(str,"0x%02X%02X%02X%02X%02X%02X%02X%02X",DS18B20ID,DS18B20ID,DS18B20ID,DS18B20ID,
DS18B20ID,DS18B20ID,DS18B20ID,DS18B20ID);
LcdShowString(8,96,"DS18B20",RED, BLACK,12);
LcdShow16x16Hz(50, 96, 32, YELLOW, BLACK);
LcdShow16x16Hz(66, 96, 33, YELLOW, BLACK);
LcdShow16x16Hz(82, 96, 34, YELLOW, BLACK);
LcdShow16x16Hz(98, 96, 35, YELLOW, BLACK);
LcdShow16x16Hz(114, 96, 40, YELLOW, BLACK);
LcdShowString(8,120,str,RED,BLACK,12);
delay_ms(2000);
while(1)
{
temperature=DS18B20_GetTemp_MatchRom(DS18B20ID);
/* 打印通过 DS18B20 序列号获取的温度值 */
printf("获取该序列号器件的温度:%.1f\n",temperature);
LcdFill(0,0,128,160,BLACK);
LcdShow16x16Hz(2, 8, 36, YELLOW, BLACK);
LcdShow16x16Hz(18, 8, 37, YELLOW, BLACK);
LcdShow16x16Hz(34, 8, 10, YELLOW, BLACK);
LcdShow16x16Hz(50, 8, 11, YELLOW, BLACK);
LcdShow16x16Hz(66, 8, 38, YELLOW, BLACK);
LcdShow16x16Hz(82, 8, 39, YELLOW, BLACK);
LcdShow16x16Hz(98, 8, 40, YELLOW, BLACK);
LCD_ShowFloatNum1(8,32,temperature,4,RED, BLACK,24);
LcdShow16x16Hz(70, 36, 41, RED, BLACK);
/* 0.5s 读取一次温度值 */
delay_ms(500);
}
} 以上代码均在上一个驱动LCD工程上完成,因此直接下载到开发板后,可以在LCD屏上直观得看到DS18B20实时采集的温度值变化。当然也是支持串口同步打印输出温度值变化的。
例如串口打印信息:
一段实时操作演示见B站视频:https://www.bilibili.com/video/BV1YFNnzrENG/
https://www.bilibili.com/video/BV1YFNnzrENG/
页:
[1]