ele_eye 发表于 2012-1-31 16:13:36

关于一个RS485总线自动组网的算法

一个RS485网络,

每个子设备都带有唯一的64位ID, 和一个用于通讯的地址ADDR(8位),addr是可以修改的,出厂时都被初始化为1,

主机可以用 ADDR和某个子设备通讯 也可以用ID 和某个子设备通讯,只是帧格式不一样而已

为了提高通讯效率 一般都是使用ADDR,

在刚设备安装的时候 只能假设ADDR是随机,多个子设备中,很可能有重复的ADDR,且 ID 对于主机是未知的

怎么样才能做到(比如算法,添加必要帧命令等),主设备刚开机的时候,能检测到所有的子设备 且重新分配ADDR

就像DS18B20 单总线(或can协议中的 标示符仲裁)的那样 通过线与可扫描出每一个子设备ID 然后通过ID 重新分配每个ID子设备的ADDR

gongnn 发表于 2012-1-31 16:44:40

64位从头开始搜索一次,如果有回应的,直接分配地址,然后地址加1,继续搜索,全搜索完,ID也分配完了。

ele_eye 发表于 2012-1-31 16:52:59

回复【1楼】gongnn 魔法师
-----------------------------------------------------------------------

这太慢了 64位地址 一个一个加 那得搜索多久啊

DS18B20 的ROMID也是64位的 它是按位来搜索的 几十个挂在一条线上 1秒左右就搜索完了

我需要类似的算法

eduhf_123 发表于 2012-1-31 17:49:15

把64位的地址分成4个字节,按字节来搜索。

gongnn 发表于 2012-1-31 17:51:27

回复【3楼】eduhf_123 经历
把64位的地址分成4个字节,按字节来搜索。
-----------------------------------------------------------------------

我前面的意思也是按字节搜索啊,8个字节吧,不是4个。

gongnn 发表于 2012-1-31 17:54:31

回复【2楼】ele_eye
-----------------------------------------------------------------------

介绍下18B20的搜索算法吧,64位,1秒钟?怎么做到的?

gongnn 发表于 2012-1-31 17:55:13

感觉很神奇了,那总线速度多少了啊?



庆祝自己4000积分了!

HadesHe 发表于 2012-1-31 18:16:15

有一种算法叫做二叉树

gongnn 发表于 2012-1-31 18:20:20

回复【7楼】HadesHe
有一种算法叫做二叉树
-----------------------------------------------------------------------

这不是搜索,是遍历,速度是一样的吧。

theophilus 发表于 2012-1-31 19:03:35

这种最好就是靠广播,不过要实现多主结构, 在RS485上实现比较困难,你可以看看uLan的实现:
http://ulan.sourceforge.net/index.php?page=3

huayuliang 发表于 2012-1-31 19:20:59

回复【9楼】theophilus
-----------------------------------------------------------------------

多谢!

ele_eye 发表于 2012-1-31 19:22:16

根据DS18B20的思路
把RS485网络搜索做成这样是否可行?

首先、 主机广播 “开始进入搜索模式”,然后所有的从设备,立即进入搜索模式。
2、主机发送第一次匹配字节(0xA5 或 0x5A),如果从机的ID第0位为0(或为1),且收到的字节为0xA5(或为0x5A),表示匹配
则立马向主机发送回应0x55,否则不做回应。
3、主机此时接收存在3种情况:
a 仅仅收到一次 0x55 字节,表示此位为0的从设备只有一台
b 没有收到任何数据,表示此位为0 的从设备没有
c 收到乱码,表示多台设备同时发送造成,表示此位为0的从设备有且有1台以上,
注:c项也可能由于多台设备互相干扰,主机收不到数据,因此,需要主机对rx引脚电平检测是否有波动来判断是否有数据进来

依次类推搜索64遍就可得到一个ID ,多搜索几遍就可得到多个ID
这和DS18B20的原理一样了,对于此原理有疑问的可找一下DS18B20或DS2431等单总线的芯片看看就会明白了 达拉斯网站有详细介绍的

changhui0222 发表于 2012-1-31 19:24:19

mk

ele_eye 发表于 2012-1-31 19:31:13

回复【9楼】theophilus
这种最好就是靠广播,不过要实现多主结构, 在rs485上实现比较困难,你可以看看ulan的实现:
http://ulan.sourceforge.net/index.php?page=3

-----------------------------------------------------------------------

多主结构在RS485上是很难实现的,多主结构的前提就是有优先级仲裁机制,像CAN那样,这样才能避免多主机同时发送造成的混乱。

gongnn 发表于 2012-1-31 19:31:20

回复【11楼】ele_eye
-----------------------------------------------------------------------

感觉没有挨个点播快啊。

ele_eye 发表于 2012-1-31 19:50:58

回复【14楼】gongnn 魔法师
回复【11楼】ele_eye
-----------------------------------------------------------------------
感觉没有挨个点播快啊。
-----------------------------------------------------------------------

挨个点播? 晕倒了 64位地址你怎么挨个点播?

1 64位地址就是说有2的64次方个ID 你怎么挨个点播?

2 ADDR肯定是无法点播了,其中肯定存在重复的ADDR,只有ID 才是唯一的,才能逐个点播

3 分8字节点播也会存在 256的8次方 种情况,这也太多了


分64个位来做的好处就是, 如果有10个从设备,扫描次数绝对 < 64*10 (除非每个ID的位都有重复的 才能=64*10)

ljt80158015 发表于 2012-1-31 19:52:21

回复【13楼】ele_eye
回复【9楼】theophilus
这种最好就是靠广播,不过要实现多主结构, 在rs485上实现比较困难,你可以看看ulan的实现:
http://ulan.sourceforge.net/index.php?page=3
-----------------------------------------------------------------------
多主结构在rs485上是很难实现的,多主结构的前提就是有优先级仲裁机制,像can那样,这样才能避免多主机同时发送造成的混乱。

-----------------------------------------------------------------------

ulan应用的多么?

utopiaprince 发表于 2012-1-31 19:56:21

回复【11楼】ele_eye
根据ds18b20的思路
把rs485网络搜索做成这样是否可行?
首先、 主机广播 “开始进入搜索模式”,然后所有的从设备,立即进入搜索模式。
2、主机发送第一次匹配字节(0xa5 或 0x5a),如果从机的id第0位为0(或为1),且收到的字节为0xa5(或为0x5a),表示匹配
则立马向主机发送回应0x55,否则不做回应。
3、主机此时接收存在3种情况:
a 仅仅收到一次 0x55 字节,表示此位为0的从设备只有一台
b 没有收到任何数据,表示此位为0 的从设备没有
c 收到乱码,表示多台设备同时发送造成,表示此位为0的从设备有且有1台以上,
注:c项也可能由于多台设备互相干扰,主机收不到数据,因此,需要主机对rx引脚电平检测是否有波动来判断是否有数据进来
依次类推搜索64遍就可得到一个id ,多搜索几遍就可得到多个id
这和ds18b20的原理一样......
-----------------------------------------------------------------------

mark,给我无线组网开阔了思路。

ele_eye 发表于 2012-1-31 20:28:49

刚才看了一下以前写的 DS2431的搜索程序,感觉应该是可以实现的,且贴出DS2431的搜索程序主要代码,从中应该可以找到思路

//---------------------------------------------
//             DS2431_First
//
// 功能                开始第一次 Search
// 输入                无
// 输出            放入ROM缓存 RomCode[]
// 返回值        TRUE -- 搜索到一个!
//                 FALSE-- 无器件
//
//---------------------------------------------
bool
DS2431_First(void)
{
    uint8_t i,B,_byte,_bit;

    //初始化参数、寄存器
    clrROMBuf();
    for (i=0;i<64;i++)
    {

      NODMARK=0xff;
    };


    //--复位
    if (!DS2431_RST())
    {
      return FALSE;
    }

    //--查找ROM命令
    DS2431_WR_BYTE(DS2431CMD_SEARCH_ROM);


//-------------------------
//    开始第一次 Search
//-------------------------
    for (i=0;i<64;i++)
    {
      _delay_us(40);


      //--读取ROM第一位
      B=0;
      if ( DS2431_RD_BIT() ) B=0x02;

      //--读取ROM第一位的补码
      if ( DS2431_RD_BIT() ) B+=1;

      _byte= i / 8;
      _bit=i % 8;

      switch (B)
      {
      case 0x00:/*存在设备 此位 有 0 也有 1*/
            DS2431_WR_BIT(0);
            NODMARK |= BIT(_bit);
            ROMBuf&= ~BIT(_bit);
            //printstr("0",1);
            break;

      case 0x01:/*存在设备 此位都为 0*/
            DS2431_WR_BIT(0);
            NODMARK &= ~BIT(_bit);
            ROMBuf&= ~BIT(_bit);
            //printstr("1",1);

            break;

      case 0x02:/*存在设备 此位都为 1*/
            DS2431_WR_BIT(1);
            NODMARK &= ~BIT(_bit);
            ROMBuf|=BIT(_bit);
            //printstr("2",1);
            break;

      case 0x03:/*不存在任何设备*/
            //printstr("3",1);
            return FALSE;
            break;
      }
    }
    ROMCnt=1;
    //printBuf(ROMBuf,ROMBufLen);
    return TRUE;

}

bool
isTheLastNode(uint8_t n)
{
    uint8_t i,_byte,_bit;

    for (i=(n+1);i<64;i++)
    {
      _byte= i / 8;
      _bit=i % 8;

      if (NODMARK & BIT(_bit))
      {
            return FALSE;
      }
    }
    return TRUE;
}
bool
isNode(uint8_t n)
{
    uint8_t _byte,_bit;

    _byte= n / 8;
    _bit=n % 8;

    if (NODMARK & BIT(_bit))
    {
      return TRUE;
    }
    else
    {
      return FALSE;
    }
}

bool
isOver(void)
{
    uint8_t i;
    for (i=0;i<8;i++)
    {
      if ( NODMARK )
      {
            return FALSE;
      }
    }
    return TRUE;
}


bool
DS2431_Next(void)
{
    uint8_t x,i,B,_byte,_bit;

    if (isOver())
    {
      return FALSE;
    }
    //--复位
    if (!DS2431_RST())
    {
      return FALSE;
    }
    //--读ROM命令
    DS2431_WR_BYTE(DS2431CMD_SEARCH_ROM);


//-------------------------
//    开始第一次 Search
//-------------------------
    for (i=0;i<64;i++)
    {
      _delay_us(40);

      //--读取ROM第一位
      B=0;
      if ( DS2431_RD_BIT() ) B=0x02;

      //--读取ROM第一位的补码
      if ( DS2431_RD_BIT() ) B+=1;

      _byte= i / 8;
      _bit=i % 8;

      switch (B)
      {
      case 0x00:/*存在设备 此位 有 0 也有 1*/
            if (isNode(i))
            {
                if (isTheLastNode(i))
                {
                  DS2431_WR_BIT( 1 );
                  ROMBuf |= BIT(_bit);
                  NODMARK &= ~BIT(_bit);
                }
                else
                {
                  DS2431_WR_BIT( 0 );
                  ROMBuf &= ~BIT(_bit);
                }
            }
            else
            {
                DS2431_WR_BIT( 1 );
                ROMBuf |= BIT(_bit);
            }
            break;

      case 0x01:/*存在设备 此位都为 0*/
            DS2431_WR_BIT(0);
            NODMARK &= ~BIT(_bit);
            ROMBuf&= ~BIT(_bit);

            break;

      case 0x02:/*存在设备 此位都为 1*/
            DS2431_WR_BIT(1);
            NODMARK &= ~BIT(_bit);
            ROMBuf|=BIT(_bit);
            break;

      case 0x03:/*不存在任何设备*/
            return FALSE;
            break;
      }
    }
    ROMCnt += 1;
    //printBuf(ROMBuf,ROMBufLen);
    return TRUE;
}
//---------------------------------------------
//             DS2431_SearchROM
//
// 功能                搜索DS2431的ROM
// 输入                无
// 输出            放入ROM缓存 RomCode[]
// 返回值        TRUE -- CRC8 OK!
//                 FALSE-- CRCR8 FAIL!
//
//---------------------------------------------
uint8_t
DS2431_SearchROM(void)
{
    uint8_t X,i,B,_byte,_bit;
    uint8_t ROMCnt=0;

    if ( DS2431_First() )
    {
      for (i=0;i<8;i++)
      {
            ActiveROM=ROMBuf;
      }

      for (i=0;i<maxROMCntofBus;i++)
      {
            if ( !DS2431_Next() )
            {
                return TRUE;
            }
      }
      return TRUE;
    }
    else
    {
      return FALSE;
    }
}

sf49ers 发表于 2012-1-31 20:53:26

485不支持总线竞争,没法实现18b20的方法。
还是觉得魔法师的方法最可行

lindabell 发表于 2012-1-31 20:55:11

假如有设备坏了呢
而这时要搜索Id,是不是又问题啊

eduhf_123 发表于 2012-1-31 20:59:52

按位搜索的代价太大,主要的原因在于一般485都是借用了MCU的UART来实现,没有对位数据的支持。


按字节遍历的一种方案:

上电后,主机在2秒内开始ID遍历过程;从机等待主机的ID遍历指令3秒,未等到则主动发送短地址分配请求。

ID遍历过程:
主机发送0xA0,ID0,所有ID前8位为ID0的从机在接收到最后一个字节后的2bit时间内响应0xF0;主机根据发送后是否收到从机的响应来判断该ID域是否存在从机并做标记(或者不做标记直接进行深度优先的遍历,用8层嵌套的循环来实现)。

接上,假设前8位为ID0的ID域存在从机,则主机再发送0xA1,ID0,ID1,检测ID前8位为ID0的从机的次8位ID是否为ID1。

……,依此类推。

主机遍历完成后,发送0xAF,通知各从机,主机已完成从机ID遍历。之前遍历过程中未能在2bit内响应的从机,以及热插拔新入网的从机,都在这个过程中向主机发送短地址分配请求。

长地址命令帧格式:0xCn,ADDR,CMD,…,CMD
长地址数据帧格式:0xDn,ADDR,DAT,…,DAT


短地址命令帧格式:0x4n,ADDR,CMD,…,CMD
短地址数据帧格式:0x5n,ADDR,DAT,…,DAT

ele_eye 发表于 2012-2-1 09:39:43

回复【21楼】eduhf_123 经历
-----------------------------------------------------------------------

还是像我说的 ID0 遍历256次 每跟换ID0后 ID1遍历256次 。。。。 即 256*256*256.。。。。 这得多少次啊

nil0 发表于 2012-2-1 09:48:46

mark。。。

phone 发表于 2012-4-12 11:59:24

正准备作设备联网,参考一下。

rt-ics 发表于 2012-11-20 17:11:28

这个是个问题,观摩

OTD_WIND 发表于 2012-11-20 17:36:17

1、主机定时发‘从机查询’广播
2、从机收到广播后随机延时10~100ms
3、从机回应自身的‘从机ID’
4、主机接收‘从机回应’并校验内容
5、5.1:如果‘从机回应’内容正确,主机就通过‘从机ID’进行修改从机ADDR。然后回到1重新开始
   5.2:如果‘从机回应’内容错误,回到1重新开始

这样从机出产时,只需要相同的ADDR就行了,并且可以随时加入从机~

zhutr99 发表于 2012-11-20 20:51:26

西门子的PROFIbuS就有多主结构的,不过协议不公开

zhutr99 发表于 2012-11-20 20:51:44

485的物理协议
页: [1]
查看完整版本: 关于一个RS485总线自动组网的算法