OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4253|回复: 2

STM32F4 SDIO DMA传输长度的疑问

[复制链接]

4

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
67
金钱
67
注册时间
2012-7-26
在线时间
10 小时
发表于 2017-10-21 00:58:16 | 显示全部楼层 |阅读模式
10金钱
本帖最后由 lrjray 于 2017-10-21 01:01 编辑

最近使用的调试STM32F4的SDIO加DMA,加FATFS,已经调试通过,但有一些疑问

SD_WriteMultiBlocks函数的参数依次为(源地址,目的地址,扇区大小,扇区数),使用DMA传输时,数据手册指明DMA的NDTR为数据的长度,范围(0~65535),也就是说DMA一次最大传输64KB字节,但SD_WriteMultiBlocks写入的大小为“扇区大小*扇区数”,也就是说SD_WriteMultiBlocks传递的参数可能溢出,网上找了多份FatFs代码,没有发现对此处做了参数检查,但多次测试没有问题。
[mw_shl_code=c,true]SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
  SD_Error errorstatus = SD_OK;

  TransferError = SD_OK;
  TransferEnd = 0;
  StopCondition = 1;

  SDIO->DCTRL = 0x0;

  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    WriteAddr /= 512;
  }

  /* Set Block Size for Card */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);

  if (SD_OK != errorstatus)
  {
    return(errorstatus);
  }

  /*!< To improve performance */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);


  errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  /*!< To improve performance */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)NumberOfBlocks;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }


  /*!< Send CMD25 WRITE_MULT_BLOCK with argument data address */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);

  if (SD_OK != errorstatus)
  {
    return(errorstatus);
  }

  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
  SDIO_DataConfig(&SDIO_DataInitStructure);

  SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
  SDIO_DMACmd(ENABLE);
  SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));

  return(errorstatus);
}[/mw_shl_code]

此处88行,用“扇区大小及扇区数计算总长度并写入,没有检查范围”,见如下函数
[mw_shl_code=c,true]void SD_LowLevel_DMA_TxConfig(uint32_t * BufferSRC, uint32_t BufferSize)
{
  DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);

  /* DMA2 Stream3  or Stream6 disable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);

  /* DMA2 Stream3  or Stream6 Config */
  DMA_DeInit(SD_SDIO_DMA_STREAM);

  /* Set DMA Structure */
  SDDMA_InitStructure.DMA_Memory0BaseAddr   = (uint32_t)BufferSRC;
  SDDMA_InitStructure.DMA_DIR         = DMA_DIR_MemoryToPeripheral;
  SDDMA_InitStructure.DMA_BufferSize      = BufferSize;
  DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
  DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
  DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);

  /* DMA2 Stream3  or Stream6 enable */
  DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
}
[/mw_shl_code]

也就是是该函数写入超过64KB可能出错,但实际使用多种张卡测试发现

直接调用SD_WriteMultiBlocks函数,发现一次写入长度小于等于256KB,数据是正常的,这远远大于DMA规定的64KB
直接调用SD_WriteMultiBlocks函数,一次写入长度512KB,前256KB数据是正常的,后256KB同先256KB的内容,不是预期写入的内容
直接调用SD_WriteMultiBlocks函数,一次写入长度1024KB,前256KB数据是正常的,后3个256KB块的内容同第一个256KB的内容,不是预期写入的内容

非常困惑,求大家指导

宁静致远
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

68

主题

165

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
388
金钱
388
注册时间
2017-3-2
在线时间
156 小时
发表于 2017-10-21 11:25:54 | 显示全部楼层
本帖最后由 kokoromi 于 2017-10-22 12:05 编辑

数据手册指明DMA的NDTR为数据的长度,范围(0~65535)。首先这个长度并不是字节,是根据你传输的字长决定的:
如果传输的是字,那么长度就小于64K*4=256KB;
如果传输的是半字,那么长度就小于64K*2=128KB;
如果传输的是字节,那么长度就小于64K*1=64KB;

回复

使用道具 举报

4

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
67
金钱
67
注册时间
2012-7-26
在线时间
10 小时
 楼主| 发表于 2017-10-22 16:50:56 | 显示全部楼层
kokoromi 发表于 2017-10-21 11:25
数据手册指明DMA的NDTR为数据的长度,范围(0~65535)。首先这个长度并不是字节,是根据你传输的字长决定的 ...

我原先也是这样想的,但实际测试发现使用字(4字节)模式,设定NDTR长度2048,写入SD扇区后,用PC读取,实际是写入了2048字节的数据,而不是8192字节,说明NDTR单位为字节
宁静致远
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-18 22:57

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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