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

[复制链接]
2419|13
 楼主| yinwuqing110 发表于 2023-11-26 22:29 | 显示全部楼层 |阅读模式
     承接上期的环境搭建篇,今儿周末,来测测此时南方室内的温度。体感不是很冷,不知道具体的温度值,赶紧用一个DS18B20简单模块来检测一下。
     众所周知,DS18B20是一种单总线数字温度传感器,测试温度范围-55℃-125℃,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。单总线,意味着没有时钟线,只有一根通信线。单总线读写数据是靠控制起始时间和采样时间来完成,所以时序要求很严格,这也是DS18B20驱动编程的难点。
     温度寄存器结构图展示如下:
温度寄存器结构图.png
     温度寄存器由两个字节组成,分为低8位和高8位。一共16位。其中,第0位到第3位,存储的是温度值的小数部分。第4位到第10位存储的是温度值的整数部分。第11位到第15位为符号位。全0表示是正温度,全1表示是负温度。表格中的数值,如果相应的位为1,表示存在。如果相应的位为0,表示不存在。
     DS18B20的单总线协议初始化过程中的复位与应答脉冲时序图。
协议时序图.png
       初始化时序包括:主机发出的复位脉冲和从机发出的应答脉冲。主机通过拉低单总线480-960μs产生复位脉冲;然后由主机释放总线,并进入接收模式。主机释放总线时,会产生一由低电平跳变为高电平的上升沿,单总线器件检测到该上升沿后,延时15~60μs,接着单总线器件通过拉低总线60~240μsμ来产生应答脉冲。主机接收到从机的以应答脉冲后,说明有单总线器件在线,到此初始化完成。然后主机就可以开始对从机进行ROM命令和功能命令操作。
      DS18B20的写时序图如下:
写时序图.png
     写时隙:当主机把数据线从逻辑高电平拉到逻辑低电平的时候,写时间隙开始。有两种写时间隙:写1的时间隙和写0时间隙。所有写时间隙必须最少持续60us,包括两个写周期间至少1us的恢复时间。DQ引脚上的电平变低后,DS18B20在一个15us到60us的时间窗口内对DQ引脚采样。如果DQ引脚是高电平,就是写1,如果DQ引脚是低电平,就是写0。主机要生成一个写1时间隙,必须把数据线拉到低电平然后释放,在写时间隙开始后的15us内允许数据线拉到高电平。主机要生成一个写0时间隙,必须把数据线拉到低电平并保持60us。
     DS18B20的读时序图如下:
读时序图.png
      当主机把总线从高电平拉低,并保持至少1us后释放总线;并在15us内读取从DS18B20输出的数据。
      硬件电路上的连线如下图所示,DS18B20的DQ数据脚与开发板上的GPIOD3引脚相连接。
硬件接线.jpg
       部分的参考代码展示如下:
  1. #include "at32f423_board.h"
  2. #include "at32f423_clock.h"
  3. #include "ds18b20.h"

  4. int main(void)
  5. {
  6.         uint8_t i, DS18B20ID[8];
  7.         char str[50];
  8.         uint8_t t = 0;
  9. float temperature;
  10.   system_clock_config();
  11.   at32_board_init();
  12.         uart_print_init(115200);
  13.        
  14.         while (DS18B20_Init()) /* DS18B20初始化 */
  15.         {
  16.                         printf("DS18B20 Init Error\r\n");
  17.                         delay_ms(1000);
  18.         }
  19.         DS18B20_ReadId(DS18B20ID);
  20.   printf("DS18B20的序列号是: 0x");  
  21.         for ( i = 0; i < 8; i ++ )            
  22.           printf ( "%.2X", DS18B20ID[i]);
  23.   printf("\n");  
  24.   sprintf(str,"DS18B20的序列号是:0x%02X%02X%02X%02X%02X%02X%02X%02X",DS18B20ID[0],DS18B20ID[1],DS18B20ID[2],DS18B20ID[3],
  25.                                                                      DS18B20ID[4],DS18B20ID[5],DS18B20ID[6],DS18B20ID[7]);
  26.         printf("DS18B20 OK\r\n");
  27.   while(1)
  28.   {
  29.     temperature=DS18B20_GetTemp_MatchRom(DS18B20ID);
  30.     /* 打印通过 DS18B20 序列号获取的温度值 */
  31.     printf("获取该序列号器件的温度:%.1f\n",temperature);
  32.                 sprintf(str,"当前温度值为:%0.3f℃",temperature);
  33.                 delay_ms(800);
  34.         t++;
  35.         if (t == 100)
  36.         {
  37.             t = 0;
  38.             at32_led_toggle(LED2);
  39.                                                 delay_ms(10);
  40.                                                 at32_led_toggle(LED3);
  41.                                                 delay_ms(10);
  42.                                                 at32_led_toggle(LED4);
  43.                                                 delay_ms(10);
  44.         }
  45.   }
  46. }
  1. #include "at32f423_board.h"
  2. #include "at32f423_clock.h"
  3. #include "ds18b20.h"

  4. static void DS18B20_GPIO_Config(void);
  5. static void DS18B20_Mode_IPU(void);
  6. static void DS18B20_Mode_Out_PP(void);
  7. static void DS18B20_Rst(void);
  8. static uint8_t DS18B20_Presence(void);
  9. static uint8_t DS18B20_ReadBit(void);
  10. static uint8_t DS18B20_ReadByte(void);
  11. static void DS18B20_WriteByte(uint8_t dat);
  12. static void DS18B20_SkipRom(void);
  13. static void DS18B20_MatchRom(void);

  14. uint8_t DS18B20_Init(void)
  15. {
  16.         DS18B20_GPIO_Config ();
  17.        
  18.         DS18B20_DQ_1;
  19.        
  20.         DS18B20_Rst();
  21.        
  22.         return DS18B20_Presence ();
  23.        
  24. }

  25. static void DS18B20_GPIO_Config(void)
  26. {               
  27.         /*定义一个GPIO_InitTypeDef类型的结构体*/
  28.   gpio_init_type GPIO_InitStructure;


  29.   /*开启DS18B20_DQ_GPIO_PORT的外设时钟*/
  30.   DS18B20_DQ_SCK_APBxClock_FUN ( DS18B20_DQ_GPIO_CLK, TRUE);

  31.   /*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/                                                                                                                          
  32.   GPIO_InitStructure.gpio_pins = DS18B20_DQ_GPIO_PIN;       

  33.   /*设置引脚模式为通用推挽输出*/
  34.   GPIO_InitStructure.gpio_mode = GPIO_MODE_OUTPUT;   

  35.   /*设置引脚速率为50MHz */   
  36.   GPIO_InitStructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  37.   /*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
  38.   gpio_init(DS18B20_DQ_GPIO_PORT, &GPIO_InitStructure);
  39. }

  40. static void DS18B20_Rst(void)
  41. {
  42.         /* 主机设置为推挽输出 */
  43.         DS18B20_Mode_Out_PP();
  44.        
  45.         DS18B20_DQ_0;
  46.         /* 主机至少产生480us的低电平复位信号 */
  47.         delay_us(750);
  48.        
  49.         /* 主机在产生复位信号后,需将总线拉高 */
  50.         DS18B20_DQ_1;
  51.        
  52.         /*从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲*/
  53.         delay_us(15);
  54. }

  55. static uint8_t DS18B20_ReadBit(void)
  56. {
  57.         uint8_t dat;
  58.        
  59.         /* 读0和读1的时间至少要大于60us */       
  60.         DS18B20_Mode_Out_PP();
  61.         /* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
  62.         DS18B20_DQ_0;
  63.         delay_us(10);
  64.        
  65.         /* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
  66.         DS18B20_Mode_IPU();
  67.         //delay_us(2);
  68.        
  69.         if( DS18B20_DQ_IN() == SET )
  70.                 dat = 1;
  71.         else
  72.                 dat = 0;
  73.        
  74.         /* 这个延时参数请参考时序图 */
  75.         delay_us(45);
  76.        
  77.         return dat;
  78. }

  79. static uint8_t DS18B20_ReadByte(void)
  80. {
  81.         uint8_t i, j, dat = 0;       
  82.        
  83.         for(i=0; i<8; i++)
  84.         {
  85.                 j = DS18B20_ReadBit();               
  86.                 dat = (dat) | (j<<i);
  87.         }
  88.        
  89.         return dat;
  90. }

  91. static void DS18B20_WriteByte(uint8_t dat)
  92. {
  93.         uint8_t i, testb;
  94.         DS18B20_Mode_Out_PP();
  95.        
  96.         for( i=0; i<8; i++ )
  97.         {
  98.                 testb = dat&0x01;
  99.                 dat = dat>>1;               
  100.                 /* 写0和写1的时间至少要大于60us */
  101.                 if (testb)
  102.                 {                       
  103.                         DS18B20_DQ_0;
  104.                         /* 1us < 这个延时 < 15us */
  105.                         delay_us(8);
  106.                        
  107.                         DS18B20_DQ_1;
  108.                         delay_us(58);
  109.                 }               
  110.                 else
  111.                 {                       
  112.                         DS18B20_DQ_0;
  113.                         /* 60us < Tx 0 < 120us */
  114.                         delay_us(70);
  115.                        
  116.                         DS18B20_DQ_1;                       
  117.                         /* 1us < Trec(恢复时间) < 无穷大*/
  118.                         delay_us(2);
  119.                 }
  120.         }
  121. }

  122. static void DS18B20_SkipRom ( void )
  123. {
  124.         DS18B20_Rst();                  
  125.         DS18B20_Presence();                
  126.         DS18B20_WriteByte(0XCC);                /* 跳过 ROM */       
  127. }

  128. static void DS18B20_MatchRom ( void )
  129. {
  130.         DS18B20_Rst();                  
  131.         DS18B20_Presence();                
  132.         DS18B20_WriteByte(0X55);                /* 匹配 ROM */       
  133. }

  134. float DS18B20_GetTemp_SkipRom ( void )
  135. {
  136.         uint8_t tpmsb, tplsb;
  137.         short s_tem;
  138.         float f_tem;
  139.        
  140.        
  141.         DS18B20_SkipRom ();
  142.         DS18B20_WriteByte(0X44);                                /* 开始转换 */
  143.        
  144.        
  145.         DS18B20_SkipRom ();
  146.   DS18B20_WriteByte(0XBE);                                /* 读温度值 */
  147.        
  148.         tplsb = DS18B20_ReadByte();                 
  149.         tpmsb = DS18B20_ReadByte();
  150.        
  151.        
  152.         s_tem = tpmsb<<8;
  153.         s_tem = s_tem | tplsb;
  154.        
  155.         if( s_tem < 0 )                /* 负温度 */
  156.                 f_tem = (~s_tem+1) * 0.0625;       
  157.         else
  158.                 f_tem = s_tem * 0.0625;
  159.        
  160.         return f_tem;        
  161. }

  162. void DS18B20_ReadId ( uint8_t * ds18b20_id )
  163. {
  164.         uint8_t uc;
  165.                
  166.         DS18B20_WriteByte(0x33);       //读取序列号
  167.        
  168.         for ( uc = 0; uc < 8; uc ++ )
  169.           ds18b20_id [ uc ] = DS18B20_ReadByte();       
  170. }

  171. float DS18B20_GetTemp_MatchRom ( uint8_t * ds18b20_id )
  172. {
  173.         uint8_t tpmsb, tplsb, i;
  174.         short s_tem;
  175.         float f_tem;
  176.        
  177.        
  178.         DS18B20_MatchRom ();            //匹配ROM
  179.        
  180.   for(i=0;i<8;i++)
  181.                 DS18B20_WriteByte ( ds18b20_id [ i ] );       
  182.        
  183.         DS18B20_WriteByte(0X44);                                /* 开始转换 */

  184.        
  185.         DS18B20_MatchRom ();            //匹配ROM
  186.        
  187.         for(i=0;i<8;i++)
  188.                 DS18B20_WriteByte ( ds18b20_id [ i ] );       
  189.        
  190.         DS18B20_WriteByte(0XBE);                                /* 读温度值 */
  191.        
  192.         tplsb = DS18B20_ReadByte();                 
  193.         tpmsb = DS18B20_ReadByte();
  194.        
  195.        
  196.         s_tem = tpmsb<<8;
  197.         s_tem = s_tem | tplsb;
  198.        
  199.         if( s_tem < 0 )                /* 负温度 */
  200.                 f_tem = (~s_tem+1) * 0.0625;       
  201.         else
  202.                 f_tem = s_tem * 0.0625;
  203.        
  204.         return f_tem;                
  205. }
  1. #ifndef __ONEWIRE_DS18B20_H__
  2. #define        __ONEWIRE_DS18B20_H__

  3. #include "at32f423_board.h"
  4. #include "at32f423_clock.h"

  5. /************************** DS18B20 连接引脚定义****************************/
  6. #define DS18B20_DQ_SCK_APBxClock_FUN              crm_periph_clock_enable
  7. #define DS18B20_DQ_GPIO_CLK                       CRM_GPIOD_PERIPH_CLOCK

  8. #define DS18B20_DQ_GPIO_PORT                      GPIOD
  9. #define DS18B20_DQ_GPIO_PIN                       GPIO_PINS_3

  10. /************************** DS18B20 函数宏定义*****************************/
  11. #define DS18B20_DQ_0                                    gpio_bits_reset(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
  12. #define DS18B20_DQ_1                                    gpio_bits_set(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)
  13. #define DS18B20_DQ_IN()                                  gpio_input_data_bit_read(DS18B20_DQ_GPIO_PORT,DS18B20_DQ_GPIO_PIN)

  14. /************************* DS18B20 函数声明 ******************************/
  15. uint8_t DS18B20_Init(void);
  16. void DS18B20_ReadId( uint8_t * ds18b20_id);
  17. float DS18B20_GetTemp_SkipRom(void);
  18. float DS18B20_GetTemp_MatchRom(uint8_t *ds18b20_id);

  19. #endif /* __ONEWIRE_DS18B20_H__ */
    编译完成后,下载到开发板中,检测过程中,使用手指靠近DS18B20模块,则温度会随之增加,手指离开DS18B20模块,则温度值回落。串口打印效果如下:
读取温度值.gif
pl202 发表于 2023-12-7 21:56 | 显示全部楼层
DS18B20内置了64位产品序列号,这方便了多个DS18B20传感器的身份识别。通过64位身份验证,可以分别读取来自不同传感器的温度信息。
geraldbetty 发表于 2023-12-8 09:39 | 显示全部楼层
DS18B20的驱动编程需要对时序有精确的控制
sesefadou 发表于 2023-12-8 15:30 | 显示全部楼层
DS18B20与微处理器间采用串行数据传送,必须严格的保证读写时序,否则将无法读取测温结果。
timfordlare 发表于 2023-12-8 20:43 | 显示全部楼层
DS18B20是一款单总线器件,其读写数据依赖于严格的时序控制。
saservice 发表于 2023-12-8 21:12 | 显示全部楼层
如果时序控制不当,可能导致无法正常驱动DS18B20读取环境温度。
timfordlare 发表于 2023-12-8 21:47 | 显示全部楼层
在主机发送复位信号后,要将IO口切换为浮空输入模式
bestwell 发表于 2023-12-9 14:20 | 显示全部楼层
DS18B20虽然具有测温系统简单、测温精度高、连接方便、占用口线少等优点
sanfuzi 发表于 2023-12-9 15:38 | 显示全部楼层
当单片机需要给DS18B20写入一个0时,需要将单片机引脚拉低,保持低电平时间在60~120us之间,然后释放总线。当需要写入一个1时,需要将单片机引脚拉低,拉低时间需要大于1us,然后在15us内拉高总线。
pmp 发表于 2023-12-9 22:25 | 显示全部楼层
单总线协议对延时要求比较严格              
pattywu 发表于 2023-12-9 22:28 | 显示全部楼层
用PT100铂电阻, 电阻分压, ADC采样, 分分钟搞定, 精度还高.
earlmax 发表于 2023-12-9 22:39 | 显示全部楼层
在读取温度之前,需要跳过ROM命令,这是因为ROM命令用于区分多个单总线器件。在单总线通信中,可以直接写一个跳过ROM的命令。
jtracy3 发表于 2023-12-10 15:35 | 显示全部楼层
最高12位分辨率,精度可达±0.5摄氏度
forgot 发表于 2023-12-11 08:23 | 显示全部楼层
DS18B20是一款单总线器件,其读写数据依赖于严格的时序控制。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

107

主题

1102

帖子

7

粉丝
快速回复 在线客服 返回列表 返回顶部