This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

IAP write/read a Struct

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);
}

Parents
  • 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.

Reply
  • 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.

Children
  • 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;