CAN DBC to C (and CSV, JSON and XML) compiler using the mpc parser combinator library
This program turns a DBC file into a number of different formats.
CAN-FD IS CURRENTLY NOT SUPPORTED.
Please consider donating to the project if you find it useful. This
project requires your support to continue. If you require paid support
please contact mailto:[email protected].
As you can see by the big note above, CAN-FD is currently not supported,
and likely will not (for a while). The problem is that CAN-FD allows for
messages above 64-bits in length and this project and the code generated
from it makes the assumption that 8-bits is all is intended a CAN message
to have in it.
dbcc is a program for converting a DBC file primarily into into C
code that can serialize and deserialize CAN messages into structures that
represent those messages and signals. It is also possible to print out the
information contained in a structure.
See the license file for details of the license for this program, it is
released under the MIT license. Dependencies, if linked against, may have
their own license and their own set of restrictions if built against.
The sources file mpc.c and mpc.h originate from a parser combinator
written in C called MPC and are licensed under the 3 Clause BSD
license.
To build, you only need a C (C99) compiler and Make (probably GNU make, I make no
effort to support other Make implementations). The dbcc program itself it
written in what should be portable C with the only external dependency being
your platforms C library.
You should be able to type:
make
To build, an executable called ‘dbcc’ is produced. To test run the tests,
xmllint is required.
-n
option can be used to specify the output version which might give you accessYou can specify the version to use on a command line with the -n
option.
The latest version will be used by default.
Version 1:
Legacy/original behaviour. Note that this still won’t provide a stable output, but will have
a better chance of not having breaking changes.
Version 2:
Latest version
Enum names are qualified with the CAN message name
encode/decode function names are also qualified with the message name
The code generator can make code to unpack a message (turn some bytes into a
data structure), decode a message (apply a scaling/offset minimum and maximum
values to the values in a data structure), and the inverse can be done
(pack/encode).
You can look at the code generated from the DBC files within the project to get
an understanding of how it should work.
If you want to process a CAN message that you have received you will need to
call the ‘unpack_message’. The code generate is agnostic to the CPUs byte
order, it takes a ‘uint64_t’ value containing a single CAN packet (along with
the CAN ID and the DLC for the that packet) and unpacks that into a structure
it generates. The first byte of the CAN packet should be put in the least
significant byte of the ‘uint64_t’.
You can use the following functions to convert to/from a CAN message:
static uint64_t u64_from_can_msg(const uint8_t m[8]) {
return ((uint64_t)m[7] << 56) | ((uint64_t)m[6] << 48) | ((uint64_t)m[5] << 40) | ((uint64_t)m[4] << 32)
| ((uint64_t)m[3] << 24) | ((uint64_t)m[2] << 16) | ((uint64_t)m[1] << 8) | ((uint64_t)m[0] << 0);
}
static void u64_to_can_msg(const uint64_t u, uint8_t m[8]) {
m[7] = u >> 56;
m[6] = u >> 48;
m[5] = u >> 40;
m[4] = u >> 32;
m[3] = u >> 24;
m[2] = u >> 16;
m[1] = u >> 8;
m[0] = u >> 0;
}
The code generator will make a structure based on the file name of the DBC
file, so for the example DBC file ‘ex1.dbc’ a data structure called
‘can_obj_ex1_h_t’ is made. This structure contains all of the CAN message
structures, which in turn contain all of the signals. Having all of the
messages/signals in one structure has advantages and disadvantages, one of the
things it makes easier is defining the data structures needed.
/* reminder of the 'unpack_message' prototype */
int unpack_message(can_obj_ex1_h_t *o, const unsigned long id, uint64_t data, uint8_t dlc);
static can_obj_ex1_h_t ex1;
uint8_t can_message_raw[8];
unsigned long id = 0;
uint8_t dlc = 0;
your_function_to_receive_a_can_message(can_message_raw, &id, &dlc);
if (unpack_message(&ex1, id, can_message_u64, dlc) < 0) {
// Error Condition; something went wrong
return -1;
}
‘unpack_message’ calls the correct unpack function for that ID, as an example
for ID ‘0x020’:
case 0x020: return unpack_can_0x020_MagicCanNode1RBootloaderAddress(&o->can_0x020_MagicCanNode1RBootloaderAddress, data, dlc);
The unpack function populates the message object in the ‘can_obj_ex1_h_t’
structure for that ID. The individual signals can then be decoded with the
appropriate functions for that signal. For example:
uint16_t b = 0;
if (decode_can_0x020_MagicNode1R_BLAddy(o, &b)) {
/* error */
}
To transmit a message, each signal has to be encoded, then the pack function
will return a packed message.
Some other notes:
For a specification, as I understand it, of the DBC file format, see dbc.md.
This is a work in progress.
There is a Vim syntax file for DBC files in the project, called dbc.vim
As well as C, XML can be generated, the project contains an XSD and
XSLT file for the generated XML.
An XML based file that can be imported into Beyond Security’s beSTORM and used to test CAN BUS infrastructure.
A flat CSV file can be generated, which is easier to import into Excel.
A JSON file can be generated, which is what all the cool kids use nowadays.
Consult the manual page for more information about the precise operation of the
program.
It would be possible to generate nice (ASCII ART) images that show how a message is
structured, which helps in understanding the message in question, and is useful
for documentation purposes, for example, something like:
Message Name: Example-1
Message ID: 0x10, 16
DLC: 1 (8-bits)
+-----+-----.-----.-----.-----+-----.-----+-----+
| | | | |
| | | | |
+-----+-----.-----.-----.-----+-----.-----+-----+
0 1 2 3 4 5 6 7
Bit 0: Signal-Name-1, 1 bit signal, scalar 1.0, offset 0
Bits 1-2: Signal-Name-2, 4 bit signal, signed, Motorola, ...
... etcetera ...
Or something similar. This would be another output module.