Hi there,
I am trying to write (a lot) of sensor data to the internal flash of a SAMD21 chip during execution. I am aware of the FlashStorage library from Cmaglie but it does not quite work for my application (storing 200kB of binary data).
I am also well aware of the limited number of write cycle of the flash and it is not an issue for my application.
I have been reading the SAMD21 datasheet and I am trying to adapt Cmaglie FlashStorage library to do my bidding.
In the example code below I am trying to write a 64 Byte array to one page of flash (but if I can figure it out I would like to write bigger array). The code keep freezing after "Test5" is printed (during the erase operation).
I am not very familiar with pointers and I have kind of tried to adapt the library but I probably made a mistake there since it relies heavily on pointers and I don't understand fully what it does.
I have been careful about the required 256 bytes alignment and the page size, row size and number of page match the particular chip I am using (SAMD21E18A).
Here is the code:
const uint32_t PAGE_SIZE = 64;
const uint32_t PAGES = 4096;
const uint32_t MAX_FLASH = 262144;
const uint32_t ROW_SIZE = 256;
uint8_t ram_buffer[64];
uint32_t adress_row;
const volatile void *flash_address;
const void *data_address;
void setup()
{
Serial.begin(1000000);
delay(2000);
Serial.println("Test");
adress_row = 0X8000; //leave 32kB for the firmware
data_address = &ram_buffer;
flash_address = &adress_row;
for (int i = 0; i < 64; i++) { ram_buffer[i] =i; } // fill the buffer
Serial.println("Test1");
flash_erase(&adress_row,256); // erase one row
flash_write(flash_address, ram_buffer, 64); // write the buffer to flash
for (int i = 0; i < 64; i++) { ram_buffer[i] = 0; } // empty ram_buffer
for (int i = 0; i < 64; i++) { Serial.println(ram_buffer[i]); delay(10); }
delay(100);
flash_read(flash_address, ram_buffer, 64); // read flash and put the data in ram_buffer
for (int i = 0; i < 64; i++) { Serial.println(ram_buffer[i]); delay(10); } // display data in ram_buffer
}
void loop()
{
Serial.println("Test");
delay(2000);
}
void flash_write(const volatile void *flash_ptr, const void *data, uint32_t size)
{
// Calculate data boundaries
size = (size + 3) / 4;
volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr;
const uint8_t *src_addr = (uint8_t *)data;
// Disable automatic page write
NVMCTRL->CTRLB.bit.MANW = 1;
// Do writes in pages
while (size) {
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
while (NVMCTRL->INTFLAG.bit.READY == 0) {}
// Fill page buffer
uint32_t i;
for (i = 0; i<(PAGE_SIZE / 4) && size; i++) {
*dst_addr = flash_read_unaligned_uint32(src_addr);
src_addr += 4;
dst_addr++;
size--;
Serial.println(size);
}
// Execute "WP" Write Page
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
while (NVMCTRL->INTFLAG.bit.READY == 0) {}
}
}
static inline uint32_t flash_read_unaligned_uint32(const void *data)
{
union {
uint32_t u32;
uint8_t u8[4];
} res;
const uint8_t *d = (const uint8_t *)data;
res.u8[0] = d[0];
res.u8[1] = d[1];
res.u8[2] = d[2];
res.u8[3] = d[3];
//Serial.println(res.u32);
return res.u32;
}
void flash_erase(const volatile void *flash_ptr, uint32_t size)
{
const uint8_t *ptr = (const uint8_t *)flash_ptr;
Serial.println("Test2");
while (size > ROW_SIZE) {
Serial.println("Test3");
flash_erase(ptr);
ptr += ROW_SIZE;
size -= ROW_SIZE;
}
Serial.println("Test4");
delay(100);
flash_erase(ptr);
}
void flash_erase(const volatile void *flash_ptr)
{
NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2;
Serial.println("Test5");
delay(100);
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
delay(1000); Serial.println("Test6"); delay(1000);
while (!NVMCTRL->INTFLAG.bit.READY) {}
Serial.println("Test7");
}
void flash_read(const volatile void *flash_ptr, void *data, uint32_t size)
{
memcpy(data, (const void *)flash_ptr, size);
}
Any insight would be much appreciated!
Thanks,
Olivier