I have successfully made an IAP write and read call to flash memory with a simple data type like char or int. Now I am trying to do the same with a structure that I have defined.
Currently, I simply initialize and set the variables within the structure, then I pass the address of the struct as the source address for the IAP call. Do I have to copy it into a specific RAM location first? I didn't have to do that when I simply wrote a string like "HELLO WORLD\n" to and from Flash. Here is my current code:
struct exercise{ char ex_name[30]; int pic_ID; // Picture reference ID int ex_info; // Exercise reference ID int goal_set; // Goal exercise variables int goal_weight; int goal_rep; int act_set; // Actual exercise variables int act_weight; int act_rep; }; struct day{ int numExercises; char name[15]; struct exercise* exercises[15]; }; struct workout{ int numDays; struct day* days[7]; };
All IAP calls return status code 0, and nothing is printed with the write command, meaning the information was not returned correctly.
// Set up a test workout in Flash void setUpTestWorkout() { struct exercise ex1, ex2, ex3; struct day day1; struct workout workout_send, workout_receive; char buffer[500], text[20]; unsigned int *source, *destination, i; unsigned int command_iap[5], result_iap[3]; unsigned long int enabled_interrupts; IAP iap_bypointer; // Set and initialize exercises strcpy(ex1.ex_name, "Bench Press"); ex1.pic_ID = PIC_CHEST; // Picture reference ID ex1.ex_info = EX_BENCHPRESS; // Exercise reference ID ex1.goal_set = 3; // Goal exercise variables ex1.goal_weight = 150; ex1.goal_rep = 10; ex1.act_set = 0; // Actual exercise variables ex1.act_weight = 0; ex1.act_rep = 0; strcpy(ex2.ex_name, "Closed Grip Press"); ex2.pic_ID = PIC_CHEST; // Picture reference ID ex2.ex_info = EX_BENCHPRESS; // Exercise reference ID ex2.goal_set = 3; // Goal exercise variables ex2.goal_weight = 100; ex2.goal_rep = 8; ex2.act_set = 0; // Actual exercise variables ex2.act_weight = 0; ex2.act_rep = 0; strcpy(ex3.ex_name, "Crunches"); ex3.pic_ID = PIC_CHEST; // Picture reference ID ex3.ex_info = EX_BENCHPRESS; // Exercise reference ID ex3.goal_set = 5; // Goal exercise variables ex3.goal_weight = 0; ex3.goal_rep = 20; ex3.act_set = 0; // Actual exercise variables ex3.act_weight = 0; ex3.act_rep = 0; // Set and initialize day day1.numExercises = 3; for(i=0; i<MAX_EXERCISES; i++) day1.exercises[i] = 0; strcpy(day1.name, "Chest"); day1.exercises[0] = &ex1; day1.exercises[1] = &ex2; day1.exercises[2] = &ex3; // Set and initialize workout for(i=0; i<MAX_DAYS; i++) workout_send.days[i] = 0; workout_send.numDays = 7; workout_send.days[0] = &day1; iap_bypointer = (IAP) 0x7FFFFFF1; // IAP function location clearScreen(); destination = (unsigned int*)0x00040000; // Dest Flash location source = (unsigned int*)&workout_send; // Source RAM location writeText("here1\n"); command_iap[0]=50; //prepare sectors 15-17 for erase call command_iap[1]=15; command_iap[2]=17; iap_bypointer(command_iap,result_iap); if(result_iap[0] != 0) writeText("error preparing\n"); writeText("here2\n"); command_iap[0]=52; //erase sectors 15-17 command_iap[1]=15; command_iap[2]=17; command_iap[3]=60000; iap_bypointer(command_iap,result_iap); if(result_iap[0] != 0) writeText("error erasing\n"); writeText("here3\n"); command_iap[0]=50; //prepare sector 15 for workout command_iap[1]=15; command_iap[2]=15; iap_bypointer(command_iap,result_iap); if(result_iap[0] != 0) writeText("error preparing sector 15\n"); writeText("here4\n"); // strcpy(text, "HELLO WORLD\n"); command_iap[0]=51; //copy RAM to flash command_iap[1]=(unsigned int) destination; // Flash destination command_iap[2]=(unsigned int) source; // RAM source command_iap[3]=256; command_iap[4]=60000; iap_bypointer(command_iap,result_iap); if(result_iap[0] != 0) writeText("error copying\n"); writeText("here5\n"); enabled_interrupts = VICIntEnable; //disable all interrupts VICIntEnClr = enabled_interrupts; memcpy(&workout_receive, destination, sizeof(struct workout)); //memcpy(buffer, destination, strlen(text)); VICIntEnable = enabled_interrupts; //restore interrupt enable register i2cMasterSendNI(TW_MR_DATA_ACK, (u08)strlen(buffer), (u08*)buffer); }
No, it doesn't matter if you write a struct or an int - the same rules apply, i.e. you do not need to copy the struct to a different RAM region before writing to flash.
However, writing a raw struct can be quite bad! If you change the struct, you will have a hard time to read back the contents. Also, if you change to a different version of the compiler, the memory layout of the struct can change. Best is to have a RAW memory buffer where you first store the version of your data, and then pack the individual fields of the struct.
You need a lot more code, but it will help a lot when you add more fields later - checking the version of the stored data will tell your application what fields are available in flash, and what fields you will have to give a default value during the load.
Another thing: why do you disable the interrupts around your final memory copy? It is when you perform the IAP calls that you should disable the interrupts to make sure that your program does not access the flash during the IAP is in progress.
So let me know if I'm wrong, but I think you're saying that I have something like this:
unsigned char raw_buffer[500]; for(i=0; i<sizeof(struct workout); i++) { // copy each variable byte by byte } // Send the raw_buffer instead of workout_send
As for the disabling interrupts, I moved it up before my IAP calls. I have been doing a lot of just static testing to try and make it work before I implement functions that will automate this task. That's why my code is pretty ugly right now.
No, that is what you are doing right now. It works, but is very problematic if you upgrade your software with more/different fields or if you upgrade your compiler.
What I am saying is:
char tmpbuf[CONFIG_SIZE]; char *p = tmpbuf,*size_pos; uint16_t used_size; p = insert_u16(p,CONFIG_VERSION); size_pos = p; p = insert_u16(p,0); p = insert_u16(p,data.field1); p = insert_u32(p,data.field2); ... used_size = p - tmpbuf; // "patch" block with consumed size. insert_u16(size_pos,used_size);
Program this block into flash.
When reading, copy from flash into this raw buffer before extracting.
p = read_u16(p,&version); if (version >= 3) { printf("Unsupported version - exiting\n"); return -1; } p = read_u16(p,&data_size); switch (version) { case 0: expected_size = SIZE_VERSION1; break; case 1: expected_size = SIZE_VERSION2; break; case 2: expected_size = SIZE_VERSION3; break; ... default: printf("Unsupported version!\n"); return -1; } if (data_size != expected_size) { printf("Incorrect size of saved data\n"); return -1; } p = read_u16(p,&data.field1); p = read_u32(p,&data.field2); if (version >= 2) { p = read_u16(p,&data.field3); ... } else { // Old config - use default for new field(s) data.field3 = FIELD3_DEFAULT; ... } if (version >= 3) { p = read_block(p,data.name,32); p = read_u32(p,data.flags); ... } else { strcpy(data.name,"missing"); data.flags = FLAGS_DEFAULT; ... } used_size = p - tmpbuf; if (used_size != expected_size) { printf("Internal error extracting configuration\n"); return -1; } return 0;