Content originally posted in LPCWare by DiligentMinds.com on Sat Sep 08 19:12:23 MST 2012
At the end of the day, it doesn't matter. An int and a long are the same size on this MPU. The ROM code doesn't care-- it just wants 2 pointers to arrays that hold 4-byte-sized things. There is no way for the ROM code to know if you passed it an int, a long, or an unsigned long (or int). It is going to get it's command and send it's answer from/to those.
The two things must agree though in your code, or the compiler will (or should) complain that the parameter types don't match. For that reason, I defined the two parameters as (void *)-- since a (void *) will take a pointer to anything, and I *think* the different commands take different arguments. You could change these to the uint32_t type if you like, which will allow for better compile-time type checking, if that's all you pass to the IAP-- (otherwise, you might be doing some type-casting to put int's into the uint32_t array-- I don't know what you compiler will do with this.)
In embedded work we need to have absolute dictatorial control over what memory gets allocated where, so I like to use the C99 standard definitions (int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, etc.)-- which avoids all of this confusion. (If you're not using Keil, you may have to include a header file for this to work). When dealing with unions and structs-- usually you want to use the "__packed" modifier so the compiler doesn't pad these (unless you want it to).
Here's some code I have in my project for using the IAP ROM code to fetch the device Unique ID [UID]:
// In-Application-Programming [IAP]
//
// caller creates these as an auto variable, and populates the command array
// the result will be in the result array
// worst-case command size is 5, worst-case result is 4
//
// uint32_t IAP_command[5];
// uint32_t IAP_result[4];
//
// IAP usage: IAP_entry (IAP_command, IAP_result);
//
typedef void (*IAP)(void *, void *);
IAP IAP_entry = (IAP)0x1FFF1FF1;
.
. (later in my code)
.
// get the 128-bit device serial number
// return 4 DWORDs into an array supplied by the caller
//
void IAP_get_device_UID (uint32_t UID_array[4])
{
uint32_t IAP_command = 58;
IAP_entry(&IAP_command, UID_array);
}
Notice here that I didn't use an array for the command, since there was only one DWORD needed. This code is tested and works well. Also, note that (depending on the device) the "magic number" that represents the IAP code entry point can vary-- so get this from YOUR device's user manual. This code was written for the LPC11U24, but I'm fairly certain that it will work for any LPC1xxxx. This is a great way to make sure your code can be re-used-- if you tried to get the device UID yourself, it could end up moving around based on the device die revision-- this way, you always get the right information.
With my device, the first number in the array element[0], is zero-- which makes me wonder if the most active number in this array isn't element[3]. I don't have a lot of devices to test-- maybe someone else could answer this.
With other IAP commands, you may need an actual command array, and then fill it up with the information needed before calling the IAP code. The size needed for the command and response arrays varies with the command. In their example code, they just made the two arrays global, and gave the worst-case sizes for each, but if these are global then the code has no chance of being re-entrant-- (which may or may not matter to you).
BTW-- I tested this code with the (void *) parameters changed to uint32_t[] parameters, and it works fine. You can also make the parameters (uint32_t *). The code will compile just fine and work just fine either way. Probably, the uint32_t[] definition is the more readable version:
typedef void (*IAP)(uint32_t[], uint32_t[]);
With the above, you can see quite well that the function requires arrays as parameters. So, define this however you like-- it will "just work"...
I hope this answers your questions!
Regards,
Ken Peek
*** EDIT *** EDIT *** EDIT *** EDIT *** EDIT *** EDIT *** EDIT ***
Good thing you asked this question. Upon reflecting on the code snippet above, I came to the conclusion that the following would be better:
typedef void (*IAP)(uint32_t[5], uint32_t[4]);
const IAP IAP_entry = (IAP)0x1FFF1FF1;
I changed the parameters to the uint32_t[] type for readability. The '5' and '4' in the array brackets does nothing-- it is just a reminder to the reader that these are the worst-case sizes needed. In addition, I placed the "const" keyword in front of the instantiation. This forces the function pointer into flash memory, with no RAM taken up and there is no flash memory needed to initialize the pointer in RAM. This saves 8 bytes of Code, increases the RO-data by 4 bytes, saves 4 bytes of RW-data, and 4-bytes of ZI-data. (This is with the Keil tool-chain). So, write your definition like this, and you should have the most efficient implementation. This code is tested and works fine.