Last active
August 3, 2020 17:22
-
-
Save rikka0w0/d22cf57ff3c96b114574a6ea4f84de73 to your computer and use it in GitHub Desktop.
This snippet demonstrates how to change the initail value of a global variable in STM32
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define FLASH_START_ADDR 0x08000000 | |
#define FLASH_PAGE_SIZE 0x400 // stm32f0xx_hal_flash_ex.h may have defined this. | |
// Warning: Other stm32 device might have difference page size!!! | |
#define FLASH_PAGE_BUFCNT (FLASH_PAGE_SIZE / sizeof(uint32_t)) // Number of 32bit ints within the buffer | |
static uint32_t flash_page_buff[FLASH_PAGE_BUFCNT]; | |
// config_addr is a pointer to a 32bit GLOBAL variable, val is the new initial value. | |
// This function does not change the current value of the variable. | |
// The initial value will be loaded to the target variable upon next reset. | |
void vWriteConfig32(uint32_t* config_addr, uint32_t val) { | |
// Declared in LinkerScript.ld, see also startup_stm32.s | |
// The .data section contain all global variables, locate in the SRAM | |
extern uint32_t _sdata; // the first element of .data section | |
extern uint32_t _edata; // the element immediately after .data section | |
// The initial data for all global variables | |
extern uint32_t _sidata; // the first initial data | |
// Find the offset of the config variable within the .data section | |
if (&_sdata > config_addr || config_addr >= &_edata) { | |
return; // config_addr does not point to a global variable (or the .data section) | |
} | |
uint32_t config_initval_offset = ((uint32_t)config_addr) - ((uint32_t)&_sdata); | |
uint32_t config_initval_addr = ((uint32_t)&_sidata) + config_initval_offset; | |
uint32_t pageId = (config_initval_addr - FLASH_START_ADDR) / FLASH_PAGE_SIZE; | |
uint32_t flash_page_addr = FLASH_START_ADDR + FLASH_PAGE_SIZE * pageId; | |
uint32_t config_page_offset = config_initval_addr - flash_page_addr; | |
// Read the entire page to memory | |
for (uint32_t i=0; i<FLASH_PAGE_BUFCNT; i++) { | |
flash_page_buff[i] = ((uint32_t*) flash_page_addr)[i]; | |
} | |
// Modify the memory | |
flash_page_buff[config_page_offset/sizeof(uint32_t)] = val; | |
// Make sure that the target flash page is not read | |
// TODO: Should we put the write action in to a RAM function? | |
__disable_irq(); // Disable the all interrupts | |
// Write back to flash | |
while (FLASH->SR & FLASH_SR_BSY); // Wait until flash is not in use | |
// Unlock | |
FLASH->KEYR = 0x45670123; | |
FLASH->KEYR = 0xCDEF89AB; | |
// Erase page | |
FLASH->CR |= FLASH_CR_PER; // (1) Set the PER bit in the FLASH_CR register to enable page erasing | |
FLASH->AR = flash_page_addr; // (2) Program the FLASH_AR register to select a page to erase | |
FLASH->CR |= FLASH_CR_STRT; // (3) Set the STRT bit in the FLASH_CR register to start the erasing | |
while ((FLASH->SR & FLASH_SR_BSY) != 0); // (4) Wait until the BSY bit is reset in the FLASH_SR register | |
if (FLASH->SR & FLASH_SR_EOP) { // (5) Check the EOP flag in the FLASH_SR register | |
FLASH->SR = FLASH_SR_EOP; // (6) Clear EOP flag by software by writing EOP at 1 | |
} else { | |
// Error | |
} | |
FLASH->CR &= ~FLASH_CR_PER; // (7) Reset the PER Bit to disable the page erase | |
// Program page | |
while (FLASH->SR & FLASH_SR_BSY); // Wait until flash is not in use | |
FLASH->CR |= FLASH_CR_PG; | |
for (uint32_t i=0; i<FLASH_PAGE_BUFCNT; i++) { | |
uint32_t val_dword = flash_page_buff[i]; | |
uint32_t* dest_addr = ((uint32_t*) flash_page_addr) + i; | |
__IO uint16_t* lower_word_addr = ((__IO uint16_t*) dest_addr); | |
// Lower word | |
lower_word_addr[0] = val_dword & 0xFFFF; | |
while (FLASH->SR & FLASH_SR_BSY); // Wait until flash is not in use | |
// Higher word | |
lower_word_addr[1] = (val_dword >> 16) & 0xFFFF; | |
while (FLASH->SR & FLASH_SR_BSY); // Wait until flash is not in use | |
} | |
if (FLASH->SR & FLASH_SR_EOP) { | |
FLASH->SR = FLASH_SR_EOP; | |
} else { | |
// Error | |
} | |
FLASH->CR &= ~FLASH_CR_PG; | |
// Flash RW operation done, re-lock the flash | |
FLASH->CR = FLASH_CR_LOCK; | |
__enable_irq(); // Re-enable the interrupts | |
// Verification | |
if (*((uint32_t*)config_initval_addr) != val) { | |
// Error | |
while(1); | |
} | |
return; // Success | |
} | |
uint32_t exampleVar = 0xDEADBEEF; | |
void main(void) { | |
printf("0x%x\n", exampleVar); | |
if (exampleVar == 0xDEADBEEF) { | |
vWriteConfig32(&exampleVar, 0x12345678); | |
// This does not change the current value of exampleVar. | |
// But exampleVar will be loaded with 0x12345678 after an reset. | |
} | |
printf("0x%x\n", exampleVar); | |
} | |
// Tested on stm32f030f4 (16kB flash, 4kB ram) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment