使用背景:

之前在做项目的时候,串口接收的数据要及时进行处理,虽然采用了自定义的串口协议,但是协议的包尾只有一个字节,经常判断不准数据是否接受完毕,所以就采用计时器+串口的方式来判定串口是否接受完成。

核心思想

根据波特率来计算接收一个字节所需要的时间,当超过这个时间没有收到数据,则表明这一帧数据已经接受完毕

实现方法

串口中断函数接收第一个字节之后,开启定时器计数。接受下一个字节的时候清空定时器计数。如此,当没有数据接收后,计时器无法清零,当计时器计数超过设定的数值之后,触发定时器溢出中断,此时数据即接收完毕

Created with Raphaël 2.2.0开始定义 计时标志位、完成标志位接收数据,清空计时标志位开启定时器,计时标志位增加串口未收到数据?计时标志位增加,超过设定值接收完成标志位置1,处理数据结束yesno

下面是代码:

  • 1、为了方便,定义一个结构体,主要内容如下所示:
typedef struct _UART_FLAG_STRUCT
{uint8_t UART1Flag;//数据接受完成标志uint8_t UART1String[160];//最大长度,自定义uint8_t UART1Counter;//收到的数据长度,计数作用uint8_t usart1_start;//接收开始,定时器计时启动uint8_t usart1_counter;//定时器计时次数
}   UartFlagSt;UartFlagSt UartFlagStC;
  • 串口配置:
/************************************************
| 关键词 |USARTInit(u32 band)
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |u32 band,配置波特率- - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |None- - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |初始化串口1,使能中断
**************************************************/
void USARTInit(u32 band)
{GPIO_InitTypeDef  GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef  NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin  =GPIO_Pin_9;//sendGPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//receiveGPIO_Init(GPIOA,&GPIO_InitStructure);USART_InitStructure.USART_BaudRate = band;USART_InitStructure.USART_HardwareFlowControl = 0;USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_StopBits =USART_StopBits_1;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_Init(USART1,&USART_InitStructure); //初始化串口1NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启串口接受中断USART_Cmd(USART1,ENABLE);                    //使能串口1
}/************************************************
| 关键词 |UART1_SendString(uint8_t* str,uint8_t counter)
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |uint8_t* str    发送的数据
|   |uint8_t counter 发送数据的长度- - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无- - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |串口1发送函数
**************************************************/
void UART1_SendString(uint8_t* str,uint8_t counter)
{for(int i = 0;i<counter;i++){USART_SendData(USART1,*str);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);str++;}
}
  • 定时器配置:
/************************************************
| 关键词 |TIM2_Init(u16 arr,u16 psc)
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |u16 arr 周期,u16 psc 预分频
| - - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无
| - - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |定时器初始化
**************************************************/
void TIM2_Init(u16 arr,u16 psc)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_TimeBaseStructure.TIM_Period = arr; // 周期 72-1 maxTIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE );NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE);
}
  • 串口中断函数:
/************************************************
| 关键词 |USART1_IRQHandler
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |None- - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |None- - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |1、消除错误;
|   |2、将数据接受至结构体中的数,
|       |并启动UartFlagStC.usart1_start = 1;UartFlagStC
|       |.usart1_counter = 0;定时/刷新功能。
**************************************************/
void USART1_IRQHandler(void)
{/*****此段注释代码为消除各种串口错误,在串口环境很差的情况下可以直接将此段代码取消注释,基本可以保证串口代码正常工作******if((USART_GetITStatus(USART1,USART_IT_ORE) == SET)||(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET)){USART_ReceiveData(USART1);USART_ClearITPendingBit(USART1,USART_IT_ORE);USART_ClearFlag(USART1,USART_IT_ORE);}if((USART_GetITStatus(USART1,USART_IT_NE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET){USART_ClearITPendingBit(USART1,USART_IT_NE);USART_ClearFlag(USART1, USART_FLAG_NE);}if((USART_GetITStatus(USART1,USART_IT_FE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET){USART_ClearITPendingBit(USART1,USART_IT_FE);USART_ClearFlag(USART1, USART_FLAG_FE);}if((USART_GetITStatus(USART1,USART_IT_PE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET){USART_ClearITPendingBit(USART1,USART_IT_PE);USART_ClearFlag(USART1, USART_FLAG_PE);}
****************************************************************************************************/if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){if(UartFlagStC.UART1Counter<160)//设定的数组最大为160,要小于这个数,防止溢出{UartFlagStC.UART1String[UartFlagStC.UART1Counter] = USART_ReceiveData(USART1);//将数据存到数组里面UartFlagStC.UART1Counter++;//收到的数据个数+1UartFlagStC.usart1_start = 1;//定时器开始工作UartFlagStC.usart1_counter = 0;//清空定时器计数}else UartFlagStC.UART1Counter = 0;//如果接受的数据超过设定值,则清空接收值,防止数据溢出USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}

可以看到,当有数据进来的时候,UartFlagStC.UART1Counter不断自增,数据将会依次存入UartFlagStC.UART1String[]数组中。
同时,UartFlagStC.usart1_start计时器开始计数标志位置一(让在定时器中断函数里面自增的UartFlagStC.usart1_counter得以正常增加),同时也将UartFlagStC.usart1_counter清零,以表示有数据接收,防止超过设定值,使得UartFlagStC.UART1Flag置一,错误的提示数据提前接收完成。

  • 定时器中断函数:
    在下面代码提示插入任务的地方插入我们想执行的任务/代码,即可正常使用
/************************************************
| 关键词 |TIM2_IRQHandler
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |无
| - - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无
| - - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |定时器中断函数
**************************************************/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);/**********************/在此处插入执行的任务。/**********************/}
}
  • 关键代码:
    这个代码是我们任务重最重要的部分,主要实现了我们上述流程图的计时/完成任务,之所以将这部分功能单独拆解写成一个函数的形式,主要是为了移植方便,并且在定时器多任务的时候让定时器中断函数看起来更整洁一点。
使用方法:将此函数插入定时器中断函数。
/************************************************
| 关键词 |USART1InsetTimer
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |None- - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无- - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |嵌入定时器中断函数中,串口数据接收完毕后
|        |立刻执行,适用于延时性小、数据量密的场景。
**************************************************/
void USART1InsetTimer(void)
{if(UartFlagStC.usart1_start == 1)//此标志位是在串口接收数据时候会置1{UartFlagStC.usart1_counter++;//定时器计数标志位if(UartFlagStC.usart1_counter >USART_COUNTER_9600)//如果超过波特率为9600时一个字节的所需要的时间,时间计算方法下面有讲解{UartFlagStC.UART1Flag = 1;//接收完成标志位置1UartFlagStC.usart1_counter = 0;//计数值清零UartFlagStC.usart1_start = 0;//计数器启动标志位置0}}
}

在这里,当(UartFlagStC.usart1_start置一后,UartFlagStC.usart1_counter会不断自增(串口中断中会清零此计数位),而一旦超过设定值USART_COUNTER_9600,就会将接收完成标志位UartFlagStC.UART1Flag置一,同时清空定时器技术位UartFlagStC.usart1_counter,并清零计数允许标志位UartFlagStC.usart1_start

  • 处理数据:
    如果处理数据很快的话可以直接放在定时器中断函数里面执行,如果还有比较长的延时函数,或者在执行过程中花费时间太久则可以放入主函数循环中进行处理:
    老规矩,先进行封装一层:
/************************************************
| 关键词 |USART1Hanndle
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |None- - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无- - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |串口1接收数据处理函数,任务若花费时间较长,
|        |可放置于while()循环中,由定时器确定是否执行。
**************************************************/
void USART1Hanndle(void)
{if(!UartFlagStC.UART1Flag) return;/*********执行任务*************/     printf("%s\r\n",UartFlagStC.UART1String);/****************************/memset(UartFlagStC.UART1String,0,160);UartFlagStC.UART1Counter = 0;UartFlagStC.UART1Flag = 0;
}

主函数实现:

/************************************************
| 关键词 |main
| - - - - - - - - - - - - - - - - - - - - - - - -
|  入参  |None
| - - - - - - - - - - - - - - - - - - - - - - - -
| 返回值 |无
| - - - - - - - - - - - - - - - - - - - - - - - -
|  功能  |主函数入口,配置文件,设置时钟,滴答定时器
|        |周期,并开启看门狗
**************************************************/
int main()
{SystemInit();                      //系统时钟72MHzSysTick_Config(SystemCoreClock/1000);        /* SysTick 1 msec interrupts */TIM2_Init(71,999);//1ms中断一次USARTInit(9600);//波特率为9600while(1){USART1Hanndle();}
}

流程/细节讲解

  • 不同波特率延时时间计算:

可能会有人对void USART1InsetTimer(void)中的溢出时间USART_COUNTER_9600有疑问,不知道如何计算,计算方法如下:

首先,1个字符串口包含起始位数据位校验位停止位,其中有些位长度可以自己设定,
这里我们按一个字节传输有1+8+1+1共10位长度来计算。
波特率表示的意思是在1sec内可以传输的位数


接下来就是一元一次方程


设1个字节所用时间为X,波特率为9600,则:

(1∗10)/X=9600/1000(ms)(1 * 10) / X = 9600 / 1000(ms)(1∗10)/X=9600/1000(ms)
解得X ≈ 1.04167 ms = 2(X为整型,必须向上取整!)

X代表的意思是一帧数据传输的时间,意思就是每过X单位时间,即有一个数据接受完毕,同时下一个数据也即将接受。
USART_COUNTER_9600的数值设定为X1
UartFlagStC.usart1_counter则只要在串口中断函数内清空,那么UartFlagStC.usart1_counter就不会超过X,
那么也就不会将接受完成标志位UartFlagStC.UART1Flag置1;
一旦没有数据继续接收,那么UartFlagStC.usart1_counter在中断函数里面将不断自增,直至超过X,此时接受完成标志位UartFlagStC.UART1Flag将会置1。
同时,时间设定要根据波特率的不同要计算不同的数值。

下载地址 :https://download.csdn.net/download/qq_31431301/12287318


  1. 实际使用过程中,一定要将USART_COUNTER_9600的设定值大于X,因为在此方法中,串口中断函数不仅要判断数据接收标志位,还有清零置一的操作,实际工作时间肯定要大于X!,一般取3~5倍X的时间。 ↩︎

串口数据实时处理:定时器+串口 判断串口数据接收完成相关推荐

  1. 大数据实时处理-基于Spark的大数据实时处理及应用技术培训

    随着互联网.移动互联网和物联网的发展,我们已经切实地迎来了一个大数据 的时代.大 数据是指无法在一定时间内用常规软件工具对其内容进行抓取.管理和处理的数据集合,对大数据的分析已经成为一个非常重要且紧迫 ...

  2. Druid:一个用于大数据实时处理的开源分布式系统——大数据实时查询和分析的高容错、高性能开源分布式系统...

    转自:http://www.36dsj.com/archives/28590 Druid 是一个用于大数据实时查询和分析的高容错.高性能开源分布式系统,旨在快速处理大规模的数据,并能够实现快速查询和分 ...

  3. 大数据【Java开发转大数据学习路线分解】(不断细化ing)

    技术点: 将基础的数仓搭建沉淀成自己的实践方法论 提高大数据实时处理平台的稳定性和高效性 提升自己分析定位及解决问题的能力 既懂得平台开发又懂得算法原理和应用 注重风控系统,监控大数据平台安全 前后端 ...

  4. 大数据实时计算工程师/Hadoop工程师/数据分析师职业路线图

    大数据实时计算工程师/Hadoop工程师/数据分析师职业路线图 http://edu.51cto.com/roadmap/view/id-29.html http://my.oschina.net/i ...

  5. java 串口判断报文完整_如何判断串口接收完成一帧数据

    1 使用定时器判断 这种方式建立在两帧数据不可能连续发送的基础上,也是modbus判断帧结束的方式,在接收到第一个字节的时候打开定时器,如果继续接收到数据则更新定时器,在被设定时间内没有接收到数据则定 ...

  6. QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号

    QT入门第十四天 串口通信[QT入门第十四天 串口通信协议+收发数据+波特率+数据位+停止位+奇偶校验+串口识别射频RFID的卡号 第一章 常见的硬件通信接口协议 [1]硬件通信接口协议 [2]使用串 ...

  7. STC15F2K60S2串口通信/波特率设置/通信初始化/发送(接收)一个数据教程

    STC15F2K60S2串口通信 . . 一.先斩后奏,上代码!!!! 先强调:通常我们做单片机与单片机通信.单片机与PC通信.PC与PC串口通信,基本选择方式1的串口通信方式,下面的讲解中我会详细的 ...

  8. 基于串口通信技术——让数码管显示的数据发送给电脑,电脑控制单片机外设——15单片机

    目录 1.使用的单片机为IAPI15F2K61S2 2.使用的外设 3.各个外设的作用 1.数码管功能 2.LED灯 3.蜂鸣器与继电器 4.按键 5.串口通信 4.利用发送单个字符函数 发送字符型的 ...

  9. openmv串口数据 串口助手_Qt小项目之串口助手控制LED

    前言 最近刚学了一点Qt开发上位机,尝试着做个小软件练练手.查找了很多资料,做了一个简单的串口助手,可以实现串口基本发送和接收功能,支持中文显示,还可以控制STM32开发板上的两个LED. 1.软件界 ...

最新文章

  1. PHP中self和static的区别,php中self与static的区别
  2. 设计模式系列1:单例模式(Singleton Pattern)
  3. OpenGL 绘图移动
  4. c++语句switch语句_错误:案例标签不在C中的switch语句内
  5. 好消息,scott的asp.net 2.0数据导航系列全部出版了
  6. 深度优先搜索 - 最短路径
  7. linux查看进程加载的jar包,[Linux] 查看jar包内容
  8. sklearn的系统学习——随机森林调参(含案例及完整python代码)
  9. 移动端轮播图——网易云音乐手机端样式
  10. uni-app常见的生命周期
  11. 计步器java计算月平均,我们行走时,计步器都会纪录,那计步器是怎么计算我们的步数的?...
  12. Word 有时候百度输入法用不
  13. 我的input /不可能这么可爱
  14. 【b站雅思笔记】Charlie有好好学习 - 雅思听力
  15. webpack : 无法加载文件 C:\Users\骚恒\AppData\Roaming\npm\webpack.ps1,因为在此系统上禁止运行脚本。
  16. ACW 835. Trie字符串统计
  17. php正则数字是否靓号,数字靓号正则表达式
  18. ubuntu系统20.4搭建c语言环境,ubuntu 20.04 中文环境和英文环境切换
  19. win7家庭版和旗舰版区别_WIN7_64位系统安装 MicroWIN_SP9后没有PC-PPI通讯协议怎么处理?...
  20. Android之后,Google找的未来是给一切加上人工智能

热门文章

  1. 蓝色简约大气公司简介企业介绍产品宣传ppt模板
  2. 共话机器翻译新风向,第二届小牛翻译论坛启幕在即
  3. 计算机扫涂卡答案什么样就合格,中高考电脑阅卷流程曝光!不避开这些“坑”,就要白白丢分了...
  4. 网站安全漏洞检测报告年度安全分析
  5. GDUT 排位赛2.19 D
  6. 20种运放典型电路总结,电路图+公式,通俗易懂,几分钟搞懂
  7. 数电实验:用74LS160/74LS161设计6进制,96进制,140进制计数器
  8. 机器学习算法拾遗:(七)隐马尔科夫模型(前向后向算法、鲍姆-韦尔奇算法、维特比算法)
  9. android ratingbar 大小,Android 调整Ratingbar中的小星星大小
  10. 学前端没这些工具怎么行