In fact it's quite easy - there are multiple methods. I outlined one in this thread.
Basically you will have two separate pieces of software running. Consider the following setup (it's just an example):
- Bootloader (Flash address 0x0000 - 0x1000)
- Application (Flash address 0x1000 - 0x2000)
(of course, those are dependent on your requirements)
Now, the application will download the binary (we'll call it FW 2.0). It will set some special bit in your controller and perform a reboot. Your CPU always starts at address 0x0000, it will load your bootloader. Your bootloader checks if the "special" bit is set and will then flash the binary file to address 0x1000-0x2000. It will then reset that special bit, indicating no new firmware is available. It will then reset itself again.
Now, again, the bootloader starts - it will detect that the special bit is in fact NOT set and do nothing but simply perform a jump to the application (0x1000) from where the program will execute (now with the new firmware).
You might also want to include an option to run a default firmware or to load a firmware via UART in case your application cannot be written correctly or power is lost during the update.
The way we do it in one of our products is like this (code is based on IAR compiler, so pragmas might differ for you):
We defined a structure which contains important information of the application part (which is called mainpart in our projects):
#pragma pack(push,1)
struct softwareRevision_t {
uint8_t VersionSystem;
uint8_t VersionFunction;
uint8_t VersionError;
uint8_t VersionCustomer;
uint8_t BuildNumber;
};
#pragma pack(pop)
#pragma pack(push,1)
struct mainInfoStruct_t {
softwareRevision_t softwareRevision;
uint32_t startFunctionAddress;
uint8_t reserve[27];
uint32_t bootControl;
};
#pragma pack(pop)
This contains among other things the uint32_t startFunctionAddress
. Now for this structure to be useful, you have to do several things:
- Initialize it with meaningful values in the application
- Make it
const
so it can be placed in flash
- Tell the linker to place the structure always on the exact same location every time you compile your project
- share the location and structure between application and boot part
In the application part:
To share the location, we created a header-file which is included in both parts with a simple define:
#define MAIN_INFO_MEMORY_POSITION (0x0807FFD4)
In a .cpp
file of our application we create an instance of the structure which is placed accordingly:
#pragma location = MAIN_INFO_MEMORY_POSITION
extern __root const mainInfoStruct_t mainInfoStruct =
{
{
1U, // mainInfo.softwareRevision.VersionSystem
0U, // mainInfo.softwareRevision.VersionFunction
0U, // mainInfo.softwareRevision.VersionError
0U, // mainInfo.softwareRevision.VersionCustomer
0U // mainInfo.softwareRevision.BuildNumber
},
reinterpret_cast<uint32_t>((&_start)), // mainInfo.startFunctionAddress
{0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U,0U}, // reserve
0xF0F0FFFFU // mainInfo.bootControl
};
Several things happen here:
#pragma location = MAIN_INFO_MEMORY_POSITION
tells the linker where to place the next structure.
The __root
tells our linker that this symbol must be kept, even if it is not referenced by the application code (which it isn't). It has to be const
otherwise it would not get placed in flash (if it doesn't end up in flash try adding a static
).
So with this we have done everything in the application.
In the boot part:
The boot part needs access to the application part info structure. We wrapped a class around this whole thing, but basically a pointer will be enough:
volatile mainInfoStruct_t* pMainInfo = reinterpret_cast<mainInfoStruct_t*>(MAIN_INFO_MEMORY_POSITION);
We use volatile
here to make sure, that the compiler will not optimize the access to the flash away. With this you can now jump into the application code with an ugly cast:
(reinterpret_cast<void (*)(void)>(pMainInfo->startFunctionAddress))();
Notes:
In our application we do a CRC check over the whole application part which includes the mainInfoStruct, so we don't jump into nirvana is something went wrong with the update.
The first thing we do in our application is to set the stackpointer to the value it is expected to start with (you can get that from the linker configuration file or your interrupt vector table). Otherwise you might end up with a stack overflow and corrupting your other data.
This is not a minimal example, but might give some hint on what might be useful in a shared structure as well.
We use C++, sorry for the reinterpret_cast
s.
Best Answer
My bootloader is working fine. I have used .bin file for downloading in flash. to convert .elf to .bin file use the post building options for GNU gcc compiler as follow : arm-none-eabi-objcopy -O binary My_project.elf My_project.bin
It converts My_project.elf file to My_project.bin file.