We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Is it possible for a function to return a union?
Storing data in the Flash/EEData memory of the ADuC845, is done through operating some SFR registers and page write. I would like to read and write different kind of variables and wounder if its possible to use the same function.
Example: // EEData page address #define SERIAL_NO 0x0000 #define DEV_ADR 0x0010
unsigned long serial_no = 123456789; unsigned int dev_adr = 255;
void main (void) { // saving data write(serial_no, SERIAL_NO); write(dev_adr, DEV_ADR); // reading data serial_no = read(SERIAL_NO); dev_adr = read(DEV_ADR); }
My idea was something like this:
union u { unsigned int ival; unsigned long lval; }
void write(union u data, long adr) { // save data in EEData at address long }
union u read(long adr) { // read data from EEData // and return (???) }
Is it possible to do it this way or perhaps a solution would be the use of pointers but how?
Any suggestions?
I do return a union like this by returning u.lval
I doubt you can return a union any longer than a long.
Erik
"... or perhaps a solution would be the use of pointers but how?"
A more conventional interface is for the read/write routines to deal with data objects of arbitrary size.
#define SERIAL_NO 0x0000 #define DEV_ADR 0x0010 unsigned long serial_no = 123456789; unsigned int dev_adr = 255; void write(unsigned ee_addr, void *p_data, size_t size) { } void read(unsigned ee_addr, void *p_data, size_t size) { } void main (void) { write(SERIAL_NO, &serial_no, sizeof serial_no); write(DEV_ADR, &dev_adr, sizeof dev_addr); read(SERIAL_NO, &serial_no, sizeof serial_no); read(DEV_ADR, &dev_adr, sizeof dev_addr); }
You can do like the malloc() family: use void* to pass a pointer to the data object, and a unsigned char to pass the size of the object. The use of void pointers is a powerful means of building polymorphic functions. You then pass the actual size of the data object using the sizeof() compiler macro.
You could do it like this:
unsigned char xdata user_data[1024]; // simulates xdata mapped space void write_xeeprom(void xdata *src, unsigned int addr, unsigned char sz) { // do your thing here // e.g., to access *src as bytes, cast it to char: unsigned char xdata *dst; // ptr to the xdata array dst = &user_data[addr]; // addressed relative to the array start do { *dst++ = *((unsigned char xdata*)src)++; } while(--sz); } void main(void) { unsigned long lvar; unsigned int ivar; lvar = 0x12345678; ivar = 0xabcd; write_xeeprom(&lvar, 0x0000, sizeof(lvar)); write_xeeprom(&ivar, 0x0008, sizeof(ivar)); }
Please note that the above exampleware has a few optimizations: 1) The pointer passed is for xdata memory, instead of a generic pointer. Passing generic pointers to functions has a very high cost. If a generic ptr is used, the parameters will be passed in global vars, instead of in registers. You may decide that you want the generic version, or you may have one function for each memory type. I always use the second alternative. 2) The do{}while() loop assumes nonzero size, and optimize to a single DJNZ instruction. 3) Look at the pointer cast used to get single bytes from the passed ptr. This is where you make the void ptr to become what you want (char* in this case).
The read() can be done similarly, with the very same prototype and concept.
The beauty of this method is that you are not limited to scalar types, but you can write and read whole structures with the same simple interface as you do for chars.
I see that Dan offered a suggestion similar to mine. We were posting simultaneously, and I couldn't see his post, sorry about that :P
Thanks everyone for your reply.
I really appreciate your time and effort in helping others.
Regards
Peter Bøgely