it work probably perfect for me.
pc.h used for only debug.
it look be helpful for understand it. so i doesn't erase it.
flash.h
#ifndef __FLASH_H
#define __FLASH_H
#define FLASH_NOK 0
#define FLASH_OK 1
#define FLASH_USED 0x1111
#define FLASH_UNUSED 0XFFFF
#define FLASH_MAX_CYCLES 10000
#define FLASH_COUNT_SIZE sizeof(uint16_t)
#define FLASH_STATE_SIZE sizeof(uint16_t)
#define FLASH_PAGE_COUNT(pageAddr) *((uint16_t *)pageAddr)
#define FLASH_BLOCK_STATE(blockAddr) *((uint16_t *)blockAddr)
typedef struct _FLASH_HANDLER_{
uint32_t pageAddr;
uint32_t blockAddr;
uint16_t blockSize;
}_FLASH_HANDLER;
uint8_t FLASH_Init(_PC_HANDLER *pchandler);
uint8_t FLASH_Read(uint32_t pageAddr, void *data, uint16_t dataSize);
uint8_t FLASH_Write(uint32_t pageAddr, void *data, uint16_t dataSize);
#endif
flash.c
#include "flash.h"
#include "pc.h"
#include <string.h>
#include <stdio.h>
_PC_HANDLER *hpce;
static uint8_t FLASH_FindUseablePage(_FLASH_HANDLER *hflash);
static uint8_t FLASH_FindRecentBlock(_FLASH_HANDLER *hflash);
static uint8_t FLASH_ErasePage(_FLASH_HANDLER *hflash);
static uint8_t FLASH_InitPage(_FLASH_HANDLER *hflash);
uint8_t FLASH_Init(_PC_HANDLER *pchandler)
{
hpce = pchandler;
return FLASH_OK;
}
uint8_t FLASH_Read(uint32_t pageAddr, void *data, uint16_t dataSize)
{
uint8_t msg[USB_BUFFER_SIZE];
_FLASH_HANDLER hflash;
hflash.pageAddr = pageAddr;
hflash.blockSize = dataSize + FLASH_STATE_SIZE;
FLASH_FindUseablePage(&hflash);
FLASH_FindRecentBlock(&hflash);
snprintf((char *)msg, sizeof(msg), "reading.. (%x)\r\n", hflash.blockAddr);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
memcpy(data, (void *)(hflash.blockAddr + FLASH_STATE_SIZE), dataSize);
return FLASH_OK;
}
uint8_t FLASH_Write(uint32_t pageAddr, void *data, uint16_t dataSize)
{
uint8_t msg[USB_BUFFER_SIZE];
uint8_t res = FLASH_OK;
uint16_t i;
_FLASH_HANDLER hflash;
hflash.pageAddr = pageAddr;
hflash.blockSize = dataSize + FLASH_STATE_SIZE;
// add padding for halfword flash write
hflash.blockSize += hflash.blockSize % 2;
// make block
uint16_t block[hflash.blockSize];
block[0] = FLASH_USED;
memcpy(&block[1], data, dataSize);
FLASH_FindUseablePage(&hflash);
FLASH_FindRecentBlock(&hflash);
// if block used, use next block
if(FLASH_BLOCK_STATE(hflash.blockAddr) == FLASH_USED) hflash.blockAddr += hflash.blockSize;
// page dosent have enough space for write
if(hflash.blockAddr + hflash.blockSize > hflash.pageAddr + FLASH_PAGE_SIZE)
{
snprintf((char *)msg, sizeof(msg), "page full %x\r\n", hflash.pageAddr);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
FLASH_ErasePage(&hflash);
FLASH_FindUseablePage(&hflash);
FLASH_FindRecentBlock(&hflash);
}
snprintf((char *)msg, sizeof(msg), "writing.. (%x)\r\n", hflash.blockAddr);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
HAL_FLASH_Unlock();
for(i = 0; i < (hflash.blockSize / 2); i++)
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash.blockAddr + (i * 2), block[i]) != HAL_OK)
{
res = FLASH_NOK;
}
}
HAL_FLASH_Lock();
return res;
}
static uint8_t FLASH_FindUseablePage(_FLASH_HANDLER *hflash)
{
uint8_t msg[USB_BUFFER_SIZE];
uint16_t count;
while((count = FLASH_PAGE_COUNT(hflash->pageAddr)) > FLASH_MAX_CYCLES)
{
if(count == FLASH_UNUSED) FLASH_InitPage(hflash);
else hflash->pageAddr += FLASH_PAGE_SIZE;
}
snprintf((char *)msg, sizeof(msg), "found useable page! (addr %x, counter %d)\r\n", hflash->pageAddr, count);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
return FLASH_OK;
}
static uint8_t FLASH_FindRecentBlock(_FLASH_HANDLER *hflash)
{
uint16_t state;
uint8_t msg[USB_BUFFER_SIZE];
// page structure : [counter] [block1] [block2] ... [blockN]
hflash->blockAddr = hflash->pageAddr + FLASH_COUNT_SIZE;
// block structure : [state] [data]
while((state = FLASH_BLOCK_STATE(hflash->blockAddr)) == FLASH_USED)
{
hflash->blockAddr += hflash->blockSize;
// page dosent have enough space for write
if(hflash->blockAddr + hflash->blockSize > hflash->pageAddr + FLASH_PAGE_SIZE)
{
break;
}
}
if(hflash->blockAddr > hflash->pageAddr + FLASH_COUNT_SIZE)
{
hflash->blockAddr -= hflash->blockSize;
}
state = FLASH_BLOCK_STATE(hflash->blockAddr);
snprintf((char *)msg, sizeof(msg), "found recent block! (addr %x state %x)\r\n", hflash->blockAddr, state);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
return FLASH_OK;
}
static uint8_t FLASH_InitPage(_FLASH_HANDLER *hflash)
{
uint8_t msg[USB_BUFFER_SIZE];
uint8_t res = FLASH_NOK;
if(FLASH_PAGE_COUNT(hflash->pageAddr) == FLASH_UNUSED)
{
res = FLASH_OK;
HAL_FLASH_Unlock();
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash->pageAddr , 0x0001) != HAL_OK)
{
res = FLASH_NOK;
}
HAL_FLASH_Lock();
}
snprintf((char *)msg, sizeof(msg), "page init done! (addr %x res %d)\r\n", hflash->pageAddr, res);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
return res;
}
static uint8_t FLASH_ErasePage(_FLASH_HANDLER *hflash)
{
uint8_t msg[USB_BUFFER_SIZE];
uint8_t res = FLASH_OK;
uint32_t PAGEerror = 0;
uint16_t count;
FLASH_EraseInitTypeDef eraseInitStruct;
eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
eraseInitStruct.PageAddress = hflash->pageAddr;
eraseInitStruct.NbPages = 1;
// add count when erase page
count = FLASH_PAGE_COUNT(hflash->pageAddr) + 1;
HAL_FLASH_Unlock();
if(HAL_FLASHEx_Erase(&eraseInitStruct, &PAGEerror) != HAL_OK) res = FLASH_NOK;
else
{
if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash->pageAddr , count) != HAL_OK)
{
res = FLASH_NOK;
}
}
HAL_FLASH_Lock();
snprintf((char *)msg, sizeof(msg), "page erasing done! (addr %x count %x)\r\n", hflash->pageAddr, count);
PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
return res;
}