目录
一、SD基础知识
1.1、SD卡分类
1.2、SD卡的物理结构
1.3、SD卡寄存器
1.4、SDIO协议简介
二、SD卡驱动
2.1、CubeMX配置
2.2、ENV配置
2.3、驱动测试
STM32F4里,只支持SD 2.0协议,SD和SDHC属于这个范畴,SDXC是4.0协议,所以不支持;另外SDHC卡,有些数据块大小是512,有的是1024,STM32F4只支持读取512大小的,意思是说,SDHC卡的一部分也不支持的。其他相关的技术,可以去SD卡协会网站查看
SD卡一般支持SDIO和SPI两种接口,STM32F429x 系列控制器的SDIO是不支持SPI 通信模式的,如果需要用到SPI 通信只能使用SPI 外设。SDIO 不管是从主机控制器向SD 卡传输,还是SD 卡向主机控制器传输都只以CLK 时钟线的上升沿为有效。SD 卡操作过程会使用两种不同频率的时钟同步数据,一个是识别卡阶段时钟频率FOD,最高为400kHz,另外一个是数据传输模式下时钟频率FPP,默认最高为25MHz,如果通过相关寄存器配置使SDIO 工作在高速模式,此时数据传输模式最高频率为50MHz
原本SD协议是用在存储上的,后来扩展了一个叫SDIO的协议(全名:Secure Digital Input and Output),这个协议属于外设接口,使得它能连接一些外设,例如:GPS、相机、Wi-Fi、调频广播、以太网、条形码读卡器、蓝牙等,从此跳出存储这个局限
MMC 卡可以说是SD 卡的前身,现阶段已经用得很少
SD I/O 卡本身不是用于存储的卡,它是指利用SDIO 传输协议的一种外设
为了能满足不同SDIO设备的驱动,比如 SD I/O设备的驱动、SD Card的驱动、MMC的驱动,rtthread提供了SDIO的设备驱动框架,路径为 \rt-thread\components\drivers\sdio,对上通过I/O设备管理层给应用程序提供标准的API接口,对下通过 \libraries\HAL_Drivers\drv_sdio.c/h 驱动完成对STM32 SDIO外设的配置及读写操作,在drv_sdio.c中并没有使用STM32提供的HAL库驱动,而是直接操作的寄存器,但是会调用CubeMX配置生成的SDIO时钟配置及引脚初始化函数 HAL_SD_MspInit,所以在添加SDIO驱动前,需要先通过CubeMX配置SDIO生成HAL_SD_MspInit函数。
1、硬件驱动使能
/*添加 BSP_USING_SDCARD 宏*/
--->Hardware Drivers Config
---> Onboard Periperal Drivers
---> Enable SDCARD (sdio)
/*添加 BSP_USING_SDIO宏*/
--->Hardware Drivers Config
---> On-chip Periperal Drivers
---> Enable SDIO
/*配置 SDIO设备驱动框架 如下图*/
--->RT-Thread Components Configuration
---> Device Drivers
---> Using SD/MMC device drivers
2、文件系统配置
SD卡一般需要文件系统的支持,有了文件系统的加持,SD卡操作才能更加灵活方便。由于我前面已经在SPI FLASH上创建了一个elm-FAT,并且挂载到了 根目录(/)下,如果要再添加一个文件系统,需要先开启 RomFS文件系统,在根目录下创建2个文件夹,然后再将SPI FLASH和SD卡挂载到这两个文件夹下,因为同一个目录下不能同时挂载2个文件系统!!所以需要在menuconfig中修改 “挂载的文件系统数量”、“支持的文件系统类型数量”,系统中使用了dev、RomFS、elm-FAT3种文件系统, 分别为dev + RomFS + SPI FLASH elm-FAT + SD卡 elm-FAT 共4个
elm-FAT文件系统配置
配置的扇区大小会用于配置FatFs的_MAX_SS,FatFs中_MIN_SS定义最小扇区大小,_MAX_SS定义最大扇区大小。都设置为512可以兼容所有SD卡和硬盘,但是在SPI FLASH上需要更大值。当_MAX_SS > _MIN_SS,FatFs被配置为扇区大小可变的并且必须在函数disk_ioctl中实现GET_SECTOR_SIZE命令。也就是在操作SPI FLSH时扇区大小为4096,而在操作SD卡时会为512
3、注册具体的文件系统到DFS中
1、devfs_init() ---> dfs_register(&_device_fs) /* 如果开启了RT_USING_DFS_DEVFS,则会在dfs_init中完成自动初始化*/
2、elm_init()/dfs_elm.c --> dfs_register(&dfs_elm) /* 初始化elm文件系统 ,已INIT_COMPONENT_EXPORT自动添加,不在需要手动调用*/
3、dfs_romfs_init()/dfs_romfs.c --> dfs_register(&_romfs) /* 初始化romfs文件系统,已INIT_COMPONENT_EXPORT自动添加,不在需要手动调用 */
4、块设备挂载
挂载是指将一个存储设备挂接到一个已存在的路径上。我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的路径上,然后通过这个路径来访问存储设备。这里分别将W25Q12864的"fs"分区挂载到 /w25q 目录下、SD卡挂载到 /sdcard目录下
#if defined(BSP_USING_SPI_FLASH)
static int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOG_CLK_ENABLE();
rt_soft_spi_device_attach("softspi1", "softspi10", GPIOG, GPIO_PIN_10);
if (RT_NULL == rt_sfud_flash_probe("W25Q64", "softspi10"))
{
return -RT_ERROR;
}
return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
#endif
#if defined(PKG_USING_FAL)
int fs_init(void)
{
/* partition initialized */
fal_init();
return 0;
}
INIT_COMPONENT_EXPORT(fs_init);
#endif
#if defined(RT_USING_DFS_ELMFAT)
#define FS_PARTITION_NAME "fs"
extern const struct romfs_dirent romfs_root;
int elm_fatfs_init(void)
{
/* partition initialized */
// elm_init();
//dfs_mkfs("elm", "fs"); /* 在fs块设备上创建elm文件系统*/
/* Create a block device on the "fs" partition of spi flash */
struct rt_device *flash_dev = fal_blk_device_create(FS_PARTITION_NAME);
if (flash_dev == NULL) {
LOG_D("Can't create a block device on '%s' partition", FS_PARTITION_NAME);
} else {
LOG_I("Create a block device on the %s partition of flash successful", FS_PARTITION_NAME);
}
#if defined(RT_USING_DFS_ROMFS)
/* mount ROMFS as root directory */
if (dfs_mount(RT_NULL, "/", "rom", 0, &(romfs_root)) == 0) {
LOG_I("ROMFS filesystem initialized!");
} else {
LOG_D("ROMFS filesystem initialized Failed!");
}
/* mount elm file system from "fs" partition of spi flash to /w25q. */
if (dfs_mount(FS_PARTITION_NAME, "/w25q", "elm", 0, 0) == 0){
LOG_I("FATFS filesystem initialized!");
} else {
LOG_E("Failed to initialize FATFS filesystem!");
LOG_D("You should create a filesystem on the block device first!");
}
#elif
/* mount elm file system from "fs" partition of spi flash to / */
if (dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0) {
LOG_I("FATFS filesystem initialized!");
} else {
LOG_E("Failed to initialize filesystem!");
LOG_D("You should create a filesystem on the block device first!");
}
#endif //RT_USING_DFS_ROMFS
return 0;
}
INIT_COMPONENT_EXPORT(elm_fatfs_init);
#endif
void sd_mount(void *parameter)
{
while (1)
{
rt_thread_mdelay(500);
if(rt_device_find("sd0") != RT_NULL)
{
if (dfs_mount("sd0", "/sdcard", "elm", 0, 0) == RT_EOK)
{
LOG_I("sd card mount to '/sdcard'");
break;
}
else
{
LOG_W("sd card mount to '/sdcard' failed!");
}
}
}
}
/w25q目录和/sdcard在挂载RomFS的时候会被自动创建
测试SD卡的读写速度