搜索
bottom↓
回复: 28

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

[复制链接]

出0入0汤圆

发表于 2012-1-31 16:13:36 | 显示全部楼层 |阅读模式
一个RS485网络,

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

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

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

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

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

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

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2012-1-31 16:44:40 | 显示全部楼层
64位从头开始搜索一次,如果有回应的,直接分配地址,然后地址加1,继续搜索,全搜索完,ID也分配完了。

出0入0汤圆

 楼主| 发表于 2012-1-31 16:52:59 | 显示全部楼层
回复【1楼】gongnn 魔法师
-----------------------------------------------------------------------

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

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

我需要类似的算法

出0入0汤圆

发表于 2012-1-31 17:49:15 | 显示全部楼层
把64位的地址分成4个字节,按字节来搜索。

出0入0汤圆

发表于 2012-1-31 17:51:27 | 显示全部楼层
回复【3楼】eduhf_123 经历
把64位的地址分成4个字节,按字节来搜索。
-----------------------------------------------------------------------

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

出0入0汤圆

发表于 2012-1-31 17:54:31 | 显示全部楼层
回复【2楼】ele_eye
-----------------------------------------------------------------------

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

出0入0汤圆

发表于 2012-1-31 17:55:13 | 显示全部楼层
感觉很神奇了,那总线速度多少了啊?



庆祝自己4000积分了!

出0入0汤圆

发表于 2012-1-31 18:16:15 | 显示全部楼层
有一种算法叫做二叉树

出0入0汤圆

发表于 2012-1-31 18:20:20 | 显示全部楼层
回复【7楼】HadesHe
有一种算法叫做二叉树
-----------------------------------------------------------------------

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

出0入0汤圆

发表于 2012-1-31 19:03:35 | 显示全部楼层
这种最好就是靠广播,不过要实现多主结构, 在RS485上实现比较困难,你可以看看uLan的实现:
http://ulan.sourceforge.net/index.php?page=3

出0入0汤圆

发表于 2012-1-31 19:20:59 | 显示全部楼层
回复【9楼】theophilus  
-----------------------------------------------------------------------

多谢!

出0入0汤圆

 楼主| 发表于 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等单总线的芯片看看就会明白了 达拉斯网站有详细介绍的

出0入0汤圆

发表于 2012-1-31 19:24:19 | 显示全部楼层
mk

出0入0汤圆

 楼主| 发表于 2012-1-31 19:31:13 | 显示全部楼层
回复【9楼】theophilus
这种最好就是靠广播,不过要实现多主结构, 在rs485上实现比较困难,你可以看看ulan的实现:
http://ulan.sourceforge.net/index.php?page=3

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

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

出0入0汤圆

发表于 2012-1-31 19:31:20 | 显示全部楼层
回复【11楼】ele_eye
-----------------------------------------------------------------------

感觉没有挨个点播快啊。

出0入0汤圆

 楼主| 发表于 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)

出0入0汤圆

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

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

ulan应用的多么?

出0入0汤圆

发表于 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,给我无线组网开阔了思路。

出0入0汤圆

 楼主| 发表于 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[_byte] |= BIT(_bit);
            ROMBuf[0][_byte]&= ~BIT(_bit);
            //printstr("0",1);
            break;

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

            break;

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

        case 0x03:  /*不存在任何设备*/
            //printstr("3",1);
            return FALSE;
            break;
        }
    }
    ROMCnt=1;
    //printBuf(ROMBuf[0],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[_byte] & BIT(_bit))
        {
            return FALSE;
        }
    }
    return TRUE;
}
bool
isNode(uint8_t n)
{
    uint8_t _byte,_bit;

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

    if (NODMARK[_byte] & 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[ROMCnt][_byte] |= BIT(_bit);
                    NODMARK[_byte] &= ~BIT(_bit);
                }
                else
                {
                    DS2431_WR_BIT( 0 );
                    ROMBuf[ROMCnt][_byte] &= ~BIT(_bit);
                }
            }
            else
            {
                DS2431_WR_BIT( 1 );
                ROMBuf[ROMCnt][_byte] |= BIT(_bit);
            }
            break;

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

            break;

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

        case 0x03:  /*不存在任何设备*/
            return FALSE;
            break;
        }
    }
    ROMCnt += 1;
    //printBuf(ROMBuf[ROMCnt-1],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[0];
        }

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

出0入0汤圆

发表于 2012-1-31 20:53:26 | 显示全部楼层
485不支持总线竞争,没法实现18b20的方法。
还是觉得魔法师的方法最可行

出0入8汤圆

发表于 2012-1-31 20:55:11 | 显示全部楼层
假如有设备坏了呢
而这时要搜索Id,是不是又问题啊

出0入0汤圆

发表于 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,ID0ID1,检测ID前8位为ID0的从机的次8位ID是否为ID1。

……,依此类推。

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

长地址命令帧格式:0xCn,ADDR,CMD[0],…,CMD[n-1]
长地址数据帧格式:0xDn,ADDR,DAT[0],…,DAT[n-1]


短地址命令帧格式:0x4n,ADDR,CMD[0],…,CMD[n-1]
短地址数据帧格式:0x5n,ADDR,DAT[0],…,DAT[n-1]

出0入0汤圆

 楼主| 发表于 2012-2-1 09:39:43 | 显示全部楼层
回复【21楼】eduhf_123 经历
-----------------------------------------------------------------------

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

出0入0汤圆

发表于 2012-2-1 09:48:46 | 显示全部楼层
mark。。。

出0入0汤圆

发表于 2012-4-12 11:59:24 | 显示全部楼层
正准备作设备联网,参考一下。

出0入0汤圆

发表于 2012-11-20 17:11:28 | 显示全部楼层
这个是个问题,观摩

出0入0汤圆

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

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

出0入0汤圆

发表于 2012-11-20 20:51:26 | 显示全部楼层
西门子的PROFIbuS就有多主结构的,不过协议不公开

出0入0汤圆

发表于 2012-11-20 20:51:44 | 显示全部楼层
485的物理协议
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 04:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表