STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)一、SDIO简介SD卡(SecureDigitalMemoryCard)在我们生活中已经非常普遍了,控制器对SD卡进行读写通信操作一般有两种通信接口可选,一种是SPI接口,另外一种就是SDIO接口。SDIO全称是安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SDI/O卡都有SDIO接口。MMC卡可以说是SD卡的前身,现阶段已经用得很少。STM32F10x系列控制器有一个SDIO主机接口,它可以与MMC卡、SD卡、SDI/O卡以及CE-A

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一、SDIO简介

SD 卡(Secure Digital Memory Card) 在我们生活中已经非常普遍了,控制器对 SD 卡进行读写通信操作一般有两种通信接口可选,一种是 SPI 接口,另外一种就是 SDIO 接口。

SDIO 全称是安全数字输入/输出接口,多媒体卡(MMC)、SD 卡、SD I/O 卡都有 SDIO 接口。 MMC 卡可以说是 SD 卡的前身,现阶段已经用得很少。STM32F10x 系列控制器有一个 SDIO 主机接口,它可以与 MMC 卡、SD 卡、SD I/O 卡以及 CE-ATA 设备进行数据传输。另外,STM32F10x 系列控制器的 SDIO 是不支持 SPI 通信模式的,如果需要用到 SPI 通信只能使用 SPI 外设。

STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

二、SD卡

SD卡除了SD-Micro卡之外,还有两种分别是SD和mini-SD,他们分别长这样:
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
SD卡的四个角有一个是没有的,以便我们认识正反来使用它,SD卡的一侧还有一个可以扳动的读写保护开关,这三种卡里面SD卡最大,SD-Micro最小。

根据SD卡的容量,可划分为SDSC(SD Standard Capacity)、SDHC(SD High Capacity)、SDXC(SD Extended Capacity)三种标准。现今,市场的主流SD产品是SDHC和SDXC这两种较大容量的存储卡,而SDSC卡因容量过小,已逐渐被市场淘汰。SD卡(三种卡的统称)的存储空间是由一个一个扇区组成的,SD卡的扇区大小是固定的,为512byte(这一点很重要) ,若干个扇区又可以组成一个分配单元(也被成为簇),分配单元常见的大小为4K、8K、16K、32K、64K。

需要注意的是,SD-Micro只有8个引脚,而SD卡是有九个引脚的,这两种都可以直线4线通讯。

三、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

2. 选择 MCU 和封装
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

四、SDIO

STM32 控制器可以控制使用单线或 4 线传输,本开发板设计使用 4 线传输。

STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

4.1 参数配置

Connetivity 中选择 SDIO 设置,并选择 SD 4 bits Wide bus 四线SD模式
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
此时 SDIO 对应的管脚也被选中。
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
Parameter Settings 进行具体参数配置。

Clock transition on which the bit capture is made: Rising transition。主时钟 SDIOCLK 产生 CLK 引脚时钟有效沿选择,可选上升沿或下降沿,它设定 SDIO 时钟控制寄存器(SDIO_CLKCR)的 NEGEDGE 位的值,一般选择设置为上升沿

SDIO Clock divider bypass: Disable。时钟分频旁路使用,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 BYPASS 位。如果使能旁路,SDIOCLK 直接驱动 CLK 线输出时钟;如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分频 SDIOCLK,然后输出到 CLK 线。一般选择禁用时钟分频旁路

SDIO Clock output enable when the bus is idle: Disable the power save for the clock。节能模式选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能节能模式,CLK 线只有在总线激活时才有时钟输出;如果禁用节能模式,始终使能 CLK 线输出时钟。

SDIO hardware flow control: The hardware control flow is disabled。硬件流控制选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 发送上溢和下溢错误。

SDIOCLK clock divide factor: 6。时钟分频系数,它设定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,设置 SDIOCLK 与 CLK 线输出时钟分频系数:CLK 线时钟频率=SDIOCLK/([CLKDIV+2])。

SDIO_CK 引脚的时钟信号在卡识别模式时要求不超过 400KHz,而在识别后的数据传输模式时则希望有更高的速度(最大不超过 25MHz),所以会针对这两种模式配置 SDIOCLK 的时钟。

这里参数描述建议将SDIOCLK clock divede factor 参数使用默认值为0,SDIOCLK为72MHz,可以得到最大频率36MHz,但请注意,有些型号的SD卡可能不支持36MHz这么高的频率,所以还是要以实际情况而定。

4.2 配置DMA

SDIO 外设支持生成 DMA 请求,使用 DMA 传输可以提高数据传输效率,因此在 SDIO 的控制代码中,可以把它设置为 DMA 传输模式或轮询模式,ST 标准库提供 SDIO 示例中针对这两个模式做了区分处理。应用中一般都使用DMA 传输模式。

STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

点击 DMA Settings 添加 SDIO 对应 DMA2 的通道4。DMA模式选择循环模式,方向选为内存到外设。

  • Priority
    当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在 DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通 道编号,编号越低优先权越高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。
  • Mode
    Normal 表示单次传输,传输一次后终止传输。
    Circular 表示循环传输,传输完成后又重新开始继续传输,不断循环永不停止。
  • Increment Address
    Peripheral 表示外设地址自增。
    Memory 表示内存地址自增。
  • Data Width
    Byte 一个字节。
    Half Word 半个字,等于两字节。
    Word 一个字,等于四字节。

4.3 配置NVIC

DMA及SDIO中断设置,原则是全局中断优先级高于DMA中断
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

4.4 生成代码

输入项目名和项目路径
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
选择应用的 IDE 开发环境 MDK-ARM V5
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)
点击 GENERATE CODE 生成代码
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

4.5 添加全局变量

main.c 头部添加

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BLOCK_START_ADDR 0 /* Block start address */
#define NUM_OF_BLOCKS 1 /* Total number of blocks */
#define BUFFER_WORDS_SIZE ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
/* USER CODE END PD */

/* USER CODE BEGIN PV */
uint8_t Buffer_Tx[512],Buffer_Rx[512] = { 
   0};
uint32_t i;
/* USER CODE END PV */

4.6 读取SD卡信息

printf("Micro SD Card Test...\r\n");
/* 检测SD卡是否正常(处于数据传输模式的传输状态) */
if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
{ 
         
    printf("Initialize SD card successfully!\r\n");
    // 打印SD卡基本信息
    printf(" SD card information! \r\n");
    printf(" CardCapacity : %llu \r\n", (unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr);// 显示容量
    printf(" CardBlockSize : %d \r\n", hsd.SdCard.BlockSize);   // 块大小
    printf(" LogBlockNbr : %d \r\n", hsd.SdCard.LogBlockNbr);	// 逻辑块数量
	printf(" LogBlockSize : %d \r\n", hsd.SdCard.LogBlockSize);// 逻辑块大小
    printf(" RCA : %d \r\n", hsd.SdCard.RelCardAdd);  // 卡相对地址
    printf(" CardType : %d \r\n", hsd.SdCard.CardType);    // 卡类型
    // 读取并打印SD卡的CID信息
    HAL_SD_CardCIDTypeDef sdcard_cid;
    HAL_SD_GetCardCID(&hsd,&sdcard_cid);
    printf(" ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);
}
else
{ 
   
    printf("SD card init fail!\r\n" );
}

4.7 擦除SD卡块数据

操作SD卡后最好先用函数HAL_SD_GetCardState()确定一下卡的状态再进行其他操作。

/* 擦除SD卡块 */
printf("------------------- Block Erase -------------------------------\r\n");
if(HAL_SD_Erase(&hsd, BLOCK_START_ADDR, NUM_OF_BLOCKS) == HAL_OK)
{ 
   
    /* Wait until SD cards are ready to use for new operation */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nErase Block Success!\r\n");
}
else
{ 
   
      printf("\r\nErase Block Failed!\r\n");					
}

五、阻塞式读写操作

5.1 写入SD卡块数据

如果读写失败,可能SD通信速度太高,可将hsd.Init.ClockDiv值改大

操作SD卡后最好先用函数HAL_SD_GetCardState()确定一下卡的状态再进行其他操作。
注意先擦除后写入。

/* 填充缓冲区数据 */
memset(Buffer_Tx, 0x15, sizeof(Buffer_Tx));
/* 向SD卡块写入数据 */
printf("------------------- Write SD card block data Test ------------------\r\n");
if(HAL_SD_WriteBlocks(&hsd, Buffer_Tx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
{ 
   
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nWrite Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Tx); i++)
    { 
   
      printf("0x%02x:%02x ", i, Buffer_Tx[i]);
    }
    printf("\r\n");
}
else
{ 
   
    printf("\r\nWrite Block Failed!\r\n");
}

5.2 读取SD卡块数据

如果读写失败,可能SD通信速度太高,可将hsd.Init.ClockDiv值改大

操作SD卡后最好先用函数HAL_SD_GetCardState()确定一下卡的状态再进行其他操作。

/* 读取操作之后的数据 */
printf("------------------- Read SD card block data after Write ------------------\r\n");
if(HAL_SD_ReadBlocks(&hsd, Buffer_Rx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
{ 
   
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nRead Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Rx); i++)
    { 
   
      printf("0x%02x:%02x ", i, Buffer_Rx[i]);
    }
    printf("\r\n");
}
else
{ 
   
    printf("\r\nRead Block Failed!\r\n");				
}

5.3 完整代码

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BLOCK_START_ADDR 0 /* Block start address */
#define NUM_OF_BLOCKS 1 /* Total number of blocks */
#define BUFFER_WORDS_SIZE ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SD_HandleTypeDef hsd;
DMA_HandleTypeDef hdma_sdio;

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USER CODE BEGIN PV */
uint8_t Buffer_Tx[512],Buffer_Rx[512] = { 
   0};
uint32_t i;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_SDIO_SD_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/** * @brief The application entry point. * @retval int */
int main(void)
{ 
   
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SDIO_SD_Init();
  /* USER CODE BEGIN 2 */
  printf("Micro SD Card Test...\r\n");
  /* 检测SD卡是否正常(处于数据传输模式的传输状态) */
  if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
  { 
         
    printf("Initialize SD card successfully!\r\n");
    // 打印SD卡基本信息
    printf(" SD card information! \r\n");
    printf(" CardCapacity : %llu \r\n", (unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr);// 显示容量
    printf(" CardBlockSize : %d \r\n", hsd.SdCard.BlockSize);   // 块大小
    printf(" LogBlockNbr : %d \r\n", hsd.SdCard.LogBlockNbr);	// 逻辑块数量
	printf(" LogBlockSize : %d \r\n", hsd.SdCard.LogBlockSize);// 逻辑块大小
    printf(" RCA : %d \r\n", hsd.SdCard.RelCardAdd);  // 卡相对地址
    printf(" CardType : %d \r\n", hsd.SdCard.CardType);    // 卡类型
    // 读取并打印SD卡的CID信息
    HAL_SD_CardCIDTypeDef sdcard_cid;
    HAL_SD_GetCardCID(&hsd,&sdcard_cid);
    printf(" ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);
  }
  else
  { 
   
    printf("SD card init fail!\r\n" );
  }
 
  /* 擦除SD卡块 */
  printf("------------------- Block Erase -------------------------------\r\n");
  if(HAL_SD_Erase(&hsd, BLOCK_START_ADDR, NUM_OF_BLOCKS) == HAL_OK)
  { 
   
    /* Wait until SD cards are ready to use for new operation */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nErase Block Success!\r\n");
  }
  else
  { 
   
      printf("\r\nErase Block Failed!\r\n");					
  }
  /* 填充缓冲区数据 */
  memset(Buffer_Tx, 0x15, sizeof(Buffer_Tx));
  /* 向SD卡块写入数据 */
  printf("------------------- Write SD card block data Test ------------------\r\n");
  if(HAL_SD_WriteBlocks(&hsd, Buffer_Tx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
  { 
   
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nWrite Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Tx); i++)
    { 
   
      printf("0x%02x:%02x ", i, Buffer_Tx[i]);
    }
    printf("\r\n");
  }
  else
  { 
   
    printf("\r\nWrite Block Failed!\r\n");
  }
  /* 读取操作之后的数据 */
  printf("------------------- Read SD card block data after Write ------------------\r\n");
  if(HAL_SD_ReadBlocks(&hsd, Buffer_Rx, BLOCK_START_ADDR, NUM_OF_BLOCKS, 10) == HAL_OK)
  { 
   
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nRead Block Success!\r\n");
    for(i = 0; i < sizeof(Buffer_Rx); i++)
    { 
   
      printf("0x%02x:%02x ", i, Buffer_Rx[i]);
    }
    printf("\r\n");
  }
  else
  { 
   
    printf("\r\nRead Block Failed!\r\n");				
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  { 
   
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/** * @brief System Clock Configuration * @retval None */
void SystemClock_Config(void)
{ 
   
  RCC_OscInitTypeDef RCC_OscInitStruct = { 
   0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = { 
   0};

  /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  { 
   
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  { 
   
    Error_Handler();
  }
}

/** * @brief SDIO Initialization Function * @param None * @retval None */
static 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_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 6;
  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 */

}

/** * @brief USART1 Initialization Function * @param None * @retval None */
static void MX_USART1_UART_Init(void)
{ 
   

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  { 
   
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/** * Enable DMA controller clock */
static void MX_DMA_Init(void)
{ 
   

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
  /* DMA2_Channel4_5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Channel4_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);

}

/** * @brief GPIO Initialization Function * @param None * @retval None */
static void MX_GPIO_Init(void)
{ 
   
  GPIO_InitTypeDef GPIO_InitStruct = { 
   0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET);

  /*Configure GPIO pins : LED_G_Pin LED_B_Pin LED_R_Pin */
  GPIO_InitStruct.Pin = LED_G_Pin|LED_B_Pin|LED_R_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /*Configure GPIO pins : D0_Pin D1_Pin */
  GPIO_InitStruct.Pin = D0_Pin|D1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

}

/* USER CODE BEGIN 4 */
/** * @brief 重定向c库函数printf到USARTx * @retval None */
int fputc(int ch, FILE *f)
{ 
   
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/** * @brief 重定向c库函数getchar,scanf到USARTx * @retval None */
int fgetc(FILE *f)
{ 
   
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}
/* USER CODE END 4 */

/** * @brief This function is executed in case of error occurrence. * @retval None */
void Error_Handler(void)
{ 
   
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */
void assert_failed(uint8_t *file, uint32_t line)
{ 
   
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

5.4 查看打印

串口打印功能查看 STM32CubeMX学习笔记(6)——USART串口使用
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

六、非阻塞式DMA读写

6.1 添加全局变量

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BLOCK_START_ADDR 0 /* Block start address */
#define NUM_OF_BLOCKS 1 /* Total number of blocks */
#define BUFFER_WORDS_SIZE ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */
/* USER CODE END PD */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t Buffer_Tx[512],Buffer_Rx[512] = { 
   0};
uint32_t i;
HAL_StatusTypeDef Return_Status;
/* USER CODE END PV */

6.2 添加DMA读写函数

注意:STM32F103的SDIO DMA每次由读数据变为写数据或者由写数据变为读数据时,都需要重新初始化DMA(主要是为了更改数据传输的方向)。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
HAL_StatusTypeDef SDIO_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{ 
   
	HAL_StatusTypeDef Return_Status;
	HAL_SD_CardStateTypeDef SD_Card_Status;
	
	do
	{ 
   
		SD_Card_Status = HAL_SD_GetCardState(hsd);
	}while(SD_Card_Status != HAL_SD_CARD_TRANSFER );
 
	/* SDIO DMA DeInit */
	/* SDIO DeInit */
	HAL_DMA_DeInit(&hdma_sdio);
	/* SDIO DMA Init */
	/* SDIO Init */
	hdma_sdio.Instance = DMA2_Channel4;
	hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY;
	hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdio.Init.Mode = DMA_NORMAL;
	hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
	if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)
	{ 
   
		Error_Handler();
	}
 
	__HAL_LINKDMA( hsd,hdmarx,hdma_sdio);
 
	Return_Status = HAL_SD_ReadBlocks_DMA( hsd,pData, BlockAdd, NumberOfBlocks);
	
	return Return_Status;
}

HAL_StatusTypeDef SDIO_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks)
{ 
   
	HAL_StatusTypeDef Return_Status;
	HAL_SD_CardStateTypeDef SD_Card_Status;
	
	do
	{ 
   
		SD_Card_Status = HAL_SD_GetCardState(hsd);
	}while(SD_Card_Status != HAL_SD_CARD_TRANSFER );
 
	/* SDIO DMA DeInit */
	/* SDIO DeInit */
	HAL_DMA_DeInit(&hdma_sdio);
	/* SDIO DMA Init */
	/* SDIO Init */
	hdma_sdio.Instance = DMA2_Channel4;
	hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdio.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdio.Init.Mode = DMA_NORMAL;
	hdma_sdio.Init.Priority = DMA_PRIORITY_LOW;
	if (HAL_DMA_Init(&hdma_sdio) != HAL_OK)
	{ 
   
		Error_Handler();
	}
 
	__HAL_LINKDMA(hsd,hdmatx,hdma_sdio);	
 
	Return_Status = HAL_SD_WriteBlocks_DMA(hsd,pData, BlockAdd, NumberOfBlocks);
	
	return Return_Status;
}
/* USER CODE END 0 */

6.3 修改main函数

int main(void)
{ 
   
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SDIO_SD_Init();
  /* USER CODE BEGIN 2 */
  printf("Micro SD Card Test...\r\n");
  /* 棿测SD卡是否正常(处于数据传输模式的传输状态) */
  if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
  { 
         
    printf("Initialize SD card successfully!\r\n");
    // 打印SD卡基本信恿
    printf(" SD card information! \r\n");
    printf(" CardCapacity : %llu \r\n", (unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr);// 显示容量
    printf(" CardBlockSize : %d \r\n", hsd.SdCard.BlockSize);   // 块大尿
    printf(" LogBlockNbr : %d \r\n", hsd.SdCard.LogBlockNbr);	// 逻辑块数釿
	printf(" LogBlockSize : %d \r\n", hsd.SdCard.LogBlockSize);// 逻辑块大尿
    printf(" RCA : %d \r\n", hsd.SdCard.RelCardAdd);  // 卡相对地坿
    printf(" CardType : %d \r\n", hsd.SdCard.CardType);    // 卡类垿
    // 读取并打印SD卡的CID信息
    HAL_SD_CardCIDTypeDef sdcard_cid;
    HAL_SD_GetCardCID(&hsd,&sdcard_cid);
    printf(" ManufacturerID: %d \r\n",sdcard_cid.ManufacturerID);
  }
  else
  { 
   
    printf("SD card init fail!\r\n" );
  }
 
  /* 擦除SD卡块 */
  printf("------------------- Block Erase -------------------------------\r\n");
  if(HAL_SD_Erase(&hsd, BLOCK_START_ADDR, NUM_OF_BLOCKS) == HAL_OK)
  { 
   
    /* Wait until SD cards are ready to use for new operation */
    while(HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER)
    { 
   
    }
    printf("\r\nErase Block Success!\r\n");
  }
  else
  { 
   
      printf("\r\nErase Block Failed!\r\n");					
  }
  /* 填充缓冲区数捿 */
  memset(Buffer_Tx, 0x22, sizeof(Buffer_Tx));
  
  /* 向SD卡块写入数据 */
  printf("------------------- Write SD card block data Test ------------------\r\n");
  SDIO_WriteBlocks_DMA(&hsd,Buffer_Tx, BLOCK_START_ADDR, NUM_OF_BLOCKS);
  printf("write status :%d\r\n",Return_Status);
  /* 读取SD卡块数据 */	
  Return_Status=SDIO_ReadBlocks_DMA(&hsd,Buffer_Rx, BLOCK_START_ADDR, NUM_OF_BLOCKS);
  printf("read status :%d\r\n",Return_Status);
 
  for(i = 0; i < sizeof(Buffer_Rx); i++)
  { 
   
    printf("0x%02x:%02x ", i, Buffer_Rx[i]);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  { 
   
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

6.4 查看打印

串口打印功能查看 STM32CubeMX学习笔记(6)——USART串口使用
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)

七、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。
STM32CubeMX学习笔记(26)——SDIO接口使用(读写SD卡)


• 由 Leung 写于 2021 年 11 月 16 日

• 参考:HAL库 CubeMX STM32通过SDIO模式实现对SD卡的读写
    STM32CubeMX系列|SD卡
    【STM32Cube-19】使用SDMMC接口读写SD卡数据
    STM32 Cube系列之SDIO(三)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/186428.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 继续安利两个漫画App

    继续安利两个漫画App之前安利了很多漫画App~今天继续,以防被用o(>ω<)o玄猫漫画飒漫画回复“1051”下载玄猫漫画+飒漫画回复666给Yama买一块大白兔…

  • docker卸载命令_删除docker

    docker卸载命令_删除docker删除容器(jenkins官网提供的安装方式,删除比较特殊,因为jenkins自己创建了数据卷,所以要删除数据卷)不然,就算删除了容器,再运行镜像,以前的配置还是删不掉,运行的还是以前的配置首先,关停并删除jenkins容器dockerstop容器iddockerrm容器id然后,查看数据卷(如果是用挂载目录方式安装的jenkins的话,就不需要执行下面的步骤了)dockervolumels发现一个jenkins_home的数据卷,删除数据卷dockervolume

  • Squid 代理服务之透明代理服务器架构搭建

    文章目录1.服务器配置2.Squid服务器部署2.1修改Squid配置文件2.2开启路由转发,实现本机中不同网段的地址转发2.3修改防火墙规则3.客户端访问测试1.服务器配置服务器主机名IP地址主要软件Squid服务器squid_server内网ens33:192.168.10.20|外网ens37:10.0.0.100squidWeb服务器web_server10.0.0.200apacheWin10客户端192.1

  • linux系统重启网卡命令_linux查看网卡配置

    linux系统重启网卡命令_linux查看网卡配置在实际工作中,经常会遇到Linux系统进行重启网卡的操作。接下来是小编为大家收集的linux系统重启网卡方法,希望能帮到大家。linux系统重启网卡方法一、servicenetworkrestart1、首先用CRT工具连接到Linux命令行界面。或者进入操作系统界面,选择终端输入。2、如果我们对所有的网卡进行重启操作。可以尝试输入:servicenetworkrestart命令进行操…

  • 微软正版 Office2007 专业版+企业版光盘ISO镜像

    微软正版 Office2007 专业版+企业版光盘ISO镜像微软正版Office2007专业版+企业版光盘ISO镜像;微软office2007为微软最新的office系列软件,不仅在功能上进行了优化,而且安全性稳定性更得到了巩固.现在终于有了简体中文版的office2007软件来供下载了.PR专业版MSDN版Office2007光盘ISO镜像下载,2006年11月13日放出,市面上唯一的、真正的、纯正的、MSDN原版Offi…

  • \r,\n,\r\n的区别

    \r,\n,\r\n的区别

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号