联盛德 HLK-W806 (十三): 运行FatFs读写FAT和exFat格式的SD卡/TF卡
目录
- 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明
- 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明
- 联盛德 HLK-W806 (三): 免按键自动下载和复位
- 联盛德 HLK-W806 (四): 软件SPI和硬件SPI驱动ST7735液晶LCD
- 联盛德 HLK-W806 (五): W801开发板上手报告
- 联盛德 HLK-W806 (六): I2C驱动SSD1306 128x64 OLED液晶屏
- 联盛德 HLK-W806 (七): 兼容开发板 LuatOS Air103
- 联盛德 HLK-W806 (八): 4线SPI驱动SSD1306/SSD1315 128x64 OLED液晶屏
- 联盛德 HLK-W806 (九): 软件SPI和硬件SPI驱动ST7789V液晶LCD
- 联盛德 HLK-W806 (十): 在 CDK IDE开发环境中使用WM-SDK-W806
- 联盛德 HLK-W806 (十一): 软件SPI和硬件SPI驱动ST7567液晶LCD
- 联盛德 HLK-W806 (十二): Makefile组织结构和编译流程说明
- 联盛德 HLK-W806 (十三): 运行FatFs读写FAT和exFat格式的SD卡/TF卡
关于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卡的更多相关文章
- 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (五): W801开发板上手报告
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (三): 免按键自动下载和复位
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (七): 兼容开发板 LuatOS Air103
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (十): 在 CDK IDE开发环境中使用WM-SDK-W806
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (四): 软件SPI和硬件SPI驱动ST7735液晶LCD
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (六): I2C驱动SSD1306 128x64 OLED液晶屏
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
- 联盛德 HLK-W806 (八): 4线SPI驱动SSD1306/SSD1315 128x64 OLED液晶屏
目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...
随机推荐
- linux 编程随笔
Linux 命令: 在linux 系统中,所有的命令都是人为编写的程序,如 who 和 ls ,而且绝大多数都是C写的.在Linux 中增加新的命令是很简单的事,把程序的可执行文件放到以下目录就可以了 ...
- 资源分配(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 资源设置好以后,不能光摆着看,分配到各任务中去才是正道. 分配资源就需要回到与任务相关的视图了,比如[任务工作表]视图或者 ...
- HTTP状态码一览表
常见Http状态码大全 2018年03月16日 11:36:31 阅读数:153 一些常见的状态码为: 200 - 服务器成功返回网页404 - 请求的网页不存在503 - 服务不可用详细分解: 1x ...
- 【LeetCode】1419. 数青蛙 Minimum Number of Frogs Croaking (Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...
- 【LeetCode】788. Rotated Digits 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 【剑指Offer】翻转单词顺序列 解题报告(Python)
[剑指Offer]翻转单词顺序列 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...
- 1632 B君的连通
B国拥有n个城市,其交通系统呈树状结构,即任意两个城市存在且仅存在一条交通线将其连接.A国是B国的敌国企图秘密发射导弹打击B国的交通线,现假设每条交通线都有50%的概率被炸毁,B国希望知道在被炸毁之后 ...
- Rectangles(hdu2461)
Rectangles Time Limit: 5000/4000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 应用程序开发 WebApp NativeApp 微信小程序
Web Native App 微信小程序 WebApp是指基于Web的系统和应用,其作用是向广大的最终用户发布一组复杂的内容和功能.webapp 框架是一种简单的与WSGI兼容的网络应用程序框 ...
- MongoDB基本介绍与安装(1)
MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功 ...