打印
[AT32F423]

【AT-START-F423测评】温度采集

[复制链接]
1599|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
     承接上期的环境搭建篇,今儿周末,来测测此时南方室内的温度。体感不是很冷,不知道具体的温度值,赶紧用一个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模块,则温度值回落。串口打印效果如下:

使用特权

评论回复
沙发
pl202| | 2023-12-7 21:56 | 只看该作者
DS18B20内置了64位产品序列号,这方便了多个DS18B20传感器的身份识别。通过64位身份验证,可以分别读取来自不同传感器的温度信息。

使用特权

评论回复
板凳
geraldbetty| | 2023-12-8 09:39 | 只看该作者
DS18B20的驱动编程需要对时序有精确的控制

使用特权

评论回复
地板
sesefadou| | 2023-12-8 15:30 | 只看该作者
DS18B20与微处理器间采用串行数据传送,必须严格的保证读写时序,否则将无法读取测温结果。

使用特权

评论回复
5
timfordlare| | 2023-12-8 20:43 | 只看该作者
DS18B20是一款单总线器件,其读写数据依赖于严格的时序控制。

使用特权

评论回复
6
saservice| | 2023-12-8 21:12 | 只看该作者
如果时序控制不当,可能导致无法正常驱动DS18B20读取环境温度。

使用特权

评论回复
7
timfordlare| | 2023-12-8 21:47 | 只看该作者
在主机发送复位信号后,要将IO口切换为浮空输入模式

使用特权

评论回复
8
bestwell| | 2023-12-9 14:20 | 只看该作者
DS18B20虽然具有测温系统简单、测温精度高、连接方便、占用口线少等优点

使用特权

评论回复
9
sanfuzi| | 2023-12-9 15:38 | 只看该作者
当单片机需要给DS18B20写入一个0时,需要将单片机引脚拉低,保持低电平时间在60~120us之间,然后释放总线。当需要写入一个1时,需要将单片机引脚拉低,拉低时间需要大于1us,然后在15us内拉高总线。

使用特权

评论回复
10
pmp| | 2023-12-9 22:25 | 只看该作者
单总线协议对延时要求比较严格              

使用特权

评论回复
11
pattywu| | 2023-12-9 22:28 | 只看该作者
用PT100铂电阻, 电阻分压, ADC采样, 分分钟搞定, 精度还高.

使用特权

评论回复
12
earlmax| | 2023-12-9 22:39 | 只看该作者
在读取温度之前,需要跳过ROM命令,这是因为ROM命令用于区分多个单总线器件。在单总线通信中,可以直接写一个跳过ROM的命令。

使用特权

评论回复
13
jtracy3| | 2023-12-10 15:35 | 只看该作者
最高12位分辨率,精度可达±0.5摄氏度

使用特权

评论回复
14
forgot| | 2023-12-11 08:23 | 只看该作者
DS18B20是一款单总线器件,其读写数据依赖于严格的时序控制。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

1021

帖子

7

粉丝