zoukankan      html  css  js  c++  java
  • 【STM32F429】第4章 ThreadX FileX文件系统移植到STM32F429(SD卡)

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

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

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

    4.1 初学者重要提示

    4.2 SD卡硬件接口设计

    4.3 SD卡基础知识

    4.4 各种存储卡区别

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

    4.6 ThreadX FlieX移植步骤

    4.7 ThreadX FlieX应用代码测试

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

    4.9 SDIO使用DMA方式的4字节对齐问题(重要)

    4.10 实验例程

    4.11 总结

    4.1   初学者重要提示

    1、 SDIO的相关知识点可以看第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、  支持128GB,64GB的大容量SD卡,需要大家使能ThreadX FileX的exFAT即可。

    4.2   SD卡硬件接口设计

    STM32F4驱动SD卡设计如下:

     

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

    •   大家自己设计推荐也接上拉电阻。
    •   这里采用SDIO的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和SDIO驱动到工程

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

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

    配套例子是放在Userspsrc和Userspinc文件。

    •   SDIO驱动文件stm32f4xx_hal_sd.c和stm32f4xx_ll_sdmmc.c

    这个是STM32F4的HAL库自带的。

    •   FileX相关源文件。

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

    4.6.3      第3步,添加工程路径

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

     

    4.6.4      第4步,配置GPIO和时钟

    根据大家使用SDIO配置相应时钟,GPIO,DMA和NVIC:

    __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);
    }
    
    __weak void BSP_SD_MspInit(SD_HandleTypeDef *hsd, void *Params)
    {
      static DMA_HandleTypeDef dmaRxHandle = {0};
      static DMA_HandleTypeDef dmaTxHandle = {0};
      GPIO_InitTypeDef GPIO_Init_Structure;
      
      /* Enable SDIO clock */
      __HAL_RCC_SDIO_CLK_ENABLE();
      
      /* Enable DMA2 clocks */
      __DMAx_TxRx_CLK_ENABLE();
    
      /* Enable GPIOs clock */
      __HAL_RCC_GPIOC_CLK_ENABLE();
      __HAL_RCC_GPIOD_CLK_ENABLE();
      
      /* Common GPIO configuration */
      GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
      GPIO_Init_Structure.Pull      = GPIO_PULLUP;
      GPIO_Init_Structure.Speed     = GPIO_SPEED_HIGH;
      GPIO_Init_Structure.Alternate = GPIO_AF12_SDIO;
      
      /* 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);
    
      /* NVIC configuration for SDIO interrupts */
      HAL_NVIC_SetPriority(SDIO_IRQn, 0x0E, 0);
      HAL_NVIC_EnableIRQ(SDIO_IRQn);
        
      /* Configure DMA Rx parameters */
      dmaRxHandle.Init.Channel             = SD_DMAx_Rx_CHANNEL;
      dmaRxHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
      dmaRxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
      dmaRxHandle.Init.MemInc              = DMA_MINC_ENABLE;
      dmaRxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      dmaRxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      dmaRxHandle.Init.Mode                = DMA_PFCTRL;
      dmaRxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
      dmaRxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
      dmaRxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
      dmaRxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
      dmaRxHandle.Init.PeriphBurst         = DMA_PBURST_INC4;
      
      dmaRxHandle.Instance = SD_DMAx_Rx_STREAM;
      
      /* Associate the DMA handle */
      __HAL_LINKDMA(hsd, hdmarx, dmaRxHandle);
      
      /* Deinitialize the stream for new transfer */
      HAL_DMA_DeInit(&dmaRxHandle);
      
      /* Configure the DMA stream */
      HAL_DMA_Init(&dmaRxHandle);
      
      /* Configure DMA Tx parameters */
      dmaTxHandle.Init.Channel             = SD_DMAx_Tx_CHANNEL;
      dmaTxHandle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
      dmaTxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
      dmaTxHandle.Init.MemInc              = DMA_MINC_ENABLE;
      dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      dmaTxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      dmaTxHandle.Init.Mode                = DMA_PFCTRL;
      dmaTxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
      dmaTxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
      dmaTxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
      dmaTxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
      dmaTxHandle.Init.PeriphBurst         = DMA_PBURST_INC4;
      
      dmaTxHandle.Instance = SD_DMAx_Tx_STREAM;
      
      /* Associate the DMA handle */
      __HAL_LINKDMA(hsd, hdmatx, dmaTxHandle);
      
      /* Deinitialize the stream for new transfer */
      HAL_DMA_DeInit(&dmaTxHandle);
      
      /* Configure the DMA stream */
      HAL_DMA_Init(&dmaTxHandle); 
      
      /* NVIC configuration for DMA transfer complete interrupt */
      HAL_NVIC_SetPriority(SD_DMAx_Rx_IRQn, 0x0F, 0);
      HAL_NVIC_EnableIRQ(SD_DMAx_Rx_IRQn);
      
      /* NVIC configuration for DMA transfer complete interrupt */
      HAL_NVIC_SetPriority(SD_DMAx_Tx_IRQn, 0x0F, 0);
      HAL_NVIC_EnableIRQ(SD_DMAx_Tx_IRQn);
    }

    4.6.5      第5步,FileX的配置文件fx_port.h设置

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

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

    #define FX_ENABLE_EXFAT

    4.6.6      第6步,添加应用代码

    这里将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
    ", status);
            return;
        }
        
        
        printf("属性        |  文件大小 | 短文件名 | 长文件名
    ");
        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
    ", (char *)entry_name);    /* 长文件名 */
        }
        
        /* SD卡剩余容量大小 */
        status = fx_media_extended_space_available(&sdio_disk, &available_bytes);    
    
        if (status == FX_SUCCESS)
        {
            printf("SD卡剩余容量大小 -- %lldMB
    ", available_bytes/1024/1024);
        }
        
        /* 卸载SD卡 */
        status =  fx_media_close(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("卸载文件系统卸载失败 -- %d
    ", 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
    ", status);
            return;
        }
        
        /* 创建文件 */
        status =  fx_file_create(&sdio_disk, "armfly.txt");
    
        /* 检测状态 */
        if (status != FX_SUCCESS)
        {
            /* 失败的话,可以考虑二次创建 */
            if (status != FX_ALREADY_CREATED)
            {
                printf("创建文件失败
    ");
            }
        }
        
        /* 打开文件  */
        status =  fx_file_open(&sdio_disk, &fx_file, "armfly.txt", FX_OPEN_FOR_WRITE);
    
        if (status != FX_SUCCESS)
        {
            printf("打开文件失败
    ");
        }
        
        /* 设置到起始位置读取  */
        status =  fx_file_seek(&fx_file, 0);
    
        if (status != FX_SUCCESS)
        {
            printf("设置读取位置失败
    ");       
        }
    
    
        /* 写入字符串到文件  */
        status =  fx_file_write(&fx_file, FsWriteBuf, strlen(FsWriteBuf));
    
        if (status == FX_SUCCESS)
        {
            printf("armfly.txt 文件写入成功
    ");        
        }
        else
        {
            printf("armfly.txt 文件写入失败 %d
    ", status); 
        }
    
        /* 关闭文件  */
        status =  fx_file_close(&fx_file);
    
        /* Check the file close status.  */
        if (status != FX_SUCCESS)
        {
            printf("关闭文件失败
    ");    
        }
    
        /* 保证文件写入全部生效 */
        status = fx_media_flush(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("flush失败
    ");   
        }
        
         /* 卸载SD卡 */
        status =  fx_media_close(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("卸载文件系统卸载失败 -- %d
    ", 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
    ", status);
            return;
        }
        
    
        /* 打开文件  */
        status =  fx_file_open(&sdio_disk, &fx_file, "armfly.txt", FX_OPEN_FOR_READ);
    
        if (status != FX_SUCCESS)
        {
            printf("打开文件失败
    ");
        } 
    
        /* 设置到起始位置读取  */
        status =  fx_file_seek(&fx_file, 0);
    
        if (status != FX_SUCCESS)
        {
            printf("设置读取位置失败
    ");       
        }
    
        /* 读取文件 */
        status =  fx_file_read(&fx_file, FsReadBuf, sizeof(FsReadBuf), &bw);
    
        printf("--%sReadLen = %d
    ", FsReadBuf, (int)bw);
    
        if ((status != FX_SUCCESS))
        {
            printf("读取失败
    ");     
        }    
        
        /* 关闭文件  */
        status =  fx_file_close(&fx_file);
    
        if (status != FX_SUCCESS)
        {
            printf("关闭文件失败
    ");    
        }
        
         /* 卸载SD卡 */
        status =  fx_media_close(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("卸载文件系统卸载失败 -- %d
    ", 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
    ", status);
            return;
        } 
    
        /* 创建目录/Dir1 */
        status = fx_directory_create(&sdio_disk, "Dir1");
        
        if (status == FX_SUCCESS)
        {
            printf("文件夹Dir1创建成功
    ");
        } 
        else if (status == FX_ALREADY_CREATED)
        {
            printf("Dir1 目录已经存在(%d)
    ", status);
        }
        else
        {
            printf("fx_directory_create Dir1 失败 (%d)
    ", status);
            return;
        }  
        
        /* 创建目录/Dir2 */
        status = fx_directory_create(&sdio_disk, "Dir2");
        
        if (status == FX_SUCCESS)
        {
            printf("文件夹Dir2创建成功
    ");
        } 
        else if (status == FX_ALREADY_CREATED)
        {
            printf("Dir2 目录已经存在(%d)
    ", status);
        }
        else
        {
            printf("fx_directory_create Dir2 失败 (%d)
    ", 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创建成功
    ");
        } 
        else if (status == FX_ALREADY_CREATED)
        {
            printf("Dir1/Dir1_1 目录已经存在(%d)
    ", status);
        }
        else
        {
            printf("fx_directory_create Dir1/Dir1_1 失败 (%d)
    ", status);
            return;
        }  
        
        /* 保证文件写入全部生效 */
        status = fx_media_flush(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("flush失败
    ");   
        }
        
         /* 卸载SD卡 */
        status =  fx_media_close(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("卸载文件系统卸载失败 -- %d
    ", 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
    ", status);
            return;
        }
    
        /* 删除目录/Dir1 【因为还存在目录非空(存在子目录),所以这次删除会失败】*/
        status = fx_directory_delete(&sdio_disk, "Dir1");
        
        if (status == FX_SUCCESS)
        {
            printf("删除目录Dir1成功
    ");
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件或目录 :%s
    ", "/Dir1");
        }
        else
        {
            printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空
    ", status);
        }
        
        
         /* 先删除目录/Dir1/Dir1_1 */  
        status = fx_directory_delete(&sdio_disk, "Dir1/Dir1_1");
        
        if (status == FX_SUCCESS)
        {
            printf("删除目录Dir1/Dir1_1成功
    ");
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件或目录 :%s
    ", "Dir1/Dir1_1");
        }
        else
        {
            printf("删除Dir1/Dir1_1失败(错误代码 = %d) 文件只读或目录非空
    ", status);
        }    
        
        /* 删除目录/Dir1*/  
        status = fx_directory_delete(&sdio_disk, "Dir1");
        
        if (status == FX_SUCCESS)
        {
            printf("删除目录Dir1成功
    ");
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件或目录 :%s
    ", "Dir1");
        }
        else
        {
            printf("删除Dir1失败(错误代码 = %d) 文件只读或目录非空
    ", status);
        }   
    
        /* 删除目录/Dir2*/  
        status = fx_directory_delete(&sdio_disk, "Dir2");
        
        if (status == FX_SUCCESS)
        {
            printf("删除目录Dir2成功
    ");
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件或目录 :%s
    ", "Dir2");
        }
        else
        {
            printf("删除Dir2失败(错误代码 = %d) 文件只读或目录非空
    ", status);
        }   
    
        /* 删除文件 armfly.txt */
        status = fx_file_delete(&sdio_disk, "armfly.txt");
        
        if (status == FX_SUCCESS)
        {
            printf("删除目录armfly.txt成功
    ");
        }
        else if (status == FX_NOT_FOUND)
        {
            printf("没有发现文件或目录 :%s
    ", "armfly.txt");
        }
        else
        {
            printf("删除armfly.txt失败(错误代码 = %d) 文件只读或目录非空
    ", 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成功
    ", path);
            }
            else if (status == FX_NOT_FOUND)
            {
                printf("没有发现文件:%s
    ", path);
            }
            else
            {
                printf("删除%s文件失败(错误代码 = %d) 文件只读或目录非空
    ", path, status);
            }   
        }
    
         /* 卸载SD卡 */
        status =  fx_media_close(&sdio_disk);
    
        if (status != FX_SUCCESS)
        {
            printf("卸载文件系统卸载失败 -- %d
    ", 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
    ", status);
            return;
        }
        
        /* 打开文件 */
        sprintf(path, "Speed%02d.txt", s_ucTestSn++); /* 每写1次,序号递增 */    
        
        /* 写一串数据 */
        printf("开始写文件%s %dKB ...
    ", path, TEST_FILE_LEN / 1024);
        
        /* 创建文件 */
        status =  fx_file_create(&sdio_disk, path);
    
        /* 检测状态 */
        if (status != FX_SUCCESS)
        {
            /* 失败的话,可以考虑二次创建 */
            if (status != FX_ALREADY_CREATED)
            {
                printf("创建文件失败
    ");
            }
        }
        
        /* 打开文件  */
        status =  fx_file_open(&sdio_disk, &fx_file, path, FX_OPEN_FOR_WRITE);
    
        if (status != FX_SUCCESS)
        {
            printf("打开文件失败
    ");
        }
        
        /* 设置到起始位置读取  */
        status =  fx_file_seek(&fx_file, 0);
    
        if (status != FX_SUCCESS)
        {
            printf("设置读取位置失败
    ");       
        }
        
        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文件写失败
    ", path);
                break; 
            }
        }
        runtime2 = bsp_GetRunTime();    /* 读取系统运行时间 */
        
        if (err == 0)
        {
            timelen = (runtime2 - runtime1);
            printf("
      写耗时 : %dms   平均写速度 : %dB/S (%dKB/S)
    ",
                    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("关闭文件失败
    ");    
        }
    
        /* 保证文件写入全部生效 */
        status = fx_media_flush(&sdio_disk);
        
        /* Check the file close status.  */
        if (status != FX_SUCCESS)
        {
            printf("fx_media_flush失败
    ");    
        }
        
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /* 开始读文件测试 */
        printf("开始读文件 %dKB ...
    ", TEST_FILE_LEN / 1024);
        
          /* 打开文件  */
        status =  fx_file_open(&sdio_disk, &fx_file, path, FX_OPEN_FOR_READ);
    
        if (status != FX_SUCCESS)
        {
            printf("打开文件失败
    ");
        }
        
        /* 设置到起始位置读取  */
        status =  fx_file_seek(&fx_file, 0);
    
        if (status != FX_SUCCESS)
        {
            printf("设置读取位置失败
    ");       
        }
        
        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 文件读成功,但是数据出错
    ");
                        break;
                    }
                }
                if (err == 1)
                {
                    break;
                }
            }
            else
            {
                   err = 1;
                printf("Speed1.txt 文件读失败
    ");
                break; 
            }
        }
        runtime2 = bsp_GetRunTime();    /* 读取系统运行时间 */
    
        if (err == 0)
        {
            timelen = (runtime2 - runtime1);
            printf("
      读耗时 : %dms   平均读速度 : %dB/S (%dKB/S)
    ", 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
    ", 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 */
    
               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:
            {
    
                /* 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;
                    }
    
                    /* 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:
            {
                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调用,代码如下。

    /**
      * @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)
    {
        UINT status;
       
        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)
            {
                status = FX_SUCCESS;
            }
            else
            {
                status =  FX_BUFFER_ERROR;
            }
        }
    
        return status;
    }

    4.8.4  磁盘写函数sd_write_data

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

    /**
      * @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)
    {
        UINT status;
    
        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  磁盘中断处理函数

    供接口函数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 SDIO_IRQHandler(void)
    {
        HAL_SD_IRQHandler(&uSdHandle);
    }
    
    void DMA2_Stream6_IRQHandler(void)
    {
        HAL_DMA_IRQHandler(uSdHandle.hdmatx);
    }
    
    void DMA2_Stream3_IRQHandler(void)
    {
        HAL_DMA_IRQHandler(uSdHandle.hdmarx);
    }

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

    4.9   SDIO使用DMA方式的4字节对齐问题(重要)

    正常情况使用SDIO的DMA方式要注意数据发送和数据接收缓冲区的4字节对齐问题,也就是要保证数据发送首地址和数据接收首地址对4求余等于0。这里提供一个非常简单的处理办法,用户无需做发送和接收缓冲区的4字节对齐:   

      /* DMA接收配置 */
      dmaRxHandle.Init.Channel             = SD_DMAx_Rx_CHANNEL;
      dmaRxHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;
      dmaRxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
      dmaRxHandle.Init.MemInc              = DMA_MINC_ENABLE;
      dmaRxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      dmaRxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      dmaRxHandle.Init.Mode                = DMA_PFCTRL;
      dmaRxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
      dmaRxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
      dmaRxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
      dmaRxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
      dmaRxHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;
      
      /* */
      dmaTxHandle.Init.Channel             = SD_DMAx_Tx_CHANNEL;
      dmaTxHandle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
      dmaTxHandle.Init.PeriphInc           = DMA_PINC_DISABLE;
      dmaTxHandle.Init.MemInc              = DMA_MINC_ENABLE;
      dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      dmaTxHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      dmaTxHandle.Init.Mode                = DMA_PFCTRL;
      dmaTxHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
      dmaTxHandle.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
      dmaTxHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
      dmaTxHandle.Init.MemBurst            = DMA_MBURST_SINGLE;
      dmaTxHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;

    使能发送和接收DMA配置的FIFO,并设置MemDataAligment对齐方式为BYTE即可解决。这样设置的原理是DMA传输的源地址和目的地址数据宽度不同时,需要开启FIFO,这样就很好的解决了DMA的4字节对齐问题。

    4.10 实验例程

    配套例子:

    V6-2201_ThreadX FileX Template

    实验目的:

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

    实验内容:

    1. 上电启动了一个软件定时器,每100ms翻转一次LED2。

    实验操作:

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

    上电后串口打印的信息:

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

     

    4.11 总结

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

     

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    观察者模式
    工厂模式
    单例模式
    代理模式
    策略模式
    Ioc容器
    Spring概述
    02:入门
    01:背景
    编译原理感悟
  • 原文地址:https://www.cnblogs.com/armfly/p/14538377.html
Copyright © 2011-2022 走看看