欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

ESP32 开发笔记(三)源码示例 9_SPI_SDCard 使用SPI总线实现TF卡文件系统示例

程序员文章站 2022-06-22 18:56:57
开发板购买链接https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111deb2Ij1As&ft=t&id=626366733674开发板简介开发环境搭建 windows源码示例: 0_Hello Bug (ESP_LOGX与printf) 工程模板/打印调试输出 1_LED LED亮灭控制 ......

开发板购买链接

https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111deb2Ij1As&ft=t&id=626366733674

开发板简介
开发环境搭建 windows
源码示例:
    0_Hello Bug (ESP_LOGX与printf)    工程模板/打印调试输出
    1_LED                                                    LED亮灭控制       
    2_LED_Task                                          使用任务方式控制LED
    3_LEDC_PWM                                      使用LEDC来控制LED实现呼吸灯效果
    4_ADC_LightR                                      使用ADC读取光敏电阻实现光照传感
    5_KEY_Short_Long                              按钮长按短按实现
    6_TouchPad_Interrupt                          电容触摸中断实现
    7_WS2812_RMT                                  RGB_LED彩虹变色示例
    8_DHT11_RMT                                    使用RMT实现读取DHT11温湿度传感器
    9_SPI_SDCard                                    使用SPI总线实现TF卡文件系统示例
    10_IIC_ADXL345                                使用IIC总线实现读取ADXL345角度加速度传感器
    11_IIC_AT24C02                                 使用IIC总线实现小容量数据储存测试
    12_IR_Rev_RMT                                使用RMT实现红外遥控接收扫码(NEC)
    13_IR_Send_RMT                              使用RMT实现红外数据发送(NEC)
    14_WIFI_Scan                                    附近WIFI信号扫描示例    
    15_WIFI_AP                                        创建软AP示例
    16_WIFI_AP_TCP_Server                  在软AP模式下实现TCP服务端
    17_WIFI_AP_TCP_Client                   在软AP模式下实现TCP客户端
    18_WIFI_AP_UDP                              在软AP模式下实现UDP通讯
    19_WIFI_STA                                      创建STA站模
    20_WIFI_STA_TCP_Server                在站模式STA下实现TCP服务端
    21_WIFI_STA_TCP_Client                 在站模式STA下实现TCP客户端
    22_WIFI_STA_UDP                            在站模式STA下实现UDP通讯
    23_LVGL_Test                                     LVGL图形库简单示例

Micro SD Card,原名Trans-flash Card(TF卡),2004年正式更名为Micro SD Card,由SanDisk(闪迪)公司发明,主要用于移动电话。
在Micro SD面市之前,手机制造商都采用嵌入式记忆体,虽然这类模组容易装设,然而有着无法应实际应潮流需求的困扰——容量被限制住了,无法再有升级空间。Micro SD仿效SIM卡的应用模式,即是同一张卡可以应用在不同型号的行动电话内,让行动电话制造商不用再为插卡式的研发设计而伤脑筋。 Micro SD卡足以堪称可移动式的储存IC。
Micro SD卡是一种极细小的快闪存储器卡,其格式源自SanDisk创造,原本这种记忆卡称为T-Flash,及后改称为Trans Flash;而重新命名为Micro SD的原因是因为被SD协会 (SDA) 采立。另一些被SDA采立的记忆卡包括Mini SD和SD卡。其主要应用于移动电话,但因它的体积微小和储存容量的不断提高,已经使用于GPS设备、便携式音乐播放器和一些快闪存储器盘中。它的体积为 15mm x 11mm x1mm ,差不多相等于手指甲的大小,是现时最细小的记忆卡。它也能通过SD转接卡来接驳于SD卡插槽中使用。 现时MicroSD卡提供128MB、256MB、512MB、1G、2G、4G、8G、16G、32G、64G、128G的容量(MWC 2014 世界移动通信大会期间,SanDisk(闪迪)打破了储存卡最高64GB容量的传统,正式发布了一款容量高达128GB的 Micro SD XC 储存卡。

SD卡的操作模式:SD卡模式(SDIO)、SPI模式(默认为SD模式)。

其中SD卡模式的信号线有:CLK、CMD、DAT0-DAT3,6根线。

SPI模式的信号线有:CS、CLK、MISO(DATAOUT)、MOSI(DATAIN),4根线。

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

  1. CLK 时钟同步线
  2. CMD 命令信号线,主机发出的命令以及从机对命令的响应都是通过这条线进行传输
  3. DAT[3:0] 表示4条数据线,主机和从机的数据都是从这四条数据线上传输

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

SD卡只能使用3.3V的I/O电平。SPI模式下信号线要加10-100K的上拉电阻。

SD卡有五个寄存器:

名称 宽度 描述
CID 128 卡标识寄存器
RCA 16 相对地址寄存器(Relative Card Address):本地系统中卡的地址,动态变化,在主机初始化的时候确定,SPI模式中没有。
CSD 128 卡描述数据:卡操作条件相关的信息数据
SCR 64 SD配置寄存器:SD卡特定信息数据
OCR 32 操作条件寄存器

SD卡的命令格式:

命令CMD0就是0,CMD16就是16,以此类推。

SPI命令格式为6字节构成,高位在前(MSB)。

字节1 字节2-5 字节6
7 6 5-0
0 1 command

字节1的最高2位固定是01,低6位为命令号(CMD0就是0,CMD16就是16,以此类推),字节2-5为命令参数,有些命令是没有参数的;字节6的高7位为CRC,最低位恒为1。

SD卡的命令总共有12类,下表为几个比较重要的命令:
SD卡的主要相关各种命令解析
CMD0: SD卡进入IDLE和复位SD卡
CMD1: 读OCR,是否为SD卡
CMD2: 获取卡CID信息
CMD3: 获取SD卡所分配的相对地址
CMD9: 获取SD卡的存储信息(容量、块大小等)
CMD12:停止传输操作
CMD13:获取卡的状态
CMD16:设置SD卡块大小
CMD17:使SD卡进入传输状态、读取单个块
CMD18: 使SD卡进入传输状态、读取多个块,直到收到CMD12为止
CMD24:使SD卡进入传输状态、写入单个块
CMD25: 使SD卡进入传输状态、写入多个块

命令 参数 回应 描述
CMD0(0X00) NONE R1 复位SD卡
CMD8(0X08) VHS+Check Pattern R7 发送接口状态命令
CMD9(0X09) NONE R1 读取卡特定数据寄存器
CMD10(0X0A) NONE R1 读取卡标志数据寄存器
CMD16(0X10) 块大小 R1 设置块大小(字节数)
CMD17(0X11) 地址 R1 读取一个块的数据
CMD24(0X18) 地址 R1 写入一个块的数据
CMD41(0X29) NONE R3 发送给主机容量支持信息和激活卡初始化过程
CMD55(0X37) NONE R1 告诉SD卡,下一个是特定应用命令
CMD58(0X3A) NONE R3 读取OCR寄存器

SD卡和单片机的通信采用发送应答机制:

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

 

ESP32 FAT 文件系统

ESP-IDF 使用 FatFs 库来实现 FAT 文件系统。FatFs 库位于 fatfs 组件中,您可以直接使用,也可以借助 C 标准库和 POSIX API 通过 VFS(虚拟文件系统)使用 FatFs 库的大多数功能。

此外,对 FatFs 库进行了扩展,新增了支持可插拔磁盘 I/O 调度层,从而允许在运行时将 FatFs 驱动映射到物理磁盘。

FatFs 与 VFS 配合使用

函数 esp_vfs_fat_register() 分配一个 FATFS 结构,并在 VFS 中注册特定路径前缀。如果文件路径以此前缀开头,则对此文件的后续操作将转至 FatFs API。函数 esp_vfs_fat_unregister_path() 删除在 VFS 中的注册,并释放 FATFS 结构。

多数应用程序在使用 esp_vfs_fat_ 函数时,采用如下步骤:

调用 esp_vfs_fat_register(),指定:

      挂载文件系统的路径前缀(例如,"/sdcard" 或 "/spiflash")
      FatFs 驱动编号
      一个用于接收指向 FATFS 结构指针的变量

调用 ff_diskio_register() 为上述步骤中的驱动编号注册磁盘 I/O 驱动;
调用 FatFs 函数 f_mount,或 f_fdisk, f_mkfs,并使用与传递到 esp_vfs_fat_register() 相同的驱动编号挂载文件系统。请参考 FatFs 文档,
调用 C 标准库和 POSIX API 对路径中带有步骤 1 中所述前缀的文件(例如,"/sdcard/hello.txt")执行打开、读取、写入、擦除、复制等操作。
您可以选择直接调用 FatFs 库函数,但需要使用没有 VFS 前缀的路径(例如,"/hello.txt");
关闭所有打开的文件;
调用 f_mount 并使用 NULL FATFS* 参数为与上述编号相同的驱动卸载文件系统;
调用 FatFs 函数 ff_diskio_register() 并使用 NULL ff_diskio_impl_t* 参数和相同的驱动编号来释放注册的磁盘 I/O 驱动。
调用 esp_vfs_fat_unregister_path() 并使用文件系统挂载的路径将 FatFs 从 NVS 中移除,并释放步骤 1 中分配的 FatFs 结构。
esp_vfs_fat_sdmmc_mount 和 esp_vfs_fat_sdmmc_unmount 这两个便捷函数对上述步骤进行了封装,并加入对 SD 卡初始化的处理,非常便捷。

一、硬件设计/原理

查看开发板原理图,TF卡使用SPI总线连接到ESP32主控,对信号线进行10K上拉。

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

二、程序设计

先引用必要头文件

// SD card and FAT filesystem example

#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "sdkconfig.h"

定义SPI总线GPIO

static const char *TAG = "TF Card Fat Example";
#define MOUNT_POINT "/sdcard"
#define SPI_DMA_CHAN    1

//在测试SD和SPI模式时,请记住,一旦在SPI模式下初始化了卡,在不接通卡电源的情况下就无法在SD模式下将其重新初始化。
#define PIN_NUM_MISO		19
#define PIN_NUM_MOSI		23
#define PIN_NUM_CLK			18
#define PIN_NUM_CS			5

主函数,测试文件系统,读取,写入、获取总容量、卡信息、剩余容量、更名、删除等操作


void app_main(void)
{
	esp_err_t ret;								// ESP错误定义
	sdmmc_card_t* card;							// SD / MMC卡信息结构
	const char mount_point[] = MOUNT_POINT;		// 根目录
	char ReadFileBuff[64];

	esp_vfs_fat_sdmmc_mount_config_t mount_config = {	// 文件系统挂载配置
		.format_if_mount_failed = false,				// 如果挂载失败:true会重新分区和格式化/false不会重新分区和格式化
		.max_files = 5,									// 打开文件最大数量
		.allocation_unit_size = 16 * 1024
	};

	printf("%s->Initializing SD card\r\n",TAG);
	printf("%s->Using SPI peripheralr\r\n",TAG);
	sdmmc_host_t host = SDSPI_HOST_DEFAULT();
	spi_bus_config_t bus_cfg = {
		.mosi_io_num = PIN_NUM_MOSI,
		.miso_io_num = PIN_NUM_MISO,
		.sclk_io_num = PIN_NUM_CLK,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = 4000,
	};
	// SPI总线初始化
	ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
	if (ret != ESP_OK) {
		printf("%s->Failed to initialize bus.\r\n",TAG);
		return;
	}
	// 这将初始化没有卡检测(CD)和写保护(WP)信号的插槽。
	// 如果您的主板有这些信号,请修改slot_config.gpio_cd和slot_config.gpio_wp。
	sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
	slot_config.gpio_cs = PIN_NUM_CS;
	slot_config.host_id = host.slot;
	// 挂载文件系统
	ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
	if (ret != ESP_OK) {
		if (ret == ESP_FAIL) {
			printf("%s->Failed to mount filesystem.%s\r\n",TAG,
				"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
		} else {
			printf("%s->Failed to initialize the card %s  (%s). ",TAG,
				"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
		}
		return;
	}
	// TF卡已经初始化,打印TF卡属性
	sdmmc_card_print_info(stdout, card);

    // Print FAT FS size information
    size_t bytes_total, bytes_free;
    get_fatfs_usage(&bytes_total, &bytes_free);
    printf("%s->FAT FS Total: %d MB, Free: %d MB \r\n",TAG, bytes_total / 1024, bytes_free / 1024);


	// 使用POSIX和C标准库函数来处理文件。
	printf("%s->Opening file\r\n",TAG);
	FILE* f = fopen(MOUNT_POINT"/hello.txt", "w");// 创建一个文件
	if (f == NULL) {
		printf("%s->Failed to open file for writing\r\n",TAG);
		return;
	}
	fprintf(f, "Hello %s!\n", card->cid.name);
	fclose(f);
	printf("%s->File written\r\n",TAG);

	// 重命名前检查目标文件是否存在
	struct stat st;
	if (stat(MOUNT_POINT"/foo.txt", &st) == 0) {
		unlink(MOUNT_POINT"/foo.txt");// 删除(如果存在)
	}

	// 重命名文件
	printf("%s->Renaming file\r\n",TAG);
	if (rename(MOUNT_POINT"/hello.txt", MOUNT_POINT"/foo.txt") != 0) {
		printf("%s->Rename failed\r\n",TAG);
		return;
	}

	// 读取文件
	printf("%s->Reading file\r\n",TAG);
	f = fopen(MOUNT_POINT"/foo.txt", "r");// 读取方式打开文件
	if (f == NULL) {
		printf("%s->Failed to open file for reading\r\n",TAG);
		return;
	}
	fgets(ReadFileBuff, sizeof(ReadFileBuff), f);	// 读取一行数据
	fclose(f);										// 关闭文件 
	char* pos = strchr(ReadFileBuff, '\n');			// 在字符串中查找换行
	if (pos) {
		*pos = '\0';								// 替换为结束符
	}
	printf("%s->Read from file: '%s'\r\n",TAG,ReadFileBuff);

	// 卸载分区并禁用SDMMC或SPI外设
	esp_vfs_fat_sdcard_unmount(mount_point, card);
	printf("%s->Card unmounted\r\n",TAG);
	//卸载总线
	spi_bus_free(host.slot);
}

三、下载测试

打开ESP-IDF Command Prompt

cd命令进入此工程目录

cd F:\ESP32_DevBoard_File\9_SPI_SDCard

查看电脑设备管理器中开发板的串口号

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

执行idf.py -p COM9 flash monitor从串口9下载并运行打开口显示设备调试信息   Ctrl+c退出运行,查看串口信息输出

测试卡为16G闪迪 class 10 TF卡

ESP32 开发笔记(三)源码示例 9_SPI_SDCard  使用SPI总线实现TF卡文件系统示例

 

本文地址:https://blog.csdn.net/cnicfhnui/article/details/108493320