打印
[STM32F1]

STM32 USART3使用DMA接收数据被覆盖出错

[复制链接]
6427|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
luozhi20060917|  楼主 | 2016-1-19 19:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近一项目调试中发现stm32的usart3在工作在dma接收模式下,如果接收数据超出了DMA1_Channel3->CNDTR的指定的长度,会覆盖且只覆盖接收buf[0]。
比如:参数配置CNDTR=3,而当发送在单片机接收引脚上的帧数据为0x01,0x02,0x03,0x04,0x05共5个字节,当DMA接收第3个字节0x03后,CNDTR=0 DMA停止读取串口接收数据,然后软件重新修改CNDTR=3,开启MDA接收功能等待发送到RX上的下一帧数据。问题来了,此时如果RX上没有接收到任何数据的情况先,DMA会自动将上一帧的0x04写入到buf[0],同时CNDTR被减一变为2。
感觉上一帧数据在CNDTR等于0时还是会触发一次DMA接收,当CNDTR重新不等于0以后就开始执行读操作。

附上调试代码,请高手指点,谢谢!!

#include "stm32f10x.h"
#include <string.h>

/* 无符号类型 */
typedef uint8_t           INT8U;
typedef uint16_t          INT16U;
typedef uint32_t          INT32U;
typedef uint64_t          INT64U;

/* 有符号类型 */
typedef int8_t            INT8;
typedef int16_t           INT16;
typedef int32_t           INT32;
typedef int64_t           INT64;

typedef ErrorStatus       RESULT_STATE;

#define DEF_COM_RECV_BUF_SIZE  50
#define DEF_COM_SEND_BUF_SIZE  50

struct
{
  INT8U SendBuf[DEF_COM_SEND_BUF_SIZE];
  INT8U RecvBuf[DEF_COM_RECV_BUF_SIZE];
  INT16U RecvLen;
  INT8U fSending :1;
  INT8U fRecv :1;
}sCom;

static void HW_ComCfg (void)
{
  /* GPIOB & AFIO Periph clock enable */
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
  
  /* PB10 USART3_Tx */
  GPIOB->CRH = (GPIOB->CRH & (~GPIO_CRH_MODE10)) | GPIO_CRH_MODE10_1; /* Output mode, max speed 2 MHz */
  GPIOB->CRH = (GPIOB->CRH & (~GPIO_CRH_CNF10)) | GPIO_CRH_CNF10_1; /* Alternate functionoutput Push-pull */
  /* PB11 USART3_Rx */
  GPIOB->CRH = (GPIOB->CRH & (~GPIO_CRH_MODE11)); /* input */
  GPIOB->CRH = (GPIOB->CRH & (~GPIO_CRH_CNF11)) | GPIO_CRH_CNF11_0; /* Floating input */
  
  /* Enable USART3 Clock */
  RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
  
  /* 1Start/9Data/1Stop */
  USART3->CR1 |= USART_CR1_M; /* 1 Start bit, 9 Data bits, n Stop bit */
  USART3->CR1 |= USART_CR1_PCE; /* Parity control enable */
  USART3->CR1 |= USART_CR1_PS; /* Odd parity */
  USART3->CR2 &= ~USART_CR2_STOP; /* 1 Stop bit */
  
  /* baud 115200bps */
  USART3->BRR = (INT32U)(((72000000ul/2)/115200/16 << 4) /* DIV_Mantissa */
                       | ((INT64U)(72000000ul/2)*10000/115200/16 - (72000000ul/2)/115200/16*10000)*16/10000); /* DIV_Fraction */
                       
  /* DMA for transmission */
  USART3->CR3 |= USART_CR3_DMAT; /* transmission DMA enabled */
  
  /* DMA1 channel2 */
  RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* Enable DMA1 Clock */
  DMA1_Channel2->CCR = (DMA1_Channel2->CCR & (~DMA_CCR2_PL)) | DMA_CCR2_PL_0; /* Channel priority level: medium */
  DMA1_Channel2->CCR = (DMA1_Channel2->CCR & (~DMA_CCR2_MSIZE)); /* Memory size:8-bits */
  DMA1_Channel2->CCR = (DMA1_Channel2->CCR & (~DMA_CCR2_PSIZE)); /* Peripheral size:8-bits */
  DMA1_Channel2->CCR |= DMA_CCR2_MINC; /* Memory increment mode enabled */
  DMA1_Channel2->CCR &= ~DMA_CCR2_PINC; /* Peripheral increment mode disabled */
  DMA1_Channel2->CCR |= DMA_CCR2_DIR; /* Read from memory */
  DMA1_Channel2->CPAR = (INT32U)&USART3->DR; /* Peripheral address */
  
  /* DMA for receive */
  USART3->CR3 |= USART_CR3_DMAR; /* receive DMA enabled */
  
  /* DMA1 channel3 */
  DMA1_Channel3->CCR = (DMA1_Channel3->CCR & (~DMA_CCR3_PL)) | DMA_CCR3_PL_1; /* Channel priority level: high */
  DMA1_Channel3->CCR = (DMA1_Channel3->CCR & (~DMA_CCR3_PSIZE)); /* Peripheral size:8-bits */
  DMA1_Channel3->CCR = (DMA1_Channel3->CCR & (~DMA_CCR3_MSIZE)); /* Memory size:8-bits */
  DMA1_Channel3->CCR &= ~DMA_CCR3_PINC; /* Peripheral increment mode disabled */
  DMA1_Channel3->CCR |= DMA_CCR3_MINC; /* Memory increment mode enabled */
  DMA1_Channel3->CCR &= ~DMA_CCR3_DIR; /* Read from Peripheral */
  DMA1_Channel3->CPAR = (INT32U)&USART3->DR; /* Peripheral address */
  
  /* interrupt */
  USART3->CR1 |= USART_CR1_IDLEIE; /* IDLE */
  
  /* USART enable */
  USART3->CR1 |= USART_CR1_TE | USART_CR1_RE; /* Transmitter and Receiver enable */
  USART3->CR1 |= USART_CR1_UE; /* USART prescaler and outputs enabled */
}

RESULT_STATE Com_Send (INT8U *p_data, INT16U len)
{
  if (sCom.fSending)
  {
    return ERROR;
  }
  
  if (len > DEF_COM_SEND_BUF_SIZE || len == 0)
  {
    return ERROR;
  }
  
  memcpy(&sCom.SendBuf[0], p_data, len);
  DMA1_Channel2->CCR &= ~DMA_CCR2_EN; /* Channel disabled */
  DMA1_Channel2->CNDTR = (INT32U)len; /* Number of data to transfer */
  DMA1_Channel2->CCR |= DMA_CCR2_EN; /* Channel enabled */
  USART3->CR1 |= USART_CR1_TCIE; /* TC */
  sCom.fSending = 1;
  
  return SUCCESS;
}

void ComInit (void)
{
  DMA1_Channel2->CMAR = (INT32U)&sCom.SendBuf[0]; /* Memory address */
  
  DMA1_Channel3->CCR &= ~DMA_CCR3_EN; /* Channel disabled */
  DMA1_Channel3->CMAR = (INT32U)&sCom.RecvBuf[0]; /* Memory address */
  DMA1_Channel3->CNDTR = (INT32U)sizeof(sCom.RecvBuf); /* Number of data to transfer */
  DMA1_Channel3->CCR |= DMA_CCR3_EN; /* Channel enabled */
}

static void HW_InterruptCfg (void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* Configures the priority grouping */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  
  /* COM */
  NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void USART3_IRQHandler(void)
{
  /* Transmission complete */
  if (sCom.fSending && (USART3->SR & USART_SR_TC))
  {
    USART3->CR1 &= ~USART_CR1_TCIE; /* TCIE */
    sCom.fSending = 0;
  }
  
  /* IDLE line detected */
  if (USART3->SR & USART_SR_IDLE)
  {
    sCom.RecvLen = (INT32U)sizeof(sCom.RecvBuf) - DMA1_Channel3->CNDTR;
   
    DMA1_Channel3->CCR &= ~DMA_CCR3_EN; /* Channel disabled */
    DMA1->IFCR |= DMA_IFCR_CGIF3;
    DMA1_Channel3->CNDTR = (INT32U)sizeof(sCom.RecvBuf); /* Number of data to transfer */
    DMA1_Channel3->CCR |= DMA_CCR3_EN; /* Channel enabled */
   
    if (USART3->SR & (USART_SR_NE | USART_SR_FE | USART_SR_PE)) /* Noise/Framing/Parity error is detected */
    {
    }
    else
    {
      sCom.fRecv = 1;
    }
   
    INT32U sr = USART3->SR; /* clear sr */
    INT32U dr = USART3->DR; /* clear dr */
  }
}

void main (void)
{
  HW_ComCfg();
  HW_InterruptCfg();
  ComInit();
  
  while (1)
  {
    if (sCom.fRecv)
    {
      Com_Send(&sCom.RecvBuf[0], sCom.RecvLen);
      sCom.fRecv = 0;
    }
  }
}

沙发
diweo| | 2016-1-20 08:06 | 只看该作者
再次启动DMA前,手动读一下USART3->DR试试看

使用特权

评论回复
板凳
luozhi20060917|  楼主 | 2016-1-20 09:15 | 只看该作者
以上面的例子来说,读出的值就是0x04,因为在读取完了第三个byte 0x03后,DMA就计数到0,不会再读取USART3->DR,此时DR=0x04,而当USART3的移位寄存器接收到0x05后,又由于DR不为空,不能覆盖DR的数据,应该产生Overrun error错误,但是奇怪的是,调试结果显示,该错误标志并未置位,感觉像是被DMA清掉了

使用特权

评论回复
地板
luozhi20060917|  楼主 | 2016-1-20 12:29 | 只看该作者
自己顶一个,麻烦大家帮忙分析下,急急急急急~

使用特权

评论回复
5
diweo| | 2016-1-21 09:02 | 只看该作者
luozhi20060917 发表于 2016-1-20 09:15
以上面的例子来说,读出的值就是0x04,因为在读取完了第三个byte 0x03后,DMA就计数到0,不会再读取USART3- ...

我的意思是读一次之后,DR就空了。再启动DMA可能就好了。
当然,这只是猜想。
我都是发送用DMA,接收就用正常的接收中断。

使用特权

评论回复
6
luozhi20060917|  楼主 | 2016-1-21 13:58 | 只看该作者
diweo 发表于 2016-1-21 09:02
我的意思是读一次之后,DR就空了。再启动DMA可能就好了。
当然,这只是猜想。
我都是发送用DMA,接收就用 ...

我测试过在DMA的TCIF中断中执行DR读取,读取的数据是整帧数据的最后字节(0x05),然后关闭DMA,USART3的IDLE中断中重新修改CNDTR,最后执行重启DMA功能后。DMA任然立即执行了DR的读取操作,写入到buf[0],与前面不同的是buf[0]=0x05

个人觉得DMA的USART功能对于接收的功能存在bug

使用特权

评论回复
7
luozhi20060917|  楼主 | 2016-1-22 09:34 | 只看该作者
自己顶一个

使用特权

评论回复
8
tony_stark| | 2016-1-22 15:52 | 只看该作者
CNDTR=0 DMA停止读取串口接收数据 后试试 能不能重新初始化DMA.
Cube库的函数好像是下面的函数

/**
  * @brief  DeInitializes the DMA peripheral
  * @param  hdma: pointer to a DMA_HandleTypeDef structure that contains
  *               the configuration information for the specified DMA Channel.  
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_DMA_DeInit(DMA_HandleTypeDef *hdma)

我不太明白为什么CNDTR不能设置的长一点。这样不是更好吗?


tony xu @ st chengdu office.

使用特权

评论回复
9
luozhi20060917|  楼主 | 2016-1-22 18:10 | 只看该作者
tony_stark 发表于 2016-1-22 15:52
CNDTR=0 DMA停止读取串口接收数据 后试试 能不能重新初始化DMA.
Cube库的函数好像是下面的函数

谢谢 tony的回复。
刚刚测试过了在TCIF中断发生时重新 DeInitializes。代码如下

DMA1 channel3 中断处理:
void DMA1_Channel3_IRQHandler (void)
{
  DMA_ClearITPendingBit(DMA1_IT_GL3);
  USART3->CR3 &= ~USART_CR3_DMAR; /* receive DMA disabled */
  DMA_Cmd(DMA1_Channel3, DISABLE); /* Channel disabled */
  DMA_DeInit(DMA1_Channel3);
  
  /* DMA for receive */
  USART3->CR3 |= USART_CR3_DMAR; /* receive DMA enabled */
  
  DMA_InitTypeDef DMA_InitStruct;
  /* DMA1 channel3 */
  /* Initialize the DMA_PeripheralBaseAddr member */
  DMA_InitStruct.DMA_PeripheralBaseAddr = (INT32U)&USART3->DR;
  /* Initialize the DMA_DIR member */
  DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
  /* Initialize the DMA_PeripheralInc member */
  DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  /* Initialize the DMA_MemoryInc member */
  DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
  /* Initialize the DMA_PeripheralDataSize member */
  DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  /* Initialize the DMA_MemoryDataSize member */
  DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  /* Initialize the DMA_Mode member */
  DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
  /* Initialize the DMA_Priority member */
  DMA_InitStruct.DMA_Priority = DMA_Priority_High;
  /* Initialize the DMA_M2M member */
  DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
  DMA_InitStruct.DMA_MemoryBaseAddr = (INT32U)&sCom.RecvBuf[0];
  DMA_Init(DMA1_Channel3, &DMA_InitStruct);
  
  DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
  
  sCom.fRecvErr = 1;
}

USART3中断:
void USART3_IRQHandler (void)
{
  /* Transmission complete */
  if (sCom.fSending && (USART3->SR & USART_SR_TC))
  {
    USART3->CR1 &= ~USART_CR1_TCIE; /* TCIE */
    sCom.fSending = 0;
  }
  
  /* IDLE line detected */
  if (USART3->SR & USART_SR_IDLE)
  {
    sCom.RecvLen = (INT32U)sizeof(sCom.RecvBuf) - DMA_GetCurrDataCounter(DMA1_Channel3);
   #if 1
    DMA_Cmd(DMA1_Channel3, DISABLE); /* Channel disabled */
    DMA_SetCurrDataCounter(DMA1_Channel3, (INT16U)sizeof(sCom.RecvBuf));
    DMA_Cmd(DMA1_Channel3, ENABLE); /* Channel enabled */
   #elif 1
    DMA1_Channel3->CCR &= ~DMA_CCR3_EN; /* Channel disabled */
    DMA1->IFCR |= DMA_IFCR_CGIF3;
    DMA1_Channel3->CNDTR = (INT32U)sizeof(sCom.RecvBuf); /* Number of data to transfer */
    DMA1_Channel3->CCR |= DMA_CCR3_EN; /* Channel enabled */
   #endif
   
    if (USART3->SR & (USART_SR_NE | USART_SR_FE | USART_SR_PE)) /* Noise/Framing/Parity error is detected */
    {
    }
    else if (sCom.fRecvErr)
    {
      sCom.fRecvErr = 0;
    }
    else
    {
      sCom.fRecv = 1;
    }
   
    INT32U sr = USART3->SR; /* clear sr */
    INT32U dr = USART3->DR; /* clear dr */
  }
}

在收到长度超过sizeof(buf)的帧后,当CNDTR=0时入DMA1中断,重新初始化了DMA1,但是在USART3中断服务中重新开启DMA1 Channel3接收后,CNDTR还是自动减一了。

另外,buf长度可以通过设置得更大,但是这并没有根本的解决"溢出"的问题,
比如:前一帧数据出现了溢出,随后接收长度=sizeof(buf)的帧,任然回发生"溢出"。因为在前面一帧数据结束后,USART3中断重新开启DMA接收,CNDTR已经异常减一了,后面可以接收的最大长度变成了sizeof(buf)-1,即便接收帧长度也变小了

使用特权

评论回复
10
justinlin2015| | 2016-1-26 15:56 | 只看该作者

长见识了

使用特权

评论回复
11
ccw1986| | 2016-1-26 20:33 | 只看该作者
这个buf长度设置成偶数的,地址不能太靠后了

使用特权

评论回复
12
xym341| | 2016-10-19 09:34 | 只看该作者
我也遇到了这个问题 楼主解决了吗?

使用特权

评论回复
13
xym341| | 2016-10-19 15:05 | 只看该作者
本帖最后由 xym341 于 2016-10-19 15:23 编辑

研究跟踪了一下,得出以下结论:
现象:在DMA正常模式下,DMABUF溢出时,串口BUF中会保留溢出的一个字节数据,使用读串口数据不能清除。再次打开DMA功能时DMA会收到这个数据。     
解决:检测到DMA溢出后有两种处理方法:1,重新初始化串口通道-清空串口DR    2,打开dma再关闭一次再打开-浪费一次dma                                                        
其他方案:在合适的应用场景中,可以考虑DMA的半满中断+循环模式  ,防止溢出。

DMA_Cmd(DMA1_Channel3,DISABLE);      // 停止DMA
.....................读数据并处理.....................................
if(0)
{
                USART3_Configuration();    // 重新配置串口
}   
else
{
                DMA_SetCurrDataCounter(DMA1_Channel3,COM3LEN_DMA);// 重启一次dma
                DMA_Cmd(DMA1_Channel3,ENABLE);                                                                                          
                DMA_Cmd(DMA1_Channel3,DISABLE);                                                                                          
}   
DMA_SetCurrDataCounter(DMA1_Channel3,COM3LEN_DMA);   
DMA_Cmd(DMA1_Channel3,ENABLE);     

使用特权

评论回复
14
包弟| | 2020-3-1 22:43 | 只看该作者
大佬!顶顶顶!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

6

帖子

1

粉丝