目录

关于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]REVERSE——[FlareOn4]login

    [FlareOn4]login 附件 步骤: 是个网页,直接打开,查看网页源码 百度了几个函数 charCodeAt(0)是返回当前字符的Unicode 编码 String.fromCharCode返 ...

  2. [BUUCTF]PWN——[ZJCTF 2019]Login

    [ZJCTF 2019]Login 附件 步骤: 例行检查,64位程序,开启了canary和nx保护 2. 试运行一下程序 3. 64位ida载入,检索字符串,在程序里找到了用户名admin和密码2j ...

  3. [BUUCTF]REVERSE——内涵的软件

    内涵的软件 附件 例行检查,32位程序 32位ida载入,shift+f12检索程序里的字符串 看到一个很像flag的字符串,拿去尝试一下,成功 flag{49d3c93df25caad8123213 ...

  4. Sysenter/Kifastcallentry hook 检测与恢复

    关于Sysenter.Kifastcallentry.中断之类的内核入口hook技术早就烂大街了,可是对hook的检测与恢复代码却是寥寥无几,一切抛开代码将原理的行为都是耍流氓. 下面以Sysente ...

  5. 自动化集成:Jenkins管理工具详解

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译.打包.镜像构建.部署等操作:本篇文章主要描述Jenkins基础用法. 一.Jenkins安装 1. ...

  6. 替换错误Table.ReplaceErrorValues(Power Query 之 M 语言)

    数据源: 任意数据源,数据中有错误值 目标: 将错误值替换为0 操作过程: [转换]>[替换值]>[替换错误] M公式: = Table.ReplaceErrorValues( 表, {{ ...

  7. 资源分配情况(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 资源的分配情况,无非就是未分配.已分配和过度分配三种,这些都可以通过各种视图查看,比如[资源]>[工作组规划器]视图 ...

  8. java多线程10:并发工具类CountDownLatch、CyclicBarrier和Semaphore

    在JDK的并发包(java.util.concurrent下)中给开发者提供了几个非常有用的并发工具类,让用户不需要再去关心如何在并发场景下写出同时兼顾线程安全性与高效率的代码. 本文分别介绍Coun ...

  9. Hystrix 监控可视化页面——Dashboard 流监控

    1.什么是Dashboard Hystrix-dashboard 是一款针对 Hystrix 进行实时监控的工具页面,通过 Hystrix Dashboard 我们可以在直观地看到各 Hystrix ...

  10. curl常用选项

    下载单个文件,默认将输出打印到标准输出中(STDOUT)中 curl http://www.centos.org 通过-o/-O选项保存下载的文件到指定的文件中:-o:将文件保存为命令行中指定的文件名 ...