精华内容
下载资源
问答
  • SDMMC接口相关资料

    2020-01-03 20:15:03
    SDMMC有关的资料,在单片机或裸机开发中,需要这些资料,有了这些资料我们就可以开始自己的操作系统的开发,希望对大家有用.
  • SD MMC 相关寄存器 的介绍 .pdf
  • STM32L476RC平台的关于SDMMC+fatfs的demo工程:亲测有效,4线传输,读写采用HAL库HAL_SD_ReadBlocks();HAL_SD_WriteBlocks();函数的轮询方式
  • 在STM32F746G-DISCO的SDMMC上运行的FatF的裸机示例完全没有HAL ...全裸机 该存储库是ST的STM32F7xx处理器上SD卡接口的驱动程序,它基于STM32F746G-DISCO,但可以在任何项目中轻松使用。 您只需要更改引脚配置即可。...
  • stm32h743加emmc/sdmmc加文件系统 的驱动和测试代码。开发环境是MDK,用ST提供的HAL库,此代码已经配置到实际工程中使用,具体配置过程在博客中有记录。也可以联系本文作者共同学习。
  • SD MMC 相关寄存器的介绍,熟悉SD/MMC 的相关寄存器对协议的理解有一定的辅助作用
  • 一、简介 SD 卡(Secure Digital Memory Card) 在我们生活中已经非常普遍了,控制器对 SD 卡进行 读写通信操作一般有两种通信接口可选,一种是 SPI ...以下 SDMMC 接口位于 driver/include/driver/sdmmc_host.h。 2.1

    一、简介

    SD 卡(Secure Digital Memory Card) 在我们生活中已经非常普遍了,控制器对 SD 卡进行
    读写通信操作一般有两种通信接口可选,一种是 SPI 接口,另外一种就是 SDIO 接口。
    SDIO 全称是安全数字输入/输出接口,多媒体卡(MMC)、SD 卡、SD I/O 卡都有 SDIO 接口。
    MMC 卡可以说是 SD 卡的前身,现阶段已经用得很少。

    二、API说明

    以下 SDMMC 接口位于 driver/include/driver/sdmmc_host.h

    2.1 SDMMC_HOST_DEFAULT

    SDMMC_HOST_DEFAULT()
    SDMMC 外设的默认sdmmc_host_t结构初始值设定项。
    使用 SDMMC 外设,启用 4 位模式,最大频率设置为 20MHz

    2.2 SDMMC_SLOT_CONFIG_DEFAULT

    SDMMC_SLOT_CONFIG_DEFAULT()
    定义 SDMMC 主机插槽默认配置的宏
    以下 FAT 文件系统接口位于 fatfs/vfs/esp_vfs_fat.h

    2.3 esp_vfs_fat_sdmmc_mount

    2.4 esp_vfs_fat_sdcard_unmount


    以下 SDMMC 接口位于 sdmmc/include/sdmmc_cmd.h

    2.5 sdmmc_card_print_info

    三、编程流程

    1. 使用“一体式” esp_vfs_fat_sdmmc_mount()函数进行初始化:
      • 初始化SDMMC外设;
      • 检测并初始化连接到SD/MMC插槽1的卡(HS2_CMD、HS2_CLK、HS2_D0、HS2_D1、HS2_D2、HS2_D3线);
      • 使用FATFS库安装FAT文件系统(如果无法安装文件系统,则使用格式化卡);
      • 在VFS中注册FAT文件系统,以使用C标准库和POSIX函数。
    2. 打印有关SD卡的信息,例如名称、类型、容量和支持的最大频率。
    3. 使用fopen()创建一个文件,并使用fprintf()写入该文件。
    4. 重命名该文件。重命名之前,请使用stat()函数检查目标文件是否已存在,并使用unlink()函数将其删除。
    5. 打开重命名的文件进行读取,读回该行,并将其打印到终端。

    四、硬件连接

    我使用的是 ESP32-LyraT V4.3 开发板

    以下功能可通过功能 DIP 开关进行选择:

    • 在 1-wire 模式下启用 MicroSD 卡
    拨码开关位置
    1OFF
    2OFF
    3OFF
    4OFF
    5OFF
    6OFF
    7OFF①
    8N/A

    ①:通过拨动 DIP SW 7 ON可以启用AUX 输入检测。请注意,系统上电时不应插入AUX 输入信号引脚。否则 ESP32 可能无法正常启动。

    在这种模式下:

    • JTAG功能不可用
    • Vol- touch 按钮可用于 API
    • 在 4-wire 模式下启用 MicroSD 卡 (例程默认使用的模式)
    拨码开关位置
    1ON
    2ON
    3OFF
    4OFF
    5OFF
    6OFF
    7OFF
    8N/A

    在这种模式下:

    • JTAG功能不可用
    • Vol- touch 按钮可用于 API
    • 来自 API 的AUX 输入检测不可用

    五、示例代码

    根据 examples\storage\sd_card 中的例程修改
    默认是4-wire 模式,1-wire 模式修改slot_config.width = 1;

    /* SD card and FAT filesystem example.
       This example code is in the Public Domain (or CC0 licensed, at your option.)
    
       Unless required by applicable law or agreed to in writing, this
       software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
       CONDITIONS OF ANY KIND, either express or implied.
    */
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/unistd.h>
    #include <sys/stat.h>
    #include "esp_err.h"
    #include "esp_log.h"
    #include "esp_vfs_fat.h"
    #include "driver/sdspi_host.h"
    #include "driver/spi_common.h"
    #include "sdmmc_cmd.h"
    #include "sdkconfig.h"
    
    #include "driver/sdmmc_host.h"
    
    static const char *TAG = "example";
    
    #define MOUNT_POINT "/sdcard"
    
    // This example can use SDMMC and SPI peripherals to communicate with SD card.
    // By default, SDMMC peripheral is used.
    
    void app_main(void)
    {
        esp_err_t ret;
        // Options for mounting the filesystem.
        // If format_if_mount_failed is set to true, SD card will be partitioned and
        // formatted in case when mounting fails.
        esp_vfs_fat_sdmmc_mount_config_t mount_config = {
            .format_if_mount_failed = true,
            .max_files = 5,
            .allocation_unit_size = 16 * 1024
        };
        sdmmc_card_t* card;
        const char mount_point[] = MOUNT_POINT;
        ESP_LOGI(TAG, "Initializing SD card");
    
        // Use settings defined above to initialize SD card and mount FAT filesystem.
        // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
        // Please check its source code and implement error recovery when developing
        // production applications.
        ESP_LOGI(TAG, "Using SDMMC peripheral");
        sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    
        // This initializes the slot without card detect (CD) and write protect (WP) signals.
        // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
        sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    
        // To use 1-line SD mode, uncomment the following line:
        // slot_config.width = 1;
    
        // GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
        // Internal pull-ups are not sufficient. However, enabling internal pull-ups
        // does make a difference some boards, so we do that here.
        gpio_set_pull_mode(15, GPIO_PULLUP_ONLY);   // CMD, needed in 4- and 1- line modes
        gpio_set_pull_mode(2, GPIO_PULLUP_ONLY);    // D0, needed in 4- and 1-line modes
        gpio_set_pull_mode(4, GPIO_PULLUP_ONLY);    // D1, needed in 4-line mode only
        gpio_set_pull_mode(12, GPIO_PULLUP_ONLY);   // D2, needed in 4-line mode only
        gpio_set_pull_mode(13, GPIO_PULLUP_ONLY);   // D3, needed in 4- and 1-line modes
    
        // 初始化 SD卡
        ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
    
        // 初始化不成功
        if (ret != ESP_OK) {
            if (ret == ESP_FAIL) {
                ESP_LOGE(TAG, "Failed to mount filesystem. "
                    "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
            } else {
                ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                    "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
            }
            return;
        }
    
        // Card has been initialized, print its properties
        sdmmc_card_print_info(stdout, card);
    
        // Use POSIX and C standard library functions to work with files.
        // First create a file.
        ESP_LOGI(TAG, "Opening file");
        FILE* f = fopen(MOUNT_POINT"/hello.txt", "w");
        if (f == NULL) {
            ESP_LOGE(TAG, "Failed to open file for writing");
            return;
        }
        fprintf(f, "Hello %s!\n", card->cid.name);
        fclose(f);
        ESP_LOGI(TAG, "File written");
    
        // Check if destination file exists before renaming
        struct stat st;
        if (stat(MOUNT_POINT"/foo.txt", &st) == 0) {
            // Delete it if it exists
            unlink(MOUNT_POINT"/foo.txt");
        }
    
        // Rename original file
        ESP_LOGI(TAG, "Renaming file");
        if (rename(MOUNT_POINT"/hello.txt", MOUNT_POINT"/foo.txt") != 0) {
            ESP_LOGE(TAG, "Rename failed");
            return;
        }
    
        // Open renamed file for reading
        ESP_LOGI(TAG, "Reading file");
        f = fopen(MOUNT_POINT"/foo.txt", "r");
        if (f == NULL) {
            ESP_LOGE(TAG, "Failed to open file for reading");
            return;
        }
        char line[64];
        fgets(line, sizeof(line), f);
        fclose(f);
        // strip newline
        char* pos = strchr(line, '\n');
        if (pos) {
            *pos = '\0';
        }
        ESP_LOGI(TAG, "Read from file: '%s'", line);
    
        // All done, unmount partition and disable SDMMC or SPI peripheral
        esp_vfs_fat_sdcard_unmount(mount_point, card);
        ESP_LOGI(TAG, "Card unmounted");
    }
    

    查看打印:


    • 由 Leung 写于 2021 年 8 月 13 日

    • 参考:ESP32笔记(4) SD卡的读写

    展开全文
  • 第87章 STM32H7的SDMMC总线基础知识和HAL库API 本章节为大家讲解SDMMC(Secure digital input/output MultiMediaCard interface)总线的基础知识和对应的HAL库API。 目录 第87章 STM32H7的SDMMC总线基础知识和HAL...

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    第87章       STM32H7的SDMMC总线基础知识和HAL库API

    本章节为大家讲解SDMMC(Secure digital input/output MultiMediaCard interface)总线的基础知识和对应的HAL库API。

    目录

    第87章       STM32H7的SDMMC总线基础知识和HAL库API

    87.1 初学者重要提示

    87.2 SDMMC总线基础知识

    87.2.1 SDMMC总线的硬件框图

    87.2.2 SDMMC时钟

    87.2.3 SDMMC1和SDMMC2支持的RAM空间区别

    87.2.4 SDMMC支持的速度

    87.2.5 SDMMC支持UHS-I模式

    87.2.6 SDMMC自带的DMA控制器IDMA

    87.3 SDMMC总线的HAL库用法

    87.3.1 SDMMC总线结构体SD_TypeDef

    87.3.2 SDMMC总线初始化结构体SD_InitTypeDef

    87.3.3 SDMMC接SD卡信息结构体HAL_SD_CardInfoTypeDef

    87.3.4 SDMMC总线句柄结构体SD_HandleTypeDef

    87.4 SDMMC总线源文件stm32h7xx_hal_sd.c

    87.4.1 函数HAL_SD_Init

    87.4.2 函数HAL_SD_DeInit

    87.4.3 函数HAL_SD_ReadBlocks

    87.4.4 函数HAL_SD_WriteBlocks

    87.4.5 函数HAL_SD_ReadBlocks_DMA

    87.4.6 函数HAL_SD_WriteBlocks_DMA

    87.4.7 函数HAL_SD_Erase

    87.5 总结


     

    87.1 初学者重要提示

    1.   对于SDMMC控制SD卡或者eMMC,掌握本章的知识点就够用了,更深入的认识可以看STM32H7的参考手册。
    2.   注意,操作SD卡是采用的函数HAL_SD_XXXX,而操作eMMC是采用的函数HAL_MMC_XXXX,也就是说他们采用的函数前缀是不同的。
    3.   SD卡官网: www.sdcard.org
    4.   SDMMC驱动eMMC支持1线,4线和8线模式,其中8线模式的最高速度可达208MB/S,实际速度受IO最大速度限制。
    5.   SDMMC驱动SD卡支持1线和4线模式。
    6.   STM32H7的SDMMC也支持eMMC:

    87.2 SDMMC总线基础知识

    87.2.1 SDMMC总线的硬件框图

    认识一个外设,最好的方式就是看它的框图,方便我们快速的了解SDMMC的基本功能,然后再看手册了解细节。

    通过这个框图,我们可以得到如下信息:

    •  sdmmc_ker_ck输入

    SDMMC内核时钟。

    •   sdmmc_hclk输入

    AHB时钟。

    •   sdmmc_it输出

    SDMMC全局中断。

    •   sdmmc_dataend_trg输出

    MDMA的SDMMC数据接收触发信号。

    •   SDMMC_CMD

    SD/SDIO/MMC卡双向/响应信号。

    •   SDMMC_D[7:0]

    SD/SDIO/MMC卡双向数据线。

    •   SDMMC_CKIN

    来自SD/SDIO/MMC卡的外部驱动器的时钟反馈(用于SDR12,SDR25,SDR50和DDR50)。

    •   SDMMC_CK

    SD/SDIO/MMC卡的时钟。

    •   SDMMC_CDIR

    SDMMC_CMD信号的SD/SDIO/MMC卡I/O方向指示。

    •   SDMMC_D123DIR

    SDMMC_D[3:1]数据线的SD/SDIO/MMC卡I/O方向指示。

    •   SDMMC_D0DIR

    SDMMC_D0数据线的SD/SDIO/MMC卡I/O方向指示。

     

    STM32H7有两个SDMMC控制器,SDMMC1和SDMMC2,这两个控制器支持的功能是一样的。

    87.2.2 SDMMC时钟

    SDMMC控制器的时钟来源:

     

    SDMMC1和SDMMC2时钟源是一样的:

    87.2.3 SDMMC1和SDMMC2支持的RAM空间区别

    注:大家应用时要特别注意这个问题。

    使用STM32H7的SDIO1仅支持AXI SRAM,而SDIO2是AXI,SRAM1,SRAM2和SRAM3都支持的

     

    87.2.4 SDMMC支持的速度

    驱动SD卡支持的最大总线速度:

    驱动eMMC支持的最大总线速度:

    关于这两个数据表,注意以下几点:

    •   驱动SD卡最大支持4bit,驱动eMMC最大支持8bit。
    •   针对信号电压1.8V或者1.2V,STM32H7需要外接专门的PHY芯片才可以驱动。
    •   最大IO翻转限制说的是SDR50,SDR104这种高速通信。平时用的DS,HS这种,无压力,刷满速不成问题。

    87.2.5 SDMMC支持UHS-I模式

    STM32H7的SDIO外接支持UHS-I 模式 (SDR12, SDR25, SDR50, SDR104和DDR50)需要1.8的电平转换器。STM32H7参考手册给了一个型号ST6G3244ME:

    87.2.6 SDMMC自带的DMA控制器IDMA

    STM32H7的SDMMC自带了专用的DMA控制器IDMA,支持突发,也支持双缓冲。为什么要自带DMA控制器? 主要原因是STM32H7的通用DMA1和DMA2已经无法满足SDMMC高速通信速度。在本教程的第62章专门为大家测试过。通过让SDMMC自带控制器,这个问题就迎刃而解。

    87.3 SDMMC总线的HAL库用法

    87.3.1 SDMMC总线结构体SD_TypeDef

    SDMMC总线相关的寄存器是通过HAL库中的结构体SD_TypeDef定义,在stm32h743xx.h中可以找到这个类型定义:

    #define SD_TypeDef          SDMMC_TypeDef
    typedef struct
    {
      __IO uint32_t POWER;          /*!< SDMMC power control register,             Address offset: 0x00  */
      __IO uint32_t CLKCR;          /*!< SDMMC clock control register,             Address offset: 0x04  */
      __IO uint32_t ARG;            /*!< SDMMC argument register,                  Address offset: 0x08  */
      __IO uint32_t CMD;            /*!< SDMMC command register,                   Address offset: 0x0C  */
      __I uint32_t  RESPCMD;        /*!< SDMMC command response register,          Address offset: 0x10  */
      __I uint32_t  RESP1;          /*!< SDMMC response 1 register,                Address offset: 0x14  */
      __I uint32_t  RESP2;          /*!< SDMMC response 2 register,                Address offset: 0x18  */
      __I uint32_t  RESP3;          /*!< SDMMC response 3 register,                Address offset: 0x1C  */
      __I uint32_t  RESP4;          /*!< SDMMC response 4 register,                Address offset: 0x20  */
      __IO uint32_t DTIMER;         /*!< SDMMC data timer register,                Address offset: 0x24  */
      __IO uint32_t DLEN;           /*!< SDMMC data length register,               Address offset: 0x28  */
      __IO uint32_t DCTRL;          /*!< SDMMC data control register,              Address offset: 0x2C  */
      __I uint32_t  DCOUNT;         /*!< SDMMC data counter register,              Address offset: 0x30  */
      __I uint32_t  STA;            /*!< SDMMC status register,                    Address offset: 0x34  */
      __IO uint32_t ICR;            /*!< SDMMC interrupt clear register,           Address offset: 0x38  */
      __IO uint32_t MASK;           /*!< SDMMC mask register,                      Address offset: 0x3C  */
      __IO uint32_t ACKTIME;        /*!< SDMMC Acknowledgement timer register,     Address offset: 0x40  */
      uint32_t      RESERVED0[3];   /*!< Reserved, 0x44 - 0x4C - 0x4C                                    */
      __IO uint32_t IDMACTRL;       /*!< SDMMC DMA control register,               Address offset: 0x50  */
      __IO uint32_t IDMABSIZE;      /*!< SDMMC DMA buffer size register,           Address offset: 0x54  */
      __IO uint32_t IDMABASE0;      /*!< SDMMC DMA buffer 0 base address register, Address offset: 0x58  */
      __IO uint32_t IDMABASE1;      /*!< SDMMC DMA buffer 1 base address register, Address offset: 0x5C  */
      uint32_t      RESERVED1[8];   /*!< Reserved, 0x60-0x7C                                             */
      __IO uint32_t FIFO;           /*!< SDMMC data FIFO register,                 Address offset: 0x80  */
      uint32_t      RESERVED2[222]; /*!< Reserved, 0x84-0x3F8                                            */
      __IO uint32_t IPVR;           /*!< SDMMC data FIFO register,                 Address offset: 0x3FC */
    } SDMMC_TypeDef;

    这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。

    __IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:

    #define     __O     volatile             /*!< Defines 'write only' permissions */
    #define     __IO    volatile             /*!< Defines 'read / write' permissions */

    下面我们看下SDMMC的定义,在stm32h743xx.h文件。

    #define PERIPH_BASE           (0x40000000UL) 
    #define D1_AHB1PERIPH_BASE    (PERIPH_BASE + 0x12000000UL)
    #define D2_AHB2PERIPH_BASE    (PERIPH_BASE + 0x08020000UL)
    
    #define SDMMC1_BASE           (D1_AHB1PERIPH_BASE + 0x7000UL)
    #define SDMMC2_BASE           (D2_AHB2PERIPH_BASE + 0x2400UL)
    
    #define SDMMC1              ((SDMMC_TypeDef *) SDMMC1_BASE)
    #define SDMMC2              ((SDMMC_TypeDef *) SDMMC2_BASE) <----- 展开这个宏,(SDMMC_TypeDef *)0x48022400

    我们访问SDMMC1的CMD寄存器可以采用这种形式:SDMMC1->CMD = 0。

    87.3.2 SDMMC总线初始化结构体SD_InitTypeDef

    下面是SDMMC总线的初始化结构体:

    #define SD_InitTypeDef      SDMMC_InitTypeDef
    typedef struct
    {
      uint32_t ClockEdge;            
      uint32_t ClockPowerSave;      
      uint32_t BusWide;             
      uint32_t HardwareFlowControl;  
      uint32_t ClockDiv;             
    #if (USE_SD_TRANSCEIVER != 0U)
      uint32_t TranceiverPresent;    
    #endif 
    }SDMMC_InitTypeDef;

    下面将结构体成员逐一做个说明:

    •   ClockEdge

    用于设置SDMMC的数据或者命令变化的时钟沿。

    #define SDMMC_CLOCK_EDGE_RISING               ((uint32_t)0x00000000U)
    #define SDMMC_CLOCK_EDGE_FALLING              SDMMC_CLKCR_NEGEDGE
    •   ClockPowerSave

    用于设置空闲状态,是否输出时钟。

    #define SDMMC_CLOCK_POWER_SAVE_DISABLE         ((uint32_t)0x00000000U)
    #define SDMMC_CLOCK_POWER_SAVE_ENABLE          SDMMC_CLKCR_PWRSAV
    •   BusWide

    用于设置SDMMC总线位宽。

    #define SDMMC_BUS_WIDE_1B                      ((uint32_t)0x00000000U)
    #define SDMMC_BUS_WIDE_4B                      SDMMC_CLKCR_WIDBUS_0
    #define SDMMC_BUS_WIDE_8B                      SDMMC_CLKCR_WIDBUS_1
    •   HardwareFlowControl

    用于设置时候使能硬件流控制。

    #define SDMMC_HARDWARE_FLOW_CONTROL_DISABLE    ((uint32_t)0x00000000U)
    #define SDMMC_HARDWARE_FLOW_CONTROL_ENABLE     SDMMC_CLKCR_HWFC_EN
    •   ClockDiv

    用于设置SDMMC时钟分频,参数范围0到1023。

    •   TranceiverPresent

    用于设置是否带1.8V收发器。

    #define SDMMC_TRANSCEIVER_UNKNOWN             ((uint32_t)0x00000000U)
    #define SDMMC_TRANSCEIVER_NOT_PRESENT         ((uint32_t)0x00000001U)
    #define SDMMC_TRANSCEIVER_PRESENT             ((uint32_t)0x00000002U)

    87.3.3 SDMMC接SD卡信息结构体HAL_SD_CardInfoTypeDef

    下面是SDMMC总线的卡信息结构体:

    typedef struct
    {
      uint32_t CardType;                     /*!< Specifies the card Type                         */
      uint32_t CardVersion;                  /*!< Specifies the card version                      */
      uint32_t Class;                        /*!< Specifies the class of the card class           */
      uint32_t RelCardAdd;                   /*!< Specifies the Relative Card Address             */
      uint32_t BlockNbr;                     /*!< Specifies the Card Capacity in blocks           */
      uint32_t BlockSize;                    /*!< Specifies one block size in bytes               */
      uint32_t LogBlockNbr;                  /*!< Specifies the Card logical Capacity in blocks   */
      uint32_t LogBlockSize;                 /*!< Specifies logical block size in bytes           */
      uint32_t CardSpeed;                    /*!< Specifies the card Speed                        */
    }HAL_SD_CardInfoTypeDef;

    下面将结构体成员逐一做个说明:

    •  CardType

    卡类型。

    /*!< SD Standard Capacity <2Go                        */
    #define CARD_SDSC                  ((uint32_t)0x00000000U) 
    /*!< SD High Capacity <32Go, SD Extended Capacity <2To  */
    #define CARD_SDHC_SDXC             ((uint32_t)0x00000001U)  
    #define CARD_SECURED               ((uint32_t)0x00000003U)
    •   CardVersion

    卡版本。

    #define CARD_V1_X                  ((uint32_t)0x00000000U)
    #define CARD_V2_X                  ((uint32_t)0x00000001U)
    •   Class

    卡类型。

    •   RelCardAdd

    卡相对地址。

    •   BlockNbr

    整个卡的块数。

    •   BlockSize

    每个块的字节数。

    •   LogBlockNbr

    整个卡的逻辑块数。

    •   LogBlockSize

    逻辑块大小

    #define SPI_FIRSTBIT_MSB                              (0x00000000UL)
    #define SPI_FIRSTBIT_LSB                              SPI_CFG2_LSBFRST
    •  CardSpeed

    用于设置是否使能SPI总线的TI模式。

    /*!< Normal Speed Card <12.5Mo/s , Spec Version 1.01    */
    #define CARD_NORMAL_SPEED        ((uint32_t)0x00000000U)  
    
    /*!< High Speed Card <25Mo/s , Spec version 2.00        */ 
    #define CARD_HIGH_SPEED          ((uint32_t)0x00000100U) 
    
    /*!< UHS-I SD Card <50Mo/s for SDR50, DDR5 Cards
         and <104Mo/s for SDR104, Spec version 3.01        */
    #define CARD_ULTRA_HIGH_SPEED    ((uint32_t)0x00000200U)  

    87.3.4 SDMMC总线句柄结构体SD_HandleTypeDef

    下面是SDMMC句柄结构体:

    #if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U)
    typedef struct __SD_HandleTypeDef
    #else
    typedef struct
    #endif /* USE_HAL_SD_REGISTER_CALLBACKS */
    {
      SD_TypeDef                   *Instance;        /*!< SD registers base address           */
      SD_InitTypeDef               Init;             /*!< SD required parameters              */
      HAL_LockTypeDef              Lock;             /*!< SD locking object                   */
      uint8_t                      *pTxBuffPtr;      /*!< Pointer to SD Tx transfer Buffer    */
      uint32_t                     TxXferSize;       /*!< SD Tx Transfer size                 */
      uint8_t                      *pRxBuffPtr;      /*!< Pointer to SD Rx transfer Buffer    */
      uint32_t                     RxXferSize;       /*!< SD Rx Transfer size                 */
      __IO uint32_t                Context;          /*!< SD transfer context                 */
      __IO HAL_SD_StateTypeDef     State;            /*!< SD card State                       */
      __IO uint32_t                ErrorCode;        /*!< SD Card Error codes                 */
      HAL_SD_CardInfoTypeDef       SdCard;           /*!< SD Card information                 */
      uint32_t                     CSD[4];           /*!< SD card specific data table         */
      uint32_t                     CID[4];           /*!< SD card identification number table */
    
    #if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U)
      void (* TxCpltCallback)                 (struct __SD_HandleTypeDef *hsd);
      void (* RxCpltCallback)                 (struct __SD_HandleTypeDef *hsd);
      void (* ErrorCallback)                  (struct __SD_HandleTypeDef *hsd);
      void (* AbortCpltCallback)              (struct __SD_HandleTypeDef *hsd);
      void (* Read_DMADblBuf0CpltCallback)    (struct __SD_HandleTypeDef *hsd);
      void (* Read_DMADblBuf1CpltCallback)    (struct __SD_HandleTypeDef *hsd);
      void (* Write_DMADblBuf0CpltCallback)   (struct __SD_HandleTypeDef *hsd);
      void (* Write_DMADblBuf1CpltCallback)   (struct __SD_HandleTypeDef *hsd);
    #if (USE_SD_TRANSCEIVER != 0U)
      void (* DriveTransceiver_1_8V_Callback) (FlagStatus status);
    #endif /* USE_SD_TRANSCEIVER */
    
      void (* MspInitCallback)                (struct __SD_HandleTypeDef *hsd);
      void (* MspDeInitCallback)              (struct __SD_HandleTypeDef *hsd);
    #endif /* USE_HAL_SD_REGISTER_CALLBACKS */
    }SD_HandleTypeDef;

    注意事项:

    条件编译USE_HAL_SD_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32h7xx_hal_conf.h文件里面设置:

      #define   USE_HAL_SD_REGISTER_CALLBACKS   1

    通过函数HAL_SD_RegisterCallback注册回调,取消注册使用函数HAL_SD_UnRegisterCallback。

    这里重点介绍下面几个参数,其它参数主要是HAL库内部使用和自定义回调函数。

    •   SD_TypeDef   *Instance

    这个参数是寄存器的例化,方便操作寄存器。

    •   SD_InitTypeDef  Init

    这个参数在本章节3.2小节已经进行了详细说明。

    87.4 SDMMC总线源文件stm32h7xx_hal_sd.c

    此文件涉及到的函数较多,这里把几个常用的函数做个说明:

    •   HAL_SD_Init
    •   HAL_SD_DeInit
    •   HAL_SD_ReadBlocks
    •   HAL_SD_WriteBlocks
    •   HAL_SD_ReadBlocks_DMA
    •   HAL_SD_WriteBlocks_DMA
    •   HAL_SD_Erase

    87.4.1 函数HAL_SD_Init

    函数原型:

    HAL_StatusTypeDef HAL_SD_Init(SD_HandleTypeDef *hsd)
    {
      HAL_SD_CardStatusTypeDef CardStatus;
      uint32_t speedgrade, unitsize;
      uint32_t tickstart;
    
      /* 检查句柄是否有效 */
      if(hsd == NULL)
      {
        return HAL_ERROR;
      }
    
      /* 检查参数 */
      assert_param(IS_SDMMC_ALL_INSTANCE(hsd->Instance));
      assert_param(IS_SDMMC_CLOCK_EDGE(hsd->Init.ClockEdge));
      assert_param(IS_SDMMC_CLOCK_POWER_SAVE(hsd->Init.ClockPowerSave));
      assert_param(IS_SDMMC_BUS_WIDE(hsd->Init.BusWide));
      assert_param(IS_SDMMC_HARDWARE_FLOW_CONTROL(hsd->Init.HardwareFlowControl));
      assert_param(IS_SDMMC_CLKDIV(hsd->Init.ClockDiv));
    
      if(hsd->State == HAL_SD_STATE_RESET)
      {
        /* 开锁 */
        hsd->Lock = HAL_UNLOCKED;
    
    #if (USE_SD_TRANSCEIVER != 0U)
        /* 兼容 */
        if (hsd->Init.TranceiverPresent == SDMMC_TRANSCEIVER_UNKNOWN)
        {
          hsd->Init.TranceiverPresent = SDMMC_TRANSCEIVER_PRESENT;
        }
    #endif
    #if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U)
        /* 复位回调 */
        hsd->TxCpltCallback    = HAL_SD_TxCpltCallback;
        hsd->RxCpltCallback    = HAL_SD_RxCpltCallback;
        hsd->ErrorCallback     = HAL_SD_ErrorCallback;
        hsd->AbortCpltCallback = HAL_SD_AbortCallback;
        hsd->Read_DMADblBuf0CpltCallback = HAL_SDEx_Read_DMADoubleBuf0CpltCallback;
        hsd->Read_DMADblBuf1CpltCallback = HAL_SDEx_Read_DMADoubleBuf1CpltCallback;
        hsd->Write_DMADblBuf0CpltCallback = HAL_SDEx_Write_DMADoubleBuf0CpltCallback;
        hsd->Write_DMADblBuf1CpltCallback = HAL_SDEx_Write_DMADoubleBuf1CpltCallback;
    #if (USE_SD_TRANSCEIVER != 0U)
        if (hsd->Init.TranceiverPresent == SDMMC_TRANSCEIVER_PRESENT)
        {
          hsd->DriveTransceiver_1_8V_Callback = HAL_SD_DriveTransceiver_1_8V_Callback;
        }
    #endif 
    
        if(hsd->MspInitCallback == NULL)
        {
          hsd->MspInitCallback = HAL_SD_MspInit;
        }
    
        /* 初始化底层 */
        hsd->MspInitCallback(hsd);
    #else
        /* 初始化底层硬件 GPIO, CLOCK, CORTEX...etc */
        HAL_SD_MspInit(hsd);
    #endif /* USE_HAL_SD_REGISTER_CALLBACKS */
      }
    
      hsd->State = HAL_SD_STATE_BUSY;
    
      /* 初始化卡参数 */
      if (HAL_SD_InitCard(hsd) != HAL_OK)
      {
        return HAL_ERROR;
      }
    
      if( HAL_SD_GetCardStatus(hsd, &CardStatus) != HAL_OK)
      {
        return HAL_ERROR;
      }
      /* 获取卡速度等信息 */
      speedgrade = CardStatus.UhsSpeedGrade;
      unitsize = CardStatus.UhsAllocationUnitSize;
      if ((hsd->SdCard.CardType == CARD_SDHC_SDXC) && ((speedgrade != 0U) || (unitsize != 0U)))
      {
        hsd->SdCard.CardSpeed = CARD_ULTRA_HIGH_SPEED;
      }
      else
      {
        if (hsd->SdCard.CardType == CARD_SDHC_SDXC)
        {
          hsd->SdCard.CardSpeed  = CARD_HIGH_SPEED;
        }
        else
        {
          hsd->SdCard.CardSpeed  = CARD_NORMAL_SPEED;
        }
    
      }
      /* 配置总线位宽 */
      if(HAL_SD_ConfigWideBusOperation(hsd, hsd->Init.BusWide) != HAL_OK)
      {
        return HAL_ERROR;
      }
    
      /* 验证卡初始化后是否就绪 */
      tickstart = HAL_GetTick();
      while((HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER))
      {
        if((HAL_GetTick()-tickstart) >=  SDMMC_DATATIMEOUT)
        {
          hsd->ErrorCode = HAL_SD_ERROR_TIMEOUT;
          hsd->State= HAL_SD_STATE_READY;
          return HAL_TIMEOUT;
        }
      }
    
      hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
      hsd->Context = SD_CONTEXT_NONE;
    
      hsd->State = HAL_SD_STATE_READY;
    
      return HAL_OK;
    }

    函数描述:

    此函数用于初始化SD卡。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    注意事项:

    1. 函数HAL_SD_MspInit用于初始化SD的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
    2. 如果形参hsd的结构体成员State没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量SD_HandleTypeDef SdHandle。

    对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_SD_STATE_RESET  = 0x00U。

    解决办法有三

    方法1:用户自己初始化SD和涉及到的GPIO等。

    方法2:定义SD_HandleTypeDef SdHandle为全局变量。

    方法3:下面的方法

    if(HAL_SD_DeInit(&SdHandle) != HAL_OK)
    {
        Error_Handler();
    }  
    if(HAL_SD_Init(&SdHandle) != HAL_OK)
    {
        Error_Handler();
    }

    使用举例:

    SD_HandleTypeDef uSdHandle;
    
    uSdHandle.Instance = SDMMC1;
    
    /* if CLKDIV = 0 then SDMMC Clock frequency = SDMMC Kernel Clock
         else SDMMC Clock frequency = SDMMC Kernel Clock / [2 * CLKDIV].
         200MHz / (2*2) = 50MHz
    */
    uSdHandle.Init.ClockDiv            = 2; 
    uSdHandle.Init.ClockPowerSave      = SDMMC_CLOCK_POWER_SAVE_DISABLE;
    uSdHandle.Init.ClockEdge           = SDMMC_CLOCK_EDGE_RISING;
    uSdHandle.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
    uSdHandle.Init.BusWide             = SDMMC_BUS_WIDE_4B;
    if(HAL_SD_Init(&uSdHandle) != HAL_OK)
    {
       sd_state = MSD_ERROR;
    }

    87.4.2 函数HAL_SD_DeInit

    函数原型:

    HAL_StatusTypeDef HAL_SD_DeInit(SD_HandleTypeDef *hsd)
    {
      /* 检查SD卡句柄是否有效 */
      if(hsd == NULL)
      {
        return HAL_ERROR;
      }
    
      /* 检查参数 */
      assert_param(IS_SDMMC_ALL_INSTANCE(hsd->Instance));
    
      hsd->State = HAL_SD_STATE_BUSY;
    
    #if (USE_SD_TRANSCEIVER != 0U)
      /* 关闭1.8V模式 */
      if (hsd->Init.TranceiverPresent == SDMMC_TRANSCEIVER_PRESENT)
      {
    #if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U)
        if(hsd->DriveTransceiver_1_8V_Callback == NULL)
        {
          hsd->DriveTransceiver_1_8V_Callback = HAL_SD_DriveTransceiver_1_8V_Callback;
        }
        hsd->DriveTransceiver_1_8V_Callback(RESET);
    #else
        HAL_SD_DriveTransceiver_1_8V_Callback(RESET);
    #endif 
      }                                                                         
    #endif
    
      /* 关闭SD卡电源 */
      SD_PowerOFF(hsd);
    
    #if defined (USE_HAL_SD_REGISTER_CALLBACKS) && (USE_HAL_SD_REGISTER_CALLBACKS == 1U)
      if(hsd->MspDeInitCallback == NULL)
      {
        hsd->MspDeInitCallback = HAL_SD_MspDeInit;
      }
    
      /* 复位底层硬件 */
      hsd->MspDeInitCallback(hsd);
    #else
      /* 复位底层硬件 */
      HAL_SD_MspDeInit(hsd);
    #endif 
    
      hsd->ErrorCode = HAL_SD_ERROR_NONE;
      hsd->State = HAL_SD_STATE_RESET;
    
      return HAL_OK;
    }

    函数描述:

    用于复位SD总线初始化。

    函数参数:

    •  第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •  返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    87.4.3 函数HAL_SD_ReadBlocks

    函数原型:

    HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    {
      SDMMC_DataInitTypeDef config;
      uint32_t errorstate;
      uint32_t tickstart = HAL_GetTick();
      uint32_t count, data, dataremaining;
      uint32_t add = BlockAdd;
      uint8_t *tempbuff = pData;
    
      if(NULL == pData)
      {
        hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
        return HAL_ERROR;
      }
    
      if(hsd->State == HAL_SD_STATE_READY)
      {
        hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
        if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
        {
          hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_BUSY;
    
        /* 初始化数据控制寄存器 */
        hsd->Instance->DCTRL = 0U;
    
        if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
        {
          add *= 512U;
        }
    
        /* 配置SD DPSM (Data Path State Machine) */
        config.DataTimeOut   = SDMMC_DATATIMEOUT;
        config.DataLength    = NumberOfBlocks * BLOCKSIZE;
        config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
        config.TransferDir   = SDMMC_TRANSFER_DIR_TO_SDMMC;
        config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
        config.DPSM          = SDMMC_DPSM_DISABLE;
        (void)SDMMC_ConfigData(hsd->Instance, &config);
        __SDMMC_CMDTRANS_ENABLE( hsd->Instance);
    
        /* 查询方式块读取 */
        if(NumberOfBlocks > 1U)
        {
          hsd->Context = SD_CONTEXT_READ_MULTIPLE_BLOCK;
    
          /* 多块读取命令 */
          errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add);
        }
        else
        {
          hsd->Context = SD_CONTEXT_READ_SINGLE_BLOCK;
    
          /* 单块读取命令 */
          errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, add);
        }
        if(errorstate != HAL_SD_ERROR_NONE)
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
    
        /* 查询SDMMC标志 */
        dataremaining = config.DataLength;
        while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND))
        {
          if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXFIFOHF) && (dataremaining >= 32U))
          {
            /* 从SDMMC Rx FIFO读取数据 */
            for(count = 0U; count < 8U; count++)
            {
              data = SDMMC_ReadFIFO(hsd->Instance);
              *tempbuff = (uint8_t)(data & 0xFFU);
              tempbuff++;
              *tempbuff = (uint8_t)((data >> 8U) & 0xFFU);
              tempbuff++;
              *tempbuff = (uint8_t)((data >> 16U) & 0xFFU);
              tempbuff++;
              *tempbuff = (uint8_t)((data >> 24U) & 0xFFU);
              tempbuff++;
            }
            dataremaining -= 32U;
          }
    
          if(((HAL_GetTick()-tickstart) >=  Timeout) || (Timeout == 0U))
          {
            /* 清除所有静态标志 */
            __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
            hsd->ErrorCode |= HAL_SD_ERROR_TIMEOUT;
            hsd->State= HAL_SD_STATE_READY;
            hsd->Context = SD_CONTEXT_NONE;
            return HAL_TIMEOUT;
          }
        }
        __SDMMC_CMDTRANS_DISABLE( hsd->Instance);
    
        /* 多块读取发送停止传输命令 */
        if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U))
        {
          if(hsd->SdCard.CardType != CARD_SECURED)
          {
            /* 发送停止传输命令 */
            errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
            if(errorstate != HAL_SD_ERROR_NONE)
            {
              /* 清除所有静态标志 */
              __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
              hsd->ErrorCode |= errorstate;
              hsd->State = HAL_SD_STATE_READY;
              hsd->Context = SD_CONTEXT_NONE;
              return HAL_ERROR;
            }
          }
        }
    
        /* 获取错误状态 */
        if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT))
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL))
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_RXOVERR))
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_RX_OVERRUN;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else
        {
          /* 什么都不做 */
        }
    
        /* 清除所有静态标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS);
    
        hsd->State = HAL_SD_STATE_READY;
    
        return HAL_OK;
      }
      else
      {
        hsd->ErrorCode |= HAL_SD_ERROR_BUSY;
        return HAL_ERROR;
      }
    }

    函数描述:

    此函数主要用于SD卡数据读取。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •   第2个参数是接收数据的缓冲地址。
    •   第3个参数是要读取的扇区地址,即从第几个扇区开始读取(512字节为一个扇区)。
    •   第4个参数是读取的扇区数。
    •   第5个参数是传输过程的溢出时间,单位ms。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    /**
      * @brief  Reads block(s) from a specified address in an SD card, in polling mode.
      * @param  pData: Pointer to the buffer that will contain the data to transmit
      * @param  ReadAddr: Address from where data is to be read
      * @param  NumOfBlocks: Number of SD blocks to read
      * @param  Timeout: Timeout for read operation
      * @retval SD status
      */
    uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)
    {
    
      if( HAL_SD_ReadBlocks(&uSdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) == HAL_OK)
      {
        return MSD_OK;
      }
      else
      {
        return MSD_ERROR;
      }
    
    }

    87.4.4 函数HAL_SD_WriteBlocks

    函数原型:

    HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
    {
      SDMMC_DataInitTypeDef config;
      uint32_t errorstate;
      uint32_t tickstart = HAL_GetTick();
      uint32_t count, data, dataremaining;
      uint32_t add = BlockAdd;
      uint8_t *tempbuff = pData;
    
      if(NULL == pData)
      {
        hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
        return HAL_ERROR;
      }
    
      if(hsd->State == HAL_SD_STATE_READY)
      {
        hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
        if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
        {
          hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_BUSY;
    
        /* 初始化数据控制寄存器 */
        hsd->Instance->DCTRL = 0U;
    
        if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
        {
          add *= 512U;
        }
    
        /* 配置SD DPSM */
        config.DataTimeOut   = SDMMC_DATATIMEOUT;
        config.DataLength    = NumberOfBlocks * BLOCKSIZE;
        config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
        config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
        config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
        config.DPSM          = SDMMC_DPSM_DISABLE;
        (void)SDMMC_ConfigData(hsd->Instance, &config);
        __SDMMC_CMDTRANS_ENABLE( hsd->Instance);
    
        /* 查询方式块写操作 */
        if(NumberOfBlocks > 1U)
        {
          hsd->Context = SD_CONTEXT_WRITE_MULTIPLE_BLOCK;
    
          /* 写多块命令 */
          errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add);
        }
        else
        {
          hsd->Context = SD_CONTEXT_WRITE_SINGLE_BLOCK;
    
          /* 写单块命令 */
          errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, add);
        }
        if(errorstate != HAL_SD_ERROR_NONE)
        {
          /* 清除所有静态命令 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
    
        /* 查询方式块写操作 */
        dataremaining = config.DataLength;
        while(!__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR | SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_DATAEND))
        {
          if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXFIFOHE) && (dataremaining >= 32U))
          {
            /* 写数据到SDMMC Tx FIFO */
            for(count = 0U; count < 8U; count++)
            {
              data = (uint32_t)(*tempbuff);
              tempbuff++;
              data |= ((uint32_t)(*tempbuff) << 8U);
              tempbuff++;
              data |= ((uint32_t)(*tempbuff) << 16U);
              tempbuff++;
              data |= ((uint32_t)(*tempbuff) << 24U);
              tempbuff++;
              (void)SDMMC_WriteFIFO(hsd->Instance, &data);
            }
            dataremaining -= 32U;
          }
    
          if(((HAL_GetTick()-tickstart) >=  Timeout) || (Timeout == 0U))
          {
            /* 清除所有静态标志 */
            __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
            hsd->ErrorCode |= errorstate;
            hsd->State = HAL_SD_STATE_READY;
            hsd->Context = SD_CONTEXT_NONE;
            return HAL_TIMEOUT;
          }
        }
        __SDMMC_CMDTRANS_DISABLE( hsd->Instance);
    
        /* 多块写操作,发送停止传输命令 */
        if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DATAEND) && (NumberOfBlocks > 1U))
        {
          if(hsd->SdCard.CardType != CARD_SECURED)
          {
            /* 发送停止传输命令 */
            errorstate = SDMMC_CmdStopTransfer(hsd->Instance);
            if(errorstate != HAL_SD_ERROR_NONE)
            {
              /* 清除所有静态传输标志 */
              __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
              hsd->ErrorCode |= errorstate;
              hsd->State = HAL_SD_STATE_READY;
              hsd->Context = SD_CONTEXT_NONE;
              return HAL_ERROR;
            }
          }
        }
    
        /* Get error state */
        if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DTIMEOUT))
        {
          /* 清除所有静态传输标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_DATA_TIMEOUT;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_DCRCFAIL))
        {
          /* 清除所有静态传输标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_DATA_CRC_FAIL;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else if(__HAL_SD_GET_FLAG(hsd, SDMMC_FLAG_TXUNDERR))
        {
          /* 清除所有静态传输标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_TX_UNDERRUN;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
        else
        {
          /* 什么都不做 */
        }
    
          /* 清除所有静态传输标志 */
        __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS);
    
        hsd->State = HAL_SD_STATE_READY;
    
        return HAL_OK;
      }
      else
      {
        hsd->ErrorCode |= HAL_SD_ERROR_BUSY;
        return HAL_ERROR;
      }
    }

    函数描述:

    此函数主要用于向SD卡写入数据。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •   第2个参数是要写入到SD卡的数据缓冲地址。
    •   第3个参数是要写入的扇区地址,即从第几个扇区开始写入(512字节为一个扇区)。
    •   第4个参数是读取的扇区数。
    •   第5个参数是传输过程的溢出时间,单位ms。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    /**
      * @brief  Writes block(s) to a specified address in an SD card, in polling mode.
      * @param  pData: Pointer to the buffer that will contain the data to transmit
      * @param  WriteAddr: Address from where data is to be written
      * @param  NumOfBlocks: Number of SD blocks to write
      * @param  Timeout: Timeout for write operation
      * @retval SD status
      */
    uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)
    {
    
      if( HAL_SD_WriteBlocks(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) == HAL_OK)
      {
        return MSD_OK;
      }
      else
      {
        return MSD_ERROR;
      }
    }

    87.4.5 函数HAL_SD_ReadBlocks_DMA

    函数原型:

    HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
    {
      SDMMC_DataInitTypeDef config;
      uint32_t errorstate;
      uint32_t add = BlockAdd;
    
      if(NULL == pData)
      {
        hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
        return HAL_ERROR;
      }
    
      if(hsd->State == HAL_SD_STATE_READY)
      {
        hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
        if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
        {
          hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_BUSY;
    
      /* 初始化数据控制寄存器 */
        hsd->Instance->DCTRL = 0U;
    
        hsd->pRxBuffPtr = pData;
        hsd->RxXferSize = BLOCKSIZE * NumberOfBlocks;
    
        if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
        {
          add *= 512U;
        }
    
      /* 配置SD DPSM (Data Path State Machine) */
        config.DataTimeOut   = SDMMC_DATATIMEOUT;
        config.DataLength    = BLOCKSIZE * NumberOfBlocks;
        config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
        config.TransferDir   = SDMMC_TRANSFER_DIR_TO_SDMMC;
        config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
        config.DPSM          = SDMMC_DPSM_DISABLE;
        (void)SDMMC_ConfigData(hsd->Instance, &config);
    
        __SDMMC_CMDTRANS_ENABLE( hsd->Instance);
        hsd->Instance->IDMABASE0 = (uint32_t) pData ;
        hsd->Instance->IDMACTRL  = SDMMC_ENABLE_IDMA_SINGLE_BUFF;
    
      /* DMA方式读取多个块 */
        if(NumberOfBlocks > 1U)
        {
          hsd->Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA);
    
       /* DMA方式读取多块命令 */
          errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add);
        }
        else
        {
          hsd->Context = (SD_CONTEXT_READ_SINGLE_BLOCK | SD_CONTEXT_DMA);
    
       /* 读取单块命令 */
          errorstate = SDMMC_CmdReadSingleBlock(hsd->Instance, add);
        }
        if(errorstate != HAL_SD_ERROR_NONE)
        {
       /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
    
      /* 使能传输中断 */
        __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND));
    
    
        return HAL_OK;
      }
      else
      {
        return HAL_BUSY;
      }
    }

    函数描述:

    此函数主要用于SD卡数据读取,DMA方式。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •   第2个参数是接收数据的缓冲地址。
    •   第3个参数是要读取的扇区地址,即从第几个扇区开始读取(512字节为一个扇区)。
    •   第4个参数是读取的扇区数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    /**
    * @brief  Reads block(s) from a specified address in an SD card, in DMA mode.
    * @param  pData: Pointer to the buffer that will contain the data to transmit
    * @param  ReadAddr: Address from where data is to be read
    * @param  NumOfBlocks: Number of SD blocks to read
    * @retval SD status
    */
    uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks)
    {
    
      if( HAL_SD_ReadBlocks_DMA(&uSdHandle, (uint8_t *)pData, ReadAddr, NumOfBlocks) == HAL_OK)
      {
        return MSD_OK;
      }
      else
      {
        return MSD_ERROR;
      }
    }

    87.4.6 函数HAL_SD_WriteBlocks_DMA

    函数原型:

    HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
    {
      SDMMC_DataInitTypeDef config;
      uint32_t errorstate;
      uint32_t add = BlockAdd;
    
      if(NULL == pData)
      {
        hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
        return HAL_ERROR;
      }
    
      if(hsd->State == HAL_SD_STATE_READY)
      {
        hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
        if((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr))
        {
          hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_BUSY;
    
      /* 初始化数据控制寄存器 */
        hsd->Instance->DCTRL = 0U;
    
        hsd->pTxBuffPtr = pData;
        hsd->TxXferSize = BLOCKSIZE * NumberOfBlocks;
    
        if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
        {
          add *= 512U;
        }
    
      /* 配置SD DPSM (Data Path State Machine) */
        config.DataTimeOut   = SDMMC_DATATIMEOUT;
        config.DataLength    = BLOCKSIZE * NumberOfBlocks;
        config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
        config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
        config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
        config.DPSM          = SDMMC_DPSM_DISABLE;
        (void)SDMMC_ConfigData(hsd->Instance, &config);
    
    
        __SDMMC_CMDTRANS_ENABLE( hsd->Instance);
    
        hsd->Instance->IDMABASE0 = (uint32_t) pData ;
        hsd->Instance->IDMACTRL  = SDMMC_ENABLE_IDMA_SINGLE_BUFF;
    
      /* 查询模式写块 */
        if(NumberOfBlocks > 1U)
        {
          hsd->Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA);
    
        /* 多块写命令 */
          errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add);
        }
        else
        {
          hsd->Context = (SD_CONTEXT_WRITE_SINGLE_BLOCK | SD_CONTEXT_DMA);
    
        /* 单块写命令 */
          errorstate = SDMMC_CmdWriteSingleBlock(hsd->Instance, add);
        }
        if(errorstate != HAL_SD_ERROR_NONE)
        {
        /* 清除静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          hsd->Context = SD_CONTEXT_NONE;
          return HAL_ERROR;
        }
    
      /* 使能传输中断 Enable */
        __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND));
    
        return HAL_OK;
      }
      else
      {
        return HAL_BUSY;
      }
    }

    函数描述:

    此函数主要用于向SD卡写入数据,DMA方式。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •   第2个参数是要写入到SD卡的数据缓冲地址。
    •   第3个参数是要写入的扇区地址,即从第几个扇区开始写入(512字节为一个扇区)。
    •   第4个参数是读取的扇区数。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    /**
    * @brief  Writes block(s) to a specified address in an SD card, in DMA mode.
    * @param  pData: Pointer to the buffer that will contain the data to transmit
    * @param  WriteAddr: Address from where data is to be written
    * @param  NumOfBlocks: Number of SD blocks to write
    * @retval SD status
    */
    uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
    {
    
      if( HAL_SD_WriteBlocks_DMA(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks) == HAL_OK)
      {
        return MSD_OK;
      }
      else
      {
        return MSD_ERROR;
      }
    }

    87.4.7 函数HAL_SD_Erase

    函数原型:

    HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd)
    {
      uint32_t errorstate;
      uint32_t start_add = BlockStartAdd;
      uint32_t end_add = BlockEndAdd;
    
      if(hsd->State == HAL_SD_STATE_READY)
      {
        hsd->ErrorCode = HAL_SD_ERROR_NONE;
    
        if(end_add < start_add)
        {
          hsd->ErrorCode |= HAL_SD_ERROR_PARAM;
          return HAL_ERROR;
        }
    
        if(end_add > (hsd->SdCard.LogBlockNbr))
        {
          hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_BUSY;
    
      /* 检测是否支持擦除命令 */
        if(((hsd->SdCard.Class) & SDMMC_CCCC_ERASE) == 0U)
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_REQUEST_NOT_APPLICABLE;
          hsd->State = HAL_SD_STATE_READY;
          return HAL_ERROR;
        }
    
        if((SDMMC_GetResponse(hsd->Instance, SDMMC_RESP1) & SDMMC_CARD_LOCKED) == SDMMC_CARD_LOCKED)
        {
        /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= HAL_SD_ERROR_LOCK_UNLOCK_FAILED;
          hsd->State = HAL_SD_STATE_READY;
          return HAL_ERROR;
        }
    
      /* 对于高容量卡,获取起始块和结束块 */
        if(hsd->SdCard.CardType != CARD_SDHC_SDXC)
        {
          start_add *= 512U;
          end_add   *= 512U;
        }
    
        /* 根据sd-card spec 1.0 ERASE_GROUP_START (CMD32) 和 erase_group_end(CMD33) */
        if(hsd->SdCard.CardType != CARD_SECURED)
        {
        /* 发送CMD32 SD_ERASE_GRP_START命令带地址参数 */
          errorstate = SDMMC_CmdSDEraseStartAdd(hsd->Instance, start_add);
          if(errorstate != HAL_SD_ERROR_NONE)
          {
            /* 清除所有静态标志 */
            __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
            hsd->ErrorCode |= errorstate;
            hsd->State = HAL_SD_STATE_READY;
            return HAL_ERROR;
          }
    
          /* 发送CMD33 SD_ERASE_GRP_END命令,带地址参数 */
          errorstate = SDMMC_CmdSDEraseEndAdd(hsd->Instance, end_add);
          if(errorstate != HAL_SD_ERROR_NONE)
          {
            /* 清除所有静态标志 */
            __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
            hsd->ErrorCode |= errorstate;
            hsd->State = HAL_SD_STATE_READY;
            return HAL_ERROR;
          }
        }
    
        /* 发送CMD38 ERASE命令 */
        errorstate = SDMMC_CmdErase(hsd->Instance, 0UL);
        if(errorstate != HAL_SD_ERROR_NONE)
        {
          /* 清除所有静态标志 */
          __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_FLAGS);
          hsd->ErrorCode |= errorstate;
          hsd->State = HAL_SD_STATE_READY;
          return HAL_ERROR;
        }
    
        hsd->State = HAL_SD_STATE_READY;
    
        return HAL_OK;
      }
      else
      {
        return HAL_BUSY;
      }
    }

    函数描述:

    此函数主要用于SD卡擦除。

    函数参数:

    •   第1个参数是SD_HandleTypeDef类型结构体指针变量。
    •   第2个参数是擦除的起始扇区地址,地址单位是第几个扇区(512字节为一个扇区)。
    •   第3个参数是擦除的结束扇区地址,地址单位是第几个扇区(512字节为一个扇区)。
    •   返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。

    使用举例:

    /**
    * @brief  Erases the specified memory area of the given SD card.
    * @param  StartAddr: Start byte address
    * @param  EndAddr: End byte address
    * @retval SD status
    */
    uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr)
    {
    
      if( HAL_SD_Erase(&uSdHandle, StartAddr, EndAddr) == HAL_OK)
      {
        return MSD_OK;
      }
      else
      {
        return MSD_ERROR;
      }
    }

    87.5 总结

    本章节就为大家讲解这么多,更多SDMMC知识可以看STM32H7的参考手册。

     

    展开全文
  • 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SDMMC外设读取SD卡数据。 1. 准备工作 硬件准备 开发板 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): Micro...
     
    

    寻求更好的阅读体验,请移步:Mculover666的个人博客

    本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SDMMC外设读取SD卡数据。

    1. 准备工作

    硬件准备

    • 开发板
      首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):

    mark

    • Micro SD卡
      小熊派开发板板载 Micro SD 卡槽,最大支持 32 GB,需要提前自行准备一张 Micro SD卡,如图:

    mark

    软件准备

    • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;
    • 准备一个串口调试助手,这里我使用的是Serial Port Utility

    Keil MDK和串口助手Serial Port Utility 的安装包都可以在文末关注公众号获取,回复关键字获取相应的安装包:

    mark

    2.生成MDK工程

    选择芯片型号

    打开STM32CubeMX,打开MCU选择器:
    mark

    搜索并选中芯片STM32L431RCT6:
    mark

    配置时钟源

    • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
    • 如果使用默认内部时钟(HSI),这一步可以略过;

    这里我都使用外部时钟:

    mark

    配置串口

    小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:

    mark

    这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。

    接下来开始配置USART1

    mark

    配置 SDMMC 接口

    知识小卡片 —— SDMMC接口

    SDMMC接口的全称叫SD/SDIO MMC card host interface,SD/SDIO MMC 卡 主机接口,通俗的来说,就是这个接口支持SD卡,支持SDIO设备,支持MMC卡。

    知识小卡片结束啦~

    首先查看小熊派开发板的原理图:

    mark

    然后根据原理图配置 SDMMC 接口:

    mark

    配置时钟树

    STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

    mark

    生成工程设置

    mark

    代码生成设置

    最后设置生成独立的初始化文件:

    mark

    生成代码

    点击GENERATE CODE即可生成MDK-V5工程:

    mark

    3. 在MDK中编写、编译、下载用户代码

    重定向printf( )函数

    参考:【STM32Cube_09】重定向printf函数到串口输出的多种方法

    读取SD卡信息并打印

    SD 卡系统(包括主机和 SD 卡)定义了两种操作模式:

    • 卡识别模式
    • 数据传输模式

    系统复位后,主机处于卡识别模式,寻找总线上可用的 SD卡设备;同时,SD 卡也处于卡
    识别模式,直到被主机识别到。

    使用STM32CubeMX初始化的工程中会自动生成 SDMMC 初始化函数,向 SD 卡发送命令,当 SD 卡接收到命令后, SD 卡就会进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式

    所以在操作之前,需要先检查 SD 卡是否处于数据传输模式并且处于数据传输状态:

    main函数中首先定义一个变量用于存储 SD 卡状态:

    int sdcard_status = 0;
    HAL_SD_CardCIDTypeDef sdcard_cid;
    

    然后在while(1)之前编写如下读取信息代码:

    /* USER CODE BEGIN 2 */
    printf("Micro SD Card Test...\r\n");
    
    /* 检测SD卡是否正常(处于数据传输模式的传输状态) */
    sdcard_status = HAL_SD_GetCardState(&hsd1);
    if(sdcard_status == HAL_SD_CARD_TRANSFER)
    {
        printf("SD card init ok!\r\n\r\n");
        
        //打印SD卡基本信息
        printf("SD card information!\r\n");
        printf("CardCapacity: %llu\r\n",((unsigned long long)hsd1.SdCard.BlockSize*hsd1.SdCard.BlockNbr));
        printf("CardBlockSize: %d \r\n",hsd1.SdCard.BlockSize);
        printf("RCA: %d \r\n",hsd1.SdCard.RelCardAdd);
        printf("CardType: %d \r\n",hsd1.SdCard.CardType);
        
        //读取并打印SD卡的CID信息
        HAL_SD_GetCardCID(&hsd1,&sdcard_cid);
        printf("ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);
    }
    else
    { 
        printf("SD card init fail!\r\n" );
        return 0;
    }
    /* USER CODE END 2 */
    

    编译下载后串口助手输出结果如下:

    mark

    擦除SD卡块数据

    为了验证实验的正确性或,先擦除数据:

    /* 擦除SD卡块 */
    printf("------------------- Block Erase -------------------------------\r\n");
    sdcard_status = HAL_SD_Erase(&hsd1, 0, 512);
    if (sdcard_status == 0)
    {
        printf("Erase block ok\r\n");
    }
    else
    {
        printf("Erase block fail\r\n");
    }
    

    读取SD卡块数据

    首先开辟一个全局缓冲区,用于存放从SD卡读出的数据:

    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    uint8_t read_buf[512];
    /* USER CODE END 0 */
    

    然后在之前读取信息的代码之后添加读取数据的代码:

    /* 读取未操作之前的数据 */
    printf("------------------- Read SD card block data Test ------------------\r\n");
    sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
    if(sdcard_status == 0)
    { 
        printf("Read block data ok \r\n" );
        for(i = 0; i < 512; i++)
        {
            printf("0x%02x ", read_buf[i]);
            if((i+1)%16 == 0)
            {
                printf("\r\n");
            }
        }
    }
    else
    {
        printf("Read block data fail!\r\n " );
    }
    

    向SD卡块写入数据

    同样的,开辟一个全局缓冲区,用于存放即将要写入SD卡的数据:

    uint8_t write_buf[512];
    

    然后在之前读取数据的代码之后添加的代码,将缓冲区的数据赋初值:

    /* 填充缓冲区数据 */
    for(i = 0; i < 512; i++)
    {
        write_buf[i] = i % 256;
    }
    

    然后继续添加代码,将该缓冲区数据写入SD卡:

    /* 向SD卡块写入数据 */
    printf("------------------- Write SD card block data Test ------------------\r\n");
    sdcard_status = HAL_SD_WriteBlocks(&hsd1,(uint8_t *)write_buf,0,1,0xffff);
    if(sdcard_status == 0)
    { 
        printf("Write block data ok \r\n" );
    }
    else
    {
        printf("Write block data fail!\r\n " );
    }
    

    添加完之后,为了检查数据是否正常写入,再将数据读出:

    /* 读取操作之后的数据 */
    printf("------------------- Read SD card block data after Write ------------------\r\n");
    sdcard_status = HAL_SD_ReadBlocks(&hsd1,(uint8_t *)read_buf,0,1,0xffff);
    if(sdcard_status == 0)
    { 
        printf("Read block data ok \r\n" );
        for(i = 0; i < 512; i++)
        {
            printf("0x%02x ", read_buf[i]);
            if((i+1)%16 == 0)
            {
                printf("\r\n");
            }
        }
    }
    

    将程序编译下载,最终的实验结果如下:

    mark

    mark

    mark

    mark

    mark

    至此,我们已经学会如何使用硬件SDMMC接口读取SD数据,STM32CubeMX系列教程完结。

    更多精彩文章及资源,请关注我的微信公众号:『mculover666』。

    mark

    展开全文
  • 嵌入式SD / MMC 此板条箱旨在使您能够像使用SdFat Arduino库一样轻松地在Rust嵌入式设备上以FAT... let mut cont = embedded_sdmmc :: Controller :: new (embedded_sdmmc :: SdMmcSpi :: new (sdmmc_spi, sdmmc_cs
  • rk sdmmc sdio

    2021-07-28 09:20:57
    [ 1.146254] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3000000 done [ 1.146256] CPU: 5 PID: 45 Comm: kworker/5:1 Not tainted 4.4.227-g334d0b436-dirty #25 [ 1.146256] Hardware name: Pine...

    [    1.146254] rockchip-iodomain ff770000.syscon:io-domains: Setting to 3000000 done
    [    1.146256] CPU: 5 PID: 45 Comm: kworker/5:1 Not tainted 4.4.227-g334d0b436-dirty #25
    [    1.146256] Hardware name: Pine64 RockPro64 (DT)
    [    1.146259] Workqueue: events_freezable mmc_rescan
    [    1.146260] Call trace:
    [    1.146262] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [    1.146264] [<ffffff80080884cc>] show_stack+0x24/0x30
    [    1.146266] [<ffffff80084149  b8>] dump_stack+0xa0/0xc8
    [    1.146268] [<ffffff800847461c>] rockchip_iodomain_notify+0xa4/0xf0
    [    1.146270] [<ffffff80080d4ee4>] notifier_call_chain+0x70/0x90
    [    1.146272] [<ffffff80080d5368>] __blocking_notifier_call_chain+0x58/0x84
    [    1.146274] [<ffffff80080d53d0>] blocking_notifier_call_chain+0x3c/0x4c
    [    1.146276] [<ffffff800850ff7c>] _regulator_do_set_voltage+0x1fc/0x3e0
    [    1.146277] [<ffffff8008510318>] regulator_set_voltage_unlocked+0x1b8/0x200
    [    1.146279] [<ffffff800851039c>] regulator_set_voltage+0x3c/0x5c
    [    1.146281] [<ffffff8008c5c16c>] mmc_regulator_set_ocr+0x54/0xe4
    [    1.146283] [<ffffff8008c7ac74>] dw_mci_set_ios+0x10c/0x23c
    [    1.146285] [<ffffff8008c5c9fc>] mmc_set_initial_state+0xe0/0xf8
    [    1.146286] [<ffffff8008c5cf68>] mmc_power_up+0x5c/0x164
    [    1.146288] [<ffffff8008c5d314>] mmc_power_cycle+0x3c/0x48
    [    1.146290        ] [<ffffff8008c64800>] mmc_sd_init_card+0x2b8/0x2e4
    [    1.146292] [<ffffff8008c64ac0>] mmc_attach_sd+0xe4/0x13c
    [    1.146294] [<ffffff8008c5dbd4>] mmc_rescan+0x288/0x2d8
    [    1.146296] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [    1.146298] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [    1.146300] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [    1.146302] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20

        mmc_set_initial_signal_voltage(host);

    drivers/mmc/host/dw_mmc-rockchip.c   static int dw_mci_rockchip_probe(struct platform_device *pdev)

    drivers/mmc/host/dw_mmc-rockchip.c:380: return dw_mci_pltfm_register(pdev, drv_data);

    drivers/mmc/host/dw_mmc-pltfm.c  int dw_mci_pltfm_register(struct platform_device *pdev,

    drivers/mmc/host/dw_mmc-pltfm.c:70:     return dw_mci_probe(host);

    drivers/mmc/host/dw_mmc.c    int dw_mci_probe(struct dw_mci *host)

    drivers/mmc/host/dw_mmc.c  static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)

    drivers/mmc/host/dw_mmc.c:2757: mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);

    drivers/mmc/core/host.c      struct mmc_host *mmc_alloc_host(int extra, struct device *dev)

    drivers/mmc/core/host.c:369:    INIT_DELAYED_WORK(&host->detect, mmc_rescan);

    drivers/mmc/core/host.c 里面是是通用接口函数 mmc 流程中

    drivers/mmc/core/core.c:2647:void mmc_rescan(struct work_struct *work)

    drivers/mmc/core/core.c--void mmc_rescan(struct work_struct *work)

    drivers/mmc/core/core.c--static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)

    drivers/mmc/core/sd.c:1370:int mmc_attach_sd(struct mmc_host *host)

    drivers/mmc/core/sd.c:939:static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)   1.8v

    drivers/mmc/core/core.c:1828:void mmc_power_cycle(struct mmc_host *host, u32 ocr)

    drivers/mmc/core/core.c  1764:       void mmc_power_up(struct mmc_host *host, u32 ocr)

    drivers/mmc/core/core.c1776      mmc_set_initial_signal_voltage(host);

    drivers/mmc/core/core.c 1065    mmc_set_ios(host);

    drivers/mmc/host/dw_mmc.c   1363    static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

                    ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,

    drivers/mmc/core/core.c 1408      int mmc_regulator_set_ocr(struct mmc_host *mmc,

            result = regulator_set_voltage(supply, min_uV, max_uV);

    drivers/regulator/core.c:3112         int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)

                                          2981        static int regulator_set_voltage_unlocked(struct regulator *regulator,int min_uV, int max_uV)

                                          2874        static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)

                                        3597        static int _notifier_call_chain(struct regulator_dev *rdev,

    kernel/notifier.c       325:int blocking_notifier_call_chain(struct blocking_notifier_head *nh,

                                   304 int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,

                                   74 static int notifier_call_chain(struct notifier_block **nl,     

     drivers/power/avs/rockchip-io-domain.c       supply->nb.notifier_call = rockchip_iodomain_notify;

                                            1585    void mmc_set_initial_signal_voltage(struct mmc_host *host)

                                            1619   int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)

                                                    err = host->ops->start_signal_voltage_switch(host, &host->ios);

    drivers/mmc/host/dw_mmc.c         static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)

    mmc 注册流程

    module_platform_driver(dw_mci_rockchip_pltfm_driver);

     static int dw_mci_rockchip_probe(struct platform_device *pdev)

    dw_mci_pltfm_register(pdev, drv_data);

      一些平台资源申请接下来

    dw_mci_probe(host); 

           dw_mci_parse_dt(host);   

                    devm_reset_control_get_optional(dev, "reset");

                    of_property_read_u32(dev->of_node, "num-slots"

                   of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);

                    of_property_read_u32(np, "clock-frequency", &clock_frequency)

                    of_find_property(np, "supports-highspeed", NULL)

            dw_mci_cto_timer

    dw_mci_init_slot(host, i);

    mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);

            INIT_DELAYED_WORK(&host->detect, mmc_rescan);(这里只初始化 ,后面会有调用过程)
     

                        

    mmc_regulator_get_supply(mmc);

            mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");

            mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");

    dw_mci_slot_of_parse(slot);

            of_property_read_bool(np, "disable-wp")

    mmc_of_parse(mmc)   // 设备树 的核心函数

    dw_mci_get_cd(mmc);

            mmc_gpio_get_cd(mmc);

                    gpiod_get_value_cansleep(ctx->cd_gpio);  //   平台sd卡检测

    mmc_add_host(mmc);

            device_add(&host->class_dev);

    mmc_start_host(host);

             _mmc_detect_change(host, 0, false);

                    mmc_schedule_delayed_work(&host->detect, delay);

                            前面初始化的 work  -》 void mmc_rescan(struct work_struct *work)

            

    mmc_rescan(struct work_struct *work)

                    mmc_rescan_try_freq(host, max(freqs[i], host->f_min))

                                    mmc_power_up(host, host->ocr_avail);

                                            mmc_set_initial_signal_voltage(host);

                                                    mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)

                                                            host->ios.signal_voltage = old_signal_voltage;

                            mmc_attach_sd(host)

                                    mmc_send_app_op_cond(host, 0, &ocr);

                                            mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES)

                                                    :#define SD_APP_OP_COND           41

                                    mmc_select_voltage(host, ocr);

                                    mmc_sd_init_card

                                    mmc_sd_get_cid(host, ocr, cid, &rocr);

                                            mmc_go_idle(host);

                                                    mmc_wait_for_cmd(host, &cmd, 0);     (CMD0)

                                                    mmc_send_if_cond(   )                         (CMD8)

                                                    mmc_host_uhs(host)   

                                                    mmc_send_app_op_cond(host, ocr, rocr)

                                                    -> mmc_set_uhs_voltage(host, MMC_SIGNAL_VOLTAGE_180

                                                              mmc_wait_for_cmd(host, &cmd, 0);     (CMD11)

                                                                     mmc_wait_for_req(host, &mrq);    

                                                            mmc_host_set_uhs_voltage(host, signal_voltage)  

                                                            mmc_delay(1);

                                                            mmc_power_cycle(host, ocr);

                                                                   mmc_power_off(host);
                                                                        /* Wait at least 1 ms according to SD spec */
                                                                  mmc_delay(1);
                                                                 mmc_power_up(host, ocr);

                                                                  mmc_set_initial_signal_voltage(host);

                                                                    mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180/3.3v)

                                               mmc_sd_get_csd(host, card);    

                                                            mmc_send_csd(card, card->raw_csd); 

                                              mmc_select_card(card);

                                              _mmc_select_card(card->host, card)  ;

                                               mmc_sd_card_using_v18(card)   

                                               mmc_read_switch(card);   

                                                         mmc_sd_switch(card, 0, 0, 0, status);    CMD6

                                            mmc_sd_card_using_v18(card)

                                            mmc_host_set_uhs_voltage(host, MMC_SIGNAL_VOLTAGE_180)

                                            mmc_sd_init_uhs_card(card)

                                                            mmc_set_ios(host);

                                                                   host->ops->set_ios(host, ios);

                                                           mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)

                                                            mmc_delay(10);

                                                            mmc_set_ios(host);

                                                  如果异常重新上电  mmc_power_cycle(host, ocr);

                                       如果dts没有配置告诉 但是   rocr & SD_ROCR_S18A     卡回复了支持  超高速仍会设置    mmc_sd_init_uhs_card(card)

                                            #define SD_ROCR_S18A          SD_OCR_S18R  /* 1.8V switching accepted by card */

                                     否则  mmc_sd_switch_hs(card);   尝试切换到高速模式  mmc_set_timing(card->host, MMC_TIMING_SD_HS);

                                        mmc_set_clock(host, mmc_sd_get_max_clock(card));

    如上 插入 sd 卡的 log 

    # dmesg -c 
    [  247.522189]  [1:    kworker/1:2:  177]  mmc_power_up  
    [  247.522219]  [1:    kworker/1:2:  177]  mmc_set_initial_state  
    [  247.522232]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.522239]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.522264]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.522274]  [1:    kworker/1:2:  177] Call trace:
    [  247.522291]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.522302]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.522316]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.522326]  [1:    kworker/1:2:  177] [<ffffff8008c5c9fc>] mmc_set_initial_state+0x80/0xec
    [  247.522335]  [1:    kworker/1:2:  177] [<ffffff8008c5cf70>] mmc_power_up+0x68/0x15c
    [  247.522345]  [1:    kworker/1:2:  177] [<ffffff8008c5db7c>] mmc_rescan+0x224/0x2d8
    [  247.522357]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.522369]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.522382]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.522391]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.522400]  [1:    kworker/1:2:  177] mmc1: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 1 timing 0
    [  247.522408]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.522416]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.522423]  [1:    kworker/1:2:  177]   MMC_POWER_UP  vmmc 
    [  247.522431]  [1:    kworker/1:2:  177]   dw_mci_switch_voltage  
    [  247.522439]  [1:    kworker/1:2:  177]   mmc_regulator_set_vqmmc 0
    [  247.522446]  [1:    kworker/1:2:  177]   mmc_regulator_set_vqmmc ++0
    [  247.522454]  [1:    kworker/1:2:  177] mmc_regulator_set_vqmmc: found vmmc voltage range of 3300000-3400000uV
    [  247.522462]  [1:    kworker/1:2:  177]  mmc_regulator_set_voltage_if_supported 3000000  3300000  3600000 
    [  247.522474]  [1:    kworker/1:2:  177]  mmc_regulator_set_voltage_if_supported 3000000  3300000  3600000  ok
    [  247.522481]  [1:    kworker/1:2:  177]   regulator_set_voltage  3300000   3600000  
    [  247.522489]  [1:    kworker/1:2:  177]  regulator_set_voltage_unlocked   3300000    3600000 
    [  247.522498]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=3300000  max_uV=3600000  
    [  247.522506]  [1:    kworker/1:2:  177]  regulator_check_voltage ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.522513]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=3300000  max_uV=3000000  
    [  247.522521]  [1:    kworker/1:2:  177] ppvar_sd_card_io: unsupportable voltage range: 3300000-3000000uV
    [  247.522529]  [1:    kworker/1:2:  177]   regulator_set_voltage  3000000   3600000  
    [  247.522536]  [1:    kworker/1:2:  177]  regulator_set_voltage_unlocked   3000000    3600000 
    [  247.522544]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=3000000  max_uV=3600000  
    [  247.522551]  [1:    kworker/1:2:  177]  regulator_check_voltage ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.522558]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=3000000  max_uV=3000000  
    [  247.522566]  [1:    kworker/1:2:  177]  regulator_check_consumers min_uV=3000000  max_uV=3000000  
    [  247.522573]  [1:    kworker/1:2:  177]  regulator_check_consumers ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.522581]  [1:    kworker/1:2:  177] aa regulator_check_consumers min_uV=3000000  max_uV=3000000  
    [  247.522588]  [1:    kworker/1:2:  177] aa regulator_check_consumers ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.522596]  [1:    kworker/1:2:  177] bb regulator_check_consumers fe310000.dwmmc-vqmmc ccmin_uV=3000000 ccmax_uV=3000000  
    [  247.522603]  [1:    kworker/1:2:  177]  regulator_check_consumers min_uV=3000000  max_uV=3000000  
    [  247.522621]  [1:    kworker/1:2:  177]  regulator   gpio 155 = set  0   (我这个项目 通过gpio 控制ldo 控制电压切换的)
    [  247.522635]  [1:    kworker/1:2:  177] dwmmc_rockchip fe310000.dwmmc: Initial signal voltage of 3.3v
    [  247.535173]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.535192]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.535210]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.535220]  [1:    kworker/1:2:  177] Call trace:
    [  247.535234]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.535245]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.535257]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.535266]  [1:    kworker/1:2:  177] [<ffffff8008c5cfc0>] mmc_power_up+0xb8/0x15c
    [  247.535275]  [1:    kworker/1:2:  177] [<ffffff8008c5db7c>] mmc_rescan+0x224/0x2d8
    [  247.535286]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.535295]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.535307]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.535316]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.535326]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 1 timing 0
    [  247.535333]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.535342]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.535352]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.547981]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.547999]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.548017]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.548027]  [1:    kworker/1:2:  177] Call trace:
    [  247.548040]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.548050]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.548062]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.548071]  [1:    kworker/1:2:  177] [<ffffff8008c5c6a4>] mmc_set_chip_select+0x2c/0x90
    [  247.548082]  [1:    kworker/1:2:  177] [<ffffff8008c626d4>] mmc_go_idle+0x3c/0xb0
    [  247.548091]  [1:    kworker/1:2:  177] [<ffffff8008c5dbb0>] mmc_rescan+0x258/0x2d8
    [  247.548102]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.548112]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.548123]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.548132]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.548166]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 width 1 timing 0
    [  247.548174]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.548182]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.548194]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.551198]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.551209]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.551220]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.551229]  [1:    kworker/1:2:  177] Call trace:
    [  247.551239]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.551249]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.551260]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.551269]  [1:    kworker/1:2:  177] [<ffffff8008c5c6a4>] mmc_set_chip_select+0x2c/0x90
    [  247.551280]  [1:    kworker/1:2:  177] [<ffffff8008c62720>] mmc_go_idle+0x88/0xb0
    [  247.551288]  [1:    kworker/1:2:  177] [<ffffff8008c5dbb0>] mmc_rescan+0x258/0x2d8
    [  247.551298]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.551308]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.551317]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.551326]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.551336]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 1 timing 0
    [  247.551343]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.551350]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.551359]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.552992]  [1:    kworker/1:2:  177]   mmc_attach_sd  
    [  247.553681]  [1:    kworker/1:2:  177] mmc_select_voltage  
    [  247.553695]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.553703]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.553717]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.553727]  [1:    kworker/1:2:  177] Call trace:
    [  247.553739]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.553749]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.553760]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.553769]  [1:    kworker/1:2:  177] [<ffffff8008c5c6a4>] mmc_set_chip_select+0x2c/0x90
    [  247.553780]  [1:    kworker/1:2:  177] [<ffffff8008c626d4>] mmc_go_idle+0x3c/0xb0
    [  247.553790]  [1:    kworker/1:2:  177] [<ffffff8008c63bac>] mmc_sd_get_cid+0x4c/0x144
    [  247.553800]  [1:    kworker/1:2:  177] [<ffffff8008c64214>] mmc_sd_init_card+0x68/0x304
    [  247.553810]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.553819]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.553829]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.553838]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.553848]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.553857]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.553867]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 width 1 timing 0
    [  247.553874]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.553881]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.553891]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.556486]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.556501]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.556519]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.556531]  [1:    kworker/1:2:  177] Call trace:
    [  247.556545]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.556557]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.556569]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.556580]  [1:    kworker/1:2:  177] [<ffffff8008c5c6a4>] mmc_set_chip_select+0x2c/0x90
    [  247.556592]  [1:    kworker/1:2:  177] [<ffffff8008c62720>] mmc_go_idle+0x88/0xb0
    [  247.556602]  [1:    kworker/1:2:  177] [<ffffff8008c63bac>] mmc_sd_get_cid+0x4c/0x144
    [  247.556614]  [1:    kworker/1:2:  177] [<ffffff8008c64214>] mmc_sd_init_card+0x68/0x304
    [  247.556624]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.556633]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.556643]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.556654]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.556665]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.556677]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.556687]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 1 timing 0
    [  247.556695]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.556702]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.556714]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.626469]  [1:    kworker/1:2:  177]  mmc_host_set_uhs_voltage 
    [  247.626495]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.626503]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.626522]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.626531]  [1:    kworker/1:2:  177] Call trace:
    [  247.626545]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.626555]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.626567]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.626577]  [1:    kworker/1:2:  177] [<ffffff8008c5cc68>] mmc_host_set_uhs_voltage+0x44/0x134
    [  247.626586]  [1:    kworker/1:2:  177] [<ffffff8008c5d4b0>] mmc_set_uhs_voltage+0xc4/0x164
    [  247.626597]  [1:    kworker/1:2:  177] [<ffffff8008c63c5c>] mmc_sd_get_cid+0xfc/0x144
    [  247.626607]  [1:    kworker/1:2:  177] [<ffffff8008c64214>] mmc_sd_init_card+0x68/0x304
    [  247.626617]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.626626]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.626637]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.626646]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.626657]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.626666]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.626676]  [1:    kworker/1:2:  177] mmc1: clock 0Hz busmode 2 powermode 2 cs 0 Vdd 21 width 1 timing 0
    [  247.626684]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.626691]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.626698]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.626717]  [1:    kworker/1:2:  177]   dw_mci_switch_voltage  
    [  247.626725]  [1:    kworker/1:2:  177]   mmc_regulator_set_vqmmc 1
    [  247.626732]  [1:    kworker/1:2:  177]   mmc_regulator_set_vqmmc ++1
    [  247.626740]  [1:    kworker/1:2:  177]  mmc_regulator_set_voltage_if_supported 1700000  1800000  1950000 
    [  247.626751]  [1:    kworker/1:2:  177]  mmc_regulator_set_voltage_if_supported 1700000  1800000  1950000  ok
    [  247.626759]  [1:    kworker/1:2:  177]   regulator_set_voltage  1800000   1950000  
    [  247.626767]  [1:    kworker/1:2:  177]  regulator_set_voltage_unlocked   1800000    1950000 
    [  247.626775]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=1800000  max_uV=1950000  
    [  247.626782]  [1:    kworker/1:2:  177]  regulator_check_voltage ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.626790]  [1:    kworker/1:2:  177]  regulator_check_voltage min_uV=1800000  max_uV=1950000  
    [  247.626797]  [1:    kworker/1:2:  177]  regulator_check_consumers min_uV=1800000  max_uV=1950000  
    [  247.626805]  [1:    kworker/1:2:  177]  regulator_check_consumers ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.626813]  [1:    kworker/1:2:  177] aa regulator_check_consumers min_uV=1800000  max_uV=1950000  
    [  247.626820]  [1:    kworker/1:2:  177] aa regulator_check_consumers ccmin_uV=1800000 ccmax_uV=3000000  
    [  247.626828]  [1:    kworker/1:2:  177] bb regulator_check_consumers fe310000.dwmmc-vqmmc ccmin_uV=1800000 ccmax_uV=1950000  
    [  247.626836]  [1:    kworker/1:2:  177]  regulator_check_consumers min_uV=1800000  max_uV=1950000  
    [  247.626853]  [1:    kworker/1:2:  177]  regulator   gpio 155 = set  1 
    [  247.626863]  [1:    kworker/1:2:  177] ppvar_sd_card_io: ramp_delay not set
    [  247.639437]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.639454]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.639472]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.639482]  [1:    kworker/1:2:  177] Call trace:
    [  247.639495]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.639505]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.639517]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.639526]  [1:    kworker/1:2:  177] [<ffffff8008c5cce4>] mmc_host_set_uhs_voltage+0xc0/0x134
    [  247.639535]  [1:    kworker/1:2:  177] [<ffffff8008c5d4b0>] mmc_set_uhs_voltage+0xc4/0x164
    [  247.639547]  [1:    kworker/1:2:  177] [<ffffff8008c63c5c>] mmc_sd_get_cid+0xfc/0x144
    [  247.639557]  [1:    kworker/1:2:  177] [<ffffff8008c64214>] mmc_sd_init_card+0x68/0x304
    [  247.639567]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.639575]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.639586]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.639595]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.639607]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.639616]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.639626]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 1 timing 0
    [  247.639633]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.639641]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.639652]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.647680]  [1:    kworker/1:2:  177] mmc  try to use  1.8v into uhs mode ???  
    [  247.648565]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.648578]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.648600]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.648611]  [1:    kworker/1:2:  177] Call trace:
    [  247.648628]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.648639]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.648653]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.648665]  [1:    kworker/1:2:  177] [<ffffff8008c5c918>] mmc_set_bus_width+0x2c/0x90
    [  247.648680]  [1:    kworker/1:2:  177] [<ffffff8008d0daf8>] mmc_sd_init_uhs_card+0x98/0x3b8
    [  247.648692]  [1:    kworker/1:2:  177] [<ffffff8008c64498>] mmc_sd_init_card+0x2ec/0x304
    [  247.648702]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.648712]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.648726]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.648736]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.648749]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.648758]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.648768]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 4 timing 0
    [  247.648776]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.648784]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.648799]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.649621]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.649630]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.649641]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.649650]  [1:    kworker/1:2:  177] Call trace:
    [  247.649659]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.649669]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.649680]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.649689]  [1:    kworker/1:2:  177] [<ffffff8008c5cd84>] mmc_set_timing+0x2c/0x90
    [  247.649701]  [1:    kworker/1:2:  177] [<ffffff8008d0ddb0>] mmc_sd_init_uhs_card+0x350/0x3b8
    [  247.649711]  [1:    kworker/1:2:  177] [<ffffff8008c64498>] mmc_sd_init_card+0x2ec/0x304
    [  247.649721]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.649731]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.649740]  [1:rker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.649749]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.649759]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.649768]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.649780]  [1:    kworker/1:2:  177] mmc1: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 4 timing 6
    [  247.649786]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.649794]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.649806]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.649815]  [1:    kworker/1:2:  177] CPU: 1 PID: 177 Comm: kworker/1:2 Not tainted 4.4.227-g245d34e4a-dirty #89
    [  247.649823]  [1:    kworker/1:2:  177] Hardware name: Pine64 RockPro64 (DT)
    [  247.649832]  [1:    kworker/1:2:  177] Workqueue: events_freezable mmc_rescan
    [  247.649840]  [1:    kworker/1:2:  177] Call trace:
    [  247.649850]  [1:    kworker/1:2:  177] [<ffffff8008088288>] dump_backtrace+0x0/0x220
    [  247.649861]  [1:    kworker/1:2:  177] [<ffffff80080884cc>] show_stack+0x24/0x30
    [  247.649871]  [1:    kworker/1:2:  177] [<ffffff8008414ab8>] dump_stack+0xa0/0xc8
    [  247.649881]  [1:    kworker/1:2:  177] [<ffffff8008c5c754>] mmc_set_clock+0x4c/0xb0
    [  247.649891]  [1:    kworker/1:2:  177] [<ffffff8008d0ddbc>] mmc_sd_init_uhs_card+0x35c/0x3b8
    [  247.649902]  [1:    kworker/1:2:  177] [<ffffff8008c64498>] mmc_sd_init_card+0x2ec/0x304
    [  247.649913]  [1:    kworker/1:2:  177] [<ffffff8008c64750>] mmc_attach_sd+0xf0/0x148
    [  247.649922]  [1:    kworker/1:2:  177] [<ffffff8008c5dbe0>] mmc_rescan+0x288/0x2d8
    [  247.649931]  [1:    kworker/1:2:  177] [<ffffff80080ce0c0>] process_one_work+0x218/0x370
    [  247.649941]  [1:    kworker/1:2:  177] [<ffffff80080cef84>] worker_thread+0x2e0/0x3a0
    [  247.649951]  [1:    kworker/1:2:  177] [<ffffff80080d3ea4>] kthread+0xdc/0xec
    [  247.649960]  [1:    kworker/1:2:  177] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
    [  247.649969]  [1:    kworker/1:2:  177] mmc1: clock 50000000Hz busmode 2 powermode 2 cs 0 Vdd 21 width 4 timing 6
    [  247.649976]  [1:    kworker/1:2:  177]  dw_mci_set_ios 
    [  247.649984]  [1:    kworker/1:2:  177]  dw_mci_rk3288_set_ios  
    [  247.650062]  [1:    kworker/1:2:  177]   MMC_POWER_ON   vqmmc
    [  247.650079]  [1:    kworker/1:2:  177] mmc_host mmc1: Bus speed (slot 0) = 50000000Hz (slot req 50000000Hz, actual 50000000HZ div = 0)
    [  247.666584]  [1:    kworker/1:2:  177] dwmmc_rockchip fe310000.dwmmc: All phases work, using default phase 0.
    [  247.666614]  [1:    kworker/1:2:  177] mmc  use  1.8v   UHS mode 
    [  247.666636]  [1:    kworker/1:2:  177] mmc1: new ultra high speed SDR104 SDHC card at address 1234
    [  247.667755]  [1:    kworker/1:2:  177] mmcblk1: mmc1:1234 SA16G 14.5 GiB 
    [  247.668919]  [1:    kworker/1:2:  177]  mmcblk1: p1 p2  

                                                                                                                                            

    上述基彩色 log 是关键 函数,基本上是  mmc_rescan  配置属性,设置电压 ,get cid (如果uhs 切1.8v,切换不成功也会继续跑流程,因为卡可以在高压工作)

     获取cid 流程中 会上电,会跑cmd0 稳定后    cmd8 获取电压  cmd 55  acmd41  收到 回复41xx xxxx ,就尝试切超高速UHS (CMD11) , mmc_set_uhs_voltage( 切换1.8v )

    获取到 cid 后   跑  mmc_attach_sd  初始化    mmc_sd_init_card    -》 get cid ---   mmc_sd_get_csd  (看情况 切 UHS --1.8v),最后   mmc_add_card,就是算识别到卡

     

     

     

     

     

      

    展开全文
  • 第88章 STM32H7的SDMMC总线应用之SD卡移植FatFs文件系统 本章节为大家讲解SD卡的FatFs文件系统移植。 目录 第88章 STM32H7的SDMMC总线应用之SD卡移植FatFs文件系统 88.1 初学者重要提示 88.2 SD卡硬件接口设计 ...
  • 一个SDMMC驱动器更换为任天堂的文件系统服务,通过m4xw 支持的Horizo​​n版本 1.0.0-11.0.0 特征 任意SDMMC后端选择这允许从SD加载eMMC甚至从eMMC加载SD 随时进行挂钩/修补,完全自我感染所有版本只需要一个有效...
  • ESP32 SDMMC技术文档翻译

    千次阅读 2018-03-08 19:39:07
    SDMMC 主机外设概述SDMMC 外设支持SD卡、MMC卡和SDIO卡。SDMMC的软件是基于SDMMC驱动之上的,由下面几个部分组成。SDMMC主机外设(driver/sdmmc_host.h)---驱动支持APIs接口发送命令到从机设备,发送和接受...
  • SDMMC理论基础知识 • SDMMC指的是SD、SDIO 、MMC 卡主机接口,提供APB2外设总线 多媒体卡(MMCs),数字安全记忆卡(SD)和SDIO卡。 • MMC的全称是”MultiMediaCard”――所以也通常被叫做”多媒 体卡”,是一种...
  • 根據官方文件"Getting started with STM32H7 Series SDMMC host controller",H743 的 SDMMC 已經內建 DMA,所以,在CubeMX 不會出現 DMA 選項可以勾選 SDMMC 的 DMA 必須在 MDMA 勾選設定。 在CubeMX,使用F
  • stm32f103 HAL库sdmmc bug

    千次阅读 2019-12-29 16:10:03
    stm32f103 HAL库sdmmc bug 发布版本: 1.0 文件密级: 公开资料 前言 概述 读者 本文档(本指南)主要使用于以下工程师: 软件开发工程师 产品版本 修订记录 日期 版本 作者/邮箱 修订说明 2019-12-29 V...
  • 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SDMMC外设读取SD卡数据。 1. 准备工作 硬件准备 开发板 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): Micro SD卡 小熊派...
  • 1.SDMMC使用的是4线,STM32CubeMX勾上后使用默认参数,中断打开,优先级调得比较低。 2.SDMMC时钟我试过60MHz和80MHz的,都正常,而用100MHz后就有点问题,不知道是不是SD卡的问题还是总线的限制。 3.Fatfs勾上了...
  • 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件SDMMC外设读取SD卡数据。 1. 准备工作 硬件准备 开发板 首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi): Micro SD卡 小熊派...
  • TQ2440的SDMMC的应用

    2012-12-14 09:37:23
    2440的SDMMC的应用 这是赵春江老师博客摘录下来TQ2440开发板裸机程序,我基本上验证过,转发给大家,希望对大家有些帮助,多多交流,一起进步。
  • ESP32 学习笔记(十七)SDMMC Host Driver

    千次阅读 2018-11-15 17:07:42
    SDMMC Host Driver概述 概述 在ESP32上,SDMMC主机外设有两个插槽: 插槽0(SDMMC_HOST_SLOT_0)是一个8位插槽。 它使用 PIN MUX 中的 HS1_ * 信号。 插槽1(SDMMC_HOST_SLOT_1)是一个4位插槽。 它使用 PIN ...
  • SDMMC学习

    2020-11-27 11:15:45
    SDMMC学习 这几天略微看了下MMC/SD相关代码,梳理如下 在Linux/driver/mmc目录下有三个文件夹:card、host、core card目录:queue.c,block.c,顾名思义是相关request和相关块设备的操作文件 core目录: bus.c:总线...
  • MMC 概念 MMC的全称是”MultiMediaCard”――所以也通常被叫做”多媒体卡”,是一种小巧大容量的快闪存储卡,特别应用于移动电话和数字影像及其他移动终端中。 外形及接口定义 如上图所示,MMC存贮卡只有7pin,...
  • 记录STM32H743使用STMCUBEMX配置SDMMC

    千次阅读 2019-12-05 16:39:04
    1、新建工程 ...(2)配置SDMMC1 4、SD卡初始化 int sdcard_status = 0; HAL_SD_CardCIDTypedef sdcard_cid; MX_SDMMC1_SD_Init(); fre = HAL_RCC_GetSysClockFreq(); printf("%d\r\n",fre)...
  • pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>; 配置 SD 卡电路的 IOMUX 功能,为必须配置项。  盈鹏飞嵌入式专注于Atmel、TI、NXP、Rockchip等平台产品的研发。公司团队拥有超过10年的ARM软硬件...
  • 行业文档-设计装置-SDMMC卡的开放式读写控制方法.zip
  • 教育科研-学习工具-SDMMC卡的开放式读写控制方法.zip
  • 先来一张中文参考手册中的系统架构图,可以看到,STM32H7系列包含两个SDMMC控制器:SDMMC1和SDMMC2。 在芯片手册中的架构图能够更直观地看出来: SDMMC内部集成一个IDMA,H7系列有很多总线主设备,包括DMA1、DMA2...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,834
精华内容 733
关键字:

SDMMC

友情链接: nobootmem.rar