diff --git a/platform/drivers/NVM/W25Qx.c b/platform/drivers/NVM/W25Qx.c index 9466e40f..61f68d65 100644 --- a/platform/drivers/NVM/W25Qx.c +++ b/platform/drivers/NVM/W25Qx.c @@ -212,3 +212,56 @@ ssize_t W25Qx_writePage(uint32_t addr, void* buf, size_t len) return -1; } +bool W25Qx_writeData(uint32_t addr, void* buf, size_t len) +{ + // Fail if we are trying to write more than 4K bytes + if(len > 4096) + return false; + // Fail if we are trying to write across 4K blocks, + // this would erase two 4K blocks for one write, which is not good for flash life + // We calculate block address using integer division of start and end address + uint32_t startBlockAddr = addr / 4096 * 4096; + uint32_t endBlockAddr = (addr + len - 1) / 4096 * 4096; + if(endBlockAddr != startBlockAddr) + return false; + + // Read data from memory to check if it's already correct + // Allocate buffer for storing data read from memory + uint8_t *flashData; + flashData = (uint8_t *) malloc(len); + W25Qx_readData(addr, flashData, len); + // If data in flash corresponds to the passed data, do not perform the write + if(memcmp(buf, flashData, len) == 0) + { + // Free the buffer buffer + free(flashData); + return true; + } + // Free the flash data buffer + free(flashData); + + // Perform the actual read-erase-write of flash data + // Allocate 4096 bytes block for storing flash block to be erased + uint8_t *flashBlock; + flashBlock = (uint8_t *) malloc(4096); + // Read the 4K block from flash + W25Qx_readData(startBlockAddr, flashBlock, 4096); + uint32_t blockOffset = addr % 4096; + // Overwrite changed portion + memcpy(flashBlock + blockOffset, buf, len); + // Erase the 4K block + if(!W25Qx_eraseSector(startBlockAddr)) + { + // The erase operation failed, return failure + free(flashBlock); + return false; + } + // Write back the modified 4K block in 256 bytes chunks + for(uint32_t offset = 0; offset < 4096; offset += 256) + { + W25Qx_writePage(startBlockAddr + offset, flashBlock[offset], 256); + } + // Free the 4K buffer + free(flashBlock); + return true; +} diff --git a/platform/drivers/NVM/W25Qx.h b/platform/drivers/NVM/W25Qx.h index f19cd1a8..21e4df01 100644 --- a/platform/drivers/NVM/W25Qx.h +++ b/platform/drivers/NVM/W25Qx.h @@ -98,4 +98,19 @@ bool W25Qx_eraseSector(uint32_t addr); */ ssize_t W25Qx_writePage(uint32_t addr, void *buf, size_t len); +/** + * Write data to flash memory. + * Copies the 4K block to a memory buffer + * Overwrites the specified part + * Writes back the 4K block at chunks of 256Bytes. + * The write is not performed if the destination content matches the source + * Maximum write size = 4096 bytes. + * This function fails if you are trying to write across 4K blocks + * + * @param addr: start address for read operation. + * @param buf: pointer to a buffer where data is written to. + * @param len: number of bytes to read. + */ +bool W25Qx_writeData(uint32_t addr, void *buf, size_t len); + #endif /* W25Qx_H */ diff --git a/tests/platform/overwriteExtFlash_MDx.c b/tests/platform/overwriteExtFlash_MDx.c new file mode 100644 index 00000000..f2ffc4dc --- /dev/null +++ b/tests/platform/overwriteExtFlash_MDx.c @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN, * + * Frederik Saraci IU2NRO, * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include "W25Qx.h" + +uint8_t block[256] = {0}; + +void printChunk(void *chunk) +{ + uint8_t *ptr = ((uint8_t *) chunk); + for(size_t i = 0; i < 16; i++) printf("%02x ", ptr[i]); + for(size_t i = 0; i < 16; i++) + { + if((ptr[i] > 0x22) && (ptr[i] < 0x7f)) printf("%c", ptr[i]); + else printf("."); + } + printf("\r\n"); +} + +int main() +{ + W25Qx_init(); + W25Qx_wakeup(); + + while(1) + { + getchar(); + + // On UV380 flash at 0x5F60 there are 36032 bytes of 0xFF + uint32_t addr = 0x5F60; + printf("Read memory @ 0x5F60\r\n"); + W25Qx_readData(addr, block, 16); + printChunk(block); + + block[3] = 0xAA; + printf("Write memory @ 0x5F60... "); + bool success = W25Qx_writeData(addr, block, 16); + printf("%s\r\n", success ? "success" : "failed"); + + printf("Read memory @ 0x5F60\r\n"); + W25Qx_readData(addr, block, 16); + printChunk(block); + } + + return 0; +}