Skip to content

Instantly share code, notes, and snippets.

@rikka0w0
Last active August 3, 2020 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rikka0w0/d22cf57ff3c96b114574a6ea4f84de73 to your computer and use it in GitHub Desktop.
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
#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