目录

关于SD卡和FatFs

SD卡和FatFs的介绍已经在 Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写 中详细说明, 对其工作机制和通信机制有兴趣的可以阅读. FatFs的作者写了一篇非常不错的介绍, How to Use MMC/SDC, 非常详细, 值得一读.

FatFs的移植

FatFs的文件结构

FatFs的文件结构如下:

├── diskio.c        # 需要负责移植的开发人员实现的方法列表, 需要修改
├── diskio.h # 对外提供的头文件, 里面定义了状态和返回值的枚举
├── ff.c # FastFs的主要逻辑实现, 不需要修改
├── ffconf.h # 配置文件, 根据自己的环境和需求作修改
├── ff.h # FastFs头文件, 不需要修改
├── ffsystem.c # 与操作系统相关的实现, 不需要修改
├── ffunicode.c # 各个编码的字符集, 之前版本都在子目录, 现在合并到一个文件里, 方便多了
├── LICENSE.txt

移植需要实现的方法

FatFs已经将Fat格式的操作作了抽象化, 在新环境下运行FatFs, 只需要实现 diskio.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);

还需要实现RTC接口, 这样才能在创建文件时写入正确的时间

DWORD get_fattime (void);

W801/W806基于SPI的实现

完整的代码在演示用例下, 使用了最新的R0.14b版本的FatFs

其中对以上方法的实现是 fatfs_mmc.c 这个文件, 主要分成三个部分:

SPI基础方法

/* 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卡通信方法

/* 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.c 接口方法的实现

因为篇幅比较长, 这里不贴完整代码了, 具体就是根据SD卡的标准, 维护一个状态变量, 在开始时加电初始化, 判断卡类型, 之后才能进行读写操作.

RTC 接口的实现

因为W806自带了RTC, 所以这个功能很容易实现, 只需要启动PMU后初始化一下RTC就可以使用. 这个方法返回的是4个字节的DWORD, 需要按照格式准备数据.

/**
* 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;
}

FatFs参数的调整

在当前的移植中, 对以下参数进行了调整

  • FF_USE_STRFUNC 0 -> 1 因为要用 f_puts 方法往文件里写字符串
  • FF_USE_LFN 0 -> 1 开启长文件名支持, 否则文件名只支持8+3格式
  • FF_LBA64 0 -> 1 这个选项和下面的选项, 是用于开启对exfat格式的支持, 这样才能正常挂载和读写64GB的TF卡
  • FF_FS_EXFAT 0 -> 1

演示用例说明

接线

需要6根接线, 连线方式在演示用例的main.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
*
******************************************************************************/

演示代码的使用

正确连线后, 编译演示用例并烧录代码,

  • 连接串口观察输出
  • 在读卡器中插入SD卡(或TF卡)
  • 按RESET键
  • 可以观察到依次进行的初始化挂载, 展示文件列表, 检查并删除测试文件, 写入测试文件, 读取测试文件, 最后卸载SD/TF卡的过程

实际演示输出

2GB FAT TF卡

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卡

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

结束

以上是对W801/W806上移植R0.14b版本FatFs的说明, 演示用例因为是作为演示使用, 所以代码并未根据实际需要进行组织, 并且使用了大量的printf.

如果在项目中使用, fatfs_mmc.c 基本可以复用, 但是在main.c中的方法需要重新组织.

联盛德 HLK-W806 (十三): 运行FatFs读写FAT和exFat格式的SD卡/TF卡的更多相关文章

  1. 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  2. 联盛德 HLK-W806 (五): W801开发板上手报告

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  3. 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  4. 联盛德 HLK-W806 (三): 免按键自动下载和复位

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  5. 联盛德 HLK-W806 (七): 兼容开发板 LuatOS Air103

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  6. 联盛德 HLK-W806 (十): 在 CDK IDE开发环境中使用WM-SDK-W806

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  7. 联盛德 HLK-W806 (四): 软件SPI和硬件SPI驱动ST7735液晶LCD

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  8. 联盛德 HLK-W806 (六): I2C驱动SSD1306 128x64 OLED液晶屏

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  9. 联盛德 HLK-W806 (八): 4线SPI驱动SSD1306/SSD1315 128x64 OLED液晶屏

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

随机推荐

  1. [BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

    [HarekazeCTF2019]baby_rop2 题目附件 步骤: 例行检查,64位,开启了nx保护 运行了一下程序,了解大概的执行情况 64位ida载入,shift+f12检索程序里的字符串,没 ...

  2. Windows10计算文件SHA1 SHA256 SHA384 SHA512 or MD5

    目录 Windows10计算文件SHA1 SHA256 SHA384 SHA512 or MD5? 1.计算SHA1 2.计算SHA256 3.计算SHA384 4.计算SHA512 5.计算MD5 ...

  3. 浅析.netcore中的Configuration

    不管是.net还是.netcore项目,我们都少不了要读取配置文件,在.net中项目,配置一般就存放在web.config中,但是在.netcore中我们新建的项目根本就看不到web.config,取 ...

  4. c++计算 char数组CRC算法

    !!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!! 作者:mohist 我使用的OS:win7. 我使用的开发环境:VS2010 + sp1 算法源码: 1 uns ...

  5. 【LeetCode】120. Triangle 解题报告(Python)

    [LeetCode]120. Triangle 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址htt ...

  6. 【LeetCode】686. Repeated String Match 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  7. electron串口通信使用serialport安装报错

    1.报错信息没有安装python环境 1 gyp ERR! find Python 2 gyp ERR! find Python Python is not set from command line ...

  8. HTML网页设计基础笔记 • 【第4章 CSS3基础】

    全部章节   >>>> 本章目录 4.1 CSS 概述 4.1.1 CSS 简介 4.1.2 CSS3 基本语法 4.1.3 样式表的分类 4.2 CSS 基本选择器 4.2. ...

  9. SpringCloud集成Security安全(Eureka注册中心)

    1.说明 为了保护注册中心的服务安全, 避免恶意服务注册到Eureka, 需要对Eureka Server进行安全保护, 本文基于Spring Security方案, 为Eureka Server增加 ...

  10. Kafka版本介绍Version2.4.0

    1.说明 Kafka的版本从0.11.0.X到1.0.X, 再到2.0.X大版本, 其实没有经过几个版本, 只是版本号变化较大. 2.最新发布版本 截止本文章2020年2月22号发布时, Kafka ...