搜索
bottom↓
回复: 106

STM32串口DMA超时接收方法,可大大节约CPU时间

  [复制链接]

出0入0汤圆

发表于 2013-6-24 14:55:09 | 显示全部楼层 |阅读模式
本办法使用定时器定时查询DMA接收到的数据,如果超过设定的周期则认为本次数据包结束,将数据拷贝到缓冲区,交由其他程序处理。可以接收任意大小的数据包,尤其适用于MODBUS等协议,曾经用于GPS、GPRS等接收,很实用。本方法占用CPU时间极少,尤其是波特率很高时,效果更加明显。
当某一个串口的数据接收超时以后,定时器中断中将数据拷贝到缓冲区,在主程序中可以判断数据标志UART1_Flag,大于0的时候即代表有数据接收到,可以处理,处理完后将此变量清零即可。
两个数据包间隔较小时,可以将定时器的周期调短些。

//超时时间定义
#define        UART1_TimeoutComp 2  //20ms
#define        UART2_TimeoutComp 10  //100ms
#define        UART3_TimeoutComp 10  //100ms

#define SRC_USART1_DR (&(USART1->DR)) //串口接收寄存器作为源头
#define SRC_USART2_DR (&(USART2->DR)) //串口接收寄存器作为源头
#define SRC_USART3_DR (&(USART3->DR)) //串口接收寄存器作为源头


extern u16 UART1_Flag,UART2_Flag,UART3_Flag;
extern u8 uart1_data[200],uart3_data[500],uart2_data[500];

u8 UART1_Timeout,UART2_Timeout,UART3_Timeout;
u16 UART1_FlagTemp,UART2_FlagTemp,UART3_FlagTemp;
u8 uart1_data_temp[200],uart2_data_temp[500],uart3_data_temp[500];

u16 uart1_Flag_last=0,uart2_Flag_last=0,uart3_Flag_last=0;

//定时器初始化
void TimerInit(void)
{
   //定时器初始化数据结构定义
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
   //初始化定时器,用于超时接收,20ms

        //复位计数器
        TIM_DeInit(TIM2);                                                       

        TIM_TimeBaseStructure.TIM_Period           = 100;                //计数上限,100*100us = 10000us = 10ms
        TIM_TimeBaseStructure.TIM_Prescaler        = 4799;        //预分频4800,48MHz主频,分频后时钟周期100us
        TIM_TimeBaseStructure.TIM_ClockDivision    = TIM_CKD_DIV1;  //不分频
        TIM_TimeBaseStructure.TIM_CounterMode      = TIM_CounterMode_Up;  //向上计数
        TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
        //初始化
        TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

        //清中断
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);


        //使能定时器中断
        TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
       TIM_UpdateDisableConfig(TIM2,DISABLE);
        //定时器清零
        TIM_SetCounter(TIM2,0);
        //定时器启动       
      TIM_Cmd(TIM2,ENABLE);       
}


//DMA初始化,只列出一个通道,其他两个通道相同
void DMA5_Init(void)
{
  DMA_InitTypeDef DMA_InitStructure;

  DMA_DeInit(DMA1_Channel5); //将DMA的通道1寄存器重设为缺省值
  DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_USART1_DR; //源头BUF既是 (&(USART1->DR))
  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)uart1_data_temp; //目标BUF 既是要写在哪个个数组之中
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是作为数据传输的目的地还是来源
  DMA_InitStructure.DMA_BufferSize = 200; //DMA缓存的大小 单位在下边设定
  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_PeripheralDataSize_Byte; //内存字节为单位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先)VeryHigh/High/Medium/Low
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存
  DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); //DMA5传输完成中断
  USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //使能USART1的接收DMA请求

  DMA_Cmd(DMA1_Channel5, ENABLE); //正式允许DMA        
}

//串口初始化,只列出一个通道,其他两个通道相同       
void USART1_Configuration(void)
{
    //串口初始化数据结构定义
        USART_InitTypeDef USART_InitStructure;

        //初始化串口为38400,n,8,1
        USART_InitStructure.USART_BaudRate            = 38400  ;
        USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits            = USART_StopBits_1;
        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(USART1, &USART_InitStructure);
       
        //启动串口,不需要接收中断
        USART_Cmd(USART1, ENABLE);
       
        //默认设置为输入状态   
        DMA5_Init();
}

//定时器中断服务程序
void TIM2_IRQHandler(void)
{
  u16 i;   
  //清定时器中断
  TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);

  UART1_Timeout++;
  UART2_Timeout++;
  UART3_Timeout++;
//------------------------------------------------------------------
  i=DMA_GetCurrDataCounter(DMA1_Channel5);
  DMA_ClearITPendingBit(DMA1_IT_GL5); //清除全部中断标志

  if(i!=uart1_Flag_last)  //未完成传输
  {
          UART1_Timeout=0;
        uart1_Flag_last=i;
  }
  else
  {
    if(UART1_Timeout>UART1_TimeoutComp)  //产生超时
        {
           if(i<200) //有数据接收到
           {
                  UART1_FlagTemp=200-i;      //得到接收到的字节数
              
                for(i=0;i<UART1_FlagTemp;i++)  //将数据拷贝到缓冲区
                 uart1_data[i]=uart1_data_temp[i];
                UART1_Flag=UART1_FlagTemp;
               
                DMA_ClearFlag(DMA1_FLAG_TC5);
                DMA_Cmd(DMA1_Channel5, DISABLE); //正式允许DMA                
                DMA5_Init();                 
           }
           UART1_Timeout=0;
        }
  }
  //------------------------------------------------------------------
  i=DMA_GetCurrDataCounter(DMA1_Channel6);
  DMA_ClearITPendingBit(DMA1_IT_GL6); //清除全部中断标志

  if(i!=uart2_Flag_last)  //未完成传输
  {
          UART2_Timeout=0;
        uart2_Flag_last=i;
  }
  else
  {
    if(UART2_Timeout>UART2_TimeoutComp)  //产生超时
        {
           if(i<500) //有数据接收到
           {
                  UART2_FlagTemp=500-i;  //得到接收到的字节数

                for(i=0;i<UART2_FlagTemp;i++)  //将数据拷贝到缓冲区
                 uart2_data[i]=uart2_data_temp[i];
                UART2_Flag=UART2_FlagTemp;
               
                DMA_ClearFlag(DMA1_FLAG_TC6);
                DMA_Cmd(DMA1_Channel6, DISABLE); //正式允许DMA                
                DMA6_Init();
                               
           }
           UART2_Timeout=0;
        }
  }
  //------------------------------------------------------------------
  i=DMA_GetCurrDataCounter(DMA1_Channel3);
  DMA_ClearITPendingBit(DMA1_IT_GL3); //清除全部中断标志

  if(i!=uart3_Flag_last)  //未完成传输
  {
          UART3_Timeout=0;
        uart3_Flag_last=i;
  }
  else
  {
    if(UART3_Timeout>UART3_TimeoutComp)  //产生超时
        {
           if(i<500) //有数据接收到
           {
                  UART3_FlagTemp=500-i;  //得到接收到的字节数

                for(i=0;i<UART3_FlagTemp;i++)  //将数据拷贝到缓冲区
                 uart3_data[i]=uart3_data_temp[i];
                UART3_Flag=UART3_FlagTemp;
               
                DMA_ClearFlag(DMA1_FLAG_TC3);
                DMA_Cmd(DMA1_Channel3, DISABLE); //正式允许DMA                
                DMA3_Init();  
               
           }
           UART3_Timeout=0;
        }
  }  
}

出0入0汤圆

发表于 2013-6-24 15:28:09 | 显示全部楼层
和我的想法不谋而合,只是不想改了,哈哈!

出0入0汤圆

发表于 2013-6-24 15:39:00 | 显示全部楼层
顶一下,学习了!

出0入0汤圆

发表于 2013-6-24 15:51:50 | 显示全部楼层
学习了

出0入0汤圆

发表于 2013-6-24 15:57:11 | 显示全部楼层
谢谢楼主分享,学习了

出0入0汤圆

发表于 2013-6-24 19:40:28 | 显示全部楼层
哈,我就是这么干的,不过我没用定时器,用到是串口总线空闲中断,数据处理方式和楼主一样,在主循环的数据处理函数中处理。

出0入0汤圆

发表于 2013-6-24 19:44:54 | 显示全部楼层
串口总线空闲中断,就怕串口在接收数据帧时,中间有两个相连字节间隔超过一个字节的传输时间,那就惨了。如果发送设备也用DMA发送的,那就没有问题了。

出0入0汤圆

发表于 2013-6-24 20:08:42 | 显示全部楼层
串口总线中断后,不能认为1帧数据接收完成,只表示可以读取DMA中接收到的数据,将DMA中断数据读取出来放到接收缓冲中,置标志位及数据个数后,在主程序中继续处理这几个数据,跟1帧数据分几次发没有太大关系。

出0入0汤圆

发表于 2013-6-24 20:44:42 | 显示全部楼层
空闲中断有问题啊,和过去的老设备连接时,baudrate大于1M时就死翘翘了,搞了一周也不行,最后还是改回来了,哎!!!高科技用不上啊!

出0入0汤圆

发表于 2013-6-24 21:38:27 | 显示全部楼层
空闲中断是指串口在接收数据时,数据字节与字节之间的间隔不能大于一个字节的传输时间,如果这个间隔大于一个字节的传输时间,就产生一个串口空闲中断.如果两个设备用DMA传输数据时,就可以用串口空闲中断来描述一个数据帧如果不是DMA传输,那就可能产生一些意想不到的后果。

出0入0汤圆

发表于 2013-6-24 22:10:36 | 显示全部楼层
程序有问题吧? 如果DMA没收到数据,
i=DMA_GetCurrDataCounter(DMA1_Channel5);

i = 0,一直进入下面的数据拷贝程序啊

出0入0汤圆

发表于 2013-6-25 08:50:56 | 显示全部楼层
喜欢用DMA的

出0入0汤圆

 楼主| 发表于 2013-6-25 09:00:40 | 显示全部楼层
shpan_111 发表于 2013-6-24 22:10
程序有问题吧? 如果DMA没收到数据,
i=DMA_GetCurrDataCounter(DMA1_Channel5);

如果没有收到数据,i是DMA初始化时候的数值,例如UART1,没有接收到数据的时候,i读出值是DMA5初始化时候设置的200,有数据接受到后,这个数会变小,一直到0溢出,所以缓冲区设置的应大于最大数据接收量

出0入0汤圆

发表于 2013-6-25 09:58:09 | 显示全部楼层
我怎么不太理解呢,DMA不是本来就会在接收完成后自动将接收到的数据放到指定的内存中吗?那为什么还需要这样处理一下呢?

出0入0汤圆

发表于 2013-9-7 21:01:39 | 显示全部楼层
最近想测试下。

出0入0汤圆

发表于 2013-9-9 09:01:20 | 显示全部楼层
学习一下

出0入0汤圆

发表于 2013-9-10 08:41:56 | 显示全部楼层
uart DMA mark

出0入0汤圆

发表于 2013-9-11 09:52:36 | 显示全部楼层
请问不需要配置NVIC吗?

出0入0汤圆

发表于 2013-9-12 13:18:17 | 显示全部楼层
定时器什么时候开启呢?

出0入0汤圆

发表于 2013-9-12 13:48:48 | 显示全部楼层
STM32F051的串口本身就有超时中断功能

出0入0汤圆

发表于 2013-9-13 22:16:12 | 显示全部楼层
这方法好,

出0入0汤圆

发表于 2013-9-17 23:15:14 | 显示全部楼层
LZ你的程序如何满足 当一个数据包的长度大于200 时的情况呢?

假设在usart1 上,以10ms的速度,发送一个300byte的包,

因为 DMA_Mode_Circular; //工作在循环缓存模式,所以,在接收满200byte数据后,剩下的100byte数据会覆盖之前的数据。

10ms的发送速度,不会触发超时判断。

那么主函数接收到的就是一个不完整的包。请问你是怎么处理的?

出0入0汤圆

发表于 2013-10-5 10:34:37 | 显示全部楼层
想法很好   

出0入0汤圆

发表于 2013-11-20 12:08:54 来自手机 | 显示全部楼层
不错啊,学习了

出0入0汤圆

发表于 2013-11-20 21:44:50 来自手机 | 显示全部楼层
谢谢楼主

出0入0汤圆

发表于 2013-11-20 22:05:35 | 显示全部楼层
跟这个方法一样吧?
http://www.amobbs.com/forum.php?mod=viewthread&tid=5486343

出0入0汤圆

发表于 2013-11-25 17:12:29 | 显示全部楼层
学习了。。。。。。。。。。。。

出0入0汤圆

发表于 2013-11-25 17:13:07 | 显示全部楼层
学习了。。。。。。。。。。。。

出0入0汤圆

发表于 2013-11-26 09:35:59 | 显示全部楼层
赞一个,感谢楼主,分享好的思路!

出0入17汤圆

发表于 2013-11-26 09:41:28 | 显示全部楼层
我就是这么干的!

出0入0汤圆

发表于 2013-11-26 10:07:05 | 显示全部楼层
最近才剛用好USART 學習了
看起來是進階的

出0入0汤圆

发表于 2013-11-26 11:00:48 | 显示全部楼层
mark 好东西

出0入0汤圆

发表于 2013-11-28 13:29:54 | 显示全部楼层
思路很好,学习了,用一下

出0入0汤圆

发表于 2013-11-29 17:43:59 | 显示全部楼层
学习了,谢谢楼住

出0入0汤圆

发表于 2013-12-3 09:25:41 | 显示全部楼层
MARK,非常实用,学习了

出0入0汤圆

发表于 2013-12-3 10:35:10 | 显示全部楼层
收藏了!!!!

出0入0汤圆

发表于 2014-1-5 11:17:02 | 显示全部楼层
mark   整在为串口犯愁呢

出0入0汤圆

发表于 2014-1-5 16:19:06 | 显示全部楼层
本帖最后由 虫虫好 于 2014-1-5 16:33 编辑

i=DMA_GetCurrDataCounter(DMA1_Channel6);
  DMA_ClearITPendingBit(DMA1_IT_GL6); //清除全部中断标志

  if(i!=uart2_Flag_last)  //未完成传输
  {
          UART2_Timeout=0;
        uart2_Flag_last=i;
  }
else()//超时判断

楼主 请教个问题 如果我收到小于200个数的话 满足 if(i!=uart2_Flag_last)   条件语句 执行后面2句话  由于  UART2_Timeout 清零 我要在等30ms 才能满足超时读取的条件  这样的话DMA  也不知道我理解的对不对  如果我理解正确的话  对于控制来说 周期变长了  还有什么优势呢  不太理解

出0入0汤圆

发表于 2014-1-6 16:08:31 | 显示全部楼层
不错的想法,可以试试。

出0入0汤圆

 楼主| 发表于 2014-1-6 20:18:24 | 显示全部楼层
虫虫好 发表于 2014-1-5 16:19
i=DMA_GetCurrDataCounter(DMA1_Channel6);
  DMA_ClearITPendingBit(DMA1_IT_GL6); //清除全部中断标志

这就是超时接收,意思就是超过一定时间收不到数据,则认为本次数据接收结束,所以就是数据结束后,必须达到设定的超时时间,才会进行数据处理程序。

这适用于比如GPS,GPRS等不定长数据包的情况,程序中的200个字节,是按照最长的数据量来定的,最长500字节,那就定为600字节,防止溢出。

如果嫌数据接收结束后等待时间长,完全可以减小超时的设定值,比如波特率9600,那传输一个字节需要大约1ms,那么定时器可以设定为周期2毫秒,超时时间为2,这样就是2*2=4ms后开始处理数据,我觉得平常应用完全够了,要做到数据结束立刻处理,那就不能用超时接收了,只能判断数据结尾字符来处理。

出0入0汤圆

发表于 2014-1-13 21:31:20 | 显示全部楼层
mark,以后说不定有用!

出0入0汤圆

发表于 2014-1-13 23:57:30 来自手机 | 显示全部楼层
白天再看

出0入0汤圆

发表于 2014-1-14 00:04:54 | 显示全部楼层
正在做这方面的工作,谢谢了。。。。。。

出0入0汤圆

发表于 2014-2-15 16:15:04 | 显示全部楼层
tdh03z 发表于 2013-6-24 19:40
哈,我就是这么干的,不过我没用定时器,用到是串口总线空闲中断,数据处理方式和楼主一样,在主循环的数据 ...


兄台你好 我用你所说的空闲中断在stm32f103上 实现了usart3的不定帧长数据收发 但是代码移植到 usart2是 去不能始终进不去 串口空闲中断了  求指教
详细描述在这个链接中http://www.amobbs.com/thread-5569296-1-1.html

出0入0汤圆

发表于 2014-2-15 21:10:59 | 显示全部楼层
学习一下。。。。。

出0入0汤圆

发表于 2014-2-15 22:13:28 | 显示全部楼层
也这样做过,STM32串口DMA超时接收方法,可大大节约CPU时间

出0入0汤圆

发表于 2014-2-15 23:03:21 | 显示全部楼层
标记先!

出0入0汤圆

发表于 2014-2-24 11:55:41 | 显示全部楼层
思路比较容易理解,但是中断里面做这么多事,还调用DMA5_Init(),非常不靠谱的做法!

出0入0汤圆

发表于 2014-2-24 14:36:44 | 显示全部楼层
perfugee 发表于 2013-9-17 23:15
LZ你的程序如何满足 当一个数据包的长度大于200 时的情况呢?

假设在usart1 上,以10ms的速度,发送一个30 ...

这个是字符间隔和包的大小是无关的, 比如波特率为9600时, 发送一个字节完整的时间约等于1ms, 这样如果是同一个包的数据, 再收到一个字节后, 1ms后会再收到一个字节, 如果超过1ms(通常会用3-5个字节的时间作为超时时间)之后没有收到字符, 则认为一个完整包接收完成.

你的假设10ms发送300字节的包, 波特率至少要大于 30000*8= 240 000bit/s 才行, 要不就是前一个包还没发完就发后一个包了

出0入0汤圆

发表于 2014-2-24 17:07:11 | 显示全部楼层
顶一下   学习啦

出10入12汤圆

发表于 2014-3-7 09:55:25 | 显示全部楼层
学习学习

出0入0汤圆

发表于 2014-3-13 23:50:48 来自手机 | 显示全部楼层
好想法,学习了

出0入0汤圆

发表于 2014-3-15 11:07:36 | 显示全部楼层
非常有用,谢谢

出0入0汤圆

发表于 2014-5-15 11:51:19 | 显示全部楼层
把lZ的移植过来可以用。

出0入0汤圆

发表于 2014-5-15 11:57:23 | 显示全部楼层
DMA还没用过呢!!

出0入0汤圆

发表于 2014-5-15 12:05:33 | 显示全部楼层
楼主,如果发送方堵塞了,导致一帧数据间间隔过长,那不是这帧数据就丢掉了?

出0入0汤圆

发表于 2014-5-15 14:43:50 | 显示全部楼层
mark, stm32 uart dma

出0入0汤圆

发表于 2014-5-16 22:17:15 | 显示全部楼层
好办法,值得研究

出0入0汤圆

发表于 2014-6-24 12:20:15 | 显示全部楼层
我没用DMA接收串口数据。STM32F103的串口只能缓存一字节吗?要是能多缓存几字节,一般情况下就用不着DMA了。

出0入21汤圆

发表于 2014-6-24 12:53:15 | 显示全部楼层
MARK!!!!!!!!!!!!!!!!!!!!!!

出0入0汤圆

发表于 2014-7-17 22:46:10 | 显示全部楼层
不错 有没有更好的dma处理呢

出0入0汤圆

发表于 2014-7-20 11:53:36 | 显示全部楼层
研究研究

出0入0汤圆

发表于 2014-7-21 10:19:13 | 显示全部楼层
学习 ,谢谢分享

出0入0汤圆

发表于 2014-8-2 17:42:56 | 显示全部楼层
好东西,谢谢分享!

出215入169汤圆

发表于 2014-8-2 20:51:09 | 显示全部楼层
本帖最后由 monkeynav 于 2014-8-2 20:59 编辑

实际移植没办法成功,无法进入中断函数,是有bug吗?仔细看是不是缺了NVIC  DMA发送中断设置 ?

出215入169汤圆

发表于 2014-8-2 21:00:27 | 显示全部楼层
f8023m 发表于 2014-5-15 11:51
把lZ的移植过来可以用。

请问你的移植是怎么样的?方便发给我参考下吗?

出215入169汤圆

发表于 2014-8-3 11:51:42 | 显示全部楼层
成功调通,说实话楼主的代码有点坑新手。居然没有使能DMA和定时器时钟,调了好久才发现。发帖要谨慎。。

出0入0汤圆

发表于 2014-10-9 16:24:28 | 显示全部楼层
只需要,O(∩_∩)O谢谢!!

出0入0汤圆

发表于 2014-10-9 17:20:16 | 显示全部楼层
略看一下,代码注释挺全的~~有空测试一下

出0入0汤圆

发表于 2014-10-13 22:35:07 | 显示全部楼层
  USART  DAM

出0入0汤圆

发表于 2014-10-14 06:37:32 | 显示全部楼层
不错,DMA串口,记下了

出0入0汤圆

发表于 2014-10-14 09:12:49 | 显示全部楼层
不错的思路,收藏了

出0入0汤圆

发表于 2014-10-14 10:44:54 | 显示全部楼层
MARK!!!!!!!!!!!!!!

出0入0汤圆

发表于 2014-11-10 21:45:41 来自手机 | 显示全部楼层
学习了  回头有空试试   谢谢分享

出0入0汤圆

发表于 2014-11-10 23:37:08 来自手机 | 显示全部楼层
Mark usart dma

出0入0汤圆

发表于 2014-12-14 18:02:04 | 显示全部楼层
方法不错!改天试试

出0入0汤圆

发表于 2014-12-14 20:16:13 | 显示全部楼层
以前是定时器加串口中断配合,尝试一下DMA方式正用得上

出0入8汤圆

发表于 2014-12-23 16:56:49 | 显示全部楼层
这个不错,DMA第一次尝试,期待不被虐~

出0入0汤圆

发表于 2014-12-23 17:41:15 | 显示全部楼层
不错,利用dma解放cpu

出0入0汤圆

发表于 2014-12-23 22:25:25 | 显示全部楼层
学习了,谢谢楼主分享

出0入0汤圆

发表于 2014-12-23 23:42:15 | 显示全部楼层
D尝试用一下DMA,希望能用会

出0入270汤圆

发表于 2014-12-23 23:50:03 | 显示全部楼层
好东西收藏一下

出0入0汤圆

发表于 2014-12-24 09:48:51 | 显示全部楼层
xuexixia,学习下

出5入42汤圆

发表于 2014-12-24 09:58:46 | 显示全部楼层
串口的DMA还没用过,只用过ADC的DMA。学习。

出0入0汤圆

发表于 2015-1-4 22:02:11 | 显示全部楼层
研究研究。。。。

出0入0汤圆

发表于 2015-1-4 22:05:07 | 显示全部楼层

出0入0汤圆

发表于 2015-1-21 10:30:02 | 显示全部楼层
恩 试用了一下,很不错的方法

出0入10汤圆

发表于 2015-3-18 08:28:35 | 显示全部楼层
楼主能说下采用这种方式和采用串口空闲中断的优缺点吗?

出0入0汤圆

发表于 2015-8-26 09:43:52 | 显示全部楼层
刚刚接触到,学习一下~

出0入0汤圆

发表于 2015-8-26 09:59:00 | 显示全部楼层
感觉DMA用起来好像有问题~

出0入0汤圆

发表于 2015-9-24 09:59:56 | 显示全部楼层
看到还有空闲中断的处理方法

出0入0汤圆

发表于 2015-9-24 10:04:43 | 显示全部楼层
好东西,收藏用用

出0入0汤圆

发表于 2016-4-24 01:45:45 | 显示全部楼层
正好能用下,感谢楼主

出0入0汤圆

发表于 2016-4-24 09:08:02 | 显示全部楼层
没有问题的,很好用,我现在串口通讯就用DMA+空闲中断来收发

出0入0汤圆

发表于 2016-4-24 09:32:28 | 显示全部楼层
我现在纠结在两个STM32通过SPI DMA双向通信怎么做,目前测试收发数据混乱

出0入0汤圆

发表于 2016-8-8 10:41:13 | 显示全部楼层
打开DMA传输完成中断的作用是什么呢。

出0入0汤圆

发表于 2016-8-14 21:38:32 | 显示全部楼层
本帖最后由 shangdawei 于 2016-8-14 21:39 编辑

很不错,学习了。顺便格式化一下看看:

  1. //超时时间定义
  2. #define UART1_TimeoutComp 2  //20ms
  3. #define UART2_TimeoutComp 10  //100ms
  4. #define UART3_TimeoutComp 10  //100ms

  5. #define SRC_USART1_DR (&(USART1->DR)) //串口接收寄存器作为源头
  6. #define SRC_USART2_DR (&(USART2->DR)) //串口接收寄存器作为源头
  7. #define SRC_USART3_DR (&(USART3->DR)) //串口接收寄存器作为源头


  8. extern u16 UART1_Flag, UART2_Flag, UART3_Flag;
  9. extern u8 uart1_data[ 200 ], uart3_data[ 500 ], uart2_data[ 500 ];

  10. u8 UART1_Timeout, UART2_Timeout, UART3_Timeout;
  11. u16 UART1_FlagTemp, UART2_FlagTemp, UART3_FlagTemp;
  12. u8 uart1_data_temp[ 200 ], uart2_data_temp[ 500 ], uart3_data_temp[ 500 ];

  13. u16 uart1_Flag_last = 0, uart2_Flag_last = 0, uart3_Flag_last = 0;

  14. //定时器初始化
  15. void TimerInit( void )
  16. {
  17.   //定时器初始化数据结构定义
  18.   TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  19.   //初始化定时器,用于超时接收,20ms

  20.   //复位计数器
  21.   TIM_DeInit( TIM2 );

  22.   TIM_TimeBaseStructure.TIM_Period = 100; //计数上限,100*100us = 10000us = 10ms
  23.   TIM_TimeBaseStructure.TIM_Prescaler = 4799; //预分频4800,48MHz主频,分频后时钟周期100us
  24.   TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //不分频
  25.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
  26.   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  27.   //初始化
  28.   TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure );

  29.   //清中断
  30.   TIM_ClearFlag( TIM2, TIM_FLAG_Update );


  31.   //使能定时器中断
  32.   TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE );
  33.   TIM_UpdateDisableConfig( TIM2, DISABLE );
  34.   //定时器清零
  35.   TIM_SetCounter( TIM2, 0 );
  36.   //定时器启动        
  37.   TIM_Cmd( TIM2, ENABLE );
  38. }


  39. //DMA初始化,只列出一个通道,其他两个通道相同
  40. void DMA5_Init( void )
  41. {
  42.   DMA_InitTypeDef DMA_InitStructure;

  43.   DMA_DeInit( DMA1_Channel5 ); //将DMA的通道1寄存器重设为缺省值
  44.   DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )SRC_USART1_DR; //源头BUF既是 (&(USART1->DR))
  45.   DMA_InitStructure.DMA_MemoryBaseAddr = ( u32 )uart1_data_temp; //目标BUF 既是要写在哪个个数组之中
  46.   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是作为数据传输的目的地还是来源
  47.   DMA_InitStructure.DMA_BufferSize = 200; //DMA缓存的大小 单位在下边设定
  48.   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增
  49.   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
  50.   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位
  51.   DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位
  52.   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
  53.   DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先)VeryHigh/High/Medium/Low
  54.   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存
  55.   DMA_Init( DMA1_Channel5, &DMA_InitStructure ); //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
  56.   DMA_ITConfig( DMA1_Channel5, DMA_IT_TC, ENABLE ); //DMA5传输完成中断
  57.   USART_DMACmd( USART1, USART_DMAReq_Rx, ENABLE ); //使能USART1的接收DMA请求

  58.   DMA_Cmd( DMA1_Channel5, ENABLE ); //正式允许DMA         
  59. }

  60. //串口初始化,只列出一个通道,其他两个通道相同        
  61. void USART1_Configuration( void )
  62. {
  63.   //串口初始化数据结构定义
  64.   USART_InitTypeDef USART_InitStructure;

  65.   //初始化串口为38400,n,8,1
  66.   USART_InitStructure.USART_BaudRate = 38400;
  67.   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  68.   USART_InitStructure.USART_StopBits = USART_StopBits_1;
  69.   USART_InitStructure.USART_Parity = USART_Parity_No;
  70.   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  71.   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  72.   //初始化
  73.   USART_Init( USART1, &USART_InitStructure );

  74.   //启动串口,不需要接收中断
  75.   USART_Cmd( USART1, ENABLE );

  76.   //默认设置为输入状态   
  77.   DMA5_Init();
  78. }

  79. //定时器中断服务程序
  80. void TIM2_IRQHandler( void )
  81. {
  82.   u16 i;
  83.   //清定时器中断
  84.   TIM_ClearITPendingBit( TIM2, TIM_FLAG_Update );

  85.   UART1_Timeout++;
  86.   UART2_Timeout++;
  87.   UART3_Timeout++;
  88.   //------------------------------------------------------------------
  89.   i = DMA_GetCurrDataCounter( DMA1_Channel5 );
  90.   DMA_ClearITPendingBit( DMA1_IT_GL5 ); //清除全部中断标志

  91.   if ( i != uart1_Flag_last )
  92.   //未完成传输
  93.   {
  94.     UART1_Timeout = 0;
  95.     uart1_Flag_last = i;
  96.   }
  97.   else
  98.   {
  99.     if ( UART1_Timeout > UART1_TimeoutComp )
  100.     //产生超时
  101.     {
  102.       if ( i < 200 )
  103.       //有数据接收到
  104.       {
  105.         UART1_FlagTemp = 200-i; //得到接收到的字节数

  106.         for ( i = 0; i < UART1_FlagTemp; i++ )
  107.         //将数据拷贝到缓冲区
  108.           uart1_data[ i ] = uart1_data_temp[ i ];
  109.         UART1_Flag = UART1_FlagTemp;

  110.         DMA_ClearFlag( DMA1_FLAG_TC5 );
  111.         DMA_Cmd( DMA1_Channel5, DISABLE ); //正式允许DMA                 
  112.         DMA5_Init();
  113.       }
  114.       UART1_Timeout = 0;
  115.     }
  116.   }
  117.   //------------------------------------------------------------------
  118.   i = DMA_GetCurrDataCounter( DMA1_Channel6 );
  119.   DMA_ClearITPendingBit( DMA1_IT_GL6 ); //清除全部中断标志

  120.   if ( i != uart2_Flag_last )
  121.   //未完成传输
  122.   {
  123.     UART2_Timeout = 0;
  124.     uart2_Flag_last = i;
  125.   }
  126.   else
  127.   {
  128.     if ( UART2_Timeout > UART2_TimeoutComp )
  129.     //产生超时
  130.     {
  131.       if ( i < 500 )
  132.       //有数据接收到
  133.       {
  134.         UART2_FlagTemp = 500-i; //得到接收到的字节数

  135.         for ( i = 0; i < UART2_FlagTemp; i++ )
  136.         //将数据拷贝到缓冲区
  137.           uart2_data[ i ] = uart2_data_temp[ i ];
  138.         UART2_Flag = UART2_FlagTemp;

  139.         DMA_ClearFlag( DMA1_FLAG_TC6 );
  140.         DMA_Cmd( DMA1_Channel6, DISABLE ); //正式允许DMA                 
  141.         DMA6_Init();

  142.       }
  143.       UART2_Timeout = 0;
  144.     }
  145.   }
  146.   //------------------------------------------------------------------
  147.   i = DMA_GetCurrDataCounter( DMA1_Channel3 );
  148.   DMA_ClearITPendingBit( DMA1_IT_GL3 ); //清除全部中断标志

  149.   if ( i != uart3_Flag_last )
  150.   //未完成传输
  151.   {
  152.     UART3_Timeout = 0;
  153.     uart3_Flag_last = i;
  154.   }
  155.   else
  156.   {
  157.     if ( UART3_Timeout > UART3_TimeoutComp )
  158.     //产生超时
  159.     {
  160.       if ( i < 500 )
  161.       //有数据接收到
  162.       {
  163.         UART3_FlagTemp = 500-i; //得到接收到的字节数

  164.         for ( i = 0; i < UART3_FlagTemp; i++ )
  165.         //将数据拷贝到缓冲区
  166.           uart3_data[ i ] = uart3_data_temp[ i ];
  167.         UART3_Flag = UART3_FlagTemp;

  168.         DMA_ClearFlag( DMA1_FLAG_TC3 );
  169.         DMA_Cmd( DMA1_Channel3, DISABLE ); //正式允许DMA                 
  170.         DMA3_Init();

  171.       }
  172.       UART3_Timeout = 0;
  173.     }
  174.   }
  175. }
复制代码

出0入0汤圆

发表于 2016-8-14 21:42:37 | 显示全部楼层
怎么不支持语法高亮呢?





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2016-8-16 18:04:58 | 显示全部楼层
收藏起来慢慢看

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-4-29 20:30

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

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