Difference between revisions of "DBC Format"
(Created page with "DBC file is a proprietary format that describes the data over a CAN bus. == DBC File Format == First, let's understand what the DBC file's format looks like. [[File:Screensh...") |
|||
Line 43: | Line 43: | ||
BS_: | BS_: | ||
− | BU_: | + | BU_: DBG SENSOR DRIVER MOTOR IO NOONE |
− | BO_ 100 | + | |
+ | BO_ 100 DRIVER_HEARTBEAT: 1 DRIVER | ||
SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR | SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR | ||
− | BO_ 200 | + | BO_ 200 SENSOR_SONARS: 8 SENSOR |
SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO | SG_ SENSOR_SONARS_mux M : 0|4@1+ (1,0) [0|0] "" DRIVER,IO | ||
− | SG_ | + | 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_left m0 : | + | SG_ SENSOR_SONARS_rear m0 : 52|12@1+ (0.1,0) [0|0] "" DRIVER,IO |
− | SG_ SENSOR_SONARS_middle m0 : | + | SG_ SENSOR_SONARS_no_filt_left m1 : 16|12@1+ (0.1,0) [0|0] "" DBG |
− | SG_ SENSOR_SONARS_right m0 : | + | SG_ SENSOR_SONARS_no_filt_middle m1 : 28|12@1+ (0.1,0) [0|0] "" DBG |
− | SG_ SENSOR_SONARS_rear m0 : | + | SG_ SENSOR_SONARS_no_filt_right m1 : 40|12@1+ (0.1,0) [0|0] "" DBG |
− | SG_ SENSOR_SONARS_no_filt_left m1 : | + | SG_ SENSOR_SONARS_no_filt_rear m1 : 52|12@1+ (0.1,0) [0|0] "" DBG |
− | SG_ SENSOR_SONARS_no_filt_middle m1 : | ||
− | SG_ SENSOR_SONARS_no_filt_right m1 : | ||
− | SG_ SENSOR_SONARS_no_filt_rear m1 : | ||
BO_ 300 MOTOR_CMD: 1 DRIVER | BO_ 300 MOTOR_CMD: 1 DRIVER | ||
Line 68: | Line 66: | ||
BO_ 400 MOTOR_STATUS: 3 MOTOR | BO_ 400 MOTOR_STATUS: 3 MOTOR | ||
− | SG_ MOTOR_STATUS_wheel_error : 0|1@1+ (1,0) [0|0] "" DRIVER | + | SG_ MOTOR_STATUS_wheel_error : 0|1@1+ (1,0) [0|0] "" DRIVER,IO |
− | SG_ | + | SG_ MOTOR_STATUS_speed_kph : 8|16@1+ (0.001,0) [0|0] "kph" DRIVER,IO |
+ | |||
+ | BO_ 901 IO_DEBUG: 1 IO | ||
+ | SG_ IO_DEBUG_dummy : 0|8@1+ (1,0) [0|0] "" DBG | ||
+ | |||
− | CM_ BU_ | + | |
+ | CM_ BU_ SENSOR "The sensor controller of the car"; | ||
CM_ BU_ DRIVER "The driver controller driving the car"; | CM_ BU_ DRIVER "The driver controller driving the car"; | ||
− | |||
CM_ BU_ MOTOR "The motor controller of the car"; | CM_ BU_ MOTOR "The motor controller of the car"; | ||
+ | CM_ BU_ NOONE "No node, used to indicate if it's a debug message going to no one"; | ||
CM_ BO_ 100 "Sync message used to synchronize the controllers"; | CM_ BO_ 100 "Sync message used to synchronize the controllers"; | ||
− | |||
BA_DEF_ "BusType" STRING ; | BA_DEF_ "BusType" STRING ; | ||
− | BA_DEF_ SG_ "FieldType" STRING ; | + | BA_DEF_ SG_ "FieldType" STRING ; |
− | BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; | + | BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0; |
− | + | BA_DEF_DEF_ "BusType" "CAN"; | |
− | BA_DEF_DEF_ "BusType" "CAN"; | + | BA_DEF_DEF_ "FieldType" ""; |
− | BA_DEF_DEF_ "FieldType" ""; | + | BA_DEF_DEF_ "GenMsgCycleTime" 0; |
− | |||
BA_ "GenMsgCycleTime" BO_ 100 1000; | BA_ "GenMsgCycleTime" BO_ 100 1000; | ||
BA_ "GenMsgCycleTime" BO_ 200 100; | BA_ "GenMsgCycleTime" BO_ 200 100; | ||
BA_ "GenMsgCycleTime" BO_ 300 100; | BA_ "GenMsgCycleTime" BO_ 300 100; | ||
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd"; | BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd"; | ||
− | + | VAL_ 100 DRIVER_HEARTBEAT_cmd 0 "DRIVER_HEARTBEAT_cmd_NOOP" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 2 "DRIVER_HEARTBEAT_cmd_REBOOT" ; | |
− | VAL_ 100 DRIVER_HEARTBEAT_cmd 0 "DRIVER_HEARTBEAT_cmd_NOOP" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 2 "DRIVER_HEARTBEAT_cmd_REBOOT"; | ||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 110: | Line 109: | ||
To handling the messages that are to be received, we must have "glue code" that pieces together the data of a received messages to the target structure. Unfortunately the AGC cannot generate the glue code because it is decoupled from the CAN driver and furthermore, it leaves it up the application to provide the destination structure to convert the CAN data onto. | To handling the messages that are to be received, we must have "glue code" that pieces together the data of a received messages to the target structure. Unfortunately the AGC cannot generate the glue code because it is decoupled from the CAN driver and furthermore, it leaves it up the application to provide the destination structure to convert the CAN data onto. | ||
<syntaxhighlight> | <syntaxhighlight> | ||
− | + | #include "_can_dbc/generated_can.h" | |
+ | #include "can.h" | ||
+ | |||
+ | // The MIA functions require that you define the: | ||
+ | // - Time when the handle_mia() functions will replace the data with the MIA | ||
+ | // - The MIA data itself (ie: MOTOR_STATUS__MIA_MSG) | ||
+ | const uint32_t SENSOR_SONARS_m0__MIA_MS = 3000; | ||
+ | const SENSOR_SONARS_m0_t SENSOR_SONARS_m0__MIA_MSG = { 0 }; | ||
+ | const uint32_t SENSOR_SONARS_m1__MIA_MS = 3000; | ||
+ | const SENSOR_SONARS_m1_t SENSOR_SONARS_m1__MIA_MSG = { 0 }; | ||
+ | const uint32_t MOTOR_STATUS__MIA_MS = 3000; | ||
+ | const MOTOR_STATUS_t MOTOR_STATUS__MIA_MSG = { 0 }; | ||
+ | |||
+ | // Sample periodic task that receives and stores messages | ||
+ | void period_100Hz(void) | ||
+ | { | ||
+ | can_msg_t can_msg; | ||
+ | SENSOR_SONARS_t sensor_can_msg; | ||
+ | MOTOR_STATUS_t motor_status_msg; | ||
+ | |||
+ | // Empty all of the queued, and received messages within the last 10ms (100Hz callback frequency) | ||
+ | while (CAN_rx(can1, &can_msg, 0)) | ||
+ | { | ||
+ | // Form the message header from the metadata of the arriving message | ||
+ | msg_hdr_t can_msg_hdr; | ||
+ | can_msg_hdr.dlc = can_msg.frame_fields.data_len; | ||
+ | can_msg_hdr.mid = can_msg.msg_id; | ||
+ | |||
+ | // Attempt to decode the message (brute force, but should use switch/case with MID) | ||
+ | dbc_decode_SENSOR_SONARS(&sensor_can_msg, can_msg.data.bytes, &can_msg_hdr); | ||
+ | dbc_decode_MOTOR_STATUS(&motor_status_msg, can_msg.data.bytes, &can_msg_hdr); | ||
+ | } | ||
+ | |||
+ | // Service the MIA counters of the MUX'd message | ||
+ | dbc_handle_mia_SENSOR_SONARS_m0(&sensor_can_msg.m0, 100); | ||
+ | dbc_handle_mia_SENSOR_SONARS_m1(&sensor_can_msg.m1, 100); | ||
+ | |||
+ | // Service the MIA counter of a regular (non MUX'd) message | ||
+ | dbc_handle_miaMOTOR_STATUS(&motor_status_msg, 100); | ||
+ | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<BR/> | <BR/> | ||
Line 118: | Line 156: | ||
<syntaxhighlight> | <syntaxhighlight> | ||
− | + | void period_10Hz(void) | |
− | + | { | |
− | + | // Send out Motor command at 10Hz | |
+ | MOTOR_CMD_t motor_cmd = { 0 }; | ||
+ | motor_cmd.MOTOR_CMD_drive = 3; | ||
+ | motor_cmd.MOTOR_CMD_steer = 0; | ||
+ | |||
+ | can_msg_t can_msg = { 0 }; | ||
+ | |||
+ | // Encode the CAN message's data bytes, get its header and set the CAN message's DLC and length | ||
+ | msg_hdr_t msg_hdr = dbc_encode_MOTOR_CMD(can_msg.data.bytes, &motor_cmd); | ||
+ | can_msg.msg_id = msg_hdr.mid; | ||
+ | can_msg.frame_fields.data_len = msg_hdr.dlc; | ||
+ | |||
+ | // Queue the CAN message to be sent out | ||
+ | CAN_tx(can1, &can_msg, 0); | ||
+ | } | ||
− | |||
− | |||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<BR/> | <BR/> |
Revision as of 05:28, 13 July 2016
DBC file is a proprietary format that describes the data over a CAN bus.
DBC File Format
First, let's understand what the DBC file's format looks like.
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_
BS_:
BU_: DBG SENSOR DRIVER MOTOR IO NOONE
BO_ 100 DRIVER_HEARTBEAT: 1 DRIVER
SG_ DRIVER_HEARTBEAT_cmd : 0|8@1+ (1,0) [0|0] "" SENSOR,MOTOR
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
BO_ 300 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_ 901 IO_DEBUG: 1 IO
SG_ IO_DEBUG_dummy : 0|8@1+ (1,0) [0|0] "" DBG
CM_ BU_ SENSOR "The sensor controller of the car";
CM_ BU_ DRIVER "The driver controller driving the car";
CM_ BU_ MOTOR "The motor controller of the car";
CM_ BU_ NOONE "No node, used to indicate if it's a debug message going to no one";
CM_ BO_ 100 "Sync message used to synchronize the controllers";
BA_DEF_ "BusType" STRING ;
BA_DEF_ SG_ "FieldType" STRING ;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 0;
BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "FieldType" "";
BA_DEF_DEF_ "GenMsgCycleTime" 0;
BA_ "GenMsgCycleTime" BO_ 100 1000;
BA_ "GenMsgCycleTime" BO_ 200 100;
BA_ "GenMsgCycleTime" BO_ 300 100;
BA_ "FieldType" SG_ 100 DRIVER_HEARTBEAT_cmd "DRIVER_HEARTBEAT_cmd";
VAL_ 100 DRIVER_HEARTBEAT_cmd 0 "DRIVER_HEARTBEAT_cmd_NOOP" 1 "DRIVER_HEARTBEAT_cmd_SYNC" 2 "DRIVER_HEARTBEAT_cmd_REBOOT" ;
CAN Communication Handling in C
I created auto-generation tool that operates on the DBC file that produces Auto-Generated-Code that we will refer to as AGC in this article. AGC contains useful artifacts such as:
- C structures that store data variables as described in the DBC message
- Each signal that your controller is a recipient of will occupy a member variable of the struct
- If your controller is the sender of the message, then all signals of the DBC message will appear in the structs
- Receive handling to convert a CAN message to the C structure
- Transmission handling to convert the C structure into the CAN message's data bytes
The AGC essentially removes the burden of encoding and decoding the CAN message data. For example, you don't have to parse fields individually by doing something such as "bit 5 of byte 6 means foo", and instead, you will simply have a C structure where the CAN message data can be parsed and stored upon.
MIA Handling
MIA means "Missing in Action", and the objective is that if a periodic message is not received as expected, then the contents of the parsed message can be defaulted to the data of your choice. For example, if a temperature sensor reading is not received, then we can tell the AGC to default the sensor reading value to 68 degrees Fahrenheit. This reduces code clutter because each time you use the temperature sensor's value, you don't have to check if the corresponding CAN data was received in the last few seconds.
CAN RX
To handling the messages that are to be received, we must have "glue code" that pieces together the data of a received messages to the target structure. Unfortunately the AGC cannot generate the glue code because it is decoupled from the CAN driver and furthermore, it leaves it up the application to provide the destination structure to convert the CAN data onto.
#include "_can_dbc/generated_can.h"
#include "can.h"
// The MIA functions require that you define the:
// - Time when the handle_mia() functions will replace the data with the MIA
// - The MIA data itself (ie: MOTOR_STATUS__MIA_MSG)
const uint32_t SENSOR_SONARS_m0__MIA_MS = 3000;
const SENSOR_SONARS_m0_t SENSOR_SONARS_m0__MIA_MSG = { 0 };
const uint32_t SENSOR_SONARS_m1__MIA_MS = 3000;
const SENSOR_SONARS_m1_t SENSOR_SONARS_m1__MIA_MSG = { 0 };
const uint32_t MOTOR_STATUS__MIA_MS = 3000;
const MOTOR_STATUS_t MOTOR_STATUS__MIA_MSG = { 0 };
// Sample periodic task that receives and stores messages
void period_100Hz(void)
{
can_msg_t can_msg;
SENSOR_SONARS_t sensor_can_msg;
MOTOR_STATUS_t motor_status_msg;
// Empty all of the queued, and received messages within the last 10ms (100Hz callback frequency)
while (CAN_rx(can1, &can_msg, 0))
{
// Form the message header from the metadata of the arriving message
msg_hdr_t can_msg_hdr;
can_msg_hdr.dlc = can_msg.frame_fields.data_len;
can_msg_hdr.mid = can_msg.msg_id;
// Attempt to decode the message (brute force, but should use switch/case with MID)
dbc_decode_SENSOR_SONARS(&sensor_can_msg, can_msg.data.bytes, &can_msg_hdr);
dbc_decode_MOTOR_STATUS(&motor_status_msg, can_msg.data.bytes, &can_msg_hdr);
}
// Service the MIA counters of the MUX'd message
dbc_handle_mia_SENSOR_SONARS_m0(&sensor_can_msg.m0, 100);
dbc_handle_mia_SENSOR_SONARS_m1(&sensor_can_msg.m1, 100);
// Service the MIA counter of a regular (non MUX'd) message
dbc_handle_miaMOTOR_STATUS(&motor_status_msg, 100);
}
CAN TX
The transmission side is simpler than receive because you simply provide an 8-byte data storage for the CAN message, and the encode_ functions convert the C structure onto the 8-bytes that you can send out as part of the CAN message. The encode_ functions also return the message ID and the length of the CAN message and this was done in an effort to decouple the CAN driver from the AGC.
void period_10Hz(void)
{
// Send out Motor command at 10Hz
MOTOR_CMD_t motor_cmd = { 0 };
motor_cmd.MOTOR_CMD_drive = 3;
motor_cmd.MOTOR_CMD_steer = 0;
can_msg_t can_msg = { 0 };
// Encode the CAN message's data bytes, get its header and set the CAN message's DLC and length
msg_hdr_t msg_hdr = dbc_encode_MOTOR_CMD(can_msg.data.bytes, &motor_cmd);
can_msg.msg_id = msg_hdr.mid;
can_msg.frame_fields.data_len = msg_hdr.dlc;
// Queue the CAN message to be sent out
CAN_tx(can1, &can_msg, 0);
}