转载自 http://blog.michiru.me/posts/canoe-tutorial-part-2.html
在这份教程的第一部分,我们提到没有CAN数据库,CANoe还不能工作。在第二部分,我们来看看CAN数据库是怎样的。通过CANoe的Tools菜单,或者从开始菜单打开Vector的CAN数据库编辑器——CANdb++ Editor。
简介
CAN总线上有4种报文:数据帧、远程帧、错误帧、超载帧。其中只有数据帧真正承载数据。CAN数据库储存这些的数据帧的所有属性,这样当我们发现了某条报文在总线上出现时,利用数据库中的数据,就能查询出相关的数据。比如这条报文的收发者,以及其表示的意义。
在Vector格式的数据库中,数据被组织成了6种不同的对象(Object):
- 信号(Signal)
- 报文(Message)
- 网络节点(Network node),或者简称节点
- 环境变量(Environment variable)
- 设备(ECU)
- CAN网络(Network)
对象是有级别的,高级的对象由若干低级对象附加额外的属性构成。如下图:
信号和报文信号的关系稍后会解释。报文信号到报文的箭头表示(若干)信号组成报文;报文到节点的箭头表示报文拥有发送节点;报文信号到节点的箭头表示拥有接收节点;节点和环境变量到ECU的箭头表示它们组成了ECU;同样,ECU组成了网络。这和CAN总线的交互结构是一样的。
在CANoe自带的CANdb++版本中,一个数据库最高级的对象是Network,且只能有一个,其名字与文件名相同。在更高级的Vector CANdb++ Admin版本中,最高级为一个叫Vehicles的对象,可以包含若干Network。
对象说明
信号
信号(Singal)代表了信息的最小单位,也就是一个“值”。例如发动机转速,开关的位置等。其主要的属性有:name
, length
, Byte order
, Value type
。具体含义可以参考CANdb++的Help中,Signal and Message Signal一节。注意Byte order属性,我们称之为“格式”,它可以设置为Motorola
或者Intel
,稍后会详细介绍。
报文信号
报文信号(Mapped signal)其实就是信号,区别在于当一个信号成为报文的组成部分时,它就会变成报文信号。因为它算是存在于报文中的,因此多了两个属性:
- Startbit: 起始位,也就是信号在报文中的位置。
- Receivers: 这个Signal会被哪个节点接收,这也是上图信号到节点的箭头的意义。
一个信号可以包含在多个报文中,之后会变成多个不同的报文信号,CANdb++ Editor中也叫映射的信号(Mapped Signal)。这是为了Singal可以重用,数据库中其它对象都不能这样重用。
报文
报文(Message)代表一条CAN报文,它就是在CAN总线上实际发送的内容。报文包含若干信号,有name
, ID
, DLC
,Transmitters
等属性,具体参考Help文档。需要说明的是,理论上只有一个节点会是报文的发送者,不过CANdb++ Editor中可以设置多个。
另外,Message的属性页中的Layout,可以直观的设置Message包含的Message Signal的位置(也就是其Startbit属性),它编辑的并不是Message的属性。
网络节点
网络节点(Node),它几乎代表了一个设备(ECU)。之所以说几乎,首先是因为ECU还包含了环境变量,其次是因为有一种特殊的ECU模型,它叫做网关(Gateway)。网关包含两个或更多节点,我们等会儿再来说明它。
所以通常,节点 + 环境变量 = ECU,节点的主要属性就只有name
而已。
环境变量
环境变量(Environment varisble),用来储存ECU中的数据,比如开关位置、传感器信号等。节点读写这些变量,同时,环境变量还能够被其它东西读写,比如虚拟的传感器。环境变量的存在是为了更好的模拟ECU这个模型。节点利用环境变量和环境交换信息。
ECU
上面已经差不多说明了,ECU其实就是节点和环境变量的组合,代表真实世界的仪器设备。在CANdb++ Editor中,ECU的可设置的属性只有其包含哪些环境变量。ECU对象名字无法编辑的,它等于节点的名字。
网络
Network表示真实世界中的一条CAN总线,它包含若干ECU。
网关
网关(Gateway),是一种特殊的ECU,它包含多个节点,以及环境变量。各个节点属于不同的网络,通过共用的环境变量交换数据。这样,各个网络就可以通过网关交换信息。这也是“网关”名字的由来。需要注意的是,CANdb++ Editor非Admin版本中,dbc文件只能含有唯一一个Network,因此网关这种模型是建立不出来的(汗
关于信号的起始位
稍微了解点CAN之后,CAN数据库非常容易理解,它其实就是CAN总线的模型。不过信号的“起始位”属性,还是得仔细说明。借助报文的Layout,理解起来会稍容易一些。
我们已经知道,报文包含若干信号。信号的格式(Byte order)可以是两种之一:Motorola或者Intel。他们是指Motorola和Intel处理器中使用的数据的储存格式,也就是两种字节序,Motorola是大端字节序,Intel是小端字节序。具体来说,十进制数128,写成二进制数1000 0000
,就是大端字节序,而写成0000 0001
就是小端字节序。
Byte order for Motorola processors (Big Endian):
MSB ... ... LSB
Byte order for Intel processors (Little Endian):
LSB ... ... MSB
CANdb++ Editor中,都表示成(注意大小写):
msb ... ... lsb
不过我认为这样理解起来更加混乱,因此我们还是按照MSB和LSB来看。
这两种字节序的信号在报文中的排列规则也不同。为了解释这些规则,我们假如报文的DLC为8,也就是报文中含有8字节的数据,共8 * 8 = 64 bit
。把每一位都列在一张表中。
如果位编号从右至左(R2L),那么:
7 6 5 4 3 2 1 0
--------------------------------
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
如果位编号从左至右(L2R),那么:
0 1 2 3 4 5 6 7
--------------------------------
| 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
这两种编号方式中,字节的编号是一样的,位的编号不同。CAN报文是串行发送的,CAN节点在发送报文时,不论如何编号,总是从表的左上第一位开始发送。从左至右,从上至下。
x x x x x x x x
--------------------------------
1st 2nd 3rd 4th 5th 6th 7th 8th | 0
... ... ... ... ... ... ... ... | 1
... ... ... ... ... ... ... ... | 2
... ... ... ... ... ... ... ... | 3
... ... ... ... ... ... ... ... | 4
... ... ... ... ... ... ... ... | 5
... ... ... ... ... ... ... ... | 6
... ... ... ... ... ... ... last| 7
上面说到不同字节序(格式)的信号在报文中的排列规则也不同:Intel格式的信号的每位,从MSB到LSB,按照从右至左,从上至下的顺序排列(向右上角塞):
x x x x x x x x
--------------------------------
>.. ... ... ... ... ... ... MSB | 0
LSB ... ... ..> | 1
| 2
| 3
| 4
| 5
| 6
| 7
在CANdb++中看时,记得把MSB和LSB分别换成lsb和msb(注意顺序)。
而Motorola格式的信号,从MSB到LSB,按照每位从左至右,从上至下排列(向左上角塞):
x x x x x x x x
--------------------------------
MSB ... ... ... ... ... ... ..< | 0
<.. ... ... LSB | 1
| 2
| 3
| 4
| 5
| 6
| 7
在CANdb++中看时,记得把MSB和LSB分别换成msb和lsb。
<
或者>
表示信号的衔接点。通常一个Message中只应该含有同一种格式的信号。否则:
x x x x x x x x
--------------------------------
>l e t n I ... ... MSB | 0
LSB ... ... ..> | 1
MSB ... M o t o r o< | 2
<l a ... LSB | 3
| 4
| 5
| 6
| 7
于是Intel向左上挤,占据了12个位。Motorola向右上挤就只能从byte2开始排列了,中间的4位不得不空出来。
至此应该没有太多问题,不过接下来才是混乱的:Intel格式的信号有2种表示方法Intel Standard
和Intel Sequential
:
Intel Standard表示法:
7 6 5 4 3 2 1 0
--------------------------------
| 0
>.. ... ... MSB | 1
LSB ... ... ... ... ... ... ..> | 2
| 3
这种表示法使用从右至左的位编号方式。例子中我们假设前12位(bit0~11)已经被其它信号占满了。这个信号起始位为MSB,即:12。说出Intel Standard、起始位12、长度12位,就能在Message中确定Singal的位置了。
Intel Sequential表示法:
0 1 2 3 4 5 6 7
--------------------------------
| 0
>.. ... ... MSB | 1
LSB ... ... ... ... ... ... ..> | 2
| 3
这种表示法使用从左至右的位编号方式。前12位(bit7~0, 15~12)已经被占,这个信号起始位为MSB,即:11。
Motorola式的信号有4种表示方法,分别是:Motorola Forward LSB
, Motorola Forward MSB
, Motorola Sequential
, Motorola Backward
。
Motorola Forward LSB:
7 6 5 4 3 2 1 0
--------------------------------
| 0
MSB ... ... ..< | 1
<.. ... ... ... ... ... ... LSB | 2
| 3
从右至左编号方式,起始位为LSB,即:16。
Motorola Forward MSB:
7 6 5 4 3 2 1 0
--------------------------------
| 0
MSB ... ... ..< | 1
<.. ... ... ... ... ... ... LSB | 2
| 3
从右至左编号方式,起始位为MSB,即:11。
Motorola Sequential:
0 1 2 3 4 5 6 7
--------------------------------
| 0
MSB ... ... ..< | 1
<.. ... ... ... ... ... ... LSB | 2
| 3
从左至右编号方式,起始位为MSB,即:12。
也许你觉得最后一种方法应该是从左至右编号,起始位为LSB,但是世间万物总是那么不如意。
Motorola Backward:
7 6 5 4 3 2 1 0
--------------------------------
| 3
MSB ... ... ..< | 2
<.. ... ... ... ... ... ... LSB | 1
| 0
非常费解的编号方式,它的字节编号是从下到上,位编号从右到左,起始位置为LSB,即8。
针对两种格式信号使用的表示法可以在CANdb++ Editor菜单栏的Option > Settings > Display
中设置。但不论设置如何,数据库进行储存时,Intel格式使用Intel Sequential表示法储存,Motorola格式使用Motorola Sequential表示法储存,避免在设置不同的编辑器中打开时出现误解。
在这份教程里,我们都使用Motorola格式的信号,并且使用Motorola Forward MSB式的表示方法。它应该和很多人(比如我)的第一感觉相同。
第一份CAN数据库
现在让我们着手建立一份简单的CAN数据库。它有1个信号,1条报文,2个节点分别作为收发节点,1个环境变量用来演示其使用。当然,不用说,有1个网络。
CANdb++ Editor中,使用File > Create Database...
来新建一个叫candb.dbc
的数据库,程序会询问你使用的模板。不同模板的区别在于每个对象的属性多少。越复杂的模板属性越多,这里我们使用最简单的EmptyTemplate.dbc
在CANdb++ Editor默认打开的Overall View窗口的左侧,我们可以看到各个对象的分类。如果不是的话,找菜单栏View > Overall View
。
建立节点
在Newwork noedes上右键新建,
Name: ECM
同样再新建一个:
Name: METER
建立报文
在Message上右键,新建:
Name: EngineState
Type: CAN Standard
ID: 0x1
DLC: 1
Transmitters中,点击Add...
,选择ECM。
建立信号
在Singal上点击右键,New...
,之后输入:
Name: Speed
Length[bit]: 8
Byte Order: Motorola
Value Type: Unsigned
Unit: m/s
Factor: 0.1
Offset: 0
Minimum: 0
Maximum: 25.5
Messages页中点击Add...
,选择EngineState。
编辑报文信号
展开左侧Messages下的EngineState,右键点击Speed,选择Edit Mapped Singal
。
Startbit[bit]: 7
Receivers页中点击Add...
,选择METER。
建立环境变量
在左侧Environment variables上右键新建
Name: env_speed
Value Type: Float
Access: Read
Unit: m/s
Initial Value: 0
Minimum: 0
Maximum: 25.5
Control units页中添加ECM。
再次确认一下,使用View > Communication Matrix
非常好,我们的数据库如此精炼。数据库的教程至此,保存在<project>\database
文件夹中吧。
额外的说明
Vector的CANdb数据库其实是一个编码为ANSI的文本文件,可以使用任何文本编辑器打开。其结构可以参考Vector的DBC File Format Documentation,在官网似乎搜不到这份文件,就只有提供民间版本了。
这在你有了某种结构的数据库文件(比如表格文件格式),想要转换成Vector的dbc文件的,可以写程序转换,而不是手动输入。