{"id":4311,"date":"2019-07-24T20:00:55","date_gmt":"2019-07-24T18:00:55","guid":{"rendered":"https:\/\/msalamon.pl\/?p=4311"},"modified":"2025-12-27T19:27:34","modified_gmt":"2025-12-27T18:27:34","slug":"stm32-has-no-eeprom-but-fortunately-theres-eeprom-emulation-based-on-f1-f4","status":"publish","type":"post","link":"https:\/\/msalamon.pl\/en\/stm32-has-no-eeprom-but-fortunately-theres-eeprom-emulation-based-on-f1-f4\/","title":{"rendered":"STM32 Has No EEPROM, But\u2026 Fortunately, There\u2019s EEPROM Emulation (Based on F1, F4)."},"content":{"rendered":"\n<p>I remember when I wanted to use EEPROM in STM32 for the first time. Everything went great until the moment I actually wanted to use that memory. It turned out that&#8230; not all STM32s have EEPROM! Those lacking this feature are especially the F series. What to do in such a situation? You can use an external I2C\/SPI chip or&#8230; emulate EEPROM in the built-in FLASH memory. Come, I\u2019ll show you how to do it.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h1 class=\"wp-block-heading\">How does EEPROM work?<\/h1>\n\n\n\n<p>I think the first thing is to know what EEPROM is. It is non-volatile memory, meaning its contents are not lost when power is removed. Of course, you can read from it and it has no effect on the data stored in the memory or their lifetime. It is also possible to write, starting from a single byte. An advantage of EEPROM is also that you can quickly write both \u201czeros\u201d and \u201cones,\u201d which is not so obvious in the case of FLASH memory. Access to each byte, both for reading and writing, is random.<\/p>\n\n\n\n<p>In this post I deal with EEPROM emulation in the microcontroller\u2019s internal FLASH memory. How does FLASH memory work? The behavior is very similar to EEPROM with one \u201csmall\u201d difference. Erasing memory (which is most often writing 0xFF into a cell) is not so free. It is performed in larger areas called sectors or pages depending on the documentation. This is the so-called FLASH erase. These sectors can have different sizes, e.g. 1 kB or even 128 kB. Writing to FLASH is actually setting \u201czeros\u201d among a fence of \u201cones.\u201d<\/p>\n\n\n\n<p>Because FLASH memory requires specific erasing, it is not possible to emulate EEPROM behavior 1:1. You cannot allocate a few bytes of FLASH for a few variables because we are not able to \u201cwrite\u201d ones. Example: attempting to overwrite 0xF0 with the value 0xFF in FLASH memory will not succeed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What does EEPROM emulation in FLASH look like?<\/h2>\n\n\n\n<p>Fortunately, ST provides extensive documentation on how EEPROM emulation in FLASH should look. Today I will discuss it using the <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/stm32f103c8t6-dev-board-2\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=jak-zaczac-z-stm32&amp;utm_content=Text\">STM32F103C8T6 known, among others, from BluePill boards<\/a> and the <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-f401re\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=jak-zaczac-z-stm32&amp;utm_content=Text\">STM32F401RE from the Nucleo kit<\/a>.&nbsp;<\/p>\n\n\n\n<p>For these two microcontrollers, ST has prepared appropriate EEPROM emulation documentation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a data-e-disable-page-transition=\"true\" class=\"download-link\" title=\"\" id=\"download-link-1068\" data-redirect=\"false\" href=\"https:\/\/msalamon.pl\/download\/1068\/?tmstv=1766857765\" rel=\"nofollow\"><br>AN2594 &#8211; EEPROM emulation in STM32F10X microcontrollers<\/a><\/li>\n\n\n\n<li><a data-e-disable-page-transition=\"true\" class=\"download-link\" title=\"\" id=\"download-link-1071\" data-redirect=\"false\" href=\"https:\/\/msalamon.pl\/download\/1071\/?tmstv=1766857765\" rel=\"nofollow\"><br>AN3969 &#8211; EEPROM emulation in STM32F40x STM32F41x microcontrollers<\/a><\/li>\n<\/ul>\n\n\n\n<p>The principle is the same for both families. I will briefly discuss how the emulation works.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Data representation<\/h3>\n\n\n\n<p>Data in FLASH is stored in 32-bit cells. These cells are divided into two parts. One is a 16-bit virtual address of the EEPROM variable. The other part is the value of that variable. Why so? Because each variable update writes its new value to the next free FLASH cell. We do not overwrite the old value.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why do we need two memory pages?<\/h3>\n\n\n\n<p>If each new value of a variable is written to a new cell, eventually those cells will run out. When that happens, the old page is designated for data transfer to a new page. We do not transfer all data. We are not interested in historical data of variables, so we scan the full page from the end and transfer the newest values of variables to the new page. After moving to the new page, it is marked as active, and the old one will be erased entirely (erasing the whole FLASH page).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reading from the emulated EEPROM<\/h3>\n\n\n\n<p>To read a variable at the appropriate virtual address, you need to scan the entire page written so far. Each time a variable with the virtual address we require is encountered, its value is remembered. The next encounter of the same variable updates the remembered temporary value. The search continues until we encounter an erased cell, i.e., one with a virtual address of 0xFFFF. Hence the use of this virtual address is forbidden. The last remembered value is returned as the one currently stored in our EEPROM.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Writing to the emulated EEPROM<\/h3>\n\n\n\n<p>Writing searches for the first free cell on the memory page. If it finds one, it writes the given virtual address and the value we provide. If there are no free cells on the page, the procedure of transferring the newest copies of variables with unique virtual addresses begins. Only after that will our variable be written to the first free cell on the new page. The old one will be erased. The pages are used alternately in this way.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Data update flow<\/h3>\n\n\n\n<p>You will find the entire process of writing sample data and transferring it to a new page illustrated in the documentation for F1 (p. 8) and F4 (p. 11).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EEPROM emulation library<\/h2>\n\n\n\n<p>Reading the documentation and devising a library for correct EEPROM emulation behavior can make your head spin a bit. Fortunately, ST has prepared an appropriate library. In addition, it works out of the box with our favorite HAL. The entire library boils down to <strong>only three functions<\/strong>, which makes it incredibly simple to use. You can, however, stretch your brain a bit with its configuration in the eeprom.h file. That\u2019s where the complete configuration of the EEPROM emulator is located. Let me first discuss this configuration. The definitions we are interested in are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>#define PAGE_SIZE<\/strong> \u2013 defines the size of the pages to be used. You can find this information in the microcontroller\u2019s Reference Manual or in the HAL libraries.<\/li>\n\n\n\n<li><strong>#define EEPROM_START_ADDRESS<\/strong> \u2013 from which address in memory the EEPROM emulation will be located. I recommend using the end of FLASH so as not to collide with the main program. This address will be the address of the first FLASH page used.<\/li>\n\n\n\n<li><strong>#define PAGE0_BASE_ADDRESS\u00a0<\/strong><br><strong>#define PAGE0_END_ADDRESS\u00a0<\/strong><br><strong>#define PAGE0_ID\u00a0<\/strong> \u2013 the start and end addresses of the zeroth emulation page and the ID of the first page in FLASH. The same exists for PAGE1 of the EEPROM.<\/li>\n\n\n\n<li><strong>#define PAGE0<\/strong><br><strong>#define PAGE1<\/strong> \u2013 the description in the header is misleading and talks about how many FLASH pages are used for EEPROM pages. In fact, the important definition is PAGE1, which says how many pages away it is relative to the start of PAGE0. If you want to use 2 FLASH pages per one EEPROM page, you will enter the number 0x2 in PAGE1.<\/li>\n\n\n\n<li><strong>#define NB_OF_VAR\u00a0<\/strong> \u2013 the number of unique variables we want to store in the emulated EEPROM. This is important because the library needs a table of virtual addresses and you have to create it earlier in the program.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Library functions<\/h3>\n\n\n\n<p>As I mentioned, the user has 3 simple functions at their disposal. Here they are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>uint16_t EE_Init(void)<\/strong> \u2013 initializes the EEPROM emulator. It checks whether pages were previously written (by the header) and whether they need to be erased. Restores usability after power loss.<\/li>\n\n\n\n<li><strong>uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data)<\/strong> \u2013 Reads a variable from EEPROM. The variable at the virtual address VirtAddress will be placed in the memory pointed to by Data.<\/li>\n\n\n\n<li><strong>uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data)<\/strong> \u2013 Writes to EEPROM. The variable at the virtual address VirtAddress will be written with the value Data.<\/li>\n<\/ul>\n\n\n\n<p>All these functions return status codes compatible with HAL.<\/p>\n\n\n\n<p>Simple to use, right? Time to work with real devices.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EEPROM emulation on STM32F103C8T6<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/produkt\/stm32f103c8t6-dev-board-2\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=emul_eeprom&amp;utm_content=bluepill\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"400\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner.jpg\" alt=\"\" class=\"wp-image-953\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-768x256.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-24x8.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-36x12.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/09\/BluePill_baner-160x53.jpg 160w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>First, I took the popular <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/stm32f103c8t6-dev-board-2\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=jak-zaczac-z-stm32&amp;utm_content=Text\">BluePill<\/a>. Allow me to skip the Cube configuration and the schematic this time. They\u2019re not important in this exercise. I only configured the built-in LED and UART2 to send the contents of the emulated EEPROM to the terminal.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Configuration<\/h3>\n\n\n\n<p>For proper configuration I will need the Reference Manual with the FLASH memory map of the microcontroller.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"262\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-300x262.jpg\" alt=\"\" class=\"wp-image-1061\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-300x262.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-768x670.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-24x21.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-36x31.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap-92x80.jpg 92w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f103c8_memorymap.jpg 858w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>We are interested in Main memory. We have 128 pages of 1 kB each. For tests I can use the last two. In general, I would recommend using the ending pages.&nbsp;<\/p>\n\n\n\n<p>In the eeprom.h header file you can notice that the addresses of each page are already predefined. You could even skip looking at the documentation to configure it correctly, but it\u2019s better to make sure no one made a mistake in the code.<\/p>\n\n\n\n<p>The page size is already defined in the HAL library. You can just make sure it is about 1 kB, i.e., 0x400.<\/p>\n\n\n\n<p>We want to use the last two pages, so I set <strong>EEPROM_START_ADDRESS<\/strong> to the penultimate page, which is <strong>ADDR_FLASH_PAGE_126.<\/strong><\/p>\n\n\n\n<p>Now the addresses of the two EEPROM emulation pages and the number of pages used. You can use the previously predefined addresses or play with offsets.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))\n#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))\n#define PAGE0_ID ADDR_FLASH_PAGE_126\n\n#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x400))\n#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x400 + PAGE_SIZE - 1))\n#define PAGE1_ID ADDR_FLASH_PAGE_127\n\n\/* Used Flash pages for EEPROM emulation *\/\n#define PAGE0 ((uint16_t)0x0000)\n#define PAGE1 ((uint16_t)0x0001)\n<\/pre>\n\n\n\n<p>And the number of stored variables.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#define NB_OF_VAR ((uint8_t)27)<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Program code<\/h3>\n\n\n\n<p>For the library to work, you need to create an array of virtual addresses.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uint16_t VirtAddVarTab[NB_OF_VAR];<\/pre>\n\n\n\n<p>Later I fill this array with consecutive values.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Fill EEPROM variables addresses\nfor(VarIndex = 1; VarIndex &amp;lt;= NB_OF_VAR; VarIndex++)\n{\n  VirtAddVarTab[VarIndex-1] = VarIndex;\n}\n<\/pre>\n\n\n\n<p>It is very important to unlock FLASH from the program level before using the function that initializes the library.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">HAL_FLASH_Unlock();<\/pre>\n\n\n\n<p>Now you can initialize the EEPROM.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">if( EE_Init() != HAL_OK)\n{\n  Error_Handler();\n}<\/pre>\n\n\n\n<p>From now on, you can use the EEPROM. In the program I created an array with sample data that I will write to EEPROM. It contains the string \u201cMateusz Salamon msalamon.pl\u201d. It consists of 27 characters, which I previously took into account in the configuration.<\/p>\n\n\n\n<p>In the program I write and read this data from the EEPROM according to the scheme:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Write the data to EEPROM. Each character at its unique virtual address.<\/li>\n\n\n\n<li>Read and print to the terminal<\/li>\n\n\n\n<li>Write the data to EEPROM in reverse order.<\/li>\n\n\n\n<li>Read and print to the terminal. The text should be reversed.<\/li>\n\n\n\n<li>Write the data to EEPROM.\u00a0<\/li>\n\n\n\n<li>Read and print to the terminal. The effect is the same as in pt. 2<\/li>\n<\/ol>\n\n\n\n<p>You can see the effects from the terminal in the screenshot.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"192\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read-300x192.jpg\" alt=\"\" class=\"wp-image-1063\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read-300x192.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read-24x15.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read-36x23.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read-125x80.jpg 125w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_write_read.jpg 688w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>And what happens to FLASH memory? What do I expect? According to the EEPROM emulation documentation, the text should land under variables with virtual addresses 1\u201327 first in normal order, then reversed, and finally normal again. The result is below.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"164\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-300x164.jpg\" alt=\"\" class=\"wp-image-1062\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-300x164.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-1024x559.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-768x419.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-1536x838.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-36x20.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8-147x80.jpg 147w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f103c8.jpg 1924w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Data marked 1 are from the first write. 2 is the reversed write. 3 is a subsequent write in normal order. Please analyze this data. The first cell at the top left is the page header. It is in the ERASED state, so you can write to this page.<\/p>\n\n\n\n<p>Then there is the data. The first 16 bits (4 digits) are the virtual address of each variable. The second 16 bits are the value, i.e., the characters from the string.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EEPROM emulation on STM32F401RE<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/dev-boardy\/stm32-nucleo\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=emul_eeprom&amp;utm_content=nucleo\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"400\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg\" alt=\"\" class=\"wp-image-1593\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-768x256.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>The organization of memory in microcontrollers can vary. For this reason, you should always check the documentation. In the F4 series we can be slightly surprised because the FLASH memory looks like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"173\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-300x173.jpg\" alt=\"\" class=\"wp-image-1064\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-300x173.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-1024x592.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-768x444.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-24x14.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-36x21.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap-138x80.jpg 138w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_f401re_memorymap.jpg 1137w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>We have only 8 sectors and they are not equal in size. Remember that FLASH erasing is done per <strong>entire sector<\/strong>. It would be sad to use 256 kB of FLASH for a few EEPROM variables. That\u2019s why I will use sectors 2 and 3. Configuration:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* Define the size of the sectors to be used *\/\n#define PAGE_SIZE (uint32_t)0x4000 \/* Page size = 16KByte *\/\n\n\/* Device voltage range supposed to be [2.7V to 3.6V], the operation will\nbe done by word *\/\n#define VOLTAGE_RANGE (uint8_t)VOLTAGE_RANGE_3\n\n\/* EEPROM start address in Flash *\/\n#define EEPROM_START_ADDRESS ((uint32_t)0x08008000) \/* EEPROM emulation start address:\nfrom sector2 : after 16KByte of used\nFlash memory *\/\n\n\/* Pages 0 and 1 base and end addresses *\/\n#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))\n#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))\n#define PAGE0_ID FLASH_SECTOR_2\n\n#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x4000))\n#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (2 * PAGE_SIZE - 1)))\n#define PAGE1_ID FLASH_SECTOR_3\n\n\/* Used Flash pages for EEPROM emulation *\/\n#define PAGE0 ((uint16_t)0x0000)\n#define PAGE1 ((uint16_t)0x0001) \/* Page nb between PAGE0_BASE_ADDRESS &amp;amp; PAGE1_BASE_ADDRESS*\/<\/pre>\n\n\n\n<p>The number of variables I set the same as for F1<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* Variables' number *\/\n\n#define NB_OF_VAR ((uint8_t)27)<\/pre>\n\n\n\n<p>The code in main.c is the same. The result on the terminal is identical to F1, and in FLASH memory it looks similar, except at a different address.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"164\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-300x164.jpg\" alt=\"\" class=\"wp-image-1065\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-300x164.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-1024x559.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-768x419.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-1536x838.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-36x20.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re-147x80.jpg 147w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_flash_content_f401re.jpg 1924w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>I also checked how long it takes to write and read my character array.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"157\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-1024x157.jpg\" alt=\"\" class=\"wp-image-1107\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-1024x157.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-300x46.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-768x118.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-1536x235.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-24x4.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-36x6.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash-160x25.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/07\/eeprom_emulation_timings_erased_flash.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>173 ms for write and 3.24 ms for read. Interestingly, these values do not change significantly with increasing FLASH occupancy. There is one moment when this time will be very large. This is the transfer of data to a new EEPROM page and the erasing of FLASH sectors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">EEPROM emulation before the main program<\/h3>\n\n\n\n<p>As you probably noticed, I defined EEPROM emulation in the middle of the F401RE FLASH. I would be reluctant to use half of the available memory for EEPROM if I defined it on the last two sectors.<\/p>\n\n\n\n<p>For this reason, I came up with the idea to put the EEPROM on the first two sectors and move the main program to sector 2. Unfortunately, performing this operation causes some error in the program\u2019s operation and the microcontroller crashes&#8230; I dug into the code a bit and found a problem in the function that verifies whether the page is erased. It is used in the initialization where the program crashes. There is even a <a href=\"https:\/\/community.st.com\/s\/question\/0D50X0000Ay9Hse\/bug-in-stm32cubef7-eepromemulation-project-function-eeverifypagefullyerased-cant-verify-if-page1-is-erased\" target=\"_blank\" rel=\"noopener\">thread on ST\u2019s forum regarding this bug<\/a>.<\/p>\n\n\n\n<p>Unfortunately, my attempts to fix this snippet did not help. Perhaps the problem is still somewhere else, or it is simply not possible to swap EEPROM with the main program.<\/p>\n\n\n\n<p>The only thing that comes to my mind while writing this article is to split the project into a bootloader and a main program. The bootloader would be run from the starting address and would jump to the main application, which is located after the next two empty sectors. Then the piece of FLASH between the bootloader and the main program could be used for EEPROM emulation \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>Is it worth using EEPROM emulation? Judging by the fact that the code has had easy-to-find bugs for many years, I suspect few people use it. The operation of EEPROM emulation is also not ideal. It takes at least two sectors (pages) of FLASH memory.<\/p>\n\n\n\n<p>As you saw, in F4 series microcontrollers, EEPROM emulation can be very troublesome due to the different sizes of memory sectors.<\/p>\n\n\n\n<p>There is one more issue I did not address. The lifetime of this solution. FLASH memory is not a longevity champion. A single memory cell can withstand <strong>only about 10 thousand<\/strong> write cycles. This is not a large number, especially compared to the lifetime of EEPROM at around <strong>one million cycles<\/strong> (AT24C32 memories from Atmel).<\/p>\n\n\n\n<p>So the question arises \u2014 is it worth emulating EEPROM? Sometimes yes, sometimes no \ud83d\ude42 You need to answer this individually. It depends on the project. You can use an STM32 that already has EEPROM in its structure. You can use an external chip. There are at least several solutions.&nbsp;<\/p>\n\n\n\n<p>However, it is worth knowing that in extreme situations you can use FLASH memory as an emulated EEPROM. It works correctly, so for rarely changed data it can be quite a good way.<\/p>\n\n\n\n<p>You can find the test codes along with libraries on my GitHub: <a href=\"https:\/\/github.com\/lamik\/EEPROM-emulation-STM32F1-HAL\" target=\"_blank\" rel=\"noopener\">F103C8<\/a>, <a href=\"https:\/\/github.com\/lamik\/EEPROM-emulation-STM32F4-HAL\" target=\"_blank\" rel=\"noopener\">F401RE<\/a><\/p>\n\n\n\n<p>Do you think I made a mistake somewhere? Do you have an interesting idea for what could be improved? Share it in the comments! Remember that the discussion should be polite and in accordance with the rules of the Polish language.<\/p>\n\n\n\n<p><a href=\"http:\/\/kursstm32.pl\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=emul_eeprom&amp;utm_content=Text\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy.png\" alt=\"kurs stm32\" width=\"820\" height=\"312\" class=\"alignnone wp-image-1022 size-full\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy.png 820w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy-300x114.png 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy-768x292.png 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy-24x9.png 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy-36x14.png 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/06\/Kurs-STM32-zapisy-160x61.png 160w\" sizes=\"auto, (max-width: 820px) 100vw, 820px\"><\/a> \u2014 content is in Polish.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Contest results<\/h3>\n\n\n\n<p>Recently I announced a small contest. From the entries I chose three winners. They are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Leoneq ;3\u00a0<\/strong> with the comment\u00a0<br><em>\u201cHmm, I would most like to see an article about generating a VGA image. There is some library on Arduino, but 120\u00d760 pixels is poor, not to mention two timers occupied. On an STM, generating an image shouldn\u2019t be so demanding, and it would give looooots of possibilities for new projects <figure><img decoding=\"async\" draggable=\"false\" class=\"emoji\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/1f609.svg\" alt=\"?\"><\/figure>\u00a0Btw, I\u2019ve been following your blog for a long time, and I have to say that it maintains a high level. Nicely written, to the point and interesting. Regards\u00a0<figure><img decoding=\"async\" draggable=\"false\" class=\"emoji\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/1f609.svg\" alt=\"?\"><\/figure>\u201d<\/em><\/li>\n\n\n\n<li><strong>Mruczek\u00a0<\/strong> with the comment<br><em>\u201cCommunication! Ethernet \u2013 the one on the big Nucleo boards, using ESP8266 as WiFi, NRF24L01+, which is great for low-power applications with wireless communication.\u201d<\/em><\/li>\n\n\n\n<li><strong>Mateusz<\/strong><strong>\u00a0<\/strong>with the comment<br><em>\u201cHello\u00a0<figure><img decoding=\"async\" draggable=\"false\" class=\"emoji\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/1f642.svg\" alt=\"?\"><\/figure>\u00a0I am delighted that the blog already has topics that interested me, including displays and the accelerometer. But I would be more interested in using both the FPU and the ADC options in \u2018more advanced\u2019 signal processing. The topic could also include simple processing procedures such as FFT to change from the time domain to the frequency domain, as well as simple\/more complex signal conditioning systems into ones friendly for the processor, measurement relative to a virtual ground, etc. I think that despite the omnipresent digitization, such a topic could be useful\u00a0<figure><img decoding=\"async\" draggable=\"false\" class=\"emoji\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/12.0.0-1\/svg\/1f642.svg\" alt=\"?\"><\/figure>\u201d<\/em><\/li>\n<\/ol>\n\n\n\n<p>Congratulations to the winners! Contact me by email: mateusz@msalamon.pl to finalize the delivery of the prizes \ud83d\ude42<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;4311&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;0&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 - (0 votes)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;title&quot;:&quot;STM32 Has No EEPROM, But\u2026 Fortunately, There\u2019s EEPROM Emulation (Based on F1, F4).&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>I remember when I wanted to use EEPROM in STM32 for the first time. Everything went great until the moment I actually wanted to use that memory. It turned out that&#8230; not all STM32s have EEPROM! Those lacking this feature are especially the F series. What to do in such [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3242,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[160],"tags":[175,176,174,177],"class_list":["post-4311","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stm32","tag-electronics","tag-programming","tag-stm32","tag-stm32cubemx"],"_links":{"self":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4311","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/comments?post=4311"}],"version-history":[{"count":3,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4311\/revisions"}],"predecessor-version":[{"id":4417,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4311\/revisions\/4417"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media\/3242"}],"wp:attachment":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media?parent=4311"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/categories?post=4311"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/tags?post=4311"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}