当前位置: 首页 > news >正文

串口超时处理原理及实现

原文:http://www.openedv.com/posts/list/11804.htm

原理大部分串口都是基于一字节、一字节传输,检测到特定的字符(比如换行或者空格)才判定一帧数据结束,这样的传输机制在自己调试时可以用,但实际运用其实用的很少,最大的坏处是cpu会“死等”特定字符,另外,若是由总线干扰出现的特定的字符,若程序同样判定帧起始(或停止)符,这明显是错误的。我们需要一帧一帧的传输,这样,就需要字节超时处理了,即只要字符与字符之间间隔超过一定的时间,那么就判定字符是一帧的结束。
      大部分教程没有提到可能是为了降低大家的学习难度,这里提供一份参考代码,STM32的所有串口都加进去了,全部测试通过,也经过实际项目(非精确严格要求)的检验。若不需要用到某些串口,只需要把app_conf.h,文件里的相关宏关闭即可。比如,不需要用到串口3、4、5,那么只需要注释掉app_conf.h里的USING_USART3、USING_USART4、USING_USART5即可。同时,串口相关配置波特率、缓冲区大小等等也在此文件,大家看看注释就明白了。默认全部开启,字节超时时间可设,例如USART1_RECEIVE_OVERTIME这类名字的宏。最后一个配置是printf 输出串口的选择,默认为串口一。
      这个实现五个串口公用一个systick,不需要每个串口需要单独的硬件定时器;二级缓冲区以加大吞吐量,一个接受缓冲区,接受缓冲区负责接收;一个帧准备缓冲区,帧准备缓冲区有一个准备好的标志USART_ready_buf_ok,应用程序可检测这个标志看是否有一帧数据存进来。还有一个帧长度USART_ready_buf_len以指示准备好的缓冲区的帧长度。代码先发放出去,大家先试试,稍后有时间在说说具体事怎么实现的,年底比较事多,大家见谅。
    对了,测试代码是简单地回发,即5个串口回发自己收到的数据帧,你可以做个测试,若字节超时时间设的比较长(在app_conf.h文件的USART1_RECEIVE_OVERTIME宏),那么你在串口调试助手里不停地点发送,等到你停下来,才会回发你刚发的内容。
     还有,这个工程LED灯部分也有些意思,除了亮状态和灭状态,还有第三种状态————闪烁,而且闪烁的次数与闪烁的间隔软件可配置。若有兴趣大家也可以试试,配置也在app_conf.h里,当然,你的板子与我的板子肯定不一样,那么"AunonBoard_led.h"里的各个LED_PORT和LED_PIN宏定义也要修改。 

(二)、软件定时器

      软件定时器是由一个硬件定时器实现的多个定时器,在定时不要求非常精确的情况下可以用到,比如串口字节超时等等,特点是需要多少个定时器就可以拥有多少个定时器,不受硬件限制。这里的软件定时器源代码soft_timer.c和soft_timer.h两个文件,在不做任何改动的情况在ARM和51下测试通过,其他平台未知,(没有相应的硬件平台测试)。先放代码和工程,后面有时间在细说。
      使用方法(平台无关):
      (1)硬件定时器初始化,中断配置什么的不要忘了,具体怎么实现无所谓,只要能不停地周期性中断(stm32的systick最合适了),中断间隔也是软件定时器的最小能分辨的间隔,然后将软件定时器刷新函数void timer_periodic_refresh()--没有参数--加到你的定时器中断服务函数里。
       (2)定义一个定时器,如:struct soft_timer timer,struct soft_timer 是软件定时器的结构体,定义在soft_timer.h文件中,timer是你的定时器的名字。
      (3)软件定时器链表复位,soft_timer_list_reset(),无参数;  
      (4)然后添加刚才你定义的定时器timer添加到定时器链表add_timer(&timer,timer_over_proc,time_count),第一个参数是你要添加的定时器结构体的指针,第二个参数是定时间到了你要调用的超时函数,第三个参数是定时时间,单位是你的周期性中断时间间隔。
      (5)开始启动定时器, start_timer(&timer),参数是你要启动的定时器的结构体指针。
      这样,等超时后,就会自动调用timer_over_proc()函数,(像不像我们自己实现的软件定时器中断?)
      注意,这个实现是一次性定时器,即一次超时后不再触发,若需要周期性触发,那么可以再timer_over_proc()函数里面重装初值--reload_timer(&timer,time_count),第一个r参数是待启动定时器结构体指针,time_count是重装值,单位依旧是你的硬件定时器中断时间间隔,然后再启动定时器start_timer(timer)即可。
      若还有使用上的问题,可参考两个测试工程的用法,一个stm32,一个51。STM32测试工程以systick建了3个软件定时器,分别以0.3s,0.5s,0.7s的时间间隔闪烁3个led灯。C51工程以定时器0建立3个软件定时器闪烁led灯。具体代码分析,后面有时间在细说。
      STM32的测试结果视频和代码见附件。

(三)软件定时器实现串口字节超时处理

      前面第一个工程是以前的一个小项目上用的,原封不动地发了上来,结构除了自个儿比较清晰外可能不会有人知道我再干什么。。。。。。所以写了这个软件定时器的版本,并做了充分的中文注释。欢迎指正!

      同前面的一样,每个串口用了两个缓冲区,一个接收缓冲区,一个准备缓冲区。当串口中断函数里面收到一个字符时,放入接收缓冲区,并开始启动定时器,若在超时范围内没有收到下一个字符,超时函数会被自动调用,将接收缓冲区的数据转移至准备缓冲区内,并将准备标志置位,以供应用程序查询。
       测试工程里面使用了串口1、2,简单回发收到的数据帧,为了对比,这两个串口的字节超时设置不一样,一个50ms,一个500ms,所以串口2在发送比较快的情况会被认为数据没有结束,直到至少停500ms才会回发。在我的板子上测试通过,由于手上没有串口3、4、5的板子,所以就没添加,需要的朋友可自己添加一下,还是比较容易的。
        写完了,不知道有人试过我这个软件定时器么?以满足下我小小的虚荣心啊,哈哈哈。

(四)

在串口通信过程中 超时处理几乎是必须的。 
如果用过Modbus协议,它的判断结束的唯一条件就是超时。

本人应用的部分代码 共享下:
//检测超时函数 供定时器中断调用-1ms一次
// t为超时时间
__inline void Chk_TimeOut(u8 t)
{
if(!uartRMsg.rcOK && (uartRMsg.rcIndex>=8) && (++uartRMsg.rcS >=t) ) //超时之后,开始重新解码
uartRMsg.rcOK = true;
}
//接收函数, 供USART中断调用
__inline void Recieve_MSG(char ch)
{
uartRMsg.rcS = 0; //如果接收到数据,则清除超市检测计数,
if(!uartRMsg.rcOK)
{
uartRMsg.Buf.buf[uartRMsg.rcIndex ++] = ch; //赋值
if(uartRMsg.rcIndex>=8 && uartRMsg.Buf.msg.cmd!=16)//其他条件成立的时候也可进入解码程序<此程序为Modbus应用,可改为接收到结束符等>
uartRMsg.rcOK = true;
}
}

//供主函数调用
void MSGTransfer(void)
{
if(!uartRMsg.rcOK) return;
//......数据解析部分
}

 

 

转载于:https://www.cnblogs.com/itloverhpu/p/3387513.html

相关文章:

  • ACM北大暑期课培训第一天
  • Delphi窗体创建释放过程及单元文件小结(转)
  • Linux部署zabbix3.4 结合钉钉智能报警
  • 学生分数排序
  • zabbix3.4上简单web监测功能测试
  • linux系统man查询命令等级与意义
  • 关于ES6的Promise的使用深入理解
  • P1065 津津的储蓄计划
  • 2018年 7月总结8月计划
  • Proteus仿真_01、 8086 IO译码仿真
  • CentOS 7之Postfix部署系列 (二) CentOS网络设置
  • AJAX PHP 请求实例
  • 使用Formik轻松开发更高质量的React表单(二)使用指南
  • HDU-3874 Necklace 线段树+离线
  • topcoder srm 590 div1 (max_flow_template)
  • @angular/forms 源码解析之双向绑定
  • @jsonView过滤属性
  • 4个实用的微服务测试策略
  • JavaScript 一些 DOM 的知识点
  • JAVA之继承和多态
  • Laravel Mix运行时关于es2015报错解决方案
  • PAT A1050
  • PHP面试之三:MySQL数据库
  • Promise初体验
  • Spring Boot MyBatis配置多种数据库
  • 机器学习中为什么要做归一化normalization
  • 基于遗传算法的优化问题求解
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 你真的知道 == 和 equals 的区别吗?
  • 前端技术周刊 2019-01-14:客户端存储
  • 如何设计一个微型分布式架构?
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • # centos7下FFmpeg环境部署记录
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (1)(1.13) SiK无线电高级配置(五)
  • (2020)Java后端开发----(面试题和笔试题)
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (二)c52学习之旅-简单了解单片机
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)UDP基本编程步骤
  • (转)LINQ之路
  • (转)为C# Windows服务添加安装程序
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .net 简单实现MD5
  • .NET 使用 XPath 来读写 XML 文件
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • @Bean, @Component, @Configuration简析
  • @EnableConfigurationProperties注解使用
  • @Query中countQuery的介绍
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • [ 英语 ] 马斯克抱水槽“入主”推特总部中那句 Let that sink in 到底是什么梗?
  • []串口通信 零星笔记