将 FatFs FAT 文件系统模块与 CH375/CH376 USB 控制器集成





4.00/5 (1投票)
如何将 FatFs FAT 文件系统模块与 CH375/CH376 USB 控制器集成
在最近的一个嵌入式项目中,我需要使用 dsPIC33EP512MC502 访问存储在 USB 闪存驱动器上的数据。 您可能已经猜到,我选择的 PIC,尽管功能非常强大,但没有集成的 USB 控制器,需要我使用外部 USB 模块。经过一些研究,我决定选择 CH376,这是一种 8051 内核 USB 接口芯片,提供 SOP-28 和 SSOP-20 封装。 支持三种主机接口模式,即 UART、SPI 和 8 位并行(仅限 SOP-28 封装)。 CH376 是 CH375 的后续产品,除了 USB 闪存驱动器外,还增加了对 SD 卡的支持。 CH375 通常用于构建 ISA 到 USB 的适配器,我已经在之前的文章中介绍过。
这是从数据手册中提取的 CH376S (SOP-28) 和 CH376T (SSOP-20) 的引脚排列。 正如你所看到的,CH376T 中缺少并行引脚。
所需连接在数据手册的“功能描述”和“SPI 串行接口”部分中进行了描述,但为了方便起见,这里也将进行总结。 要使用 SPI,请将 WR# 和 RD# 引脚接地(# 表示低电平有效引脚)。 尽管数据手册还声明 PCS#/TXD#/RXD#/A0# 应连接到 VCC,但这些引脚已经具有内部上拉电阻,因此在 SPI 模式下不需要这些引脚的外部连接。 还建议使用 5V 操作 CH376,因为在我的测试中,当在 3.3V 上运行 CH376 时,我遇到了一些奇怪的问题,而使用 5V 时不会发生这种情况。 CH376 还支持 SPI 中断(当输入数据可用时),可以通过专用引脚或通过 MISO 引脚,可以在初始化期间指定。
这是我使用的电路图
通过使用来自 这个 Github 仓库 的示例代码,通过在 SPI 初始化期间设置 CKE=CKP=SMP=1
,我可以在发送 CMD_CHECK_EXIST
命令后快速从 CH376 检索到 SPI 响应。
SPI2CON1bits.CKE = 1;
SPI2CON1bits.CKP = 1;
SPI2CON1bits.SMP = 1;
接下来,我通过发送 CMD_DISK_MOUNT
命令安装了我的驱动器,该命令在包含单个 FAT16/FAT32 分区的 USB 闪存驱动器上运行良好,并且我能够从我的闪存驱动器中读取一个简单的文本文件。 然而,经过一些实验,很明显,CH376 对读取/写入文件/目录的支持非常有限,缺乏文件复制、长文件名 (LFN) 支持等等。 由于 CH376 支持原始扇区访问(通过 CMD_DISK_READ
和 CMD_DISK_WRITE
),我决定将我的 CH376 库与 FatFs(一个通用的 FAT 文件系统模块)集成,以便更方便地管理 USB 驱动器上的文件。
默认情况下,FatFs 附带示例代码,用于使用 PIC 的集成 SPI 控制器与 SD 卡连接(文件 *mm_pic.h*、*diskio.h* 和 *diskio.c*),必须对其进行修改以允许输入源选择(SD 卡或通过 CH376 的 USB),并在必要时将数据路由到 CH376。为此,我将 setFatFsIsUsb
添加到 *diskio.c*
void setFatFsIsUsb(unsigned char isUSB)
{
isUsingCH376 = isUSB;
}
当然,FatFS 初始化代码(以及其他内容)必须更改
DSTATUS disk_initialize(BYTE pdrv) {
// SendUARTStr("disk_initialize");
if (isUsingCH376)
{
// SendUARTStr("Init USB");
return disk_initialize_usb();
}
else {
// SendUARTStr("Init SD");
return disk_initialize_sd(pdrv);
}
}
需要注意的是,函数 disk_read()
和 disk_write()
也必须重写以允许 SD 和 USB 源。 这是必需的,因为在读取/写入扇区数据时,FatFs 支持读取/写入任意数量的扇区,而 CH376 只能支持 1 个或 4 个扇区。 因此,读取/写入(比如)3 个扇区的请求必须作为三个不同的请求发送。
DRESULT disk_read_usb (
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
// CH376 only supports reading 1 or 4 sectors at the same time
// But FATFS may read 3, we therefore read only 1 sector at a time
// for cases of 3 sectors.
BYTE *buffPT = buff;
UINT c = 0;
UINT readCnt = 0;
UINT cnt = 0;
if (count == 1 || count == 4)
{
cnt = ch376_sectorRead(sector, count, buffPT);
readCnt += cnt;
buffPT += cnt;
if (cnt != SECTOR_SIZE)
{
// read 0 bytes indicating error
SendUARTStr("sctRdE1");
return RES_ERROR;
}
}
else {
for (c = 0; c < count; c++)
{
cnt = ch376_sectorRead(sector + c, 1, buffPT);
readCnt += cnt;
buffPT += cnt;
if (cnt != SECTOR_SIZE)
{
// read 0 bytes indicating error
SendUARTStr("sctRdE2");
return RES_ERROR;
}
}
}
return RES_OK;
}
尽管 CH376 也支持 SD 卡,可以通过类似于 USB 驱动器的方式访问 SD 卡,只需切换操作模式 (CMD_SET_USB_MODE
),但根据我的经验,通过 CH376 访问 SD 卡会消耗不必要的额外电流。 因此,我直接使用我的 PIC 访问 SD 卡,并且仅使用 CH376 访问 USB 闪存驱动器。
通过这些更改,FatFs 函数现在可用于使用以下代码片段访问存储在我的 USB 驱动器上的文件
setFatFsIsUsb(TRUE);
SPI2STATbits.SPIEN = 1;
ch376_clearvars();
ch376_reset();
FRESULT res = f_mount(&fs, "", 1);
if (res == FR_OK)
{
SendUARTStr("USB Mounted");
}
通过 FatFs 成功挂载驱动器后,您可以使用类似于 C 语言的函数(f_read
、f_write
、f_open
、f_close
等)访问 USB 文件系统,而不必担心 CH376 上有限的文件系统支持。
您可以从这里下载完整的源代码以及 CH375 和 CH376 数据手册。 我的代码改编自 Ch376msc 存储库,通过 SPI 初始化 CH376,并允许您仅使用 CH376 的扇区读取/写入命令来使用 FatFs 访问 USB 文件系统。 为了简单起见,其他未使用的功能已被删除。