串口工作在DMA模式下有时接收异常

发布者:数字火花最新更新时间:2016-12-27 来源: eefocus关键字:串口  DMA模式  接收异常 手机看文章 扫描二维码
随时随地手机看文章

1 前言

客户反馈在使用STM32F205的串口工作在DMA模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接收又能100%正常收到数据。

2 复现现象

2.1 问题背景

与客户沟通,客户使用的是STM32F2标准库V1.1.0,串口波特率为1.408Mbps,不经过串口RS232,直接连接主CPU和从MCU(STM32F205)的串口发送和接收引脚,如下图所示: 

图1 


图1


2.2 尝试重现问题

由于客户使用的是主从架构,实验采用两块STM3220G-EVAL评估板来重现现象。一块用来不间断发送串口数据,另一块采用串口DMA进行接收,直接通过杜邦线连接串口PIN脚并共地,不使用评估板上的RS232收发器。接收端使用STM32F2xx_StdPeriph_Examples\ USART\USART_TwoBoards的示例代码。代码片段如下:

int main(void)
{
  ...
  USART_Config();
  ...  while (1)
  {
    /* Clear Buffers */
    Fill_Buffer(RxBuffer, TXBUFFERSIZE);
    Fill_Buffer(CmdBuffer, 2);

    DMA_DeInit(USARTx_RX_DMA_STREAM);
    DMA_InitStructure.DMA_Channel = USARTx_RX_DMA_CHANNEL;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    /************* USART will receive the the transaction data ****************/
    /* Transaction data (length defined by CmdBuffer[1] variable) */       
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;
    DMA_InitStructure.DMA_BufferSize =10;// (uint16_t)CmdBuffer[1];
    DMA_InitStructure.DMA_Mode =DMA_Mode_Normal;//DMA_Mode_Circular;
    DMA_Init(USARTx_RX_DMA_STREAM, &DMA_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 
    /* Enable DMA Stream Transfer Complete interrupt */
    DMA_ITConfig(USARTx_RX_DMA_STREAM, DMA_IT_TE|DMA_IT_DME|DMA_IT_FE, ENABLE);

    /* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);//  USART_Cmd(USARTx, ENABLE);//  while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
//  {
//    Tmp =USART_ReceiveData(USARTx);//  }    while ((DMA_GetFlagStatus(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_TCIF) ==     RESET)
    {
    }   
    /* Clear all DMA Streams flags */
    DMA_ClearFlag(USARTx_RX_DMA_STREAM, USARTx_RX_DMA_FLAG_HTIF | USARTx_RX_DMA_FLAG_TCIF);

    /* Disable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, DISABLE);

    /* Disable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx, DISABLE);//handle the RxBuffer data...
    //...
  }
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657

USART_Config()函数如下:

static void USART_Config(void)
{
  USART_InitTypeDef USART_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Peripheral Clock Enable -------------------------------------------------*/
  /* Enable GPIO clock */
  RCC_AHB1PeriphClockCmd(USARTx_TX_GPIO_CLK | USARTx_RX_GPIO_CLK, ENABLE);

  /* Enable USART clock */
  USARTx_CLK_INIT(USARTx_CLK, ENABLE);

  /* Enable the DMA clock */
  RCC_AHB1PeriphClockCmd(USARTx_DMAx_CLK, ENABLE);

  /* USARTx GPIO configuration -----------------------------------------------*/ 
  /* Connect USART pins to AF7 */
  GPIO_PinAFConfig(USARTx_TX_GPIO_PORT, USARTx_TX_SOURCE, USARTx_TX_AF);
  GPIO_PinAFConfig(USARTx_RX_GPIO_PORT, USARTx_RX_SOURCE, USARTx_RX_AF);

  /* Configure USART Tx and Rx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

  GPIO_InitStructure.GPIO_Pin = USARTx_TX_PIN;
  GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = USARTx_RX_PIN;
  GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStructure);

  /* USARTx configuration ----------------------------------------------------*/
  /* Enable the USART OverSampling by 8 */
  USART_OverSampling8Cmd(USARTx, ENABLE); 

  USART_InitStructure.USART_BaudRate = 1408000;//3750000;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  /* When using Parity the word length must be configured to 9 bits */
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(USARTx, &USART_InitStructure);

  /* Configure DMA controller to manage USART TX and RX DMA request ----------*/  
  DMA_InitStructure.DMA_PeripheralBaseAddr = USARTx_DR_ADDRESS;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  /* Here only the unchanged parameters of the DMA initialization structure are
     configured. During the program operation, the DMA will be configured with 
     different parameters according to the operation phase */

  /* Enable USART */
  USART_Cmd(USARTx, ENABLE);}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364651234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465

按如上代码,有如下现象: 
1. 代码不做修改,若先启动接收端MCU再启动发送端MCU,接收端MCU的串口能正常接收。 
2. 代码不做修改,若先启动发送端MCU再启动接收端MCU,接收端MCU的串口100%接收异常。 
3. 修改发送端代码,改为发送端MCU串口每1秒间隔发送一次,则无论启动顺序如何,接收端MCU的串口都能正常。

3 程序分析

由上述代码可知,程序是先在USART_Config()函数函数内初始化串口并使能,然后再在接下来的main函数的while循环内初始化DMA并使能。这个是标准库内附带的示例代码,咋一看没什么问题,但仔细一想,针对用户的使用场景,这里就会产生一个问题:由于用户的主CPU有可能在从MCU启动之前就已经有可能启动,那么在这种情况下,在初始化完串口并使能后,到DMA使能之前这段时间内,若主CPU向从MCU发送串口数据,从MCU是否能正确接收?

从上述测试代码的结果2可以得出,若在串口初始化并使能后到DMA使能之前有数据来,MCU是不能接收的,经进一步调试,发现此时数据寄存器USART_DR存在一个数据,且在状态寄存器USART_SR中ORE值1,由此可知,串口的接收寄存器中已经接收到一个数据,但是后面的数据又来了,由于数据寄存器中的数据没有及时转移走(此时DMA还没有开启),从而导致后面的数据无法存入,所以产生了上溢错误(ORE),而一旦产生上溢错误后,就无法再触发DAM请求,及时之后再启动DMA也不行,无法触发DMA请求就无法将数据寄存器内的数据及时转移走,如此陷入死锁,这就是串口无法正常接收的原因。这时反观一下代码的结果3,这又将做如何解释?

仔细查看测试结果3,发现这个发送端每1秒间隔发送一次,那么就会存在这个一个概率,这个发送的时间点是否刚好在接收端MCU的串口初始化并使能和DMA使能之间还是之后,这个时间窗口非常关键,如果刚好在时间窗,那么串口接收就不正常,如果在这个时间窗之后,串口接收就能正常。由于测试代码采用的是1秒间隔,对于MCU来说这个是非常大的时间长度,还是很小概率能碰中这个时间窗的,因此,测试结果看起来是都能正常,实际严格来说,还是存在刚好碰中的可能。如果间隔时间缩短,那个碰中的几率就增大。由此看来,这也就能解释测试结果3了,也能解释客户提到的有时正常有时不正常的现象了。

4 问题处理

处理有两种方法,第一种方法是在使能DMA后,及时将数据寄存器DR中的数据清除掉,如下代码所示:

//.../* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);  while(SET ==USART_GetFlagStatus(USARTx,USART_FLAG_ORE))
  {
    Tmp =USART_ReceiveData(USARTx);
  }
  //...123456789101112123456789101112

这里是使用读DR的方法来清除的,从参考手册中也提到使用这种方法来清除ORE标志: 

图2


图2


第一种方法类似于一种纠错措施,下面介绍另一种推荐的方法,如下代码所示:


//.../* Enable the DMA Stream */
    DMA_Cmd(USARTx_RX_DMA_STREAM, ENABLE);
    /* Enable the USART Rx DMA requests */
    USART_DMACmd(USARTx, USART_DMAReq_Rx , ENABLE);

  USART_Cmd(USARTx, ENABLE);
  //...123456789123456789

如上所示,可以先使能DMA再使能串口,这样就彻底不存在那个时间窗了,不管数据何时过来能能被DAM及时转走。这个是推荐的解决方法。

5 结论

标准库中的示例代码一般来说只供参考,对于大部分情况来说都是能正常工作的,但偶尔也会出现不适用的情况,此时更需要我们针对问题进行思考分析,进一步找到原因才能解决问题。对于串口使用DMA来接收的情况,这里建议一定要先使能DMA,最后使能串口,这样就能避免类似问题出现了。


关键字:串口  DMA模式  接收异常 引用地址:串口工作在DMA模式下有时接收异常

上一篇:STM32F030低温下RTC不工作
下一篇:在进行USB CDC类开发时,无法发送64整数倍的数据

推荐阅读最新更新时间:2024-03-16 15:27

用8位单片机实现串口-以太网转换器
    摘要: 用8位单片机8031和ISA总线网卡,实现RS485串行设备与以太网的连接,以传送控制信令和数据文件。程序设计采用C51语言,便于移植和调试。     关键词: 以太网 串行通讯 单片机 C51 单片机或微控制器(MCU)(也称为嵌入式系统)已经在各个领域得到了广泛的应用。目前绝大多数系统都是以MCU为核心,与监测、伺服、指示设备配合实现一定的功能。以太网是当今最受欢迎的局域网之一,现已成为社会重要的基础信息设施,是信息流通的重要渠道。如果嵌入式系统能够连接到Internet,则可以方便、低廉地将信息传送到世界上的任何一个地方。 将嵌入式系统与Internet相连的主要困难在于:Inter
[工业控制]
MSP430 G2553 单片机 串口 测试 调试
MSP430G2553 单片机 只有一个串口,初始化后即可使用。 板子插电脑上有这2个,上面一个用于串口通信,下面一个用于程序下载。 打开串口调试助手即可看到:
[单片机]
MSP430 G2553 单片机 <font color='red'>串口</font> 测试 调试
串口与普通IO口的区别
General Purpose Input Output (通用输入/输出)简称为GPIO,或总线扩展器,人们利用工业标准I2C、SMBus或SPI接口简化了I/O口的扩展。当微控制器或芯片组没有足够的I/O端口,或当系统需要采用远端串行通信或控制时,GPIO产品能够提供额外的控制和监视功能。 每个GPIO端口可通过软件分别配置成输入或输出。Maxim的GPIO产品线包括8端口至28端口的GPIO,提供推挽式输出或漏极开路输出。提供微型3mm x 3mm QFN封装。 串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一
[单片机]
<font color='red'>串口</font>与普通IO口的区别
STM32串口的部分映射与完全映射
以stm32的USART1来举例 默认使用的是PA9为TX,PA10为RX,当需要开启复用功能时,需要注意修改以下几个地方的代码 1 时钟的开启 当使用复用功能后,就需要开启复用时钟RCC_APB2Periph_AFIO 2 初始化引脚的不同,原来的是PA9,PA10,但是现在要初始化PB6,PB7,这一部分就不贴代码了。 3 调用GPIO_PinRemapConfig()函数 GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE); USART1的重映射开启 注意:GPIO_PinRemapConfig()函数的参数是不能随便写的
[单片机]
STM32<font color='red'>串口</font>的部分映射与完全映射
STM32学习之串口的使用
串口的使用 1、为什么要用串口? 自上一篇写的时间是1月20号,今6月7号了,半年没更新了。 这半年发生了什么?过完年就去找公司实习,在那里自我感觉进步很大。其实在公司大多都是自学,师傅基本不会给你说什么。但这并不能说明你的师傅对你不好,带我的那个师傅只比我高一届,但他的水平比我高的好多届。他也是自学,也没人告诉他该怎么做,因为老板也不太懂。所以自学能力很重要,当然有人带你的话,这样会更好。 不说这些了,串口在调试的时候作用非常大。也学我们在学51的时候,只是将程序下载到开发板,看看是否能运行起来,通过数码管将结果显示出来,从而就知道程序设计的正确性。以前我也是这样做的,没什么不好。 在公司实习的时候,他们调试都是使用
[单片机]
STM32学习之<font color='red'>串口</font>的使用
单片机C51串口中断接收和发送测试例程(含通信协议的实现)
  通信协议: 第1字节,MSB为1,为第1字节标志,第2字节,MSB为0,为非第一字节标志,其余类推……,最后一个字节为前几个字节后7位的异或校验和。   测试方法:可以将串口调试助手的发送框写上 95 10 20 25,并选上16进制发送,接收框选上16进制显示,如果每发送一次就接收到95 10 20 25,说明测试成功。 //这是一个单片机C51串口接收(中断)和发送例程,可以用来测试51单片机的中断接收 //和查询发送,另外我觉得发送没有必要用中断,因为程序的开销是一样的 #include #include #define INBUF_LEN 4 //数据长度 unsigned char inbuf1
[单片机]
新唐N76E003+GPRS 串口乱码故障排除手记
设计目的 单片机采用N76E003,GPRS模块采用AIR208,N76E003使用USART0与AIR208通信,使用USART1与另一个设备通信。由于每个设备在服务的唯一性,每个设备编号都应该是唯一的。而GPRS模块的IMEI号正好是唯一的,所以设计思路是读取该IMEI号码并以此作为设备的唯一编号。 读取IMEI号码出现乱码故障 N76E003发送控制指令,读取AIR208的IMEI号码。遂尝试着定位问题。 1、使用串口助手观察,N76E003确实发送了控制命令,且AIR208确实返回了包含MEI号码的返回数据。问题是,单片机无论是单步运行还是全速运行,读取的数据均有丢失。 2、使用串口助手对GPRS模块发送控制命
[单片机]
新唐N76E003+GPRS <font color='red'>串口</font>乱码故障排除手记
详解单片机串口通讯RXD与TXD对接
相信很多人都对单片机与计算机或者芯片通信时,RXD与TXD如何连接比较困惑。因为在一些电路图中,有的是直连接法,有的是交叉接法,让人有点摸不着头脑。 首先需要明白两个概念,就是DTE和DCE。DTE是指数据终端设备,典型的DTE就是计算机和单片机。DCE是指数据通信设备,典型的DCE就是MODEM。RS232串口标准中的RXD和TXD都是站在DTE立场上的,而不是DCE。明白了这一点,再讲下面的接线方法,就很好理解了。 单片机与计算机进行串口通信时,单片机的RXD接计算机的TXD,单片机的TXD接计算机的RXD。 (1)使用串口直通线。设计电路时,单片机的RXD连接电路板DB9的TXD,单片机的TXD连
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved