[应用方案] Wire搜索算法详解

[复制链接]
6022|5
 楼主| 雨果喝水 发表于 2023-7-24 13:15 | 显示全部楼层 |阅读模式
C代码:


  1. #include
  2. #include

  3. sbit DQ=P0^7;  //1-wire总线
  4. sbit K1=P0^6;  //标志变量,用于观察进出某段代码所用时间
  5. sbit P_Read=P0^5; //
  6. sbit P_Write=P0^4; //

  7. // definitions
  8. #define FALSE 0
  9. #define TRUE  1

  10. //几个延时函数,供一线低级操作时调用
  11. //如果改用不同的MPU,如12T,则必须修改这几个函数,确保时间符合协议要求
  12. void Delay480us();  //@12.000MHz
  13. void Delay410us();  //@12.000MHz
  14. void Delay3_88us(unsigned char i);

  15. //一线低级操作函数
  16. bit  OWReset();       //复位
  17. void OWWriteBit(bit bit_value);  //写一位
  18. bit  OWReadBit();     //读一位
  19. void OWWriteByte(unsigned char byte_value); //写一个字节
  20. //

  21. //搜索函数
  22. bit  OWSearch();  //算法核心函数,完成一次ROM搜索过程
  23. bit  OWFirst();   //调用OWSearch完成第一次搜索
  24. bit  OWNext();   //调用OWSearch完成下一次搜索
  25. unsigned char docrc8(unsigned char value); //执行CRC校验

  26. //全局搜索变量
  27. unsigned char ROM_NO[8];  //数组,存放本次搜索到的ROM码(8个字节)
  28. char LastDiscrepancy;  //每轮搜索后指向最后一个走0的差异位
  29. char LastFamilyDiscrepancy; //指向家族码(前8位)中最后一个走0的差异位
  30. bit LastDeviceFlag;   //搜到最后一个ROM后,程序通过判别将该变量置1,下轮搜索时即会结束退出
  31. unsigned char crc8;    //CRC校验变量



  32. //--------------------------------------------------------------------------
  33. //    在单总线上搜索第一个器件
  34. // 返回TRUE: 找到, 存入ROM_NO缓冲;FALSE:无设备
  35. // 先将初始化3个变量,然后调用OWSearch算法进行搜索
  36. //--------------------------------------------------------------------------
  37. bit OWFirst()
  38. {
  39.    LastDiscrepancy = 0;
  40.    LastDeviceFlag = FALSE;
  41.    LastFamilyDiscrepancy = 0;
  42.    return OWSearch();
  43. }

  44. //--------------------------------------------------------------------------
  45. //    在单总线上搜索下一个器件
  46. // 返回TRUE: 找到, 存入ROM_NO缓冲;FALSE:无设备,结束搜索
  47. // 在前一轮搜索的基础上(3个变量均在前一轮搜索中有明确的值),再执行一轮搜索
  48. //--------------------------------------------------------------------------
  49. bit OWNext()
  50. {
  51.    return OWSearch();
  52. }







  53. //--------------------------------------------------------------------------
  54. //     单总线搜索算法,利用了一些状态变量,这是算法的核心程序,代码也较长
  55. //     返回TRUE: 找到, 存入ROM_NO缓冲;FALSE:无设备,结束搜索
  56. //--------------------------------------------------------------------------
  57. bit OWSearch()
  58. {
  59.    char id_bit_number;    //指示当前搜索ROM位(取值范围为1-64)
  60.      //下面三个状态变量含义:
  61.   //last_zero:  指针,记录一次搜索(ROM1-64位)最后一位往0走的混码点编号
  62.   //search_direction:搜索某一位时选择的搜索方向(0或1),也是“一写”的bit位值
  63.   //rom_byte_number: ROM字节序号,作为ROM_no[]数组的下标,取值为1—8
  64.    char last_zero, rom_byte_number, search_result;

  65.    bit id_bit, cmp_id_bit,search_direction; //二读(正码、反码)、及一写(决定二叉搜索方向)
  66.    unsigned char rom_byte_mask ; //ROM字节掩码,

  67.    // 初始化本次搜索变量
  68.    id_bit_number = 1;
  69.    last_zero = 0;
  70.    rom_byte_number = 0;
  71.    rom_byte_mask = 1;
  72.    search_result = 0;
  73.    crc8 = 0;

  74. // ------------------------------------------------------------------
  75. //1。是否搜索完成(已到最后一个设备)?
  76. //-------------------------------------------------------------------
  77.    if (!LastDeviceFlag)  // LastDeviceFlag由上轮搜索确定是否为最后器件,当然首次进入前必须置False
  78.    {
  79.       if (OWReset())    //复位总线
  80.       {
  81.          LastDiscrepancy = 0;  //复位几个搜索变量
  82.          LastDeviceFlag = FALSE;
  83.          LastFamilyDiscrepancy = 0;
  84.          return FALSE;    //如果无应答,返回F,退出本轮搜索程序
  85.       }

  86.       OWWriteByte(0xF0);   //发送ROM搜索命令F0H
  87.    Delay3_88us(60);

  88. //=====================================================================
  89. // 开始循环处理1-64位ROM,每位必须进行“二读”后进行判断,确定搜索路径
  90. // 然后按选定的路径进行“一写”,直至完成全部位的搜索,这样一次循环
  91. // 可以完成一轮搜索,找到其中一个ROM码。
  92. //=====================================================================
  93.       do        //逐位读写搜索,1-64位循环
  94.       {
  95.          id_bit = OWReadBit();   //二读:先读正码、再读反码
  96.          cmp_id_bit = OWReadBit();

  97.          if (id_bit  && cmp_id_bit)  //二读11,则无器件退出程序
  98.             break;
  99.          else       //二读不为11,则需分二种情况
  100.          {
  101.             //*********************************************
  102.    // 第一种情况:01或10,直接可明确搜索方向
  103.             if (id_bit != cmp_id_bit)
  104.                search_direction = id_bit;  // 记下搜索方向search_direction的值待“一写”

  105.    //*********************************************
  106.             else
  107.             {
  108.                // 否则就是第二种情况:遇到了混码点,需分三种可能分析:
  109.                // 1。当前位未到达上轮搜索的“最末走0混码点”(由LastDiscrepancy存储)
  110.       //    说明当前经历的是一个老的混码点,判别特征为当前位在(小于)LastDiscrepancy前
  111.       //    不管上次走的是0还是1,只需按上次走的路即可,该值需从ROM_NO中的当前位获取
  112.                if (id_bit_number < LastDiscrepancy)
  113.                   search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0);
  114.       // 从
  115.               
  116.       else
  117.                // 2。当前位正好为上轮标记的最末的混码点,这个混码点也就是上次走0的点
  118.       //    那么这次就需要走1
  119.       // 3。除去上二种可能,那就是第3种可能: 这是一个新的混码点,
  120.       //    id_bit_number>LastDiscrepancy
  121.       //。。然而下一条语句巧妙地将上二种可能合在一起处理,看不懂我也没办法了
  122.                   search_direction = (id_bit_number == LastDiscrepancy);
  123.    
  124.    //************************************************

  125.             // 确定了混码点的路径方向还没完事,还需要更新一个指针:last_zero
  126.    // 这个指针每搜索完一位后(注意是一bit不是一轮)总是指向新的混码点
  127.    // 凡遇到新的混码点,我们按算法都是先走0,所以凡遇走0的混码点必须更新此指针
  128.                if (search_direction == 0)
  129.                {
  130.                   last_zero = id_bit_number;

  131.                   // 下面二条是程序的高级功能了:64位ROM中的前8位是器件的家族代码,
  132.       // 用LastFamilyDiscrepancy这个指针来记录前8位ROM中的最末一个混码点
  133.       // 可用于在多类型器件的单线网络中对家族分组进行操作
  134.                   if (last_zero < 9)
  135.                      LastFamilyDiscrepancy = last_zero;
  136.                }
  137.             }

  138.             // 确定了要搜索的方向search_direction,该值即ROM中当前位的值,需要写入ROM
  139.             // 然而64位ROM需分8个字节存入ROM_NO[],程序使用了一个掩码字节rom_byte_mask
  140.    // 以最低位为例:该字节值为00000001,如记录1则二字节或,写0则与反掩码
  141.             if (search_direction == 1)
  142.                ROM_NO[rom_byte_number] |= rom_byte_mask;
  143.             else
  144.                ROM_NO[rom_byte_number] &= ~rom_byte_mask;

  145.             // 关键的一步操作终于到来了:一写
  146.             OWWriteBit(search_direction);

  147.             // 一个位的操作终于完成,但还需做些工作,以准备下一位的操作:
  148.             // 包括:位变量id_bit_number指向下一位;字节掩码左移一位
  149.             id_bit_number++;
  150.             rom_byte_mask <<= 1;

  151.             // 如果够8位一字节了,则对该字节计算CRC处理、更新字节号变量、重设掩码
  152.             if (rom_byte_mask == 0)
  153.             {
  154.                 docrc8(ROM_NO[rom_byte_number]);  // CRC计算原理参考其他文章
  155.                 rom_byte_number++;
  156.                 rom_byte_mask = 1;
  157.             }
  158.          }
  159.       }
  160.       while(rom_byte_number < 8);  // ROM bytes编号为 0-7
  161.    //流程图中描述从1到64的位循环,本代码中是利用rom_byte_number<8来判断的
  162.    //至此,终于完成8个字节共64位的循环处理

  163. //=================================================================================

  164.       // 一轮搜索成功,找到的一个ROM码也校验OK,则还要处理二个变量
  165.       if (!((id_bit_number < 65) || (crc8 != 0)))
  166.       {
  167.          // 一轮搜索结束后,变量last_zero指向了本轮中最后一个走0的混码位
  168.    // 然后再把此变量保存在LastDiscrepancy中,用于下一轮的判断
  169.    // 当然,last_zero在下轮初始为0,搜索是该变量是不断变动的
  170.         LastDiscrepancy = last_zero;

  171.          // 如果这个指针为0,说明全部搜索结束,再也没有新ROM号器件了
  172.          if (LastDiscrepancy == 0)
  173.             LastDeviceFlag = TRUE;  //设置结束标志
  174.         
  175.          search_result = TRUE;  //返回搜索成功
  176.       }
  177.    }

  178. // ------------------------------------------------------------------
  179. //搜索完成,如果搜索不成功包括搜索到了但CRC错误,复位状态变量到首次搜索的状态。
  180. //-------------------------------------------------------------------
  181.    if (!search_result || !ROM_NO[0])
  182.    {
  183.       LastDiscrepancy = 0;
  184.       LastDeviceFlag = FALSE;
  185.       LastFamilyDiscrepancy = 0;
  186.       search_result = FALSE;
  187.    }
  188.    return search_result;
  189. }

  190. //=====================================================================
  191.    至此,OWSearch函数结束。函数实现的是一轮搜索,如成功,可得到一个ROM码
  192. //=======================================================================


  193. //=====================================================================
  194. // 1-Wire函数调用所需的延时函数,注意不同MCU下须重调参数
  195. //=====================================================================

  196. void Delay480us()  //@12.000MHz
  197. {
  198. unsigned char i, j;
  199. i = 2;
  200. j = 90;
  201. do
  202. {
  203.   while (--j);
  204. } while (--i);
  205. }

  206. void Delay410us()  //@12.000MHz
  207. {
  208. unsigned char i, j;
  209. i = 2;
  210. j = 40;
  211. do
  212. {
  213.   while (--j);
  214. } while (--i);
  215. }


  216. void Delay3_88us(unsigned char i)  //@12.000MHz  只能3-80us
  217. {
  218. //i*=3;
  219. i-=1;
  220. while (--i);
  221. }
  222.   


  223. //=====================================================================
  224. //  一线函数:复位、读一位、写一位、写一字节
  225. //=====================================================================
  226. bit OWReset()
  227. {
  228.         bit result;
  229.   unsigned char i;
  230.         DQ=0;    // 拉低总线启动复位信号
  231.         Delay480us();  
  232.         DQ=1;    // Releases the bus
  233.   i=53;   //延时70us
  234.   while(--i);
  235.         result = DQ;  // 采样总线上从机存在信号
  236.         Delay410us();  //
  237.         return result;  //
  238. }

  239. //-----------------------------------------------------------------------------
  240. void OWWriteBit(bit dat)  //写一位函数
  241. //
  242. {
  243.   unsigned char i;
  244.   P_Write=1;     //写函数执行的标志变量,用于调试
  245.         if (dat)    //写1
  246.         {
  247.                 DQ=0;    // Drives DQ low
  248.                 i=4;     // 延时6us
  249. u
  250.     while(--i);
  251.                 DQ=1;    // 释放总线
  252.                 i=38;     // 延时54us,不要看i的值,实测为54us
  253.     while(--i);  //
  254.         }
  255.         else     //写0
  256.         {
  257.                 DQ=0;    //
  258.     i=35;     // 延时60
  259.     while(--i);
  260.                 DQ=1;    // 释放总线
  261.     i=7;   // 延时10
  262.     while(--i);
  263.         }
  264.   P_Write=0;
  265. }

  266. //-----------------------------------------------------------------------------
  267. bit OWReadBit()     //读一位
  268. {
  269.         bit result;
  270.   unsigned char i;
  271.      P_Read=1;    // 时间隙6+9+55
  272.         DQ=0;      // Drives DQ low
  273.         i=8;   //6
  274.   while(--i);
  275.         DQ=1;      // 释放
  276.         i=6;   //9
  277.   while(--i);
  278.         result = DQ;    // 取样总线
  279.         i=32;   //45
  280.   while(--i);     //
  281.     P_Read=0;
  282.         return result;
  283. }

  284. //-----------------------------------------------------------------------------
  285. void OWWriteByte(unsigned char dat) //写一字节
  286. {
  287.         char loop;
  288.         for (loop = 0; loop < 8; loop++) //低位先传
  289.         {
  290.                 OWWriteBit(dat & 0x01);
  291.                 dat >>= 1;     //右移
  292.          }
  293. }

  294. //-----------------------------------------------------------------------------
  295. char OWReadByte(void)  //读一字节,本例中并未用到
  296. {
  297.         char loop, result=0;
  298.         for (loop = 0; loop < 8; loop++)
  299.         {
  300.           result >>= 1;
  301.             if (OWReadBit())
  302.              result |= 0x80;
  303.         }
  304.         return result;
  305. }

  306. //  CRC计算用表
  307. static unsigned char dscrc_table[] = {
  308.         0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
  309.       157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
  310.        35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
  311.       190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
  312.        70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
  313.       219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
  314.       101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
  315.       248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
  316.       140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
  317.        17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
  318.       175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
  319.        50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
  320.       202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
  321.        87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
  322.       233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
  323.       116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
  324.   

  325. //--------------------------------------------------------------------------
  326. //迭代计算CRC,返回当前CRC值
  327. unsigned char docrc8(unsigned char value)
  328. {
  329.    // 详见应用笔记AN27
  330.    crc8 = dscrc_table[crc8 ^ value];   //^表示按位异或
  331.    return crc8;
  332. }

  333. //主函数,
  334. void main()
  335. {
  336.    bit rslt;
  337.    K1=0;   //这三个位用于观察“二读一写”
  338.    P_Read=0;
  339.    P_Write=0;

  340.    rslt = OWFirst();  //搜索第一个ROM
  341.    while (rslt)   //如果搜索成功,继续搜索下一个
  342.    {
  343.       rslt = OWNext();
  344.    }
  345.    while(1);
  346. }



小夏天的大西瓜 发表于 2023-7-25 12:44 | 显示全部楼层
笙泉THO65B
LOVEEVER 发表于 2023-7-26 17:47 | 显示全部楼层
Wire搜索算法具体原理是如何进行的
小小蚂蚁举千斤 发表于 2023-7-26 19:44 | 显示全部楼层
Wire搜索算法看着挺高大上,实际应用多吗
weifeng90 发表于 2023-8-6 16:18 来自手机 | 显示全部楼层
学到了,还第一次听说。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

1213

帖子

0

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