这次在AB32VG1评估板上单线挂载2个DS18B20实现测温功能。硬件如下:
微信图片_20210606213811.png
微信图片_20210912194657.jpg

新建工程,DS18B20 ROM搜索部分代码使用http://blog.sina.com.cn/s/blog_57ad1bd20102uxxw.html
ds18b20.c代码如下:
  1. //CRC计算用表
  2. static uint8_t dscrc_table[] =
  3. {
  4. 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
  5. 157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
  6. 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
  7. 190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
  8. 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
  9. 219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
  10. 101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
  11. 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
  12. 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
  13. 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
  14. 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
  15. 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
  16. 202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
  17. 87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
  18. 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
  19. 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
  20. };
  21. uint8_t ROM_NO[8]; //数组,存放本次搜索到的ROM码(8个字节)
  22. uint8_t LastDiscrepancy; //每轮搜索后指向最后一个走0的差异位
  23. uint8_t LastFamilyDiscrepancy; //指向家族码(前8位)中最后一个走0的差异位
  24. uint8_t LastDeviceFlag; //搜到最后一个ROM后,程序通过判别将该变量置1,下轮搜索时即会结束退出
  25. uint8_t crc8; //CRC校验变量
  26. uint8_t ID_Buff[DS18B20_NUM][8]; //将搜索到的ROM存放在二维数组
  27. /*
  28.     DS18B20 APIs
  29. */
  30. //复位DS18B20
  31. void DS18B20_Rst(void)
  32. {
  33.     rt_pin_mode(DS18B20_DQ, PIN_MODE_OUTPUT); //输出模式
  34.     rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  35.     rt_hw_us_delay(380); //拉低750us(480~960us)
  36.     rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  37. }
  38. //等待DS18B20应答,有应答返回0,无应答返回1
  39. uint8_t DS18B20_Check(void)
  40. {
  41.     uint8_t retry = 0;
  42.     //进入接收模式,等待应答信号
  43.     //等待时间
  44.     rt_pin_mode(DS18B20_DQ, PIN_MODE_INPUT); //输入模式
  45.     rt_hw_us_delay(15); //等待15~60us
  46.     while(rt_pin_read(DS18B20_DQ) && retry<30) //最多再等待120us
  47.     {
  48.         retry++;
  49.         rt_hw_us_delay(1);
  50.     };
  51.     if(retry >= 30)
  52.         return 1; //120us未响应,则判断未检测到
  53.     else
  54.         retry = 0;
  55.     //DS18B20开始拉低DQ
  56.     while(!rt_pin_read(DS18B20_DQ) && retry<38) //最长拉低240us
  57.     {
  58.         retry++;
  59.         rt_hw_us_delay(1);
  60.     };
  61.     if(retry >= 38)
  62.         return 1;
  63.     return 0;
  64. }
  65. //写位到DS18B20
  66. void DS18B20_Write_Bit(uint8_t dat)
  67. {
  68.     rt_pin_mode(DS18B20_DQ, PIN_MODE_OUTPUT); //输出模式
  69.     if (dat) //输出高
  70.     {
  71.         rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  72.         rt_hw_us_delay(1); //延时2us
  73.         rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  74.         rt_hw_us_delay(29); //延时60us
  75.     }
  76.     else //输出低
  77.     {
  78.         rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  79.         rt_hw_us_delay(29); //延时60us
  80.         rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  81.         rt_hw_us_delay(1); //延时2us
  82.     }
  83.     rt_pin_mode(DS18B20_DQ, PIN_MODE_INPUT); //输入模式
  84. }
  85. //写字节到DS18B20
  86. void DS18B20_Write_Byte(uint8_t dat)
  87. {
  88.     uint8_t j;
  89.     uint8_t temp;
  90.     rt_pin_mode(DS18B20_DQ, PIN_MODE_OUTPUT); //输出模式
  91.     for(j=1; j<=8; j++)
  92.     {
  93.         temp = dat & 0x01;
  94.         dat = dat >> 1;
  95.         if (temp) //输出高
  96.         {
  97.             rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  98.             rt_hw_us_delay(1); //延时2us
  99.             rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  100.             rt_hw_us_delay(29); //延时60us
  101.         }
  102.         else //输出低
  103.         {
  104.             rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  105.             rt_hw_us_delay(29); //延时60us
  106.             rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  107.             rt_hw_us_delay(1); //延时2us
  108.         }
  109.     }
  110.     rt_pin_mode(DS18B20_DQ, PIN_MODE_INPUT); //输入模式
  111. }
  112. //从DS18B20读位
  113. uint8_t DS18B20_Read_Bit(void)
  114. {
  115.     uint8_t data;
  116.     rt_pin_mode(DS18B20_DQ, PIN_MODE_OUTPUT); //输出模式
  117.     rt_pin_write(DS18B20_DQ, PIN_LOW); //拉低DQ
  118.     rt_hw_us_delay(1); //延时2us
  119.     rt_pin_write(DS18B20_DQ, PIN_HIGH); //拉高DQ
  120.     rt_pin_mode(DS18B20_DQ, PIN_MODE_INPUT); //输入模式
  121.     rt_hw_us_delay(2); //延时12us
  122.     if(rt_pin_read(DS18B20_DQ)) //读DQ数据
  123.         data = 1;
  124.     else
  125.         data = 0;
  126.     rt_hw_us_delay(25); //延时50us
  127.     return data;
  128. }
  129. //从DS18B20读字节
  130. uint8_t DS18B20_Read_Byte(void)
  131. {
  132.     uint8_t i, j, dat = 0;
  133.     for(i=1; i<=8; i++)
  134.     {
  135.         j = DS18B20_Read_Bit();
  136.         dat = (j << 7) | (dat >> 1);
  137.     }
  138.     return dat;
  139. }
  140. //开始温度转换
  141. void DS18B20_Start(void)
  142. {
  143.     DS18B20_Rst();
  144.     DS18B20_Check();
  145.     DS18B20_Write_Byte(0xcc);
  146.     DS18B20_Write_Byte(0x44);
  147. }
  148. //读取温度值
  149. /*搜索到的DS18B20 ROM码存放在DS18B20_NUM*8的二维数组,每行存放一个ROM码,分8字节存放,获取每个DS18B20温度值时,发送二维数组每行字节,进行序列号匹配*/
  150. float DS18B20_Get_Temp(uint8_t pID[8])
  151. {
  152.     uint8_t i;
  153.     uint8_t sign; //温度符号,0为-,1为+
  154.     uint8_t TL, TH;
  155.     uint16_t temp;
  156.     float temp1;
  157.     DS18B20_Start ();
  158.     rt_thread_mdelay(800); //等待转换完成
  159.     DS18B20_Rst();
  160.     DS18B20_Check();
  161.     DS18B20_Write_Byte(0x55); //发送序列号匹配命令
  162.     for(i=0; i<8; i++)  //发送8byte的序列号
  163.     {
  164.         DS18B20_Write_Byte(pID[i]);
  165.     }
  166.     rt_hw_us_delay(10);
  167.     DS18B20_Write_Byte(0xbe);
  168.     TL = DS18B20_Read_Byte();
  169.     TH = DS18B20_Read_Byte();
  170.     if(TH > 7)
  171.     {
  172.         TH = ~TH;
  173.         TL = ~TL;
  174.         sign = 0; //温度为负
  175.     }
  176.     else
  177.         sign = 1; //温度为正
  178.     temp = TH; //高八位
  179.     temp <<= 8;
  180.     temp += TL; //低八位
  181.     temp1 = (float)temp * 0.0625; //转换实际温度
  182.     if(sign)
  183.         return temp1; //返回温度值
  184.     else
  185.         return -temp1;
  186. }
  187. /*
  188. * 初始化
  189. */
  190. uint8_t DS18B20_Init(void)
  191. {
  192.     DS18B20_Rst();
  193.     return DS18B20_Check();
  194. }
  195. /*
  196. 生成CRC8校验码
  197. */
  198. uint8_t GenerateCRC8(uint8_t value)
  199. {
  200.    crc8 = dscrc_table[crc8 ^ value]; //^表示按位异或
  201.    return crc8;
  202. }
  203. /*
  204. 单总线搜索算法,返回TRUE:找到,存入ROM_NO缓冲;FALSE:无设备,结束搜索
  205. */
  206. uint8_t OWSearch(void)
  207. {
  208.     uint8_t id_bit_number; //指示当前搜索ROM位(取值范围为1-64)
  209.     /*下面三个状态变量含义:last_zero:指针,记录一次搜索(ROM1-64位)最后一位往0走的混码点编号
  210.                           search_direction:搜索某一位时选择的搜索方向(0或1),也是“一写”的bit位值
  211.                           rom_byte_number:ROM字节序号,作为ROM_no[]数组的下标,取值为1—8
  212.     */
  213.     uint8_t last_zero, rom_byte_number, search_result;
  214.     uint8_t id_bit, cmp_id_bit,search_direction; //二读(正码、反码)、及一写(决定二叉搜索方向)
  215.     uint8_t rom_byte_mask ; //ROM字节掩码
  216.     //初始化本次搜索变量
  217.     id_bit_number = 1;
  218.     last_zero = 0;
  219.     rom_byte_number = 0;
  220.     rom_byte_mask = 1;
  221.     search_result = 0;
  222.     crc8 = 0;
  223.     /*
  224.     是否搜索完成(已到最后一个设备)?
  225.     */
  226.     if(!LastDeviceFlag) //LastDeviceFlag由上轮搜索确定是否为最后器件,当然首次进入前必须置FALSE
  227.     {
  228.         DS18B20_Rst();
  229.         //if(OWReset()) //复位总线
  230.         if(DS18B20_Check())
  231.         {
  232.             LastDiscrepancy = 0; //复位几个搜索变量
  233.             LastDeviceFlag = FALSE;
  234.             LastFamilyDiscrepancy = 0;
  235.             return FALSE; //如果无应答,返回FALSE,退出本轮搜索程序
  236.         }
  237.         DS18B20_Write_Byte(0xF0); //发送ROM搜索命令F0H
  238.         rt_hw_us_delay(45);
  239.         /*
  240.         开始循环处理1-64位ROM,每位必须进行“二读”后进行判断,确定搜索路径
  241.         然后按选定的路径进行“一写”,直至完成全部位的搜索,这样一次循环
  242.         可以完成一轮搜索,找到其中一个ROM码
  243.         */
  244.         do //逐位读写搜索,1-64位循环
  245.         {
  246.             id_bit = DS18B20_Read_Bit(); //二读:先读正码、再读反码
  247.             cmp_id_bit = DS18B20_Read_Bit();
  248.             if(id_bit && cmp_id_bit) //二读11,则无器件退出程序
  249.                 break;
  250.             else //二读不为11,则需分二种情况
  251.             {
  252.             /*
  253.             第一种情况:01或10,直接可明确搜索方向
  254.             */
  255.                 if(id_bit != cmp_id_bit)
  256.                     search_direction = id_bit; //记下搜索方向search_direction的值待“一写”
  257.                 else
  258.                 {
  259.                 /*
  260.                 第二种情况:遇到了混码点,需分三种可能分析:
  261.                 1、当前位未到达上轮搜索的“最末走0混码点”(由LastDiscrepancy存储)
  262.                 说明当前经历的是一个老的混码点,判别特征为当前位在(小于)LastDiscrepancy前
  263.                 不管上次走的是0还是1,只需按上次走的路即可,该值需从ROM_NO中的当前位获取
  264.                 */
  265.                     if(id_bit_number < LastDiscrepancy)
  266.                         search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
  267.                     else
  268.                     /*
  269.                     2、当前位正好为上轮标记的最末的混码点,这个混码点也就是上次走0的点,那么这次就需要走1
  270.                     3、除去上二种可能,那就是第3种可能: 这是一个新的混码点,id_bit_number>LastDiscrepancy
  271.                     下一条语句巧妙地将上二种可能合在一起处理
  272.                     */
  273.                         search_direction = (id_bit_number == LastDiscrepancy);
  274.                     /*
  275.                     确定了混码点的路径方向还没完事,还需要更新一个指针:last_zero
  276.                     这个指针每搜索完一位后(注意是一bit不是一轮)总是指向新的混码点
  277.                     凡遇到新的混码点,我们按算法都是先走0,所以凡遇走0的混码点必须更新此指针
  278.                     */
  279.                     if(search_direction == 0)
  280.                     {
  281.                         last_zero = id_bit_number;
  282.                     /*
  283.                     下面二条是程序的高级功能了:64位ROM中的前8位是器件的家族代码,
  284.                     用LastFamilyDiscrepancy这个指针来记录前8位ROM中的最末一个混码点
  285.                     可用于在多类型器件的单线网络中对家族分组进行操作
  286.                     */
  287.                         if(last_zero < 9)
  288.                             LastFamilyDiscrepancy = last_zero;
  289.                     }
  290.                 }
  291.                 /*
  292.                 确定了要搜索的方向search_direction,该值即ROM中当前位的值,需要写入ROM
  293.                 然而64位ROM需分8个字节存入ROM_NO[],程序使用了一个掩码字节rom_byte_mask
  294.                 以最低位为例:该字节值为00000001,如记录1则二字节或,写0则与反掩码
  295.                 */
  296.                 if(search_direction == 1)
  297.                     ROM_NO[rom_byte_number] |= rom_byte_mask;
  298.                 else
  299.                     ROM_NO[rom_byte_number] &= ~rom_byte_mask;
  300.                 //关键的一步操作终于到来了:一写
  301.                 DS18B20_Write_Bit(search_direction);
  302.                 /*
  303.                 一个位的操作终于完成,但还需做些工作,以准备下一位的操作:
  304.                 包括:位变量id_bit_number指向下一位,字节掩码左移一位
  305.                 */
  306.                 id_bit_number++;
  307.                 rom_byte_mask <<= 1;
  308.                 //如果够8位一字节了,则对该字节计算CRC处理,更新字节号变量,重设掩码
  309.                 if(rom_byte_mask == 0)
  310.                 {
  311.                     GenerateCRC8(ROM_NO[rom_byte_number]); //CRC计算
  312.                     rom_byte_number++;
  313.                     rom_byte_mask = 1;
  314.                 }
  315.             }
  316.         }
  317.         while(rom_byte_number < 8); //ROM bytes编号为 0-7
  318.         //代码中是利用rom_byte_number<8来判断的,至此,完成8个字节共64位的循环处理
  319.         //一轮搜索成功,找到的一个ROM码也校验OK,则还要处理二个变量
  320.         if(!((id_bit_number < 65) || (crc8 != 0)))
  321.         {
  322.         /*
  323.         一轮搜索结束后,变量last_zero指向了本轮中最后一个走0的混码位
  324.         然后再把此变量保存在LastDiscrepancy中,用于下一轮的判断
  325.         last_zero在下轮初始为0,搜索是该变量是不断变动的
  326.         */
  327.             LastDiscrepancy = last_zero;
  328.             //如果这个指针为0,说明全部搜索结束,再也没有新ROM号器件了
  329.             if(LastDiscrepancy == 0)
  330.                 LastDeviceFlag = TRUE; //设置结束标志
  331.                 search_result = TRUE; //返回搜索成功
  332.         }
  333.     }
  334.     /*
  335.     //搜索完成,如果搜索不成功包括搜索到了但CRC错误,复位状态变量到首次搜索的状态
  336.     */
  337.     if(!search_result || !ROM_NO[0])
  338.     {
  339.         LastDiscrepancy = 0;
  340.         LastDeviceFlag = FALSE;
  341.         LastFamilyDiscrepancy = 0;
  342.         search_result = FALSE;
  343.     }
  344.     return search_result;
  345. }
  346. /*
  347. 在单总线上搜索第一个器件
  348. 返回TRUE:找到,存入ROM_NO缓冲;FALSE:无设备
  349. 先将初始化3个变量,然后调用OWSearch算法进行搜索
  350. */
  351. uint8_t OWFirst(void)
  352. {
  353.     LastDiscrepancy = 0;
  354.     LastDeviceFlag = FALSE;
  355.     LastFamilyDiscrepancy = 0;
  356.     return OWSearch();
  357. }
  358. /*
  359. 在单总线上搜索下一个器件
  360. 返回TRUE:找到,存入ROM_NO缓冲;FALSE:无设备,结束搜索
  361. 在前一轮搜索的基础上(3个变量均在前一轮搜索中有明确的值),再执行一轮搜索
  362. */
  363. uint8_t OWNext(void)
  364. {
  365.     return OWSearch();
  366. }
  367. /*
  368. 搜索并返回ROM
  369. */
  370. uint8_t DS18B20_SearchROM(void)
  371. {
  372.     uint8_t num = 0, result = 0, i;
  373.     result = OWFirst(); //搜索第一个ROM
  374.     if(result)
  375.     {
  376.         for(i=0; i<8; i++)
  377.             ID_Buff[num][i] = ROM_NO[i];
  378.         num++;
  379.     }
  380.     while(result) //搜索成功,继续搜索下一个
  381.     {
  382.         result = OWNext();
  383.         if(result)
  384.         {
  385.             for(i=0; i<8; i++)
  386.                 ID_Buff[num][i] = ROM_NO[i];
  387.             num++;
  388.         }
  389.     }
  390.     return num;
  391. }
编译下载程序,效果如下:
20210912_185441.gif