几周前的项目到了, 现在不记录补上。。。。。

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();       //退出临界区

 

01-19 19:33