Go Down

Topic: how to write and read arduino due internal flash memory? (Read 16408 times) previous topic - next topic

Tevian

Ok I hope someone is still looking here.

I had a project written on a Nano using the EEPROMAnything.h library for help store multiple array's in the EEPROM. Ultimately I ran out of RAM space so I got a shinny new Due. After some hours I was able to get a working sketch on the Due of my project. Now that there is no EEPROM I needed a way to store some values between power cycles. I tried to use the DueFlashStorage but I'm having trouble. BTW this is the first 'real' coding I've done so my knowledge is limited. I'm kinda learning as I go here. This project is an LED controller that uses a Nextion Display as the control interface.


Here is the structure I'm working with.


Code: [Select]

struct LEDAttributes {
  byte onHour;
  byte onMinute;
   int onTimeMins;
  byte offHour;
  byte offMinute;
   int offTimeMins;
  byte rampMinutes;
  byte lightIntensity;
  byte LEDState;  //0-ON, 1-RAMPING UP, 2-RAMPING DOWN, 3-OFF, 4-Bypass
  byte Lightning;
  byte CurrentPWM;
  byte Debug;
};

LEDAttributes LEDChannel[4] =
{
  {0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0}
};


This reads from an address derived by the structure size as far as I know and writes it to the appropriate structure.


Code: [Select]

for (int i = 0; i <= 3; i++)
  {
    EEPROM_readAnything((i * sizeof LEDChannel[i]), LEDChannel[i]);
   
  }


This pulls the values from the Nextion display and writes them to the correct structure. Then an EEPROM_writeAnything command saves the data.

Code: [Select]

void ESPushCallback(void *ptr)
{
    uint32_t hrON = 0;
    n0.getValue(&hrON);
    uint32_t hrOFF = 0;
    n2.getValue(&hrOFF);
    uint32_t minON = 0;
    n1.getValue(&minON);
    uint32_t minOFF = 0;
    n3.getValue(&minOFF);
    uint32_t LEDramp = 0;
    n4.getValue(&LEDramp);
    uint32_t LEDIntensity = 0;
    h0.getValue(&LEDIntensity);
    uint32_t Lightning = 0;
   

  if(LEDChannel[LEDch - 1].LEDState == 4)
  {
    LEDChannel[LEDch - 1].onHour = hrON;
    LEDChannel[LEDch - 1].onMinute = minON;
    LEDChannel[LEDch - 1].onTimeMins = ((LEDChannel[LEDch - 1].onHour * 60) + LEDChannel[LEDch - 1].onMinute);
    LEDChannel[LEDch - 1].offHour = hrOFF;
    LEDChannel[LEDch - 1].offMinute = minOFF; 
    LEDChannel[LEDch - 1].offTimeMins = ((LEDChannel[LEDch - 1].offHour * 60) + LEDChannel[LEDch - 1].offMinute);
    LEDChannel[LEDch - 1].rampMinutes = LEDramp;
    LEDChannel[LEDch - 1].lightIntensity = LEDIntensity;
    LEDChannel[LEDch - 1].Lightning = Lightning;   
    EEPROM_writeAnything(((LEDch -1) * sizeof LEDChannel[LEDch -1]), LEDChannel[LEDch -1]);
  }
}


This allows me to read a single structure into memory for use in the code.

Code: [Select]

EEPROM_readAnything(((LEDch -1) * sizeof LEDChannel[LEDch -1]), LEDChannel[LEDch -1]);


This was a simple test to see how this thing works but I can't seem to make it work. I can get one structure to write but not two. I'm not sure how to write something that will function similar to the EEPROMeverything library. Also if I change the address in the "dueFlashStorage.write(4, b2, sizeof(LEDChannel[0]));" command from 4 to 5 I get an error on the serial terminal that says "Flash start address must be on four byte boundary". I have no idea what that means and my brains are fried because I'm writing this at about 6am after trying all night to make this work.

Any help is great appreciated! Thx.



Code: [Select]

#include <DueFlashStorage.h>
DueFlashStorage dueFlashStorage;


struct LEDAttributes {
  byte onHour;
  byte onMinute;
   int onTimeMins;
  byte offHour;
  byte offMinute;
   int offTimeMins;
  byte rampMinutes;
  byte lightIntensity;
  byte LEDState;  //0-ON, 1-RAMPING UP, 2-RAMPING DOWN, 3-OFF, 4-Bypass
  byte Lightning;
  byte CurrentPWM;
  byte Debug;
};

LEDAttributes LEDChannel[4] =
{
  {12,0,9999,0,0,0,0,0,0,0,0,0},
  {23,23,23,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0}
};

void setup()
{
    Serial.begin(115200);
    delay(500);
   
      byte b2[sizeof(LEDChannel[0])]; // create byte array to store the struct
      memcpy(b2, &LEDChannel[0], sizeof(LEDChannel[0])); // copy the struct to the byte array
      dueFlashStorage.write(4, b2, sizeof(LEDChannel[0])); // write byte array to flash

 
}

void loop() {
  // put your main code here, to run repeatedly:
 
  byte* b = dueFlashStorage.readAddress(4); // byte array which is read from flash at adress 4
  LEDAttributes configurationFromFlash; // create a temporary struct
  memcpy(&configurationFromFlash, b, sizeof(LEDChannel[0])); // copy byte array to temporary struct


 
  Serial.print(configurationFromFlash.onHour);
  Serial.print(",");
  Serial.print(configurationFromFlash.onMinute);
  Serial.print(",");
  Serial.print(configurationFromFlash.onTimeMins);
  Serial.println();
  delay(1000);

}



ard_newbie

Whenever you are using a struct with a DUE (ARM Cortex M3 uc), you have to know that your struct is 4 bytes aligned. Moreover, it is most useful to use uint8_t, uint16_t, uint_32_t, uint64_t, int8_t, int16_t, int32_t, int64_t rather than byte or int (an int is 4 bytes long for a DUE !).

It's easy to see what means "4 bytes aligned" with sizeof() in the example sketch below:

 
Code: [Select]

struct LEDAttributes {
  uint8_t onHour;
  uint8_t onMinute;


  uint32_t onTimeMins;

  uint8_t offHour;
  uint8_t offMinute;


  uint32_t offTimeMins;

  uint8_t rampMinutes;
  uint8_t lightIntensity;
  uint8_t LEDState;  //0-ON, 1-RAMPING UP, 2-RAMPING DOWN, 3-OFF, 4-Bypass
  uint8_t Lightning;

  uint8_t CurrentPWM;
  uint8_t Debug;

};
LEDAttributes LEDChannel;

struct LEDAttributes2 {
  uint8_t onHour;
  uint8_t onMinute;
  uint16_t Padding0;  //***************

  uint32_t onTimeMins;

  uint8_t offHour;
  uint8_t offMinute;
  uint16_t Padding1;  //******************

  uint32_t offTimeMins;

  uint8_t rampMinutes;
  uint8_t lightIntensity;
  uint8_t LEDState;  //0-ON, 1-RAMPING UP, 2-RAMPING DOWN, 3-OFF, 4-Bypass
  uint8_t Lightning;

  uint8_t CurrentPWM;
  uint8_t Debug;
  uint16_t Padding2;  //********************
};
LEDAttributes2 LEDChannel2;

void setup() {

  Serial.begin(250000);

  printf("Size of LEDChannel = %d\n", sizeof(LEDChannel));

  printf("Size of LEDChannel2 = %d\n", sizeof(LEDChannel2));
}


void loop() {

}









MorganS

4-bytes aligned means you should use addresses divisible by 4.

0 4 8 12 16 20...
"The problem is in the code you didn't post."

Tevian

Code: [Select]

      byte b2[sizeof(LEDChannel[0])]; // create byte array to store the struct
      memcpy(b2, &LEDChannel[0], sizeof(LEDChannel[0])); // copy the struct to the byte array
      dueFlashStorage.write(4, b2, sizeof(LEDChannel[0])); // write byte array to flash


Ok so in this example "dueFlashStorage.write(4, b2, sizeof(LEDChannel[0]));" the 4 is indead the address to write to? So as the struct gets written it starts at address 4 and fills 4 byte blocks until its done writting? So If I want to write another struct I need to find out the first open block that is a divisor of 4 and put that into the address variable? Sorry if this sounds like a newbie question.

And also if I 'pad' out the variables will the array of structures still work so I can use the for loops to innumerate the struct names? 

Like this..
Code: [Select]

for (int i = 0; i <= 3; i++)
  {
    "flash write"...((i * sizeof LEDChannel[i]), LEDChannel[i]);
  }


Or will I need something more specific dealing with the individual structs. Sorry again if this sounds like a weird question. When I get home i'll try with padding the struct. Thx much everyone!

ard_newbie

#19
Nov 17, 2019, 05:16 am Last Edit: Nov 17, 2019, 06:12 am by ard_newbie
The padding of structures is done automatically by the DUE compiler (except at the end of a structure).

The object of padding in the example sketch in #16 is only to show you that struct are 4 bytes aligned. IMO adding manualy paddings in struct avoids some misunderstandings.

And yes, the "idea" of your last line of code should work.

Code: [Select]

#include <DueFlashStorage.h>
DueFlashStorage dueFlashStorage;

// I add padding to make sure struct size is a multiple of 4
struct Configuration {
  uint8_t a;
  uint8_t b;
  uint8_t c;
  uint8_t Padding;
};

const uint8_t StructSize = 2;
Configuration configuration[StructSize];
const uint8_t Size = sizeof(Configuration);

void setup() {

  Serial.begin(250000);

  configuration[0].a = 0;
  configuration[0].b = 1;
  configuration[0].c = 2;

  configuration[1].a = 3;
  configuration[1].b = 4;
  configuration[1].c = 5;

  uint8_t MyArray[Size]; // create byte array to store the struct
  for (uint8_t i = 0; i < StructSize; i++)
  {
    memcpy(MyArray, &configuration[i], Size); // copy the struct to the byte array
    dueFlashStorage.write(4 + (i * Size), MyArray, Size); // write byte array to flash
  }

}


void loop() {
  uint8_t* Array;
  Configuration configurationFromFlash; // create a temporary struct

  for (uint8_t i = 0; i < StructSize; i++)
  {
    Array = dueFlashStorage.readAddress(4 + (i * Size));
    memcpy(&configurationFromFlash, Array, Size); // copy byte array to temporary struct

    // print the content
    Serial.print("a:");
    Serial.print(configurationFromFlash.a);

    Serial.print(" b:");
    Serial.print(configurationFromFlash.b);

    Serial.print(" c:");
    Serial.print(configurationFromFlash.c);
    Serial.println();
  }

  Serial.println();
}



BTW, some users have had issues with DueFlashStorage library, after writing in Flash, they can't upload a new sketch. This issue has been solved in several threads in the DUE sub forum in order to unlock lock bits.


Tevian

This has been a big help!! It kinda works now!! LOL

One thing I had to do was to pre-format the space with zeros. When I flash the blocks are all at max value and it messes with the loops I have. Wasn't sure of the best way to do this so I'm checking the first saved value to see if its larger than 23. That being the largest value in hours to be stored there. And if so clear the whole space.

Code: [Select]

  uint8_t test = dueFlashStorage.read(4);
    if (test > 23)
    {
    uint8_t MyArray[LEDStructSize];
   
     for (uint8_t i = 0; i < LEDAttributeCount; i++)
      {
        memcpy(MyArray, &LEDChannel[i], LEDStructSize);
        dueFlashStorage.write(4 + (i * LEDStructSize), MyArray, LEDStructSize);
      }


Also I have another array that has relay attributes. These structures are a different size than the LED ones. I need to store them at the end of the LED attribute space. So would something like this be appropriate for the dueFlashStorage.write command?
Code: [Select]

for (uint8_t i = 0; i < RLYAttributeCount; i++)
  {
    memcpy(MyArray2, &RLYChannel[i], RLYStructSize);
    dueFlashStorage.write(4 + ((i * RLYStructSize) + (LEDAttributeCount * LEDStructSize)), MyArray2, RLYStructSize);
  }


Super helpful guys. Thank you!

Go Up