串口持续地接收不定长、不定时的数据,把每一帧数据缓存下来且灵活地利用内存空间,下面提供一种方式供参考。原理是利用串口空闲中断和DMA,每当对方发来一帧完整的数据后,串口接收开始空闲,触发中断,在中断处理中新建一个接收队列节点,把DMA缓存的数据copy到接收队列里。当需要的时候就从接收队列里提出数据。定期清理队列防止堆空间溢出。

话不多说,上代码。

定义数据结构:

/*USART接收队列*/

typedef struct _USART_REC_Queue

{

    u16 index;                          //序号

    char *buf;                          //链接的字符串

    struct _USART_REC_Queue*  next;     //链接到下一个节点

}USART_REC_Queue;

声明全局变量:

#define USART3_REC_len 320                       //单次最大接收数

extern u8 USART3_REC_buf[USART3_REC_len];        //用于DMA的临时数据中转

extern u16 USART3_REC_counter;                   //接收计数器

extern USART_REC_Queue* USART3_REC_Queue_head;   //接收队列固定头节点

extern USART_REC_Queue* USART3_REC_Queue_tail;   //始终指向最后一个节点

准备阶段:

在启动汇编文件里,把堆空间改大,防止接收一点点数据就内存溢出。

Heap_Size       EQU     0x00004000    //默认200字节,改大

实例化全局变量:

u8 USART3_REC_buf[] = {}; 

u16 USART3_REC_counter = ;

USART_REC_Queue* USART3_REC_Queue_head = NULL;

USART_REC_Queue* USART3_REC_Queue_tail = NULL;

初始化各个硬件,使能了串口接收空闲中断,串口接收DMA,为接收队列头节点分配内存空间:

void USART3_Init(u32 BaudRate)

{

    //初始化参数结构体

    GPIO_InitTypeDef   GPIO_InitStruct;       //IO

    USART_InitTypeDef  USART_InitStruct;      //串口

    NVIC_InitTypeDef   NVIC_InitStruct;       //中断控制

    DMA_InitTypeDef    DMA_InitStruct;        //DMA

    /*全局指针初始化*/

    USART3_REC_Queue_head = USART_REC_Queue_Creat();  //构建串口3接收队列头节点

    USART3_REC_Queue_tail = USART3_REC_Queue_head;    //构建串口3接收队列尾节点

    //RCC使能

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     //IO时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);    //串口3时钟

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //DMA时钟

    //PB11 USART1_TXD

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;

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

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &GPIO_InitStruct);  

    //PB10 USART1_RXD

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;

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

    //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOB, &GPIO_InitStruct);

    //内嵌向量中断控制器初始化

    NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;

    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = ;//抢占优先级1

    NVIC_InitStruct.NVIC_IRQChannelSubPriority = ;//子优先级1

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

    NVIC_Init(&NVIC_InitStruct);

    //USART初始化

    USART_InitStruct.USART_BaudRate = BaudRate;//波特率 一般9600

    USART_InitStruct.USART_WordLength = USART_WordLength_8b;//字节数据格式8位

    USART_InitStruct.USART_StopBits = USART_StopBits_1;//一个停止位

    USART_InitStruct.USART_Parity = USART_Parity_No;//无奇偶字节校验

    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制

    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式

    USART_Init(USART3, &USART_InitStruct);//初始化USART

    //USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//使能接收中断

    USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);//使能总线空闲中断

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

    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&USART3->DR); //读取哪一个寄存器

    DMA_InitStruct.DMA_MemoryBaseAddr = (u32)(&USART3_REC_buf);      //读取到的数据的存放地址

    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                  //指定外设为源地址

    DMA_InitStruct.DMA_BufferSize = USART3_REC_len;                  //数据存放区大小

    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    //外设寄存器地址是否偏移

    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;             //数据存放地址是否偏移

    DMA_InitStruct.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte; //外设数据宽度8位

    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     //定义存储器数据宽度8位

    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                       //正常操作模式

    DMA_InitStruct.DMA_Priority = DMA_Priority_High;                 //通道优先级

    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                        //是否开启存储器到存储器模式

    DMA_Init(DMA1_Channel3, &DMA_InitStruct);                        //写入设置到DMA1通道  

    DMA_Cmd(DMA1_Channel3, ENABLE);                                  //使能DMA1通道 

    USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);                   //注意不要忘了使能串口的DMA功能

}

串口中断处理(核心):

void USART3_IRQHandler(void)

{ 

    if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)

    {

        char *buf_new;                  //新字符串

        USART_REC_Queue* queue_new;     //新队列节点               

        u16 len;

        USART3->DR;                                       //读取数据。注意:这句必须要,否则不能够清除中断标志位

        USART_ClearITPendingBit(USART3, USART_IT_IDLE);                  //清中断

        DMA_Cmd(DMA1_Channel3, DISABLE);                                 //关闭DMA1通道3

        len = USART3_REC_len - DMA_GetCurrDataCounter(DMA1_Channel3);    //计算接收长度

        buf_new = (char *)malloc((len+) * sizeof(char));                //为新字符串分配内存,预留空间添加序号

        //if(buf_new == NULL)  GPIO_SetBits(LedPort, Led1);              //内存不够的提示

        queue_new = USART_REC_Queue_Creat();                             //为新队列节点分配内存

        //if(queue_new == NULL)  GPIO_SetBits(LedPort, Led2);            //内存不够的提示

        USART3_REC_counter ++;                                           //计数器加1

        queue_new->index = USART3_REC_counter;                           //新节点的序号

        sprintf(buf_new, "#%d:%s", USART3_REC_counter, USART3_REC_buf);  //复制缓存到新字符串并添加序号

        queue_new->buf = buf_new;                                        //新队列节点链接新字符串

        USART3_REC_Queue_tail->next = queue_new;                         //接收队列尾节点链接新的节点

        USART3_REC_Queue_tail = queue_new;                               //更新尾节点

        DMA1_Channel3->CNDTR = USART3_REC_len;                           //重置DMA1通道3缓存计数器

        DMA_Cmd(DMA1_Channel3, ENABLE);                                  //重开DMA1通道3

    }

}

创建与销毁接收队列节点:

USART_REC_Queue* USART_REC_Queue_Creat(void)

{

    USART_REC_Queue* p_temp = (USART_REC_Queue*)malloc(sizeof(USART_REC_Queue));

    if(p_temp == NULL)   return NULL;   

    memset(p_temp, , sizeof(USART_REC_Queue));

    //p_temp->next = NULL;

    return p_temp; 

}

void USART_REC_Queue_Delete(USART_TypeDef* USARTx)

{

    if(USARTx == USART3)

    {

        USART_REC_Queue* temp;

        temp = USART3_REC_Queue_head->next;

        if(temp == NULL || temp->next == NULL)   return;

        free(temp->buf);

        USART3_REC_Queue_head->next = temp->next;

        free(temp);

    } 

}

主函数里每隔5秒刷新显示接收队列的数据,并清理。

while()

{                        

    if(tim3_flag == )
{
tim3_flag = ; USART_REC_Queue_display(USART3); USART_REC_Queue_Delete(USART3);
} }

在OLED屏上显示接收队列里的数据:

void USART_REC_Queue_display(USART_TypeDef* USARTx)

{

    if(USARTx == USART3)

    {

        USART_REC_Queue* temp = USART3_REC_Queue_head->next;

        if(temp == NULL)   return;                                //队列还没生成则返回

        OLED_Clear();                                             //清屏

        OLED_ShowString(, , USART3_REC_Queue_head->next->buf);  //显示字符串

    }

}

Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据的更多相关文章

  1. STM32串口接收不定长数据原理与源程序(转)

    今天说一下STM32单片机的接收不定长度字节数据的方法.由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的A ...

  2. 串口配合DMA接收不定长数据(空闲中断+DMA接收)-(转载)

    1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间  认为桢收完,总线空闲中断是在检测到在接收数据后, ...

  3. 串口1配合DMA接收不定长数据(空闲中断+DMA接收)

    1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间  认为桢收完,总线空闲中断是在检测到在接收数据后, ...

  4. STM32 HAL库使用中断实现串口接收不定长数据

    以前用DMA实现接收不定长数据,DMA的方法接收串口助手的数据,全部没问题,不过如果接收模块返回的数据,而这些数据如果包含回车换行的话就会停止接收,例如接收:AT\r\nOK\r\n,就只能接收到AT ...

  5. STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷

    STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...

  6. STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

  7. 2-关于单片机通信数据传输(中断接收,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)

    上一篇链接 http://www.cnblogs.com/yangfengwu/p/8628219.html 先说明一点这种方式,不光对于单片机类的,,对于上位机接收数据同样适用----不骗人的,自己 ...

  8. STM32 ~ USART接收不定长数据

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

  9. STM32串口空闲中断

    串口初始化 #include "usart5.h" vu16 UART5_RX_STA=0; char UART5_RX_BUF[UART5_REC_LEN]; u8 UART5_ ...

随机推荐

  1. 关于jsp中jstl报错Can not find the tag library descriptor for "http://java.sun.com/jsp/jstl/core

    有的时候在开发jsp时,需要使用jstl时,在jsp上面引用jstl却出现错误:Can not find the tag library descriptor for "http://jav ...

  2. Hudi基本概念

    Apache Hudi(发音为"Hoodie")在DFS的数据集上提供以下流原语 插入更新 (如何改变数据集?) 增量拉取 (如何获取变更的数据?) 在本节中,我们将讨论重要的概念 ...

  3. 实战!轻松搭建图像分类 AI 服务

    人工智能技术(以下称 AI)是人类优秀的发现和创造之一,它代表着至少几十年的未来.在传统的编程中,工程师将自己的想法和业务变成代码,计算机会根据代码设定的逻辑运行.与之不同的是,AI 使计算机有了「属 ...

  4. 从React 编程到"好莱坞"

    目录 概念 面向流设计 异步化 响应式宣言 参考文档 概念 Reactive Programming(响应式编程)已经不是一个新东西了. 关于 Reactive 其实是一个泛化的概念,由于很抽象,一些 ...

  5. node.js调试入门

    1-1 Inspector介绍 使用Inspector调试Node.js的优势 可查看当前上下文的变量 可观察当前函数调用堆栈 不侵入代码 可在暂停状态下执行指定代码 Inspector的构成以及原理 ...

  6. nginx部署基于http负载均衡器

    nginx跨多个应用程序实例的负载平衡是一种用于优化资源利用率,最大化吞吐量,减少延迟和确保容错配置的常用技术. 环境介绍 配置nginx负载均衡器因会用到多台服务器来进行,所以下面我会用到docke ...

  7. iSensor APP 之 摄像头调试 OV5642

    iSensor APP 之 摄像头调试  OV5642 iSensor app 非常适合调试各种摄像头,已测试通过的sensor有: l  OV7670.OV7725.OV9650.OV9655.OV ...

  8. Nios II的Boot过程分析

    目录 1       概述....................................................................... 1 2       几种常见的 ...

  9. base64编码的字符串与图片相互转换

    #region 图片转为base64编码的字符串---ImgToBase64String /// <summary> /// 图片转为base64编码的字符串 /// </summa ...

  10. Xshell连接阿里云服务器

    1.遇到的问题 ​ 直接用阿里云的终端,还需要登录浏览器很是麻烦,所以用Xshell,ssh远程登录这样就轻松方便了很多. 2.打开 ​ 打开安装好的Xshell,点击新建 出现了这个界面,首先注意主 ...