Firmware Developer Guide¶
As much as possible, the firmware is written as vanilla C that can compile and run on any platform. The advantages of this are that we can write unit tests and run them on a development computer, which really speeds up the code-compile-debug cycle.
Each embedded platform that the firmware supports needs a set of board support
implementations in the
platform folder. Right now we support the PIC32 (2
different variants) and LPC1768. There are a number of functions declared in
header files that are indicated as “platform-specific” that must be implemented
for each board you want to support. There isn’t a good central place to find
this list right now, but if you start from one of the existing platforms it
should be clear what needs to be implemented. The code only uses
platform-specific C extensions and library includes in these
The unit tests are all in
src/tests, and the tests implement their own
platform specific code in
src/tests/platform - many of these include test spy
functionality so the unit tests can verify that an LED turned on, a CAN message
was sent to the controller, etc. You can run the test suite with
The firmware runs on bare metal with a main event loop. To understand the firmware, start walking through the code from there, as that is the primary driver of the application. At a high level it runs this each time through the loop:
- Process any received CAN messages that have been queued up during a CAN rx interrupt - call any registered handler functions, translate messages and queue output to send back to the client via USB/BT/network.
- Send any pending diagnostic requests to the bus.
- Handle any data received from a USB/BT/network connection, including incoming commands and CAN write requests.
- Flush all queued outgoing CAN messages to the bus.
- Update the LEDs to indicate if the CAN bus is connected, and which output interface is active.
- Finally, flush any queued data to the output interfaces (USB/BT/network).
You’ll notice when receiving and sending data, we make use of a buffer - this is to avoid doing very much work in the interrupt handlers for CAN/USB/UART.