Building the Firmware
The goal here is to create the file foo.ordered.hex
, which will be the file needed by the Arduino or other program that flashes the new firmware.
This is the overall process:
We start with foo.c
, the source file discussed in the previous two posts. When this is compiled, we end up with a relocatable image, foo.obj
, but for reasons outlined next, we need to keep the assembler file (which is needed to generate that relocatable image). So that's the first step at the top left of the diagram, where we generate foo.hex
. (Of course, foo
will be replaced with the real name of our project.)
This file has the data which will be inserted into a "canonical" disassembled image. For now, that image is 1500W_master_rep.a51
. I've created that file from IDA Pro, by disassembling an image I obtained from an Elcon/TC charger. For this image, the data of interest happens to start at address 0xD76. In order to force the data to be linked at that address, we need to edit the assembler source for foo.c slightly. This is done by a sed script (sed is a Stream EDitor, a Cygwin utility). It inserts an ORG statement, and also deletes two lines which cause problems. So that's the second step, where we generate foo.a51
. Don't be too concerned about the number of steps; once everything is set up, most of it is done automatically, either by the Keil development environment, or by the Cygwin script combine.sh
We use a second Keil project (sorry, I can't find a way to combine these two projects). This project takes the two .a51
files, assembles them, links them, and produces two output files: an executable (which we don't need), and the foo.hex
file. Alas, this foo.hex
file needs a little massaging, mainly to combine the various odd-sized segments so that the final foo.ordered.hex
has contiguous lines that all have 16 bytes of data in them. That makes the processing of that file much easier. This is done by two calls to objcopy
(one converts from hex to binary, the other from binary to hex; in the process, the massaging is accomplished.) The script file combine.sh
does this for us, as well as the assemble and link process via a batch file generated from the second Keil project. Phew!
The two Keil project files needed have paths embedded in them, so it's best to generate these as needed. The first one, foo.uv2, is the one you'll be using most of the time. From Project / New uVision Project... enter the name of the project; use the same name as the .c file but with .uv2 extension. You will be asked for the CPU; there are an amazing range of these. Find NXP then near the end of the list of NXP processors, select P89LPC938. When it offers to add the standard startup files, choose No.
You should now have a new project. At left should be a Project Workspace with a Target 1 and a Source Group 1 under that. Right mouse on that source group and select Add Files to Group. Add your foo.c
Because we end up with two project files, the next step is important. Use Project / Options for target Target 1. Click on the Output tab. In the text box next to Name of Executable, enter "dummy" (without the quotes). This is so that it doesn't overwrite important files with the name foo
. Now you can use the build toolbar icons to do the compile. Even after you have fixed all compile time errors, there could well be some sort of error message about a file not being found. This is normal, and can be ignored. However, you should see the file foo.SRC
with a recent time stamp. You should run the combine.sh script now, even though it will bomb out with errors, to generate foo.a51
. To invoke it, in a Cygwin window, change to the directory with foo.c
and enter this command:
You should see foo.a51
with a recent time stamp.
To create the second project, begin again with Project / New uVision Project and give it the name foo_combine.uv2
. Select the CPU as before, and choose No for the startup files as before. Right mouse button on the Source Group 1 and add these files: foo.a51
, and 1500W_master_rep.a51
. Choose Project / Options for Target Target 1, and click on the Output tab. This time, ensure that the Name of Executable is foo
. Also tick these two boxes: Create Batch File
, and Create Hex File
Use the Rebuild All Target Files toolbar icon to build everything. This should create foo.hex
with a recent timestamp, and the file Target 1.BAT
. The latter file is needed by the combine.sh script to do the compile and link with the updated foo.a51
file, when you make changes to foo.c
later. Finally, run the combine.sh
script again, passing foo
as before. You should now have foo.ordered.hex
, ready for flashing.
Later when you make changes to foo.c, you just need to use the first projrect (foo.uv2
, not foo_combine.uv2
) to regenerate foo.SRC
. Then run the combine.sh
script again, and the foo.ordered.hex
file should pop out with no further effort.
file should contain the files you need.