硬汉嵌入式论坛

 找回密码
 立即注册
查看: 5812|回复: 15
收起左侧

[SD/SDIO] ST这骚操作,解决H7的SDIO DMA的4字节对齐问题,搞了个复制粘贴

  [复制链接]

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
发表于 2020-9-22 13:34:20 | 显示全部楼层 |阅读模式
其实H7自带了个SDIO DMA后,远没有使用通用DMA方便了,通用DMA可以轻松解决4字节对齐问题。

问题由来:

STM32H7的SDIO自带的DMA控制器数据传输的地址是强制4字节对齐,这就非常不方便了
https://forum.anfulai.cn/forum.php?m ... id=94066&fromuid=58
(出处: 硬汉嵌入式论坛)


解决方法,如下代码,搞了个memcpy的骚操作,而且是512字节为单位的复制粘贴,SDIO批量块传输特性将发挥不出来:
  1.   /**
  2.   ******************************************************************************
  3.   * @file    sd_diskio_dma_template.c
  4.   * @author  MCD Application Team
  5.   * @brief   SD DMA Disk I/O template driver. This file needs to be renamed and
  6.              copied into the application project alongside the respective header
  7.              file.
  8.   ******************************************************************************
  9.   * @attention
  10.   *
  11.   * Copyright (c) 2019 STMicroelectronics. All rights reserved.
  12.   *
  13.   * This software component is licensed by ST under BSD 3-Clause license,
  14.   * the "License"; You may not use this file except in compliance with the
  15.   * License. You may obtain a copy of the License at:
  16.   *                       opensource.org/licenses/BSD-3-Clause
  17.   *
  18.   ******************************************************************************
  19. **/

  20. /* Includes ------------------------------------------------------------------*/
  21. #include <string.h>
  22. #include "ff_gen_drv.h"
  23. #include "sd_diskio_dma.h"

  24. /* Private typedef -----------------------------------------------------------*/
  25. /* Private define ------------------------------------------------------------*/
  26. /*
  27. * the following Timeout is useful to give the control back to the applications
  28. * in case of errors in either BSP_SD_ReadCpltCallback() or BSP_SD_WriteCpltCallback()
  29. * the value by default is as defined in the BSP platform driver otherwise 30 secs
  30. */

  31. #define SD_TIMEOUT 30 * 1000

  32. #define SD_DEFAULT_BLOCK_SIZE 512

  33. /*
  34. * Depending on the usecase, the SD card initialization could be done at the
  35. * application level, if it is the case define the flag below to disable
  36. * the BSP_SD_Init() call in the SD_Initialize().
  37. */

  38. /* #define DISABLE_SD_INIT */

  39. /*
  40. * when using cachable memory region, it may be needed to maintain the cache
  41. * validity. Enable the define below to activate a cache maintenance at each
  42. * read and write operation.
  43. * Notice: This is applicable only for cortex M7 based platform.
  44. */

  45. /* #define ENABLE_SD_DMA_CACHE_MAINTENANCE  1 */

  46. /*
  47. * Some DMA requires 4-Byte aligned address buffer to correctly read/wite data,
  48. * in FatFs some accesses aren't thus we need a 4-byte aligned scratch buffer to correctly
  49. * transfer data
  50. */
  51. /* #define ENABLE_SCRATCH_BUFFER */


  52. /* Private variables ---------------------------------------------------------*/

  53. #if defined(ENABLE_SCRATCH_BUFFER)
  54. #if defined (ENABLE_SD_DMA_CACHE_MAINTENANCE)
  55. ALIGN_32BYTES(static uint8_t scratch[BLOCKSIZE]); // 32-Byte aligned for cache maintenance
  56. #else
  57. __ALIGN_BEGIN static uint8_t scratch[BLOCKSIZE] __ALIGN_END;
  58. #endif
  59. #endif

  60. /* Disk status */
  61. static volatile DSTATUS Stat = STA_NOINIT;
  62. static volatile  UINT  WriteStatus = 0, ReadStatus = 0;
  63. /* Private function prototypes -----------------------------------------------*/
  64. static DSTATUS SD_CheckStatus(BYTE lun);
  65. DSTATUS SD_initialize (BYTE);
  66. DSTATUS SD_status (BYTE);
  67. DRESULT SD_read (BYTE, BYTE*, DWORD, UINT);
  68. #if _USE_WRITE == 1
  69. DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT);
  70. #endif /* _USE_WRITE == 1 */
  71. #if _USE_IOCTL == 1
  72. DRESULT SD_ioctl (BYTE, BYTE, void*);
  73. #endif  /* _USE_IOCTL == 1 */

  74. const Diskio_drvTypeDef  SD_Driver =
  75. {
  76.   SD_initialize,
  77.   SD_status,
  78.   SD_read,
  79. #if  _USE_WRITE == 1
  80.   SD_write,
  81. #endif /* _USE_WRITE == 1 */

  82. #if  _USE_IOCTL == 1
  83.   SD_ioctl,
  84. #endif /* _USE_IOCTL == 1 */
  85. };

  86. /* Private functions ---------------------------------------------------------*/
  87. static int SD_CheckStatusWithTimeout(uint32_t timeout)
  88. {
  89.   uint32_t timer = HAL_GetTick();
  90.   /* block until SDIO IP is ready again or a timeout occur */
  91.   while(HAL_GetTick() - timer < timeout)
  92.   {
  93.     if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
  94.     {
  95.       return 0;
  96.     }
  97.   }

  98.   return -1;
  99. }

  100. static DSTATUS SD_CheckStatus(BYTE lun)
  101. {
  102.   Stat = STA_NOINIT;

  103.   if(BSP_SD_GetCardState() == MSD_OK)
  104.   {
  105.     Stat &= ~STA_NOINIT;
  106.   }

  107.   return Stat;
  108. }

  109. /**
  110. * @brief  Initializes a Drive
  111. * @param  lun : not used
  112. * @retval DSTATUS: Operation status
  113. */
  114. DSTATUS SD_initialize(BYTE lun)
  115. {
  116. #if !defined(DISABLE_SD_INIT)

  117.   if(BSP_SD_Init() == MSD_OK)
  118.   {
  119.     Stat = SD_CheckStatus(lun);
  120.   }

  121. #else
  122.   Stat = SD_CheckStatus(lun);
  123. #endif
  124.   return Stat;
  125. }

  126. /**
  127. * @brief  Gets Disk Status
  128. * @param  lun : not used
  129. * @retval DSTATUS: Operation status
  130. */
  131. DSTATUS SD_status(BYTE lun)
  132. {
  133.   return SD_CheckStatus(lun);
  134. }

  135. /**
  136. * @brief  Reads Sector(s)
  137. * @param  lun : not used
  138. * @param  *buff: Data buffer to store read data
  139. * @param  sector: Sector address (LBA)
  140. * @param  count: Number of sectors to read (1..128)
  141. * @retval DRESULT: Operation result
  142. */
  143. DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
  144. {
  145.   DRESULT res = RES_ERROR;
  146.   uint32_t timeout;
  147. #if defined(ENABLE_SCRATCH_BUFFER)
  148.   uint8_t ret;
  149. #endif
  150. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  151.   uint32_t alignedAddr;
  152. #endif

  153.   /*
  154.   * ensure the SDCard is ready for a new operation
  155.   */

  156.   if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
  157.   {
  158.     return res;
  159.   }

  160. #if defined(ENABLE_SCRATCH_BUFFER)
  161.   if (!((uint32_t)buff & 0x3))
  162.   {
  163. #endif
  164.     if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
  165.                              (uint32_t) (sector),
  166.                              count) == MSD_OK)
  167.     {
  168.       ReadStatus = 0;
  169.       /* Wait that the reading process is completed or a timeout occurs */
  170.       timeout = HAL_GetTick();
  171.       while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
  172.       {
  173.       }
  174.       /* incase of a timeout return error */
  175.       if (ReadStatus == 0)
  176.       {
  177.         res = RES_ERROR;
  178.       }
  179.       else
  180.       {
  181.         ReadStatus = 0;
  182.         timeout = HAL_GetTick();

  183.         while((HAL_GetTick() - timeout) < SD_TIMEOUT)
  184.         {
  185.           if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
  186.           {
  187.             res = RES_OK;
  188. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  189.             /*
  190.             the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
  191.             adjust the address and the D-Cache size to invalidate accordingly.
  192.             */
  193.             alignedAddr = (uint32_t)buff & ~0x1F;
  194.             SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
  195. #endif
  196.             break;
  197.           }
  198.         }
  199.       }
  200.     }
  201. #if defined(ENABLE_SCRATCH_BUFFER)
  202.   }
  203.     else
  204.     {
  205.       /* Slow path, fetch each sector a part and memcpy to destination buffer */
  206.       int i;

  207.       for (i = 0; i < count; i++) {
  208.         ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
  209.         if (ret == MSD_OK) {
  210.           /* wait until the read is successful or a timeout occurs */

  211.           timeout = HAL_GetTick();
  212.           while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
  213.           {
  214.           }
  215.           if (ReadStatus == 0)
  216.           {
  217.             res = RES_ERROR;
  218.             break;
  219.           }
  220.           ReadStatus = 0;

  221. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  222.           /*
  223.           *
  224.           * invalidate the scratch buffer before the next read to get the actual data instead of the cached one
  225.           */
  226.           SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
  227. #endif
  228.           memcpy(buff, scratch, BLOCKSIZE);
  229.           buff += BLOCKSIZE;
  230.         }
  231.         else
  232.         {
  233.           break;
  234.         }
  235.       }

  236.       if ((i == count) && (ret == MSD_OK))
  237.         res = RES_OK;
  238.     }
  239. #endif

  240.   return res;
  241. }
  242. /**
  243. * @brief  Writes Sector(s)
  244. * @param  lun : not used
  245. * @param  *buff: Data to be written
  246. * @param  sector: Sector address (LBA)
  247. * @param  count: Number of sectors to write (1..128)
  248. * @retval DRESULT: Operation result
  249. */
  250. #if _USE_WRITE == 1
  251. DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
  252. {
  253.   DRESULT res = RES_ERROR;
  254.   uint32_t timeout;
  255. #if defined(ENABLE_SCRATCH_BUFFER)
  256.   uint8_t ret;
  257.   int i;
  258. #endif

  259.    WriteStatus = 0;
  260. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  261.   uint32_t alignedAddr;
  262. #endif

  263.   if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
  264.   {
  265.     return res;
  266.   }

  267. #if defined(ENABLE_SCRATCH_BUFFER)
  268.   if (!((uint32_t)buff & 0x3))
  269.   {
  270. #endif
  271. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)

  272.     /*
  273.     the SCB_CleanDCache_by_Addr() requires a 32-Byte aligned address
  274.     adjust the address and the D-Cache size to clean accordingly.
  275.     */
  276.     alignedAddr = (uint32_t)buff &  ~0x1F;
  277.     SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
  278. #endif


  279.     if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff,
  280.                               (uint32_t)(sector),
  281.                               count) == MSD_OK)
  282.     {
  283.       /* Wait that writing process is completed or a timeout occurs */

  284.       timeout = HAL_GetTick();
  285.       while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
  286.       {
  287.       }
  288.       /* incase of a timeout return error */
  289.       if (WriteStatus == 0)
  290.       {
  291.         res = RES_ERROR;
  292.       }
  293.       else
  294.       {
  295.         WriteStatus = 0;
  296.         timeout = HAL_GetTick();

  297.         while((HAL_GetTick() - timeout) < SD_TIMEOUT)
  298.         {
  299.           if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
  300.           {
  301.             res = RES_OK;
  302.             break;
  303.           }
  304.         }
  305.       }
  306.     }
  307. #if defined(ENABLE_SCRATCH_BUFFER)
  308.   }
  309.     else
  310.     {
  311.       /* Slow path, fetch each sector a part and memcpy to destination buffer */
  312. #if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  313.       /*
  314.       * invalidate the scratch buffer before the next write to get the actual data instead of the cached one
  315.       */
  316.       SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
  317. #endif

  318.       for (i = 0; i < count; i++)
  319.       {
  320.         WriteStatus = 0;

  321.         memcpy((void *)scratch, (void *)buff, BLOCKSIZE);
  322.         buff += BLOCKSIZE;

  323.         ret = BSP_SD_WriteBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
  324.         if (ret == MSD_OK) {
  325.           /* wait for a message from the queue or a timeout */
  326.           timeout = HAL_GetTick();
  327.           while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
  328.           {
  329.           }
  330.           if (WriteStatus == 0)
  331.           {
  332.             break;
  333.           }

  334.         }
  335.         else
  336.         {
  337.           break;
  338.         }
  339.       }
  340.       if ((i == count) && (ret == MSD_OK))
  341.         res = RES_OK;
  342.     }
  343. #endif
  344.   return res;
  345. }
  346. #endif /* _USE_WRITE == 1 */

  347. /**
  348. * @brief  I/O control operation
  349. * @param  lun : not used
  350. * @param  cmd: Control code
  351. * @param  *buff: Buffer to send/receive control data
  352. * @retval DRESULT: Operation result
  353. */
  354. #if _USE_IOCTL == 1
  355. DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff)
  356. {
  357.   DRESULT res = RES_ERROR;
  358.   BSP_SD_CardInfo CardInfo;

  359.   if (Stat & STA_NOINIT) return RES_NOTRDY;

  360.   switch (cmd)
  361.   {
  362.     /* Make sure that no pending write process */
  363.   case CTRL_SYNC :
  364.     res = RES_OK;
  365.     break;

  366.     /* Get number of sectors on the disk (DWORD) */
  367.   case GET_SECTOR_COUNT :
  368.     BSP_SD_GetCardInfo(&CardInfo);
  369.     *(DWORD*)buff = CardInfo.LogBlockNbr;
  370.     res = RES_OK;
  371.     break;

  372.     /* Get R/W sector size (WORD) */
  373.   case GET_SECTOR_SIZE :
  374.     BSP_SD_GetCardInfo(&CardInfo);
  375.     *(WORD*)buff = CardInfo.LogBlockSize;
  376.     res = RES_OK;
  377.     break;

  378.     /* Get erase block size in unit of sector (DWORD) */
  379.   case GET_BLOCK_SIZE :
  380.     BSP_SD_GetCardInfo(&CardInfo);
  381.     *(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE;
  382.         res = RES_OK;
  383.     break;

  384.   default:
  385.     res = RES_PARERR;
  386.   }

  387.   return res;
  388. }
  389. #endif /* _USE_IOCTL == 1 */



  390. /**
  391. * @brief Tx Transfer completed callbacks
  392. * @param hsd: SD handle
  393. * @retval None
  394. */

  395. /*
  396. ===============================================================================
  397. Select the correct function signature depending on your platform.
  398. please refer to the file "stm32xxxx_eval_sd.h" to verify the correct function
  399. prototype
  400. ===============================================================================
  401. */
  402. //void BSP_SD_WriteCpltCallback(uint32_t SdCard)
  403. void BSP_SD_WriteCpltCallback(void)
  404. {
  405.   WriteStatus = 1;
  406. }

  407. /**
  408. * @brief Rx Transfer completed callbacks
  409. * @param hsd: SD handle
  410. * @retval None
  411. */

  412. /*
  413. ===============================================================================
  414. Select the correct function signature depending on your platform.
  415. please refer to the file "stm32xxxx_eval_sd.h" to verify the correct function
  416. prototype
  417. ===============================================================================
  418. */
  419. //void BSP_SD_ReadCpltCallback(uint32_t SdCard)
  420. void BSP_SD_ReadCpltCallback(void)
  421. {
  422.   ReadStatus = 1;
  423. }

  424. /*
  425. ==============================================================================================
  426.   depending on the SD_HAL_Driver version, either the HAL_SD_ErrorCallback() or HAL_SD_AbortCallback()
  427.   or both could be defined, activate the callbacks below when suitable and needed
  428. ==============================================================================================
  429. void BSP_SD_AbortCallback(void)
  430. {
  431. }

  432. void    BSP_SD_ErrorCallback(void)
  433. {
  434. }
  435. */
  436. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

复制代码






回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2020-9-22 13:43:40 | 显示全部楼层
现在想想知道为什么使用SDIO自带DMA,因为通用的DMA性能不行,无法满足H7的SDMMC超高速模式

STM32H7的SDIO外接支持UHS-I 模式 (SDR12, SDR25, SDR50, SDR104和DDR50)需要1.8的电平转换器
https://forum.anfulai.cn/forum.php?m ... id=89590&fromuid=58
(出处: 硬汉嵌入式论坛)

通用DMA性能:

STM32H7的SDIO外接支持UHS-I 模式 (SDR12, SDR25, SDR50, SDR104和DDR50)需要1.8的电平转换器
https://forum.anfulai.cn/forum.php?m ... id=89590&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

692

主题

3576

回帖

5677

积分

论坛元老

积分
5677
发表于 2020-9-22 17:12:16 | 显示全部楼层
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2020-9-23 01:52:12 | 显示全部楼层
实际测试,官方提供的这种方式,性能略挫。
回复

使用道具 举报

12

主题

40

回帖

76

积分

初级会员

积分
76
发表于 2020-12-10 09:55:11 | 显示全部楼层
通用DMA 怎么做。有没有例子。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2020-12-10 10:34:27 | 显示全部楼层
终极菜鸟 发表于 2020-12-10 09:55
通用DMA 怎么做。有没有例子。

没有用通用DMA做,用的SDIO自带的专用DMA
回复

使用道具 举报

15

主题

65

回帖

110

积分

初级会员

积分
110
发表于 2024-9-25 16:19:44 | 显示全部楼层
所以这里正确的操作是什么样的,
看ST单独代码 fx_stm32_sd_driver.c 78~83行,与0x3做与操作,但是也不是4字节对齐吧
[C] 纯文本查看 复制代码
#if (FX_STM32_SD_DMA_API == 1)
  /* the SD DMA requires a 4-byte aligned buffers */
  unaligned_buffer = (UINT)(media_ptr->fx_media_driver_buffer) & 0x3;
#else
  /* if the DMA is not used there isn't any constraint on buffer alignment */
  unaligned_buffer = 0;
#endif
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2024-9-26 07:52:53 | 显示全部楼层
Superusrss 发表于 2024-9-25 16:19
所以这里正确的操作是什么样的,
看ST单独代码 fx_stm32_sd_driver.c 78~83行,与0x3做与操作,但是也不是 ...

也改成开了个缓冲,做复制粘贴了。
回复

使用道具 举报

22

主题

196

回帖

262

积分

高级会员

积分
262
发表于 2025-1-16 13:47:56 | 显示全部楼层
f4的不对齐有什么后果呢
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2025-1-17 06:11:44 | 显示全部楼层
yuanzhongda 发表于 2025-1-16 13:47
f4的不对齐有什么后果呢

F4系列可以通过DMA FIFO方式的收发位宽设置解决

标准库版:
https://forum.anfulai.cn/forum.p ... C%D0%F8%CC%E1%C9%FD

HAL版:
https://forum.anfulai.cn/forum.p ... &extra=page%3D1
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
发表于 2025-1-23 10:30:05 | 显示全部楼层
我是完全自己写的sdmmc的驱动,驱动的时8线的EMMC芯片,由于电路的问题,最终能运行在33MHz的DDR模式,实测的DMA写入速度能达到50MB/s左右,基本能跑满整个时钟线的频率。 在eMMC芯片的内部cacah写满后的真实写入速度也能到20MB/s左右。
我的SDMMC中断的程序是这么写的。 不知道和你们的有什么不一样。
void bsp_emmc_handler(void *param)
{
        EMMC_Info *emmc_info = (EMMC_Info *)param;
       
        if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_FLAG_RXFIFOHF) != RESET)                                // 检查接收FIFO半满中断,如果触发就读取FIFO的数据, FIFO一半共32个字节
        {
                bsp_emmc_read_fifo(emmc_info);                                                                                                        // 读取接收FIFO的数据
        }
        else if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_FLAG_TXFIFOHE) != RESET)
        {
                bsp_emmc_write_fifo(emmc_info);
        }
        else if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_FLAG_DATAEND) != RESET)                        // 检查数据传输结束中断
        {
                bsp_sdmmc_clear_flag(emmc_info->sdmmc, SDMMC_FLAG_DATAEND);                                                // 清除数据传输结束中断标志
       
                bsp_sdmmc_disable_IT(emmc_info->sdmmc, SDMMC_IT_DATAEND  | SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | \
                                                         SDMMC_IT_TXUNDERR | SDMMC_IT_RXOVERR  | SDMMC_IT_TXFIFOHE | SDMMC_IT_RXFIFOHF);

                bsp_sdmmc_disable_IT(emmc_info->sdmmc, SDMMC_IT_IDMABTC);
                bsp_sdmmc_cmd_transmission_disable(emmc_info->sdmmc);                                                        // 停止传输

                if (emmc_info->context & MMC_CONTEXT_DMA)
                {
                        emmc_info->sdmmc->DLEN = 0;
                        emmc_info->sdmmc->DCTRL = 0;
                        emmc_info->sdmmc->IDMACTRL = SDMMC_DISABLE_IDMA ;
                }

                if (((emmc_info->context & MMC_CONTEXT_READ_MULTIPLE_BLOCK) != 0U) ||
                        ((emmc_info->context & MMC_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
                        emmc_info->error_code =  emmc_stop_transmission(emmc_info->sdmmc, emmc_info->addr);

                emmc_info->trans_length = 0;
                emmc_info->context = 0;
                /* Clear all the static flags */
                bsp_sdmmc_clear_flag(emmc_info->sdmmc, SDMMC_STATIC_DATA_FLAGS);
        }
        else if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_FLAG_DCRCFAIL | SDMMC_FLAG_DTIMEOUT | SDMMC_FLAG_RXOVERR | SDMMC_FLAG_TXUNDERR) != RESET)
        {
        /* Set Error code */
                if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_IT_DCRCFAIL))
                {
                  emmc_info->error_code |= SDMMC_IT_DCRCFAIL;
                }
                if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_IT_DTIMEOUT))
                {
                  emmc_info->error_code |= SDMMC_IT_DCRCFAIL;
                }
                if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_IT_RXOVERR))
                {
                  emmc_info->error_code |= SDMMC_IT_DCRCFAIL;
                }
                if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_IT_TXUNDERR))
                {
                  emmc_info->error_code |= SDMMC_IT_DCRCFAIL;
                }

                /* Clear All flags */
                bsp_sdmmc_clear_flag(emmc_info->sdmmc, SDMMC_STATIC_DATA_FLAGS);

                /* Disable all interrupts */
                bsp_sdmmc_disable_IT(emmc_info->sdmmc, SDMMC_IT_DATAEND | SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | \
                                                         SDMMC_IT_TXUNDERR | SDMMC_IT_RXOVERR);

                bsp_sdmmc_cmd_transmission_disable(emmc_info->sdmmc);
                emmc_info->sdmmc->DCTRL |= SDMMC_DCTRL_FIFORST;
                emmc_info->sdmmc->CMD |= SDMMC_CMD_CMDSTOP;
                               
                if (((emmc_info->context & MMC_CONTEXT_READ_MULTIPLE_BLOCK) != 0U) || ((emmc_info->context & MMC_CONTEXT_WRITE_MULTIPLE_BLOCK) != 0U))
                        emmc_info->error_code =  emmc_stop_transmission(emmc_info->sdmmc, emmc_info->addr);
               
                emmc_info->sdmmc->CMD &= ~(SDMMC_CMD_CMDSTOP);
                bsp_sdmmc_clear_flag(emmc_info->sdmmc, SDMMC_FLAG_DABORT);

                if ((emmc_info->context & MMC_CONTEXT_DMA) != 0U)
                {
                        if (emmc_info->error_code)
                        {
                                /* Disable Internal DMA */
                                bsp_sdmmc_disable_IT(emmc_info->sdmmc, SDMMC_IT_IDMABTC);
                                emmc_info->sdmmc->IDMACTRL = SDMMC_DISABLE_IDMA;
                        }
                }
        }

        else if (bsp_sdmmc_get_flag(emmc_info->sdmmc, SDMMC_FLAG_IDMABTC))
        {
                bsp_sdmmc_clear_flag(emmc_info->sdmmc, SDMMC_IT_IDMABTC);
        }
}

回复

使用道具 举报

22

主题

196

回帖

262

积分

高级会员

积分
262
发表于 2025-1-23 13:50:29 | 显示全部楼层
cuijunling00 发表于 2025-1-23 10:30
我是完全自己写的sdmmc的驱动,驱动的时8线的EMMC芯片,由于电路的问题,最终能运行在33MHz的DDR模式,实测 ...

想看下mmc的ddr使能那里
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2025-1-24 06:24:33 | 显示全部楼层
yuanzhongda 发表于 2025-1-23 13:50
想看下mmc的ddr使能那里

HAL有专门的配置项。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117546
QQ
 楼主| 发表于 2025-1-24 06:24:58 | 显示全部楼层
cuijunling00 发表于 2025-1-23 10:30
我是完全自己写的sdmmc的驱动,驱动的时8线的EMMC芯片,由于电路的问题,最终能运行在33MHz的DDR模式,实测 ...

自己写SDMMC驱动,相当可以
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
发表于 2025-1-26 08:59:50 | 显示全部楼层
eric2013 发表于 2025-1-24 06:24
自己写SDMMC驱动,相当可以

因为HAL库的SDMMC的驱动有大bug,他无法实现多个EMMC芯片或多个SD卡的操作。不符合EMMC4.51的协议规范。所以我只能是自己写。
回复

使用道具 举报

1

主题

12

回帖

15

积分

新手上路

积分
15
发表于 2025-1-26 09:05:22 | 显示全部楼层
yuanzhongda 发表于 2025-1-23 13:50
想看下mmc的ddr使能那里

HAL库的我也没有弄成功DDR的操作。 我是自己写的驱动。按照emmc4.51的协议,你要完成emmc的初始化,然后再进行高速,线宽,和DDR的设置。 下面是我的代码,包含了必要的函数。 你自己看看吧。

EM_EMMC_FLASH_STATE bsp_emmc_init(SDMMC_TypeDef *sdmmc, EMMC_CLOCK_CONFIG *emmc_init, EMMC_Info *EMMC)
{
    uint32_t errorstate;
    SDMMC_CLOCK_CONFIG Init;
    uint32_t sdmmc_clk;
    bool result = true;
       
        if(EMMC->card_state >= 4)
                return EM_EMMC_FLASH_OK;
       
        result = bsp_emmc_gpio_init(sdmmc, emmc_init);
        if(result == false)
                return EM_EMMC_GPIO_ERROR;
       
        EMMC->sdmmc = sdmmc;
       
        memcpy(&EMMC->init,emmc_init, sizeof(EMMC_CLOCK_CONFIG));
       
        bsp_rcc_set_SDMMC_clock_source(SDMMC_CLKSOURCE_PLL2_R);
        if(sdmmc == SDMMC1)
                bsp_rcc_AHB3_GRP1_enable_clock(AHB3_GRP1_PERIPH_SDMMC1);
        else if(sdmmc == SDMMC2)
                bsp_rcc_AHB2_GRP1_enable_clock(AHB2_GRP1_PERIPH_SDMMC2);
       
        Init.bus_speed = 0;
        Init.bus_width = EM_SDMMC_BUS_WIDE_1B;
        Init.hardware_flow_control = EM_SDMMC_HWFC_DIS;
        Init.negedge = EM_SDMMC_RISING;
        Init.power_save = EM_SDMMC_POWER_SAVE_DIS;
        Init.select_clock_rx = EM_SDMMC_IO_IN_CLK;
        Init.use_ddr = 0;

    // 1.刚开始初始化时钟要将频率分配到400K以下
    sdmmc_clk = bsp_rcc_get_peripheral_clock_freq(RCC_SDMMC_CLKSOURCE);
    Init.clock_division = sdmmc_clk/(2U*400000U);

    bsp_sdmmc_clk_init(EMMC->sdmmc, &Init);
    bsp_sdmmc_power_on(EMMC->sdmmc);

    /* 等待74个周期:在开始MMC初始化序列之前所需的电源启动等待时间 */
    //前面已经将时钟设置在400KHz了,等待时间 = (74 /400) = 185 微秒
    //所以等待200微秒完全够用
    sleep_1us(200);

    errorstate = emmc_goto_idle_state(EMMC->sdmmc);                         // 4.发送CMD0 进入空闲状态
    if(errorstate != SDMMC_ERROR_NONE)
                return EM_EMMC_CMD_ERROR;
       
        EMMC->addr = 2;
    errorstate = emmc_bus_init(EMMC->sdmmc, EMMC->addr, &(EMMC->card_info));                        // 总线初始化
    if(errorstate != SDMMC_ERROR_NONE)
    {
        EMMC->error_code = errorstate;
        EMMC->card_state = emmc_get_card_state();
        return EM_EMMC_CMD_ERROR;
    }
       
        errorstate = emmc_change_to_high_speed(EMMC);
        if(errorstate != SDMMC_ERROR_NONE)
                return EM_EMMC_CMD_ERROR;
       
        Init.hardware_flow_control = emmc_init->hardware_flow_control;
        Init.hardware_flow_control = emmc_init->hardware_flow_control;;
        Init.negedge = emmc_init->negedge;
        Init.power_save = emmc_init->power_save;
        Init.select_clock_rx = emmc_init->select_clock_rx;
        Init.clock_division = emmc_init->clock_division;
       
        Init.bus_speed = EM_SDMMC_SDR50_DDR50_SDR104;
       
        sdmmc_clk = sdmmc_clk >> Init.clock_division;
        if(sdmmc_clk <= 25000000)
                Init.bus_speed = EM_SDMMC_DS_HS_SDR12_SDR25;
        else
                Init.bus_speed = EM_SDMMC_SDR50_DDR50_SDR104;

        Init.use_ddr = 0;
        switch(emmc_init->bus_config.bus_wide)
        {
                case SDMMC_BUS_WIDE_1B: Init.bus_width = EM_SDMMC_BUS_WIDE_1B; break;
                case SDMMC_BUS_WIDE_4B: Init.bus_width = EM_SDMMC_BUS_WIDE_4B; break;
                case SDMMC_BUS_WIDE_8B: Init.bus_width = EM_SDMMC_BUS_WIDE_8B; break;
                case SDMMC_BUS_WIDE_4B_DDR:
                        Init.bus_width = EM_SDMMC_BUS_WIDE_4B;
                        Init.use_ddr = 1;
                        break;
                case SDMMC_BUS_WIDE_8B_DDR:
                        Init.bus_width = EM_SDMMC_BUS_WIDE_8B;
                        Init.use_ddr = 1;
                        break;
                default:break;
        }
    bsp_sdmmc_clk_init(EMMC->sdmmc, &Init);
       
        if(sdmmc == SDMMC1)
        {
                NVIC_SetPriority(SDMMC1_IRQn, 0);
                NVIC_EnableIRQ(SDMMC1_IRQn);
        }
        if(sdmmc == SDMMC2)
        {
                NVIC_SetPriority(SDMMC2_IRQn, 0);
                NVIC_EnableIRQ(SDMMC2_IRQn);
        }
       
    return EM_EMMC_FLASH_OK;
}

void bsp_sdmmc_clk_init(SDMMC_TypeDef *SDMMCx, SDMMC_CLOCK_CONFIG *clock_config)
{
    unsigned int tmpreg = 0;

    tmpreg |= clock_config->select_clock_rx                 << SDMMC_CLKCR_SELCLKRX_Pos;
    tmpreg |= clock_config->bus_speed                                 << SDMMC_CLKCR_BUSSPEED_Pos;
    tmpreg |= clock_config->use_ddr                                 << SDMMC_CLKCR_DDR_Pos;
    tmpreg |= clock_config->hardware_flow_control         << SDMMC_CLKCR_HWFC_EN_Pos;
    tmpreg |= clock_config->negedge                                 << SDMMC_CLKCR_NEGEDGE_Pos;
    tmpreg |= clock_config->bus_width                                 << SDMMC_CLKCR_WIDBUS_Pos;
    tmpreg |= clock_config->power_save                                 << SDMMC_CLKCR_PWRSAV_Pos;
    tmpreg |= clock_config->clock_division                         << 0;

    MODIFY_REG(SDMMCx->CLKCR, 0xffffffff, tmpreg);
}

/**
  * @brief  Initializes the mmc card.
  * @param  hmmc: Pointer to MMC handle
  * @retval MMC Card error state
  */
unsigned int emmc_bus_init(SDMMC_TypeDef  *sdmmc, unsigned char addr, EMMC_CardInfoTypeDef *card_info)
{
    uint32_t errorstate;
        int time_out = 0;
    EMMC_CardCSDTypeDef csd_def ;
       
    time_out = g_system_tick_count + 8;

        while(g_system_tick_count < time_out)                                   
        {
                errorstate = emmc_send_op_cond(sdmmc, card_info);                          // 5~6.发送CMD1 发送OCR 接收到支持的电压参数
                if(errorstate == 0)                                                        // 7. 如果设备忙就重复5~6
                        break;
        }
       
        if(g_system_tick_count >= time_out)                                            // 如果8ms没有获取到支持的电压就返回错误
                return SDMMC_ERROR_INVALID_VOLTRANGE;
       
    if(bsp_sdmmc_get_power_state(sdmmc) == 0U)                                  // 确定外设SDMMC接口时上电的状态
        return SDMMC_ERROR_REQUEST_NOT_APPLICABLE;

    errorstate = emmc_all_send_cid(sdmmc, card_info);                                         // 16~17. 发送CMD9 发送CID 接收到卡的信息
    if(errorstate != SDMMC_ERROR_NONE)
        return errorstate;

        errorstate = emmc_set_relative_addr(sdmmc, addr);                                      // 18. 发送CMD10 设置相对地址
        if(errorstate != SDMMC_ERROR_NONE)
                return errorstate;

    errorstate = emmc_send_csd(sdmmc, addr, &csd_def);                                            // 19. 发送CMD12 发送CSD 接收到卡的信息
    if(errorstate != SDMMC_ERROR_NONE)
        return errorstate;
               
        errorstate =  emmc_select_device(sdmmc, addr );                                                    // 22 发送CMD7 选择设备 将设备设置为tran模式
        if(errorstate != SDMMC_ERROR_NONE)
        return errorstate;

        // 读取ext_csd的重要信息
        errorstate = emmc_read_ext_csd_info(sdmmc, card_info);
        if(errorstate != SDMMC_ERROR_NONE)
        return errorstate;
       
  /* All cards are initialized */
  return SDMMC_ERROR_NONE;
}

EM_EMMC_FLASH_STATE emmc_change_to_high_speed(EMMC_Info  *emmc_info)
{
        unsigned char power_class;

        emmc_info->error_code =  emmc_set_block_length(emmc_info->sdmmc, emmc_info->card_info.sector_size);    // 23 发送CMD16 设置块长度
        if(emmc_info->error_code != SDMMC_ERROR_NONE)
                goto EMMC_HIGH_SPEED_ERROR;
       
        // 修改功率等级
        emmc_info->error_code =  emmc_write_ext_csd(emmc_info->sdmmc, emmc_info->addr, 187, emmc_info->card_info.power_class_52_360 );
        if(emmc_info->error_code != SDMMC_ERROR_NONE)
                goto EMMC_HIGH_SPEED_ERROR;
       
        // 修改高速时序
        emmc_info->error_code = emmc_write_ext_csd(emmc_info->sdmmc, emmc_info->addr, 185, emmc_info->init.bus_config.bus_timing);
        if(emmc_info->error_code != SDMMC_ERROR_NONE)
                goto EMMC_HIGH_SPEED_ERROR;
       
        // 更改线宽
        emmc_info->error_code = emmc_write_ext_csd(emmc_info->sdmmc, emmc_info->addr, 183, emmc_info->init.bus_config.bus_wide);
        if(emmc_info->error_code != SDMMC_ERROR_NONE)
                goto EMMC_HIGH_SPEED_ERROR;
                       
        emmc_info->card_state = emmc_get_card_state();
        return EM_EMMC_FLASH_OK;
        EMMC_HIGH_SPEED_ERROR:
        emmc_info->card_state = emmc_get_card_state();
        return EM_EMMC_CMD_ERROR;
}


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-14 00:52 , Processed in 0.064737 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表