硬汉嵌入式论坛

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

[FreeRTOS] FREERTOS SDIO DMA FATFS 读SD卡的时候偶尔获取不到消息

[复制链接]

30

主题

205

回帖

295

积分

高级会员

积分
295
发表于 2025-10-10 23:49:07 | 显示全部楼层 |阅读模式
本帖最后由 ccschen 于 2025-10-11 00:25 编辑

如题,设备跑着跑着就出现读卡的时候偶尔获取不了消息,之前跟踪代码发现信号本身是给了的,但是获取没获取到,不知道是不是RTOS的问题或没用好。
代码里面发现出错,重新初始化卡后,就又对了。
如果不用DMA的话,不会出问题,只是不用DMA的时候,读写SD要不要开关中断?

[C] 纯文本查看 复制代码
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  uint8_t ret;
        DRESULT res = RES_ERROR;
  uint32_t timer;
#if (osCMSIS < 0x20000U)
  osEvent event;
#else
  uint16_t event;
  osStatus_t status;
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  uint32_t alignedAddr;
#endif
  /*
  * ensure the SDCard is ready for a new operation
  */

  if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
  {
    return res;
  }

#if defined(ENABLE_SCRATCH_BUFFER)
  if (!((uint32_t)buff & 0x3))
  {
#endif
    /* Fast path cause destination buffer is correctly aligned */
    ret = BSP_SD_ReadBlocks_DMA((uint32_t*)buff, (uint32_t)(sector), count);

    if (ret == MSD_OK) {
#if (osCMSIS < 0x20000U)
    /* wait for a message from the queue or a timeout */
    event = osMessageGet(SDQueueID, SD_TIMEOUT);

    if (event.status == osEventMessage)
    {
      if (event.value.v == READ_CPLT_MSG)
      {
        timer = osKernelSysTick();
        /* block until SDIO IP is ready or a timeout occur */
        while(osKernelSysTick() - timer <SD_TIMEOUT)
#else
[color=#ff0000]          status = osMessageQueueGet(SDQueueID, (void *)&event, NULL, SD_TIMEOUT);[/color]
          if ((status == osOK) && (event == READ_CPLT_MSG))
          {
            timer = osKernelGetTickCount();
            /* block until SDIO IP is ready or a timeout occur */
            while(osKernelGetTickCount() - timer <SD_TIMEOUT)
#endif
            {
              if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
              {
                res = RES_OK;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
                /*
                the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
                adjust the address and the D-Cache size to invalidate accordingly.
                */
                alignedAddr = (uint32_t)buff & ~0x1F;
                SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
                break;
              }
            }
#if (osCMSIS < 0x20000U)
          }
        }
#else
      }
#endif
    }

#if defined(ENABLE_SCRATCH_BUFFER)
    }
    else
    {
      /* Slow path, fetch each sector a part and memcpy to destination buffer */
      int i;

      for (i = 0; i < count; i++)
      {
        ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
        if (ret == MSD_OK )
        {
          /* wait until the read is successful or a timeout occurs */
#if (osCMSIS < 0x20000U)
          /* wait for a message from the queue or a timeout */
          event = osMessageGet(SDQueueID, SD_TIMEOUT);

          if (event.status == osEventMessage)
          {
            if (event.value.v == READ_CPLT_MSG)
            {
              timer = osKernelSysTick();
              /* block until SDIO IP is ready or a timeout occur */
              while(osKernelSysTick() - timer <SD_TIMEOUT)
#else
                status = osMessageQueueGet(SDQueueID, (void *)&event, NULL, SD_TIMEOUT);
              if ((status == osOK) && (event == READ_CPLT_MSG))
              {
                timer = osKernelGetTickCount();
                /* block until SDIO IP is ready or a timeout occur */
                ret = MSD_ERROR;
                while(osKernelGetTickCount() - timer < SD_TIMEOUT)
#endif
                {
                  ret = BSP_SD_GetCardState();

                  if (ret == MSD_OK)
                  {
                    break;
                  }
                }

                if (ret != MSD_OK)
                {
                  break;
                }
#if (osCMSIS < 0x20000U)
              }
            }
#else
          }
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
          /*
          *
          * invalidate the scratch buffer before the next read to get the actual data instead of the cached one
          */
          SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
#endif
          memcpy(buff, scratch, BLOCKSIZE);
          buff += BLOCKSIZE;
        }
        else
        {
          break;
        }
      }

      if ((i == count) && (ret == MSD_OK ))
        res = RES_OK;
    }
#endif
  return res;
}


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-11 10:01:31 | 显示全部楼层
继续使用SDIO DMA方式,把信号量处理都关闭了,独立一个任务里面操作是否正常。
回复

使用道具 举报

30

主题

205

回帖

295

积分

高级会员

积分
295
 楼主| 发表于 2025-10-11 15:44:08 | 显示全部楼层
eric2013 发表于 2025-10-11 10:01
继续使用SDIO DMA方式,把信号量处理都关闭了,独立一个任务里面操作是否正常。

这个我得仔细研究下了,目前手上事情多,又不是一直出的问题就很麻烦。FATFS的互斥,重入都关了的,现在保证同时只有一个地方操作文件,前天跟踪了下,感觉信号量也是发出了的,怀疑是生成的代码的有问题,比如地址,长度这些,SDQueueID默认的是16位的变量。

void BSP_SD_ReadCpltCallback(void)
{
  /*
   * No need to add an "osKernelRunning()" check here, as the SD_initialize()
   * is always called before any SD_Read()/SD_Write() call
   */
#if (osCMSIS < 0x20000U)
   osMessagePut(SDQueueID, READ_CPLT_MSG, 0);
#else
   const uint16_t msg = READ_CPLT_MSG;
   osMessageQueuePut(SDQueueID, (const void *)&msg, NULL, 0);
#endif
}
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-11 16:07:13 | 显示全部楼层
ccschen 发表于 2025-10-11 15:44
这个我得仔细研究下了,目前手上事情多,又不是一直出的问题就很麻烦。FATFS的互斥,重入都关了的,现在 ...

建议越简单越好,互斥什么的都不要加。实际应用仅开一个任务测试即可
回复

使用道具 举报

30

主题

205

回帖

295

积分

高级会员

积分
295
 楼主| 发表于 2025-10-11 20:12:18 | 显示全部楼层
eric2013 发表于 2025-10-11 16:07
建议越简单越好,互斥什么的都不要加。实际应用仅开一个任务测试即可

是的,之前最早生成的工程,就没有用DMA,就没有RTOS相关的内容。后来版本升级后,就算不用DMA,都有RTOS相关的内容。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-12 10:25:37 | 显示全部楼层
ccschen 发表于 2025-10-11 20:12
是的,之前最早生成的工程,就没有用DMA,就没有RTOS相关的内容。后来版本升级后,就算不用DMA,都有RTOS ...

在RTOS下也是,使用文件系统,建议仅1个任务使用,不涉及多任务,这样信号量什么的都不用加,跟裸机一样使用就行。
回复

使用道具 举报

30

主题

205

回帖

295

积分

高级会员

积分
295
 楼主| 发表于 2025-10-14 15:19:30 | 显示全部楼层
eric2013 发表于 2025-10-12 10:25
在RTOS下也是,使用文件系统,建议仅1个任务使用,不涉及多任务,这样信号量什么的都不用加,跟裸机一样 ...

跟踪了下,DMA写反而没问题,DMA读,很多时候没读到,没收到读完成的消息,读写都是一样的方式,不至于啊。
回复

使用道具 举报

30

主题

205

回帖

295

积分

高级会员

积分
295
 楼主| 发表于 2025-10-14 23:08:49 | 显示全部楼层
把库从L4 1.18.1退回1.18.0就没啥问题了。。。真的是。还好手上有别的型号板子测试对比。
回复

使用道具 举报

30

主题

205

回帖

295

积分

高级会员

积分
295
 楼主| 发表于 2025-10-15 08:15:07 | 显示全部楼层
ccschen 发表于 2025-10-14 23:08
把库从L4 1.18.1退回1.18.0就没啥问题了。。。真的是。还好手上有别的型号板子测试对比。

从半夜测试到现在,有2次读信号没收到,按老办法,重新初始化卡和文件系统,然后打开文件接着写。也许和文件频繁打开有关,但写的频度很低的,几十秒钟才写点数据。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-11-22 01:07 , Processed in 0.042029 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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