精华内容
下载资源
问答
  • 可能接收数据的时候会分成两次接收,即触发了两次串口中断,一次中断接收01 02 03 FF FF ,然后再一次中断接收37 12 53 75 12 FA AA 12 AA AA 52 73 57 12 53 AA AA 73 01 0F ,因为从打印结果如下! ![图片说明...
  • 串口是一种很常见的通讯接口,通过串口回传数据是很智能型的传感器都拥有的特点。...主要思想: 配置一个串口DMA接收 任务,任务搬运的数据量要大于一次通讯的总数据量(也就是DMA 的搬运工作还没结束 我...

    串口是一种很常见的通讯接口,通过串口回传数据是很多智能型的传感器都拥有的特点。 很多智能型传感器内置单片机 通过单片机将原始的数据处理,然后以串口的形式发送给用户单片机,用户单片机在利用串口得来的信息进行决策。 那么有没有一种简化通讯的方式呢 ?   那就是串口 +DMA了!

    主要思想: 配置一个串口DMA接收 任务,任务搬运的数据量要大于一次通讯的总数据量(也就是DMA 的搬运工作还没结束 我们的数据就已经搬完了) 然后这时候因为串口的总线 没有数据传输了(数据传输频率不是特别高 完全占用总线的情况下, 总线肯定会有一段时间空闲!) 这时候 因为串口的总线空闲会触发一个 串口总线控线中断,在这个中断里面  我们进行数据处理(获取感兴趣的信息),并且在数据处理完成后 将DMA 重新设置一个搬运任务。  这样 我们只需要在一帧(次)的数据传输完成后去处理一次数据即可,不需要每次都去处理串口接收中断,这样有好处!

    下面话不多说,上代码!  我是用的是Stm32F407VET6 代码下载 搜索 ‘stm32f407 配置6路串口DMA 接收 不定长数据


    void USART1_init()  //  PB7
    {
      GPIO_InitTypeDef   GPIO_InitStructure;
        USART_InitTypeDef  USART_InitStructure;
        NVIC_InitTypeDef   NVIC_InitStructure;
        DMA_InitTypeDef    DMA_InitStructure;
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);   
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
       //以上是初始化各种时钟
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
      //  以上是初始化 串口的IO
        USART_InitStructure.USART_BaudRate = 115200;
        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_Init(USART1, &USART_InitStructure);
        USART_Cmd(USART1, ENABLE);  
        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 这里开启的是总线空闲中段!  不是接收非空中断
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;        
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            
        NVIC_Init(&NVIC_InitStructure);    
         USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);  
       //以上是定时器的基本配置   主要是一些通讯相关的参数  不多做介绍 配置中断部分要看一下
        DMA_DeInit(DMA2_Stream5);
        while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE);
        DMA_InitStructure.DMA_Channel = DMA_Channel_4;                             //  DMA通道
        DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;    // 外设地址
        DMA_InitStructure.DMA_Memory0BaseAddr = (u32)USART_Cache[0];  //内部存储区地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;           // 传输方向 外设到内存
        DMA_InitStructure.DMA_BufferSize = RECEIVE_BUF_SIZE;                  //  接收的数据大小  一个常数 大于一帧的数据量
        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_Medium;
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
        DMA_Init(DMA2_Stream5, &DMA_InitStructure);
        DMA_Cmd(DMA2_Stream5, ENABLE);
    }

    以上就是初始化函数 其中的IO口 请查阅

    这本书。。   在pinmap  里面有定义

    这是我们所用到的 PB7 的  我使用的是福昕阅读器   可以直接 ctrl +F 查找 USART1  就可以找到串口1的 接口 

    DMA配置方面  要去 stm32F4 中文参考手册里面去查找

    这个在 DMA章节里面会提到 具体的 DMA 数据流和 外设的对应关系

    我的是在文档的 第205页  如果你下载了文档应该也会在差不多的位置可以找到相应的介绍。

     

    如果配置部分没有问题了  就可以继续向下看 终端部分的函数!

    void USART1_IRQHandler(void)                
    {
      u16 data
        if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)  //
        {
            DMA_Cmd(DMA2_Stream5, DISABLE);
            data = USART1->SR;
            data = USART1->DR;  // 这里必须读一下串口的SR和DR寄存器  这样程序才能正常运行, 具体原因我没有找到 望大佬解答
              这里可以插入自己的数据处理程序, 注意这里是中端 程序占用的时间别太多 而且绝对不能用delay函数!
            DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);
            DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF_SIZE);  
            DMA_Cmd(DMA2_Stream5, ENABLE); //  这两行是重新设置DMA  让这个搬运工准备下一次的工作
      }
        USART_ClearITPendingBit(USART1,USART_IT_IDLE); // 清除标志位
    }

     

     

     

     

     

    展开全文
  • STM32使用串口IDLE中断的两种接收不定长数据的方式

    万次阅读 多人点赞 2017-03-18 17:09:14
    现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,现提供两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:方法1:实现思路:...

    现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,现提供两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:

    方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理。

    应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。

    关键代码分析:

    usart.H

    #ifndef __USART_H
    #define __USART_H
    #include "stdio.h" 
    #include "sys.h" 
    
    #define DMA_Rec_Len 200      //定义一个长度为200个字节的数据缓冲区。(建议定义的长度比你可能接收到的最长单帧数据长度长!)
    
    void uart_init(u32 bound);
    void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
    
    #endif
    usart.C
    //初始化IO 串口1 
    //bound:波特率
    void uart_init(u32 bound)
    {
        //GPIO端口设置
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        DMA_InitTypeDef DMA_InitStructure;
    
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE); //使能USART1,GPIOA时钟
       RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟
    
       USART_DeInit(USART1);  //复位串口1
       //USART1_TX   PA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
        GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
       
        //USART1_RX  PA.10
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10
    
        //Usart1 NVIC 配置
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
        NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
      
       //USART 初始化设置
      USART_InitStructure.USART_BaudRate = bound;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
      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_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启空闲中断
        USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);   //使能串口1 DMA接收
        USART_Cmd(USART1, ENABLE);                   //使能串口 
     
        //相应的DMA配置
      DMA_DeInit(DMA1_Channel5);   //将DMA的通道5寄存器重设为缺省值  串口1对应的是DMA通道5
      DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA外设usart基地址
      DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;  //DMA内存基地址
      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存
      DMA_InitStructure.DMA_BufferSize = DMA_Rec_Len;  //DMA通道的DMA缓存的大小
      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
      DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
      DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
      DMA_Init(DMA1_Channel5, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道
    
        DMA_Cmd(DMA1_Channel5, ENABLE);  //正式驱动DMA传输
    }
    
    //重新恢复DMA指针
    void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
    { 
    	DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1所指示的通道    
     	DMA_SetCurrDataCounter(DMA_CHx,DMA_Rec_Len);//DMA通道的DMA缓存的大小
     	DMA_Cmd(DMA_CHx, ENABLE);  //打开USART1 TX DMA1所指示的通道  
    }	
    
    //发送len个字节
    //buf:发送区首地址
    //len:发送的字节数
    void Usart1_Send(u8 *buf,u8 len)
    {
    	u8 t;
      	for(t=0;t<len;t++)		//循环发送数据
    	{		   
    		while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);	  
    		USART_SendData(USART1,buf[t]);
    	}	 
    	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);		
    }
    
    //串口中断函数
    void USART1_IRQHandler(void)                //串口1中断服务程序
    {
    
         if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
          {
              USART_ReceiveData(USART1);//读取数据注意:这句必须要,否则不能够清除中断标志位。
              Usart1_Rec_Cnt =DMA_Rec_Len-DMA_GetCurrDataCounter(DMA1_Channel5); //算出接本帧数据长度
       
             //***********帧数据处理函数************//
              printf ("Thelenght:%d\r\n",Usart1_Rec_Cnt);
              printf ("The data:\r\n");
              Usart1_Send(DMA_Rece_Buf,Usart1_Rec_Cnt);
             printf ("\r\nOver! \r\n");
            //*************************************//
             USART_ClearITPendingBit(USART1,USART_IT_IDLE);         //清除中断标志
             MYDMA_Enable(DMA1_Channel5);                  //恢复DMA指针,等待下一次的接收
         } 
    
    }
    
    

    方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。
    基本知识:
    IDLE中断什么时候发生?
    IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。
    如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。
    看了前面IDLE中断的定义,你就会明白了,一帧数据结束后,就会产生IDLE中断。

    如何配置好IDLE中断?
    下面我们就配置好串口IDLE中断吧。
    这里写图片描述
    这是串口CR1寄存器,其中,对bit4写1开启IDLE中断,对bit5写1开启接收数据中断。(注意:不同系列的STM32,对应的寄存器位可能不同)

    RXNE中断和IDLE中断的区别?
    当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
    这里写图片描述
    这是状态寄存器,当串口接收到数据时,bit5就会自动变成1,当接收完一帧数据后,bit4就会变成1.
    需要注意的是,在中断函数里面,需要把对应的位清0,否则会影响下一次数据的接收。比如RXNE接收数据中断,只要把接收到的一个字节读出来,就会清除这个中断**。IDLE中断,如何是F0系列的单片机,需要用ICR寄存器来清除,如果是F1系列的单片机,清除方法是“先读SR寄存器,再读DR寄存器”。**(我怎么知道?手册上写的)

    下面以STM32F103为例给出源程序。
    我们先来看程序中的主要部分。
    串口初始化函数片段
    这里写图片描述
    串口中断函数
    这里写图片描述
    串口中断函数里面,最重要的两条语句,就是上图中圈出来的两条语句。第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据。(是不是感觉超级方便?妈妈再也不用担心我如何判断是否接收完1帧数据了。)
    主函数
    这里写图片描述
    这个主函数,是用来验证接收的正确性的。RxCounter表示的是这一帧数据有几个字节,接收完一帧数据,会在中断函数里面把ReceiveState置1,然后,通过串口把接收到的数据发送回串口。这样,既验证了接收了多少字节的正确性,又验证了接收到的数据是否正确。

    两个程序代码均采用stm32f103zet6测试过,完全没问题。
    参考文章:http://www.51hei.com/bbs/dpj-39885-1.html(STM32串口接收不定长数据原理与源程序)
    http://www.openedv.com/thread-63849-1-1.htmlSTM32(使用串口1配合DMA接收不定长数据,大大减轻CPU载荷。)

    展开全文
  • 现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,现提供两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:方法1:实现思路:...

    现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,现提供两种利用串口IDLE空闲中断的方式接收一帧数据,方法如下:

    方法1:实现思路:采用STM32F103的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理。

    应用对象:适用于各种串口相关的通信协议,如:MODBUS,PPI ;还有类似于GPS数据接收解析,串口WIFI的数据接收等,都是很好的应用对象。

    关键代码分析:

    usart.H

    #ifndef __USART_H

    #define __USART_H

    #include "stdio.h"

    #include "sys.h"

    #define DMA_Rec_Len 200      //定义一个长度为200个字节的数据缓冲区。(建议定义的长度比你可能接收到的最长单帧数据长度长!)

    void uart_init(u32 bound);

    void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);

    #endif

    usart.C

    //初始化IO 串口1

    //bound:波特率

    void uart_init(u32 bound)

    {

    //GPIO端口设置

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    DMA_InitTypeDef DMA_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE); //使能USART1,GPIOA时钟

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟

    USART_DeInit(USART1);  //复位串口1

    //USART1_TX   PA.9

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9

    //USART1_RX  PA.10

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

    //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能

    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

    //USART 初始化设置

    USART_InitStructure.USART_BaudRate = bound;

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

    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_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启空闲中断

    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);   //使能串口1 DMA接收

    USART_Cmd(USART1, ENABLE);                   //使能串口

    //相应的DMA配置

    DMA_DeInit(DMA1_Channel5);   //将DMA的通道5寄存器重设为缺省值  串口1对应的是DMA通道5

    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR; //DMA外设usart基地址

    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;  //DMA内存基地址

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //数据传输方向,从外设读取发送到内存

    DMA_InitStructure.DMA_BufferSize = DMA_Rec_Len;  //DMA通道的DMA缓存的大小

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位

    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式

    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输

    DMA_Init(DMA1_Channel5, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道

    DMA_Cmd(DMA1_Channel5, ENABLE);  //正式驱动DMA传输

    }

    //重新恢复DMA指针

    void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)

    {

    DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1所指示的通道

    DMA_SetCurrDataCounter(DMA_CHx,DMA_Rec_Len);//DMA通道的DMA缓存的大小

    DMA_Cmd(DMA_CHx, ENABLE);  //打开USART1 TX DMA1所指示的通道

    }

    //发送len个字节

    //buf:发送区首地址

    //len:发送的字节数

    void Usart1_Send(u8 *buf,u8 len)

    {

    u8 t;

    for(t=0;t{

    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

    USART_SendData(USART1,buf[t]);

    }

    while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

    }

    //串口中断函数

    void USART1_IRQHandler(void)                //串口1中断服务程序

    {

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)

    {

    USART_ReceiveData(USART1);//读取数据注意:这句必须要,否则不能够清除中断标志位。

    Usart1_Rec_Cnt =DMA_Rec_Len-DMA_GetCurrDataCounter(DMA1_Channel5); //算出接本帧数据长度

    //***********帧数据处理函数************//

    printf ("Thelenght:%d\r\n",Usart1_Rec_Cnt);

    printf ("The data:\r\n");

    Usart1_Send(DMA_Rece_Buf,Usart1_Rec_Cnt);

    printf ("\r\nOver! \r\n");

    //*************************************//

    USART_ClearITPendingBit(USART1,USART_IT_IDLE);         //清除中断标志

    MYDMA_Enable(DMA1_Channel5);                  //恢复DMA指针,等待下一次的接收

    }

    }

    方法2:实现思路:直接利用stm32的RXNE和IDLE中断进行接收不定字节数据。

    基本知识:

    IDLE中断什么时候发生?

    IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。

    如何判断一帧数据结束,就是我们今天讨论的问题。因为很多项目中都要用到这个,因为只有接收到一帧数据以后,你才可以判断这次收了几个字节和每个字节的内容是否符合协议要求。

    看了前面IDLE中断的定义,你就会明白了,一帧数据结束后,就会产生IDLE中断。

    如何配置好IDLE中断?

    下面我们就配置好串口IDLE中断吧。

     

    这是串口CR1寄存器,其中,对bit4写1开启IDLE中断,对bit5写1开启接收数据中断。(注意:不同系列的STM32,对应的寄存器位可能不同)

    RXNE中断和IDLE中断的区别?

    当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。

     

    这是状态寄存器,当串口接收到数据时,bit5就会自动变成1,当接收完一帧数据后,bit4就会变成1.

    需要注意的是,在中断函数里面,需要把对应的位清0,否则会影响下一次数据的接收。比如RXNE接收数据中断,只要把接收到的一个字节读出来,就会清除这个中断。IDLE中断,如何是F0系列的单片机,需要用ICR寄存器来清除,如果是F1系列的单片机,清除方法是“先读SR寄存器,再读DR寄存器”。(我怎么知道?手册上写的)

    下面以STM32F103为例给出源程序。

    我们先来看程序中的主要部分。

    串口初始化函数片段

     

    串口中断函数

     

    串口中断函数里面,最重要的两条语句,就是上图中圈出来的两条语句。第一条语句用来判断是否接收到1个字节,第二条语句用来判断是否接收到1帧数据。(是不是感觉超级方便?妈妈再也不用担心我如何判断是否接收完1帧数据了。)

    主函数

     

    这个主函数,是用来验证接收的正确性的。RxCounter表示的是这一帧数据有几个字节,接收完一帧数据,会在中断函数里面把ReceiveState置1,然后,通过串口把接收到的数据发送回串口。这样,既验证了接收了多少字节的正确性,又验证了接收到的数据是否正确。

    两个程序代码均采用stm32f103zet6测试过,完全没问题。

    参考文章:http://www.51hei.com/bbs/dpj-39885-1.html(STM32串口接收不定长数据原理与源程序)

    http://www.openedv.com/thread-63849-1-1.htmlSTM32(使用串口1配合DMA接收不定长数据,大大减轻CPU载荷。)

    本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

    展开全文
  • 配送一个自己写的串口驱动程序 用DMA接收数据 接收完会产生一个空闲中断 由此可判断接收一个包的数据 再配送一个我自己写的动态内存管理 跟ESP8266的驱动 在项目中测试460800的波特率 30kb一秒的数据接收 包...
  • 本篇文章主要介绍如何使用STM32CubeMX初始化STM32L431RCT6的USART,并使用中断模式发送和接收数据。1. 准备工作硬件准备首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):软件准备需要安装好Keil - ...
    537bb6c2833566ae7845373667bf5839.png更多精彩~点击上面“蓝字”关注我们呀  cab9faa673edd5c308fa078e1e96abb0.png

    寻求更好的阅读体验,请点击『阅读原文』移步:Mculover666的个人博客。

    本篇文章主要介绍如何使用STM32CubeMX初始化STM32L431RCT6的USART,并使用中断模式发送和接收数据。

    1. 准备工作

    硬件准备

    首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):

    d08a87b259c7d8b8078dbacdd36f5eec.png

    软件准备

    • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;

    • 准备一个串口调试助手,这里我使用的是Serial Port Utility

    Keil MDK和串口助手Serial Port Utility 的安装包都可以在文末关注公众号获取,回复关键字获取相应的安装包:

    44c0f6ed7466d2558de5460e13dcb19a.png

    2.生成MDK工程

    选择芯片型号

    打开STM32CubeMX,打开MCU选择器:

    d765173b97cc038db09e8b7b0f8f72e8.png

    搜索并选中芯片STM32L431RCT6:
    5bb556a1bf707401d6a91cfa045b8141.png

    配置时钟源

    • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;

    • 如果使用默认内部时钟(HSI),这一步可以略过;

    这里我都使用外部时钟:

    9dc9168597fe36701c97301182fbe0a9.png

    配置串口

    小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:

    1e8bd56e7a2bd2d9b9128b1b0fb6f48f.png
    d0b444771bcf1ff3898368f3413ffd3c.png

    接下来开始配置USART1
    40694fcbcde995fa9e17c2a973177b35.png

    NVIC配置

    224204f93bb75290d4e743bac4ec4c81.png

    配置时钟树

    STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

    cd9ab84051e8f41a36324ebb71ec4174.png

    生成工程设置

    d0b50eb7cf343ebe97c6b5722518a0ed.png

    代码生成设置

    最后设置生成独立的初始化文件:

    61203a2e54f6cad585d08d15eb4b16af.png

    生成代码

    点击GENERATE CODE即可生成MDK-V5工程:

    487fc65d583520a466d91242569c72d2.png

    3. 在MDK中编写、编译、下载用户代码

    定义发送和接收缓冲区

    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    uint8_t hello[] = "USART1 is ready...\n";
    uint8_t recv_buf[13] = {0};
    /* USER CODE END 0 */

    重新实现中断回调函数

    在NVIC一讲中我们探索了HAL库的中断处理机制,HAL中弱定义了一个中断回调函数 HAL_UART_RxCpltCallback, 我们需要在用户文件中重新定义该函数,放在哪都可以,这里我放在 main.c 中:

    /* USER CODE BEGIN 4 */
    /* 中断回调函数 */
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
        /* 判断是哪个串口触发的中断 */
        if(huart ->Instance == USART1)
        {
            //将接收到的数据发送
            HAL_UART_Transmit_IT(huart, (uint8_t*)recv_buf, 13);
            //重新使能串口接收中断
            HAL_UART_Receive_IT(huart, (uint8_t*)recv_buf, 13);
        }
    }
    /* USER CODE END 4 */

    修改main函数

    在main函数中首先开启串口中断接收,然后发送提示信息:

    int main(void){
      HAL_Init();

      SystemClock_Config();

      MX_GPIO_Init();
      MX_USART1_UART_Init();

      /* USER CODE BEGIN 2 */
      //使能串口中断接收
      HAL_UART_Receive_IT(&huart1, (uint8_t*)recv_buf, 13);
      //发送提示信息
      HAL_UART_Transmit_IT(&huart1, (uint8_t*)hello, sizeof(hello));
      /* USER CODE END 2 */

      while (1)
      {
      }
    }

    编译代码

    编译整个工程:

    50f27659780e079e2e8cac1ae5595729.png

    设置下载器

    c002ade6d501cc975a6894821f303738.png
    272398f654566e7cf3ec64dce747e029.png

    实验现象

    下载运行后,实验现象如下:

    7bb9282442638f9cd7d89d929b2d9046.png

    至此,我们已经学会了如何配置USART使用中断模式发送和接收数据,下一节将讨论实现printf()函数的多种方法。

    更多精彩文章及资源,请关注我的微信公众号:『mculover666』。

    1e7103bf2e239f56106c77366d8acdf3.png
    83c32d6583280a8cf0bf8906f610b29e.gif
    展开全文
  • 串口一次只能传1byte数据,在实际应用中,我们会发送和接收一串数据,如果没发送和接收个数据就会进去中断会严重影响程序的正常执行,占用过多的cpu资源。如果串口模块能够自动判别一串数据的结束,并且把接收数据...
  • 串口接收/发送有三种模式:中断、轮询、DMA,轮询方式并不推荐,也不经常使用,这里主要看的是中断和...接收个数据中断一次去处理数据,数据的话,程序老是被被打断,这样有可能会产生不好的效果。推荐还是一...
  • 单片机串口接收多字节

    千次阅读 2019-05-02 22:28:13
    感觉串口多字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是有点难度的——... 串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收的数...
  • stm32F4 串口空闲中断智能型传感器内置单片机 ...wY=k$F%z$^ m-_6.Y3+7I主要思想: 配置一个串口DMA接收 任务,任务搬运的数据量要大于一次通讯的总数据量(也就是DMA 的搬运工作还没结束 我们的数据就已经搬完了...
  • 第三:实际做项目的时候,尽量利用单片机内部自带的集成串口,不到万不得已尽量不要用自制的模拟串口,如果非要用本节讲的模拟串口,那么一次接收的数据包不要太长,尽可能越短越好,因为自己做的模拟串口在稳定性...
  • 比如说给单片机一次发来1字节,或者一次发来8字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。 如何判断一帧数据结束,就是我们今天讨论的问题。因为很项目中都要用到这,因为只有接收到一...
  • 第三:实际做项目的时候,尽量利用单片机内部自带的集成串口,不到万不得已尽量不要用自制的模拟串口,如果非要用本节讲的模拟串口,那么一次接收的数据包不要太长,尽可能越短越好,因为自己做的模拟串口在稳定性...
  • 串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收数据了。然而在实际应用当中,基本上不会有单字节接收的情况。一般都是基于一定串口通信...
  • 它实现的方法是每收到一字节发送一次,这做法用在串口转发数据上实时性还是不错的,但是在平时多数的串口应用中更的是需要实际通讯。往往是单片机接收一帧数据,根据通讯协议实现某些功能,然后再回答。为此我...
  • //给当前串口增加一个监听器 comPort.notifyOnDataAvailable(true); //当有数据是通知 } catch (TooManyListenersException e) { e.printStackTrace(); } try { //设置串口参数依次为(波特率,数据位,停止位,...
  • 串口通信中,我们很时候是需要接收不定长的数据,并且对接收数据放入到缓存中,等待接下来的处理。 整一个流程大致如下: 实现方法: 串口 空闲中断; DMA接收数据:(循环模式、不使用中断)1、可以得知...
  • STM32底层A/D转换采样 通过DMA连接片上USART 定时地发送数据到上位机 其中我对个通道进行采样 采样十 即同个传感器 一共20个数据(STM32是12位的片上AD 参考电压3.3V) 数据传入Matlab的串口中 用矩阵相乘的...
  • 在使用串口接收时,我调用了一个数据缓存的函数,函数里面就是写个数组的循环操作,但调试发现发送单个数组无问题,发送数据就出现问题,数据会丢失,而且挺严重的,然后我就开始怀疑是我缓存逻辑有问题,但我看了好多...
  •  工作了一年,写了不少单片机串口程序。感觉串口多字节接收部分的逻辑相对于配置寄存器跟串口回复来说,是... 串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄...
  • 需要用到单片机的人基本上都需要用到串口功能,看了好多教程提供的是中断接收和发送功能。但是通过中断收发数据有不少弊端。其一:中断影响程序的运行速度。(想想发一字节中断就中断一次,难受!!!)其二:频繁...
  • 最近要用ATtiny2313作一个小电子设计,用到UART。... 我为此进行了多次程序修改,当我参考io2313.h文件,把SIGNAL(SIG_UART_RECV)改成SIGNAL(_VECTOR(7))后,在中断条件下ATtiny2313可以接收串口数据了!
  • 中断又分为一次接收一个数据,和DMA一次接收多个数据。 考虑到数据的高速情况,我们这里当然是选择DMA了, 但是DMA有一个不好的点就是中断触发机制,要么是half,要么是接收完毕, 那么就有一个很难受的问题,一帧...
  • STM32底层A/D转换采样 通过DMA连接片上USART 定时地发送数据到上位机 其中我对个通道进行采样 采样十 即同个传感器 一共20个数据(STM32是12位的片上AD 参考电压3.3V) 数据传入Matlab的串口中 用矩阵相乘的...
  • 比如说给单片机一次发来1字节,或者一次发来8字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。 如何判断一帧数据结束,就是我们今天讨论的问题。因为很项目中都要用到这,因为只有接收到一...
  • 并行:一次发送多位或接收多数据时钟周期可以发送多位数据 串行:优点:节约硬件资源 缺点:速度慢 并行:优点:速度快 缺点:浪费硬件资源 传输方向: 半双工:在同一时刻只能有一传输方向 双工: 全...

空空如也

空空如也

1 2 3 4 5
收藏数 86
精华内容 34
关键字:

串口一次中断接收多个数据