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

news/2024/12/27 12:16:33 标签: 学习

一、需要移植的接口

我们通过看官网的手册,可以看到我们只要完成下面函数的实现,就可以完成移植。我们这里只移植前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.niftyadmin.cn/n/5801709.html

相关文章

【MySQL】索引 面试题

文章目录 适合创建索引的情况创建索引的注意事项MySQL中不适合创建索引的情况索引失效的常见情况 索引定义与作用 索引是帮助MySQL高效获取数据的有序数据结构&#xff0c;通过维护特定查找算法的数据结构&#xff08;如B树&#xff09;&#xff0c;以某种方式引用数据&#xf…

鸿蒙UI开发——使用WidthTheme实现局部深浅色

1、场景描述 在实际的应用开发中&#xff0c;我们可能需要在界面中局部应用深色或者浅色的界面样式&#xff0c;与全局的深色、亮色同时生效。场景例如&#xff1a;深/亮色预览。此时&#xff0c;我们可以使用WithTheme能力来达到我们的效果。 2、WithTheme WithTheme组件可…

VSCode 插件开发实战(十二):如何集成Git操作能力

前言 VSCode 不仅提供了丰富的编辑功能&#xff0c;还支持通过扩展插件来提升工作效率。本文将通过构建一个自定义 VSCode 插件&#xff0c;带你深入了解如何在编辑器中简化和自动化 Git 操作。这不仅能提升开发效率&#xff0c;还能减少人为操作失误&#xff0c;从而更加专注…

Excel中一次查询返回多列

使用Excel或wps的时候&#xff0c;有时候需要一次查询返回多列内容&#xff0c;这种情况可以选择多次vlookup或者多次xlookup&#xff0c;但是这种做法费时费力不说&#xff0c;效率还有些低下&#xff0c;特别是要查询的列数过多时。我放了3种查询方法&#xff0c;效果图&…

C++ 面向对象编程:关系运算符重载、函数调用运算符重载

对 、<、> 三个运算符分别进行重载&#xff0c;可见以下代码&#xff1a; #include<iostream> using namespace std;class location { public:location(int x1, int y1) :x(x1), y(y1){};bool operator(const location& l1) const{return x l1.x && …

文件路径与Resource接口详解

目录 第一章、快速了解文件路径1.1&#xff09;什么是文件路径&#xff1f;1.1.1&#xff09;绝对路径1.1.2&#xff09;相对路径 1.2&#xff09;重要&#xff1a;相对路径的表示方法1.2.1) ./ 与 ../ 1.3&#xff09;文件路径与环境变量1.3.1&#xff09;什么是环境变量1.3.2…

scala基础学习(数据类型)-数组

文章目录 数组 Array创建数组直接定义fillofDimtabulate range打印数组toSeqdeepforeach(println) length获取长度indexOf 获取元素索引获取元素/修改元素遍历数组数组内元素转换filter 过滤found 查找元素数组折叠 foldLeft切片拼接排序拷贝copyclone 数组 Array Array是一个…

计算机的错误计算(一百九十二)

摘要 用两个大模型计算 csc(0.999), 其中&#xff0c;0.999是以弧度为单位的角度&#xff0c;结果保留5位有效数字。两个大模型均给出了 Python代码与答案。但是&#xff0c;答案是错误的。 例1. 计算 csc(0.999), 其中&#xff0c;0.999是以弧度为单位的角度&#xff0c;结…