几周前的项目到了, 现在不记录补上。。。。。
1、项目需求 由于我们使用的rt1050 程序时在外部flash 中跑的, 现在又想保存一些数据, 坑的1050 没有内部eeprom 。在者我们保存的数据量比较大, 所有想办法同时去操作外部flash 保存数据。
网络摘抄原文参考——http://blog.chinaaet.com/jihceng0622/p/5100053391 (感谢网络大神)
我们的代码以XIP的方式在QSPI Flash上执行的同时还要能擦写自己,首先想到的就是冲突,QSPI Flash只接了一块(RT1050可以接两块独立的QSPI Flash,这种情况不提)而且QSPI Flash又不是双端口的,这样擦写QSPI flash相关的函数就务必要保证运行在SRAM里才行,是不能边执行边擦写自己的,这逻辑上肯定是不对的,所以需要把Flash操作相关操作的API分配到了SRAM里执行(代码基于Keil工程,为了尽量兼容且减少改动工程配置,我直接把相关函数分配到了SRAM的NoCacheable数据段,实际上分配到任意一个SRAM区都行,只是需要手动在.scf文件里RAM区新添加个地址段),然后将需要分配到SRAM区的Flash相关操作函数通过添加__attribute__((section("NonCacheable")))编译属性即可实现,如下图所示,我将flash擦除的操作放到RAM里了;
2、总体的理解大概就是把操作flsh 的代码放在内部RAM 中,当需要操作外部flash 作为保存数据的时候,程序跳进内部ram 执行,此时spi总线就会空闲出来,这时候再去操作flash 应该就没问题了。。
例如擦除函数
/*********************************************************************
*
* 函数名: qspi_erase_sector()
* 功 能: 擦出指定扇区, 4k大小
* 参 数: FLEXSPI_Type *base spi 参数 EXAMPLE_FLEXSPI
* 参 数:uint8_t address : 擦出扇区的起始地址, : EXAMPLE_SECTORn(x)
* 注 意:
擦出的参数为扇区锁
* 返 回:
*
**********************************************************************/
uint8_t __attribute__((section("NonCacheable"))) qspi_erase_sector(FLEXSPI_Type *base, uint32_t address)
{
status_t status;
taskENTER_CRITICAL(); //使能临界区
SCB_InvalidateDCache();
status = flexspi_nor_flash_erase_sector(base, address);
taskEXIT_CRITICAL(); //退出临界区
if (status != kStatus_Success)
{
Uart1_Send("Erase sector failure !\r\n");
return 0;
}
// Uart1_Send("Erase sector success !\r\n");
return 1;
}
/*********************************************************************
*
* 函数名: qspi_page_program()
* 功 能: 写入数据到flash 的一页中
* 参 数: FLEXSPI_Type *base spi 参数 EXAMPLE_FLEXSPI
* 参 数:uint8_t dstAddr : 写入数据起始地址
* 参 数:uint8_t *src : 写入数据缓冲区
* 参 数:size_t length : 写入数据的长度
* 注 意:
写入的数据的起始地址和 数据大小不能超过这一页,
* 返 回:
*
**********************************************************************/
uint8_t __attribute__((section("NonCacheable"))) qspi_page_program(FLEXSPI_Type *base, uint32_t dstAddr, uint32_t *src,size_t length)
{
status_t status;
taskENTER_CRITICAL(); //使能临界区
SCB_InvalidateDCache();
status = flexspi_nor_flash_page_program(base, dstAddr, (void *)src,length);
taskEXIT_CRITICAL(); //退出临界区
if (status != kStatus_Success)
{
Uart1_Send("Page program failure !\r\n");
return 0;
}
// Uart1_Send("Page program success !\r\n");
return 1;
}
/*********************************************************************
*
* 函数名: Qspi_Read_Sector()
* 功 能: 从特定flah 地址读取指定数据长度
* 参 数: FLEXSPI_Type *base spi 参数 EXAMPLE_FLEXSPI
* 参 数:uint8_t address : 读取数据起始地址
* 参 数:uint8_t *read_buf: 读取数据缓冲区
* 参 数:size_t leng : 读取数据的长度
* 注 意:
由于rt1050 是再外部flash 中启动的, 所以在启动的时候会将flash 的0 地址映射0x60000000 地址上,
所以读取的时候可以直接 更具映射地址memcpy
* 返 回:
*
**********************************************************************/
uint8_t Qspi_Read_Sector(FLEXSPI_Type *base, uint32_t address,uint8_t *read_buf,size_t leng)
{
// status_t status;
//
//
// taskENTER_CRITICAL(); //使能临界区
//
// status =
// flexspi_nor_flash_read_sector(base, address, (void *)read_buf, leng);
// taskEXIT_CRITICAL(); //退出临界区
//
//
// if (status != kStatus_Success)
// {
// Uart1_Send("Page read failure !\r\n");
// return 0;
// }
//
// Uart1_Send("Page read success !\r\n");
// return 1;
//
memcpy(read_buf, (void *)(EXAMPLE_FLEXSPI_AMBA_BASE + (address)),leng);
return 1;
}
/*********************************************************************
*
* 函数名: Qspi_Write_NoCheck()
* 功 能: 向flash 中写入输入
* 参 数: FLEXSPI_Type *base spi 参数 EXAMPLE_FLEXSPI
* 参 数:uint8_t* pBuffer : 写入的数据缓冲区
* 参 数:uint32_t WriteAddr: 写入数据的地址
* 参 数:uint16_t NumByteToWrite: 写入数据的长度
* 注 意:
该函数使用时尽量不要跨越 扇区
当写入的数据长度 NumByteToWrite 大于256 的时候需要注意一下
* 返 回:
*
**********************************************************************/
uint8_t Qspi_Write_NoCheck(FLEXSPI_Type *base,uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=FLASH_PAGE_SIZE-WriteAddr%FLASH_PAGE_SIZE; //单页剩余的字节数
if(NumByteToWrite<=pageremain)
{
pageremain=NumByteToWrite;//不大于256个字节
}
while(1)
{
qspi_page_program(base, WriteAddr, (void *)pBuffer,pageremain);
if(NumByteToWrite==pageremain)break;//写入结束了
else //NumByteToWrite>pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; //减去已经写入了的字节数
if(NumByteToWrite>FLASH_PAGE_SIZE)pageremain=FLASH_PAGE_SIZE; //一次可以写入256个字节
else pageremain=NumByteToWrite; //不够256个字节了
}
}
return 1;
}
3、工程测试跑了 freertos , 测试发现在操作flash 的时候,操作系统会炸掉。。。。。。
后来怀疑擦除读写 flash 操作时间影响 操作系统的调度 ,操作系统的时间片好像时在 10ms 左右,
所以需要添加临界区,在flash 的时候,告诉操作系统禁止任务调度,
添加如下代码即可。
taskENTER_CRITICAL(); //使能临界区
taskEXIT_CRITICAL(); //退出临界区