HAL library CubeMX STM32 uses SDIO to read and write SD cards and NAND Flash

Table of Contents

Complete project source code download address: HAL library CubeMXSTM32 uses SDIO to realize reading and writing resources for SD cards and NAND Flash – CSDN library

1. Choose the appropriate memory chip.

You can go to Leilong official website for free prostitution, free prostitution link: free samples

2. The underlying principles of SD card/SD NAND

3. Hardware design

1. SD NAND pin diagram

2. Chip appearance and packaging:

3. Hardware circuit schematic diagram

?4. Physical picture of the adapter board for testing

4. Specific steps to configure STM32 in CubeMX

1. Clock and system configuration

2. Configure SDIO

3. Configure DMA (optional)

4. Set up the serial port

5. Code writing

1. Public code

2. Read and write in conventional ways

3. DMA mode reading and writing

6. Result analysis

1. The input function parameter is the sector number, not the actual offset address.

2. Test results


Full project source code download address: HAL library CubeMXSTM32 uses SDIO to implement SD Reading and writing resources for cards and NAND Flash – CSDN library

1. Choose the appropriate memory chip.

I am currently working on a project that requires a large amount of storage and reading of data, but the memory capacity of the stm32 is too small. For example, the STM32F103ZET6 I am using has a flash capacity of 512K, which is not enough.

Related MCU chip model resources are as follows:

The solution adopted in the final project is: CSNP4GCR01 SD NAND provided by Brontosaurus Company. Of course, you can also use a TF card. The usage method is to use the SDIO bus driver and the procedures are exactly the same. However, this card has more advantages than ordinary TF cards, which are specifically reflected in the following points:

In the PCB design of some SMD chips, whether it is in strict area requirements or in actual harsh environments, it has advantages in price, packaging and stability. Generally speaking, it is more cost-effective. The products provided by Thunder Dragon Company The CS SD NAND FLASH solution has obvious advantages, which is why I chose to use it in the project.

2. The underlying principles of SD card/SD NAND

According to the capacity of SD card, it can be divided into three standards: SDSC, SDHC and SDXC. Today, the mainstream SD products in the market are SDHC and SDXC, two larger-capacity memory cards. The SDSC card has been gradually eliminated by the market due to its small capacity. The storage space of the SD card (general name for the three types of cards) is composed of sectors. The sector size of the SD card is 512 bytes. Several sectors can form an allocation unit (also called a cluster). Allocation units are common. The sizes are 4K, 8K, 16K, 32K, 64K.

I won’t write down the specific principles here. There are many online. You can refer to the following links:

Detailed explanation of the application of SD NAND’s SDIO on STM32 (Part 1)_sdio connected to two chips_Shenzhen Thunder Dragon Development Co., Ltd.’s blog-CSDN blog

3. Hardware Design

1. SD NAND pin diagram

2. Chip appearance and packaging:

3. Hardware circuit schematic diagram

As shown below, we can see that we only need to add a few resistors and capacitors around the chip to use it, which can be easily integrated into our own PCB package.

4. Physical picture of the adapter board for testing

The actual development pictures used in this experiment:

Actual picture in operation:

4. Specific steps for configuring STM32 with CubeMX

1. Clock and system configuration

2. Configure SDIO

(1) Clock transition on which the bit capture is made: Rising transition. The main clock SDIOCLK generates the CLK pin clock valid edge selection, which can be rising edge or falling edge. It sets the value of the NEGEDGE bit of the SDIO clock control register (SDIO_CLKCR). Generally, the rising edge is selected. (reference link)

(2)SDIO Clock divider bypass:Disbale. Clock divider bypass is used, optionally enabled or disabled, it sets the BYPASS bit of the SDIO_CLKCR register. If bypass is enabled,SDIOCLK directly drives the CLK line output clock; if disabled, SDIOCLK is divided using the CLKDIV bit value of the SDIO_CLKCR register and then output to the CLK line. Generally choose to disable clock divider bypass.

(3) SDIO Clock output enable when the bus is idle: Disable the power save for the clock. Power-saving mode selection, optionally enabled or disabled, sets the value of the PWRSAV bit of the SDIO_CLKCR register. If power-saving mode is enabled, the CLK line has a clock output only when the bus is active; if power-saving mode is disabled, the CLK line output clock is always enabled.

(4) SDIO hardware flow control: The hardware control flow is disabled. Hardware flow control selection, optionally enabled or disabled, which sets the value of the HWFC_EN bit of the SDIO_CLKCR register. Hardware flow control features avoid FIFO transmit overflow and underflow errors.

SDIOCLK clock divide factor: 6. Clock division coefficient, which sets the value of the CLKDIV bit of the SDIO_CLKCR register and sets the SDIOCLK and CLK line output clock division coefficients:

CLK line clock frequency =SDIOCLK/([CLKDIV + 2]).

Tag stage: Clock frequency FOD, up to 400kHz
Data transmission mode: Clock frequency FPP, the default is up to 25MHz
If SDIO is configured to work in high-speed mode through relevant register configuration, the maximum frequency of data transmission mode is 50MHz.

### Note: When I first started doing it, I referred to the SDIOCLK below, and then set the clock division coefficient to 0, resulting in the CLK line clock frequency = 72/([0 + 2])=36M is greater than the maximum data transfer rate of 25M. After generating the code, the program has been stuck in the SDIO initialization function, causing the SD card to fail to initialize.

Look carefully at the second point (2) above. Since the clock division bypass was disabled in our previous configuration, we cannot refer to the SDIOCLK below. Therefore, if we actually use SDIOCLK=72M, we should set the clock division coefficient < /strong>is 2 or more.

3. Configure DMA (optional)

In some practical use cases, such as when camera frame data needs to be stored in an SD card and a large amount of data needs to be stored and retrieved efficiently, we often use DMA to reduce the burden on the CPU.

The SDIO peripheral supports generating DMA requests. Using DMA transfer can improve data transfer efficiency, so in the SDIO control code, it can be set to DMA transfer mode.

4. Set up the serial port

Open the serial port to print out the SD card information and view debugging information in real time through the serial port.

5. Code Writing

1. Public code

Common code sections added in the following two modes

#include <stdio.h>
#include <string.h>

#define BLOCK_START_ADDR 0 /* Block start address */
#define NUM_OF_BLOCKS 1 /* Sector number */
#define BUFFER_WORDS_SIZE ((BLOCKSIZE * NUM_OF_BLOCKS) >> 2) /* Total data size in bytes */

//The size defined here is 512byte, which is exactly one sector of the SD card, and the offset address is 0x00000200
uint8_t Buffer_Tx[512],Buffer_Rx[512] = {0};
uint8_t Buffer_Tx_DMA[1024],Buffer_Rx_DMA[1024] = {0};
uint32_t i;

extern DMA_HandleTypeDef hdma_sdio;

int fputc(int c, FILE *stream) //Rewrite the fputc function
{
 /*
    huart1 is the UART1 structure defined by the tool-generated code.
    If you want to use other serial port printing in the future, you only need to change this structure to other UART structures.
*/
    HAL_UART_Transmit( & amp;huart1, (unsigned char *) & amp;c, 1, 1000);
    return 1;
}


//Print basic information of SD card
void show_sdcard_info(void)
{
printf("Micro SD Card Test...\r\
");
/* Check whether the SD card is normal (in the transmission state of data transmission mode) */
if(HAL_SD_GetCardState( & amp;hsd) == HAL_SD_CARD_TRANSFER)
{
printf("Initialize SD card successfully!\r\
");
//Print basic information of SD card
printf(" SD card information! \r\
");
printf("CardCapacity : %llu \r\
", (unsigned long long)hsd.SdCard.BlockSize * hsd.SdCard.BlockNbr);//Display capacity
printf(" CardBlockSize : %d \r\
", hsd.SdCard.BlockSize); // block size
printf("LogBlockNbr : %d \r\
", hsd.SdCard.LogBlockNbr); //Number of logical blocks
printf("LogBlockSize : %d \r\
", hsd.SdCard.LogBlockSize);//Logical block size
printf(" RCA : %d \r\
", hsd.SdCard.RelCardAdd); // Card relative address
printf(" CardType : %d \r\
", hsd.SdCard.CardType); // Card type
//Read and print the CID information of the SD card
HAL_SD_CardCIDTypeDef sdcard_cid;
HAL_SD_GetCardCID( & amp;hsd, & amp;sdcard_cid);
printf("ManufacturerID: %d \r\
",sdcard_cid.ManufacturerID);
}
else
{
printf("SD card init fail!\r\
" );
}
}

/* Erase SD card block */
void erase_sdcard(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd)
{
  printf("------------------- Block Erase -------------------------- -----\r\
");
  if(HAL_SD_Erase(hsd, BlockStartAdd, BlockEndAdd) == HAL_OK)
  {
    /* Wait until SD cards are ready to use for new operation */
    while(HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\
Erase Block Success!\r\
");
  }
  else
  {
      printf("\r\
Erase Block Failed!\r\
");
  }
}

2. Read and write in normal way

/* Fill buffer data */
void write_sdcard(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
{
  /* Write data to SD card block */
  printf("------------------- Start Write SD card block data ------------------\r\
 ");
  if(HAL_SD_WriteBlocks(hsd, pData, BlockAdd, NumberOfBlocks, Timeout) == HAL_OK)
  {
    while(HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\
Write Block Success!\r\
");
  }
  else
  {
    printf("\r\
Write Block Failed!\r\
");
  }
}

/* Read data after operation */
void read_sdcard(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)
{
  printf("------------------- Start Read SD card block data ------------------\r\
 ");
  if(HAL_SD_ReadBlocks(hsd, pData, BlockAdd, NumberOfBlocks, Timeout) == HAL_OK)
  {
    while(HAL_SD_GetCardState(hsd) != HAL_SD_CARD_TRANSFER)
    {
    }
    printf("\r\
Read Block Success!\r\
");
  }
  else
  {
    printf("\r\
Read Block Failed!\r\
");
  }
}

The code in the main function is as follows:

 /* USER CODE BEGIN 2 */
show_sdcard_info();
//Erase 5 sectors
erase_sdcard( & amp;hsd, 0, 5);
memset(Buffer_Tx, 0x55, sizeof(Buffer_Tx));
write_sdcard( & amp;hsd, Buffer_Tx, 1, 2, 10);
read_sdcard( & amp;hsd, Buffer_Rx, 1, 2, 10);
//View the read data
for(i = 0; i < sizeof(Buffer_Rx)/sizeof(Buffer_Rx[0]); i + + )
{
   printf("0x x: x ", i, Buffer_Rx[i]);
}
  /* USER CODE END 2 */

3. DMA reading and writing

Note here: Every time the SDIO DMA changes from reading data to writing data or from writing data to reading data, the DMA needs to be reinitialized in order to change the direction of data transmission.

(This is particularly tricky. I found out that it cannot be used by referring to other bloggers’ codes. After personally modifying the code, I discovered this problem after changing the code many times. The following code has been verified and can be used directly)

//Non-blocking DMA read
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;
\t
do{ SD_Card_Status = HAL_SD_GetCardState(hsd);}while(SD_Card_Status != HAL_SD_CARD_TRANSFER );

HAL_DMA_DeInit( & amp;hdma_sdio);
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( & amp;hdma_sdio) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hsd,hdmarx,hdma_sdio);
 
MX_SDIO_SD_Init();
\t
Return_Status = HAL_SD_ReadBlocks_DMA(hsd, pData, BlockAdd, NumberOfBlocks);
\t
return Return_Status;
}

//Non-blocking DMA write
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 );

HAL_DMA_DeInit( & amp;hdma_sdio);
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( & amp;hdma_sdio) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hsd,hdmarx,hdma_sdio);
 
MX_SDIO_SD_Init();

Return_Status = HAL_SD_WriteBlocks_DMA(hsd,pData, BlockAdd, NumberOfBlocks);
\t
return Return_Status;
}

The code in the main function is as follows:

/* USER CODE BEGIN 2 */

HAL_StatusTypeDef Return_Status;
memset(Buffer_Tx_DMA, 0x55, sizeof(Buffer_Tx_DMA));
Return_Status = SDIO_WriteBlocks_DMA( & amp;hsd,Buffer_Tx_DMA, 0, 1);
printf("write status :%d\r\
",Return_Status);
Return_Status = SDIO_ReadBlocks_DMA( & amp;hsd,Buffer_Rx_DMA, 0, 2);
printf("read status :%d\r\
",Return_Status);
//View the read data
for(i = 0; i < sizeof(Buffer_Rx_DMA)/sizeof(Buffer_Rx_DMA[0]); i + + )
{
  printf("0x x: x ", i, Buffer_Rx_DMA[i]);
}

/* USER CODE END 2 */

6. Result Analysis

1. The input function parameter is the sector number, not the actual offset address.

The sector size of the SD card is 512byte, so the offset address of each sector is 0x200

The parameter here must be passed in the SD card sector number, not the address. Entering the original function, we can see that the official internal address offset has been done for us.

2. Test results

syntaxbug.com © 2021 All Rights Reserved.