We recently worked on implementing a bootloader for a Texas Instruments (TI) Piccolo microcontroller. Ultimately, a GUI (developed separately) would send SRecord data over the CAN bus to our device using a specific protocol. The trickiest aspect of the embedded side was working through the nuances of TI’s Flash API to make sure that the application image was correctly written to the appropriate location in Flash. Once we completed the implementation, we needed a way to test it.
However, the GUI was under development and we didn’t know if it was implemented correctly. We needed a quick way to send SRecord data over the CAN bus, confirm that the data on the bus matched that data in the SRecord, and step through our embedded software implementation with a debugger to confirm that our implementation was correct.
We used a Canable, can-utils on Linux, and Python. We created a simple script that would parse an SRecord file and invoke the cansend application in can-utils to send a SRecord over the CAN bus. The script may be found on our github.
Our hardware setup was simple, as shown in below:
The TI development board is in the middle and the Canable is at the top. We have a micro USB cable going from the Canable to our Linux PC. Finally, the CANH and CANL lines from the Canable are connected to the CAN transceiver on the TI development board.
The initial configuration of the Canable using can-utils on our Linux PC was straightforward. First, we looked at the output of dmesg to determine which serial port was enumerated when we plugged the Canable into our Linux PC:
Here we can see that the device was enumerated as device ttyACM0. Then we executed the following commands on our Linux PC to configure the Canable as just another network interface using slcand (Note: the argument for the “-s” parameter may be different depending on the configuration of your embedded system):
sudo slcand -o -c -s6 /dev/ttyACM0 can0 sudo ifconfig can0 up sudo ifconfig can0 txqueuelen 1000
Then, we ran our Python script to parse an SRecord firmware file and send each record over the CAN bus.
Our script, which can be found on our github, is straightforward.The first portion imports the required libraries, sets some constant variables, and performs some error checking:
We created contrived ARB IDs to indicate whether a CAN frame is the start of an SRecord, a continuation of an SRecord, or the end of an SRecord. We also created an ARB ID to indicate that the CAN frame consists of a complete SRecord.
The fact that an entire SRecord may not fit into a CAN frame necessitated different ARB IDs. A complete S3 SRecord, which is what we are using, can have up to 69 bytes of data (you can see this really nice Wikipedia page about SRecord formats). However, since we are using “traditional” CAN and not using CAN-FD, our CAN frame could only contain up to 8 bytes. Thus, if an SRecord were too big, we would need to break it up and use the ARB ID as an indication to the embedded side.
The second portion of the script is more interesting:
First, we attempt to open the file that was passed in for reading. If there was an issue in opening the file, we catch the the exception and print out a message (B). Then, we read the file line by line. If the first character in the line doesn’t start with an ‘S’, we print an error message (Note: We should probably exit out of the script, instead of continuing to read each line, since the file is most likely not an SRecord file). If the line does start with an ‘S’, we strip any trailing newlines, and replace the first character with a ‘0’ (A). We do this on the PC side to eliminate a similar need on the embedded side and to keep it simple.
Then, we break up each SRecord into chunks that can fit in a CAN frame (C). At (D), we see if the size of the resulting list is 1. If it is, that means the entire SRecord can fit into a single frame. We set the appropriate ARB ID and formulate the correct CAN message.
At point (E), we determine whether the chunk is the start of a SRecord, a continuation, or an end. We set the appropriate ARB ID, and formulate the CAN message.
Finally, we make a system call to the cansend utility, passing it the CAN interface defined above and the CAN message.
We used the candump utility in another window to watch the CAN messages going out and confirmed that their contents matched the SRecord file.
MAB Labs needed to quickly spin up a testbench to validate our embedded system design. We leveraged inexpensive hardware, open source utilities, and a simple script in Python to ensure that our embedded software implementation was correct and performant.
If you need any help with your IoT needs, schedule a conversation by clicking here and completing the form.