cancel
Showing results for 
Search instead for 
Did you mean: 

FatFS on SDMMC not working with DMA on STM32L4A6ZGT6U

CKugl.1
Senior II

I'm on STM32CubeIDE Version: 1.9.0 Build: 12015_20220302_0855 (UTC), MCU STM32L4A6ZGTx, Firmware STM32Cube FW_L4 V1.17.1. I'm using a NUCLEO-L4A6ZG.

I'm trying to get FatFS working on SDMMC, with DMA, and having problems.

First, the bundled version of FatFS seems to be ancient. It is so old that it doesn't resemble the documented API at http://elm-chan.org/fsw/ff/00index_e.html. Is there a way I can update it such that my changes won't be wiped out each time I run or update the Device Configuration tool?

The big problem is that it won't work if I select "Use dma template" in Device Configuration Tool/Pinout & Configuration/FATFS/Advanced Settings. If I disable "Use dma template", it works OK (with polling).

With DMA enabled, it gets hung up here:

SD_write() at sd_diskio.c:351 0x80081c8

disk_write() at diskio.c:104 0x800837c

f_mkfs() at ff.c:5,661 0x8008af0

test() at test.c:23 0x80011fc

main() at main.c:103 0x8000914

where I see

if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff,
                              (uint32_t)(sector),
                              count) == MSD_OK)
    {
      /* Wait that writing process is completed or a timeout occurs */
 
      timeout = HAL_GetTick();
      while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
      {
      }
      /* in case of a timeout return error */
      if (WriteStatus == 0)
      {
        res = RES_ERROR;
      }

(SD_write() at sd_diskio.c:351 is line 8 in the snippet).

Apparently, it is waiting for BSP_SD_WriteCpltCallback (or HAL_SD_TxCpltCallback, HAL_SD_IRQHandler, SDMMC1_IRQHandler) which is never called:

void BSP_SD_WriteCpltCallback(void)
{
 
  WriteStatus = 1;
}

At this point, if I look at hsd1, I see:

Instance SDMMC_TypeDef * 0x40012800 (Hex)

POWER volatile uint32_t 0x3 (Hex)

CLKCR volatile uint32_t 0x4900 (Hex)

ARG volatile uint32_t 0x3f (Hex)

CMD volatile uint32_t 0x458 (Hex)

RESPCMD const volatile uint32_t 0x18 (Hex)

RESP1 const volatile uint32_t 0x900 (Hex)

RESP2 const volatile uint32_t 0x5b590000 (Hex)

RESP3 const volatile uint32_t 0x76b27f80 (Hex)

RESP4 const volatile uint32_t 0xa404012 (Hex)

DTIMER volatile uint32_t 0xffffffff (Hex)

DLEN volatile uint32_t 0x200 (Hex)

DCTRL volatile uint32_t 0x99 (Hex)

DCOUNT const volatile uint32_t 0x200 (Hex)

STA const volatile uint32_t 0x145000 (Hex)

ICR volatile uint32_t 0x0 (Hex)

MASK volatile uint32_t 0x1a (Hex)

RESERVED0 uint32_t [2] 0x40012840 (Hex)

FIFOCNT const volatile uint32_t 0x80 (Hex)

RESERVED1 uint32_t [13] 0x4001284c (Hex)

FIFO volatile uint32_t 0x4d90feeb (Hex)

I find it interesting that the MASK is Binary:11010, so only bits 1, 3, and 4 are set, which are:

  • Bit 1 DCRCFAILIE: Data CRC fail interrupt enable
  • Bit 3 DTIMEOUTIE: Data timeout interrupt enable
  • Bit 4 TXUNDERRIE: Tx FIFO underrun error interrupt enable

so this list does not include Bit 8 DATAENDIE: Data end interrupt enable.

STA is Binary:101000101000000000000, which is bits 12, 14, 18, and 20:

  • Bit 20 TXDAVL: Data available in transmit FIFO
  • Bit 18 TXFIFOE: Transmit FIFO empty
  • Bit 14 TXFIFOHE: Transmit FIFO half empty: at least 8 words can be written into the FIFO
  • Bit 12 TXACT: Data transmit in progress

It's trying to send a 512 byte block:

  • DLEN volatile uint32_t 0x200 (Hex)
  • DCOUNT const volatile uint32_t 0x200 (Hex)

Since DCOUNT has not decremented, it looks like no data has been transferred.

What could be the problem?

I put the project on GitHub: https://github.com/carlk3/STM32L4A6ZGTx_SDMMC. It should be complete enough to build. (Is that the best way to share code here? It's fairly painless with EGit: the Git integration for Eclipse).

40 REPLIES 40
CKugl.1
Senior II

I'm still trying to get this working. For now, I've disabled FatFS in the Device Configuration Tool and I'm just trying to work at the HAL level.

First, initialization:

 	HAL_StatusTypeDef hal_rc = HAL_SD_Init(&hsd1);
 	if (hal_rc != HAL_OK)
		return RES_ERROR;
 
	/* Configure SD Bus width (4 bits mode selected) */
	/* Enable wide operation */
	if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK)
		return RES_ERROR;
 

Next, to write a block to the SD card:

assert(0 == ((uintptr_t )buff & 0x3)); // Check for WORD alignment for DMA
 
	if (HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t*) buff, sector, count) != HAL_OK) {
		return RES_ERROR;
	}

At this point I would expect a `HAL_SD_TxCpltCallback` but it never comes.

`HAL_DMA_GetState` just returns HAL_DMA_STATE_BUSY forever.

In hsd1, DLEN and DCOUNT are both 512 (the block size).

DCTRL is Binary:10011001

STA is Binary:100000101000000000000.

MASK is Binary:11010.

In hsd1.hdmatx,

CCR: Binary:10101010011011

CNDTR: Decimal:128 (Hex:0x80)

If I'm reading this right, DCTRL says:

  • DTEN: Data transfer enabled bit: enabled
  • DMAEN: DMA enable bit: enabled
  • DBLOCKSIZE: Data block size: 512 bytes

DMA CCR says:

  • PL[1:0]: priority level: high
  • MSIZE[1:0]: memory size: 32 bits
  • PSIZE[1:0]: peripheral size: 32 bits
  • MINC: memory increment mode: enabled
  • PINC: peripheral increment mode: disabled
  • CIRC: circular mode: disabled
  • DIR: data transfer direction: read from memory
  • TEIE: transfer error interrupt enable: enabled
  • HTIE: half transfer interrupt enable: disabled
  • TCIE: transfer complete interrupt enable: enabled
  • EN: channel enable: enabled

Why isn't my transfer completing?

If I change the code to `HAL_SD_WriteBlocks(&hsd1, (uint8_t*) buff, sector, count, Timeout)` instead of `HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t*) buff, sector, count)` it runs.

I have tried with and without Hardware Flow Control. (If I don't use DMA I get underrun errors without the Flow Control).

Haithem Rahmani
ST Employee

Hi @CKugl.1​ 

>STM32Cube FW_L4 V1.17.1. I'm using a NUCLEO-L4A6ZG.

AFAIK, this board is not equipped with uSD slot, how are you running the example on it?

are you using an arduino shield?

CKugl.1
Senior II

I'm using an Adafruit 4682 Micro SD SPI or SDIO Card Breakout Board wired to SDMMC1. Originally, I used a breadboard and some jumpers. Since then I've moved it to a soldered protoboard plugged into CN8 and CN9 with berg strips (but it doesn't work any better).

CKugl.1
Senior II

I can read successfully with DMA, but not write. I.e., HAL_SD_ReadBlocks_DMA works, but HAL_SD_WriteBlocks_DMA does not. If I write with HAL_SD_WriteBlocks and read the data back with HAL_SD_ReadBlocks_DMA, it works, as shown in the attached test case. It produces the following output:

test:152: SD Card State:Card is in transfer state        

test:172: SD Card State:Card is in programming state       

test:172: SD Card State:Card is in transfer state        

test:184: DMA State:DMA initialized and ready for use  

test:188: SD Card State:Card is in transfer state        

 Read data matched.

CKugl.1
Senior II

I modified my test case to use HAL_SD_WriteBlocks_DMA. I added some code to print out the SDMMC and DMA registers before and after the call. Here is the output:

test:185: Starting
test:201: SD Card State: Card is in transfer state               
test:219: SDMMC registers:
                    3         2         1         0
                   10987654321098765432109876543210
        POWER  : 0b00000000000000000000000000000011
        CLKCR  : 0b00000000000000000100100100000001
        ARG    : 0b11100110001001000000000000000000
        CMD    : 0b00000000000000000000010001001101
        RESPCMD: 0b00000000000000000000000000001101
        RESP1  : 0b00000000000000000000100100000000
        RESP2  : 0b01011011010110010000000000000000
        RESP3  : 0b01110110101100100111111110000000
        RESP4  : 0b00001010010000000100000000010010
        DTIMER : 0b11111111111111111111111111111111
        DLEN   : 0b00000000000000000000000000001000
        DCTRL  : 0b00000000000000000000000000110011
        DCOUNT : 0b00000000000000000000000000000000
        STA    : 0b00000000000000000000000000000000
        ICR    : 0b00000000000000000000000000000000
        MASK   : 0b00000000000000000000000000000000
        FIFOCNT: 0b00000000000000000000000000000000
        FIFO   : 0b00000011100000000011010100000010
test:221: TX DMA State: DMA initialized and ready for use  
test:223: TX DMA registers:
                    3         2         1         0
                   10987654321098765432109876543210
        CCR    : 0b00000000000000000011101010010000
        CNDTR  : 0b00000000000000000000000000000000
        CPAR   : 0b00000000000000000000000000000000
        CMAR   : 0b00000000000000000000000000000000
 
test:226: Writing
test:233: SDMMC registers:
                    3         2         1         0
                   10987654321098765432109876543210
        POWER  : 0b00000000000000000000000000000011
        CLKCR  : 0b00000000000000000100100100000001
        ARG    : 0b00000000000000000000000000000000
        CMD    : 0b00000000000000000000010001011000
        RESPCMD: 0b00000000000000000000000000011000
        RESP1  : 0b00000000000000000000100100000000
        RESP2  : 0b01011011010110010000000000000000
        RESP3  : 0b01110110101100100111111110000000
        RESP4  : 0b00001010010000000100000000010010
        DTIMER : 0b11111111111111111111111111111111
        DLEN   : 0b00000000000000000000001000000000
        DCTRL  : 0b00000000000000000000000010011001
        DCOUNT : 0b00000000000000000000001000000000
        STA    : 0b00000000000101000101000000000000
        ICR    : 0b00000000000000000000000000000000
        MASK   : 0b00000000000000000000000000011010
        FIFOCNT: 0b00000000000000000000000010000000
        FIFO   : 0b00000011000000100000000100000000
test:236: TX DMA State: DMA process is ongoing             
test:238: TX DMA registers:
                    3         2         1         0
                   10987654321098765432109876543210
        CCR    : 0b00000000000000000011101010011011
        CNDTR  : 0b00000000000000000000000010000000
        CPAR   : 0b01000000000000010010100010000000
        CMAR   : 0b00100000000001001111011111001100
test:245: SD Card State: Card is receiving operation information 
test:245: SD Card State: Card is receiving operation information 
test:245: SD Card State: Card is receiving operation information 
test:245: SD Card State: Card is receiving operation information 
test:245: SD Card State: Card is receiving operation information 
test:245: SD Card State: Card is receiving operation information 
 

 Here is a diff of before and after the HAL_SD_WriteBlocks_DMA call:

1c1
< test:219: SDMMC registers:
---
> test:233: SDMMC registers:
6,8c6,8
<         ARG    : 0b11100110001001000000000000000000
<         CMD    : 0b00000000000000000000010001001101
<         RESPCMD: 0b00000000000000000000000000001101
---
>         ARG    : 0b00000000000000000000000000000000
>         CMD    : 0b00000000000000000000010001011000
>         RESPCMD: 0b00000000000000000000000000011000
14,17c14,17
<         DLEN   : 0b00000000000000000000000000001000
<         DCTRL  : 0b00000000000000000000000000110011
<         DCOUNT : 0b00000000000000000000000000000000
<         STA    : 0b00000000000000000000000000000000
---
>         DLEN   : 0b00000000000000000000001000000000
>         DCTRL  : 0b00000000000000000000000010011001
>         DCOUNT : 0b00000000000000000000001000000000
>         STA    : 0b00000000000101000101000000000000
19,23c19,23
<         MASK   : 0b00000000000000000000000000000000
<         FIFOCNT: 0b00000000000000000000000000000000
<         FIFO   : 0b00000011100000000011010100000010
< test:221: TX DMA State: DMA initialized and ready for use
< test:223: TX DMA registers:
---
>         MASK   : 0b00000000000000000000000000011010
>         FIFOCNT: 0b00000000000000000000000010000000
>         FIFO   : 0b00000011000000100000000100000000
> test:236: TX DMA State: DMA process is ongoing
> test:238: TX DMA registers:
26,29c26,29
<         CCR    : 0b00000000000000000011101010010000
<         CNDTR  : 0b00000000000000000000000000000000
<         CPAR   : 0b00000000000000000000000000000000
<         CMAR   : 0b00000000000000000000000000000000
---
>         CCR    : 0b00000000000000000011101010011011
>         CNDTR  : 0b00000000000000000000000010000000
>         CPAR   : 0b01000000000000010010100010000000
>         CMAR   : 0b00100000000001001111011111001100

CKugl.1
Senior II

I happen to have a NUCLEO-F413ZH lying around, so I unplugged my SD card adapter from the NUCLEO-L4A6ZG and plugged it into that, then ported my test case. It runs great, reading and writing with DMA! However, I need to use the L4 chip (for the low power consumption). So, I'm still trying to figure out why that gets hung up in HAL_SD_WriteBlocks_DMA .

CKugl.1
Senior II

I'm back on the NUCLEO-L4A6ZG trying to run the same test case that runs successfully on the NUCLEO-F413ZH.

On the NUCLEO-F413ZH, here's what I see with a logic analyzer on the SD bus when I call HAL_SD_WriteBlocks_DMA:

0693W00000LxcmFQAR.pngOn the NUCLEO-L4A6ZG, here's what I see with a logic analyzer on the SD bus when I call HAL_SD_WriteBlocks_DMA:

0693W00000LxcsmQAB.pngThere is no activity at all on the data lines!

Now, again on NUCLEO-L4A6ZG, if I simply change HAL_SD_WriteBlocks_DMA to HAL_SD_WriteBlocks, I get this:

0693W00000LxcuiQAB.pngSo, when I call HAL_SD_WriteBlocks_DMA on the L4A6ZG, the command is sent but no data is ever sent.

CKugl.1
Senior II

Prior to the call to HAL_SD_WriteBlocks_DMA on the L4A6ZG, the TX DMA registers:

 TX DMA registers:
                    3         2         1         0
                   10987654321098765432109876543210
        CCR    : 0b00000000000000000011101010010000
        CNDTR  : 0b00000000000000000000000000000000
        CPAR   : 0b00000000000000000000000000000000
        CMAR   : 0b00000000000000000000000000000000
        ISR    : 0b00000000000000000000000000000000

The DMA channel x configuration register (DMA_CCRx) says:

  • Bit 0 EN: channel enable: 0: disabled
  • Bit 1 TCIE: transfer complete interrupt enable: 0: disabled
  • HTIE: half transfer interrupt enable and TEIE: transfer error interrupt enable: disabled
  • Bit 4 DIR: data transfer direction: 1: read from memory
  • Bit 5 CIRC: circular mode: 0: disabled
  • Bit 6 PINC: peripheral increment mode: 0: disabled
  • Bit 7 MINC: memory increment mode: 1: enabled
  • Bits 9:8 PSIZE[1:0]: peripheral size: 10: 32 bits
  • Bits 11:10 MSIZE[1:0]: memory size: 10: 32 bits
  • Bits 13:12 PL[1:0]: priority level: 11: very high
  • Bit 14 MEM2MEM: memory-to-memory mode: 0: disabled

DMA channel x number of data to transfer register (DMA_CNDTRx) is 0.

DMA channel x peripheral address register (DMA_CPARx) is 0.

DMA channel x memory address register (DMA_CMARx) is 0.

DMA interrupt status register (DMA_ISR) is all 0.

The F4 and L4+ have different SDIO vs SDMMC IP, and the L4 has at least 3 implementations, including different approaches to integrating DMA, and using 1 or 2 DMA channels So let's avoid an Apples to Oranges comparison about how thing need to work, because its different between various architectures.

The L496/L4A6 DMA for write should looks something like this, as it has ONE DMA resource, and it must be configured in a directionally appropriate manner.

/**
  * @brief  Writes block(s) to a specified address in an SD card, in DMA mode.
  * @param  pData: Pointer to the buffer that will contain the data to transmit
  * @param  WriteAddr: Address from where data is to be written
  * @param  NumOfBlocks: Number of SD blocks to write
  * @retval SD status
  */
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{
  HAL_StatusTypeDef  sd_state = HAL_OK;
 
  /* Invalidate the dma rx handle*/
  uSdHandle.hdmarx = NULL;
 
  /* Prepare the dma channel for a write/tx operation */
  sd_state = SD_DMAConfigTx(&uSdHandle);
 
  if (sd_state == HAL_OK)
  {
    /* Write block(s) in DMA transfer mode */
    sd_state = HAL_SD_WriteBlocks_DMA(&uSdHandle, (uint8_t *)pData, WriteAddr, NumOfBlocks);
  }
 
  if (sd_state == HAL_OK)
  {
    return MSD_OK;
  }
  else
  {
    return MSD_ERROR;
  }
}
...
/**
  * @brief Configure the DMA to transmit data to the SD card
  * @retval
  *  HAL_ERROR or HAL_OK
  */
static HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd)
{
  static DMA_HandleTypeDef hdma_tx;
  HAL_StatusTypeDef status;
 
  /* Configure DMA Tx parameters */
  hdma_tx.Init.Request             = DMA_REQUEST_7;
  hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
  hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
  hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  hdma_tx.Init.Priority            = DMA_PRIORITY_VERY_HIGH;
 
  hdma_tx.Instance = SD_DMAx_STREAM;
 
  /* Associate the DMA handle */
  __HAL_LINKDMA(hsd, hdmatx, hdma_tx);
 
  /* Stop any ongoing transfer and reset the state*/
  HAL_DMA_Abort(&hdma_tx);
 
  /* Deinitialize the Channel for new transfer */
  HAL_DMA_DeInit(&hdma_tx);
 
  /* Configure the DMA Channel */
  status = HAL_DMA_Init(&hdma_tx);
 
  /* NVIC configuration for DMA transfer complete interrupt */
  HAL_NVIC_SetPriority(SD_DMAx_IRQn, 6, 0);
  HAL_NVIC_EnableIRQ(SD_DMAx_IRQn);
 
  return (status);
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..