本文主要介绍SD卡虚拟文件系统的配置,包括rtthread和裸板移植 rtthread包括手动添加系统文件和env自动配置两种方法 裸板是通过STM32CubeMX配置 最终都实现了SD卡的挂载和文件读写
一.rtthread
1.介绍
rt thread(后面简称rtt)中mmcsd架构如下图所示。 最上面一层还是rtt的设备驱动框架 第二层是mmcsd的块设备层,这里主要描述了sd或者mmc卡的块设备行为。但实际上在block_dev.c里没有太多复杂的操作,仅仅只是转发上层发过来的命令给下层 第三层是mmcsd的sd层,当然如果用的是mmc卡,那么这一层对应的是mmc.c。本文用sd卡做演示,所以选的sd.c。这一层主要描述了sd卡的行为,实现了部分SD卡的协议 第四层是mmcsd的core层,主要实现了MMC和SD中的通用协议以及mmcsd_detect线程。 最底层是芯片驱动层,最终所有的操作都要落实到controller的操作上。 原文链接:https://blog.csdn.net/u011280717/article/details/112777446
SD卡虚拟文件系统是在我之前的SPI虚拟文件系统的基础上做的,所以只会介绍SD卡配置部分,DFS配置跟之前的一样就不详细介绍了。
2.手动添加文件
2.1添加驱动文件
2.2.添加头文件
2.3.添加配置
rtconfig.h中添加SDIO相关配置
#define RT_USING_SDIO
#define RT_SDIO_STACK_SIZE 512
#define RT_SDIO_THREAD_PRIORITY 15
#define RT_MMCSD_STACK_SIZE 1024
#define RT_MMCSD_THREAD_PREORITY 22
#define RT_MMCSD_MAX_PARTITION 16
#define BSP_USING_SDIO
3.env自动添加文件
打开SD相关配置 打开硬件相关配置 打开文件系统配置
4.验证
虽然有报错,switching card to high speed failed! 我也不知道为什么,但是块设备已经有了,接下来对块设备进行挂载
5.挂载两个设备
程序实现了两种挂载,SPIFlash和SD卡挂载,不能同时都挂载在根目录下,所以先挂载SPI,然后建一个文件夹,在文件夹里挂载SD卡内容
新建一个文件thread_sdio.c list_device发现有两个块设备 w25q256_mount先挂载SPI,ls可以看见文件 mkdir SD创建文件夹SD cd SD进入文件夹SD sd0_mount挂载SD卡 可以看到挂载成功,可以看见SD卡信息
6.读写文件
此部分只操作了SD卡,所以SD卡挂载在根文件系统下,方便操作
#include <rtthread.h>
#include <rtdevice.h>
#include <dfs_fs.h>
#include <dfs_posix.h>
void sd0_mount(void)
{
rt_device_t dev;
dev = rt_device_find("sd0");
if(dev != RT_NULL)
{
if(dfs_mount("sd0", "/", "elm", 0, 0) == 0)
{
rt_kprintf("sd card mount to SD!\n");
}
else {
rt_kprintf("sd card mount to SD failed!\n");
}
}
}
MSH_CMD_EXPORT(sd0_mount, sd0 mount);
void sd0_unmount(void)
{
rt_device_t dev;
dev = rt_device_find("sd0");
if(dev != RT_NULL) {
if(dfs_unmount("/") == 0){
rt_kprintf("sd card unmount to SD!\n");
} else {
rt_kprintf("sd card unmount to SD failed!\n");
}
}
}
MSH_CMD_EXPORT(sd0_unmount, sd0 unmount);
void sd0_readwrite(void)
{
DIR *dirp;
int fd;
uint8_t rtext[100];
uint8_t wtext[] = "hello";
struct dirent *d;
dirp = opendir("/MUSIC");
if (dirp == RT_NULL)
{
rt_kprintf("open directory error!\n");
}
else
{
while ((d = readdir(dirp)) != RT_NULL)
{
rt_kprintf("found %s\n", d->d_name);
}
}
fd = open("hello.txt", O_RDWR|O_APPEND);
uint8_t n_write = write(fd,wtext,sizeof(wtext)-1);
if(n_write > 0)
{
rt_kprintf("write %d byte to file\n",n_write);
}
lseek(fd,0,SEEK_SET);
if(read(fd,rtext,sizeof(rtext)) > 0)
{
rt_kprintf("read hello.txt sucess\r\n");
rt_kprintf("hello.txt = %s\r\n",rtext);
}
close(fd);
closedir(dirp);
}
MSH_CMD_EXPORT(sd0_readwrite, sd0 read write);
注意:
打开文件后,先写入文件内容,再读文件内容,必须加lseek(fd,0,SEEK_SET),让光标回到文件头的位置读取文件,否则无法读取文件; 写文件时,注意写入字符大小,为sizeof()-1,sizeof会有结束符号,导致读取文件时,只能读取结束符前的内容
问题:
这个地方的转高速失败不知道为什么?
二、STM32CubeMX
1.时钟配置
卡时钟(SDIO_CK ):每个时钟周期在命令和数据线上传输 1 位命令或数据。 对于多媒体卡 V3.31 协议,时钟频率可以在 0MHz 至 20MHz 间变化; 对于多媒体卡 V4.0/4.2 协议,时钟频率可以在 0MHz 至 48MHz 间变化; 对于 SD 或 SD I/O 卡,时钟频率可以在 0MHz 至 25MHz间变化。 SDIO适配器时钟( SDIOCLK ):该时钟用于驱动 SDIO 适配器,其频率等于 AHB 总线频率( HCLK ),并用于产生 SDIO_CK 时钟。 AHB 总线接口时钟( HCLK/2 ):该时钟用于驱动 SDIO 的 AHB 总线接口,其频率为HCLK/2 。 前面提到,我们的SD 卡时钟( SDIO_CK ),根据卡的不同,可能有好几个区间,这就涉及到时钟频率的设置, SDIO_CK 与SDIOCLK 的关系为: SDIO_CK=SDIOCLK/(2+CLKDIV) 其中,SDIOCLK 为 HCLK ,一般是 168Mhz,而 CLKDIV 则是分配系数,可以通过 SDIO的 SDIO_CLKCR 寄存器进行设置(确保 SDIO_CK 不超过卡的最大操作频率)。 在SD 卡刚刚初始化的时候,其时钟频率( SDIO_CK )是不能超过 400Khz的,否则可能无法完成初始化。在初始化以后,就可以设置时钟频率到最大了(但不可超过 SD卡的最大操作时钟频率)。 注:当配置完发现无法moutSD卡,可以尝试加大CLKDIV值
SDIO_CK=SDIOCLK/(2+CLKDIV) =168/(2+5)=24Mhz,不知道算的对不对
2.SDIO
使能SDIO中断,这里的中断优先级默认不是5的,不知道需不需更改 需要配置DMA,否则后面配置FATFS无法运行,FreeRTOS也强制要求使用DMA
3.FATFS
Fatfs基本都是使用默认值 生成工程之前还要修改Project Manager->Project->Linker Setting中的最小堆栈大小,堆栈大小根据需求修改,但是太小就会无法挂载SD卡或者读写时失败,基本上默认值都是无法正常运行的 到这里就可以生成工程,测试一下SD卡是否正常,生成工程会有如下警告,可以忽略
4.代码
main函数中添加代码
#include "ff.h"
FATFS fs;
FIL fil;
uint32_t byteswritten;
uint32_t bytesread;
uint8_t wtext[] = "ok";
uint8_t rtext[100];
char filename[] = "/MUSIC/hello.txt";
int main(void)
{
DIR picdir;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_DMA2D_Init();
MX_FMC_Init();
MX_LTDC_Init();
MX_SDIO_SD_Init();
MX_FATFS_Init();
LTDC_Display_Dir(0);
LTDC_Select_Layer(0);
LTDC_Clear(BLACK);
POINT_COLOR=RED;
LCD_ShowString(10,40,460,32,32,"Apollo STM32F429IABCDEFGHIJKLMN");
LCD_ShowString(10,80,240,24,24,"LTDC TEST");
LCD_ShowString(10,110,240,16,16,"ATOM@ALIENTEK");
LCD_ShowString(10,130,240,16,16,"2021/7/6");
if(SD_Init())
{
LCD_ShowString(10,150,200,16,16,"SD Card Error!");
delay_ms(500);
LCD_ShowString(10,150,200,16,16,"Please Check! ");
delay_ms(500);
}
else
{
LCD_ShowString(10,150,200,16,16,"SD Card Sucess!");
delay_ms(500);
}
if(f_mount(&fs, "0:", 1))
LCD_ShowString(10,170,200,16,16,"mount error");
else
{
LCD_ShowString(10,170,200,16,16,"mount sucess");
if(f_opendir(&picdir,"0:/MUSIC"))
LCD_ShowString(10,190,240,16,16,"OPEN DIR ERROR!");
else
{
LCD_ShowString(10,190,240,16,16,"OPEN DIR SUCESS!");
if(f_open(&fil, filename, FA_OPEN_APPEND|FA_WRITE|FA_READ))
LCD_ShowString(10,210,200,16,16,"open file error ");
else
{
LCD_ShowString(10,210,200,16,16,"open file sucess");
if(f_write(&fil, wtext, sizeof(wtext)-1, (UINT*)&byteswritten))
LCD_ShowString(10,230,200,16,16,"write error!!!");
else
LCD_ShowString(10,230,200,16,16,"write sucess!!!");
f_lseek(&fil,0);
if(f_read(&fil, rtext, sizeof(rtext), (UINT*)&bytesread))
LCD_ShowString(10,250,200,16,16,"read error!!!");
else
{
LCD_ShowString(10,250,200,16,16,"read sucess!!!");
LCD_ShowString(10,270,460,16,16,rtext);
}
f_close(&fil);
}
f_closedir(&picdir);
}
}
f_mount(NULL, "0:", 0);
while (1)
{
}
}
sdio.c中添加代码
u8 SD_Init(void)
{
u8 SD_Error;
SD_Error=HAL_SD_Init(&hsd);
if(SD_Error!= 0) return 1;
SD_Error=HAL_SD_ConfigWideBusOperation(&hsd,SDIO_BUS_WIDE_4B);
if(SD_Error!= 0) return 2;
return 0;
}
可以把屏幕显示换成串口打印
|