Lianshengde HLK-W806: run FatFs to read and write SD card / TF Card in FAT and exFat format

catalogue

About SD cards and FatFs

The introduction of SD card and FatFs has been Keil MDK STM32 series (IX) FAT format SD card TF card reading and writing based on HAL and FatFs The working mechanism and communication mechanism can be read if you are interested The author of FatFs wrote a very good introduction, How to Use MMC/SDC , very detailed, worth reading

Transplantation of FatFs

File structure of FatFs

The file structure of FatFs is as follows:

├── diskio.c        # The list of methods to be implemented by the developer responsible for migration needs to be modified
├── diskio.h        # The externally provided header file defines the enumeration of status and return values
├── ff.c            # The main logic implementation of FastFs does not need to be modified
├── ffconf.h        # The configuration file can be modified according to your own environment and needs
├── ff.h            # FastFs header file, no modification required
├── ffsystem.c      # The implementation related to the operating system does not need to be modified
├── ffunicode.c     # The character sets of various codes are in subdirectories before. Now they are merged into one file, which is much more convenient
├── LICENSE.txt

Methods to be implemented for migration

FatFs has abstracted the operation of Fat format. To run FatFs in the new environment, you only need to implement diskio Several methods in C

DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

You also need to implement the RTC interface so that you can write the correct time when creating the file

DWORD get_fattime (void);

Implementation of W801/W806 based on SPI

The complete code uses the latest R0 14b version of FatFs

The implementation of the above method is fatfs_mmc.c this document is mainly divided into three parts:

SPI basic method

/* SPI transmit a byte */
static void MMC_SPI_TxByte(uint8_t data)
{
    HAL_SPI_Transmit(&hspi, &data, 1, SPI_TIMEOUT);
}

/* SPI transmit buffer */
static void MMC_SPI_TxBuffer(uint8_t *buffer, uint16_t len)
{
    HAL_SPI_Transmit(&hspi, buffer, len, SPI_TIMEOUT);
}

/* SPI receive a byte */
static uint8_t MMC_SPI_RxByte(void)
{
    uint8_t dummy, data;
    dummy = 0xFF;
    HAL_SPI_TransmitReceive(&hspi, &dummy, &data, 1, SPI_TIMEOUT);
    return data;
}

SD card communication method

/* wait SD ready */
static uint8_t MMC_ReadyWait(void)
{
    uint8_t res;
    uint32_t tickstart = HAL_GetTick();
    /* if SD goes ready, receives 0xFF, timeout 500 */
    do 
    {
        res = MMC_SPI_RxByte();
    } while ((res != 0xFF) && (HAL_GetTick() - tickstart < 500));
    return res;
}

/* power on */
static void MMC_PowerOn(void) 
{
    uint8_t args[6];
    uint32_t cnt = 0x1FFF;

    /* transmit bytes to wake up */
    MMC_CS_HIGH;
    for(int i = 0; i < 10; i++)
    {
        MMC_SPI_TxByte(0xFF);
    }

    /* slave select */
    MMC_CS_LOW;

    /* make idle state */
    args[0] = CMD0;        /* CMD0:GO_IDLE_STATE */
    args[1] = 0;
    args[2] = 0;
    args[3] = 0;
    args[4] = 0;
    args[5] = 0x95;        /* CRC */

    MMC_SPI_TxBuffer(args, sizeof(args));

    /* wait response */
    while ((MMC_SPI_RxByte() != 0x01) && cnt)
    {
        cnt--;
    }

    MMC_CS_HIGH;
    MMC_SPI_TxByte(0XFF);

    PowerFlag = 1;
}

/* power off */
static void MMC_PowerOff(void) 
{
    PowerFlag = 0;
}

/* check power flag */
static uint8_t MMC_CheckPower(void) 
{
    return PowerFlag;
}

/* receive data block */
static bool MMC_RxDataBlock(BYTE *buff, UINT len)
{
    uint8_t token;
    uint32_t tickstart = HAL_GetTick();

    /* loop until receive a response or timeout, timeout 200ms */
    do 
    {
        token = MMC_SPI_RxByte();
    } while((token == 0xFF) && (HAL_GetTick() - tickstart < 200));

    /* invalid response */
    if(token != 0xFE) return false;

    /* receive data */
    do {
        *buff++ = MMC_SPI_RxByte();
    } while(len--);

    /* discard CRC */
    MMC_SPI_RxByte();
    MMC_SPI_RxByte();

    return true;
}

/* transmit data block */

static bool MMC_TxDataBlock(const uint8_t *buff, BYTE token)
{
    uint8_t resp = 0;
    uint8_t i = 0;

    /* wait SD ready */
    if (MMC_ReadyWait() != 0xFF) return false;

    /* transmit token */
    MMC_SPI_TxByte(token);

    /* if it's not STOP token, transmit data */
    if (token != 0xFD)
    {
        MMC_SPI_TxBuffer((uint8_t*)buff, 512);

        /* discard CRC */
        MMC_SPI_RxByte();
        MMC_SPI_RxByte();

        /* receive response */
        while (i <= 64)
        {
            resp = MMC_SPI_RxByte();

            /* transmit 0x05 accepted */
            if ((resp & 0x1F) == 0x05) break;
            i++;
        }

        /* recv buffer clear */
        while (MMC_SPI_RxByte() == 0);
    }

    /* transmit 0x05 accepted */
    if ((resp & 0x1F) == 0x05) return true;

    return false;
}

/* transmit command */
static BYTE MMC_SendCmd(BYTE cmd, uint32_t arg)
{
    uint8_t crc, res;

    /* wait SD ready */
    if (MMC_ReadyWait() != 0xFF) return 0xFF;

    /* transmit command */
    MMC_SPI_TxByte(cmd);                     /* Command */
    MMC_SPI_TxByte((uint8_t)(arg >> 24));     /* Argument[31..24] */
    MMC_SPI_TxByte((uint8_t)(arg >> 16));     /* Argument[23..16] */
    MMC_SPI_TxByte((uint8_t)(arg >> 8));     /* Argument[15..8] */
    MMC_SPI_TxByte((uint8_t)arg);             /* Argument[7..0] */

    /* prepare CRC */
    if(cmd == CMD0) crc = 0x95;    /* CRC for CMD0(0) */
    else if(cmd == CMD8) crc = 0x87;    /* CRC for CMD8(0x1AA) */
    else crc = 1;

    /* transmit CRC */
    MMC_SPI_TxByte(crc);

    /* Skip a stuff byte when STOP_TRANSMISSION */
    if (cmd == CMD12) MMC_SPI_RxByte();

    /* receive response */
    uint8_t n = 10;
    do {
        res = MMC_SPI_RxByte();
    } while ((res & 0x80) && --n);

    return res;
}

diskio. Implementation of C interface method

Because of the long space, the complete code will not be pasted here. Specifically, according to the SD card standard, maintain a state variable, power on and initialize at the beginning, judge the card type, and then read and write

Implementation of RTC interface

Because W806 comes with RTC, this function is easy to implement. You only need to initialize RTC after starting PMU This method returns a 4 - byte DWORD and needs to prepare the data according to the format

/**
 * DWORD, 4-bytes, format:
 * 
 * [25,31], year, [0,127] from 1980
 * [21,24], month, [1,12]
 * [16,20], day, [1,31]
 * [11,15], hour, [0,23]
 * [5,10], minute, [0,59]
 * [0,4], second, [0,59]
*/
DWORD MMC_get_fattime(void)
{
    DWORD val;
    val = (rtc_time.Year - 80) << 25;
    val += rtc_time.Month << 21;
    val += rtc_time.Date << 16;
    val += rtc_time.Hours << 11;
    val += rtc_time.Minutes << 5;
    val += rtc_time.Seconds;
    return val;
}

Adjustment of FatFs parameters

In the current migration, the following parameters have been adjusted

  • FF_USE_STRFUNC 0 - > 1 because f is used_ The puts method writes a string to a file
  • FF_ USE_ LFN 0 - > 1 enables long file name support, otherwise the file name only supports 8 + 3 format
  • FF_ Lba64 0 - > 1 and the following options are used to enable support for exfat format, so that 64GB TF card can be mounted, read and written normally
  • FF_FS_EXFAT 0 -> 1

Demonstration use case description

connection

Six wires are required, and the connection mode is in the main of the demonstration case C

/******************************************************************************
 * \brief       Demo code of FatFs on SD Card with RTC
 * \remarks     test-board: HLK-W806-KIT-V1.0
 * 
 *    This test will perform the following tests:
 *      1. Start RTC with time 2022-1-12 12:28:10
 *      2. Mount SD Card
 *      3. Scan SD Card and list files
 *      4. Check if w806test_long_name.txt exists
 *      5. Delete w806test_long_name.txt if it exists
 *      6. Open w806test_long_name.txt, write and close
 *      7. Open w806test_long_name.txt, read and close
 *      8. Display time in main loop
 * 
 *    Pin Wiring:
 *        B14   -> CS/DAT3
 *        B15   -> SCK, SCL, CLK
 *        B16   -> MISO/DAT0/DO
 *        B17   -> MOSI/CMD/DI
 *        GND   -> VSS
 *        3.3V  -> VDD
 * 
******************************************************************************/

Demonstrate the use of code

After the correct connection, compile the demonstration case and burn the code,

  • Connect the serial port to observe the output
  • Insert SD card (or TF Card) into the card reader
  • Press the RESET key
  • You can observe the process of initializing and mounting successively, displaying the file list, checking and deleting the test file, writing the test file, reading the test file, and finally unloading the SD/TF card

Actual demo output

2GB FAT TF Card

enter main␍␊
MPU_Init␍␊
RTC_Init␍␊
SPI_Init␍␊
MMC_disk_initialize␍␊
␍␊
CMD0␍␊
CMD8... succeeded, SDC V2+␍␊
ACMD41 ACMD41_HCS.. succeeded␍␊
CMD58 80 FF 80 00 type:04␍␊
f_mount succeeded␍␊
total:1922368KB, free:1922360KB␍␊
/w806test_long_name.txt 51␍␊
Test for w806test_long_name.txt...␊
Size: 51␊
Timestamp: 2022/01/12, 12:28␊
Attributes: ----A␊
file deleted␍␊
open to write: w806test_long_name.txt␍␊
close: w806test_long_name.txt␍␊
open to read: w806test_long_name.txt␍␊
read: w806test_long_name.txt␍␊
W806 SD Card I/O Example via SPI␊
-- Planet 4032-877␍␊
done␍␊
close: w806test_long_name.txt
Unmount sd card␍␊
f_unmount succeeded

64GB exFat TF Card

enter main␍␊
MPU_Init␍␊
RTC_Init␍␊
SPI_Init␍␊
MMC_disk_initialize␍␊
␍␊
CMD0␍␊
CMD8... succeeded, SDC V2+␍␊
ACMD41 ACMD41_HCS.. succeeded␍␊
CMD58 C0 FF 80 00 type:0C␍␊
f_mount succeeded␍␊
Total:61036544KB, Free:61029120KB␍␊
/first.txt 54␍␊
/System Volume Information/WPSettings.dat 12␍␊
/System Volume Information/IndexerVolumeGuid 76␍␊
/? 504400␍␊
/? 12608␍␊
Test for w806test_long_name.txt...␊
Size: 51␊
Timestamp: 2022/01/12, 12:28␊
Attributes: ----A␊
file deleted␍␊
open to write: w806test_long_name.txt␍␊
close: w806test_long_name.txt␍␊
open to read: w806test_long_name.txt␍␊
read: w806test_long_name.txt␍␊
W806 SD Card I/O Example via SPI␊
-- Planet 4032-877␍␊
done␍␊
close: w806test_long_name.txt␍␊
Unmount sd card␍␊
f_unmount succeeded

end

The above is the transplantation of R0 on W801/W806 According to the description of FatFs in version 14b, because the demonstration use case is used as a demonstration, the code is not organized according to the actual needs, and a lot of printf is used
If used in a project, fatfs_mmc.c can basically be reused, but in main The methods in C need to be reorganized

Added by believer on Thu, 13 Jan 2022 10:46:52 +0200