CAN code generator.
More...
CAN code generator.
RE: Jamie Pruett
+1 (312) 882-5212
pruet.nosp@m.t4@i.nosp@m.llino.nosp@m.is.e.nosp@m.du
Please reach out with any questions :D
CAN Codegen
- Todo
- pruett4 Update all of this for Bazel
The CAN codegen library aims to minimize development/debugging headaches when dealing with CAN message data. C code is generated from a DBC file (CAN message database) to pack real values into their encoded state in a CAN message for transmission and to unpack encoded signals in a received CAN message into real values. Skip to the usage examples section to get started quickly or keep reading for a more detailed explanation :)
The codegen.py Python script generates C source/header files for each DBC in the can_codec/dbc/ directory. There's one DBC for each CAN bus (CAN bus refresher) - can_1.c and can_1.h contain generated code for all the messages on CAN 1, can_2.c/h for CAN 2, etc.
CAN messages follow (or should follow) this naming scheme in DBCs: [sending node]_[message_name]. All identifiers generated for a message (structs, functions, defines, etc.) use the message name as it appears in the DBC, prefixed with the name of the CAN bus, i.e. CAN_1_VNAV_TV_INPUTS_ID, struct can_1_vnav_tv_inputs, can_1_vnav_tv_inputs_pack(). See the codegen example for an example of all the generated code for a message.
[bus_name].h:
- Defines for message ID, length (in bytes), and whether or not the message has an extended/29-bit ID.
- Two structs for each message:
- One contains unpacked/decoded values for the signals in a CAN message.
- The other (_packed suffix) represents the message in its packed state. There are very few reasons to use the _packed struct for a message in application code - they're mostly for internal use
- _packed structs use GCC's __attribute__((packed)) so a pointer to a uint8_t array can be cast to a pointer to a _packed struct without issues and so padding can be manually inserted to match the CAN message definition
- Both structs have detailed Doxygen documentation describing the units/range (very useful) for each signal in a message as well as the scale/offset parameters from the DBC that define how signals are encoded.
[bus_name].c:
- [message_name]_pack()
- Packs "real" values from an instance of struct [message_name] into a uint8_t array to be sent as a CAN message
- Parameters:
- Pointer to uint8_t destination array
- Pointer to struct [message_name] (contains values to pack)
- Length of destination array
- Returns 0 if the values were successfully packed or a nonzero error code on failure
- [message_name]_unpack()
- Unpacks encoded values from a uint8_t array of data received over CAN into real values in an instance of struct [message_name]
- Same as [message_name]_pack(), but in reverse
- Parameters:
- Pointer to struct [message_name] (will be populated with unpacked signal values)
- Pointer to uint8_t source array
- Length of source array
- Returns 0 if the values were successfully unpacked or a nonzero error code on failure
Codegen Usage Example
This is the code generated in can_1.h for the (current version of the) VectorNAV Buddy Board TV inputs message.
#define CAN_1_VNAV_TV_INPUTS_ID 0x210
#define CAN_1_VNAV_TV_INPUTS_LENGTH 8U
#define CAN_1_VNAV_TV_INPUTS_IS_EXT false
struct can_1_vnav_tv_inputs {
float ax;
float ay;
float gz;
float vx;
uint8_t vnav_status;
};
struct __attribute__((packed)) can_1_vnav_tv_inputs_packed {
uint16_t ax : 16;
uint16_t ay : 16;
uint16_t gz : 16;
uint16_t vx : 14;
uint8_t vnav_status : 2;
};
int can_1_vnav_tv_inputs_pack(uint8_t* dst_ptr, const struct can_1_vnav_tv_inputs* src_ptr, size_t size);
int can_1_vnav_tv_inputs_unpack(struct can_1_vnav_tv_inputs* dst_ptr, const uint8_t* src_ptr, size_t size);
Example usage - unpacking a received CAN message:
case CAN_1_VNAV_TV_INPUTS_ID: {
struct can_1_vnav_tv_inputs vnav_msg;
}
}
volatile CAN_RxHeaderTypeDef rx_header
Definition main.c:106
volatile uint8_t rx_data[8]
Definition main.c:108
FDCAN_HandleTypeDef hfdcan1
Definition main.c:58
Example usage - packing a CAN message for transmission:
tx_header.Identifier = CAN_1_VNAV_TV_INPUTS_ID;
uint8_t
tx_data[CAN_1_VNAV_TV_INPUTS_LENGTH];
struct can_1_vnav_tv_inputs vnav_msg;
vnav_msg.ax = vnav_uart_data.ax;
vnav_msg.ay = vnav_uart_data.ay;
vnav_msg.gz = vnav_uart_data.gz;
vnav_msg.vx = vnav_uart_data.vx;
vnav_msg.vnav_status = vnav_status;
can_1_vnav_tv_inputs_pack(
tx_data, &vnav_msg, CAN_1_VNAV_TV_INPUTS_LENGTH);
uint8_t tx_data[8]
Definition main.c:107
CAN_TxHeaderTypeDef tx_header
Definition main.c:105
Editing DBCs
Wiki page on the way!
The easiest way to edit DBC files is to use CANdb++ Editor (the free version of CANdb++). Vector makes it a little difficult to find on their website - the installer (Windows only :( ) is available if you search for it in their download center. Please follow the [](CAN message/signal naming convention) and (especially for CAN 1) the [](CAN ID assignment scheme) when adding new messages to a DBC!
Generating Code After DBC Changes
- Make sure you have Python installed
- Create, activate, and set up a Python virtual environment (can be reused for future code generation) by running the following commands:
- python -m venv venv - creates a new virtual environment in the current directory under the venv/ subdirectory
- Linux/MacOS: source venv/bin/activate, Windows: .\venv\Scripts\activate - future Python commands will now use the virtual environment instead of your system Python until you run deactivate. If you're on Windows and using Powershell, you'll have to enable running scripts on your system first. Command Prompt (cmd) should be fine out of the box
- In the project root directory, run pip install -r requirements.txt to install necessary Python packages
- In the can_codec/ subdirectory, run python codegen.py. That's it!
CAN Bus Refresher
- CAN 1:
- Car-critical messages
- All message received by a CAN node other than the logger or Plex (dashboard display) should be on CAN 1
- Limited high-importance logging and board status messages
- CAN 2:
- Logging bus
- As a general rule, only the logger and Plex should be receiving messages on CAN 2
- CAN 3:
- Inverter CAN
- Safety Board and AMK (all 4 inverters) are the only nodes on CAN 3 to maximize stability/reliability and minimize latency
- Logger is not on CAN 3 - all CAN 3 messages transmitted or received by Safety are echoed on CAN 2 for logging