登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

易拉罐的博客

心静自然凉

 
 
 

日志

 
 

转 如何DIY一个属于你的超声波测距传感器三:程序的构思和设计  

2016-07-12 21:58:36|  分类: 传感器 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
转  如何DIY一个属于你的超声波测距传感器三:程序的构思和设计 - 易拉罐bb - 易拉罐的博客

二、需求分析
    测距传感器的核心功能是测量距离,但当其用于不同场合时,会有许多不同的需求。
    如果是传统的传感器概念,只需将“非电量转换为便于测量的电量”即可,这是一个比较通俗也基本正确的定义,“便于测量的电量”通常为:直流小电流、小电压以及方波等。
    这类传统传感器给系统带来了不少“麻烦”,因为其输出的所谓“便于测量的电量”只是物理上的,充其量达到“可测”而已,由于其输出的不统一、不灵活,甚至有些“粗糙”,使得系统不得不付出一些开销去弥补之。
    就拿GP2D12来说,其输出是直流电压,可与距离的关系是非线性的,且是反比例,输出还是非连续的,就这三个特征就足以让系统耗费不少周折才能得到想要的距离值。
    还有很多类似的例子,如热电偶温度测量传感器、光敏传感器等,在此就不一一枚举了。

    从系统设计的角度考虑,最好是传感器将所测量的量转换成数字信息,系统不必再去理会这些“底层”的处理,专心于功能的实现。如同现在的PC操作系统,有统一的设备接口,系统级应用是“与硬件无关”的,设备的差异由各设备厂家通过驱动程序实现统一。
    以往由于技术和成本的限制,为了节省开支,将很多功能都交给了主控系统完成,形成所谓“树形”架构,只有“主干”是有智能的,其余都是“末梢神经”,只具备最低级的信号采集能力,也就是传统传感器的角色。
    随着单片机的功能提升、价格下降,新的构架方式逐渐显现:一个系统中,每个部分都自成体系,主控只是负责策略、协调,各个独立的功能模块“自行其事”。这就是“分布式”系统。
分布式系统概念的普及,催生了智能传感器的需求。
    所谓“智能传感器”,至少有以下特征:
1) 能够将被测量转换为数字值,而非简单的模拟量;
2) 能够根据要求独立完成测量;
3) 能够通过数字通讯接口接受命令、输出数据。
    具备此特征的传感器已有很多,有些已制成IC,如常见的温度传感器 18B20。
    智能传感器除了降低了系统的软硬件开销外,附带的一个好处就是便于传送,传统传感器的输出信号传输时的“干扰”和“衰减”是最令设计者头痛的!
    因此,智能传感器是未来的方向。实际也是如此,读者可搜索一下新兴的MESM(微机电系统)传感器,不论是两轴、三轴加速度,还是陀螺仪等,新产品几乎都是I2C、SPI等数字总线接口。

所以,我们这个超声波测距传感器也是按智能传感器理念设计的。
    因本篇只是示范性软件设计,没有特定的应用场合,所以只好就测量本身来定义需求:
    在性能上,测量关注两方面:一是得到数据的速度,二是数据的可靠度。
    在功能上,测量有两类:一是不断的测量并输出结果,二是触发后开始测量。

    所以至少应满足上述需求:
1) 可设置为快速测量模式,让系统最快得到测量数据;
2) 可设置为精确测量模式,返回给系统比较可靠的数据;
3) 可以设置为连续测量模式,不断提供给系统测量数据;
4) 可以按照系统请求开始测量,返回即时数据。

三、功能设计
    按上述需求,传感器的功能设计如下:
传感器上电处于待命状态,等待系统命令做以下操作:
1) 可以支持连续测量,并存放最近8次数据,测量周期可以由系统设置。在此状态下,系统根据需要读取数据。
2) 可以支持连续测量,并且将每次的数据返回给系统,由系统进行需要的后处理。
3) 可以接受系统命令,返回待命状态。
4) 可以支持单轮测量,即系统发出命令通知传感器,采集几次数据,传感器可做基本的数据处理,如取平均、剔除最大最小值,完成后返回这组数据后,恢复到待命状态。

    此外,为了便于调试,增加读、写单片机内存的功能。

四、详细设计
4.1 题外话
    看懂别人软件是件相当困难的事,即使那些较正规的、有完善文档的项目,也不是十分轻松,因为记录下来的只是结果,思维的过程无法再现,而读者有时更多关注的是如何“想到的”,特别是初学者!
但描述软件的构思过程也并非易事!
    前期我写过的“圆梦小车StepbyStep”系列文章中,尝试通过一步步“搭建”的方式来引导读者理解思考过程,并在程序中特别注释了每一步所增加的内容,程序中排版顺序都放弃了逻辑关系而“屈从”于“搭建”的顺序,可似乎收效甚微!?猜测是没有交代最基本的思路所致,因为即使是每一步都很具体,读者仍会问:怎么来的?为何?
    本文不是技术论文,其目的是帮助有意学习者实现自己的愿望,所以本篇尝试简述一下思考方式,看是否对学习者有帮助,但声明一点:此乃个人观点,并非“宝典”,不保证正确,仅供参考!

4.2 程序构建思考过程
    我开始涉足单片机编程时,由于只有汇编语言可用,且编译环境较弱,变量名、标号限制较多,所以那时很讲究使用流程图来表达程序的构思,因为从汇编代码上看懂程序实在困难,毕竟那是为机器思维服务的逻辑顺序,与人理解所需的表述相差甚远。
    当我转换到C语言编程时,开始还保持着画流程图的习惯,但逐渐觉得有些多余,因为C语言的自注释性(即语句和变量名的组合表达方式已接近人的理解需要)以及编译环境的提升,配合各类几乎无限制的定义手段,使程序本身就可以方便的为人所理解。如今编辑器也在优化,读者可以尝试一下 UltraEdit,其“折叠”、“展开”功能十分有助于理解程序的思路。所以渐渐的放弃了流程图。但还维持着按实现过程来构建程序的习惯。
    自从我尝试编写PC环境下的VC程序后,逐渐构思习惯有了很大变化,读者如果没有尝试过,可以参照“圆梦小车StepbyStep之二”做一次,然后再用类似的方式构建几个自己想象的题目,一定会有所感受!

    在VC中构建一个程序,其过程大致如下:
1) 设计功能 —— 这是机器所不能代替的,靠你的创造力实现之,需要用文本记录之;
2) 构思界面 —— 这就是VC为你提供的方便了,根据功能和工具可以实现你所要的界面
3) 变量定义 —— 构建界面时VC会自动生成变量,根据功能对这些变量进行类型定义;
4) 编写处理程序 —— 基于界面所产生的操作(按钮等)编写对上述变量进行处理的程序,这是你的智慧展示的空间。

    在PC上编程(默认是Windows下),由于很多事情都由Windows操作系统帮助做了,所以在VC环境下编程确实比较轻松,只需关注和功能相关的事,无创意的琐碎事务都由系统和VC处理了。
    单片机中虽没有这么“美”的事,但是这种构建过程倒是改变了我,我现在基本也是按此思路去构建一个程序,只不过一些VC帮助自动生成的过程由自己完成了。

    首先,是确定所做的东西要完成哪些功能,这是基础。在需求分析和概要设计阶段基本搞定,在详细设计的开始处将其具体化,用技术术语表达之。
    之后根据这些功能定义相应的变量。如需要记录 8 次测量数据,就需要有一个 8元的数组,同时要有存放指针和取数指针(注意:此处所述“指针”,非C语言的指针,是指数组的下标,只是个人表达习惯而已,下同),以便于对数组操作。
根据硬件的性能和需求确定数组的类型,是用整型还是字节型等;因为我定义的测量范围为5米,即使用cm为单位字节型也不够,所以用整型。因为不可能有负数,所以用无符号整型。数值表达范围大了,将单位提高到mm,虽然不一定需要,但不增加工作量,感觉却好多了 : P 何乐而不为?
    在定义变量的同时,定义一些与变量处理相关的常量,一方面为了程序的可读性,同时也是为了日后便于修改。如现在设计是保留最近8次数据,但日后也许需要更多或较少,将数组的单元数声明为符号常量 —— DATA_SAVE_NUM,定义为8,需要时只需修改此处定义即可,不用在程序中“遍地”去找,遗漏一个就形成一个bug!
    在构思服务于功能的变量时顺便考虑如何处理之,还是拿测量数据存放为例。既然需要存放最近N次的数据,可以这样处理:存满8个之后依次向前移,覆盖序号最小的单元,腾出序号最大的存放新数据。这样处理效率太低,常用的方式是环形缓冲区的概念,即将数据存放区看成是个首尾相接的环,存放数据时指针不断“加”,到尾时自动环到首,不用任何数据搬家操作,而取数也是同样,只是从存数指针向回“减”,到首时自动环到尾。
    基于这个思路,自然2个指针变量的需求就产生了。这两个指针据需要能够“加”到尾环头,或“减”到头环尾。如凭直觉,就用比较的方式判断,每次运算都作一次检测,虽能完成,但似乎有些繁琐。考虑一下有无更好的方式?如果还记得二进制的基本运算,就可以理解我为何在程序中设计环形数据存放区的时候均要求单元数是2的幂,即4、8、16……

    按上述方式,可以依次定义出功能用的变量。之后就要结合运行定义一些处理用的变量,这就是VC和操作系统可以帮你完成的那部分。

 嵌入式系统(或俗称“单片机系统”)有以下几个概念:
1)死循环
    嵌入式系统是连续不断运行的,所以必然有一个“死循环”,一般用以下程序实现:
While(1)
{
……
}
2)标志驱动
    对于嵌入式系统,小的应用一般没有操作系统,内存不够,开销也大,所以需要自己设置一些标志,以实现类似于PC上的“消息驱动”。
    在上述循环中,程序顺序检测各个标志,根据标志做相应的处理。标志的建立一般由中断完成,或者是中断处理后产生。
3)时基
    多数程序都有按时间处理的需求,如延时、定时查询、周期测量等等,所以一定有一个时基,由定时器中断产生,建立标志。在PC中也有类似的机制,DOS时代的PC我知道系统有一个18.2ms的时钟信号,现在的PC不太清楚了 : (
    我设计系统通常使用1ms时基,因为多数处理不会小于这个间隔。由此,要设计一个1ms中断标志。所有与时间相关的处理都安排在1ms中断标志建立后的处理中。
4)状态
    一个功能的实现,往往不能“一蹴而就”。
    就拿超声波测距来说:首先得发出超声波,之后等待回波,等待过程中要根据时间控制增益,逐渐增大,以弥补声波随距离增加的衰减。收到后再计算,至此才能得到一个测量结果。
    按照声波速度,2m距离约需要12ms(来回4m)。如果程序设计成顺序依次处理模式,直到完成后再处理其它的任务,则大概通讯成功率只有一半了,因为MCU的处理给测距过程独占了。
    也许有人说:可以用中断来处理。
    在此,顺便说一下:在成熟的程序中,中断处理中尽量少安排操作。因为一是会由于中断内、外同时处理相同变量产生错误,除非你设计了严格的“闭锁”机制;二是降低了其它中断的响应速度,也许会导致程序性能下降;如需要精确捕捉脉冲,则会由于延时响应而降低精度。优先级机制虽可弥补,但那更容易导致数据错误,而且需要更多的堆栈空间。
    为了避免上述问题,通常使用状态来标注一个功能做到了哪一步?设置一个状态变量,记录功能实现的进程,每次轮回时根据状态做相应的事,把能做的做完后立刻退出,把MCU的处理能力交给别的任务。
    这就是“分时”处理的概念,只不过分时是由自己写的程序所调度,而非“操作系统”,随着所做的项目复杂度加大,你会感到“操作系统”的重要!
而利用状态控制大概就是所谓的“有限状态机”,这在嵌入式系统中是常见的。
    具体实施时,采用螺旋式步骤完成程序。
    先构建一个最基本的程序框架:(这一步在VC中,建立MFC工程时就自动完成了,可在单片机上,得自己为之 ? )

 转  如何DIY一个属于你的超声波测距传感器三:程序的构思和设计 - 易拉罐bb - 易拉罐的博客
 图 1 程序主框架

(使用图片表示并非想阻碍拷贝,只是想借用编辑器的彩色表示方式,更直观,下同)
    从上图可以看出,程序基本上由初始化和死循环组成,死循环中设计了三个处理:时基、通讯、测量(工作),这三件事因为需要同时处理,所以设计在循环中依次得到MCU的处理时间。读者可回忆一下流程图格式,有了这个还需要吗?
然后逐步充实,每构思一个功能:
1) 定义相关变量
2) 在init_var()中初始化;
3) 根据需要定义相关常数;
4) 涉及硬件,在init_hardware()中初始化硬件;
5) 构建处理方法,也就是函数,完成功能;
6) 如完成需要等待外部条件,则定义状态变量,并定义状态,转换条件;
7) 处理时如涉及定时,则设置计时器,建立相应标志,在时基处理中添加处理;

    不一定要一个螺旋就上到“顶”,可以逐步添加,首先是实现功能必备的处理,其次是防护出错的保护性处理。程序将逐步变得完善、可靠。最终得到的程序自然比较复杂,而读程序者通常看到的是这个版本,所以自然费解!

    读者如果接触过编程,一定知道“面向对象”,这个概念开始时我很不理解,汇编程序写多了,思维更“接近”机器。在编写几次VC程序后,觉得“面向对象”倒是一个不错的思维方式,即从你要做的事情开始思考,它要完成什么?它有什么特征?它怎么去做?可以类比一下,上述功能定义说明了它要完成什么?变量定义说明了特征,而处理方法指明它如何去做。也许不太贴切,但是自我感觉有不小的帮助。

本开源项目地址:

http://open.robotsky.com/chuanganqi/

配套套件索取: http://bbs.robotsky.com/thread-3030-1-1.html







 
  评论这张
 
阅读(326)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018