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

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码

大家都知道使用CubMAX可以很快的将,FATFS文件管理系统移植到单片机上,但是别的芯片没有这么好用的工具,就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题,然后再移植FATFS文件管理系统。

二、SD卡的DMA读写实现

 这个我们直接使用工具生成就可以了,需要注意上面的那几点,不然会工作不正常。为了让大家看得更加清楚,下面的几个函数下的都比较简单,没有过多的判断。尽可能地缩短代码。下面这几个函数的实现是必须的不能在减少了。

//获取卡的状态
uint8_t SD_GetCardState(void)
{
	HAL_SD_CardStatusTypeDef pStatus;
	if (HAL_SD_GetCardStatus(&hsd, &pStatus) != 0)
	{
		return HAL_BUSY;
	}
	else
	{
		return HAL_OK;
	}
	
}
void SD_ReadBlocks_DMA(uint8_t *buf, uint32_t sector, uint32_t cnt)
{
	HAL_SD_ReadBlocks_DMA(&hsd, buf, sector, cnt);//通过DMA读取SD卡n个扇区
	while(SD_GetCardState() != SD_TRANSFER_OK);
	//等待SD卡读完
}
 
uint8_t SD_WriteBlocks_DMA(uint8_t *buf, uint32_t sector, uint32_t cnt)
{
	HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)buf, sector, cnt);//通过DMA写SD卡n个扇区
	//等待SD卡写完
	while(SD_GetCardState()!=SD_TRANSFER_OK)
}

 三、FATFS中diskio.c里面相关函数的实现

#define DEV_FALSH   0
#define DEV_SD      1

#define SECTOR_SIZE 4096               //定义扇区大小

DSTATUS disk_status (
	BYTE pdrv		/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat;
	switch (pdrv) {
		case DEV_FALSH :
			stat = EN25QXX_ReadSR();  //读取FLASH状态的函数
			return stat;
		case DEV_SD :
			stat = SD_GetCardState(); //读取SD卡状态的函数
			return stat;		
	}
	return STA_NOINIT;
}
DSTATUS disk_initialize (
	BYTE pdrv				/* Physical drive nmuber to identify the drive */
)
{
	DSTATUS stat = 0;;

	switch (pdrv) {
		case DEV_FALSH :   
			return stat;   //如果你在外面已经做过了相关模块的初始化啥的这里直接返回接可以了
		case DEV_SD :
			return stat;
	}
	return STA_NOINIT;
}
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;
	uint8_t result = 0;
	
	switch (pdrv) {
		case DEV_FALSH :{
			uint32_t addr = sector*SECTOR_SIZE;
			for (i=0; i<count; i++)
			{
				EN25QXX_Read((BYTE *)buff, addr, SECTOR_SIZE);
				addr += SECTOR_SIZE;
				buff += SECTOR_SIZE;
			}
			return RES_OK;
		}
		case DEV_SD :{
			SD_ReadBlocks_DMA(buff, sector, count);
			return RES_OK;
		}
		
	}

	return RES_PARERR;
}

 这里需要注意的是FLASH和SD卡里面的两个函数是不太一样的,FLASH里面的函数只能写一个扇区,要想写多个扇区那你就得循环写,在FATFS文件系统里面,他的sector地址是从0-0xffffffff他是一个连续的地址,一个sector就是一个地址,但是在FLASH里面他的最小单位不是扇区,他有比扇区更小的操作也就是页,但是他最小的单位是可以按字节来读取的,也就是说他是一个字节一个地址,如果我们直接将FATFS里面的地址传过来,在FLASH里面他就会认为是字节地址,因此我们需要一个地址的偏移,也就是FLASH里面一个扇区的大小,也就是4096.理解这里你需要知道他们呢两个的基本操作的单元是不一样的。但是在SD卡里面就不需要这样的偏移,因为SD卡里面他就是以扇区为大小来进行读取的。也就是SD卡里面每个地址是512个字节。他的最小读写单元就是512个字节,也就是一个扇区。因此我们在传入参数的时候不需要将地址进行偏移。而且SD卡的读写函数是一次可以读多个扇区的,因此可以直接将count传入,他也是可以直接读的。其实说白了就是如果你的最小读写单元就是512个字节那么你就不需要地址的偏倚,否则你就要根据自己设置的扇区大小进行偏移。

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;
	uint8_t result = 0;

	switch (pdrv) {
		case DEV_FALSH :{
			uint32_t addr = sector*SECTOR_SIZE;
			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;
		}
		case DEV_SD :{
			SD_WriteBlocks_DMA((BYTE *)buff, sector, count);
			return RES_OK;
		}
	}

	return RES_PARERR;
}
DRESULT disk_ioctl (
	BYTE pdrv,		/* Physical drive nmuber (0..) */
	BYTE cmd,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
//	DRESULT res = 0;
//	int result;

	switch (pdrv) {
		case DEV_FALSH :{
			switch (cmd){
				case CTRL_SYNC:
					return RES_OK;
				case GET_SECTOR_COUNT:{
					*(DWORD *)buff = 4096;     //表示扇区的个数
					return RES_OK;
				}		
				case GET_SECTOR_SIZE:{
					*(WORD *)buff = SECTOR_SIZE;  //表示每个扇区的大小
					return RES_OK;
				}	
			}
		}
		case DEV_SD :{
			switch (cmd){
				case CTRL_SYNC:
					return RES_OK;
				case GET_SECTOR_COUNT:{
					*(DWORD *)buff = 31116288;     //表示扇区的个数
					return RES_OK;
				}		
				case GET_SECTOR_SIZE:{
					*(WORD *)buff = 512;  //表示每个扇区的大小
					return RES_OK;
				}	
				case GET_BLOCK_SIZE:{
					*(DWORD *)buff = 512;  //表示每个扇区的大小
					return RES_OK;
				}
				default :
					return RES_PARERR;
			}
		}
	}

	return RES_PARERR;
}

上面这个函数的实现我是直接将数据写到里面的,这样操作如果换了别的SD卡,就完蛋了,严谨一点的操作是使用相关函数,读出来。我这里就比较粗糙的实现了,主打一个能用就行。

四、进行挂载

如果你的挂载直接就返回0,那么恭喜你直接就成功了,但是在大多数的情况下我们的挂载是不能成功的。下面就针对几个返回值进行故障的排除。

f_mount返回11  也就是你要使用几个外设,如果你就一个SD卡,那么问题,如果有2个你这里就得改成2,不然就会返回错误代码11

 f_mount返回1,当这些函数没有正确的返回值就会报这个错误,我将哪里注释掉之后,就会产生这个错误。

还有一种就是返回值是13的错误,他的意思是卡上面没有文件系统导致的。但是我将卡格式化后发现,还是识别不上。还是返回13。经过一夜的查找发现是DMA设置那里没有设置地址的递增,就导致他一次只能读几个字节,后面的读写完全就是不正常的,内存里面是有一个缓冲区的,如果地址传完不发生偏移,那这个缓冲区就是没有意义的,因此只要你传入的DMA里面的buf[]数组里面的元素不止一个那么这里就得设置地址偏移。

	res = f_mount(&fs, "1:/", 1);

 一般来讲只要能挂载成功,其他的函数都是可以正常工作的,前提是你挂载的时候,后面选项里面一定要是 1,如果是0,就算有问题,他写会显示挂载成功的,这时候肯定是不行的。


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

相关文章:

  • RustDesk内置ID服务器,Key教程
  • 【ES6复习笔记】Spread 扩展运算符(8)
  • 【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
  • 一种寻路的应用
  • OpenCV相机标定与3D重建(35)计算两幅图像之间本质矩阵(Essential Matrix)的函数findEssentialMat()的使用
  • 自学记录HarmonyOS Next的HMS AI API 13:语音合成与语音识别
  • AI客服机器人如何帮助企业应对高峰期客户咨询压力?
  • LeetCode--239.滑动窗口最大值(使用双端队列解决)
  • docker拉取rabbitmq镜像时报Client.Timeout exceeded while awaiting header错误处理办法
  • Linux-Ubuntu之串口通信
  • ChatGPT助力数据可视化与数据分析效率的提升(二)
  • lua debug相关方法详解
  • leetcode82:删除链表中的重复元素II
  • 【蓝桥杯】走迷宫
  • 题海拾贝:蓝桥杯 2020 省AB 乘法表
  • 免费资源网站
  • ANSYS EMC Plus:谐振腔中的天线
  • Markdown语法字体字号讲解
  • git revert
  • 【C#】WPF设置Separator为垂直方向
  • (icml2024)SLAattention,基于原文时序模型进行改进
  • 【AIGC篇】AIGC 引擎:点燃创作自动化的未来之火
  • 项目报 OutOfMemoryError 、GC overhead limit exceeded 问题排查以及解决思路实战
  • LeetCode 热题 100_二叉树的中序遍历(36_94_简单_C++)(二叉树;递归(中序遍历);迭代)
  • 如何在 Ubuntu 22.04 上安装 Ansible 教程
  • OpenStack系列第三篇:CentOS7 上部署 OpenStack(Train版)集群教程 Ⅲ Nova Neutron 服务部署