Zephyr使用TF卡

Creative Commons
本作品采用知识共享署名

本文说明如何在zephyr下使用TF卡.

zephyr使用的device大多是内置几十K到几M Flash, 如果有较大量的数据需要本地存储就需要扩展外部Flash,对于数据存储的情况挂总线flash有io需求,挂SPI Flash容量单价又比较高。然后想起SDCard 有SPI Mode,当然就选它了。
本文的使用的device问nrf53832.

代码说明

原本打算移植或者重写SPI Mode 访问SD Card ,后来在制定API的时候要参考disk_access统一接口,发现zephyr已经有代码可以支援SPI Mode 访问SD CARD,这就省下不少功夫。
源代码参考

1
subsys/disk/disk_access_spi_sdhc.c

原理不做介绍,只是按照预定的方式通过SPI接口向TF卡发命令和读写数据,感兴趣可以看文末参考内容。

配置

修改DTS

在你板子的dts下添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&spi2 {
status = "okay";
sck-pin = <31>;
mosi-pin = <2>;
miso-pin = <3>;

cs-gpios = <&gpio0 30 0>;

sdhc0: sdhc@0 {
compatible = "zephyr,mmc-spi-slot";
reg = <0>;
status = "okay";
label = "SDHC0";
spi-max-frequency = <24000000>;
};

上面的dts内容解释如下:

  • tf卡(SDHC)使用spi2作为通信接口
  • spi2的pin配置情况是sck-31, mosi-2, miso-3,cs-pin30
  • spi通信速率为24M

修改配置文件

配置文件prj.conf添加如下内容

1
2
3
4
5
6
CONFIG_SPI_2=y
CONFIG_SPI_2_NRF_SPI=y
CONFIG_DISK_ACCESS=y
CONFIG_DISK_ACCESS_SDHC=y
CONFIG_DISK_ACCESS_SPI_SDHC=y
CONFIG_DISK_SDHC_VOLUME_NAME="SD"

在zephyr 上面SDHC是专为disk access访问用的,因此必须同时配置启动DISK ACCESS

使用

配置

zephyr上SDHC被放入了disk access,因此我们可以直接搭配文件系统使用,这里配置为FAT

1
2
3
4
5
6
#使用文件系统
CONFIG_FILE_SYSTEM=y
#使用FAT文件系统
CONFIG_FAT_FILESYSTEM_ELM=y
#启动文件系统shell,可选如果只是代码使用文件系统可以不开这项
CONFIG_FILE_SYSTEM_SHELL=y

代码使用文件系统

在app中建立如下代码,可以访问TF卡,详细请看注释说明

初始化disk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static const char *disk_pdrv = "SD";        
u64_t memory_size_mb;
u32_t block_count;
u32_t block_size;

//初始化disk
if (disk_access_init(disk_pdrv) != 0) {
LOG_ERR("Storage init ERROR!");
break;
}

//获取disk的sector数量
if (disk_access_ioctl(disk_pdrv,
DISK_IOCTL_GET_SECTOR_COUNT, &block_count)) {
LOG_ERR("Unable to get sector count");
break;
}
LOG_INF("Block count %u", block_count);

//获取sector大小
if (disk_access_ioctl(disk_pdrv,
DISK_IOCTL_GET_SECTOR_SIZE, &block_size)) {
LOG_ERR("Unable to get sector size");
break;
}

Mount文件系统

1
2
3
4
5
6
7
8
9
10
11
static const char *disk_mount_pt = "/SD:";      //mount点参看CONFIG_DISK_SDHC_VOLUME_NAME 
mp.mnt_point = disk_mount_pt;

int res = fs_mount(&mp);

if (res == FR_OK) {
printk("Disk mounted.\n");
lsdir(disk_mount_pt);
} else {
printk("Error mounting disk.\n");
}

文件操作

统一的文件操作接口,可以参考如何使用Zephyr文件系统一文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int res;
struct fs_dir_t dirp;
static struct fs_dirent entry;

//开启mount点文件夹
res = fs_opendir(&dirp, disk_mount_pt);
if (res) {
printk("Error opening dir %s [%d]\n", disk_mount_pt, res);
return res;
}

//列出mount点文件夹下所有的文件
printk("\nListing dir %s ...\n", disk_mount_pt);
for (;;) {
/* Verify fs_readdir() */
res = fs_readdir(&dirp, &entry);

/* entry.name[0] == 0 means end-of-dir */
if (res || entry.name[0] == 0) {
break;
}

if (entry.type == FS_DIR_ENTRY_DIR) {
printk("[DIR ] %s\n", entry.name);
} else {
printk("[FILE] %s (size = %zu)\n",
entry.name, entry.size);
}
}

//关闭开启的文件夹
fs_closedir(&dirp);

//创建文件
res = fs_open(&file, "/SD:/log/file.log");
//seek到末尾
res =fs_seek(&file, 0, FS_SEEK_END);
//写文件
res = fs_write(&file, data, len);
printk("%02x ", data[0]);
//sync文件
fs_sync(&file);
//关闭文件
fs_close(&file);

示例log

前文代码示log例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[00:00:00.007,934] <inf> spi_nrfx_spi: CS control inhibited (no GPIO device)
[00:00:00.076,965] <inf> sdhc_spi: Found a ~30436 MiB SDHC card.
[00:00:00.078,460] <inf> sdhc_spi: Manufacturer ID=3 OEM='SD' Name='ACLCD' Revision=0x80 Serial=0x1bd93ca8
[00:00:00.078,491] <inf> fstest: Block count 62333952

Sector size 512
Memory Size(MB) 1764
Disk mounted.

Listing dir /SD: ...
[DIR ] LOST.DIR
[DIR ] ANDROI~1
[DIR ] ANDROID
[DIR ] DATAST~1
[DIR ] ICON_P~1
...
[DIR ] LSSPOR~1
[DIR ] LIFESE~1
[DIR ] RECORD~1
open file success.

shell使用文件系统

只要配置CONFIG_FILE_SYSTEM_SHELL后就可以通过shell来通过操作文件系统来读写TF卡,详细请参考如何使用Zephyr文件系统一文

其它事项

  1. 因为zephyr内没有直接提供创建文件系统的API,所以需要先将TF卡在电脑上格式化为FAT32格式
  2. 目前测试FAT32下创建文件夹比较慢

参考

http://elm-chan.org/docs/mmc/mmc_e.html
https://www.microchip.com/forums/m530149.aspx