CSK.Blog--个人原创Weblog

Arduino-Lite, 轻量级且高性能的AVR固件库

一篇拖了2年多的文章...目前首发于我们RoboPeak团队网站,可以过去查看中英文版本以及最好的阅读体验。这里只是缩略版本。

RP Blog文章:

Arduino-Lite, RoboPeak使用的高效轻量级AVR库(1) -- 介绍篇

http://www.robopeak.net/blog/?p=42

Arduino-Lite, RoboPeak使用的高效轻量级AVR库(2) -- 使用篇

http://www.robopeak.net/blog/?p=70

Arduino-Lite开发参考文档

http://www.robopeak.net/blog/?p=107

我blog早先时候写的一篇相关文章:

说说我从去年9月份开始的AVR单片机学习和使用

http://www.csksoft.net/blog/post/255.html

Arduino-Lite是我们RoboPeak机器人团队开发使用的轻量级高性能AVR固件库,我们机器人小车的AVR系统中全部采用Arduino-Lite所开发。目前已经将他全部开源了,希望对各位有所帮助。

Google Code项目主页: http://code.google.com/p/arduino-lite/

简介

Arduino固件库提供了非常易于初学者使用的函数库,可以很快对AVR进行开发,即使是专业人员用这个库也会觉得便利。同时他对引脚的抽象编号也有利于固件代码的移植。不过这个库相对体积过大,性能也不理想,所以难以在产品领域使用。这就是我们开发Arduino-Lite的原因所在。

Arduino-Lite包括了2大部分

1) 改良和扩充的Arduino固件库,

提供大多数情况下大于50%的代码尺寸缩小和10倍以上的运行速度加速。同时支持更多的AVR芯片和时钟频率。使得这个库能可被用于对成本和性能敏感的产品应用中

2) 自我包含的编译开发环境

不需要安装任何第三方程序即可完成AVR的开发、下载工作。Arduino-Lite自带了avr-gcc(WINAVR),不过创建新工程非常简单,基本的文件夹复制操作即可自动产生新的工程,也不用编写和修改Makefile脚本,都是智能进行的

 

这里先给出2个直观的例子:

实现PWM输出功能:

代码类型 代码 执行所需的AVR时钟周期
Arduino analogWrite(9, pwm_val); ~80
Arduino-Lite ANALOG_WRITE(9, pwm_val); 2
Arduino-Lite PWM_SET(9, pwm_val); 1
avr-gcc直接寄存器操作 OCR01 = pwm_val; 1

可以看出,Arduino-Lite提供的函数和使用方式与Arduino非常接近,但效率上他有和直接去操作AVR寄存器性能一致。

编写一个PWM驱动LED闪烁程序的代码尺寸对比: 

  • Arduino库产生的最终代码: 2048 byte
  • Arduino-Lite产生的最终代码: 100byte

对于AVR ISP编程器的支持,Arduino-Lite支持Arduino方式的串口下载,也支持HIDboot的USB-HID下载,同时原生支持我们RoboPeak做的开源USB免驱动编程器RoboPeak USB Connector进行AVR芯片编程(见http://www.robopeak.net/blog/?p=133)。

特点和适合使用的场合

同样使用C++/C编写且基于avr-gcc编译器。但与Arduino固件库相比,Arduino-Lite有如下优势。

非常轻量级

使用Arduino-Lite的固件往往比使用Arduino固件库小了50%以上.

高效率

许多Arduino-Lite提供的与Arduino固件相同功能的函数,例如digitalWrite之Arduino-Lite版本:DIGITAL_WRITE仅使用一条AVR指令实现.

支持更多的AVR芯片和时钟频率

除了 Atmega8(A), Atmega168(PA), Atmega328(PA), Atmega1280 芯片外, Arduino-Lite 也支持以下芯片: Attiny2313, Attiny26, Atmega48(PA), Atmega88(PA)

对于时钟频率, Arduino-Lite 支持从1Mhz 至 20Mhz 的频率范围.

 

除此之外,Arduino-Lite还有如下特点:

 

自包含,无需依赖任何第三方工具/编译器/库

只要系统中带有文本编辑器,即可直接用Arduino-Lite进行AVR固件开发、编译、烧录等动作。Arduino-Lite自带了avr-gcc(WINAVR)以及相关的函数库。

灵活易与整合的编译环境,基于Make,但无需用户编写或是生成任何Makefile

创建一个新的Arduino-Lite工程,最简单的办法是将模板工程文件夹解压缩并重命名为希望的工程文件。并将相关的源代码以任何目录结构放置于工程目录下,Arduino-Lite就能编译项目,无需用户修改/编写/生成Makefile.

 

我们认为Arduino-Lite适用于以下领域

  1. 对固件代码尺寸/器件成本敏感的场合,比如需要使用Attiny或者Atmega48等小Rom尺寸的芯片的场合
  2. 对固件执行速度有较高要求的场合,比如对实时性要求较高的工控领域和机器人控制器领域
  3. 喜欢使用Make脚本、自定义IDE等的开发环境
  4. Arduino/AVR爱好者,且有一定的编程经验,不满足于Arduino IDE环境,期望更高效的固件库
  5. 希望将Arduino的简易开发特性运用于Attiny, Atmega48以及不同时钟条件下的硬件环境
相比Ardunio的固件库,Arduino-Lite或许不适合
  1. 不喜欢命令行界面、Make编译脚本的人群 (我们也有计划将Arduino-Lite支持Arduino-IDE)
  2. 希望直接使用Arduino各类第三方库,急需应用的情况

实现细节

省略,请参考RP Blog原文:http://www.robopeak.net/blog/?p=42

 

如何使用

 可以在Google code上下载已经打包好的zip包,或者直接checkout 源代码使用,具体见:http://www.robopeak.net/blog/?p=70

 

开发和配置过程都异常的简单,基本都是鼠标操作即可搞定,也无需以来别的软件、库。

 

下面有段使用RP USB Connector进行AVR烧录的视频:

(http://www.tudou.com/programs/view/Den9uh3HTHE/)

 

函数手册

 略文,详见:http://www.robopeak.net/blog/?p=107

Arduino-Lite新增函数/宏

基本IO引脚控制

PWM输出控制

模拟量采集(ADC)

睡眠和延迟

中断处理和管理

串口通讯

调试功能

文本格式化

太阳能供电无线气象站及信件检测器和AVR以太网终端的设计制作

自己的概括能力不强,只好用很长的标题来描述了。在写这篇文章前我已经有预感这篇不是一两句话能够说完的,所以现在深吸一口气,开始写...

需要提前交待的:

  1. 欢迎转载,但请保留原始的文章链接、作者信息等。
  2. 文章中提到的设计均会提供源代码、电路原理图。但是这个制作跨度比较大,也涉及到我的其它一些项目,所以对于一些依赖的代码、设计,这里将不给出。或许他们其中一些的资料我会在以后给出。
  3. 文章中的照片是在设计过程中无意或者有意拍摄的,其中一些部分已经出现改动。如果发现图片与文字描述不符的情况,以本文描述为主
  4. 本人水平有限,也不是电子专业的,错误难免。请大家多多包涵和指正

1. 介绍

简单的说,这里将要介绍我制作的一个系统,用它来做下面几件事情:

  1. 定期检测位于室外的邮政信箱,如果有信件,会用中文语音通知我和家人。
  2. 一个简单的室外气象站,以半小时的间隔不间断的采集室外的温度和湿度信息,供后期分析。

同时,这个系统也是:

  1. 对太阳能供电设备和低功耗系统制作的尝试
  2. 对2.4G无线网络通讯电路的设计
  3. 基于AVR单片机的低成本以太网终端的设计

这里先给出这个系统的实际效果视频,给大家一个直观的印象:

 

在视频中我将一份报纸投入家中的信箱,随后在家里的IHES(家庭智能化环境系统)终端便会用中文语音报告“有信件,请查收!”。同时,在必要的时候它也会播报出当前由这个系统所采集到的温度和湿度情况。整个过程并无人工干预。

接下来我将开始介绍这个系统的制作过程。下文首先为介绍系统整体构架和技术要求,然后会按照硬件->软件,每个模块的顺序介绍原理和实现过程。然后会给出一些数据作性能分析,最后将交待整个系统的成本。对文章涉及的相关背景资料,会在文末的参考资料给出。相关代码、原理图也会在文章末尾给出。

“这个”系统包括的是:放置在室外的传感器和控制电路,在室内的以太网终端系统,它负责通过无线信号与室外传感器通讯,并将结果转发到以太网上。至于视频中看到(听到)的语音以及IHES核心系统的介绍,本文将不作涉及。我或许会在今后写这方面的介绍文章,也可以参考在参考资料章节给出的我已经写过的文章[5]。

2. 系统的构架和性能要求

或许你会想,要检测信箱的信件,方法有非常多。最简单的办法就是在信箱入口装一个开关,然后拖一根线到室内和电脑连接。这样应该也能实现视频中的功能。至于温湿度检测,那也直接连线出去不就得了?

但实际上这样做也会存在些问题。首先要布线,考虑到家里已经装修过,走明线会影响美观。同时如果直接将室外的信号与室内计算机系统连接,危险很大。在打雷是很容易被雷击到,同时室内外电势差也不得不考虑。另外,要能够有效地检测出信箱中是否存在信件,也不是简单的装一个开关那么简单。首先信箱是现成的,不方便做很大的改动,同时这个开关的设计(机械开关或者是光电)也需要一定的考虑。

总之,我还是用了一种比较折腾,但是仍旧适合个人DIY的办法:

首先这个系统会通过无线信号把位于室外的传感器和室内的计算机系统隔离开。当然,这样一来,事情又复杂了一些:室外的传感器需要自我供电,并且需要设计专门的控制器来负责与室内的系统进行通讯。另外,因为这里采用的无线信号并非采用标准的蓝牙或者WIFI等可以被PC所识别的协议,因此需要另外设计硬件将我们定义的无线协议转换成PC可以识别的协议,这里需要有电路将无线信号转换成以太网的TCP数据包中转给PC。

当然,要实现这些还会有更多的细节需要考虑,这也是为何我写这篇文章的原因了。我们暂时先不考虑这些细节,下面给出分析道这里我这个系统的框图:

 

IHES室外传感器框图

这里按照各模块所在地理位置(室外/室内)进行划分。图中上半部分是位于室外的传感器和控制器。其中各传感器会将它采集到的原始模拟信号转化成数字信号发送给传感器控制器。而传感器控制器会将各传感器报告的数据汇总并作简单处理后,通过无线模块发送给室内的接受终端。因为采用无线通讯,所以再考虑用一个电缆来给室外传感器供电就多此一举了。那么自然这里的电能供给就是指用电池供电了。但电池用尽后需要更换,这又显得比较麻烦。所以我的设计中传感器采用了太阳能供电+锂电池蓄电的方式来给室外传感器供电。考虑到成本,这里的太阳能电池不能用很大功率的,自然,整个室外传感器电路就需要运行在比较低的功耗条件下了。并且,南方经常有连续几周的阴雨天气,在阴雨天气中太阳能电池几乎不输出功率,此时就需要在设计中将系统的功耗压缩到最小。这里对供电和功耗指标是,室外传感器需要能够在一次完全电池充电后,仅靠电池电力维持持续工作15天以上。另一个指标是,在晴天太阳能电池的输出功率可以将电池完全充满。

图中的无线模块采用工作在2.4GHz波段具有收发功能的芯片/模块。这类模块仅实现了物理层的通讯协议,需要为他们设计编写对应的链路通讯协议。当然这样也适合用单片机来操作。因为功耗的限制,这里的无线模块的通讯距离比较小。

上图的下半部分是位于室内的以太网/无线转发器。他的主要工作就是将由以太网发来的IHES中央服务器请求转发成无线信号与室外的传感器通讯,并将收到的无线数据包转发到以太网络反馈给中央服务器。另外,这个模块上也开设了一个Web服务器,允许我们直接用浏览器登陆该模块来与室外传感器通讯。

其实这个模块做的大部分工作也可以直接交给中央服务器(PC)来实现,这样仅需要实现一个诸如usb口->无线信号的适配器即可,但是考虑到这里选用的无线模块功率较低,在中央服务器处以经无法介绍到室外发来的信号。所以我在设计中将这部分单独设计为一个模块,将他放置于离室外传感器比较近的阳台上。这样的一个限制就是:这个模块不适合用一台单独的PC来实现(考虑到阳台的环境恶劣、占地以及成本因素)。这里我选择用成本低廉的单片机来实现这个模块的转发和webserver功能。这样的另外一个好处就是功耗很低,可以通过网线供电的方式来给模块提供电能,而不需要另外接电源适配器。这后文我也会介绍如何改造交换机网口来实现用五类双绞线(以太网用线)给设备供电。

室外传感器的数据最终将以TCP数据包的形式发送到中央服务器上作处理和分析,并通过语音合成以中文语音将检测结果反馈给我们。最后就是上面视频中的效果了。

在开始具体介绍原理前,给出2张图片让大家有个感性认识:-)

 

位于室外传感器的摆放。其中传感其控制单元隐藏在了灌木后,图上不可见

 

 室内(阳台上)的以太网/无线转发控制器。

3. 各模块的原理、设计和制作

(终于到核心部分了...)这里将按照各模块:信件检测->温湿度检测->太阳能电池组->室外传感器控制器->以太网/无线控制器->以太网供电的顺序进行介绍。

信件检测

要检测信箱是否有信件,可以从2个方面入手。一方面是检测当有新信件的情况:在邮递员投递信件的时候,信件会从信箱的入口经过。要检测这个过程可以在信箱入口安装一个光电传感器。信件经过入口时候,光电传感器光路会被阻隔,因此产生一个脉冲信号。通过检测这个脉冲信号就可以知道此时有邮递员送信了。

但是这并不符合现在的要求。对于邮件检测,这里的一个需求是:

因为家中不可能时刻都有人在,所以当有新信件的时候,除了能够在当时尽可能早的通知这个事件外,还需要在每天的特定时间,比如晚上吃饭和早晨起床的时候告诉家里目前信箱里是否还有信件,显然检测新投递了信件的办法是不行的(因为很难检测信件取走的事件)。

要检测出信箱当前是否有信件,办法也有不少,复杂的可以安装摄像头通过图像识别来处理,但这显然没有必要。简单的有在信箱底部安放一个重力传感器,但是有时候信件就是一张纸(广告传单?),用重力传感器似乎不是很好的办法。

这里我采取的是在信箱底部安装一系列红外传感器,组成一个整列。当有信件投入信箱,它就会覆盖在这个传感器阵列上方,此时着要有1个传感器被遮盖,就可以检测出有信件存在。为了方便理解,这里给出一张原理图:

 

基于红外传感器进行信件检测

上图中蓝色框就是装有红外传感器阵列的平板,当有信件投入信箱后,它会覆盖在这个平板上方。当然,也会像图中那样因为面积较大只有一端与传感器平板接触。

这里的红外传感器采用的是反射式对管,既一个红外发射LED和一个红外接收管(光敏二极管)。当有信件覆盖在一个传感器上方时,红外发射LED产生的红外光会通过信件反射并被接收管感应。而如果传感器上方没有信件(或者在很远的距离),则接收管几乎不能收到由发射管产生的红外光。当然这里的一个前提是信箱内壁的反光率很低。

另外,因为信件的大小不一,从很大的报纸到小尺寸的信封都有。如果传感器数目过少或者摆放过于集中,很有可能出现信件正好落在没有放置传感器的区域。所以这里我用了8个传感器,放置成前排5个,后排3个的形式。至于如何摆放有效,主要是看实际测试的效果,可以参考的是2个横向的传感器的之间的间距比一个较小的信封宽度略小。

这里描述的传感器阵列目前市面没有现成的(至少我不知道),我找到了一块装修多余的木板自行加工:

首先是将木板切割成合适的大小,然后打孔,并按装红外传感器(一个红外发射LED和一个红外接收管为一对),效果如下图所示。用热胶将他们固定。

 

下图是安装好后的效果。

 

传感器阵列做好了,但是这样还不能被使用。需要有驱动电路来控制它并给出方便控制器理解的信号。这里需要考虑的问题是现在我有8个红外发射管和8个红外接收管,如果分别驱动,就需要至少16组信号线以及2根供电线。因为信箱是个密封性很差的容器,不适合将控制器也放置在信箱当中。这样就要求控制器与这个红外传感器阵列的信号连接用尽可能少的线路来实现。

这里采用的策略是将所有的红外接收管并联,所有的接收管只用一路信号来采集。这样一个好处是可以提高接受灵敏度。而对于发射管,采用轮流点亮的方式驱动。如果在一个发射管被点亮的同时,接受管检测到信号,就认为这个发射管上方有信件遮挡。

红外线不能被人眼所见,但是却可以用各种感光器件如摄像机感受到,下面是制作过程中将发射LED轮流点亮过程降低1000倍拍摄的视频。方便大家理解:

 

红外传感器阵列控制电路

要实现轮流点亮8组LED,自然就想到用74595移位寄存器来实现。这样可以仅采用5组信号线(电源2路,1路串行信号,1路使能信号,1路时钟信号)。而对于红外接收管,再需要一路信号传输它感受到的光电流即可。这样一共消耗了6组信号线。另外,邮政信箱虽然密封性不强,但是防雨,这很适合放置湿度传感器。所以在我的设计中湿度传感器也被安装在邮件传感器一起。湿度传感器我采用了国产的DHT11[7]单总线数字式传感器。虽然精度不是很高,但是对我来说已经够用了。关键是它成本比较低廉,数字量输出也适合单片机使用。

将上面提到的74595和DHT11整合在一起,便成了这里的传感器控制电路,它的原理图如下所示:

 

可以点击这里下载电路图的pdf文档:http://www.csksoft.net/data/ihes_osensor/circuit/IR_matrix.pdf

原理图中可以看出这样一来正好使用了8路的信号线,我最终使用了五类双绞线来进行信号的传输。

 

上图为这个控制电路的成品照片。将控制电路与传感器阵列焊接在一起后,稍微对这个传感器做一下包装

 

 

 

 

 

下面给出一段在测试这个信件传感器时拍摄的视频,给大家一个直观印象:

视频中传感器已经连接到了控制单元上,我制作了一块临时电路用于与控制单元建立无线链接,将邮件传感器的信息发送回PC供调试。可以看出这个传感器可以很有效的检测出位于其上方一定距离内的信件。

温湿度检测

我采用DS18B20单总线温度传感器[9]以及DHT11温湿度传感器[7]来采集室外的温湿度情况。其实仅仅使用DHT11便可以采集这2个数据,但是相比较DS18B20的误差和分辨率都更高,同时在我的制作过程中DHT11是在后期新增加的,所以我仅适用了DHT11的湿度采集功能。

在前文中已经有所介绍DHT11是和信件传感器组装在一起的,目的是为了将它放置在一个相对空气流通的环境。而DS18b20处于方便,我将它焊接在室外传感器控制器的PCB上,并按装在一个密封的壳体当中,这将在后文介绍。

太阳能面板和锂电池

处于成本和其他方面考虑,这里我选用了功率较小的面板。虽然输出功率不大,但是因为其表面积小,适合安装。

太阳能面板是我在淘宝网购得的单片价格为¥10的多晶面板,其性能参数如下:

最大输出电压:6V

短路电流:75mA

工作电流:~50mA

我使用4片这样的面板加以拼装,并且利用废旧机器的部件作了一个支架。效果如下:

 

 

这四块面板采用并联的方式连接(需要在之间串联保护二极管如5819来防止因为部分电池板电压差异造成的倒流)。在上海近期(冬天)晴天阳光照射的条件下,测得这块面板的短路电流可到170mA以上。

而对于电池的选择,按照前文提到的功耗和待机要求,我使用了900mAh的聚合物锂电池。这样如果按照无日照情况系统可以工作15天来计算,那么整个室外传感器系统允许的平均功耗大概为2.5mA@3.8V。并且,如果算一天日照为8小时计算,那么对这块电池完全充电所需的平均充电电流在112mA。差不多可以满足我的要求。

严格的说,要对锂电池充电需要有专门的充电电路来提供先恒流后恒压的充电方式。但是这里为了考虑成本以及考虑太阳能电池输出功率比较低,我就偷了下懒。一般锂电池自带的保护电路都会提供过充过压保护。所以在后文可以看到太阳能面板的输出在经过几个二极管降压后直接对锂电池进行充电。而在后文的验证数据中也可以看到锂电池保护电路对过充的保护。当然这是不标准的办法,不推荐使用。

无线模块

我选用了基于em198810芯片[6]的2.4G无线收发模块,市面上可以买到的模块型号如Tr24B/Tr24A以及其他类似模块。做出这样的选择没有什么特殊原因,主要是手头有现成的模块在。该模块内建64byte FIFO收发缓冲区,并且带有CRC校验。我在实际测试中该芯片在最大输出功率下室内可以穿透2-3层墙壁,并维持10-20米的通讯距离。按照手册的介绍,其工作在收发模式下的功耗为25mA@3.6V。相比而言比他优秀的其他模块也不少,同时如果情况允许可以选用314Mhz等较低频率的收发模块,可以有效提高通讯距离。

传感器控制单元

 该控制单元采用Atmega48PA单片机进行中央控制,负责如下功能

1. 监听来自室内无线终端的通讯请求,并读取信件传感器、温湿度传感器得到数据并通过无线发送回室内终端

2. 锂电池电压监控

3. 无线模块的驱动

4. 日光检测

 

其原理图如下:

 

可以点击这里下载电路图的pdf文档:http://www.csksoft.net/data/ihes_osensor/circuit/checker.pdf

PA后缀的AVR芯片相对它的之前版本(Atmega48V等)功耗更加低[10],适合这里的应用要求。原理图比较简单,基本就是对几个传感器的驱动,以及采用电阻分压后的锂电池电压检测。

对锂电池电压检测主要是为了我在调试过程中观察系统实际的工作性能。并且今后可以针对当前电池剩余电量适当调整无线通讯的频繁程度从而提高系统待机时间。锂电池的工作范围一般在2.7V-4.2V之间,这里我采用3:1电阻分压,将缩小4倍的电压信号(落入0-1.1V范围)接入AVR芯片的一个ADC引脚。通过用AVR内部的1.1V参考电压比较就可求得实际的电池电压。我最后固件中可以提供0.01V的电压分辨率。

图上的Solar Panel Probe是用于检测当前是否有光照的,不过目前我固件当中没有使用到。可以用它来做一些行为的调节,比如在白天有日照的时候允许开启更多的传感器。

这里的VCC实际就是锂电池的当前电压了,目前设计上只要锂电池维持在3-4.2V就可以保证系统的正常工作。由于无线模块需要工作在3.6V以下,所以这里串联了一个1N4148提供0.6V的压降,使得即使工作在4.2V时,无线模块也可以安全的工作。

该控制单元工作在1Mhz频率,使用内部RC振荡器。经过后期的固件优化,整个室外传感器电路工作功耗在1.1mA@3.8V。这样即使没有日照,室外传感器也可以工作近一个月时间!

下面是按照原理图实现的PCB:

 

 

 上面2幅图是分别在不同制作阶段拍摄的,所以可以看到他们的接口有区别。第二幅图上已经安装了无线模块。

室外传感器的组装

这里为了理解方便先介绍我将上述传感器各模块组装并安放在视频中方位的过程。至于固件的编写会在后文介绍。

前文已经提到,传感器的控制单元不适合与信件传感器一同放置在信箱内,除了前面提到的密封问题外,还有个因素是我家的信箱是铁质的,会对无线信号造成屏蔽。所以上图中的控制单元需要另外防止。为了保护电路不受风吹雨淋破坏,我使用了如下的密封机壳:

 

这类机壳可以从淘宝网购得,价格一般在¥15元左右,其中含有密封橡胶,防护性应该不错。

 

为了将PCB固定在壳体内,我用热胶固定出PCB的铜座。

 

因为传感器控制单元、太阳能电池组以及信件传感器分散的比较远,这里我用以太网双绞线作为信号线,一根用于信件传感器和湿度传感器信号,另一根用于太阳能供电传输。用热胶保护好接口引线。

 

随后在壳体上打孔,将这2根引线穿进壳内,并用热胶以及AB胶密封住缝隙。

 

最后,便是把控制单元PCB、锂电池等装入壳内并固定。

 

盖上盖子的效果

 

接下来把信件传感器放进信箱底部。

 

将太阳能电池组放在一个比较高的平台上,并用尼龙绳固定住。

 

把机壳放置在灌木丛中的隐蔽位置。目的之一是为了防止阳光直射。

 

 最后,如上图所示,把这些部件用双绞线连接。上图是还没有把连线隐藏的画面方便大家理解。到此室外传感器部分就制作完成了。

 

室内以太网/无线转发器

之前已经提过因为无线模块的通讯距离有限,且中央服务器不能直接与传感器无线信号建立通讯,所以需要设计一个模块负责将来自中央服务器发来的TCP数据请求打包成无线数据包发送至室外传感器,并且将接受的结果用TCP包送回中央服务器。

这里我使用Atmega8L作为该转发器的中央处理器,并使用ENC28J60 SPI接口的以太网适配芯片[8]实现AVR芯片接入以太网的工作。其原理图如下:

 

可以点击这里下载电路图的pdf文档:http://www.csksoft.net/data/ihes_osensor/circuit/etherbridge.pdf

该模块的AVR单片机工作在8Mhz的频率,电路也很简单,单片机负责将来自以太网的请求转发到无线网络。在后文的固件介绍部分会涉及它的工作策略。目前该模块实现了TCP数据请求的转发任务同时也内建了web服务器供人工登陆至该模块进行室外传感器操作和状态查询。

对于AVR与ENC28J60的配合使用,可以参考tuxgraphics.org的名为:An AVR microcontroller based Ethernet device地文章[2]。他的地址将在后文参考资料处给出。

整个模块工作在3.3V电压下,因为整个模块功耗很低,所以这里采用从以太网取电来给模块供电。在后文将介绍,以太网将提供13V左右的直流电能,为了有效利用,这里采用了DC-DC开关电源模块将12V电压转化成5V左右的电压再通过三端稳压器稳定在3.3V。该模块PCB如下:

 

这里我使用了现成的DC-DC降压开关电源模块以及ENC28J60以太网模块,他们都可以在网上购买到。

 

图上方为12->5V DC-DC开关电源模块

 

将以太网模块放置于PCB的下方

 

PCB的背面

 

实现以太网供电

以太网供电其实有专门的设备,也有专门的名词,叫做(POE)Power Over Ethernet。但是这类设备一般很贵,这里没有必要为此去购买他们。我用了一种不规范的办法来实现供电。因为对于10M/100M的以太网,8根双绞线只用到了4根传输信号,其余4根是空余的。可以用来传输电能。

当然这样做也有一定危险,比如误将其他设备介入这根以太网线,那么就会造成设备烧毁。所以如果要用这个办法需要平时多加小心。

那么现在的问题是用来传送的电能又谁来供给?我的办法是将这根以太网线连接的交换机引出自身的供电:

 

上图是我家用的某品牌交换机的PCB。我将它的电源部分引出(图上红蓝线)。测得他的电源电压在13.5V左右。

 

 然后观察用于连接室内收发传感器以太网口的对应网口,可以发现另外4根双绞信号线焊盘没有被使用(实际上都会把这些不占用的信号线用电阻拉低接地,所以自制时要注意将他们断开,防止短路)。将刚才引出的电源线焊接在上面。这样凡是连接在这个网口的以太网线便有了电源供应。

将带有电源供应的以太网线连接到转发器网口上,这里还需要把电源线单独引出,并接到DC-DC模块上,如下图所示。

 

下图展示了通过以太网给模块供电的情况:

 

模块的安装

这里同样先介绍最后的组装部分,固件的编写将在后文介绍。这个模块同样也用密封塑料机壳加以保护。下面首先固定住PCB的铜座。

 

然后将所有部件装入壳体。

 

完成后把它放置在阳台的一个角落。

 

 

好了,现在所有电路制作的介绍已经完成,下面我来介绍他们的固件开发和运行策略。

3.软件/固件设计

这里仅交待相关模块的固件的设计思路以及高层次的逻辑分析。至于具体的细节,诸如依赖的代码库,这里不作过多介绍。请参考文后的相关资料。

系统协作模式

先从比较高的层次来介绍整个系统的运作。整个系统中,室外传感器和室内无线/以太网转发器永远都是被动工作的。也就是说,如果没有外界的请求,它们永远只会工作在监听状态,不会主动去与外界联系。这样设计的好处是简化流程,同时也可以降低功耗。

整个系统中IHES中央服务器将占有主导权,在服务器中运行着一个守护进程,它负责每隔一定的时间通过以太网向室内转发器发出请求,室内转发器在接受到了请求后,进而向室外传感器发送无线请求。最后把收到的数据发送回IHES主服务器。

但是,因为无线通讯的不确定性,在设计当中中央服务器不适合在向室内转发器发出请求后等待着对方回应。这里中央服务器在发出请求并确认转发器收到请求后就切换到其他任务继续。在一定时间后,中央服务器会再次发出请求,要求转发器把上次请求到的数据发送回来。

同样的通讯模式也应用在转发器中。转发器会在收到IHES服务器数据请求后开始与室外传感器联络,但它不会一直等待对方将传感器数据传回,同样它也会切换到其他任务(处理Web请求?)并在一定时间后再次联系室外传感器,将采集到的数据收回并存储在自身内存中,等待IHES服务器来取回。

这样的通讯模式含有下面2个原语:

  1. 预取命令 (Prefetch Command)
  2. 取回命令 (Retrieve Command)

下图展示出了整个系统的协作模式:

 

如图所示,Sensor Checking Daemon是运行在IHES中央服务器上的一个“意识进程”,他会每半小时向以太网上的EtherBridge Agent(也就是室内转发器)发送一个IHES TCP请求包,但此时转发器不会开始无线通讯,而只是向服务器回应,告诉该请求已经收到,并告诉服务器数据将在多少时间后可以获取。中央服务器在收到回应后就转而调度其他IHES“意识进程”。

EtherBridge Agent在收到预取指令之后,就会将自身状态机转换到通讯模式,开始联络室外传感器(Outside Sensor)。但同样它也只是先发送预取指令,因为室外传感器可能会用比较长的时间(>5ms)来收集传感器数据。这里涉及到一个无线通讯的策略问题,所以适合用预取指令来实现。同样的,室外传感器在收到预取指令后,也仅仅是回应自身已经收到了请求。在室内转发其收到传感器回应之后,它会切换工作模式到其他任务,比如监听并处理新的TCP或者HTTP请求。室外传感器在回应预取命令后,开始真正的传感器数据收集,并且将数据缓存在自身内存中。在一定时间后,转发器又开始联系室外传感器,此时室外传感器就会将真正的数据传回。受到传回数据后同样转发器也将这份数据缓存在自身内存。等待IHES主服务器回收。

采用预取模式的原因:

对于无线部分的通讯,丢包现象比较普遍。目前的处理是在发生丢包时,命令发送者会再次发送重试,直到受到了对方的正确回应(通过CRC交验)。基于这样的设计,如果室外传感器在收到请求后就进行传感器数据读取并将结果送回,很有可能会因为这个过程中的丢包使得转发器再次不断的请求。这样一次数据请求就会造成室外传感器多次去收集数据,而这样会消耗较大电能(各传感器仅在收集数据时开启)同时数据收集比较耗时,需要大约5ms的时间,也就是说这样的模式,转发器也需要等待5ms重新发送数据包,可能会导致总的通讯时间延长。

对于以太网部分通讯,虽然丢包现象相对很低,但是会出现同时多个请求者的情况,比如IHES服务器和从web服务器登陆的用户都希望访问室外传感器。这里要处理的问题就是降低一次数据通讯的延迟。同时因为AVR单片机相对简单,不能很有效的处理多任务协调,如果在一次新的以太网数据请求过程中又发生新的请求,就难以进行数据的一致性维护。

好了,写的我自己都晕了,来看段视频来描述这个过程吧:

这段视频是IHES服务器进行开机自检中的片段。它会将与室外传感器的数据请求全过程用语音朗读出来。从中可以看(听)出这个系统的协作过程。

好了,有了总体的框架后,下面介绍室外传感器和室内转发器的固件实现。

室外传感器固件设计

前面已经介绍了该部分的电路以及协作关系,这部分的固件主要实现如下功能:

  1. 驱动各类传感器,读取传感器发来的数字信号
  2. 读取当前电池电压
  3. 无线模块驱动程序
  4. 监听无线网络请求
  5. 自身运行状态控制和节能控制

前2部分不需要做很多解释,当前电池电压会采用如下格式保存:

[15:8] Voltage integer : 2^7+2^6+...+2^0
[7:0]  Voltage decimal : 2^-1+2^-2+...+2^-7

他可以提供0.01V的电压分辨率。

无线模块驱动我参考了Alexander Yerezeyev编写的驱动代码[1],可以在后文参考资料出找到他的链接。这里主要对其驱动逻辑进行时序上的优化,同时增加了自动丢包重发功能。

信件传感器的信号采集:

之前已经介绍了信件传感器的工作原理,这里介绍一些细节问题。如果有信件遮挡一个传感器,则它会受到来自红外发射LED的反射光,在固件看来,这个反射光就是一个电压信号,且电压约低,反射光越强。

固件会驱动74595轮流点亮红外发射管,随后读取此时接收管上的电平信号:

ir_sensor_voltage = analogRead(IR_SENSOR);

这是一个0-1023范围的数值,如果某一次接收管的电平比较低时,则可以怀疑他对应的发射管上方有信件遮挡。所以还需要在之前读取一次参考电平,即在电亮任何发射管之前接收管的电平数值,因为此时没有任何红外光产生,所以这个电平信号可以认为是没有信件时接收管的输出信号(实际上并非如此,后文会有介绍)。

在我编写固件时,进行了一些测试,最后判断当前接收管电平和参考电平之差如果小于阈值:IR_DETECTION_THRESHOLD (0x65),则可以认为有信件存在。

但是实际情况比预想的存在偏差,因为阳光与随着信箱的入口照入箱内,日光会对接收管造成干扰。表现在接收管会将日光认为是反射信号。这样会使得参考电平也非常小,从而无法检测当前是否有信件。解决这个的办法是比较参考电平,如果它自身也小于一个阈值时,就认为本次的检测结果无效。下面给出实际的测试数据来说明这个问题:

2010-02-20      07:08   00.7    94       4.09   0666
2010-02-20      07:38   00.9    95       4.12   0071

上面2组数据是目前传感器在这些时间点采集到的信息。注意最后一列数值,他表示的就是红外接收管的参考电平,可以很明显的看到,7:08时阳光还比较柔和,参考电平比较高,但到了太阳升起后,参考电平迅速降低为71。这样的条件是无法测出信件是否存在的。

解决办法其实也很简单,就是在信箱入口安装遮阳布,不过我没有这么做。另外,如果信箱内有信件,它同样也会遮挡阳光入射,从而提高参考电平,这样又可以有效地检测出信件的存在了。

因为参考电平的波动性,我打算将参考电平有效性的比较交给IHES中央服务器来处理,这样可以避免频繁的修改室外传感器固件。所以,对信件的检测,将返回如下结果:

触发的红外传感器编号,如果没有传感器被触发,为0

参考电平

相信大家已经听到了之前的自检语音中播报参考电平的情况了。

运行状态和节能处理

降低功耗对室外传感器非常关键,固件可以做的其实很简单,就是尽可能的降低每个设备的功耗。对于各类传感器,仅在需要采集数据时将他们唤醒或者接通电源。对于无线模块,25mA的电流消耗绝对是这个模块中最耗电的。所幸可以将RF芯片设置为睡眠模式,这样可以将功耗设置为微安电流级别。对于AVR芯片自身,则尽可能在空闲的时候将其设置为睡眠或者待机模式。

这里面临的问题是无线模块在睡眠模式下无法接收信号。所以需要定期将它唤醒,监听一段时间后再次进入睡眠。但是,也需要在接收到数据预取指令后,保证无线模块工作一定的时间,方便室内终端发送取回指令后能够立刻做出响应。

所以,最终室外传感器会使用如下状态机进行工作:

 

分类两大状态,空闲和激活状态。在大部分情况下,室外传感器都将工作在空闲情况下,此时所有传感器都被关闭或者设置在睡眠模式,无线模块会定期唤醒进行监听,如果在监听过程中受到了来自室内终端的情况,则会切换到激活模式。否则无线模块会再次进入睡眠模式。这个模式下AVR芯片只有在维护状态变换的情况下才工作,平时都工作在睡眠模式(关闭时钟信号)。

空闲模式下室外传感器功耗很低,仅为1.1mA@3.8V的水平。

这里无线模块每2.9秒将被唤醒并设为监听模式,在工作约20毫秒后又进入睡眠模式。我们可以利用这个数据计算无线模块的平均功耗= 20/2900*25mA =~ 172uA。这下由无线模块产生的功耗负担就没有了。

如果按照1.1mA的平均功耗计算,那么900mAh的锂电池可以供整个传感器模块在没有日照情况下工作:34天。

当然如此短暂的监听时间会延长通讯的时常,所以在空闲模式下只要收到了预取指令,固件就会切换到激活模式下,已进入这个模式固件便开始收集传感器数据。而同时无线模块将持续监听请求。当然,激活模式有一个有效时常,当过了一定时间,固件又会回到空闲模式下。这个时长目前为1秒。

对于处理无线请求,在引入了状态管理后就非常简单。对于预取指令,固件要做的就是将Idle State切换到Active State即可。而对于取回指令,只需要简单的发送回目前内存中缓存的上一次去读结果即可。

室内无线收发器固件设计

收发器的以太网操作部分由tuxgraphics.org中提供的参考例程修改而来。该模块会同时监听80端口(Http服务)和8003(IHES agent service)。前者用于人工登陆至该模块操作,后者用于IHES中央服务器通讯。

该固件采用如下状态机工作:

 

 其中黑色箭头是实际的状态转换关系,而蓝色的是逻辑上的状态转换关系。前面已经指出,无线通讯可能需要最多3秒时间与室外传感器取得联系,为了保证这个等待时间内收发器可以及时相应来自网络的请求。所以这里将无线通讯拆分成几个子状态,同时必要时可以将CPU时间让出来处理以太网请求。

下面给出分别通过Web服务和IHES Agent协议于该模块驱动联系的效果图:

下图为用浏览器登陆至收发器后的画面。

 

下图是Linux下我编写的用于调试IHES协议的客户端得到数据画面。

 

 

 软件部分就介绍到此,相关的源代码在文章后续部分提供。

实测数据分析与讨论

实际功耗分析

写到这里,该系统已经持续工作了2天半时间,虽然这2天上海都是晴好天气,不过也不妨来看看实际的功耗情况。当然,依据就是采集到的数据了:

 

 上面的曲线记录了前天(2/19)到昨天晚上8点锂电池的工作情况,可以得到的结论有

  1. 夜间电池电压会因消耗降低0.03-0.06V左右
  2. 锂电池保护电路的过充模块发挥作用,电压在4.27V后就不再继续上升。

当然因为电池原先就处于充满状态,所以目前还不能判断出白天的阳光是否能够将电池完全充满。不过看着图中上升的曲线还是很有把握的。

 

温湿度曲线图

这2张曲线图就作分析了。

 

 

制作成本和用时

制作整个系统的成本还是很低廉的,下面列出一些器件的价格,大家可以自行估算

 

无线模块 ¥9-15 x2

ENC18J60 ¥50 x1

Atmega48Pa ¥8

Atmega8L ¥8.5

太阳能电池板 ¥10 x 4

锂电池和保护板 ¥20

壳体,小  ¥15

壳体,大 ¥20

DS18B20 ¥4.5

DHT11  ¥9

其他的一些小器件就不列出了,可以看出成本是不会超过150的。

 

对于花费的时间,这个项目在1-2年前就开始酝酿中。就单单算制作这篇文章介绍的部分,大致花费我4个周六和春节2天时间。也就差不多一周左右。当然,其中涉及的IHES中央服务器和构架、固件依赖的ArduinoLite库,RP infrastructure等这些的编写,如果把它们花费的时间算入,可能用来3-4个月左右。应该说还算是个短期的项目。

 

下一步工作和改进

目前这个系统虽然已经开始工作,但是还需要进一步验证,除了功耗以外,是否防水也是一个需要关注的问题。

对于改进,从前面分析上已经可以看出锂电池的过充是一个问题,解决办法可以通过单片机监控锂电池电压,当达到过充标准后就进入放电模式来抵消电压上升。

另外,无线模块也是可以改进的地方。可以选用314Mhz的模块,这样可以提高传输距离。

对于功能上来说,下一步可以考虑安装气压、风向传感器。那样差不多真的能叫做气象站了(山寨的)

相关源代码和仿制信息

 这里将提供如下代码:

  1. 室外传感控制器固件
  2. 室内无线/以太网收发器固件

不过需要说明的是因为这些固件依赖了许多额外的库和框架,那些框架属于我正在进行的其他项目,且还没有到发布阶段,所以暂时不提供,请谅解

固件采用avr-gcc4.2(WinAVR-20090313)编写。其中采用了Arduino-Lite AVR运行库。Arduino-Lite是我针对arduino库进行优化改良后的增强库,他提供类似的接口,但代码比原先可以缩减近一倍,执行效率提高10倍左右。Arduino-Lite我会在不久发布。也可以参考下面的参考资料中我之前写过的Arduino-Lite预告文章。

对于依赖的Tr24无线模块的驱动,可以去采用Alexander Yerezeyev编写的代码(见参考资料),也可以选用你喜欢的无线模块。

另外,这里也给出万用PCB的layout信息:

 

 

 

参考资料

[1] 基于AVR的TR24a驱动例程:spiriton-tr24a-demo, by Alexander Yerezeyev

http://code.google.com/p/spiriton-tr24a-demo/

[2] An AVR microcontroller based Ethernet device, by Guido Socher

http://www.tuxgraphics.org/electronics/200606/article06061.shtml

[3] Arduino-Lite Google Code repos, By 我(Shikai Chen)

http://code.google.com/p/arduino-lite/

[4] Arduino-Lite简介, By 我

http://www.csksoft.net/blog/post/255.html

[5] IHES中的语音合成介绍, By 我

http://www.csksoft.net/blog/post/linux_weatherforecast.html

[8] ENC28J60 Stand-alone Ethernet Controller with SPI

http://ww1.microchip.com/downloads/en/DeviceDoc/39662a.pdf

[9] DS18B20 Programmable Resolution 1-Wire Digital Thermometerler

http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf

 

 终于写完了,没想到花了2天时间来写这个文章。也挺折腾得。我并不是闲得没事做,还是那句话,有时候就是要逼着自己做点事情,否则可能什么都做不了。好了,很感谢各位看到这里,尤其感谢坚持全部看完的:-)

 

说说我从去年9月份开始的AVR单片机学习和使用

原本是想把这篇文章的内容作为一个前言背景部分,不过发现写了不少,就干脆单独提出来一片。好给后续的文章做铺垫。

我会在明后几天发布一篇“ArduinoLite,高效且易用的AVR单片机运行库”的文章,来把这半年来我的一些工作成果推荐和分享给大家。

我从去年(2009)9月起开始接触AVR单片机。其实也就是利用业余时间基于他制作一些电子制作,当然也有将来会比较大规模的项目。或许大家也从之前几篇凑数性质的文章中看到了。

在正式开始本文前,我打算先介绍下一些背景知识,好让对这块领域暂时陌生的朋友有个了解。

AVR单片机是Atmel(atmel.com)公司推出的一款RISC指令集的8位单片机。因为它的高性价比(片上集成PWM, ADC, I2C, SPI,etc)和高运算效率(16MIPS@16Mhz),所以目前被广泛应用。
同时,另一个使得AVR受欢迎的原因是他的开发工具链很丰富。所谓丰富就是由商业的收费编译器(如ICC),也有开源免费的编译器gcc-avr。这使得对AVR的开发几乎是0成本的,并且gcc的强大也吸引着不少人。
我之前对单片机的了解仅是大二学校的一门电子科技创新课上接触过89S51单片机,从去年9月份开始打算利用AVR单片机来做些事情。于是就和其他人一样,我也开始去学习使用这个系列的芯片。这里就介绍下我再这个过程中的一些体会和心得,如果有朋友想开始涉足这个领域,不妨听我说说。顺便也是给后面的文章做铺垫...
首先我并没有去买市面上各类单片机的教程,而只是下载了AVR芯片的datasheet。毕竟一切的细节和资料都是来自datasheet的。不过如果光看着datasheet开始AVR编程实在有点痛苦,需要有一个容易上手的途径。
之前我已经从Makezine.org上听说有一个意大利的开源硬件项目Arduino(arduino.cc)是基于AVR芯片的(Atmega168),这个项目的特点是他定义了一个基于Atmega168的标准PCB版以及标准的引脚接口,并且提供了一套运行库和IDE环境方便开发者对AVR编程。

Arduino Board

图:标准Arduino板(图片来自arduino.cc首页)

 

虽然在很多专门从事单片机开发的人眼里看来这个东西基本和玩具无异(其实的确有自身的问题),但是作为一个没有接触过AVR芯片甚至完全没有单片机开发经验的人来说,Arduino大大降低了入门门槛,同时因为全世界有许多人都贡献了基于Arduino的一些应用,比如简单的如驱动马达转动到复杂的去实现一个机器人、基于Arduino的webserver等。丰富的外围代码和各类扩展硬件使得即使一个完全没有硬件设计背景的人也可以轻松的实现很复杂的应用。关于Arduino我就介绍到此,如果有兴趣可以去google了解下。

我当时就直接在taobao上购买了一块Arduino版正式开始AVR的使用。目标是一方面可以立刻基于Arduino带来的易用性来实现出我的想法,同时开始看Arduino运行库的代码,作为我对AVR芯片学习的开始。

Arduino的确是相当易用,首先它提供的IDE已经自包含了几乎所有单片机开发的工具链(avr-gcc, 烧录程序, 串口调试),同时也包含了不少的例子程序可以让使用者立刻解决一些现实问题,比如要实现控制LED很柔和的明暗渐变交替,有一个fading的现成例子就可以实现,如果略加改动,开发者还可以实现如通过PC来控制LED亮度这样的实验。

下面举个具体点的例子:

上图中可以看到PCB版上上下各有一排插口,并标有着数字。实际上这些插口是直接和AVR的芯片引脚连接的。那么,如果我将一个LED连接在上面标号为#9的针脚,希望做到的效果是让AVR控制这个LED从0%的亮度逐渐变成100%亮度,用Arduino我只要写入下的代码即可轻松实现。

pinMode(9, OUTPUT);

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   analogWrite(9, led_brightness);

}

代码相信不需要我解释大家也能知道原理了,analogWrite就是设置一个引脚的模拟值(可以认为是#9的电压,自然数值越大LED就越亮),不过这里其实并不是真的设置了模拟的电压量,而是PWM占空比(wiki:Pulse-width modulation

这个analogWrite就是由Arduino所提供的函数。他已经将许多冗长的硬件寄存器配置操作包装了起来,提供给使用者易用的接口。这的确给初学者减轻了不少负担。如果不使用Arduino,仅依靠gcc提供的标准函数来实现这个看似简单的功能,则需要编写如下的代码。

//Set PWM mode: fast mode for timer 0
sbi(TCCR0A, WGM01);
sbi(TCCR0A, WGM00);

//set timer 0 prescale factor to 64
sbi(TCCR0B, CS01);
sbi(TCCR0B, CS00);

sbi(DDRB, PB1);

sbi(TCCR, COM11);

 

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   OCR01 = led_brightness;

}

上面代码其实也不长,但一堆寄存器的设置相信还是会让许多第一次接触AVR的人头晕的。并且相信大部分人并不可能记住要实现这样一个功能需要设置哪些寄存器,更多的时候还是会去参考datasheet或者从已有的代码里复制过来。其实这些都是无谓的劳动,也容易增加出错的概率。

不过,作为对AVR的学习,还是必须了解其中底层的运行机制的。同时Arduino也有一些缺陷所以并不是所有情况下都能使用。所以之后我就边使用Arduino来做事情,同时对他的运行库代码作了许多修改并且进行大规模优化。这个将在后续的文章中介绍了。半年过去,我觉得至少在AVR这个领域,我应该算是精通了。

 

接下来我来分析下Arduino的一些限制和缺陷,也为衔接后续文章:-P

正如前面所说的,在很多专门从事单片机开发的人眼里,Arduino就像玩具一样。那么为何那么易用的东西会被如此看待?

当然一方面,正是因为宜用所以有人认为这是玩具,这种逻辑很多见:C#很易用,有人认为是玩具,Mac很易用,有人认识为玩具, etc... 当然这是不理性的想法,对于完全没有调查研究就下这样的结论自然是没有道理的。

撇开这个,我觉得主要还是因为Arduino自身的限制和缺陷造成的。

首先,Arduino这个概念其实有2个含义

a. Arduino PCB电路板

b. Arduino IDE,运行库

其实很多时候提到Arduino是2个含义兼有的,比如看到Arduino的标准电路板,自然就是a含义,而开发起来所谓的Arduino就是运行库。

那么这里先看Arduino作为一块硬件电路时候的情况:我想成熟产品中总不止于把一块Arduino板放进去吧...自然在这个含义下,那个就是“玩具”。这块版的用途很明显,就是给业余爱好者使用的。

那么作为运行库(软件)的情况下,Arduino也面临一些问题:

1). 仅支持Atmega8/Atmega168等/Atmega1280

2). 程序体积大,代码效率低

3). 对外围硬件存在一定假设,不能自由运用在任何外围电路中

1)和3)其实可以认为是一个问题,就是Arduino的运行库是专门为Arduino板或者兼容硬件设计的。Arduino板不可能用于产品,那么自然这个Arduino运行库也是没法用的(至少不加修改是不可能了)。同时还有别的问题,比如AVR的芯片种类很多,有不少是基于特定应用的。例如要采用AVR开发锂电池充电器,对于这样的需求,Attiny系列的芯片可能更加合适(价格便宜,充电器逻辑简单,过于强大的芯片资源也是浪费)。Arduino仅支持那些“中高端”部分的AVR芯片显然是不实用的。对于3),一个具体的例子是Arduino板上AVR普遍工作在16Mhz和8Mhz,所以Arduino运行库的代码实际上也仅支持16Mhz和8Mhz。但实际应用中可能要求AVR运行在很低的主频(1Mhz或者更低)。

其实上面这2个问题通过简单的修改Arduino运行库代码就可以解决了,应该说相对arduino库带来的好处相比还是很划得来的。当时关键就出在2)上。

程序体积大和代码效率低是关联的。再举一个很实际的例子:前面我提到的让LED渐变显示的功能,如果用Arduino,编译出来的程序需要消耗1-2Kb。如果用x86上的情况来看,1-2kb非常小。但其实情况是单片机都只有8KB左右的存储空间。有些型号,比如前面提过的Attiny系列只有2Kb。仅仅是实现一个LED渐变,或者说PWM输出,就消耗了Attiny的全部程序空间,这个肯定是不合算也没有人愿意的。就算是Arduino板采用的16KAVR,也意味着消耗了1/8的空间。这几乎都没做别的事情。做一个对比,用前面我给出的直接调用标准库的实现方式,虽然代码看上去啰嗦,但是编译产生的程序只有100字节左右。100:2048,这个差距实在是太大了。如此大的程序体积,自然效率就会低很多。

这几乎也是为什么Arduino不会被专门从事开发的人接受的根本原因了。

其实看过Arduino实现就知道出现2)的根本原因了:a.采用C++,b.过分的注重灵活性。

关于a.其实也不是核心问题,但是C++在默认情况下的确会产生加大的代码。而b.这里就非常明显。

这里再举前面的例子,我们来研究下analogWrite这个函数:

void analogWrite(uint8_t pin, uint8_t val)
{
 pinMode(pin, OUTPUT);
 if (digitalPinToTimer(pin) == TIMER1A) {
  // connect pwm to pin on timer 1, channel A
  sbi(TCCR1A, COM1A1);
  // set pwm duty
  OCR1A = val;
 } else if (digitalPinToTimer(pin) == TIMER1B) {
  // connect pwm to pin on timer 1, channel B
  sbi(TCCR1A, COM1B1);
  // set pwm duty
  OCR1B = val;

 } else if (digitalPinToTimer(pin) == TIMER0A) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel A
   sbi(TCCR0A, COM0A1);
   // set pwm duty
   OCR0A = val;     
  }
 } else if (digitalPinToTimer(pin) == TIMER0B) {
  if (val == 0) {
   digitalWrite(pin, LOW);
  } else {
   // connect pwm to pin on timer 0, channel B
   sbi(TCCR0A, COM0B1);
   // set pwm duty
   OCR0B = val;
  }
 } else if (digitalPinToTimer(pin) == TIMER2A) {
  // connect pwm to pin on timer 2, channel A
  sbi(TCCR2A, COM2A1);
  // set pwm duty
  OCR2A = val; 
 } else if (digitalPinToTimer(pin) == TIMER2B) {
  // connect pwm to pin on timer 2, channel B
  sbi(TCCR2A, COM2B1);
  // set pwm duty
  OCR2B = val;
 } else if (val < 128)
  digitalWrite(pin, LOW);
 else
  digitalWrite(pin, HIGH);
}

实在太长了,我把其中的注释已经删去。可以看出arduino在实现上很讲究灵活性(这算是一种比较客气的说法)。那么,真的需要这么多代码吗?看看不用arduino时对他的等效实现: OCR01 = led_brightness;

仅仅是一行简单的赋值语句,即使是编译成了AVR机器码,也只有1条outb指令(2字节),而前者会产生多少代码就不用具体给出了。而这个的运行效率差别有多可怕也可以看出来了。

那么,这么多代码可否精简,但又能保持和以前完全一样的功能呢?

这个几乎是不可能的,如果仔细分析代码,可以看出那么多代码都只是在做一件事情:将Arduino采用的数字引脚的标号通过查表的手段映射回对应的AVR引脚。而只要这个函数允许接受任何Arduino引脚作为参数,它就必须有这样的动作存在。

但是实际情况中,PCB已经是做好的,比如LED和AVR的连接时固定不变的,自然也没有必要每次运行中都去查表。如果这个函数所操作的引脚是固定的话,自然就可以去除那些查表的操作了。

对Arduino运行库的改进:

针对上面这些分析,如果把Arduino运行库存在的这些问题都加以克服,并且仍旧继承Arduino易用的特点。那样相信会有很多人乐于使用它。从这里开始,Arduino所表示的含义将是他的运行库。硬件(Arduino板)自然是无法有什么改进的。

下面是我对arduino的改进,我将这个改进后的库称作ArduinoLite。

和Arduino运行库相比,ArduinoLite有如下增强

1. 增加了Attiny2313和Attiny26的支持

2. 支持1Mhz至20Mhz所有频率

3. 没有外部硬件假设,也就是说ArduinoLite可以用于任何支持的AVR芯片中,不需要对Arduino板兼容

4. 代码体积非常小,运行效率高

5. 新增加了一些工具函数

6. 全部改用C实现

具体的介绍和代码以及参考我将在后续的文章中给出,下面给一个直观的例子:

要在Attiny2313芯片上实现LED灯渐变点亮:假设LED连接在用ArduinoLite编号法则#9的引脚。

代码如下

#define LED_PIN 9

PIN_MODE(LED_PIN, OUTPUT);

PWM_ENABLE(LED_PIN);

unsigned char led_brightness = 0;

for (led_brightness=0; led_brightness = 255; led_brightness++)

{

   PWM_SET(LED_PIN, led_brightness);

}

 

这代码是否和用Arduino非常相似呢?编写者也不用去关心具体的寄存器配置状况。而且产生的代码是和之前直接去操作寄存器的版本完全一致的。像PIN_MODE() PWM_ENABLE() PWM_SET()最终均只产生一条指令。而且,和直接去操作寄存器相比,也有很大的优势:Attiny2313寄存器配置情况和Atmega168存在比较大的不同,采用ArduinoLite接口就完全不必考虑这种细节。自然也提高了代码的可移植性。

 

关于ArduinoLite的具体细节,请见我的后续文章:-)

分页:[«]1[»]

日历

<< 2015-6 >>

Sun

Mon

Tue

Wed

Thu

Fri

Sat

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

Copyright Shikai Chen 2000-2012. Powered By Z-Blog(CSK Modified)