65.9K
CodeProject 正在变化。 阅读更多。
Home

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

2023 年 5 月 4 日

CPOL

4分钟阅读

viewsIcon

5072

如何将 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 中缺少并行引脚。

ch376_pinout

所需连接在数据手册的“功能描述”和“SPI 串行接口”部分中进行了描述,但为了方便起见,这里也将进行总结。 要使用 SPI,请将 WR# 和 RD# 引脚接地(# 表示低电平有效引脚)。 尽管数据手册还声明 PCS#/TXD#/RXD#/A0# 应连接到 VCC,但这些引脚已经具有内部上拉电阻,因此在 SPI 模式下不需要这些引脚的外部连接。 还建议使用 5V 操作 CH376,因为在我的测试中,当在 3.3V 上运行 CH376 时,我遇到了一些奇怪的问题,而使用 5V 时不会发生这种情况。 CH376 还支持 SPI 中断(当输入数据可用时),可以通过专用引脚或通过 MISO 引脚,可以在初始化期间指定。

这是我使用的电路图

CH376S SPI connection

通过使用来自 这个 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_READCMD_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_readf_writef_openf_close 等)访问 USB 文件系统,而不必担心 CH376 上有限的文件系统支持。

您可以从这里下载完整的源代码以及 CH375 和 CH376 数据手册。 我的代码改编自 Ch376msc 存储库,通过 SPI 初始化 CH376,并允许您仅使用 CH376 的扇区读取/写入命令来使用 FatFs 访问 USB 文件系统。 为了简单起见,其他未使用的功能已被删除。

另请参阅

© . All rights reserved.