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

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