搜索
bottom↓
回复: 26

usart1实现DMA的发送和接收功能,出现的接收问题

[复制链接]

出0入0汤圆

发表于 2013-3-4 23:43:06 | 显示全部楼层 |阅读模式
小弟刚上手stm32不久,我现在搞了一个用usart1实现了DMA的发送和接收功能。已经成功了,但是我设置的是一次接收32

个字符,如果接收的超过或者少于32个字符,就会出现问题,就无法接收了(我们实现的是用ZigBee无线传输,一次定义32

个字符为一个数据包,但是就怕zigbee的接收的问题,使得DMA无法收到定义好的32个字符,这样就会无法完成后续的任务



我的思路是这样的:
1.先配置USART1的时钟以及相应的GPIO,初始化USART1,使能USART1;
2.0使能DMA时钟
2.1配置DMA发送:
----配置DMA发送中断(DMA1_Channel4)
----DMA发送设置,DMA_BufferSize设为32,都是8bit(DMA_PeripheralDataSize_Byte,DMA_MemoryDataSize_Byte),DMA_Mode为循环模式,然后:
        DMA_Cmd (DMA1_Channel4,ENABLE);       
        DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
2.2配置DMA接收:
----配置DMA接收中断(DMA1_Channel5)
----DMA接收设置,DMA_BufferSize设为32,都是8bit(DMA_PeripheralDataSize_Byte,DMA_MemoryDataSize_Byte),DMA_Mode为循环模式,然后:
        DMA_Cmd (DMA1_Channel5,ENABLE);       
        DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
2.3串口向 DMA发出请求(先让DMA接收,接收到了在发送)
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
3.1在DMA接收中断函数中(void DMA1_Channel5_IRQHandler(void))
----如果接收完成 //if(DMA_GetFlagStatus(DMA1_FLAG_TC5)==SET)
----就先给一个全局变量flag=1(为了在main函数中的循环里识别)
----再关闭串口的DMA接收请求  //USART_DMACmd(USART1, USART_DMAReq_Rx, DISABLE);
----清除标志        //DMA_ClearFlag(DMA1_FLAG_TC5);
3.2在main函数的while循环中 如果发现flag==1,说明接收完成,可以传想要传输的数据了;
----于是,打开串口的DMA发送请求  //USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);       
----同时flag=0
3.3在DMA发送中断函数中(void DMA1_Channel4_IRQHandler(void))
----如果发送完成  //if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
----就关闭串口的DMA发送请求,再打开串口的DMA接收请求:
        USART_DMACmd(USART1, USART_DMAReq_Tx, DISABLE);
        USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
----清除标志        //DMA_ClearFlag(DMA1_FLAG_TC4);
//////////////////////////////////////////////////////////////////////////////////////////////
这就是整个函数,函数实现的很好,接了32个字符(PC给stm32 发送32个字符),马上就发回32个字符,看似已经ok,但是遇到两种蛋疼的情况:

1.pc给stm32 发送的字符少于32个;
2.发送的多于32个;
这样都会导致,stm32无法在继续接收和发送字符了,即使我在向stm32发送正确的32个字符,必须复位才行,这样的话,我们的stm32就无法接收后续的数据了,这怎么办,我想了让它在先关闭串口的DMA请求,在打开,可惜还是不行,请求高人解答。
希望高人谈一下引起这种情况的原因,以及详细的解答方案,最好能有关键性的代码和解释。
谢谢。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2013-3-5 21:06:23 | 显示全部楼层
怎么没有人来看看,唉

出0入0汤圆

 楼主| 发表于 2013-3-5 21:11:58 | 显示全部楼层
怎么没有人来看看,唉

出0入0汤圆

发表于 2013-3-5 23:26:48 | 显示全部楼层
这里有个老帖子,或许对你有帮助,研究下
STM32 UART DMA实现未知数据长度接收
http://www.amobbs.com/thread-5186206-1-1.html

出0入0汤圆

 楼主| 发表于 2013-3-6 17:03:56 | 显示全部楼层
coding2011 发表于 2013-3-5 23:26
这里有个老帖子,或许对你有帮助,研究下
STM32 UART DMA实现未知数据长度接收
http://www.amobbs.com/thre ...

那个帖子很巧妙,但是解决我的问题有点大材小用了,而且也需要改硬件,我的这个核心问题就是,接收的数据少于DMA_BufferSize(我设置的是32个),我用pc给stm32发送小于32的数据,和多于32的数据,stm32都会工作长期处于一种错误的状态。尤其是少发了,stm32完全无法接收了,这里的机理到底是怎么回事。求大侠指导

出0入0汤圆

发表于 2013-3-6 19:36:05 | 显示全部楼层
串口接收长度不定的数据,DMA不好弄,

用串口接收中断呗,STM32的中断响应、处理还是很快的。

出0入0汤圆

 楼主| 发表于 2013-3-6 23:13:19 | 显示全部楼层
erxun 发表于 2013-3-6 19:36
串口接收长度不定的数据,DMA不好弄,

用串口接收中断呗,STM32的中断响应、处理还是很快的。

确实,我也萌生了退意,这太纠结了,不过我还有一路串口要开,不知道stm32会不会吃力,大侠,有没有深入介绍stm32 dma操作的资料,我想研究研究,谢谢啊

出0入0汤圆

发表于 2013-3-7 08:28:23 | 显示全部楼层
固件库,提供的DMA例程,多测试几次,就了然了。

出0入0汤圆

 楼主| 发表于 2013-3-10 00:22:02 | 显示全部楼层
erxun 发表于 2013-3-7 08:28
固件库,提供的DMA例程,多测试几次,就了然了。

谢谢你了

出0入0汤圆

发表于 2013-3-11 08:52:47 | 显示全部楼层
这个当然是那样的了,到了你的设置的数据长度就中断了,要是你多于那么多数据,少于那么多数据中断是会产生的,不过你的操作可能就没有了!数据的结构发生变化了,检测不到你所设计的标志位!当然就会出现你所描述的问题!

出0入0汤圆

 楼主| 发表于 2013-3-11 23:25:29 | 显示全部楼层
TigerRay 发表于 2013-3-11 08:52
这个当然是那样的了,到了你的设置的数据长度就中断了,要是你多于那么多数据,少于那么多数据中断是会产生的, ...

那如何解决呀,您能不能相信阐释一下这个dma深入一点的机制,不过关键是为了防止这种情况,如何解决呢?谢谢您的参与

出0入24汤圆

发表于 2013-3-11 23:36:32 | 显示全部楼层
humanking7 发表于 2013-3-11 23:25
那如何解决呀,您能不能相信阐释一下这个dma深入一点的机制,不过关键是为了防止这种情况,如何解决呢? ...

总线空闲中断

出0入0汤圆

 楼主| 发表于 2013-3-12 23:11:01 | 显示全部楼层
20061002838 发表于 2013-3-11 23:36
总线空闲中断

您说的是检测usart是否空闲,然后做出判断?您有没有合适的代码,一些东西还是不太清楚,有源码更好一点,谢谢

出0入24汤圆

发表于 2013-3-12 23:26:05 | 显示全部楼层
humanking7 发表于 2013-3-12 23:11
您说的是检测usart是否空闲,然后做出判断?您有没有合适的代码,一些东西还是不太清楚,有源码更好一点 ...

这需要什么源码
STM32硬件支持总线空闲中断了

直接使能这个中断(已经设置好接收DMA,且DMA接收长度大于等于实际数据长度);
STM32检测到总线空闲就会进入中断,中断里面读取数据、重新初始化DMA(你要是用大的环形缓冲的话连重新初始化DMA都不用)就行了

好好看看STM32的参考手册吧

出0入0汤圆

 楼主| 发表于 2013-3-13 12:12:28 | 显示全部楼层
20061002838 发表于 2013-3-12 23:26
这需要什么源码
STM32硬件支持总线空闲中断了

谢谢您,其他都好办,就是初始化dma每次都不行,我估计我是对dma的操作认识的不够彻底,就是卡在这里了,希望您能给一个dma初始化的思路(真心不怕被您笑话)

出0入24汤圆

发表于 2013-3-13 12:52:43 | 显示全部楼层
humanking7 发表于 2013-3-13 12:12
谢谢您,其他都好办,就是初始化dma每次都不行,我估计我是对dma的操作认识的不够彻底,就是卡在这里了, ...
  1. #define  UART_RXD_BUF_SIZE 100

  2. static uint8_t UART_ReceiveBuffer[UART_RXD_BUF_SIZE];
  3. static uint8_t UART_RXD_Str[80];

  4. volatile uint32_t Flag_UART_RXD = 0;

  5. static void USART_RXD_DMA_Configuration(void)
  6. {

  7.     DMA_InitTypeDef DMA_InitStructure;

  8.     RCC_AHBPeriphClockCmd(UART_DMA_CLK, ENABLE);

  9.     /* DMA1 channel1 configuration ----------------------------------------------*/
  10.     DMA_DeInit(UART_DMA_RXD);

  11.     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & (UART_NUM->DR);
  12.     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)UART_ReceiveBuffer;
  13.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  14.     DMA_InitStructure.DMA_BufferSize = UART_RXD_BUF_SIZE;
  15.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  16.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  17.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  18.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  19.     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  20.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
  21.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  22.     DMA_Init(UART_DMA_RXD, &DMA_InitStructure);

  23.     DMA_ITConfig(UART_DMA_RXD, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE, DISABLE);

  24.     DMA_Cmd(UART_DMA_RXD, ENABLE);
  25. }
复制代码
初始化DMA的
  1. char TXD_Buffer[200];

  2. void UART_IRQ_Handler(void)
  3. {
  4.     unsigned long len;
  5.     char *str;

  6.     if(UART_NUM->SR & USART_FLAG_IDLE)   //总线空闲中断
  7.     {
  8.         USART_ClearFlag(UART_NUM, USART_FLAG_RXNE);        //清除空闲中断标志位
  9.         len = USART_ReceiveData(UART_NUM);

  10.         len = UART_RXD_BUF_SIZE - DMA_GetCurrDataCounter(UART_DMA_RXD);   //获得接收字符串长度

  11.         memcpy_a(UART_RXD_Str, UART_ReceiveBuffer, len);
  12.         UART_RXD_Str[len] = 0;

  13.         USART_RXD_DMA_Configuration();

  14.         str = User_Cmd(UART_RXD_Str, &len);

  15.         USART_SendStr_DMA(str, len);
  16.     }
  17. }
复制代码
中断

出0入0汤圆

 楼主| 发表于 2013-3-16 03:13:31 | 显示全部楼层
20061002838 发表于 2013-3-13 12:52
初始化DMA的中断

最近比较忙,还没试,我的原程序用中断实现了,谁让不是所想,但是安全可靠,您的这个代码对我以后研究大有裨益,再谢

出0入0汤圆

发表于 2013-3-29 17:27:10 | 显示全部楼层
上面的大侠提供的方法没有做测试吗?效果如何呢!!

出0入0汤圆

发表于 2013-3-29 17:28:01 | 显示全部楼层
humanking7 发表于 2013-3-16 03:13
最近比较忙,还没试,我的原程序用中断实现了,谁让不是所想,但是安全可靠,您的这个代码对我以后研究大 ...

上面的大侠提供的方法你有没有试一下呀,效果如何?

出0入30汤圆

发表于 2013-3-29 17:40:34 | 显示全部楼层
总线空闲中断+DMA很方便啊。打开USART_IDLE中断,关闭RXNE中断,允许UART接收DMA,配置好DMA的源为USART的数据寄存器,大小比你最大一包数据的长度大就可以,然后在uart中断中查询空闲中断标记,读取DMA接收到的数据。

出0入0汤圆

 楼主| 发表于 2013-3-31 00:34:38 | 显示全部楼层
lovelywwei 发表于 2013-3-29 17:40
总线空闲中断+DMA很方便啊。打开USART_IDLE中断,关闭RXNE中断,允许UART接收DMA,配置好DMA的源为USART的数 ...

谢谢,我现在用的是中断在做这个项目,感觉这个安全(项目中用stm32做飞控板与地面数据交互的枢纽,用dma发送还得延时后才能给下一个包赋值,有点怕),闲下来会研究一下,谢谢您

出0入0汤圆

 楼主| 发表于 2013-3-31 00:36:11 | 显示全部楼层
wsfry 发表于 2013-3-29 17:28
上面的大侠提供的方法你有没有试一下呀,效果如何?

还没有呢,这段时间忙的焦头烂额,研究成功了,一定会通知你的,谢谢您的关注

出0入0汤圆

发表于 2013-3-31 16:21:48 | 显示全部楼层
humanking7 发表于 2013-3-31 00:36
还没有呢,这段时间忙的焦头烂额,研究成功了,一定会通知你的,谢谢您的关注 ...

请教一个问题呀:
       我的STM32串口波特率设置的是115200
     串口接受中断中用的是状态机来识别不同的帧头,比如我要识别AA 02 ......或者AA 01.... 我发现一个问题就是:通过电脑串口发送数据的时候,串口返回的数据比较慢,我在51上做的一个状态机,在同样的波特率的条件下,发送串口指令,返回的数据很快,我想知道是不是我的设计思想有问题部分代码如下:
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
      
       {          
                  c = USART_ReceiveData(USART1);
                          switch(recv_state)
                          {
                          
                             case 0:
                                         if(c == 0xaa)
                                                 {
                                                   recv_state=1;

                                                 }
                                                 else
                                                 {
                                                    recv_state=0;
                                                   //return 0
                                                 }
                                         break;
                                 case 1:
                                         if(c == 0x01)
                                                 {
                                                   recv_state=2;
                                                 }
                                                 else if(c == 0x02)
                                                 {
                                                    recv_state=3;
                                                 }
                                                  else
                                                 {
                                                   recv_state=0;
                                                  // return 0
                                                 }
                                         break;
                                 case 2 :
                                                 if(k<3)
                                                 {
                                                   RXBUF1[k++] = USART_ReceiveData(USART1);
                                                 }
                                                 if(k>=3)
                                                 {
                                                    flag_1=1;
                                                    k=0;
                                                        USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//关闭串口1的中断相应函数
                                                 }
                                         break;
                                 case 3 :
                                                 if(k<3)
                                                 {
                                                   RXBUF1[k++] = USART_ReceiveData(USART1);
                                                 }
                                                 if(k>=3)
                                                 {
                                                    flag_2=1;
                                                    k=0;
                                                        USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//关闭串口1的中断相应函数
                                                 }
                                         break;
                       
                          }
                             
       }
在主程序中通过判断标志位 来确定相应的返回指令主程序部分函数如下所示:
if(flag_1 == 1)
           {
              flag_1 = 0;
                  k=0;
             //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//打开串口1的中断相应函数
             if((RXBUF1[0] == 0x00 ) && (RXBUF1[1] == 0x00 ) && (RXBUF1[2] == 0x00) )
                {
                  
                                USART_SendData(USART1,0x03);
                                //while( USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET );
                                USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//打开串口1的中断相应函数       
                       
                }
                else
                {
                     USART_SendData(USART1,0xff);
                        // while( USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET );
                         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//打开串口1的中断相应函数
                }
                 
           }
          
           if(flag_2 == 1)
           {
              flag_2 = 0;
                  k=0;
             //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//打开串口1的中断相应函数
             if((RXBUF1[0] == 0x00 ) && (RXBUF1[1] == 0x00 )&&(RXBUF1[2] == 0x00 ) )
                {
                  
                                USART_SendData(USART1,0x04);
                                while( USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET );
                                USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//打开串口1的中断相应函数       
                       
                }
                else
                {
                     USART_SendData(USART1,0xff);
                         while( USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET );
                         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//打开串口1的中断相应函数
                }
                 
           }
可以正常的通讯 但是发现复位后发送第一个 AA 02 00 00 00的时候正常返回,然后马上再点击发送,返回就很慢,好像有延时,是不是我设置的有问题呀!求解中 谢谢,这种思想 我以前在51中用过反应很快的,AVR中也用过,反应也很快!请问是怎么回事???

出0入4汤圆

发表于 2013-4-7 14:38:08 | 显示全部楼层
lovelywwei 发表于 2013-3-29 17:40
总线空闲中断+DMA很方便啊。打开USART_IDLE中断,关闭RXNE中断,允许UART接收DMA,配置好DMA的源为USART的数 ...

这个方法非常棒!
对最后一句进行再一次解释一下:在UART中断中查询空闲中断标志,方便其他人理解。  UART中断是由多种中断(发送中断,接收中断,空中断,空闲中断等9个)共用一个入口地址。

出0入4汤圆

发表于 2013-4-7 14:42:39 | 显示全部楼层
补充:空闲中断,平时不会一直产生中断。只有当接收过字符后,然后一直接收直到没有再接收到数据,处于空闲状态,才会产生中断。

出0入0汤圆

发表于 2013-9-3 20:57:27 | 显示全部楼层
tarzar 发表于 2013-4-7 14:42
补充:空闲中断,平时不会一直产生中断。只有当接收过字符后,然后一直接收直到没有再接收到数据,处于空闲 ...

不错,这个给力,很有用!

出0入4汤圆

发表于 2017-1-23 21:15:39 | 显示全部楼层
很好的思路,谢谢。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 19:47

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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