Introduction
I had a hard time finding a simple and well documented CAN based bootloader of AVR processors so I decided to implement one myself. As of now, the bootloader supports basic functions such as reading and writing flash and eeprom. It also has the possibility of rebooting the module and starting the main application. The documentation should be pretty good and simple to understand. I have done my best to keep things simple and readable. The latest version of the code is always available at https://github.com/huukit/public The latest documentation can be found at http://proximia.fi/doc/canbootloader/
At this point the full bootloader consumes just over 2k of flash and could probably be optimized further to fit in to 2k.
- Note
- Advanced functions such as writing fuses is not supported yet. These are coming in future versions.
Communicating and protocol.
- Protocol
The protocol that the bootloader uses is based on a simplified scheme of CANOpen communication. See: https://en.wikipedia.org/wiki/CANopen This means that the 11bit CAN message ID is broken in to two different sections. A 4bit function code and a 7bit node identifier. Thus any network can have up to 127 nodes (127 unique node ID's) Function codes define what the message type is.
- Note
- The reason this scheme has been chosen is that transition to a actual CANOpen compliant bootloader will be possible in the future.
-
For the function code definitions see: http://www.canopensolutions.com/english/about_canopen/predefined.shtml
This bootloader uses the following messages to communicate between a network master (Node ID 0) and the node.
0x580 + NodeID : Node Transmit
0x600 + NodeID : Node Receive
This is where the similarities witn CANOpen end. Normally CANOpen uses a quite heavy directory structure to access data but instead of implementing a full directory i have designated the first data byte of an SDO message to be the command byte. This brings some limitations (for example a limited amount of commands) but it is much easier to implement and undestand. The data length of a message is predifined and so it is not needed in the message. Thus all 7-bytes of the message can be used for data. Any command that is sent must be replied by the node with the same command byte. This makes it easy to implement timeouts.
- Note
- All used command bytes are defined in the messagedefinitions.h file.
- An example
- To better understand the principle of the communciation between the master and a node, here is an example where a session request is sent to the node by the master.
#include "master.h"
#include "can.h"
void sendSessionOpenRequest(nodeid id){
CanMessage m;
m.id = nodeDefinitions::mtypeSDOMaster | id);
m.data[0] = nodeDefinitions::mactypeSendBootloaderVersion;
n.dlen = 1;
sendCanMessage(&m);
}
If the node id was 5, this would send a CAN message looking as follors: ID DLEN DATA0
0x605 1 0x50
Then the node would reply to the request (this is actually from CanBootloader.c): if(can_getMessage(&m)Y){
switch(mcommand){
{
m.data[4] = boot_signature_byte_get(0x00);
m.data[5] = boot_signature_byte_get(0x02);
m.data[6] = boot_signature_byte_get(0x04);
m.length = 8;
sessionOpen = 1;
can_sendMessage(&m);
break;
}
...
The reply would look as follows (depending on the node type and processor used. ID DLEN DATA0 DATA1 DATA2 DATA3 DATA4 DATA5 DATA6 DATA7
0x585 8 0x50 0x00 0x01 0x00 0x23 0x34 0x45 0x0F
Usage
- Configuration
- Before you can use this bootloader you must configure loaderconfiguration.h for your device. All data is sent in the big endian format for easier bus debugging.
- Session
- Once you have an idea of the communication usage is very simple. The only thing you need to do is open a session with MACTYPESENDBOOTLOADERVERSION and after that use the commands to write and read flash.
- EEPROM
- Writing and reading EEPROM is simple and straight forward. There are only two commands:
#define MACTYPEREADEEPROMBYTE 0x55
#define MACTYPEWRITEEEPROMBYTE 0x56
The data structure is as follows:
- data byte is the command above. 2-3. data bytes are the address to read/write
- data byte is the data to write when writing data.
- Note
- The length of the CAN data is 3 bytes for reading and 4 for writing.
- FLASH
- Because of the page structure of flash memory writing it is a little more complex and it has to be written page by page. This bootloader implements a pagebuffer that has to be filled with writable data before it can be written to the actual memory of the device. There are several commands involved here:
#define MACTYPEREADFLASHBYTE 0x5A
#define MACTYPESELECTFLASHPAGE 0x5B
#define MACTYPEWRITEFLASHPAGE 0x5C
#define MACTYPEREADBYTEFROMFLASHBUFFER 0x5D
#define MACTYPEWRITEBYTETOFLASHBUFFER 0x5E
The steps for writing flash are as follows:
- First select flash page with MACTYPESELECTFLASHPAGE. First byte is command, second is the page number.
- Next write to the page buffer with MACTYPEWRITEBYTETOFLASHBUFFER. First byte is command, second and third are address and fourth is data byte. Address is to internal page buffer.
- Optionally check the buffer with MACTYPEREADFROMFLASHBUFFER (you may skip this step and verify the actual flash). Address is to internal page buffer.
- After you have filled the buffer, request a write to flash with MACTYPEWRITEFLASHPAGE.
- Finally verify the written section with MACTYPEREADFLASHBYTE. First byte is command, second and third are addrress. Here the address is the actual device flash address 0x00 to FLASHEND.
- Note
- Address for page buffers are page buffer addresses (0x00 to SPM_PAGESIZE), not the internal flash.
- Restarting
- After you have verified the FLASH and EEPROM, you may send the node the reset command (MACTYPERESETNODE). This will reset the bootloader enable byte defined with EEPROM_BOOTLOADER_ENABLE_ADDRESS and start the actual application.
- Commands and responses
- See messagedefinitions.h for command responses and byte positioning.