当前位置: 首页 > article >正文

STM32完全学习——FLASH上FATFS文件管理系统

一、需要移植的接口

我们通过看官网的手册,可以看到我们只要完成下面函数的实现,就可以完成移植。我们这里只移植前5个函数,获取时间的函数我们不在这里移植。

二、移植接口函数

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat;
	switch (pdrv) {
		case DEV_FALSH :
			stat = EN25QXX_ReadSR();

			return stat;

		return stat;
	}
	return STA_NOINIT;
}
//获取存储器状态的函数
uint8_t EN25QXX_ReadSR(void)   
{  
	uint8_t byte = 0;   
	FLASH_CS = 0;                            //使能器件   
	SPI1_ReadWriteByte(EN25X_ReadStatusReg);    //发送读取状态寄存器命令    
	byte = SPI1_ReadWriteByte(0Xff);             //读取一个字节  
	FLASH_CS = 1;                            //取消片选     
	return byte;   
} 
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = 0;;
	if (pdrv == DEV_FALSH)  //由于我只里关于FLASH的初始化,在main函数里面已经完成了,这里直接返回就行
	{
		return stat;
	}

	return STA_NOINIT;
}

 需要注意的是读和写的函数里面的LBA_t sector这个程序内部代表的是扇区号,而不是扇区地址,但是我们的FLASH写和读函数里面传的参数是地址,因此一定要先将扇区号转化成对应的地址然后才可以进行传参。count代表的是扇区的个数而不是我们要写的字节数。而我们的读函数一次是读一个扇区,因此count是多少就代表我们要读多少个扇区。读完一个扇区后一定要将地址和数据进行更新,虽然在FALSH层面上读是不会限制大小的,但是由于我们是给FATFS文件系统使用,因此我们就要写成按一个扇区来读。

DRESULT disk_read (
	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
	BYTE *buff,		/* Data buffer to store read data */
	LBA_t sector,	/* Start sector in LBA 这个参数是扇区的个数 而不是扇区的地址*/  
	UINT count		/* Number of sectors to read */
)
{
	uint32_t i = 0;
	uint32_t addr = sector*SECTOR_SIZE;
	if (pdrv == DEV_FALSH)
	{
		for (i=0; i<count; i++)
		{
			EN25QXX_Read((BYTE *)buff, addr, SECTOR_SIZE);  //每运行一次就读一个扇区
			addr += SECTOR_SIZE;
			buff += SECTOR_SIZE;

		}
		return RES_OK;
	}


	return RES_PARERR;
}


//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void EN25QXX_Read(uint8_t *pBuffer,uint32_t ReadAddr, uint16_t NumByteToRead)   
{ 
 	uint16_t i;   										    
	FLASH_CS = 0;                            //使能器件   
    SPI1_ReadWriteByte(EN25X_ReadData);         //发送读取命令   
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>16));  //发送24bit地址    
    SPI1_ReadWriteByte((uint8_t)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((uint8_t)ReadAddr);   
    for(i=0; i<NumByteToRead; i++)
	{ 
        pBuffer[i] = SPI1_ReadWriteByte(0XFF);   //循环读数 
    }
	FLASH_CS = 1;  				    	      
} 


//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint8_t SPI1_ReadWriteByte(uint8_t TxData)
{
    uint8_t Rxdata;
	HAL_SPI_TransmitReceive_DMA(&hspi1, &TxData, &Rxdata, 1);
	delay_us(1);   //必须的需要这个延时,不然速度太快了

 	return Rxdata;          		    //返回收到的数据		
}



 这里需要注意的是FLASH在每次写之前一定要先进行擦除,才可以写,因此我们这里还需要一个按扇区擦除的函数。

DRESULT disk_write (
	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
	const BYTE *buff,	/* Data to be written */
	LBA_t sector,		/* Start sector in LBA */
	UINT count			/* Number of sectors to write */
)
{
	uint32_t i = 0;
	uint32_t addr = sector*SECTOR_SIZE;
	if (pdrv == DEV_FALSH)
	{
		for (i=0; i<count; i++)
		{
			EN25QXX_Erase_Sector(addr);
			EN25QXX_Write_Sector((BYTE *)buff, addr, SECTOR_SIZE);
			addr += SECTOR_SIZE;
			buff += SECTOR_SIZE;
		}
		return RES_OK;
	}
	


	return RES_PARERR;
}

//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个山区的最少时间:150ms
void EN25QXX_Erase_Sector(uint32_t Dst_Addr)   
{  
	//监视falsh擦除情况,测试用   
// 	printf("fe:%x\r\n",Dst_Addr);	  
    EN25QXX_Write_Enable();                  //SET WEL 	 
    EN25QXX_Wait_Busy();   
  	FLASH_CS = 0;                            //使能器件   
    SPI1_ReadWriteByte(EN25X_SectorErase);      //发送扇区擦除指令 
    SPI1_ReadWriteByte((uint8_t)((Dst_Addr)>>16));  //发送24bit地址    
    SPI1_ReadWriteByte((uint8_t)((Dst_Addr)>>8));   
    SPI1_ReadWriteByte((uint8_t)Dst_Addr);  
	FLASH_CS = 1;                            //取消片选     	      
    EN25QXX_Wait_Busy();   				   //等待擦除完成
}
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	if (pdrv == DEV_FALSH)
	{
		switch (cmd){
			case CTRL_SYNC:               //确保设备已完成挂起的写入过程。如果磁盘I/O层或存储设备具有回写式缓存,则脏缓存数据必须立即提交到介质。如果对介质的每个写操作都在以下时间内完成,则此命令不执行任何操作 disk_write 功能。
				return RES_OK;
			case GET_SECTOR_COUNT:{
				*(DWORD *)buff = 4096;     //表示扇区的个数
				return RES_OK;
			}		
			case GET_SECTOR_SIZE:{
				*(WORD *)buff = SECTOR_SIZE;  //表示每个扇区的大小
				return RES_OK;
			}	
		}
		return RES_PARERR;
	}
	return RES_PARERR;
}

三、注意事项

移植完上面的接口函数后,因为我们无法手动给FLASH里面格式化成FAT32文件系统,因此我们需要使用f_mkfs()函数,来完成格式化。当你使用这个函数的时候,你会发现会报错,那是因为在ffconf.h里面的相关配置没有打开。

 如果你在编译的过程中发现下面的错误,那也是因为配置里面没有关闭使用时间这个选项

 

如果发现运行过程中,程序老是死在某个地方,他不是死循环,而是直接程序就不动了。那有可能是因为扇区的大小设置的不对,因为FATFS默认情况将512个字节作为一个簇,但是我们的FLASH里面的最小擦出的单元是一个扇区,而我们的一个扇区大小是4KB也就是4096个字节。因此我们要将扇区范围的上限提高一下。

res = f_mkfs("0:/", 0, work, 4096);
这里传的参数也是一个扇区的大小,如果不对格式化会有问题。


http://www.kler.cn/a/459826.html

相关文章:

  • JavaWeb开发(五)Servlet-ServletContext
  • 从单点 Redis 到 1 主 2 从 3 哨兵的架构演进之路
  • 2024 年发布的 Android AI 手机都有什么功能?
  • 算法题(23):只出现一次的数字
  • Python 字符串定义及前缀
  • android stdudio环境: gradle一直安装失败
  • OpenHarmony源码编译后烧录镜像教程,RK3566鸿蒙开发板演示
  • 本地创建了一个 Git 仓库推送到GitHub中
  • Android笔试面试题AI答之非技术问题(2)
  • OPPO手机如何正确使用金融理财计算器
  • vue3学习笔记(11)-组件通信
  • XL系列433芯片、2.4G收发芯片 通讯对码说明
  • 【LeetCode 面试经典150题】详细题解之矩阵篇
  • 人民医院网络安全规划与设计
  • 正大的资产配置理念解析
  • day-102 二进制矩阵中的最短路径
  • STM32 高级 WIFi案例1:测试AT指令
  • 数据库自增 id 过大导致前端时数据丢失
  • 活动预告 |【Part1】Microsoft Azure 在线技术公开课:数据基础知识
  • Debian12使用RKE2离线部署3master2node三主两从的k8s集群详细教程
  • 通过iptables限制docker 容器的运行端口
  • Spring Boot 项目 与 其他依赖版本兼容对应表
  • K8S网络流量路径
  • 数据库系列之分布式数据库下误删表怎么恢复?
  • UE5材质节点CameraDepthFade
  • Kafka安全优化文档:漏洞修复到安全加固