赞
踩
写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
MIMXRT1050 EVKB评估板上有一个SD卡插槽(J20)。J20是用于 USDHC1接口的 Micro SD插槽。
SD 卡一般都支持 SDIO 和 SPI 这两种接口,Micro SD卡接口模式引脚定义如下:
Micro SD卡与 SD卡相比,Micro SD卡只有 8个引脚是因为比SD卡少了一个 Vss,区别如下图:
在评估板上,是使用了 SDIO 接口来操作 Micro SD卡的,(下文统一以 SD卡称呼),其硬件电路如下:
在 RT1052上,提供了 uSDHC这个外设功能,它的信号引脚定义如下:
总的概括如下:
其中,CD,WP,LCTL,RST和 VSELECT对于系统实现都是可选的。 如果 uSDHC需要支持 4位数据传输,则 DAT7〜DAT4 也可以是可选的并绑定为高电平;如果 uSDHC不支持 HS400模式,则 STROBE也可以是可选的并绑定为低电平。
从上图可知,uSDHC 根时钟有 2 个可选输入来源(以 uSDHC1为例,红色线跟蓝色线部分):
最后配置出来的时钟一般是 198 MHz,这也是最大时钟频率。在官方例程中,利用的时钟线就是上面的蓝色线路部分。
从上图可知,RT1052的 uSDHC外设支持 DMA传输技术的。
直接内存访问(Direct Memory Access,DMA)是计算机科学中的一种内存访问技术。它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载;否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方;在这个时间中,CPU 对于其他的工作来说就无法使用。
在 uSDHC 中使用了一个可配置的数据缓冲区以优化的方式在系统总线(IP总线或 AHB总线)和 SD卡之间传输数据,从而最大化两个时钟域(IP外设时钟和主时钟)之间的吞吐量。
该缓冲区用作在主机系统和卡之间传输数据的临时存储。 支持配置读写水印值(1~128 个字)以及配置读写突发长度(1~31 个字)。
写入操作序列:
当用户将数据传输到卡时,有两种将数据写入缓冲区的方法:
读取操作序列:
当用户将数据传输到卡时,有两种从缓冲区读取数据的方法:
在官方的工程里面,已经有包含移植了 SDMMC这一类的驱动文件了,所以我们只需要把相应的文件添加到自己的工程文件里就好了,如下图:
接着,还需要一个驱动上层的配置文件(sdmmc_config.c),这个文件在官方SDK包的 …\boards\evkbimxrt1050\sdmmc_examples\sdcard_polling 路径下找到,如下图:
然后把上面用到的全部提取到个人的工程文件夹里面,放到下图的路径文件夹中;其中 port文件夹里存放的是上图的 sdmmc_config两个文件:
对于 SDCard所需的文件有以上框选的几个,其中 osa文件夹值得注意一下,这个文件夹里其实存放一个引用了 OS处理的上级 API文件,这是因为在官方提供的 SDCard驱动中,是需要一个类似于 OS操作的处理;同时,还需要把组件里面的 fsl_os_abstraction_bm.c文件(路径:…\components\osa)添加到工程,这个文件就是上面的 osa文件夹里所依赖的模拟 OS处理文件,并且可以在这个文件的开头得到 /* This is the source file for the OS Abstraction layer for MQXLite. */
这个信息,若是使用 FreeRTOS,则需要选取另外一个文件;另外,后缀带 “ _bm ” 的,其实就是不外带 OS的支持文件;然后添加到的工程架构如下图:
最后,在原有的预处理宏上,还需要再添加以下宏去使能相应的代码:
FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL=1, SD_ENABLED
底层驱动有了,那么,我们还需要一个例程来校验,添加以下代码:
sd_card.c 源文件
#include "sd_card.h" #include "sdmmc_config.h" #include "pin_mux.h" #include "board.h" #include "fsl_debug_console.h" /* SD Card硬件检测使能 */ #define SD_DETECT_ENABLE 1 #define BOARD_USDHC1_CLK_FREQ (CLOCK_GetSysPfdFreq(kCLOCK_Pfd2) / (CLOCK_GetDiv(kCLOCK_Usdhc1Div) + 1U)) #define BOARD_SD_HOST_CLK_FREQ BOARD_USDHC1_CLK_FREQ /*! @brief Data block count accessed in card */ #define DATA_BLOCK_COUNT (5U) /*! @brief Start data block number accessed in card */ #define DATA_BLOCK_START (2U) /*! @brief Data buffer size. */ #define DATA_BUFFER_SIZE (FSL_SDMMC_DEFAULT_BLOCK_SIZE * DATA_BLOCK_COUNT) /*! @brief Card descriptor. */ sd_card_t g_sd; /*! @brief sdmmc dma buffer */ AT_NONCACHEABLE_SECTION_ALIGN(static uint32_t s_sdmmcHostDmaBuffer[BOARD_SDMMC_HOST_DMA_DESCRIPTOR_BUFFER_SIZE], SDMMCHOST_DMA_DESCRIPTOR_BUFFER_ALIGN_SIZE); /*! @brief Data written to the card */ SDK_ALIGN(uint8_t g_dataWrite[DATA_BUFFER_SIZE], BOARD_SDMMC_DATA_BUFFER_ALIGN_SIZE); /*! @brief Data read from the card */ SDK_ALIGN(uint8_t g_dataRead[DATA_BUFFER_SIZE], BOARD_SDMMC_DATA_BUFFER_ALIGN_SIZE); #if (0 == SD_DETECT_ENABLE) static sd_detect_card_t s_cd; static sdmmchost_t s_host; OSA_EVENT_HANDLE_DEFINE(host_event); #endif /* SD_DETECT_ENABLE */ static bool s_isReadOnly; static void SDCard_ClockConfig(void); static status_t AccessCard(sd_card_t *card, bool isReadOnly); static void CardInformationLog(sd_card_t *card); /************************************************ 函数名称 : SDCard_Test 功 能 : SD Card测试 参 数 : 无 返 回 值 : -1 / 0 *************************************************/ int SDCard_Test(void) { sd_card_t *card = &g_sd; char ch = '0'; while (ch != 'q') { PRINTF("\r\nRead/Write/Erase the card continuously until encounter error......\r\n"); for (;;) { if (kStatus_Success != AccessCard(card, s_isReadOnly)) { /* access card fail, due to card remove. */ if (SD_IsCardPresent(card) == false) { SD_HostDoReset(card); PRINTF("\r\nCard removed\r\n"); PRINTF( "\r\nInput 'q' to quit read/write/erase process.\ \r\nInput other char to wait card re-insert.\r\n"); ch = GETCHAR(); PUTCHAR(ch); } /* access card fail, due to transfer error */ else { ch = 'q'; } break; } else { PRINTF("\r\nInput 'q' to quit read/write/erase process.\ \r\nInput other char to read/write/erase data blocks again.\r\n"); ch = GETCHAR(); PUTCHAR(ch); if (ch == 'q') { break; } } } } PRINTF("\r\nThe example will not read/write data blocks again.\r\n"); SD_Deinit(card); return 0; } /************************************************ 函数名称 : SDCard_Init 功 能 : SD Card用户初始化 参 数 : 无 返 回 值 : 无 *************************************************/ void SDCard_Init(void) { sd_card_t *card = &g_sd; #if SD_DETECT_ENABLE BOARD_SD_Config(card, NULL, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY, NULL); #else SDCard_ClockConfig(); card->host = &s_host; card->host->dmaDesBuffer = s_sdmmcHostDmaBuffer; // dma缓冲区 card->host->dmaDesBufferWordsNum = BOARD_SDMMC_HOST_DMA_DESCRIPTOR_BUFFER_SIZE;// dma缓冲区大小 card->host->hostController.base = BOARD_SDMMC_SD_HOST_BASEADDR; // 主机外设地址 card->host->hostController.sourceClock_Hz = BOARD_SD_HOST_CLK_FREQ; // 时钟频率 card->host->hostEvent = &host_event; // 事件处理程序指针 card->usrParam.cd = &s_cd; // 卡检测回调函数指针(这里主要去除断言警告) NVIC_SetPriority(BOARD_SDMMC_SD_HOST_IRQ, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY); #endif /* SD_DETECT_ENABLE */ /* SD host init function */ if (SD_HostInit(card) != kStatus_Success) { PRINTF("\r\nSD host init fail\r\n"); } PRINTF("\r\nPlease insert a card into board.\r\n"); #if SD_DETECT_ENABLE /* power off card */ SD_SetCardPower(card, false); /* wait card insert */ SD_PollingCardInsert(card, kSD_Inserted); /* power on the card */ SD_SetCardPower(card, true); PRINTF("\r\nCard inserted.\r\n"); #else PRINTF("\r\nWait Card initialization......\r\n"); /* power on the card */ SD_SetCardPower(card, true); #endif /* SD_DETECT_ENABLE */ /* Init card. */ if (SD_CardInit(card)){ PRINTF("\r\nSD card init failed.\r\n"); } else{ PRINTF("\r\nSD card init succeed.\r\n"); } /* card information log */ CardInformationLog(card); /* Check if card is readonly. */ s_isReadOnly = SD_CheckReadOnly(card); } /************************************************ 函数名称 : SDCard_ClockConfig 功 能 : SD Card时钟配置 参 数 : 无 返 回 值 : 无 *************************************************/ static void SDCard_ClockConfig(void) { /* 检查是否预定义了宏 SKIP_SYSCLK_INIT, 一般是默认定义了的,若是定义了, 那么在 clock_config.c中 sys pll是停止 关闭配置的交由用户自己配置选择。 */ CLOCK_InitSysPll(&sysPllConfig_BOARD_BootClockRUN); /*configure system pll PFD2 fractional divider to 24, output clock is 528MHZ * 18 / 24 = 396 MHZ*/ CLOCK_InitSysPfd(kCLOCK_Pfd2, 24U); /* Configure USDHC clock source and divider */ CLOCK_SetDiv(kCLOCK_Usdhc1Div, 1U); /* USDHC clock root frequency maximum: 198MHZ */ CLOCK_SetMux(kCLOCK_Usdhc1Mux, 0U); } /************************************************ 函数名称 : AccessCard 功 能 : 存取卡数据 参 数 : card ---- sd卡结构体指针 isReadOnly ---- 只读操作 返 回 值 : status_t ---- 状态 *************************************************/ static status_t AccessCard(sd_card_t *card, bool isReadOnly) { if (isReadOnly) { PRINTF("\r\nRead one data block......\r\n"); if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, DATA_BLOCK_START, 1U)) { PRINTF("Read one data block failed.\r\n"); return kStatus_Fail; } PRINTF("Read multiple data blocks......\r\n"); if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, DATA_BLOCK_START, DATA_BLOCK_COUNT)) { PRINTF("Read multiple data blocks failed.\r\n"); return kStatus_Fail; } } else { memset(g_dataWrite, 0x67U, sizeof(g_dataWrite)); PRINTF("\r\nWrite/read one data block......\r\n"); if (kStatus_Success != SD_WriteBlocks(card, g_dataWrite, DATA_BLOCK_START, 1U)) { PRINTF("Write one data block failed.\r\n"); return kStatus_Fail; } memset(g_dataRead, 0U, sizeof(g_dataRead)); if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, DATA_BLOCK_START, 1U)) { PRINTF("Read one data block failed.\r\n"); return kStatus_Fail; } PRINTF("Compare the read/write content......\r\n"); if (memcmp(g_dataRead, g_dataWrite, FSL_SDMMC_DEFAULT_BLOCK_SIZE)) { PRINTF("The read/write content isn't consistent.\r\n"); return kStatus_Fail; } PRINTF("The read/write content is consistent.\r\n"); PRINTF("Write/read multiple data blocks......\r\n"); if (kStatus_Success != SD_WriteBlocks(card, g_dataWrite, DATA_BLOCK_START, DATA_BLOCK_COUNT)) { PRINTF("Write multiple data blocks failed.\r\n"); return kStatus_Fail; } memset(g_dataRead, 0U, sizeof(g_dataRead)); if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, DATA_BLOCK_START, DATA_BLOCK_COUNT)) { PRINTF("Read multiple data blocks failed.\r\n"); return kStatus_Fail; } PRINTF("Compare the read/write content......\r\n"); if (memcmp(g_dataRead, g_dataWrite, FSL_SDMMC_DEFAULT_BLOCK_SIZE)) { PRINTF("The read/write content isn't consistent.\r\n"); return kStatus_Fail; } PRINTF("The read/write content is consistent.\r\n"); PRINTF("Erase multiple data blocks......\r\n"); if (kStatus_Success != SD_EraseBlocks(card, DATA_BLOCK_START, DATA_BLOCK_COUNT)) { PRINTF("Erase multiple data blocks failed.\r\n"); return kStatus_Fail; } } return kStatus_Success; } /************************************************ 函数名称 : CardInformationLog 功 能 : 卡信息打印 参 数 : card ---- sd卡结构体指针 返 回 值 : 无 *************************************************/ static void CardInformationLog(sd_card_t *card) { assert(card); PRINTF("\r\nCard size %d * %d bytes\r\n", card->blockCount, card->blockSize); // sd card内存大小 PRINTF("\r\nWorking condition:\r\n"); /* 工作电压 */ if (card->operationVoltage == kSDMMC_OperationVoltage330V) { PRINTF("\r\n Voltage : 3.3V\r\n"); // 3.3V } else if (card->operationVoltage == kSDMMC_OperationVoltage180V) { PRINTF("\r\n Voltage : 1.8V\r\n"); // 1.8V } /* 时序模式 */ if (card->currentTiming == kSD_TimingSDR12DefaultMode) { if (card->operationVoltage == kSDMMC_OperationVoltage330V) { PRINTF("\r\n Timing mode: Default mode\r\n"); // 常规模式 } else if (card->operationVoltage == kSDMMC_OperationVoltage180V) { PRINTF("\r\n Timing mode: SDR12 mode\r\n"); // SDR12 模式 } } else if (card->currentTiming == kSD_TimingSDR25HighSpeedMode) { if (card->operationVoltage == kSDMMC_OperationVoltage180V) { PRINTF("\r\n Timing mode: SDR25\r\n"); // SDR25 模式 } else { PRINTF("\r\n Timing mode: High Speed\r\n"); // 高速模式 } } else if (card->currentTiming == kSD_TimingSDR50Mode) { PRINTF("\r\n Timing mode: SDR50\r\n"); // SDR50 模式 } else if (card->currentTiming == kSD_TimingSDR104Mode) { PRINTF("\r\n Timing mode: SDR104\r\n"); // SDR104 模式 } else if (card->currentTiming == kSD_TimingDDR50Mode) { PRINTF("\r\n Timing mode: DDR50\r\n"); // DDR50 模式 } PRINTF("\r\n Freq : %d HZ\r\n", card->busClock_Hz); // 频率 } /*---------------------------- END ----------------------------*/
sd_card.h 头文件
#ifndef __SD_CARD_H #define __SD_CARD_H #include "MIMXRT1052.h" #include "fsl_usdhc.h" int SDCard_Test(void); void SDCard_Init(void); #endif /* __SD_CARD_H */ /*---------------------------- END ----------------------------*/
从上面的引脚说明那里知道,CD,WP,LCTL,RST和 VSELECT的引脚都是可选的。
所以有时候因为成本或者引脚紧凑之类,往往不使用硬件检测功能,所以这里可以像上面的代码一样利用宏 SD_DETECT_ENABLE 来确定是否启用硬件检测功能,但一旦取消检测功能,那么就要在设备上电运行之前插好 SDCard,否则就会出现死机卡死。
其中,BOARD_SD_Config();函数主要就是用来配置硬件检测功能引脚的处理函数;另外,值得注意的是,BOARD_SD_Config();函数里面的时钟配置用的是 sys pll pfd0的来源时钟,而当取消硬件检测功能时,可以看到执行的 SDCard_ClockConfig();函数是使用 sys pll pfd2的来源时钟。
sd_card.c 头注:
/* * ReadMe: * 下面例程只是演示 SD Card操作的演示; * 引脚配置在 sdmmc_config.h中更改; * SD Card插入检测是使用阻塞轮训的,非中断处理; * 中断处理可以参考官网 SDK包里的以下工程路径: * ...\boards\evkbimxrt1050\sdmmc_examples\sdcard_interrupt * * 注意:如果将 DATA3用作卡检测 PIN, * 请确保 DATA3被下拉,无论是内部还是外部, * 同时确保卡可以拉 DATA3,然后主机可以通过 DATA3检测卡。 * 而 SDHC不支持主机通过 CD检测卡,可以通过 DATA3或 GPIO检测卡。 * 无论通过主机还是gpio检测卡,都要确保 pinmux配置正确。 * 需要添加以下宏: * (FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL=1, SD_ENABLED) * * 当 SD Card硬件检测使能时,时钟用的是 sys pll pfd0, * 不使用时,时钟用的是 sys pll pfd2。 */ /* 打印示例: [2020-08-17 17:13:52.118]# RECV ASCII> Please insert a card into board. [2020-08-17 17:13:58.809]# RECV ASCII> Card inserted. [2020-08-17 17:13:59.076]# RECV ASCII> Card size 247808 * 512 bytes Working condition: Voltage : 3.3V Timing mode: High Speed Freq : 49500000 HZ [2020-08-17 17:14:10.909]# RECV ASCII> Read/Write/Erase the card continuously until encounter error...... Write/read one data block...... Compare the read/write content...... The read/write content is consistent. Write/read multiple data blocks...... Compare the read/write content...... The read/write content is consistent. Erase multiple data blocks...... Input 'q' to quit read/write/erase process. Input other char to read/write/erase data blocks again. [2020-08-17 17:14:20.992]# SEND ASCII> q [2020-08-17 17:14:21.041]# RECV ASCII> q The example will not read/write data blocks again. */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。