[AT32M412] 【AT-START-M412测评】+ ③驱动DS18B20温度采集

[复制链接]
 楼主| yinwuqing110 发表于 2025-6-19 21:08 | 显示全部楼层 |阅读模式
本帖最后由 yinwuqing110 于 2025-6-19 21:28 编辑

         温度传感器有多种,采用IIC通讯的方式的比较多,而在IO口资源有限的单片机设计中,使用DS18B20的仍然比较常见。之所以DS18B20不会退出市场,不少教学开发板上仍保留它的一席之地,是因为它的驱动方式只需要一根线,也就是一个IO口即能完成数据的精准采集,而且灵敏度不输IIC通讯方式的温敏传感器。“一线式”通讯方式,资源占用少,但驱动起来其实比IIC通讯要求更苛刻,尤其是时序上的要求。今天,咱们使用AT-START-M412驱动DS18B20模块,温度值显示在LCD屏上,并通过串口打印相关信息。当用手握DS18B20模块给传感器加温,采集温度瞬间增加;挪开手,则采集的温度值明显下降。


          首先来认识一下DS18B20模块,实物图如下:
实物图.png
          由上可知,除了给模块正常上电外,只需一个DQ引脚,完成数字信号的输入输出,读写时序需要严格按照官方给定的数据手册来。
          DS18B20可以测量-55℃至+125℃的温度范围,通常采用外部供电方式,并在数据线上并联一个4.7k的上拉电阻,以增强数据的抗干扰能力。
          为了高效得驱动DS18B20,先来阅读一下数据手册。
DS18B20中文.pdf (459.33 KB, 下载次数: 0)
          其中比较重要的是读写时序,该数据手册中提供了关于驱动DS18B20的读写时序图,如下图所示:
读写时序图.png
       解读控制时序:
       ①、初始化时序:与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
  1. #ifndef __DS18B20_H__
  2. #define __DS18B20_H__

  3. /* 包含头文件 ----------------------------------------------------------------*/
  4. #include "at32m412_416_board.h"

  5. /************************** DS18B20 连接引脚定义********************************/
  6. #define DS18B20_DQ_GPIO_CLK                       CRM_GPIOB_PERIPH_CLOCK

  7. #define DS18B20_DQ_GPIO_PORT                      GPIOB
  8. #define DS18B20_DQ_GPIO_PIN                       GPIO_PINS_12

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

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

  18. #endif
DS18B20.c
  1. #include "at32m412_416_board.h"
  2. #include "at32m412_416_clock.h"

  3. #include "DS18B20.h"

  4. /**
  5.   * 函数功能: DS18B20 初始化函数
  6.   * 输入参数: 无
  7.   * 返 回 值: 0:初始化成功,检测到传感器,1:初始化失败
  8.   * 说    明:无
  9.   */
  10. uint8_t DS18B20_Init(void)
  11. {
  12.         DS18B20_GPIO_Config ();
  13.         DS18B20_DQ_1;
  14.         DS18B20_Rst();
  15.         return DS18B20_Presence ();
  16. }

  17. /**
  18.   * 函数功能: 配置DS18B20用到的I/O口
  19.   * 输入参数: 无
  20.   * 返 回 值: 无
  21.   * 说    明:无
  22.   */
  23. static void DS18B20_GPIO_Config(void)
  24. {               
  25.   gpio_init_type gpio_init_structure;
  26.   crm_periph_clock_enable(DS18B20_DQ_GPIO_CLK, TRUE);
  27.                                                                                           
  28.   gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;        
  29.   gpio_init_structure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  30.   gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
  31.   gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  32.   gpio_init(DS18B20_DQ_GPIO_PORT , &gpio_init_structure);
  33. }

  34. /**
  35.   * 函数功能: 使DS18B20-DATA引脚变为输入模式
  36.   * 输入参数: 无
  37.   * 返 回 值: 无
  38.   * 说    明:无
  39.   */
  40. static void DS18B20_Mode_IPU(void)
  41. {
  42.            gpio_init_type gpio_init_structure;

  43.           gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;
  44.           gpio_init_structure.gpio_pull  = GPIO_PULL_NONE;        
  45.           gpio_init_structure.gpio_mode = GPIO_MODE_INPUT;        
  46.           gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
  47. }

  48. /**
  49.   * 函数功能: 使DS18B20-DATA引脚变为输出模式
  50.   * 输入参数: 无
  51.   * 返 回 值: 无
  52.   * 说    明:无
  53.   */
  54. static void DS18B20_Mode_Out_PP(void)
  55. {
  56.          gpio_init_type gpio_init_structure;
  57.                                                                                                            
  58.         gpio_init_structure.gpio_pins = DS18B20_DQ_GPIO_PIN;        
  59.         gpio_init_structure.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  60.         gpio_init_structure.gpio_mode = GPIO_MODE_OUTPUT;
  61.         gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  62.         gpio_init(DS18B20_DQ_GPIO_PORT, &gpio_init_structure);
  63. }

  64. /**
  65.   * 函数功能: 主机给从机发送复位脉冲
  66.   * 输入参数: 无
  67.   * 返 回 值: 无
  68.   * 说    明:无
  69.   */
  70. static void DS18B20_Rst(void)
  71. {
  72.         DS18B20_Mode_Out_PP();
  73.         DS18B20_DQ_0;
  74.         delay_us(750);
  75.         DS18B20_DQ_1;
  76.         delay_us(15);
  77. }

  78. /**
  79.   * 函数功能: 检测从机给主机返回的存在脉冲
  80.   * 输入参数: 无
  81.   * 返 回 值: 0:成功,1:失败
  82.   * 说    明:无
  83.   */
  84. static uint8_t DS18B20_Presence(void)
  85. {
  86.         uint8_t pulse_time = 0;
  87.         
  88.         DS18B20_Mode_IPU();
  89.         while( DS18B20_DQ_IN() && pulse_time<100 )
  90.         {
  91.                 pulse_time++;
  92.                 delay_us(1);
  93.         }        
  94.         if( pulse_time >=100 )
  95.                 return 1;
  96.         else
  97.                 pulse_time = 0;
  98.         
  99.         while( !DS18B20_DQ_IN() && pulse_time<240 )
  100.         {
  101.                 pulse_time++;
  102.                 delay_us(1);
  103.         }        
  104.         if( pulse_time >=240 )
  105.                 return 1;
  106.         else
  107.                 return 0;
  108. }

  109. /**
  110.   * 函数功能: 从DS18B20读取一个bit
  111.   * 输入参数: 无
  112.   * 返 回 值: 读取到的数据
  113.   * 说    明:无
  114.   */
  115. static uint8_t DS18B20_ReadBit(void)
  116. {
  117.         uint8_t dat;
  118.         
  119.         DS18B20_Mode_Out_PP();
  120.         DS18B20_DQ_0;
  121.         delay_us(10);
  122.         DS18B20_Mode_IPU();
  123.         if( DS18B20_DQ_IN() == SET )
  124.                 dat = 1;
  125.         else
  126.                 dat = 0;
  127.         delay_us(45);
  128.         
  129.         return dat;
  130. }

  131. /**
  132.   * 函数功能: 从DS18B20读一个字节,低位先行
  133.   * 输入参数: 无
  134.   * 返 回 值: 读到的数据
  135.   * 说    明:无
  136.   */
  137. static uint8_t DS18B20_ReadByte(void)
  138. {
  139.         uint8_t i, j, dat = 0;        
  140.         
  141.         for(i=0; i<8; i++)
  142.         {
  143.                 j = DS18B20_ReadBit();               
  144.                 dat = (dat) | (j<<i);
  145.         }
  146.         
  147.         return dat;
  148. }

  149. /**
  150.   * 函数功能: 写一个字节到DS18B20,低位先行
  151.   * 输入参数: dat:待写入数据
  152.   * 返 回 值: 无
  153.   * 说    明:无
  154.   */
  155. static void DS18B20_WriteByte(uint8_t dat)
  156. {
  157.         uint8_t i, testb;
  158.         DS18B20_Mode_Out_PP();
  159.         
  160.         for( i=0; i<8; i++ )
  161.         {
  162.                 testb = dat&0x01;
  163.                 dat = dat>>1;               
  164.                 /* 写0和写1的时间至少要大于60us */
  165.                 if (testb)
  166.                 {                        
  167.                         DS18B20_DQ_0;
  168.                         /* 1us < 这个延时 < 15us */
  169.                         delay_us(8);
  170.                         
  171.                         DS18B20_DQ_1;
  172.                         delay_us(58);
  173.                 }               
  174.                 else
  175.                 {                        
  176.                         DS18B20_DQ_0;
  177.                         /* 60us < Tx 0 < 120us */
  178.                         delay_us(70);
  179.                         
  180.                         DS18B20_DQ_1;                        
  181.                         /* 1us < Trec(恢复时间) < 无限大*/
  182.                         delay_us(2);
  183.                 }
  184.         }
  185. }

  186. /**
  187.   * 函数功能: 跳过匹配 DS18B20 ROM
  188.   * 输入参数: 无
  189.   * 返 回 值: 无
  190.   * 说    明:无
  191.   */
  192. static void DS18B20_SkipRom ( void )
  193. {
  194.         DS18B20_Rst();                  
  195.         DS18B20_Presence();                 
  196.         DS18B20_WriteByte(0XCC);      /* 跳过 ROM */        
  197. }

  198. /**
  199.   * 函数功能: 执行匹配 DS18B20 ROM
  200.   * 输入参数: 无
  201.   * 返 回 值: 无
  202.   * 说    明:无
  203.   */
  204. static void DS18B20_MatchRom ( void )
  205. {
  206.         DS18B20_Rst();                  
  207.         DS18B20_Presence();                 
  208.         DS18B20_WriteByte(0X55);
  209. }

  210. /**
  211.   * 函数功能: 在跳过匹配 ROM 情况下获取 DS18B20 温度值
  212.   * 输入参数: 无
  213.   * 返 回 值: 温度值
  214.   * 说    明:无
  215.   */
  216. float DS18B20_GetTemp_SkipRom ( void )
  217. {
  218.         uint8_t tpmsb, tplsb;
  219.         short s_tem;
  220.         float f_tem;
  221.         
  222.         DS18B20_SkipRom ();
  223.         DS18B20_WriteByte(0X44);      /* 开始转换 */
  224.         
  225.         DS18B20_SkipRom ();
  226.         DS18B20_WriteByte(0XBE);      /* 读温度值 */
  227.         
  228.         tplsb = DS18B20_ReadByte();                 
  229.         tpmsb = DS18B20_ReadByte();
  230.         
  231.         s_tem = tpmsb<<8;
  232.         s_tem = s_tem | tplsb;
  233.         
  234.         if( s_tem < 0 )            /* 负温度 */
  235.                 f_tem = (~s_tem+1) * 0.0625;        
  236.         else
  237.                 f_tem = s_tem * 0.0625;
  238.         
  239.         return f_tem;         
  240. }

  241. /**
  242.   * 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
  243.   * 输入参数: ds18b20_id:用于存放 DS18B20 序列号的数组的首地址
  244.   * 返 回 值: 无
  245.   * 说    明:无
  246.   */
  247. void DS18B20_ReadId ( uint8_t * ds18b20_id )
  248. {
  249.         uint8_t uc;
  250.                
  251.         DS18B20_WriteByte(0x33);      //读取序列号
  252.         
  253.         for ( uc = 0; uc < 8; uc ++ )
  254.           ds18b20_id [ uc ] = DS18B20_ReadByte();        
  255. }

  256. /**
  257.   * 函数功能: 在匹配 ROM 情况下获取 DS18B20 温度值
  258.   * 输入参数: ds18b20_id:存放 DS18B20 序列号的数组的首地址
  259.   * 返 回 值: 温度值
  260.   * 说    明:无
  261.   */
  262. float DS18B20_GetTemp_MatchRom ( uint8_t * ds18b20_id )
  263. {
  264.         uint8_t tpmsb, tplsb, i;
  265.         short s_tem;
  266.         float f_tem;
  267.         
  268.         DS18B20_MatchRom ();      //匹配ROM
  269.         
  270.     for(i=0;i<8;i++)
  271.                 DS18B20_WriteByte ( ds18b20_id [ i ] );        
  272.         
  273.         DS18B20_WriteByte(0X44);      //开始转换

  274.         DS18B20_MatchRom ();      //匹配ROM
  275.         
  276.         for(i=0;i<8;i++)
  277.                 DS18B20_WriteByte ( ds18b20_id [ i ] );        
  278.         
  279.         DS18B20_WriteByte(0XBE);      //读温度值
  280.         
  281.         tplsb = DS18B20_ReadByte();                 
  282.         tpmsb = DS18B20_ReadByte();
  283.         
  284.         s_tem = tpmsb<<8;
  285.         s_tem = s_tem | tplsb;
  286.         
  287.         if( s_tem < 0 )           /* 负温度 */
  288.                 f_tem = (~s_tem+1) * 0.0625;        
  289.         else
  290.                 f_tem = s_tem * 0.0625;
  291.         
  292.         return f_tem;                 
  293. }
main.c
  1. /* includes */
  2. #include "at32m412_416_board.h"
  3. #include "at32m412_416_clock.h"

  4. #include "lcd_st7735.h"
  5. #include "DS18B20.h"

  6. int main(void)
  7. {
  8.   uint8_t i, DS18B20ID[8];
  9.   char str[50];
  10.   float temperature;
  11.         
  12.   system_clock_config();

  13.   at32_board_init();
  14.   uart_print_init(115200);
  15.   LcdInit();
  16.   printf("DS18B20温度传感器信息读取\n");
  17.   LcdFill(0,0,128,160,BLACK);
  18.   LcdShowString(2,24,"DS18B20",RED, BLACK,16);
  19.   LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
  20.   LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
  21.   LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
  22.   LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
  23.   LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
  24.   LcdShow16x16Hz(18, 48, 15, YELLOW, BLACK);
  25.   LcdShow16x16Hz(34, 48, 16, YELLOW, BLACK);
  26.   LcdShow16x16Hz(50, 48, 17, YELLOW, BLACK);
  27.   LcdShow16x16Hz(66, 48, 18, YELLOW, BLACK);
  28.   delay_ms(2000);
  29.   while(DS18B20_Init())        
  30.   {
  31.     printf("DS18B20温度传感器不存在\n");
  32.     LcdFill(0,0,128,160,BLACK);
  33.     LcdShowString(2,24,"DS18B20",RED, BLACK,16);
  34.     LcdShow16x16Hz(60, 24, 10, YELLOW, BLACK);
  35.     LcdShow16x16Hz(76, 24, 11, YELLOW, BLACK);
  36.     LcdShow16x16Hz(92, 24, 12, YELLOW, BLACK);
  37.     LcdShow16x16Hz(108, 24, 13, YELLOW, BLACK);
  38.     LcdShow16x16Hz(2, 48, 14, YELLOW, BLACK);
  39.     LcdShow16x16Hz(18, 48, 19, YELLOW, BLACK);
  40.     LcdShow16x16Hz(34, 48, 20, YELLOW, BLACK);
  41.     LcdShow16x16Hz(50, 48, 21, YELLOW, BLACK);
  42.     delay_ms(1000);
  43.   }
  44.   printf("检测到DS18B20温度传感器,并初始化成功\n");
  45.   LcdFill(0,0,128,160,BLACK);
  46.   LcdShow16x16Hz(2, 24, 22, YELLOW, BLACK);
  47.   LcdShow16x16Hz(18, 24, 23, YELLOW, BLACK);
  48.   LcdShow16x16Hz(34, 24, 24, YELLOW, BLACK);
  49.   LcdShowString(50,24,"DS18B20",RED, BLACK,16);
  50.   LcdShow16x16Hz(108, 24, 10, YELLOW, BLACK);
  51.   LcdShow16x16Hz(2, 48, 11, YELLOW, BLACK);
  52.   LcdShow16x16Hz(18, 48, 12, YELLOW, BLACK);
  53.   LcdShow16x16Hz(34, 48, 13, YELLOW, BLACK);
  54.   LcdShow16x16Hz(50, 48, 14, YELLOW, BLACK);
  55.   LcdShowString(66,48,",",YELLOW, BLACK,16);
  56.   LcdShow16x16Hz(74, 48, 25, YELLOW, BLACK);
  57.   LcdShow16x16Hz(90, 48, 26, YELLOW, BLACK);
  58.   LcdShow16x16Hz(106, 48, 27, YELLOW, BLACK);
  59.   LcdShow16x16Hz(2, 72, 28, YELLOW, BLACK);
  60.   LcdShow16x16Hz(18, 72, 29, YELLOW, BLACK);
  61.   LcdShow16x16Hz(34, 72, 30, YELLOW, BLACK);

  62.   DS18B20_ReadId(DS18B20ID);
  63.   printf("DS18B20的序列号是: 0x");  
  64.         for ( i = 0; i < 8; i ++ )            
  65.           printf ( "%.2X", DS18B20ID[i]);
  66.   printf("\n");
  67.         
  68.   sprintf(str,"0x%02X%02X%02X%02X%02X%02X%02X%02X",DS18B20ID[0],DS18B20ID[1],DS18B20ID[2],DS18B20ID[3],
  69.                                                                      DS18B20ID[4],DS18B20ID[5],DS18B20ID[6],DS18B20ID[7]);
  70.   
  71.   LcdShowString(8,96,"DS18B20",RED, BLACK,12);
  72.   LcdShow16x16Hz(50, 96, 32, YELLOW, BLACK);
  73.   LcdShow16x16Hz(66, 96, 33, YELLOW, BLACK);
  74.   LcdShow16x16Hz(82, 96, 34, YELLOW, BLACK);
  75.   LcdShow16x16Hz(98, 96, 35, YELLOW, BLACK);
  76.   LcdShow16x16Hz(114, 96, 40, YELLOW, BLACK);
  77.   LcdShowString(8,120,str,RED,BLACK,12);
  78.   delay_ms(2000);
  79.   while(1)
  80.   {
  81.     temperature=DS18B20_GetTemp_MatchRom(DS18B20ID);
  82.     /* 打印通过 DS18B20 序列号获取的温度值 */
  83.     printf("获取该序列号器件的温度:%.1f\n",temperature);
  84.     LcdFill(0,0,128,160,BLACK);
  85.     LcdShow16x16Hz(2, 8, 36, YELLOW, BLACK);
  86.     LcdShow16x16Hz(18, 8, 37, YELLOW, BLACK);
  87.     LcdShow16x16Hz(34, 8, 10, YELLOW, BLACK);
  88.     LcdShow16x16Hz(50, 8, 11, YELLOW, BLACK);
  89.     LcdShow16x16Hz(66, 8, 38, YELLOW, BLACK);
  90.     LcdShow16x16Hz(82, 8, 39, YELLOW, BLACK);
  91.     LcdShow16x16Hz(98, 8, 40, YELLOW, BLACK);
  92.     LCD_ShowFloatNum1(8,32,temperature,4,RED, BLACK,24);
  93.     LcdShow16x16Hz(70, 36, 41, RED, BLACK);
  94.     /* 0.5s 读取一次温度值 */
  95.     delay_ms(500);               
  96.   }
  97. }
       以上代码均在上一个驱动LCD工程上完成,因此直接下载到开发板后,可以在LCD屏上直观得看到DS18B20实时采集的温度值变化。当然也是支持串口同步打印输出温度值变化的。
        例如串口打印信息:
AT32M412驱动DS18B20进行温度采集.gif
      一段实时操作演示见B站视频:https://www.bilibili.com/video/BV1YFNnzrENG/


您需要登录后才可以回帖 登录 | 注册

本版积分规则

106

主题

1098

帖子

7

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