Stm32使用串口空闲中断,基于队列来接收不定长、不定时数据
串口持续地接收不定长、不定时的数据,把每一帧数据缓存下来且灵活地利用内存空间,下面提供一种方式供参考。原理是利用串口空闲中断和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使用串口空闲中断,基于队列来接收不定长、不定时数据的更多相关文章
- STM32串口接收不定长数据原理与源程序(转)
今天说一下STM32单片机的接收不定长度字节数据的方法.由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的A ...
- 串口配合DMA接收不定长数据(空闲中断+DMA接收)-(转载)
1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间 认为桢收完,总线空闲中断是在检测到在接收数据后, ...
- 串口1配合DMA接收不定长数据(空闲中断+DMA接收)
1.空闲中断和别的接收完成(一个字节)中断,发送完成(发送寄存器控)中断的一样是串口中断: 2.空闲中断是接收到一个数据以后,接收停顿超过一字节时间 认为桢收完,总线空闲中断是在检测到在接收数据后, ...
- STM32 HAL库使用中断实现串口接收不定长数据
以前用DMA实现接收不定长数据,DMA的方法接收串口助手的数据,全部没问题,不过如果接收模块返回的数据,而这些数据如果包含回车换行的话就会停止接收,例如接收:AT\r\nOK\r\n,就只能接收到AT ...
- STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷
STM32使用串口1配合DMA接收不定长数据,减轻CPU载荷 http://www.openedv.com/thread-63849-1-1.html 实现思路:采 用STM32F103的串口1,并配 ...
- STM32之串口DMA接收不定长数据
STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...
- 2-关于单片机通信数据传输(中断接收,大小端,IEEE754浮点型格式,共用体,空闲中断,环形队列)
上一篇链接 http://www.cnblogs.com/yangfengwu/p/8628219.html 先说明一点这种方式,不光对于单片机类的,,对于上位机接收数据同样适用----不骗人的,自己 ...
- STM32 ~ USART接收不定长数据
IDLE中断什么时候发生? IDLE就是串口收到一帧数据后,发生的中断.什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据. ...
- STM32串口空闲中断
串口初始化 #include "usart5.h" vu16 UART5_RX_STA=0; char UART5_RX_BUF[UART5_REC_LEN]; u8 UART5_ ...
随机推荐
- 空气质量管理系统+SSM(Spring+SpringMVC+Mybatis)+前后端分离总结
作者:故事我忘了¢个人微信公众号:程序猿的月光宝盒 点我进SSM演示地址 1.目录结构: 2.需要注意的地方 2.1在WEB-INFO下新建 2.1.1 springMVC-servlet.xml & ...
- java JDK安装包的获取与安装
Java JDK 安装包获取和安装: JDK 1.8.211 官网下载地址 https://www.oracle.com/technetwork/java/javase/downloads/jdk8- ...
- 【前端】之CSS3基础知识
CSS3 私有化前缀 考虑到CSS3的兼容性问题,某些属性需要添加浏览器的私有化前缀 几种主流浏览器的私有化前缀如下: Chrome.Safari:-webkit- Firefox:-moz- IE: ...
- java switch语句 要点注意
1.格式 switch(表达式){ case 常量1:语句1:break: case 常量2:语句2:break: case 常量3:语句3:break: .... case 常量n:语句n:brea ...
- Sql like模糊查询 区分大小写
Sql模糊查询,Like默认是不区分大小写的 使用Like时,怎么支持大小写呢? upper.lower,只能模糊所有的内容,不能区分内容中的大小写. sqlite数据库对text字段默认是大小写敏感 ...
- 在ubuntu18.04中安装opencv_contrib-3.2.0采坑教程
由于最近要在OpenCV3中使用SIFT和SURF特征提取,而自从OpenCV2升级到OpenCV3版本后,SIFT.SURF等这些算法都被移出opencv默认项目库,而被放到叫opencv_cont ...
- 转:关于JAVA项目中CLASSPATH路径详解
在dos下编译Java程序,就要用到classpath这个概念,尤其是在没有设置环境变量的时候.classpath就是存放.class等编译后文件的路径. javac:如果当前你要编译的Java文件中 ...
- luogu P1832 A+B Problem
题目背景 ·题目名称是吸引你点进来的 ·实际上该题还是很水的 题目描述 ·1+1=? 显然是2 ·a+b=? 1001回看不谢 ·哥德巴赫猜想 似乎已呈泛滥趋势 ·以上纯属个人吐槽 ·给定一个正整数n ...
- SI4432和SI1212通讯
SI4432是美国Silabs研发的一款工作频段低于1GHz的高性能射频收发芯片,属于其EzRadioPro系类.在国内已经广泛应用于工业.科研.医疗(ISM)以及短距离无线通信设备(SRD),其输出 ...
- 配置React Native的开发环境
本文转载自:http://mp.weixin.qq.com/s?__biz=MzIxNjEzNjUzOQ==&mid=402020148&idx=2&sn=ccad14a919 ...