[MCU] 【极海APM32F407 Tiny Board】6. 内部Flash模拟eeprom

caizhiwei   2023-6-11 21:38 楼主

      在很多设备中,需要存储几个字节的参数,且需要掉电保存。如果新增一个eeprom或一片Flash,会增加bom成本。对于这种掉电存储的且不需要频繁擦写的参数,可以利用mcu内部的Flash进行模拟eeprom。

  优点: 1.  节约成本,可减少外部芯片的使用;
              2. 读写速率快,MCU内部的Flash读写速率要远远高于EEPROM;
              3. 抗干扰能力强,MCU内部的Flash没有使用I2C、SPI这类通讯总线,不存在这类总线被干扰的问题。

步骤1.  开辟mcu内部三个扇区作为存储空间:

9cbcefc261f35d61b78dd6d8b3ccd8cc.jpeg 这里要注意,三个扇区大小要一致才行。

所以代码中使用了 1~3 这三个扇区:

 +--------------------------------------------------------------+
 |                             Flash                            |
 +-------------------------------+------------------------------+
 |  Code  |      User Para area      |           Code           |
 +--------+--------+--------+--------+--------+-----------------+
 |  16KB  |  16KB  |  16KB  |  16KB  |  64KB  | 128KB  | 128KB  |
 | sector | sector | sector | sector | sector | sector | sector |
 |   0    |    1   |   2    |   3    |    4   |   N    | N + 1  |
 |        |        |        |        |        |        |        |
 +--------+--------+--------+--------+--------+-----------------+

第二步: 写读写接口:

/* flash sector satrt address */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000)   /* 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000)   /* 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000)   /* 16 Kbytes */

/* flash sector size */
#define FLASH_SECTOR_SIZE                ((uint32_t)(1024 * 16))

/* flash emulation eeprom total size. This value must be a multiple of 16KB */
#define FLASH_EE_TOTAL_SIZE              ((uint32_t)(1024 * 16 * 2))

/* flash emulation eeprom sector start address, it's must be sector aligned */
#define FLASH_EE_START_ADDR              ADDR_FLASH_SECTOR_1

/* flash emulation eeprom sector start address */
#define FLASH_EE_END_ADDR                (ADDR_FLASH_SECTOR_1 + FLASH_EE_TOTAL_SIZE)

Write接口内部自带flash 擦除操作哦,争对用户来说,只需要按照eeprom的操作方式读写即可:

/*!
 * [url=home.php?mod=space&uid=159083]@brief[/url] Write the specified length of data from the specified address.
 *            Can be written across sectors.
 *
 * @param     WriteAddr: write address.
 *
 * @param     pData: save the write data.
 *
 * @param     len: write data length.
 *
 * @retval    None.
 *
 * @note      The example must be performed in the sectors 1~3.
 *
 */
void Flash_EE_Write(uint32_t WriteAddr, uint8_t* pData, uint32_t len)
{
    uint32_t NumOfSector = 0, NumOfByte = 0, OfsetAddr = 0;
    uint32_t count = 0, temp = 0;
    
    /* offerset address in the sector */
    OfsetAddr = WriteAddr % FLASH_SECTOR_SIZE;
    
    /* The size of the remaining space inthe sector from WriteAddr */
    count = FLASH_SECTOR_SIZE - OfsetAddr;
    
    /* Calculate how many sectors to write */
    NumOfSector =  len / FLASH_SECTOR_SIZE;
    
    /* Calculate how many bytes are left less than one sector */
    NumOfByte = len % FLASH_SECTOR_SIZE;
    
    /* OfsetAddr = 0, WriteAddr is sector aligned */
    if (OfsetAddr == 0)
    {
        /* len < FLASH_SECTOR_SIZE */
        if (NumOfSector == 0) 
        {
            Flash_EE_WriteOneSector(WriteAddr, pData, len);
        }
        /* len > FLASH_SECTOR_SIZE */
        else 
        {
            /* write NumOfSector sector */
            while (NumOfSector--)
            {
                Flash_EE_WriteOneSector(WriteAddr, pData, FLASH_SECTOR_SIZE);
                WriteAddr +=  FLASH_SECTOR_SIZE;
                pData += FLASH_SECTOR_SIZE;
            }
            
            /* write remaining data */
            Flash_EE_WriteOneSector(WriteAddr, pData, NumOfByte);
        }
    }
    /* OfsetAddr != 0, WriteAddr is not sector aligned */
    else 
    {
        /* len < FLASH_SECTOR_SIZE, the data length is less than one sector */
        if (NumOfSector == 0)
        {
            /* NumOfByte > count,  need to write across the sector */
            if (NumOfByte > count) 
            {
                temp = NumOfByte - count;
                /* fill the current sector */
                Flash_EE_WriteOneSector(WriteAddr, pData, count);
                
                WriteAddr +=  count;
                pData += count;
                /* write remaining data */
                Flash_EE_WriteOneSector(WriteAddr, pData, temp);
            }
            else 
            {
                Flash_EE_WriteOneSector(WriteAddr, pData, len);
            }
        }
        /* len > FLASH_SECTOR_SIZE */
        else 
        {
            len -= count;
            NumOfSector =  len / FLASH_SECTOR_SIZE;
            NumOfByte = len % FLASH_SECTOR_SIZE;
                
            /* write count data */
            Flash_EE_WriteOneSector(WriteAddr, pData, count);
            
            WriteAddr +=  count;
            pData += count;
            
            /* write NumOfSector sector */
            while (NumOfSector--)
            {
                Flash_EE_WriteOneSector(WriteAddr, pData, FLASH_SECTOR_SIZE);
                WriteAddr +=  FLASH_SECTOR_SIZE;
                pData += FLASH_SECTOR_SIZE;
            }
            
            if (NumOfByte != 0)
            {
                Flash_EE_WriteOneSector(WriteAddr, pData, NumOfByte);
            }
        }
    }
}

 

读接口:

static uint8_t Flash_EE_ReadByte(uint32_t Addr)
{
    return (*(__IO uint8_t*)Addr); 
}

然后,用一个测试程序进行测试:

void eeprom_test(void)
{
/* flash emulation eeprom test address */
#define FLASH_EE_TEST_ADDR  ((uint32_t)0x08008000)
 
	/* test write buffer */
	static uint8_t Test_Write_buffer[64];
	/* test read buffer */
	static uint8_t Test_Read_buffer[64];

    /* fill Test_Write_buffer */
    for (int i = 0; i< 64; i++)
    {
        Test_Write_buffer[i] = i;
    }
	
	/* write the specified sector address data */
    Flash_EE_Write(FLASH_EE_TEST_ADDR, Test_Write_buffer, 64);
    
    /* read the specified sector address data */
    Flash_EE_Read(FLASH_EE_TEST_ADDR, Test_Read_buffer, 64);
    
    /* compare the values of two buffers for equality */
    for (int i = 0; i < 64; i++)
    {
        if (Test_Write_buffer[i] != Test_Read_buffer[i])
        {
            printf("Test Error!\r\n");
        }
    }
    printf("Test Successful!\r\n");
	
	/* write the specified sector address data */
    Flash_EE_Write(FLASH_EE_TEST_ADDR + 128, Test_Write_buffer, 64);
    
}

 

测试发现:读出来的和写入的一致:

70b0e166fdbf3fc91c8aef5a41dcfdc.png

我打算在原来写入的地址处偏移128字节处再次写入64字节,看之前写入的是否会被清除:

780ba0cd5174d0c1c2188f6be1cc7d5.png

image.png    

结论:这个模拟eeprom方式很实用,也可以移植到其他mcu上。

源码见附件:
游客,如果您要查看本帖隐藏内容请回复

 

 

 

gitee/casy

回复评论 (9)

沙发沙发
点赞  2023-6-11 21:39

板凳板凳

 

掉电存储为何一定要模拟eeprom,直接用不可以吗?

点赞  2023-6-12 10:39

省钱 省钱。

点赞  2023-6-12 12:36

eePROM的擦写寿命是FLASH的10倍以上,用FLASH模拟出来也只适合做参数配置类的数据保存,不适合做记录数据等存储。所以像ST等大部分芯片,把FLASH的最后几个扇区的工艺改进,延长FLASH的擦写寿命,如果要用FLASH当EEPROM用,建议还是参考芯片手册,使用它为此增强的那部分FLASH空间。

点赞  2023-6-15 08:21

今天回头测试,发现之前的代码有bug:不同的编译器需要分开定义静态数组:

#ifdef  MDK

/* Specifies the start address of the sector. The purpose is to occupy a space at the specified address of MCU flash. */
static const uint8_t Flash_Para_Area[FLASH_EE_TOTAL_SIZE] __attribute__((section(".ARM.__at_0x08004000")));

#else

__root static const uint8_t Flash_Para_Area[FLASH_EE_TOTAL_SIZE]@ FLASH_EE_START_ADDR = {0}; 


#endif

 

gitee/casy
点赞  2023-7-8 15:40

BUG 修复后的工程源码:开源到了gitee上:

https://gitee.com/casy/APM32F407IG_TinyBoard

gitee/casy
点赞  2023-7-8 15:41

1

点赞  2023-11-14 14:53

谢谢,学习了,这款芯片资料相对ST的少得可怜。

 

点赞  2023-12-1 17:27

ddddddd

点赞  2023-12-9 20:01
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复