在MCU的使用中,经常遇到需要存储参数或掉电保持数据等功能。其中,Flash和EEPROM是常见的非易失性存储器,都可以做到设备掉电重启后,数据还会保留。但二者有明显的区别:EEPROM可以被编程和电擦除,而且大多数的EEPROM可以被编程和电擦除,大多数串行EEPROM允许逐字节程序或擦除操作。与EEPROM相比,闪存具有更高的密度,这允许在芯片上实现更大的内存阵列(扇区)。通过对每个单元施加时间控制的电压来执行闪存擦除和写入周期。典型的Flash写时间是50µs/8位字;然而,EEPROM通常需要5到10 ms。EEPROM不需要进行页面(扇区)擦除操作,可以擦除一个需要指定时间的特定字节。与EEPROM相比,Flash具有更高的密度和更低的价格。
先楫产品可以外接大容量Flash芯片,支持可达256Mbyte程序或数据存储;部分产品如HPM6754、HPM6364、HPM6284内置4Mbyte Flash,HPM53XX系列全系支持1Mbyte Flash。在使用Flash模拟EEPROM时,最重要的挑战是满足Flash程序/擦除持久性和数据保留方面的可靠性目标。
其次,在应用程序的控制下,需要满足更新和读取数据的实时应用要求。请注意,在Flash擦写期间,它不能执行Flash应用程序,因为在此时间内不能执行在Flash中的程序,通常程序是将Flash擦写程序拷贝到RAM中执行。先楫半导体为了方便客户程序应用,已经将Flash驱动程序集成到ROM中,减少了系统对RAM的需求,用户使用时更加灵活方便。
由于Flash的块擦除要求,必须完全为模拟的EEPROM保留至少一个Flash扇区。例如,一个4K x 8bit大小的Flash扇区可以分为16页,每个页的大小为256 x 8bit。这使得每个页面相当于一个256 x 8字节的EEPROM。要保存的数据首先写入RAM中的缓冲区中,每个部分RAM可以模拟EEPROM的存储的数据。
如何实现?
根据Flash扇区和模拟的EEPROM的大小,划分相应的Flash和RAM空间。
功能:
• 读取片内或片外flash信息。
• 批量读取flash中数据到RAM缓存中。
• 用户可以自由读写RAM缓存中数据。
• 用户可以将RAM缓存中数据写入flash。
• 用户可以根据自己的需要定制存储空间大小和存储地址。
由于先楫半导体MCU已经集成了Flash驱动,用户可以不再需要把精力放到繁琐的底层Flash驱动部分。
为了实现此功能,需要8个函数来进行编程、读取和擦除,3个宏定义确定存储空间和位置。
/* Sector size */
#define SECTOR_SIZE (uint32_t) (0x1000)
/* Sectors 0 and 1 base and end addresses */
#define Flash_base 0x80000000L
#define SECTOR1_BASE_ADDRESS (Flash_base+0x3FE000)
#define SECTOR1_END_ADDRESS (SECTOR1_BASE_ADDRESS+SECTOR_SIZE*2-1)
其中,SECTOR_SIZE定义了flash扇区大小,单位是byte。若不确定flash扇区大小可以在Initial_EEProm函数中获取flash信息。Flash_base定义flash起始地址,具体可以参考user guider中系统内存映射 System Memory Map地址。SECTOR1_BASE_ADDRESS和SECTOR1_END_ADDRESS为数据存放起始地址,SECTOR1_BASE_ADDRESS必须是特定扇区起始地址。
ATTR_PLACE_AT_WITH_ALIGNMENT(".ahb_sram",8) uint8_t EEPROM_data[SECTOR1_END_ADDRESS-SECTOR1_BASE_ADDRESS+1];
Flash模拟EEPROM时需在RAM中开辟缓存用于常态数据读写,开辟数据时应注意RAM区数据应放到ahb_sram或noncacheable区域。
if defined(FLASH_XIP) && FLASH_XIP
ATTR_RAMFUNC hpm_stat_t Initial_EEProm(void)
#else
hpm_stat_t Initial_EEProm(void)
#endif
{
xpi_nor_config_option_t option;
option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
hpm_stat_t status = rom_xpi_nor_auto_config(HPM_XPI0, &s_xpi_nor_config, &option);
if (status != status_success) {
return status;
}
rom_xpi_nor_get_property(HPM_XPI0, &s_xpi_nor_config, xpi_nor_property_total_size,
&flash_size);
rom_xpi_nor_get_property(HPM_XPI0, &s_xpi_nor_config, xpi_nor_property_sector_size,
§or_size);
rom_xpi_nor_get_property(HPM_XPI0, &s_xpi_nor_config, xpi_nor_property_page_size, &page_size);
printf("Flash Size:%dMBytes\nFlash Sector Size:%dKBytes\nFlash Page Size:%dBytes\n",
flash_size / 1024U / 1024U, sector_size / 1024U, page_size);
EEProm_Flush();
} /* End Initial_EEProm */
通过调用rom_xpi_nor_auto_config()、rom_xpi_nor_get_property()获取flash信息。
/*******************************************************************************
* Routine: EEPromFlush
* Purpose: Refresh data from flash to buffer.
*******************************************************************************/
inline void EEProm_Flush(void)
{
memcpy((void*)EEPROM_data,(const void*)SECTOR1_BASE_ADDRESS,(SECTOR1_END_ADDRESS-SECTOR1_BASE_ADDRESS));
} /* End EEProm_Flush */
从flash中读取数据无需单独调用API函数,直接寻址读取效率更高,文中通过memcpy()函数直接从flash中读取数据到RAM缓存中,后面读写参数直接读写RAM缓存即可。
如果需要将参数写入flash中,需将整块flash擦写,由于数据已经存在RAM缓存,不会存在flash擦写时数据丢失的问题。
/*******************************************************************************
* Routine: writeEEProm_withflush
* Purpose: Writes variable to EEPROM and flush flash later.
* Input : none
* Output: None.
* Return: Returns 0
*******************************************************************************/
#if defined(FLASH_XIP) && FLASH_XIP
ATTR_RAMFUNC hpm_stat_t writeEEProm_withflush(uint16_t index, uint8_t *data, uint16_t size)
#else
hpm_stat_t writeEEProm_withflush(uint16_t index, uint8_t *data, uint16_t size)
#endif
{
hpm_stat_t status;
if(flash_size==0) return status_fail;
memcpy((void*)&EEPROM_data[index],(const void*)data,size);
status = rom_xpi_nor_erase(HPM_XPI0, xpi_xfer_channel_auto, &s_xpi_nor_config,
SECTOR1_BASE_ADDRESS-Flash_base, SECTOR1_END_ADDRESS-SECTOR1_BASE_ADDRESS);
if (status != status_success) {
return status;
}
status = rom_xpi_nor_program(HPM_XPI0, xpi_xfer_channel_auto, &s_xpi_nor_config,
(const uint32_t *)EEPROM_data, SECTOR1_BASE_ADDRESS-Flash_base, SECTOR1_END_ADDRESS-SECTOR1_BASE_ADDRESS);
if (status != status_success) {
return status;
}
}
考虑到flash擦写期间不能读取flash,flash擦写函数需放置在RAM执行的程序存储空间。先楫SDK中已经定义好了ram运行区域,并在HPM_COMMON.H文件中将函数和数字放置属性重新封装,通过ATTR_RAMFUNC等效定义__attribute__((section(“.fast”)))。为确保擦写flash期间不会被中断打断从而调用其他flash中的程序,需在运行中关闭中断。
//disable all interrupt before programming flash
CSR_reg = disable_global_irq(CSR_MSTATUS_MIE_MASK);
disable_global_irq(CSR_MSTATUS_SIE_MASK);
disable_global_irq(CSR_MSTATUS_UIE_MASK);
writeEEProm_withflush(0,(uint8_t*)s_write_buf,0x1000);//update eeprom with flash
//restore interrupt
restore_global_irq(CSR_reg);
小 结
本文首先介绍了基于HPM6000系列芯片如何使用Flash模拟EEPROM存储参数。由于先楫SDK中已经提供了强大的驱动库,用户可以方便地通过Flash存储数据,降低成本和提高使用灵活性。
全部0条评论
快来发表一下你的评论吧 !