Click here to Skip to main content
15,886,919 members
Articles / Programming Languages / C
Technical Blog

Integrating FatFs FAT filesystem Module with CH375/CH376 USB Controller

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
3 May 2023CPOL4 min read 3.3K   1  
How to integrate FatFs FAT filesystem module with CH375/CH376 USB controller
The code in this post is adapted from the Ch376msc repository, initializes the CH376 via SPI, and allows to use FatFs to access the USB filesystem by using only the CH376’s sector read/write commands.

In a recent embedded project, I need to access data stored on a USB thumb drive using a dsPIC33EP512MC502. As you might have guessed, the PIC that I selected, despite being a very capable device, does not have an integrated USB controller, requiring me to use an external USB module. After some research, I decided to pick the CH376, a 8051-core USB interface chip which is available in SOP-28 and SSOP-20 packages. Three host interface modes are supported, namely UART, SPI and 8-bit parallel (SOP-28 packages only). The CH376 is the successor to the CH375, with added support for SD cards, in addition to USB thumb drives. The CH375 is commonly used to build ISA to USB adapters, which I have covered in my previous article.

This is the pinout for the CH376S (SOP-28) and CH376T (SSOP-20), extracted from the datasheet. As you can see, parallel pins are missing from the CH376T:

ch376_pinout

The required connections are described in sections “Functions Description” and “SPI Serial Interface” of the datasheet, but will also be summarized here for convenience. To use SPI, ground the WR# and RD# pins (# denotes active-low pins). Although the datasheet also states that PCS#/TXD#/RXD#/A0# should be connected to VCC, these pins already have internal pull-up resistors and hence no external connections for these pins are needed in SPI mode. It is also recommended to operate the CH376 using 5V, as during my tests, I encountered some weird issues when running the CH376 on 3.3V, which did not occur if 5V was used. The CH376 also supports SPI interrupt (when input data is available), either via a dedicated pin, or via the MISO pin, which can be specified during initialization.

This is the circuit diagram that I used:

CH376S SPI connection

By using the sample code from this Github repository, I was quickly able to retrieve an SPI response from the CH376 after sending CMD_CHECK_EXIST command, by setting CKE=CKP=SMP=1 during SPI initialization:

C++
SPI2CON1bits.CKE = 1;
SPI2CON1bits.CKP = 1;
SPI2CON1bits.SMP = 1;

Next, I mounted my drive by sending CMD_DISK_MOUNT command, which worked well with a USB thumbdrive containing a single FAT16/FAT32 partition, and I was able to read a simple text file from my thumb drive. However, after some experiments, it is clear that support for reading/writing files/directories on the CH376 is very limited, lacking files copying, long file names (LFN) support, among other things. As the CH376 supports raw sector access (via CMD_DISK_READ and CMD_DISK_WRITE), I decided to integrate my CH376 library with FatFs, a generic FAT filesystem module, to make it more convenient to manage files on the USB drive.

By default, FatFs comes with sample code to interface with the SD card using the PIC’s integrated SPI controller, (files mm_pic.h, diskio.h and diskio.c), which has to be modified to allow input source selection (either SD card or USB via CH376), and to route data to the CH376 if necessary. To do this, I added setFatFsIsUsb to diskio.c:

C++
void setFatFsIsUsb(unsigned char isUSB)
{
    isUsingCH376 = isUSB;
}

Of course, the FatFS initialization code (among other things) will have to be changed:

C++
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);
    }
}

To note, functions disk_read() and disk_write() will also have to be re-written to allow both SD and USB sources. This is needed because when reading/writing sector data, FatFs supports read/writing an arbitrary number of sectors, whereas CH376 can only support either 1 or 4 sectors. Therefore, a request to read/write, let’s say 3 sectors, will have to be sent as three different requests:

C++
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;
}

Although CH376 also supports SD cards, which can be accessed in a manner similar to USB drives, by just switching the operating mode (CMD_SET_USB_MODE), in my experience accessing SD card via the CH376 consumes unneeded extra amount of current. I therefore access the SD card directly using my PIC and only use the CH376 to access the USB thumb drives.

With these changes, FatFs functions can now be used to access files stored on my USB drive using the following code snippet:

C++
setFatFsIsUsb(TRUE);
SPI2STATbits.SPIEN = 1;
ch376_clearvars();
ch376_reset();                          

FRESULT res = f_mount(&fs, "", 1);
if (res == FR_OK)
{
   SendUARTStr("USB Mounted");
}

Once the drive has been successfully mounted via FatFs, you can access the USB filesystem using C-like functions (f_read, f_write, f_open, f_close, etc.) without having to worry about limited filesystem support on the CH376.

You can download the full source code, together with the CH375 and CH376 datasheets here. My code is adapted from the Ch376msc repository, initializes the CH376 via SPI, and allows you to use FatFs to access the USB filesystem by using only the CH376’s sector read/write commands. Other unused features have been removed for simplicity.

See Also

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Writer
Singapore Singapore
Since 2008, ToughDev has been publishing technical sharing articles on a wide range of topics from software development to electronics design. Our interests include, but are not limited to, Android/iOS programming, VoIP products, embedded design using Arduino/PIC microcontrollers, reverse-engineering, retro-computing, and many others. We also perform product reviews, both on new and vintage products, and share our findings with the community. In addition, our team also develops customized software/hardware solutions highly adapted to suit your needs. Contact us for more information.

Comments and Discussions

 
-- There are no messages in this forum --