【STM32H7】第4章 ThreadX FileX文件系统移植到STM32H7(SD卡)

论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=100749

第4章   ThreadX FileX文件系统移植到STM32H7(SD卡)

本章节为大家讲解SD卡的ThreadX FileX文件系统移植。

目录

第4章   ThreadX FileX文件系统移植到STM32H7(SD卡)

4.1   初学者重要提示

4.2   SD卡硬件接口设计

4.3   SD卡基础知识

4.3.1      SD卡分类

4.3.2      SD卡容量及其使用的文件系统

4.3.3      SD卡总线速度和速度等级

4.4   各种存储卡区别

4.4.1      SD卡,miniSD卡,TF卡,MircoSD卡

4.4.2      SDIO卡

4.4.3      MMC卡,eMMC

4.4.4      CF卡

4.4.5      总体区别

4.5   关于SD卡内部是否自带擦写均衡

4.6   ThreadX FileX移植步骤

4.6.1      第1步,了解整体设计框架

4.6.2      第2步,添加FileX和SDMMC驱动到工程

4.6.3      第3步,添加工程路径

4.6.4      第4步,配置GPIO和时钟

4.6.5      第5步,MPU配置

4.6.6      第6步,FileX的配置文件fx_port.h设置

4.6.7      第7步,添加应用代码

4.7   ThreadX FileX应用代码测试

4.7.1      初始化FileX

4.7.2      挂载SD卡驱动

4.7.3      SD卡文件浏览

4.7.4      SD卡创建txt文件并写入数据

4.7.5      SD卡文件读取

4.7.6      SD卡创建文件夹

4.7.7      SD卡文件和文件夹删除

4.7.8      SD卡读写速度测试

4.8   ThreadX FileX移植接口文件fx_stm32_sdio_driver.c说明

4.8.1  磁盘驱动接口函数fx_stm32_sd_driver

4.8.2  磁盘状态函数check_sd_status

4.8.3  磁盘读函数sd_read_data

4.8.4  磁盘写函数sd_write_data

4.8.5  磁盘中断处理函数SDMMC1_IRQHandler

4.9   实验例程

4.10 总结


 

4.1   初学者重要提示

1、  SDMMC的相关知识点可以看第3章。

2、  操作SD卡是以扇区(512字节)为单位进行操作。

3、  SD卡联盟强烈强烈建议使用此软件来格式化SD/SDHC/SDXC卡,而不要使用各个操作系统随附的格式化工具。通常,操作系统附带的格式化工具可以格式化包括SD/SDHC/SDXC卡在内的各种存储介质,但是可能无法针对SD/SDHC/SDXC卡进行优化,并且可能导致性能降低。

软件下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96181

4、  STM32H7的SDMMC也支持eMMC:

  • 【普及贴】各个厂家eMMC读写速度,镁光,东芝,三星,ISSI和旺宏

http://www.armbbs.cn/forum.php?mod=viewthread&tid=95954

  •  H7的8线SDIO DMA驱动eMMC的裸机性能,读43MB/S,写18.8MB/S

http://www.armbbs.cn/forum.php?mod=viewthread&tid=95953

5、  支持128GB,64GB的大容量SD卡,需要大家使能ThreadX FileX的exFAT即可。

4.2   SD卡硬件接口设计

STM32H7驱动SD卡设计如下:

关于这个原理图,要了解到以下几个知识:

  •   大家自己设计推荐也接上拉电阻。
  •   这里采用SDMMC的4线方式。

4.3   SD卡基础知识

这里将SD卡相关的基础知识为大家做个普及。

4.3.1      SD卡分类

根据不同容量做的区分,主要包括Full SD,miniSD和microSD。

4.3.2      SD卡容量及其使用的文件系统

容量小于2GB(SD卡)使用FAT12或者FAT16,容量在2GB和32GB之间(SDHC卡)使用FAT32,容量大于32GB小于2TB(SDXC卡)使用exFAT。

4.3.3      SD卡总线速度和速度等级

SD卡速度:

SD卡速度等级:

4.4   各种存储卡区别

市面上的卡种类非常多,容易把人搞糊涂,这里将这些卡种类为大家做个区分:

4.4.1      SD卡,miniSD卡,TF卡,MircoSD卡

TF卡是MicroSD卡的另一种叫法,无需做区分。SD卡,miniSD卡,MircoSD卡其实是一种卡,区别是引脚使用上。

 

 

 

4.4.2      SDIO卡

SDIO卡就是使用SDIO外设来接SD卡。

而为什么叫SDIO,根据wiki百科说明,其实就是SD卡接口规范的扩展,带了输入输出功能,这个接口不仅可以接SD卡,还可以接其它外设,如条形码读卡器,WiFi,蓝牙,调制解调器等。

https://en.wikipedia.org/wiki/SD_card#SDIO_cards

对于STM32的SDIO来说,他就是指STM32的一个外设接口,不仅能够来接SD卡,还可以接其它外设。

4.4.3      MMC卡,eMMC

截止2018年,市场上已经没有设备内置MMC卡槽,但eMMC得到了广泛应用https://en.wikipedia.org/wiki/MultiMediaCard

4.4.4      CF卡

CF卡是早期最成功的存储卡格式之一,像MMC/SD卡都是后来才推出的。CF卡仍然很受欢迎卡之一,并得到许多专业设备和高端消费类设备的支持。截至2017年,佳能和尼康都将CompactFlash用于其旗舰数码相机。佳能还选择了CompactFlash作为其专业高清无带摄像机的记录介质。

基础规格:

实际效果:

 

4.4.5      总体区别

来自Wiki:https://en.wikipedia.org/wiki/SD_card#Micro

 

4.5   关于SD卡内部是否自带擦写均衡

根据网上搜的一个闪迪的规格书,里面说是带擦写均衡的:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=102891

4.6   ThreadX FileX移植步骤

ThreadX FileX的移植步骤如下:

4.6.1      第1步,了解整体设计框架

为了方便大家移植,需要大家先对移植好的工程有个整体认识:

4.6.2      第2步,添加FileX和SDMMC驱动到工程

本教程前面章节配套的例子都可以作为模板使用,在模板的基础上需要添加FileX文件,SDMMC驱动文件和SD卡驱动文件,大家可以直接从本章教程提供的例子里面复制。

  •   SD卡驱动文件bsp_sdio_sd.c和bsp_sdio_sd.h添加到自己的工程里面,路径不限。

配套例子是放在\User\bsp\src和\User\bsp\inc文件。

  •   SDMMC驱动文件stm32h7xx_hal_sd.c和stm32h7xx_ll_sdmmc.c

这个是STM32H7的HAL库自带的。

  •   FileX相关源文件。

大家可以将所有相关文件都复制到自己的工程里面,配套例子是放在\FileX。

4.6.3      第3步,添加工程路径

当前需要添加的两个FileX路径,大家根据自己添加的源文件位置,添加相关路径即可:

4.6.4      第4步,配置GPIO和时钟

根据大家使用SDMMC1或者SDMMC2配置相应时钟和GPIO,当前V7板子是用的SDMMC1:

__weak void BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params)
{
  GPIO_InitTypeDef gpio_init_structure;

  /* Enable SDIO clock */
  __HAL_RCC_SDMMC1_CLK_ENABLE();

  /* Enable GPIOs clock */
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  gpio_init_structure.Mode      = GPIO_MODE_AF_PP;
  gpio_init_structure.Pull      = GPIO_NOPULL;
  gpio_init_structure.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;

  /* D0(PC8), D1(PC9), D2(PC10), D3(PC11), CK(PC12), CMD(PD2) */
  /* Common GPIO configuration */
  gpio_init_structure.Alternate = GPIO_AF12_SDIO1;

  /* GPIOC configuration */
  gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
  HAL_GPIO_Init(GPIOC, &gpio_init_structure);

  /* GPIOD configuration */
  gpio_init_structure.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpio_init_structure);

  __HAL_RCC_SDMMC1_FORCE_RESET();
  __HAL_RCC_SDMMC1_RELEASE_RESET();

  /* NVIC configuration for SDIO interrupts */
  HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(SDMMC1_IRQn);
}

4.6.5      第5步,MPU配置

为了方便大家移植测试,我们这里直接关闭AXI SRAM的读Cache和写Cache(这样就跟使用STM32F1或者STM32F4系列里面的SRAM一样)。此配置是在bsp.c文件的MPU_Config函数里面实现:

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

#if 0
       /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

 #else
     /* 当前是采用下面的配置 */
    /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
#endif
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

4.6.6      第6步,FileX的配置文件fx_port.h设置

fx_port.h虽然是个移植的头文件,推荐在直接在这个文件里面做宏定义修改。

移植阶段,这个里面的配置可以不用动,无需任何修改。需要注意的是我们这里开启了exFAT,对应的宏定义:

#define FX_ENABLE_EXFAT

4.6.7      第7步,添加应用代码

这里将FileX大部分操作函数都做了应用,专门整理到了文件demo_sd_filex.c。通过串口命令进行操作,大家可以直接将这个文件添加到自己的工程里面。

另外注意,如果自己的工程里面没有移植我们其它的驱动,可以直接调用FileX的测试函数,比如浏览SD根目录文件,可以直接调用函数ViewRootDir。

4.7   ThreadX FileX应用代码测试

这里将FileX大部分函数都做了测试。注意,所有用到的函数在FileX官网都有详细说明。

4.7.1      初始化FileX

FileX的初始化通过调用函数fx_system_initialize()实现。

4.7.2      挂载SD卡驱动

挂载SD卡功能是通过函数fx_media_open实现,方便用户实现FileX驱动多个磁盘。

代码如下:

status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory, sizeof(media_memory));

4.7.3      SD卡文件浏览

SD卡根目录的文件浏览代码实现如下:

/*
*********************************************************************************************************
*    函 数 名: ViewRootDir
*    功能说明: 显示SD卡根目录下的文件名
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
extern SD_HandleTypeDef uSdHandle;
static void ViewRootDir(void)
{
    UINT status;
    UINT attributes;
    ULONG size;
    UINT year;
    UINT month;
    UINT day;
    UINT hour;
    UINT minute;
    UINT second;
    UINT cnt;
    ULONG64 available_bytes;
    
    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    }
    
    
    printf("属性        |  文件大小 | 短文件名 | 长文件名\r\n");
    for (cnt = 0; ;cnt++)
    {
        /* 读取目录项,索引会自动下移 */
        status = fx_directory_next_full_entry_find(&sdio_disk,
                                                    entry_name, 
                                                    &attributes, 
                                                    &size,
                                                    &year, &month, &day, 
                                                    &hour, &minute, &second);
        
        if (status != FX_SUCCESS || entry_name[0] == 0)
        {
            break;
        }

        if (entry_name[0] == '.')
        {
            continue;
        }

        /* 判断是文件还是子目录 */
        if (attributes & FX_DIRECTORY)
        {
            printf("目录  ");
        }
        else
        {
            printf("文件  ");
        }

        /* 打印文件大小, 最大4G */
        printf(" %10d", (int)size);


        printf("  %s\r\n", (char *)entry_name);    /* 长文件名 */
    }
    
    /* SD卡剩余容量大小 */
    status = fx_media_extended_space_available(&sdio_disk, &available_bytes);    

    if (status == FX_SUCCESS)
    {
        printf("SD卡剩余容量大小 -- %lldMB\r\n", available_bytes/1024/1024);
    }
    
    /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }
}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   代码里面加入了SD卡速度信息打印功能,方便大家了解自己的卡类型。通过查询全局结构体变量uSdHandle来实现。
  •   文件浏览通过函数fx_directory_next_full_entry_find实现。

4.7.4      SD卡创建txt文件并写入数据

代码实现如下:

/*
*********************************************************************************************************
*    函 数 名: CreateNewFile
*    功能说明: 在SD卡创建一个新文件,文件内容填写“www.armfly.com”
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CreateNewFile(void)
{
    UINT status;

    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    }
    
    /* 创建文件 */
    status =  fx_file_create(&sdio_disk, "armfly.txt");

    /* 检测状态 */
    if (status != FX_SUCCESS)
    {
        /* 失败的话,可以考虑二次创建 */
        if (status != FX_ALREADY_CREATED)
        {
            printf("创建文件失败\r\n");
        }
    }
    
    /* 打开文件  */
    status =  fx_file_open(&sdio_disk, &fx_file, "armfly.txt", FX_OPEN_FOR_WRITE);

    if (status != FX_SUCCESS)
    {
        printf("打开文件失败\r\n");
    }
    
    /* 设置到起始位置读取  */
    status =  fx_file_seek(&fx_file, 0);

    if (status != FX_SUCCESS)
    {
        printf("设置读取位置失败\r\n");       
    }


    /* 写入字符串到文件  */
    status =  fx_file_write(&fx_file, FsWriteBuf, strlen(FsWriteBuf));

    if (status == FX_SUCCESS)
    {
        printf("armfly.txt 文件写入成功\r\n");        
    }
    else
    {
        printf("armfly.txt 文件写入失败 %d\r\n", status); 
    }

    /* 关闭文件  */
    status =  fx_file_close(&fx_file);

    /* Check the file close status.  */
    if (status != FX_SUCCESS)
    {
        printf("关闭文件失败\r\n");    
    }

    /* 保证文件写入全部生效 */
    status = fx_media_flush(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("flush失败\r\n");   
    }
    
     /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }   
}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   函数fx_file_create用来创建文件。
  •   函数fx_file_open用来打开文件。
  •   函数fx_file_seek用来设置操作的起始位置。
  •   函数fx_file_write用来写入数据。
  •   函数fx_file_close用于关闭文件。
  •   函数fx_media_flush用于文件写入全部生效。
  •   函数fx_media_close用于卸载SD卡。

4.7.5      SD卡文件读取

代码实现如下:

/*
*********************************************************************************************************
*    函 数 名: ReadFileData
*    功能说明: 读取文件armfly.txt前128个字符,并打印到串口
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void ReadFileData(void)
{
    UINT status;
    ULONG bw;
    
    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    }
    

    /* 打开文件  */
    status =  fx_file_open(&sdio_disk, &fx_file, "armfly.txt", FX_OPEN_FOR_READ);

    if (status != FX_SUCCESS)
    {
        printf("打开文件失败\r\n");
    } 

    /* 设置到起始位置读取  */
    status =  fx_file_seek(&fx_file, 0);

    if (status != FX_SUCCESS)
    {
        printf("设置读取位置失败\r\n");       
    }

    /* 读取文件 */
    status =  fx_file_read(&fx_file, FsReadBuf, sizeof(FsReadBuf), &bw);

    printf("--%sReadLen = %d\r\n", FsReadBuf, (int)bw);

    if ((status != FX_SUCCESS))
    {
        printf("读取失败\r\n");     
    }    
    
    /* 关闭文件  */
    status =  fx_file_close(&fx_file);

    if (status != FX_SUCCESS)
    {
        printf("关闭文件失败\r\n");    
    }
    
     /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }   
}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   函数fx_file_create用来创建文件。
  •   函数fx_file_open用来打开文件。
  •   函数fx_file_seek用来设置操作的起始位置。
  •   函数fx_file_read用来读取数据。
  •   函数fx_file_close用于关闭文件。
  •   函数fx_media_flush用于文件写入全部生效。
  •   函数fx_media_close用于卸载SD卡。

4.7.6      SD卡创建文件夹

代码实现如下:

/*
*********************************************************************************************************
*    函 数 名: CreateDir
*    功能说明: 在SD卡根目录创建Dir1和Dir2目录,在Dir1目录下创建子目录Dir1_1
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CreateDir(void)
{
    UINT status;
    
    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    } 

    /* 创建目录/Dir1 */
    status = fx_directory_create(&sdio_disk, "Dir1");
    
    if (status == FX_SUCCESS)
    {
        printf("文件夹Dir1创建成功\r\n");
    } 
    else if (status == FX_ALREADY_CREATED)
    {
        printf("Dir1 目录已经存在(%d)\r\n", status);
    }
    else
    {
        printf("fx_directory_create Dir1 失败 (%d)\r\n", status);
        return;
    }  
    
    /* 创建目录/Dir2 */
    status = fx_directory_create(&sdio_disk, "Dir2");
    
    if (status == FX_SUCCESS)
    {
        printf("文件夹Dir2创建成功\r\n");
    } 
    else if (status == FX_ALREADY_CREATED)
    {
        printf("Dir2 目录已经存在(%d)\r\n", status);
    }
    else
    {
        printf("fx_directory_create Dir2 失败 (%d)\r\n", status);
        return;
    }     

    /* 创建子目录 /Dir1/Dir1_1       注意:创建子目录Dir1_1时,必须先创建好Dir1 ? */    
    status = fx_directory_create(&sdio_disk, "Dir1/Dir1_1");
    
    if (status == FX_SUCCESS)
    {
        printf("文件夹Dir1/Dir1_1创建成功\r\n");
    } 
    else if (status == FX_ALREADY_CREATED)
    {
        printf("Dir1/Dir1_1 目录已经存在(%d)\r\n", status);
    }
    else
    {
        printf("fx_directory_create Dir1/Dir1_1 失败 (%d)\r\n", status);
        return;
    }  
    
    /* 保证文件写入全部生效 */
    status = fx_media_flush(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("flush失败\r\n");   
    }
    
     /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }  
}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   函数fx_directory_create用于创建文件夹。
  •   函数fx_media_flush用于文件写入全部生效。
  •   函数fx_media_close用于卸载SD卡。

4.7.7      SD卡文件和文件夹删除

代码实现如下:

/*
*********************************************************************************************************
*    函 数 名: DeleteDirFile
*    功能说明: 删除SD卡根目录下的 armfly.txt 文件和 Dir1,Dir2 目录
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void DeleteDirFile(void)
{
    UINT status;
    UINT i;
    char path[50];
    
    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    }

    /* 删除目录/Dir1 【因为还存在目录非空(存在子目录),所以这次删除会失败】*/
    status = fx_directory_delete(&sdio_disk, "Dir1");
    
    if (status == FX_SUCCESS)
    {
        printf("删除目录Dir1成功\r\n");
    }
    else if (status == FX_NOT_FOUND)
    {
        printf("没有发现文件或目录 :%s\r\n", "/Dir1");
    }
    else
    {
        printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", status);
    }
    
    
     /* 先删除目录/Dir1/Dir1_1 */  
    status = fx_directory_delete(&sdio_disk, "Dir1/Dir1_1");
    
    if (status == FX_SUCCESS)
    {
        printf("删除目录Dir1/Dir1_1成功\r\n");
    }
    else if (status == FX_NOT_FOUND)
    {
        printf("没有发现文件或目录 :%s\r\n", "Dir1/Dir1_1");
    }
    else
    {
        printf("删除Dir1/Dir1_1失败(错误代码 = %d) 文件只读或目录非空\r\n", status);
    }    
    
    /* 删除目录/Dir1*/  
    status = fx_directory_delete(&sdio_disk, "Dir1");
    
    if (status == FX_SUCCESS)
    {
        printf("删除目录Dir1成功\r\n");
    }
    else if (status == FX_NOT_FOUND)
    {
        printf("没有发现文件或目录 :%s\r\n", "Dir1");
    }
    else
    {
        printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空\r\n", status);
    }   

    /* 删除目录/Dir2*/  
    status = fx_directory_delete(&sdio_disk, "Dir2");
    
    if (status == FX_SUCCESS)
    {
        printf("删除目录Dir2成功\r\n");
    }
    else if (status == FX_NOT_FOUND)
    {
        printf("没有发现文件或目录 :%s\r\n", "Dir2");
    }
    else
    {
        printf("删除Dir2失败(错误代码 = %d) 文件只读或目录非空\r\n", status);
    }   

    /* 删除文件 armfly.txt */
    status = fx_file_delete(&sdio_disk, "armfly.txt");
    
    if (status == FX_SUCCESS)
    {
        printf("删除目录armfly.txt成功\r\n");
    }
    else if (status == FX_NOT_FOUND)
    {
        printf("没有发现文件或目录 :%s\r\n", "armfly.txt");
    }
    else
    {
        printf("删除armfly.txt失败(错误代码 = %d) 文件只读或目录非空\r\n", status);
    }   

    /* 删除文件 speed1.txt */
    for (i = 0; i < 20; i++)
    {
        sprintf(path, "Speed%02d.txt", i);/* 每写1次,序号递增 */    
        
        status = fx_file_delete(&sdio_disk, path);
    
        if (status == FX_SUCCESS)
        {
            printf("删除文件%s成功\r\n", path);
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件:%s\r\n", path);
        }
        else
        {
            printf("删除%s文件失败(错误代码 = %d) 文件只读或目录非空\r\n", path, status);
        }   
    }

     /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }  
}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   函数fx_directory_delete用于删除文件夹。
  •   函数fx_file_delete用于删除文件。
  •   函数fx_media_close用于卸载SD卡。

4.7.8      SD卡读写速度测试

代码实现如下,主要是方便大家测试SD卡的读写性能。

/*
*********************************************************************************************************
*    函 数 名: WriteFileTest
*    功能说明: 测试文件读写速度
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
static void WriteFileTest(void)
{
    UINT status;
    char path[64]; 
    
    ULONG bw;
    uint32_t i,k;
    uint32_t runtime1,runtime2,timelen;
    uint8_t err = 0;
    static uint8_t s_ucTestSn = 0;

    
    for (i = 0; i < sizeof(g_TestBuf); i++)
    {
        g_TestBuf[i] = (i / 512) + '0';
    }
    
    
    /* 挂载SD卡 */
status =  fx_media_open(&sdio_disk, "STM32_SDIO_DISK", fx_stm32_sd_driver, 0, media_memory,
 sizeof(media_memory));

    if (status != FX_SUCCESS)
    {
        printf("挂载文件系统失败 -- %d\r\n", status);
        return;
    }
    
    /* 打开文件 */
    sprintf(path, "Speed%02d.txt", s_ucTestSn++); /* 每写1次,序号递增 */    
    
    /* 写一串数据 */
    printf("开始写文件%s %dKB ...\r\n", path, TEST_FILE_LEN / 1024);
    
    /* 创建文件 */
    status =  fx_file_create(&sdio_disk, path);

    /* 检测状态 */
    if (status != FX_SUCCESS)
    {
        /* 失败的话,可以考虑二次创建 */
        if (status != FX_ALREADY_CREATED)
        {
            printf("创建文件失败\r\n");
        }
    }
    
    /* 打开文件  */
    status =  fx_file_open(&sdio_disk, &fx_file, path, FX_OPEN_FOR_WRITE);

    if (status != FX_SUCCESS)
    {
        printf("打开文件失败\r\n");
    }
    
    /* 设置到起始位置读取  */
    status =  fx_file_seek(&fx_file, 0);

    if (status != FX_SUCCESS)
    {
        printf("设置读取位置失败\r\n");       
    }
    
    runtime1 = bsp_GetRunTime();    /* 读取系统运行时间 */
    for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
    {
        /* 写入字符串到文件  */
        status =  fx_file_write(&fx_file, g_TestBuf, sizeof(g_TestBuf));

        if (status == FX_SUCCESS)
        {
            if (((i + 1) % 8) == 0)
            {
                printf(".");
            }     
        }
        else
        {
            err = 1;
            printf("%s文件写失败\r\n", path);
            break; 
        }
    }
    runtime2 = bsp_GetRunTime();    /* 读取系统运行时间 */
    
    if (err == 0)
    {
        timelen = (runtime2 - runtime1);
        printf("\r\n  写耗时 : %dms   平均写速度 : %dB/S (%dKB/S)\r\n",
                timelen,
                (TEST_FILE_LEN * 1000) / timelen,
                ((TEST_FILE_LEN / 1024) * 1000) / timelen);
    }

    /* 关闭文件  */
    status =  fx_file_close(&fx_file);

    /* Check the file close status.  */
    if (status != FX_SUCCESS)
    {
        printf("关闭文件失败\r\n");    
    }

    /* 保证文件写入全部生效 */
    status = fx_media_flush(&sdio_disk);
    
    /* Check the file close status.  */
    if (status != FX_SUCCESS)
    {
        printf("fx_media_flush失败\r\n");    
    }
    
    
    /* 开始读文件测试 */
    printf("开始读文件 %dKB ...\r\n", TEST_FILE_LEN / 1024);
    
      /* 打开文件  */
    status =  fx_file_open(&sdio_disk, &fx_file, path, FX_OPEN_FOR_READ);

    if (status != FX_SUCCESS)
    {
        printf("打开文件失败\r\n");
    }
    
    /* 设置到起始位置读取  */
    status =  fx_file_seek(&fx_file, 0);

    if (status != FX_SUCCESS)
    {
        printf("设置读取位置失败\r\n");       
    }
    
    runtime1 = bsp_GetRunTime();    /* 读取系统运行时间 */
    for (i = 0; i < TEST_FILE_LEN / BUF_SIZE; i++)
    {
        /* 写入字符串到文件  */
        
        status =  fx_file_read(&fx_file, g_TestBuf, sizeof(g_TestBuf), &bw);
        if (status == FX_SUCCESS)
        {
            if (((i + 1) % 8) == 0)
            {
                printf(".");
            }

            /* 比较写入的数据是否正确,此语句会导致读卡速度结果降低到 3.5MBytes/S */
            for (k = 0; k < sizeof(g_TestBuf); k++)
            {
                if (g_TestBuf[k] != (k / 512) + '0')
                {
                      err = 1;
                    printf("Speed1.txt 文件读成功,但是数据出错\r\n");
                    break;
                }
            }
            if (err == 1)
            {
                break;
            }
        }
        else
        {
               err = 1;
            printf("Speed1.txt 文件读失败\r\n");
            break; 
        }
    }
    runtime2 = bsp_GetRunTime();    /* 读取系统运行时间 */

    if (err == 0)
    {
        timelen = (runtime2 - runtime1);
        printf("\r\n  读耗时 : %dms   平均读速度 : %dB/S (%dKB/S)\r\n", timelen,
            (TEST_FILE_LEN * 1000) / timelen, ((TEST_FILE_LEN / 1024) * 1000) / timelen);
    }
    
     /* 卸载SD卡 */
    status =  fx_media_close(&sdio_disk);

    if (status != FX_SUCCESS)
    {
        printf("卸载文件系统卸载失败 -- %d\r\n", status);
    }  

}
  •   fx_media_open可以上电后仅调用一次,我们这里是为了测试方式,使用前挂载,使用完毕后卸载。
  •   为了实现更高性能的测试,大家可以加大宏定义

#define BUF_SIZE                           (32*1024)            /* 每次读写SD卡的最大数据长度 */

设置的缓冲大小,比如设置为64KB进行测试。

4.8   ThreadX FileX移植接口文件fx_stm32_sdio_driver.c说明

这里将FileX的底层接口文件fx_stm32_sdio_driver.c的实现为大家简单做个说明。

4.8.1  磁盘驱动接口函数fx_stm32_sd_driver

代码如下:

/**
  * @brief This function is the entry point to the STM32 SDIO disk driver.     */
 /*        It relies on the STM32 peripheral library from ST.
  * @param None
  * @retval None
  */
VOID  fx_stm32_sd_driver(FX_MEDIA *media_ptr)
{
    int32_t status;
    ULONG       partition_start;
    ULONG       partition_size;

#if (FX_DRIVER_CALLS_BSP_SD_INIT == 0)
    is_initialized = 1; /* the SD  was initialized by the application*/
#endif
//TX_INTERRUPT_SAVE_AREA
   /* before performing any operation, check the status of the SDMMC */
    if (is_initialized == 1)
    {
      if (check_sd_status() != BSP_ERROR_NONE)
      {
          media_ptr->fx_media_driver_status =  FX_IO_ERROR;
          return;
      }
    }

    /* Process the driver request specified in the media control block.  */
    switch(media_ptr->fx_media_driver_request)
    {
        case FX_DRIVER_INIT:
        {
#if (FX_DRIVER_CALLS_BSP_SD_INIT == 1)
            /* Initialize the SD instance */
            if (is_initialized == 0)
            {
                status = BSP_SD_Init();

                if (status == BSP_ERROR_NONE)
                {
                    is_initialized = 1;
#endif
                    /* create a binary semaphore to check the DMA transfer status */
                    if (tx_semaphore_create(&transfer_semaphore, "sdmmc dma transfer semaphore", 0) !=
 TX_SUCCESS)
                    {
                        media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                    }
                    else
                    {
                        media_ptr->fx_media_driver_status =  FX_SUCCESS;
                    }
#if (FX_DRIVER_CALLS_BSP_SD_INIT == 1)
                }
                else
                {
                    media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                }
            }
#endif
            break;
        }

        case FX_DRIVER_UNINIT:
        {
            tx_semaphore_delete(&transfer_semaphore);

#if (FX_DRIVER_CALLS_BSP_SD_INIT == 1)
            BSP_SD_DeInit();
            is_initialized = 0;
#endif
            /* Successful driver request.  */
           media_ptr->fx_media_driver_status = FX_SUCCESS;
           break;
        }

        case FX_DRIVER_READ:
        {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            //TX_DISABLE /* disable interrupts */
           if ((ULONG)(media_ptr->fx_media_driver_buffer) & 0x3)
           {
              if (sd_read_data(media_ptr, media_ptr->fx_media_driver_logical_sector +
 media_ptr->fx_media_hidden_sectors,
                                media_ptr->fx_media_driver_sectors, 1) == FX_SUCCESS)
              {
                  media_ptr->fx_media_driver_status = FX_SUCCESS;
              }
           }
           else
           {
               if (sd_read_data(media_ptr, media_ptr->fx_media_driver_logical_sector +
 media_ptr->fx_media_hidden_sectors,
                       media_ptr->fx_media_driver_sectors, 0) == FX_SUCCESS)
               {
                   media_ptr->fx_media_driver_status = FX_SUCCESS;
               }
           }
           //TX_RESTORE /* restore interrupts */

            break;
        }

        case FX_DRIVER_WRITE:
        {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            //TX_DISABLE /* disable interrupts */

         /* the buffer is unaligned use a scratch buffer for the write operation */
         if ((ULONG)(media_ptr->fx_media_driver_buffer) & 0x3)
         {
              if (sd_write_data(media_ptr, media_ptr->fx_media_driver_logical_sector +
 media_ptr->fx_media_hidden_sectors,
                   media_ptr->fx_media_driver_sectors, 1) == FX_SUCCESS)
              {
                  media_ptr->fx_media_driver_status = FX_SUCCESS;
              }
           }
           else
           {
               if (sd_write_data(media_ptr, media_ptr->fx_media_driver_logical_sector + 
media_ptr->fx_media_hidden_sectors,
                       media_ptr->fx_media_driver_sectors, 0) == FX_SUCCESS)
               {
                   media_ptr->fx_media_driver_status = FX_SUCCESS;
               }
           }
           //TX_RESTORE /* restore interrupts */

            break;
        }

        case FX_DRIVER_FLUSH:
        {
            /* Return driver success.  */
           media_ptr->fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_ABORT:
        {
            /* Return driver success.  */
           media_ptr->fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_BOOT_READ:
        {
#if (ENABLE_CACHE_MAINTENANCE == 1)
            SCB_InvalidateDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer,
 media_ptr->fx_media_driver_sectors * DEFAULT_SECTOR_SIZE);
#endif
            /* the boot sector is the sector 0 */
            status = BSP_SD_ReadBlocks_DMA((uint32_t*)media_ptr->fx_media_driver_buffer, 0, 1);

            if (status != BSP_ERROR_NONE)
            {
                media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                break;
            }

            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
            {
                media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                break;
            }

            /* Check if the sector 0 is the actual boot sector, otherwise calculate the offset into it.
            Please note that this should belong to higher level of MW to do this check and it is here
            as a temporary work solution */

            partition_start =  0;

            status =  _fx_partition_offset_calculate(media_ptr -> fx_media_driver_buffer, 0,
                                                                &partition_start, &partition_size);

            /* Check partition read error.  */
            if (status)
            {
                /* Unsuccessful driver request.  */
                media_ptr -> fx_media_driver_status =  FX_IO_ERROR;
                return;
            }

            /* Now determine if there is a partition...   */
            if (partition_start)
            {

                if (check_sd_status() != BSP_ERROR_NONE)
                {
                    media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                    return;
                }

                /*Invalidate cache before DMA read */
#if (ENABLE_CACHE_MAINTENANCE == 1)
            SCB_InvalidateDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer,
 media_ptr->fx_media_driver_sectors * DEFAULT_SECTOR_SIZE);
#endif

                /* Yes, now lets read the actual boot record.  */
                status = BSP_SD_ReadBlocks_DMA((uint32_t*)media_ptr -> fx_media_driver_buffer,
 partition_start , 1);

                /* Check status of SDIO Read.  */
                if (status != BSP_ERROR_NONE)
                {

                    /* Unsuccessful driver request.  */
                    media_ptr -> fx_media_driver_status =  FX_IO_ERROR;
                    return;
                }

                /* Wait for Rx Transfer completion */
                if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
                {
                    media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                    break;
                }
            }

            /* Successful driver request.  */
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_BOOT_WRITE:
        {
#if (ENABLE_CACHE_MAINTENANCE == 1)
            SCB_CleanDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer,
 media_ptr->fx_media_driver_sectors * DEFAULT_SECTOR_SIZE);
#endif
            status = BSP_SD_WriteBlocks_DMA((uint32_t*)media_ptr->fx_media_driver_buffer, 0, 1);
            if (status == BSP_ERROR_NONE)
            {
                if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) == TX_SUCCESS)
                {
                    media_ptr->fx_media_driver_status =  FX_SUCCESS;
                }
                else
                {
                    media_ptr->fx_media_driver_status =  FX_IO_ERROR;
                }
            }
            break;
        }

        default:
        {
           media_ptr->fx_media_driver_status =  FX_IO_ERROR;
            break;
        }
    }
}

此函数实现了存储设备初始化,读写等操作,供存储设备挂载函数fx_media_open调用。

  •   FX_DRIVER_INIT消息

用于实现存储设备初始化。

  •   FX_DRIVER_UNINIT消息

用于实现存储设备卸载。

  •   FX_DRIVER_READ消息

用于实现存储设备数据读取操作。

  •   FX_DRIVER_WRITE消息

用于实现存储设备数据写入操作

  •   FX_DRIVER_FLUSH消息

刷新操作,用于保证数据全部写入完毕。

  •   FX_DRIVER_BOOT_READ消息

用于实现boot扇区sector 0数据读取。

  •   FX_DRIVER_BOOT_WRITE消息

用于实现boot扇区sector 0数据写入。

4.8.2  磁盘状态函数check_sd_status

供接口函数fx_stm32_sd_driver调用,代码如下:

static int32_t check_sd_status()
{
    uint32_t start = tx_time_get();

    while (tx_time_get() - start < DEFAULT_TIMEOUT)
    {
      if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
      {
        return BSP_ERROR_NONE;
      }
    }

    return BSP_ERROR_BUSY;
}

4.8.3  磁盘读函数sd_read_data

供接口函数fx_stm32_sd_driver调用,下面代码中最关键的处理是形参use_scratch_buffer的4字节对齐问题(SDMMC自带的IDMA需要4字节对齐),如果use_scratch_buffer地址是4字节对齐的,不做处理,如果不是对齐,通过复制到一个4字节对齐的缓冲里面做DMA传递。这个是理解下面代码的关键。

/**
  * @brief Read buffer using BSP SD API taking into account the scratch buffer
  * @param FX_MEDIA *media_ptr a pointer the main FileX structure
  * @param ULONG start_sector first sector to start reading from
  * @param UINT num_sectors number of sectors to be read
  * @param UINT use_scratch_buffer to enable scratch buffer usage or not.
  * @retval FX_SUCCESS on success FX_BUFFER_ERROR otherwise
  */

static UINT sd_read_data(FX_MEDIA *media_ptr, ULONG start_sector, UINT num_sectors, UINT use_scratch_buffer)
{
    INT i = 0;
    UINT status;
    if (use_scratch_buffer)
    {
        memset(scratch, '\0', DEFAULT_SECTOR_SIZE);
        for (i = 0; i < num_sectors; i++)
        {
            status = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, start_sector++, 1);

            if (status != BSP_ERROR_NONE)
            {
                status = FX_IO_ERROR;
                break;
            }

            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
            {
                status = FX_BUFFER_ERROR;
                break;
            }
            else
            {
#if (ENABLE_CACHE_MAINTENANCE == 1)
                SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, DEFAULT_SECTOR_SIZE);
#endif
                _fx_utility_memory_copy(scratch, media_ptr->fx_media_driver_buffer, DEFAULT_SECTOR_SIZE);
                media_ptr->fx_media_driver_buffer += DEFAULT_SECTOR_SIZE;
            }
        }

        if (i == num_sectors)
        {
            status = FX_SUCCESS;
        }
    }
    else
    {
        status = BSP_SD_ReadBlocks_DMA((uint32_t*)media_ptr->fx_media_driver_buffer, start_sector,
 num_sectors);

        if (status == BSP_ERROR_NONE)
        {
            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) == TX_SUCCESS)
            {
#if (ENABLE_CACHE_MAINTENANCE == 1)
                SCB_InvalidateDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer, 
num_sectors * DEFAULT_SECTOR_SIZE);
#endif
                status = FX_SUCCESS;
            }
            else
            {
                status =  FX_BUFFER_ERROR;
            }
        }
    }

    return status;
}

4.8.4  磁盘写函数sd_write_data

供接口函数fx_stm32_sd_driver调用,下面代码中最关键的处理是形参use_scratch_buffer的4字节对齐问题(SDMMC自带的IDMA需要4字节对齐),如果use_scratch_buffer地址是4字节对齐的,不做处理,如果不是对齐,通过复制到一个4字节对齐的缓冲里面做DMA传递。这个是理解下面代码的关键。

/**
  * @brief write buffer using BSP SD API taking into account the scratch buffer
  * @param FX_MEDIA *media_ptr a pointer the main FileX structure
  * @param ULONG start_sector first sector to start writing from
  * @param UINT num_sectors number of sectors to be written
  * @param UINT use_scratch_buffer to enable scratch buffer usage or not.
  * @retval FX_SUCCESS on success FX_BUFFER_ERROR otherwise
  */

static UINT sd_write_data(FX_MEDIA *media_ptr, ULONG start_sector, UINT num_sectors, UINT use_scratch_buffer)
{
    INT i = 0;
    UINT status;

    if (use_scratch_buffer)
    {
        for (i = 0; i < num_sectors; i++)
        {
            _fx_utility_memory_copy(media_ptr->fx_media_driver_buffer, scratch, DEFAULT_SECTOR_SIZE);
            media_ptr->fx_media_driver_buffer += DEFAULT_SECTOR_SIZE;

#if (ENABLE_CACHE_MAINTENANCE == 1)
            /* Clean the DCache to make the SD DMA see the actual content of the scratch buffer */
            SCB_CleanDCache_by_Addr((uint32_t*)scratch, DEFAULT_SECTOR_SIZE);
#endif
            status = BSP_SD_WriteBlocks_DMA((uint32_t*)scratch, (uint32_t)start_sector++, 1);

            if (status != BSP_ERROR_NONE)
            {
                status = FX_IO_ERROR;
                break;
            }

            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) != TX_SUCCESS)
            {
                status = FX_BUFFER_ERROR;
                break;
            }
        }

        if (i == num_sectors)
        {
            status = FX_SUCCESS;
        }
    }
    else
    {
        SCB_CleanDCache_by_Addr((uint32_t*)media_ptr->fx_media_driver_buffer, 
num_sectors * DEFAULT_SECTOR_SIZE);
        status = BSP_SD_WriteBlocks_DMA((uint32_t*)media_ptr->fx_media_driver_buffer, start_sector,
 num_sectors);

        if (status == BSP_ERROR_NONE)
        {
            if(tx_semaphore_get(&transfer_semaphore, DEFAULT_TIMEOUT) == TX_SUCCESS)
            {
                status = FX_SUCCESS;
            }
            else
            {
                status = FX_IO_ERROR;
            }
        }
    }

    return status;
}

4.8.5  磁盘中断处理函数SDMMC1_IRQHandler

供接口函数fx_stm32_sd_driver调用,代码如下:

/**
  * @brief BSP Tx Transfer completed callbacks
  * @param Instance the SD instance
  * @retval None
  */
void BSP_SD_WriteCpltCallback()
{
    tx_semaphore_put(&transfer_semaphore);
}

/**
  * @brief BSP Rx Transfer completed callbacks
  * @param Instance the sd instance
  * @retval None
  */
void BSP_SD_ReadCpltCallback()
{
    tx_semaphore_put(&transfer_semaphore);
}
void SDMMC1_IRQHandler(void)
{
    HAL_SD_IRQHandler(&uSdHandle);
}

实际对应的函数在文件sd_diskio_dma.c

4.9   实验例程

配套例子:

V7-2201_ThreadX FileX Template

实验目的:

  1. 学习SD卡的ThreadX FileX移植实现。

实验内容:

  1. 上电启动了一个软件定时器,每100ms翻转一次LED2。
  2. V7开发板的SD卡接口是用的SDMMC1,而这个接口仅支持AXI SRAM区访问。

实验操作:

  1. 测试前务必将SD卡插入到开发板左上角的卡座中。
  2. 支持以下6个功能,用户通过电脑端串口软件发送数字1-6给开发板即可
  3. printf("1 - 显示根目录下的文件列表\r\n");
  4. printf("2 - 创建一个新文件armfly.txt\r\n");
  5. printf("3 - 读armfly.txt文件的内容\r\n");
  6. printf("4 - 创建目录\r\n");
  7. printf("5 - 删除文件和目录\r\n");
  8. printf("6 - 读写文件速度测试\r\n");

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

4.10 总结

本章节就为大家讲解这么多,需要大家实现操作一遍来熟练掌握FileX的移植,然后FileX相关的知识点可以到FileX官网查看,资料非常详细。

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32 FileX是一款嵌入式文件系统,专为STM32微控制器系列而设计和优化。它提供了一个强大而灵活的文件管理系统,适用于存储和管理嵌入式系统中的各种文件类型。 STM32 FileX具有以下特点和优势: 1. 系统资源占用低:FileX对内存资源的要求非常低,可以在有限的嵌入式系统中运行,而不会造成系统资源的浪费,从而使得STM32微控制器可以更加高效地执行其他任务。 2. 多种文件类型支持:FileX可以处理各种类型的文件,包括文本文件、二进制文件、图像文件等。它支持常见的文件操作,如打开、读取、写入、重命名、删除等,使得嵌入式系统可以灵活地处理不同类型的文件。 3. 文件系统安全性:FileX提供了数据完整性和文件系统安全性的保护机制。通过使用校验和、CRC校验、写入保护等技术,可以保证文件在读写过程中不会被破坏或损坏,确保数据的可靠性和完整性。 4. 支持多线程:FileX支持多线程操作,可以同时处理多个文件的读写操作。这在复杂的嵌入式系统中非常有用,可以提高系统的整体性能和效率。 5. 高度可配置:FileX可以根据具体的应用需求进行灵活的配置。用户可以根据需要选择适合的文件系统大小、块大小、簇大小等参数,以满足自己的存储容量和性能要求。 总的来说,STM32 FileX是一款功能强大、高效可靠的嵌入式文件系统。它在不占用过多系统资源的同时,提供了丰富的文件操作功能,可以满足各种嵌入式系统文件管理的需求。无论是嵌入式设备的开发者还是应用工程师,都能够通过使用STM32 FileX轻松实现文件的存储、管理和操作。 ### 回答2: STM32 FileXSTMicroelectronics开发的一款嵌入式文件系统。它是专门为STM32微控制器设计的,提供了可靠的文件系统功能,能够在嵌入式系统中进行文件的读写和管理。 STM32 FileX具有以下特点和功能: 1. 高性能和低资源占用:STM32 FileX采用了高效的算法和数据结构,使其能够在嵌入式系统中快速且高效地进行文件的读写操作,并且占用的系统资源较少,有利于节省处理器的计算能力和内存空间。 2. 可靠的数据保护:STM32 FileX提供了可靠的数据保护机制,包括存储介质上的文件一致性检查、写操作的缓存管理、数据的完整性和可靠性验证等,能够有效地防止数据损坏和丢失,保证了数据的可靠性和一致性。 3. 多种文件系统支持:STM32 FileX支持多种文件系统,包括FAT12、FAT16、FAT32等,能够适应不同的应用需求和存储介质类型。 4. 灵活的文件管理功能:STM32 FileX提供了丰富的文件管理功能,包括文件的创建、打开、读写、删除等操作,能够方便地进行文件的管理和维护。 5. 完善的API接口和开发工具:STM32 FileX提供了完善的API接口和开发工具,使开发者能够快速地集成和使用该文件系统,提高开发效率。 6. 应用广泛:STM32 FileX适用于多种嵌入式应用场景,包括工业自动化、智能家居、车载娱乐系统、医疗设备等,能够满足不同领域的文件管理需求。 总的来说,STM32 FileX是一款高性能、可靠的嵌入式文件系统,能够方便地进行文件的读写和管理,适用于各种嵌入式系统的开发。 ### 回答3: STM32 FileX是一个嵌入式文件系统,专为STM32系列微控制器开发的。它是一个可靠、高效、安全的文件系统,能够管理和操作嵌入式系统中的文件文件夹。 STM32 FileX提供了许多功能,包括文件的创建、读取、写入、删除等。用户可以通过简单的API来访问这些功能,并可以根据自己的需求进行定制。 STM32 FileX能够有效地管理嵌入式系统中的存储器资源。它支持不同类型的存储介质,如RAM、Flash、SD等。通过使用FileX,开发人员可以轻松地在嵌入式系统中添加文件系统,从而使得数据的存储和管理更加方便。 STM32 FileX具有很高的性能和可靠性。它采用了先进的文件索引技术,能够快速地定位文件文件夹。同时,它还具备数据保护机制,能够确保文件的完整性和安全性。 STM32 FileX是易于使用和集成的。它提供了一个直观的编程接口,使开发人员能够快速地熟悉和使用。此外,它还提供了丰富的示例代码和文档,帮助开发人员更好地理解和使用该文件系统。 总之,STM32 FileX是一个专为STM32系列微控制器开发的嵌入式文件系统。它具有高效、可靠、安全的特性,能够方便地管理和操作嵌入式系统中的文件文件夹。无论是存储数据,还是进行软件更新和配置,STM32 FileX都能够提供可靠的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值