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); /* 成功退出并返回 */
}