2019-05-20 04:15 AM
2019-05-20 05:40 AM
Hello,
first of all, you should reserve a flash page (or several pages depending on the total parameter length) for your parameter data to separate these data from any executable code, because the internal erasing/writing cycles of/to the flash take time (10ms and more) whilst the page is unreadable for the CPU: the CPU would crash trying to execute a code from such a page. Please refer to compiler/linker docs how to define and place your parameter data to the desired flash memory region/page(s).
The further point: before writing even a byte of new data to the flash you should completely erase the whole flash page before. Thus, to write/modify a parameter you should buffer all the parameters in RAM, modify the data inside that buffer, erase the whole flash page and write the buffer back to the page.
The main workflow of flash programming consists of copying of the target page to a buffer, modifying the buffer, unlocking the flash page, writing the buffer to it and locking up the page again.
/* -----------------------------------------------------------------------------
*
* Writes to the flash at address 'addr' from the source 'src' of
* length 'len' bytes.
*
* Returns false if the target address would be out of available flash
* or any flash error has occurred.
*
* Implements a multipage algorithm: can write over the page boundaries.
*
* NOTE! - requires C99 option ON to use the dynamical buffer allocation
* of variable size (mostly implemented by compiler by means of
* malloc()/free(), see buf[size] declaration below.
*/
__attribute__((weak))
bool flash_Data(void *src, const void *addr, int len)
{
int ofs, portion, state, size = HAL_FlashDriver.pagesize();
unsigned char
buf[size], res = ((size) && buf != NULL),
*base, *ptr = (unsigned char *)addr;// To convenient type.
while (len && res)
{
base = (unsigned char *)(((int)ptr/size)*size);
ofs = ptr - base; // offset in the current flash page
portion = size - ofs; // portion to modify in the page
if (portion > len)
portion = len; // cut to available data
if (res = (HAL_FlashDriver.flashvar(ptr) &&
HAL_FlashDriver.flashvar(ptr + portion - 1)))
{
memcpy(buf, base, size); // backup the whole flash page
memcpy(buf+ofs, src, portion); // substitute the data portion
BEGIN_CRITICAL_SECTION(state);
if (HAL_FlashDriver.init)
HAL_FlashDriver.init();
if (res = (HAL_FlashDriver.erase(base) &&
HAL_FlashDriver.write(base, buf, size)))
{
src = (unsigned char *)src + portion; // move the source
ptr += portion; // move the destination
len -= portion; // fix the length
}
if (HAL_FlashDriver.deinit)
HAL_FlashDriver.deinit();
END_CRITICAL_SECTION(state);
}
}
return res;
}
I'm using a "driver" approach referring to a structure "HAL_FlashDriver":
/* -----------------------------------------------------------------------------
*
* ATTENTION! Function (*write) to write wordwise,
* where 'len' is a multiply of 4.
*/
typedef struct hal_flash_driver_s {
void (*init)(void);
void (*deinit)(void);
uint32_t(*pagesize)(void);
bool (*flashvar)(const void *base);
bool (*erase)(const void *base);
bool (*write)(const void *base, unsigned char *buf, int len);
} const HAL_Flash_Driver_TypeDef;
This driver structure is implemented as:
//---------------------------------------------------------------------------
//
// Strong for HAL_Fla.c
//
HAL_Flash_Driver_TypeDef
HAL_FlashDriver = {
FLASH_UnlockFlash,
FLASH_Lock,
FLASH_Page_Size,
FLASH_Var,
FLASH_Erase,
FLASH_DoProgram,
};
I'm still using the SPL (Standard Peripheral Library) for F1xx processors. The functions "FLASH_Lock" is directly implemented in SPL, the other are my own functions that refer to the SPL:
//---------------------------------------------------------------------------
static void FLASH_UnlockFlash(void)
{
FLASH_Unlock();
FLASH_ClearFlag (FLASH_FLAG_BSY |
FLASH_FLAG_EOP |
FLASH_FLAG_PGERR |
FLASH_FLAG_WRPRTERR); // Clear All pending flags
}
//---------------------------------------------------------------------------
static uint32_t FLASH_Page_Size(void) {return FLASH_PAGE_SIZE;}
//---------------------------------------------------------------------------
static bool FLASH_Var(const void *base)
{
return (U32)base >= FLASH_BASE &&
(U32)base < (FLASH_BASE + FLASH_SIZE);
}
//---------------------------------------------------------------------------
//
// The call here is always aligned to the page boundary and with cnt >= 4.
// Attention! - cnt is the word counter (bytes * 4).
// Actually the whole page is requested to be written.
//
static bool FLASH_DoProgram(const void *addr, unsigned char *src, int cnt)
{
bool res = (src) && cnt > 0;
if (res)
{
volatile FLASH_Status f_status = FLASH_COMPLETE;
U32 dst_addr = (U32)addr;
for (; cnt >= sizeof(U32) && f_status == FLASH_COMPLETE;
cnt -= sizeof(U32), dst_addr += sizeof(U32), src += sizeof(U32))
{
f_status = FLASH_ProgramWord (dst_addr, *(U32 *) src);
}
if (cnt && f_status == FLASH_COMPLETE) // the rest
{
U32 val = 0;
U8 *p = (U8 *) &val;
while (cnt--)
*p++ = *src++;
f_status = FLASH_ProgramWord(dst_addr, val);
}
res = (f_status == FLASH_COMPLETE);
}
return res;
}
//---------------------------------------------------------------------------
static bool FLASH_Erase(const void *base)
{
return FLASH_ErasePage((U32)base) == FLASH_COMPLETE;
}
Best regards
2019-05-20 06:10 AM
How to define user flash memory.
I only have to save 10 32-bits parameters in a memory.
2019-05-20 06:10 AM
Where should I define it??
2019-05-20 06:40 AM
Normally, you can define your parameters as normal constants:
const uint32_t param1 = DEFAULT_VALUE1;
and use them as usual in the code. The point is, how to place all them to a dedicated flash page. This depends upon you compiler/linker. I've been using KEIL and am not really familiar with other tool chains. AFAIK, other tool chains also support __attribute__((section("section_name"), noinline, used)), so the declaration of a constant would look like:
const uint32_t param1 __attribute__((section("my_params"), noinline, used)) = DEFAULT_VALUE1;
const uint32_t param2 __attribute__((section("my_params"), noinline, used)) = DEFAULT_VALUE2;
and so on.
The second step is to place the section "my_params" at the desired address. Assuming, you have your code size of 40KB, and the total flash memory is 64KB, so you can place the section to the last flash page. I believe, your CPU must have the page size of 1KB, thus place the section at 0xFC00. How? Refer to your linker or try __attribute__((at(absolute_address), noinline, used))
const uint32_t param1 __attribute__((at(0xFC00), noinline, used)) = DEFAULT_VALUE1;
const uint32_t param2 __attribute__((at(0xFC04), noinline, used)) = DEFAULT_VALUE2;
but this approach is not stylish.
2019-05-20 08:26 AM
Here we use a structure, and in the F1 case where the flash sectors are uniform in size, one can simple use those at the end of the memory for a given device, and shrink the size reported to the linker so it doesn't use them.
Then in your application, you can read the content from the flash memory into a RAM copy of the structure, use a checksum or crc so you can determine if the contents are valid, and set default values for a bad/blank device. The the RAM settings get updated you can then erase the flash and write back the new content.
The F1 HAL code trees should have several examples using the flash memory, including writing and erasing, and doing firmware updates, etc. The flash memory can be read using normal functions/actions as it is mapped into the usable space of the micro-controller.
2019-05-20 11:30 PM
Please check my code I am unable to erase and write in a flash memory.
Reading can be done easily. LCD shown Flash: 0xFFFFFFFF
void Flash_Erase(uint32_t flashAdd)
{
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG (FLASH_FLAG_BSY |
FLASH_FLAG_EOP |
FLASH_FLAG_PGERR |
FLASH_FLAG_WRPERR);
FLASH_PageErase(flashAdd);
HAL_FLASH_Lock();
}
void Flash_Write(uint32_t flashAdd, uint32_t flashData)
{
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG (FLASH_FLAG_BSY |
FLASH_FLAG_EOP |
FLASH_FLAG_PGERR |
FLASH_FLAG_WRPERR);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,flashAdd,flashData);
HAL_FLASH_Lock();
}
uint32_t Flash_Read(uint32_t flashAdd)
{
uint32_t flashData;
flashData = *(uint32_t*)flashAdd;
return flashData;
}
///and in main function......
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
Flash_Erase(0x800FC00);
HAL_Delay(1000);
Flash_Write(0x800FC00,16);
HAL_Delay(100);
LiquidCrystal(LCD_PORT, LCD_RS, LCD_RW, LCD_EN, LCD_DB4, LCD_DB5,
LCD_DB6,LCD_DB7);
begin(20,4);
HAL_Delay(100);
uint32_t temp = Flash_Read(0x800FC00);
while(1)
{
snprintf(str,20,"Flash: %x",temp);
setCursor(0,0);
print(str);
HAL_Delay(1);
}
}