关于一个RS485总线自动组网的算法
一个RS485网络,每个子设备都带有唯一的64位ID, 和一个用于通讯的地址ADDR(8位),addr是可以修改的,出厂时都被初始化为1,
主机可以用 ADDR和某个子设备通讯 也可以用ID 和某个子设备通讯,只是帧格式不一样而已
为了提高通讯效率 一般都是使用ADDR,
在刚设备安装的时候 只能假设ADDR是随机,多个子设备中,很可能有重复的ADDR,且 ID 对于主机是未知的
怎么样才能做到(比如算法,添加必要帧命令等),主设备刚开机的时候,能检测到所有的子设备 且重新分配ADDR
就像DS18B20 单总线(或can协议中的 标示符仲裁)的那样 通过线与可扫描出每一个子设备ID 然后通过ID 重新分配每个ID子设备的ADDR 64位从头开始搜索一次,如果有回应的,直接分配地址,然后地址加1,继续搜索,全搜索完,ID也分配完了。 回复【1楼】gongnn 魔法师
-----------------------------------------------------------------------
这太慢了 64位地址 一个一个加 那得搜索多久啊
DS18B20 的ROMID也是64位的 它是按位来搜索的 几十个挂在一条线上 1秒左右就搜索完了
我需要类似的算法 把64位的地址分成4个字节,按字节来搜索。 回复【3楼】eduhf_123 经历
把64位的地址分成4个字节,按字节来搜索。
-----------------------------------------------------------------------
我前面的意思也是按字节搜索啊,8个字节吧,不是4个。 回复【2楼】ele_eye
-----------------------------------------------------------------------
介绍下18B20的搜索算法吧,64位,1秒钟?怎么做到的? 感觉很神奇了,那总线速度多少了啊?
庆祝自己4000积分了! 有一种算法叫做二叉树 回复【7楼】HadesHe
有一种算法叫做二叉树
-----------------------------------------------------------------------
这不是搜索,是遍历,速度是一样的吧。 这种最好就是靠广播,不过要实现多主结构, 在RS485上实现比较困难,你可以看看uLan的实现:
http://ulan.sourceforge.net/index.php?page=3 回复【9楼】theophilus
-----------------------------------------------------------------------
多谢! 根据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等单总线的芯片看看就会明白了 达拉斯网站有详细介绍的 mk 回复【9楼】theophilus
这种最好就是靠广播,不过要实现多主结构, 在rs485上实现比较困难,你可以看看ulan的实现:
http://ulan.sourceforge.net/index.php?page=3
-----------------------------------------------------------------------
多主结构在RS485上是很难实现的,多主结构的前提就是有优先级仲裁机制,像CAN那样,这样才能避免多主机同时发送造成的混乱。 回复【11楼】ele_eye
-----------------------------------------------------------------------
感觉没有挨个点播快啊。 回复【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) 回复【13楼】ele_eye
回复【9楼】theophilus
这种最好就是靠广播,不过要实现多主结构, 在rs485上实现比较困难,你可以看看ulan的实现:
http://ulan.sourceforge.net/index.php?page=3
-----------------------------------------------------------------------
多主结构在rs485上是很难实现的,多主结构的前提就是有优先级仲裁机制,像can那样,这样才能避免多主机同时发送造成的混乱。
-----------------------------------------------------------------------
ulan应用的多么? 回复【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,给我无线组网开阔了思路。 刚才看了一下以前写的 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;
}
} 485不支持总线竞争,没法实现18b20的方法。
还是觉得魔法师的方法最可行 假如有设备坏了呢
而这时要搜索Id,是不是又问题啊 按位搜索的代价太大,主要的原因在于一般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 回复【21楼】eduhf_123 经历
-----------------------------------------------------------------------
还是像我说的 ID0 遍历256次 每跟换ID0后 ID1遍历256次 。。。。 即 256*256*256.。。。。 这得多少次啊 mark。。。 正准备作设备联网,参考一下。 这个是个问题,观摩 1、主机定时发‘从机查询’广播
2、从机收到广播后随机延时10~100ms
3、从机回应自身的‘从机ID’
4、主机接收‘从机回应’并校验内容
5、5.1:如果‘从机回应’内容正确,主机就通过‘从机ID’进行修改从机ADDR。然后回到1重新开始
5.2:如果‘从机回应’内容错误,回到1重新开始
这样从机出产时,只需要相同的ADDR就行了,并且可以随时加入从机~ 西门子的PROFIbuS就有多主结构的,不过协议不公开 485的物理协议
页:
[1]