【FATFS】f_read函数详细解析

FATFS f_read函数详细解析

f_read 函数用于从打开的文件中读取数据,将读取到的数据存储在提供的缓冲区中。该函数的主要作用是从文件中读取指定数量的字节

FRESULT f_read (
	FIL* fp,    /* 打开的文件对象指针 */
	void* buff,	/* 存储读取数据的缓冲区 */
	UINT btr,	/* 需要读取的字节数 */
	UINT* br	/* 实际读取的字节数(输出参数) */
)
{
	FRESULT res;              /* 操作结果,返回值 */
	FATFS *fs;                /* 文件系统对象指针 */
	DWORD clst;               /* 当前簇号 */
	LBA_t sect;               /* 当前扇区号 */
	FSIZE_t remain;           /* 文件剩余的字节数 */
	UINT rcnt, cc, csect;     /* 分别为:实际读取的字节数,连续读取的扇区数,当前簇中的扇区偏移 */
	BYTE *rbuff = (BYTE*)buff;/* 读取缓冲区指针,方便按字节操作 */

	*br = 0;	/* 初始化已读取字节数为 0 */
	
	res = validate(&fp->obj, &fs); /* 验证文件对象是否合法,获取文件系统对象 */
	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) /* 如果文件对象无效或有错误,退出 */
		LEAVE_FF(fs, res); /* 返回错误状态 */
	
	if (!(fp->flag & FA_READ)) /* 检查文件是否打开为可读模式 */
		LEAVE_FF(fs, FR_DENIED); /* 如果文件不可读,返回 "拒绝访问" 错误 */
	
	remain = fp->obj.objsize - fp->fptr; /* 计算文件剩余未读取的字节数 */
	if (btr > remain) btr = (UINT)remain; /* 如果要读取的字节数超过文件剩余部分,则调整为剩余字节数 */

	/* 循环读取数据,直到读取完所需的字节数 */
	for (; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) {	
		if (fp->fptr % SS(fs) == 0) { /* 判断是否位于扇区边界 */
			csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* 计算当前簇中的扇区偏移 */
			if (csect == 0) { /* 如果在簇的边界 */
				if (fp->fptr == 0) { /* 文件起始处的簇 */
					clst = fp->obj.sclust; /* 获取文件的第一个簇 */
				} else { /* 中间或文件结尾 */
#if FF_USE_FASTSEEK
					if (fp->cltbl) {
						clst = clmt_clust(fp, fp->fptr); /* 快速寻址模式,获取当前簇号 */
					} else
#endif
					{
						clst = get_fat(&fp->obj, fp->clust); /* 从 FAT 表中获取当前簇的下一个簇 */
					}
				}
				if (clst < 2) ABORT(fs, FR_INT_ERR); /* FAT 表损坏,簇号错误 */
				if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); /* 读取 FAT 表时发生磁盘错误 */
				fp->clust = clst; /* 更新当前簇号 */
			}
			sect = clst2sect(fs, fp->clust); /* 将当前簇转换为对应的起始扇区 */
			if (sect == 0) ABORT(fs, FR_INT_ERR); /* 扇区无效,返回内部错误 */
			sect += csect; /* 计算实际的物理扇区号 */
			cc = btr / SS(fs); /* 计算剩余字节数能读取的完整扇区数 */
			if (cc > 0) { /* 如果可以读取一个或多个完整扇区 */
				if (csect + cc > fs->csize) { /* 如果超过当前簇的边界 */
					cc = fs->csize - csect; /* 限制在当前簇范围内读取 */
				}
				if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); /* 直接从磁盘读取多个扇区到缓冲区 */
#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2
#if FF_FS_TINY
				if (fs->wflag && fs->winsect - sect < cc) {
					memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); /* 缓存中有脏扇区时,替换数据 */
				}
#else
				if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
					memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); /* 替换缓存中的脏扇区数据 */
				}
#endif
#endif
				rcnt = SS(fs) * cc; /* 记录已传输的字节数 */
				continue; /* 跳过以下步骤,继续读取 */
			}
#if !FF_FS_TINY
			if (fp->sect != sect) { /* 如果要读取的扇区不在缓存中 */
#if !FF_FS_READONLY
				if (fp->flag & FA_DIRTY) { /* 如果缓存的扇区已被修改,需要写回 */
					if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* 将脏扇区写回磁盘 */
					fp->flag &= (BYTE)~FA_DIRTY; /* 清除脏标志 */
				}
#endif
				if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* 将当前扇区读入缓存 */
			}
#endif
			fp->sect = sect; /* 更新当前的缓存扇区号 */
		}
		rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* 计算当前扇区中剩余的字节数 */
		if (rcnt > btr) rcnt = btr; /* 如果剩余字节数比需要读取的多,进行裁剪 */
#if FF_FS_TINY
		if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* 移动扇区窗口 */
		memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* 从文件系统窗口中提取部分扇区数据 */
#else
		memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* 从缓存中提取部分扇区数据 */
#endif
	}

	LEAVE_FF(fs, FR_OK); /* 成功退出并返回 */
}
相关推荐
太陈抱不动9 个月前
STM32进阶笔记——FATFS文件系统(下)
笔记·stm32·sd卡·fatfs