历史上的今天

今天是:2024年10月19日(星期六)

正在发生

2020年10月19日 | STM32串口DMA超时接收方法,可大大节约CPU时间

发布者:Asawen 来源: eefocus关键字:STM32  串口DMA  超时接收  CPU时间 手机看文章 扫描二维码
随时随地手机看文章

本办法使用定时器定时查询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_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_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_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;
        }
  }  
}

[1] [1]
关键字:STM32  串口DMA  超时接收  CPU时间 引用地址:STM32串口DMA超时接收方法,可大大节约CPU时间

上一篇:关于stm32 的 USB 转串口 virtual_Com_Port的例程的一些问题
下一篇:STM32F10x芯片RTC实时时钟

推荐阅读

FPGA行业高管将领导莱迪思全球企业营销和战略部门以期实现快速盈利增长 莱迪思半导体公司(NASDAQ: LSCC),客制化智能互连解决方案市场的领先供应商,近日宣布任命Esam Elashmawi为首席营销和战略官,即日上任。Elashmawi先生将为莱迪思带来他在销售、市场营销、战略规划和综合管理等领域的丰富经验。加入莱迪思之前,Elashmawi先生曾任Microsemi公司高...
集成电路刚被发明出来的时候,当时的特征尺寸大概是10μm(10000nm),之后逐步缩小到了5μm、3μm、1μm、0.8μm、0.5μm、0.35μm、0.25μm、0.18μm、0.13μm、90nm、65nm、45nm、32nm、22nm、16nm、10nm,发展至今,台积电已经开始量产7nm+(采用EUV的7nm)的芯片了,明年还将量产5nm的。在这个过程当中,制程共经历了20几代变革,未来几年,3nm、...
根据Yole Developpement报告,在冠状病毒的推动下,UVC市场在2025年可能达到25亿美元,2019-2025年的复合年增长率为61%。2020年,整个UVC的供应链出现了整体短缺,该行业现在正寻求迅速增加产能的方式。主要供应商是LG Innotek和Seoul Viosys/Semiconductor,它们是使UVC LED市场发展的关键公司。UVC市场从2008年的2000万美元增长到2015年的1亿美元,2...
广告摘要声明广告【文/sya】近日,央视财经频道播出《工业母机行业观察》,视频分析,受益于制造业明显复苏,东莞的工业母机企业订单爆满。其中,拓斯达控股子公司东莞市埃弗米数控设备科技有限公司(以下简称“埃弗米”)就是订单爆满企业之一。2020年下半年开始,我国制造业复苏明显,加上海外疫情倒逼制造业回流中国,拉动了机床的需求。此外,新能源汽...

史海拾趣

问答坊 | AI 解惑

求电子钟的设计

跪求电子钟的设计…

查看全部问答∨

Keil c51v612完全中文破解版

本帖最后由 paulhyde 于 2014-9-15 09:31 编辑 keil C51v612中文完全破解版.rar  …

查看全部问答∨

AVR_figter20090728版

AVR_figter20090728版 AVR_fighter已经更新到20090728版,可以到www.ourdev.cn/bbs中的usb分论谈中顶置贴里查看更新或下载AVR_fighter20090728版. 新版增加了一点实用功能.如时钟效准写入flash,在flash或eeporm选项卡中进行查找......等等. OURDE ...…

查看全部问答∨

proteus 安装的我一个小问题 在线等QQ 174366021

proteus 安装的我一个小问题 在线等QQ 174366021   下线等大侠们,…

查看全部问答∨

关于arm9200的IO驱动设计

各位老师好:   我想请教一个关于arm9200的IO驱动的一个问题,我想简单的给一个IO脚可以置位,有没有针对这方面的一些示例,谢谢各位。…

查看全部问答∨

通用makefile

OBJS = send_voip.o fun.o main.o CrcPublic.oARCH ?=  #ARCH ?= arm-hismall-linux-CC = $(ARCH)gccINC_DIR +=.SRC_DIR +=.INSTALL_PATH=../TARGET=helloCFLAGS= -Wall -g $(TARGET): $(OBJS) $(CC) $(OBJS) -o $@ $(CFLAGS) cp -f $(TA ...…

查看全部问答∨

安防中用到的线尾电阻,是起什么作用的?

安防中用到的线尾电阻,是起什么作用的?…

查看全部问答∨

cc2531驱动问题

   cc2531作为协调器,然后cc2530作为节点,可以实现组网功能。现在问题是我想通过2531的usb口连接pc,此时不知道2531的驱动如何添加和使用(添加详细解释下),求大神指点。   本人小白一个,在学习中。。。求助啊 先谢过大 ...…

查看全部问答∨

两只老虎,自己写的程序,亲测完全没有问题!!!

花了半天时间研究了下蜂鸣器演奏乐曲的基本原理,有兴趣的朋友可以在这里讨论一下。《两只老虎》送给你,由于是楼主的处女座所以有偿奉献,望大家体谅。…

查看全部问答∨

音乐让你 High !!! - 图说车载音乐系统

1. 左右通道 刚开汽车都没喇叭,后来发达了,装了两个,听听小曲。 小朋友说:二叔车不错,听歌的时候,敲鼓的在这边,大罗的在那边了。 二叔说:这是立体声。 构造也简单,主机输出 两路线,个接一喇叭 …

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

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

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved