承接上期的环境搭建篇,今儿周末,来测测此时南方室内的温度。体感不是很冷,不知道具体的温度值,赶紧用一个DS18B20简单模块来检测一下。
众所周知,DS18B20是一种单总线数字温度传感器,测试温度范围-55℃-125℃,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。单总线,意味着没有时钟线,只有一根通信线。单总线读写数据是靠控制起始时间和采样时间来完成,所以时序要求很严格,这也是DS18B20驱动编程的难点。
温度寄存器结构图展示如下:
温度寄存器由两个字节组成,分为低8位和高8位。一共16位。其中,第0位到第3位,存储的是温度值的小数部分。第4位到第10位存储的是温度值的整数部分。第11位到第15位为符号位。全0表示是正温度,全1表示是负温度。表格中的数值,如果相应的位为1,表示存在。如果相应的位为0,表示不存在。
DS18B20的单总线协议初始化过程中的复位与应答脉冲时序图。
初始化时序包括:主机发出的复位脉冲和从机发出的应答脉冲。主机通过拉低单总线480-960μs产生复位脉冲;然后由主机释放总线,并进入接收模式。主机释放总线时,会产生一由低电平跳变为高电平的上升沿,单总线器件检测到该上升沿后,延时15~60μs,接着单总线器件通过拉低总线60~240μsμ来产生应答脉冲。主机接收到从机的以应答脉冲后,说明有单总线器件在线,到此初始化完成。然后主机就可以开始对从机进行ROM命令和功能命令操作。
DS18B20的写时序图如下:
写时隙:当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。有两种写时间隙:写1的时间隙和写0时间隙。所有写时间隙必须最少持续60us,包括两个写周期间至少1us的恢复时间。DQ引脚上的电平变低后,DS18B20在一个15us到60us的时间窗口内对DQ引脚采样。如果DQ引脚是高电平,就是写1,如果DQ引脚是低电平,就是写0。主机要生成一个写1时间隙,必须把数据线拉到低电平然后释放,在写时间隙开始后的15us内允许数据线拉到高电平。主机要生成一个写0时间隙,必须把数据线拉到低电平并保持60us。
DS18B20的读时序图如下:
当主机把总线从高电平拉低,并保持至少1us后释放总线;并在15us内读取从DS18B20输出的数据。
硬件电路上的连线如下图所示,DS18B20的DQ数据脚与开发板上的GPIOD3引脚相连接。
部分的参考代码展示如下:
#include "at32f423_board.h"
#include "at32f423_clock.h"
#include "ds18b20.h"
int main(void)
{
uint8_t i, DS18B20ID[8];
char str[50];
uint8_t t = 0;
float temperature;
system_clock_config();
at32_board_init();
uart_print_init(115200);
while (DS18B20_Init()) /* DS18B20初始化 */
{
printf("DS18B20 Init Error\r\n");
delay_ms(1000);
}
DS18B20_ReadId(DS18B20ID);
printf("DS18B20的序列号是: 0x");
for ( i = 0; i < 8; i ++ )
printf ( "%.2X", DS18B20ID[i]);
printf("\n");
sprintf(str,"DS18B20的序列号是:0x%02X%02X%02X%02X%02X%02X%02X%02X",DS18B20ID[0],DS18B20ID[1],DS18B20ID[2],DS18B20ID[3],
DS18B20ID[4],DS18B20ID[5],DS18B20ID[6],DS18B20ID[7]);
printf("DS18B20 OK\r\n");
while(1)
{
temperature=DS18B20_GetTemp_MatchRom(DS18B20ID);
/* 打印通过 DS18B20 序列号获取的温度值 */
printf("获取该序列号器件的温度:%.1f\n",temperature);
sprintf(str,"当前温度值为:%0.3f℃",temperature);
delay_ms(800);
t++;
if (t == 100)
{
t = 0;
at32_led_toggle(LED2);
delay_ms(10);
at32_led_toggle(LED3);
delay_ms(10);
at32_led_toggle(LED4);
delay_ms(10);
}
}
}
#include "at32f423_board.h"
#include "at32f423_clock.h"
#include "ds18b20.h"
static void DS18B20_GPIO_Config(void);
static void DS18B20_Mode_IPU(void);
static void DS18B20_Mode_Out_PP(void);
static void DS18B20_Rst(void);
static uint8_t DS18B20_Presence(void);
static uint8_t DS18B20_ReadBit(void);
static uint8_t DS18B20_ReadByte(void);
static void DS18B20_WriteByte(uint8_t dat);
static void DS18B20_SkipRom(void);
static void DS18B20_MatchRom(void);
uint8_t DS18B20_Init(void)
{
DS18B20_GPIO_Config ();
DS18B20_DQ_1;
DS18B20_Rst();
return DS18B20_Presence ();
}
static void DS18B20_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
gpio_init_type GPIO_InitStructure;
/*开启DS18B20_DQ_GPIO_PORT的外设时钟*/
DS18B20_DQ_SCK_APBxClock_FUN ( DS18B20_DQ_GPIO_CLK, TRUE);
/*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/
GPIO_InitStructure.gpio_pins = DS18B20_DQ_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.gpio_mode = GPIO_MODE_OUTPUT;
/*设置引脚速率为50MHz */
GPIO_InitStructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
/*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
gpio_init(DS18B20_DQ_GPIO_PORT, &GPIO_InitStructure);
}
static void DS18B20_Rst(void)
{
/* 主机设置为推挽输出 */
DS18B20_Mode_Out_PP();
DS18B20_DQ_0;
/* 主机至少产生480us的低电平复位信号 */
delay_us(750);
/* 主机在产生复位信号后,需将总线拉高 */
DS18B20_DQ_1;
/*从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲*/
delay_us(15);
}
static uint8_t DS18B20_ReadBit(void)
{
uint8_t dat;
/* 读0和读1的时间至少要大于60us */
DS18B20_Mode_Out_PP();
/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
DS18B20_DQ_0;
delay_us(10);
/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
DS18B20_Mode_IPU();
//delay_us(2);
if( DS18B20_DQ_IN() == SET )
dat = 1;
else
dat = 0;
/* 这个延时参数请参考时序图 */
delay_us(45);
return dat;
}
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;
}
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);
}
}
}
static void DS18B20_SkipRom ( void )
{
DS18B20_Rst();
DS18B20_Presence();
DS18B20_WriteByte(0XCC); /* 跳过 ROM */
}
static void DS18B20_MatchRom ( void )
{
DS18B20_Rst();
DS18B20_Presence();
DS18B20_WriteByte(0X55); /* 匹配 ROM */
}
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;
}
void DS18B20_ReadId ( uint8_t * ds18b20_id )
{
uint8_t uc;
DS18B20_WriteByte(0x33); //读取序列号
for ( uc = 0; uc < 8; uc ++ )
ds18b20_id [ uc ] = DS18B20_ReadByte();
}
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;
}
#ifndef __ONEWIRE_DS18B20_H__
#define __ONEWIRE_DS18B20_H__
#include "at32f423_board.h"
#include "at32f423_clock.h"
/************************** DS18B20 连接引脚定义****************************/
#define DS18B20_DQ_SCK_APBxClock_FUN crm_periph_clock_enable
#define DS18B20_DQ_GPIO_CLK CRM_GPIOD_PERIPH_CLOCK
#define DS18B20_DQ_GPIO_PORT GPIOD
#define DS18B20_DQ_GPIO_PIN GPIO_PINS_3
/************************** 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 /* __ONEWIRE_DS18B20_H__ */
编译完成后,下载到开发板中,检测过程中,使用手指靠近DS18B20模块,则温度会随之增加,手指离开DS18B20模块,则温度值回落。串口打印效果如下:
|