## SD ALIENTKE 战舰 STM32F103 自带了标准的 SD 卡接口 4 位模式,最高通信速度可达 24Mhz,最高每秒可传输数据 12M 字节 支持三种不同的数据总线模式:1 位(默认)、4 位和 8 位。 ***SD SDIO是不同的*** ### 时钟 SDIO_CK时钟是通过PC12引脚连接到SD卡的,是SDIO接口与SD卡用于同步的时钟。 SDIO选配器挂载到AHB总线上,通过HCLK二分频输入到适配器得到SDIO_CK的时钟,这时SDIO_CK = HCLK/(2+CLKDIV)。其中CLKDIV是SDIO_CLK(寄存器)中的CLKDIV位。 另外,SDIO_CK也可以由SDIOCLK通过设置bypass模式直接得到,这时SDIO_CK = SDIOCLK=HCLK 对SD卡的操作一般是大吞吐量的数据传输,所以采用DMA来提高效率,SDIO采用的是DMA2中的通道4。 SDIO_CK = SDIOCLK / (ClockDiv + 2) ***写注意事项*** ### 配置 98 PC8 SDIO_D0 N SD 卡接口的 D0 99 PC9 SDIO_D1 N SD 卡接口的 D1 111 PC10 SDIO_D2 N SD 卡接口的 D2 112 PC11 SDIO_D3 N SD 卡接口的 D3 113 PC12 SDIO_SCK Y SD 卡接口的 SCK 选 SD 4 bits Wide bus SD 1 bits 单线SD模式 SD 4 bits Wide bus 4线SD模式 MMC 1 bit MMC卡单线模式 MMC 4 bits Wide bus MMC卡4线模式 MMC 8 bits Wide bus MMC卡8线模式 第一行相当于是在时钟的上升沿还是下降沿采集数据,一般都是上升沿。 第二行决定是否使用bypass(旁路),即跳过分频直接从HCLK得到时钟,那样频率会非常高,所以也不需要。 第三行空闲时,也就是在没有数据交互的时候是否关闭时钟电源,在不考虑功耗的情况下还是尽量不要用比较好。 第四行硬件流控,可用于解决数据量大或者高速传输时的速度匹配和准确率不足等问题,这里不需要。 第五行,时钟分频,与读写速度有关,这里不多说了,时钟树配好48M的话这里选0就好。 #### SD卡检测引脚 PE4 High PP ***SDIO不需要*** #### SD 初始化代码 没有挂载文件系统之前的 ```C void MX_SDIO_SD_Init(void) { /* USER CODE BEGIN SDIO_Init 0 */ /* USER CODE END SDIO_Init 0 */ /* USER CODE BEGIN SDIO_Init 1 */ /* USER CODE END SDIO_Init 1 */ hsd.Instance = SDIO; hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE; hsd.Init.BusWide = SDIO_BUS_WIDE_4B; hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; hsd.Init.ClockDiv = 0; if (HAL_SD_Init(&hsd) != HAL_OK) { Error_Handler(); } if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN SDIO_Init 2 */ /* USER CODE END SDIO_Init 2 */ } ``` ```C HAL_SD_CardInfoTypeDef SDCardInfo; void show_sdcard_info(void) { uint64_t CardCap; //SD卡容量 HAL_SD_CardCIDTypeDef SDCard_CID; HAL_SD_GetCardCID(&hsd,&SDCard_CID); //获取CID HAL_SD_GetCardInfo(&hsd,&SDCardInfo); //获取SD卡信息 switch(SDCardInfo.CardType) { case CARD_SDSC: { if(SDCardInfo.CardVersion == CARD_V1_X) printf("Card Type:SDSC V1\r\n"); else if(SDCardInfo.CardVersion == CARD_V2_X) printf("Card Type:SDSC V2\r\n"); } break; case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break; } CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize); //计算SD卡容量 printf("Card ManufacturerID:%d\r\n",SDCard_CID.ManufacturerID); //制造商ID printf("Card RCA:%d\r\n",SDCardInfo.RelCardAdd); //卡相对地址 printf("LogBlockNbr:%d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr)); //显示逻辑块数量 printf("LogBlockSize:%d \r\n",(uint32_t)(SDCardInfo.LogBlockSize)); //显示逻辑块大小 printf("Card Capacity:%d MB\r\n",(uint32_t)(CardCap>>20)); //显示容量 printf("Card BlockSize:%d\r\n\r\n",SDCardInfo.BlockSize); //显示块大小 } ``` ### 测试程序 测试SD是否检测到 挂在文件系统测试 ```C int SDTest(void){ // 挂载文件系统 retSD = f_mount(&SDFatFS, SDPath, 1); if(retSD) { SEGGER_RTT_printf(0,"mount error : %d \r\n",retSD); return -1; } else { SEGGER_RTT_printf(0,"mount sucess!!! \r\n"); } HAL_SD_CardCIDTypeDef CID; HAL_StatusTypeDef s1 = HAL_SD_GetCardCID(&hsd, &CID); HAL_SD_CardCSDTypeDef CSD; HAL_StatusTypeDef s2 = HAL_SD_GetCardCSD(&hsd, &CSD); HAL_SD_CardStatusTypeDef status; HAL_StatusTypeDef s3 = HAL_SD_GetCardStatus(&hsd, &status); HAL_SD_CardInfoTypeDef cardInfo; HAL_StatusTypeDef s4 = HAL_SD_GetCardInfo(&hsd, &cardInfo); uint32_t fCardSize = 1.0*cardInfo.BlockNbr*cardInfo.BlockSize/1024/1024; SEGGER_RTT_printf(0,"BlockNum=%d BlockSize=%d \r\n",cardInfo.BlockNbr,cardInfo.BlockSize); SEGGER_RTT_printf(0,"Card Size:%d M\r\n", fCardSize); return 1; } ``` ### SD实例化 attenison: 这个不能配置为 4B, 就是启动的时候,用1B hsd.Init.BusWide = SDIO_BUS_WIDE_1B; 后面配置为4B, 否则这个地方会报错 if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { Error_Handler(); } PE SD_Insert Pull_down Input 检测不到SD卡 不启动 测试记录: 有无配SDIO的DMA 只要hsd.Init.BusWide = SDIO_BUS_WIDE_1B 都能通过检测 改为4B ,注销HAL_SD_ConfigWideBusOperation后面的修改也可以通过检测,不能获取SD卡状态 优先级 SDIO 》DMA 》 USB