-
Notifications
You must be signed in to change notification settings - Fork 0
This document is based off (http://socialledge.com/sjsu/index.php/DBC_Format)
I am adding info as I figure it out, because the internet has bad documentation on this and most of it is basically just this wiki page anyways.
I am just starting this and working from an existing document. More information is available.
- CANBus.app One of the best and most complete overall sources of CAN information. I found it after I started this wiki, and I may eventually just link to it.
- Cantools source file gives good insight into lesser used syntax.
- CSS Electronics DBC file is a proprietary format that describes the data over a CAN bus.
- Canbus Repo actually pretty good documentation .
In this article, you will learn the basic syntax of a DBC file that defines up to 8 bytes of CAN message data. A lot of CAN bus related tools can read the DBC file and display values next to each "signal" that you define in the DBC file.
The second part of this article discusses how the auto-generated code can help you read and write the CAN message data. Essentially, each "message" defined in a DBC becomes a C structure with the signals being the members of the C structure.
A simple DBC message contains the Message ID MID(CAN ID), and at least one signal. Let's demonstrate by showing a message that contains a single 8-bit signal. Spaces and the syntax is really strict, so if you get a single space incorrect, the auto-generation script will likely fail.
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG
Observations:
-
The message name is
IO_DEBUG
and MID is500
(decimal), and the length is4
bytes (though we only need 1 for 8-bit signal)- IF the MIDs are standard (11bit) it is just the decimal value instead of the HEX as it is normally written. For extended, a 32 bit number is used and the highest bit is set to 1, so basically just add 2147483648 to the decimal value of the MID to make it extended.
-
The sender is
IO
-
0|8
: The unsigned signal starts at bit position 0, and the size of this signal is 8 -
@1+
: Defines that the signal is little-endian, and unsigned:@0+
is for big-endian order. -
(1,0)
: The scale and offset (discussed later) -
[0|0]
: Min and Max is not defined (discussed later) -
""
: There are no units (it could be, for instance "inches") -
DBG
is the receiver name
When referencing start bits and length, use the table below as a guide.
- For little endian, choose as start bit then move left and down.
- For Big endian, choose a start bit then move right and down. So to get a two-bit signal that is in bits 0,1
- Big endian:
1|2@0+
- Little endian:
0|2@1+
The ways some signals are packed into the frame is what really forces this decision.
For example:
To get an 8 bit signal that has its Most significant 4 bits in the bottom of byte1 and 4 least significant bits in the top of byte2
- Big Endian:
11|8@0+
(ie. 11,10,9,8,23,22,21,20)
To get an 8 bit signal that has its least significant 4 bits in the top of byte1 and 4 most significant bits in the bottom of byte2
- Little Endian:
12|8@1+
(ie. 12,13,14,15,16,17,18,19)
Bit indices
Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
2 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
3 | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
4 | 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 |
5 | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 |
6 | 55 | 54 | 53 | 52 | 51 | 50 | 49 | 48 |
7 | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 |
CSS Electronics has maintained a Google Doc for a while that gives a good visualization at least for little endian if that helps you.
CM_ BO_ 500 "Comment about the IO_DEBUG frames.";
CM_ BO_ 500 IO_DEBUG_test_unsigned "Comment about the IO_DEBUG_test_unsigned signal.";
A signed signal can be sent by simply applying a negative offset to an unsigned signal. The raw bits can also be interpreted as a twos-complement signed integer by specifying (-) after the endianess. Let's add two signed signals to the previous message to see the difference.
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|255] "" DBG
SG_ IO_DEBUG_test_signed : 8|8@1+ (1,-128) [-128|127] "" DBG
SG_ IO_DEBUG_test_signed2 : 8|8@1- (1,0) [-128|127] "" DBG
A floating point variable can be sent by deciding the range, and the precision that you require. For example, if we choose 8-bits, with 0.1
as a fraction, we can send the data range of 0.0 -> 25.5
. On the other hand, if we want more precision and negative representation, we could use 12-bits with 0.01
as a fraction, and an offset. The second fractional signal also contains an explicit minimum and maximum, which is limited by 12-bit that can represent 4096 different numbers, and by factoring in the offset, and using half of the range for negative representation, it ends up with the limited range of -20.48 -> 20.47
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_signed : 8|8@1- (1,-128) [0|0] "" DBG
SG_ IO_DEBUG_test_float1 : 16|8@1+ (0.1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_float2 : 24|12@1+ (0.01,-20.48) [-20.48|20.47] "" DBG
An enumeration type is used where the user wishes to use or see names, instead of numbers. For example, instead of a state machine showing up as "0, 1, 2", we could see it as "stopped, running, paused". It is accomplished by adding two new lines in the DBC file.
A "BA_" field needs to be added, and for the sake of simplicity, you can follow the example below to list an enumeration as a "FieldType" first. Then, you need a "VAL_" field that actually defines the enumeration values.
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_enum : 8|8@1+ (1,0) [0|0] "" DBG
BA_ "FieldType" SG_ 500 IO_DEBUG_test_enum "IO_DEBUG_test_enum";
VAL_ 500 IO_DEBUG_test_enum 2 "IO_DEBUG_test2_enum_two" 1 "IO_DEBUG_test2_enum_one" ;
A multiplexed message can be used (indirectly) to send more than 8 bytes using a single message ID. For example, if we use a 2-bit MUX, we can send 62-bits of data with four multiplexers (M0, M1, M2, M3). In other words, your message could state that:
- If first 2-bits are 0 (M0), then 62-bits of data is for the car's front sensors
- If first 2-bits are 1 (M1), then 62-bits of data is for the car's rear sensors
Likewise, we could use 8-bit multiplexer, and then we can send 7 bytes of unique data * 256 multiplexers using the same MID. Multiplexed messages are used quite often when:
- Certain information should be grouped under a single MID. *: If there are 100 sensors that use 32-bit value, it is better to use a multipexer rather than 100 different message IDs.
- 11-bit MID does not provide any space to send information under a different MID
- We wish to use the same MID for CAN priority purposes
Observe the following things in the example below:
- There are two multiplexed messages, m0, and m1. *: m0 has the value of 0b0000 for the MUX, and m1 has the value of 0b0001 *: We could have a theoretical m15 with value 0b1111 since there is only a 4 bit MUX.
- The 4-bit "M" needs to be defined first.
- In this rather advanced example, we also have a non-mux'd signal called '''SENSOR_SONARS_err_count''' that is sent with all multiplexed messages
- There are four sensor values sent with multiplexor m0
- There are four "un filtered" sensor values sent with multipexor m1
In conclusion, you can define a multiplexed message that uses a single message ID, however, they are treated, and decoded differently depending on which multipexed value was sent. '''In order to send a multiplexed message below, you will have to send two separate messages, one for the m0 and one for the m1'''.
BO_ 200 SENSOR_SONARS: 8 SENSOR
SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG
There can be multiple levels of Multiplex Nesting. The OBDII protocol uses this in DBC files.
BO_ 2024 OBD2: 8 Vector__XXX
SG_ S1_PID_0C_EngineRPM m12 : 31|16@0+ (0.25,0) [0|16383.75] "rpm" Vector__XXX
SG_ ParameterID_Service01 m1M : 23|8@0+ (1,0) [0|255] "" Vector__XXX
SG_ service M : 13|6@0+ (1,0) [0|63] "" Vector__XXX
...more
SG_MUL_VAL_ 2024 S1_PID_0C_EngineRPM ParameterID_Service01 12-12;
...more
SG_MUL_VAL_ 2024 ParameterID_Service01 service 1-1;
Take a look above:
-
BO_ 2024 OBD2: 8 Vector__XXX
Defines the OBDII response frame (remember the MID is decimal so 2024=0x7E8) -
SG_ service M : 13|6@0+ (1,0) [0|63] "" Vector__XXX
defines a field named "service"- 6 bit field starting at bit 13, big-endian.
-
SG_MUL_VAL_ 2024 ParameterID_Service01 service 1-1;
defines thatParameterID_Service01
is decoded when service=1 -
SG_ ParameterID_Service01 m1M : 23|8@0+ (1,0) [0|255] "" Vector__XXX
defines another level of multiplex once ParameterID_Service01 is selected. -
SG_MUL_VAL_ 2024 S1_PID_0C_EngineRPM ParameterID_Service01 12-12;
says to readS1_PID_0C_EngineRPM
when ParameterID_Service01=12 -
SG_ S1_PID_0C_EngineRPM m12 : 31|16@0+ (0.25,0) [0|16383.75] "rpm" Vector__XXX
signal defined as before. It is only decoded after two levels of multiplex nesting.- 0.25 is the scale, so the LSB value is 0.25, 16 bit big-endian
VERSION ""
NS_ :
BA_
BA_DEF_
BA_DEF_DEF_
BA_DEF_DEF_REL_
BA_DEF_REL_
BA_DEF_SGTYPE_
BA_REL_
BA_SGTYPE_
BO_TX_BU_
BU_BO_REL_
BU_EV_REL_
BU_SG_REL_
CAT_
CAT_DEF_
CM_
ENVVAR_DATA_
EV_DATA_
FILTER
NS_DESC_
SGTYPE_
SGTYPE_VAL_
SG_MUL_VAL_
SIGTYPE_VALTYPE_
SIG_GROUP_
SIG_TYPE_REF_
SIG_VALTYPE_
VAL_
VAL_TABLE_
BS_:
BU_: DBG DRIVER IO MOTOR SENSOR
BO_ 100 DRIVER_HEARTBEAT: 1 DRIVER
SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_enum : 8|8@1+ (1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_signed : 16|8@1- (1,0) [0|0] "" DBG
SG_ IO_DEBUG_test_float : 24|8@1+ (0.5,0) [0|0] "" DBG
BO_ 101 MOTOR_CMD: 1 DRIVER
SG_ MOTOR_CMD_steer : 0|4@1- (1,-5) [-5|5] "" MOTOR
SG_ MOTOR_CMD_drive : 4|4@1+ (1,0) [0|9] "" MOTOR
BO_ 400 MOTOR_STATUS: 3 MOTOR
SG_ MOTOR_STATUS_wheel_error : 0|1@1+ (1,0) [0|0] "" DRIVER,IO
SG_ MOTOR_STATUS_speed_kph : 8|16@1+ (0.001,0) [0|0] "kph" DRIVER,IO
BO_ 200 SENSOR_SONARS: 8 SENSOR
SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_err_count : 4|12@1+ (1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_left m0 : 16|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_middle m0 : 28|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_right m0 : 40|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO
SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG
SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG
CM_ BU_ DRIVER "The driver controller driving the car";
CM_ BU_ MOTOR "The motor controller of the car";
CM_ BU_ SENSOR "The sensor controller of the car";
CM_ BO_ 100 "Sync message used to synchronize the controllers";
BA_DEF_ "BusType" STRING ;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0;
BA_DEF_ SG_ "FieldType" STRING ;
BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "FieldType" "";
BA_DEF_DEF_ "GenMsgCycleTime" 0;
BA_ "GenMsgCycleTime" BO_ 100 1000;
BA_ "GenMsgCycleTime" BO_ 500 100;
BA_ "GenMsgCycleTime" BO_ 101 100;
BA_ "GenMsgCycleTime" BO_ 400 100;
BA_ "GenMsgCycleTime" BO_ 200 100;
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd";
BA_ "FieldType" SG_ 500 IO_DEBUG_test_enum "IO_DEBUG_test_enum";
VAL_ 100 DRIVER_HEARTBEAT_cmd 2 "DRIVER_HEARTBEAT_cmd_REBOOT" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 0 "DRIVER_HEARTBEAT_cmd_NOOP" ;
VAL_ 500 IO_DEBUG_test_enum 2 "IO_DEBUG_test2_enum_two" 1 "IO_DEBUG_test2_enum_one" ;