你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32F407VE单片机通过DMA往SDIO发送数据,出现Underrun和FIFO错误

[复制链接]
zlk1214 提问时间:2018-10-8 21:32 /
  1. #include <stdio.h>
  2. #include <stm32f4xx.h>
  3. #include <string.h>

  4. #define USE_DMA

  5. int fputc(int ch, FILE *fp)
  6. {
  7.   if (fp == stdout)
  8.   {
  9.     if (ch == '\n')
  10.     {
  11.       while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  12.       USART_SendData(USART1, '\r');
  13.     }
  14.     while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  15.     USART_SendData(USART1, ch);
  16.   }
  17.   return ch;
  18. }

  19. int main(void)
  20. {
  21.   char *src = "01234567890123456789012345678901"; // 32个字符
  22.   
  23. #ifdef USE_DMA
  24.   DMA_InitTypeDef dma;
  25. #else
  26.   uint8_t i;
  27. #endif
  28.   GPIO_InitTypeDef gpio;
  29.   SDIO_InitTypeDef sdio;
  30.   SDIO_DataInitTypeDef sdio_data;
  31.   USART_InitTypeDef usart;
  32.   
  33.   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);
  34.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO | RCC_APB2Periph_USART1, ENABLE);
  35.   
  36.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
  37.   GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
  38.   GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
  39.   GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
  40.   GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
  41.   GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
  42.   GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
  43.   GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);
  44.   
  45.   gpio.GPIO_Mode = GPIO_Mode_AF;
  46.   gpio.GPIO_OType = GPIO_OType_PP;
  47.   gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
  48.   gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
  49.   gpio.GPIO_Speed = GPIO_High_Speed;
  50.   GPIO_Init(GPIOA, &gpio);
  51.   
  52.   gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  53.   GPIO_Init(GPIOC, &gpio);
  54.   gpio.GPIO_Pin = GPIO_Pin_2;
  55.   GPIO_Init(GPIOD, &gpio);
  56.   
  57.   USART_StructInit(&usart);
  58.   usart.USART_BaudRate = 115200;
  59.   USART_Init(USART1, &usart);
  60.   USART_Cmd(USART1, ENABLE);
  61.   printf("STM32F407VE DMA\n");
  62.   printf("strlen=%d\n", strlen(src));
  63.   
  64.   SDIO_SetPowerState(SDIO_PowerState_ON);
  65.   SDIO_StructInit(&sdio);
  66.   sdio.SDIO_BusWide = SDIO_BusWide_4b;
  67.   sdio.SDIO_ClockDiv = 255;
  68.   //sdio.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Enable;
  69.   SDIO_Init(&sdio);
  70.   SDIO_ClockCmd(ENABLE);
  71.   //SDIO_SetSDIOOperation(ENABLE);
  72.   
  73. #ifdef USE_DMA
  74.   dma.DMA_BufferSize = 8;
  75.   dma.DMA_Channel = DMA_Channel_4;
  76.   dma.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  77.   dma.DMA_FIFOMode = DMA_FIFOMode_Enable;
  78.   dma.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  79.   dma.DMA_Memory0BaseAddr = (uint32_t)src;
  80.   dma.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
  81.   dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  82.   dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
  83.   dma.DMA_Mode = DMA_Mode_Normal;
  84.   dma.DMA_PeripheralBaseAddr = (uint32_t)&SDIO->FIFO;
  85.   dma.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
  86.   dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  87.   dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  88.   dma.DMA_Priority = DMA_Priority_VeryHigh;
  89.   DMA_Init(DMA2_Stream3, &dma);
  90.   //DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);
  91.   DMA_Cmd(DMA2_Stream3, ENABLE);
  92. #endif
  93.   
  94.   sdio_data.SDIO_DataBlockSize = SDIO_DataBlockSize_32b;
  95.   sdio_data.SDIO_DataLength = 32;
  96.   sdio_data.SDIO_DataTimeOut = 1000000;
  97.   sdio_data.SDIO_DPSM = SDIO_DPSM_Enable;
  98.   sdio_data.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  99.   sdio_data.SDIO_TransferMode = SDIO_TransferMode_Block;
  100.   SDIO_DataConfig(&sdio_data);
  101.   
  102. #ifdef USE_DMA
  103.   SDIO_DMACmd(ENABLE);
  104. #else
  105.   for (i = 0; i < 8; i++)
  106.   {
  107.     while (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) == RESET);
  108.     SDIO_WriteData(*(uint32_t *)(src + 4 * i));
  109.   }
  110. #endif
  111.   
  112.   while (1)
  113.   {
  114.     if (DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_HTIF3) == SET)
  115.     {
  116.       DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_HTIF3);
  117.       printf("Half transfer! remaining=%d\n", DMA_GetCurrDataCounter(DMA2_Stream3));
  118.     }
  119.    
  120.     if (DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_TCIF3) == SET)
  121.     {
  122.       DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_TCIF3);
  123.       printf("Transfer complete! remaining=%d\n", DMA_GetCurrDataCounter(DMA2_Stream3));
  124.     }
  125.    
  126.     if (DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_FEIF3) == SET)
  127.     {
  128.       DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_FEIF3);
  129.       printf("FIFO error! remaining=%d\n", DMA_GetCurrDataCounter(DMA2_Stream3));
  130.     }
  131.    
  132.     if (DMA_GetFlagStatus(DMA2_Stream3, DMA_FLAG_DMEIF3) == SET)
  133.     {
  134.       DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_DMEIF3);
  135.       printf("Direct mode error!\n");
  136.     }
  137.    
  138.     if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) == SET)
  139.     {
  140.       SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
  141.       printf("SDIO data CRC failed!\n");
  142.     }
  143.    
  144.     if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) == SET)
  145.     {
  146.       SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
  147.       printf("SDIO data timeout!\n");
  148.     }
  149.    
  150.     if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) == SET)
  151.     {
  152.       SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);
  153.       printf("SDIO data underrun! DCOUNT=%d, NDT=%d\n", SDIO_GetDataCounter(), DMA_GetCurrDataCounter(DMA2_Stream3));
  154.     }
  155.   }
  156. }
复制代码


收藏 1 评论6 发布时间:2018-10-8 21:32

举报

6个回答
zlk1214 回答时间:2018-10-8 21:35:40
注释掉USE_DMA宏定义(不使用DMA模式),数据能够发送成功,并输出:
STM32F407VE DMA
strlen=32
SDIO data timeout!
(或SDIO data CRC failed!)

但如果使用DMA方式(定义了USE_DMA),则会输出:
STM32F407VE DMA
strlen=32
Half transfer! remaining=4
SDIO data underrun! DCOUNT=16, NDT=4
也就是说,DMA在还有16字节的时候就停止工作了。这是怎么回事?
zlk1214 回答时间:2018-10-8 21:36:30
有没有谁在STM32F4单片机上成功用DMA驱动了SD卡的?
hugo1990 回答时间:2018-10-9 09:39:42
看看学习下,谢谢      
zlk1214 回答时间:2018-10-10 08:36:20
找到解决办法了。修改如下两行:
  1. dma.DMA_BufferSize = 0;

  2. DMA_FlowControllerConfig(DMA2_Stream3, DMA_FlowCtrl_Peripheral);
复制代码

也就是说,必须由SDIO来控制DMA的数据传输量。

程序运行结果为:
STM32F407VE DMA
strlen=32
Transfer complete! remaining=65527
FIFO error! remaining=65527
SDIO data CRC failed!
成功触发了TCIF中断,且(65535-65527)×4=32字节。
同时还触发了FEIF中断,提示FIFO error。

STM32F4xx_DSP_StdPeriph_Lib_V1.8.0标准外设库里面有一个SD卡的例程程序stm324x7i_eval_sdio_sd.c,其中处理DMA中断的SD_ProcessDMAIRQ函数实现如下:
/**
  * @brief  This function waits until the SDIO DMA data transfer is finished.
  * @param  None.
  * @retval None.
  */
void SD_ProcessDMAIRQ(void)
{
  if(DMA2->LISR & SD_SDIO_DMA_FLAG_TCIF)
  {
    DMAEndOfTransfer = 0x01;
    DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_TCIF|SD_SDIO_DMA_FLAG_FEIF);
  }
}
程序在处理TCIF中断的时候,将FEIF标志位也直接清除掉了。所以从这里可以得出结论:FIFO error错误信息可以直接忽略。


看了一下STM32F4的参考手册,里面有这两段话:
If the DMEIFx or the FEIFx flag is set due to an overrun or underrun condition, the faulty
stream is not automatically disabled and it is up to the software to disable or not the stream
by resetting the EN bit in the DMA_SxCR register. This is because there is no data loss
when this kind of errors occur.
When a FIFO overrun or underrun condition occurs, the data are not lost because the
peripheral request is not acknowledged by the stream until the overrun or underrun
condition is cleared. If this acknowledge takes too much time, the peripheral itself may
detect an overrun or underrun condition of its internal buffer and data might be lost.
所以,FIFO error错误的产生原因是DMA本身的FIFO暂时出现了underrun/overrun的错误,此时DMA不会对SDIO的DMA请求发出确认,这是一个可自恢复的、不会影响数据传输的错误。

zlk1214 回答时间:2018-10-10 08:46:35
接收数据时,除了dma.DMA_DIR = DMA_DIR_PeripheralToMemory外,其他的配置都是一样的,也是必须由SDIO来控制DMA传输量。接收数据不会导致FIFO error错误,所以不需要清除FEIF标志位。
cdt2000 回答时间:2018-10-19 13:48:29
mark mark
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版