基于设备唯一标识符的CAN网络临时编址方法

1. 案例概述

  在制作在线升级软件的CAN通信协议时,为了能够对多个MCU进行同时升级,并且可靠地获取每个MCU的升级状态,需要对MCU的CAN节点进行编址。

  假如我们只想实现每次仅仅升级一个MCU,那就没必要对其进行编址了。

  假如我们想要每次升级多个MCU,并且采用对正确结果不回复、只对错误结果回复的方式,也不必编址。只有升级失败的MCU回复失败标志,但这样统计的结果却是不可靠的。


2. 问题原因分析

  在过去,曾经使用拨码开关,对电池均衡装置的24个模块手工编址。但是拨码开关并不是标准配置,退一步说,作为通用Boot loader当然也不应该依赖拨码开关,所以要考虑一种不需要拨码开关的自动编址方法。我们使用的STM系列芯片,每个芯片有一个全球唯一的“设备唯一标识符”(Unique Device ID),简称UDID。这是能够可靠地区分各个芯片的数据。

  接下来考虑CAN通信的特点。CAN通信是一种不分主机从机的通信,所有节点都可以主动发送报文,这是其他通信方式,比如以485为例的串口等,所不具备的。为了防止多个节点同时发送不同报文产生的错误,CAN通信采用仲裁机制对报文的ID进行仲裁。因为CAN通信的仲裁机制不对DLC和DATA进行仲裁,所以我们不可以通过DATA传递UDID信息的。上位机要想获取所有MCU的UDID信息,MCU就必须将UDID信息放置在CAN报文的ID中。

3. 解决方案

  我们分析一下STM系列芯片的UDID的特点,以及CAN报文ID的特点。UDID是96位的整数,而CAN报文ID分两种情况,标准帧有11位,扩展帧有29位。为了每条报文能够容纳尽可能多的信息,我们采用扩展帧格式。将96位的UDID分成若干组,分别放置在CAN报文ID中,只要简单的算术就能算出,至少要拆成4组,每组24位。这样,ID剩余了5位,这5位能够表达32种命令,对一个简单的Boot loader来说,已经足够了。

  我们通过4条不同的召唤报文,将4组UDID片段分别召唤上来。上位机要做的事情,当然就是压缩了,将每个24位的片段压缩成n位,然后将压缩前的片段和压缩后的片段,它们的对应关系广播给全体MCU。MCU收到所有的对应关系,对照自身的UDID,算出属于自己的4组压缩后的片段。MCU将4组压缩后的片段,再次拼接起来,得到4n位的压缩UDID,作为自己的地址,传送给上位机。上位机收到地址,便可知道对应MCU的存在。

  首先考虑压缩的办法,这里使用最单纯的占座法。将收到的片段,按照时间顺序,从1开始对号入座。

  接下来考虑n的取值,要想将4n位全部放置在CAN的29位ID中,n能取到的最大值是7,这样理论上可以区分128个不同的MCU。但这样会对CAN报文的ID空间造成很大破坏。退而求其次,n取值为6,这样理论上可以区分64个不同的MCU,4组压缩后的地址片段共占用24位,这和压缩前的每个地址片段的位数恰好是一样的,也便于制作协议。考虑到地址中不宜出现全0和全1,以及保留一些情况以备扩展,我们编号时,从1到60进行编号,这样就避开了全0的0号和全1的63号,同时保留了61号和62号备用。目前我们的产品,同时连接60个已经足够了。

  上位机收到24位的压缩UDID后,将其拆成4组,每组6位,并各自解压缩,便可还原出真实的UDID。

  这里其实涉及一个说法问题。两片MCU的UDID一定不同,但是其分成4份之后,1片MCU的某1份很可能和其他MCU的对应位置的1份相同。考虑最极端的情况,我们连接了60 × 60 × 60 × 60个MCU(暂不考虑CAN网络本身容纳节点的能力),而它们的UDID在压缩后,每个UDID片段,在去除重复后都只剩60个不同的。这样的结果,使得所有MCU都正常识别了。但我们不能因此而宣称我们支持60 × 60 × 60 × 60个MCU的同时升级,我们只能宣称我们支持60个MCU可以同时升级。为了避免多于60个的MCU的成功编址,上位机要最后加一个确认报文,认可60个以内的MCU的地址,否决第60个以后的MCU的地址。

4. 实践情况

  定义上位机与MCU的通信协议。上位机不必给自己编址,只要注意避免发生ID冲突即可。下面是协议中有关编址的部分。

4.1. 请求UDID

  上位机向MCU请求各自的UDID。每次召唤其中的1组。

表 4-1 由PC发给MCU的UDID请求

ID[28:24]

0x13

BYTE0

UDID_NO

请求ID的组号

ID[23:16]

0x00

 

 

 

ID[15:8]

0x00

 

 

 

ID[7:0]

0x13

 

 

 

DLC

1

 

 

 

  请求UDID的组号的有效取值及其含义如下表:

表 4-2 请求UDID的组号与片段的关系

UDID_NO

0x14

0x15

0x16

0x17

UDID片段

UDID[95:72]

UDID[71:48]

UDID[47:24]

UDID[23:0]

  MCU收到请求后,回送对应的UDID片段。

表 4-3 由MCU发给PC的UDID回应

UDID_NO

0x14

0x15

0x16

0x17

ID[28:24]

0x14

0x15

0x16

0x17

ID[23:16]

UDID[95:88]

UDID[71:64]

UDID[47:40]

UDID[23:16]

ID[15:8]

UDID[87:80]

UDID[63:56]

UDID[39:32]

UDID[15:8]

ID[7:0]

UDID[79:72]

UDID[55:48]

UDID[31:24]

UDID[7:0]

DLC

1

1

1

1

BYTE0

0x00

0x00

0x00

0x00

  这些报文有相同的DLC和数据场,所以可避免由报文冲突引发的总线错误。

4.2. 分配地址

  上位机通过收集UDID的报文,统计每个UDID片段并各自独立编号:

  1°统计UDID[95:72]的数据,去掉重复数据后,以报文中的时间标识为准,取不超过60个数据,从1开始分配编号,这个编号记做ADDR_I;

  2°统计UDID[71:48]的数据,去掉重复数据后,以报文中的时间标识为准,取不超过60个数据,从1开始分配编号,这个编号记做ADDR_J;  

  3°统计UDID[47:24]的数据,去掉重复数据后,以报文中的时间标识为准,取不超过60个数据,从1开始分配编号,这个编号记做ADDR_K;

  4°统计UDID[23:0]的数据,去掉重复数据后,以报文中的时间标识为准,取不超过60个数据,从1开始分配编号,这个编号记做ADDR_L。

表 4-4 由PC发给MCU的分配ADDR_I报文

ID[28:24]

0x13

BYTE0

UDID[95:88]

UDID[95:72]

ID[23:16]

0x00

BYTE1

UDID[87:80]

ID[15:8]

0x00

BYTE2

UDID[79:72]

ID[7:0]

0x14

BYTE3

ADDR_I

地址片段I

DLC

4

 

 

 

表 4-5 由PC发给MCU的分配ADDR_J报文

ID[28:24]

0x13

BYTE0

UDID[71:64]

UDID[71:48]

ID[23:16]

0x00

BYTE1

UDID[63:56]

ID[15:8]

0x00

BYTE2

UDID[55:48]

ID[7:0]

0x15

BYTE3

ADDR_J

地址片段J

DLC

4

 

 

 

表 4-6 由PC发给MCU的分配ADDR_K报文

ID[28:24]

0x13

BYTE0

UDID[47:40]

UDID[47:24]

ID[23:16]

0x00

BYTE1

UDID[39:32]

ID[15:8]

0x00

BYTE2

UDID[31:24]

ID[7:0]

0x16

BYTE3

ADDR_K

地址片段K

DLC

4

 

 

 

表 4-7 由PC发给MCU的分配ADDR_L报文

ID[28:24]

0x13

BYTE0

UDID[23:16]

UDID[23:0]

ID[23:16]

0x00

BYTE1

UDID[15:8]

ID[15:8]

0x00

BYTE2

UDID[7:0]

ID[7:0]

0x17

BYTE3

ADDR_L

地址片段L

DLC

4

 

 

 

  地址片段与地址的拼接关系如下表:

表 4-8 地址片段与地址的拼接关系

ADDR

ADDR_I

ADDR_J

ADDR_K

ADDR_L

DEV

DEV[23:18]

DEV[17:12]

DEV[11:6]

DEV[5:0]

  MCU得到完整的装置地址后,将自身的内置Flash容量回复给上位机。

表 4-9 由MCU发给PC的设备信息

ID[28:24]

0x0C

BYTE0

FLASH_KB[15:8]

Flash容量/KB

ID[23:16]

DEV[23:16]

BYTE1

FLASH_KB[7:0]

ID[15:8]

DEV[15:8]

BYTE2

USER_VER[15:8]

User版本

ID[7:0]

DEV[7:0]

BYTE3

USER_VER[7:0]

DLC

8

BYTE4

0xFF

保留备用

不做验证

 

 

BYTE5

0xFF

 

 

BYTE6

0xFF

 

 

BYTE7

0xFF

  User版本:STM32F107版本是0xF107,STM32F439版本是0xF439,……

  上位机据此报文,按照时间顺序取60个有效地址,发送允许升级报文。

表 4-10 由PC发给MCU的允许升级报文

ID[28:24]

0x13

BYTE0

DEV[23:16]

装置地址

ID[23:16]

0x00

BYTE1

DEV[15:8]

ID[15:8]

0x00

BYTE2

DEV[7:0]

ID[7:0]

0x0C

BYTE3

UP_ALLOW

升级允许标志

DLC

4

 

 

 

  其中:DEV是允许升级的装置地址;

  UP_ALLOW是升级允许标志,0x55表示允许升级,0xAA表示禁止升级。

5. 效果评价

  效果和我们预期的一致。

  拿12个MCU进行测试,截取上位机记录的编址部分的报文如下:

     TX: 13000013 14 
                  RX: 14431337 00 
                  RX: 14431337 00 
                  RX: 14431339 00 
                  RX: 14431556 00 
                  RX: 14431557 00 
                  RX: 14431757 00 
                  RX: 14432042 00 
     TX: 13000013 15 
                  RX: 15253232 00 
                  RX: 15343135 00 
                  RX: 15403135 00 
                  RX: 15413135 00 
                  RX: 15433135 00 
                  RX: 15483135 00 
                  RX: 15503135 00 
                  RX: 15513135 00 
                  RX: 15523135 00 
                  RX: 15533135 00 
                  RX: 15593135 00 
                  RX: 15613135 00 
     TX: 13000013 16 
                  RX: 164E3405 00 
                  RX: 16524105 00 
     TX: 13000013 17 
                  RX: 17D2FF32 00 
                  RX: 17D5FF32 00 
                  RX: 17D6FF32 00 
                  RX: 17D6FF34 00 
                  RX: 17D7FF32 00 
                  RX: 17DAFF32 00 
     TX: 13000014 43 13 37 01 
     TX: 13000014 43 13 39 02 
     TX: 13000014 43 15 56 03 
     TX: 13000014 43 15 57 04 
     TX: 13000014 43 17 57 05 
     TX: 13000014 43 20 42 06 
     TX: 13000015 25 32 32 01 
     TX: 13000015 34 31 35 02 
     TX: 13000015 40 31 35 03 
     TX: 13000015 41 31 35 04 
     TX: 13000015 43 31 35 05 
     TX: 13000015 48 31 35 06 
     TX: 13000015 50 31 35 07 
     TX: 13000015 51 31 35 08 
     TX: 13000015 52 31 35 09 
     TX: 13000015 53 31 35 0A 
     TX: 13000015 59 31 35 0B 
     TX: 13000015 61 31 35 0C 
     TX: 13000016 4E 34 05 01 
     TX: 13000016 52 41 05 02 
     TX: 13000017 D2 FF 32 01 
                  RX: 0C045081 01 00 F1 07 
     TX: 13000017 D5 FF 32 02 
                  RX: 0C046082 01 00 F1 07 
                  RX: 0C048082 01 00 F1 07 
                  RX: 0C049082 01 00 F1 07 
                  RX: 0C0CB082 01 00 F1 07 
                  RX: 0C10C082 01 00 F1 07 
     TX: 13000017 D6 FF 32 03 
                  RX: 0C043083 01 00 F1 07 
                  RX: 0C14A083 01 00 F1 07 
     TX: 13000017 D6 FF 34 04 
                  RX: 0C181044 01 00 F1 07 
     TX: 13000017 D7 FF 32 05 
                  RX: 0C082085 01 00 F1 07 
                  RX: 0C047085 01 00 F1 07 
     TX: 13000017 DA FF 32 06 
                  RX: 0C044086 01 00 F1 07 
     TX: 1300000C 04 50 81 55 
     TX: 1300000C 04 60 82 55 
     TX: 1300000C 04 80 82 55 
     TX: 1300000C 04 90 82 55 
     TX: 1300000C 0C B0 82 55 
     TX: 1300000C 10 C0 82 55 
     TX: 1300000C 04 30 83 55 
     TX: 1300000C 14 A0 83 55 
     TX: 1300000C 18 10 44 55 
     TX: 1300000C 08 20 85 55 
     TX: 1300000C 04 70 85 55 
     TX: 1300000C 04 40 86 55 

  上位机成功整理出这12片MCU的UDID,如下(分4组以十六进制显示):

     431337.433135.524105.D2FF32
     431337.483135.524105.D5FF32
     431337.513135.524105.D5FF32
     431337.523135.524105.D5FF32
     431556.593135.524105.D5FF32
     431557.613135.524105.D5FF32
     431337.403135.524105.D6FF32
     431757.533135.524105.D6FF32
     432042.253232.4E3405.D6FF34
     431339.343135.524105.D7FF32
     431337.503135.524105.D7FF32
     431337.413135.524105.DAFF32

  我们看一看上位机发出的第一条报文,即TX: 13000013 14。

  理论上应该收到12条报文回复,但实际上只收到了7条,可以看出,其中有很多条,由于报文内容完全一致,在报文仲裁时同时通过仲裁,使得上位机只收到了1条。但也并非所有的相同报文都这样合并了,实际上我们收到了两条RX: 14431337 00。

  经过多次调整和测试,为了能够可靠认出全部的MCU,我们需要将报文的等待时间放得很长。采用1000kbps的比特率,每条召唤报文需要等200毫秒。

6. 推广建议

  这种自动编址方案,对于自动升级这类一次性使用的场合,非常合适。

  但是,对于需要长久运行的场合,这种自动编址方案并不适合。首先这需要一个扮演上位机的节点,进行地址分配;这个地址分配会很耗时;由于所有地址都是临时地址,所以任何一个节点,一旦因为任何原因而丢失地址,就必须令整个网络重新进行地址分配,否则这个节点便再也无法回到网络中。

  要想将这种自动编址方案应用到产品中,还需要改良。

参考资料

  《RM0008 Reference manual STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM?-based 32-bit MCUs》

  《RM0090 Reference manual STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced ARM?-based 32-bit MCUs》


posted @ 2015-12-11 16:48  失散糖  阅读(628)  评论(0编辑  收藏  举报