Ken
Ken CTO of Canis Automotive Labs

The canframe.py tool

We have developed a simple CAN calculator in Python called canframe.py. It defines a class CANFrame that calculates the bitstream for a CAN frame, and includes a print method that displays the fields in the CAN frame. It is available in the CANHack GitHub repository in the src folder.

For example, here is the CAN frame with standard ID of 0x014 and a payload of 0x01:

Correct bit pattern for the CAN frame

The output of the calculator shows the stuff bits in red. The CRC for the frame is 0x7753, or 111011101010011. The reason we picked this frame is that it’s the one shown in the Wikipedia page on CAN. This this image is used everywhere, from academic papers to conference papers to blogs:

Wikipedia CAN frame with the wrong CRC

This image isn’t a real CAN frame trace (the stuff bits have been omitted). But it’s also not a proper CAN frame trace because the CRC is wrong. It’s been there since 2014, so probably it’s now time it was fixed.

We’ve created a new image but Wikipedia won’t let me upload it for some reason. If anyone else wants to take this image and replace the existing broken one then feel free.

Fixed image for Wikipedia

The traces in the image were taken from a real CAN system: we used the Digilent Analog Discovery 2 USB scope. This is a great piece of hobbyist kit: it can do a lot more than just be a scope: it also has digital inputs and the software includes a logic analyzer, which has a number of protocol decodes, including for CAN. Here’s what it shows when that’s used:

Digilent logic analyzer trace for Wikipedia CAN frame

CAN H and CAN L was measured on our benchtop CAN bus (more about this in another post) and the digital CAN RX trace comes from the RX pin of a Microchip 2562FD CAN transceiver on a PCB produced by Canis Automotive Labs. The CAN frame was transmitted from an STM32F405 microcontroller with the on-chip bxCAN CAN controller.

UPDATE 2020/25/10

We’ve made some changes to the tool: there is some more detail in the detailed output (giving individual field values) but the big change is that we’ve added a protocol decoder: feed in a bitstream (a string of ‘0’ and ‘1’ characters) and a CAN frame is extracted from the bitstream. The static method from_bitseq returns a dictionary consisting of the decoded field values and either a key of 'stuff_error' and the bit number where the stuff error was found, or an instance of CANFrame created from the field values.

This decode feature is useful for creating a protocol decoder. Our next goal is to create a GTKWave filter so that an arbitrary waveform can be decoded (@Matthew Venn has some example code on how to do this).

comments powered by Disqus