GD32F303 Debugging Notes (6) On-chip FLASH reading and writing

Preface

When developing with a microcontroller, in order to implement certain functions of the product, we usually need to record some data and place it in an area that will not be lost when the microcontroller is powered off. In today's mainstream 8-bit computers , they are generally EEPROM and FLASH storage areas. These storage areas are generally separate from our code storage areas and are generally not large (most are within 1KB). EEPROM can be read and written in bytes , while FLASH can be erased and written in blocks . In 32-bit microcontrollers , an area is generally not opened for separate data storage, that is, the code and the data are stored in the same area . Therefore, if you have read other information about internal FLASH read and write operations in 32-bit machines, they basically operate from the last block or blocks of FLASH, and the same is true for the examples given in this article.

RAM and ROM

Before talking about internal FLASH reading and writing, let's briefly talk about the RAM and ROM of the microcontroller. RAM is what we call memory. It has the characteristics of fast reading and writing, but data will be lost when power is turned off. ROM is read-only memory, which refers to FLASH here, and has the characteristic of not being lost when power is turned off. The advantage of choosing a FLASH MCU is that it facilitates early, mid-term debugging and post-maintenance. In contrast, there are OTP and MTP MCUs. The biggest difference between them is the limit on the number of times they can be reprogrammed, which also leads to many differences in the entire code debugging.

Programming of each module

Before configuring, please make sure you already have a GD32F303 keil project that includes its corresponding standard library . The project can be created using official routines or according to the project creation and compilation of GD32F303 Debugging Notes (Zero) .

1. On-chip FLASH mapping relationship

Please add image description
Please add image description

  • According to the above description, we know the address range of the entire FLASH area. The entire FALSH is divided into two BANK areas. For FLASH with a capacity of no more than 512K, it is in the BANK0 area, and the size of each page is 2KB. For FLASH capacity greater than 512, except that the first 512KB is outside BANK0, the rest are in BANK1, and the size of each page is 4KB.
  • Since the FLASH area is mostly used to store code, the code you write and burn will occupy a certain number of FLASH pages starting from page 0 of FLASH, that is, from 0x-0800-0000. If you operate in these places, we can imagine what the consequences will be. Therefore, we generally operate from the last or last few FLASH pages based on the size of the chip you have:
/*
 * bank0--2kB一页  bank1--4KB一页
 * 前512K在Bank0中,后面在Bank1中
 * 前256K CPU执行指令零等待
 */

/* 256KB FLASH */ 
//#define FMC_PAGE_SIZE           ((uint16_t)0x800U)		
//#define FMC_WRITE_START_ADDR    ((uint32_t)0x0803F800U)
//#define FMC_WRITE_END_ADDR      ((uint32_t)0x0803FFFFU)	

/* 1MB FLASH */ 
#define FMC_PAGE_SIZE           ((uint16_t)0x1000U)			/* bank1 4kB each page */
#define FMC_WRITE_START_ADDR    ((uint32_t)0x080FF000U)
#define FMC_WRITE_END_ADDR      ((uint32_t)0x080FFFFFU)	

2. FLASH erasure

  • Directly apply the erase function in the official routine. Here I use the FALSH page in BANK1, corresponding to the three flags FMC_FLAG_BANK1_END, FMC_FLAG_BANK1_WPERR, and FMC_FLAG_BANK1_PGERR. For BANK0, just modify these three.
  • This is multi-page erasure. The variable PageNum counts the number of pages. Here I am erasing one page.
/* calculate the number of page to be programmed/erased */
uint32_t PageNum = (FMC_WRITE_END_ADDR - FMC_WRITE_START_ADDR + 1) / FMC_PAGE_SIZE;

void fmc_erase_pages(void)
{
    
    
    uint32_t EraseCounter;

    /* unlock the flash program/erase controller */
    fmc_unlock();

    /* clear all pending flags */
    fmc_flag_clear(FMC_FLAG_BANK1_END);
    fmc_flag_clear(FMC_FLAG_BANK1_WPERR);
    fmc_flag_clear(FMC_FLAG_BANK1_PGERR);
    
    /* erase the flash pages */
    for(EraseCounter = 0; EraseCounter < PageNum; EraseCounter++)
    {
    
    
 		fmc_page_erase(FMC_WRITE_START_ADDR + (FMC_PAGE_SIZE * EraseCounter));
		fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    }

    /* lock the main FMC after the erase operation */
    fmc_lock();
}

2. FLASH writing

  • This also follows the writing function of the official routine. At the bottom level, I used word writing. I originally wanted to encapsulate it into multi-word writing, but I forgot to change it because I was busy. Pay attention to the BANK flag corresponding to the page you are clearing.
void fmc_program(uint32_t * data,uint32_t addressx)
{
    
    
    /* unlock the flash program/erase controller */
    fmc_unlock();
 
	uint32_t address = addressx;
    /* program flash */
    fmc_word_program(address, *data);
    fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    /* lock the main FMC after the program operation */
    fmc_lock();
}
  • You can try changing it to this, writing each word:
void fmc_program(uint32_t data,uint32_t addressx)
{
    
    
    /* unlock the flash program/erase controller */
    fmc_unlock();
    
    /* program flash */
    fmc_word_program(addressx, data);
    fmc_flag_clear(FMC_FLAG_BANK1_END | FMC_FLAG_BANK1_WPERR | FMC_FLAG_BANK1_PGERR);
    /* lock the main FMC after the program operation */
    fmc_lock();
}

3. FLASH reading

  • This is the simplest. Define a pointer type that points to the previously memorized address, and you can obtain the contents. It is the same as the official package register address. We write it like this:
/* 读FLASH */
#define FMC_READ(addrx) ( *(volatile uint32_t*)(uint32_t)(addrx) )
  • Let’s define some more addresses where we want to store data:
/* 定义地址(32位机总线一次访问4个字节地址) */
#define CLIP12V_AD_ADDR ((uint32_t)(FMC_WRITE_START_ADDR + 0x000U))
#define LANGUAGE_ADDR 	((uint32_t)(FMC_WRITE_START_ADDR + 0x004U))
#define BRIGHT_ADDR 	((uint32_t)(FMC_WRITE_START_ADDR + 0x008U))

4. Main function

  • There is actually nothing to show here. Let’s first give the pseudo code written:

  • When a button is pressed or some condition is triggered, the FLASH page is first erased and then the data to be stored is written.

void task_key_event(void)
{
    
    
	if(key_flag == 3)
	{
    
    
		fmc_erase_pages();
		fmc_program(&ADV_Clip12V_AD,CLIP12V_AD_ADDR);			
		fmc_program(&Language_current,LANGUAGE_ADDR);	
	}
}
  • Reading is more casual, just add what you want wherever you need:
	ADV_Clip12V_AD = FMC_READ(CLIP12V_AD_ADDR);
	Language_current = FMC_READ(LANGUAGE_ADDR);
	Bright_Level = FMC_READ(BRIGHT_ADDR);

5. Summary

  • In general, the operation of 32-bit machines is simpler, especially in reading.
  • Another piece of news is that Mr. Guo Tianxiang and his team will publish a set of tutorials on GD32F303 on the entire platform ( the one that taught the 51 microcontroller in 10 days ), and the knowledge covered will be more comprehensive, click here .
  • Next, there are two parts left in my personal article about GD32F303, window watchdog and timer output PWM (part of the function). After that, the content about GD32 will basically come to an end. Thank you for your clicks.

!!!This article was originally published by Huanxi 6666 on CSDN. Please indicate the source when copying or reprinting:)!!!

Guess you like

Origin blog.csdn.net/qq_37554315/article/details/123447334