STM32

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

2021-11-16  本文已影响0人  Leung_ManWah

一、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 外设。

二、SD卡

SD卡除了SD-Micro卡之外,还有两种分别是SD和mini-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 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)


选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

四、SDIO

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

4.1 参数配置

Connetivity 中选择 SDIO 设置,并选择 SD 4 bits Wide bus 四线SD模式


此时 SDIO 对应的管脚也被选中。

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 传输模式。

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

4.3 配置NVIC

DMA及SDIO中断设置,原则是全局中断优先级高于DMA中断


4.4 生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

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串口使用

六、非阻塞式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串口使用

七、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。


• 由 Leung 写于 2021 年 11 月 16 日

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

上一篇下一篇

猜你喜欢

热点阅读