• 我要投稿  加入收藏
内容详情

Arduino+Tick Tock扩展板编程入门

时间:2017-06-21 15:30:49  作者:臧海波  来源:  查看:222  评论:0
导读: 本文以Tick Tock扩展板为例,说说怎么编写简单的Arduino程序。因为主要的对象是入门者,涉及的内容比较基础,文中也加入了我从Arduino开始接触开源硬件的一些心得体会,对没接触过编程的电子爱好者也可以起到一定参考作用。 Arduino扩展板简介 如果你手里只有一块..
无标题1.png

  本文以Tick Tock扩展板为例,说说怎么编写简单的Arduino程序。因为主要的对象是入门者,涉及的内容比较基础,文中也加入了我从Arduino开始接触开源硬件的一些心得体会,对没接触过编程的电子爱好者也可以起到一定参考作用。

Arduino扩展板简介

  如果你手里只有一块Arduino,基本上什么也做不了,既无法点亮LED,也无法驱动舵机。为了让Arduino控制外部设备,必须先把它的引脚引出来,这就用到了扩展板。一般扩展板是插在Arduino上使用的,这种安装方式很像给Arduino披上了一件护甲,因此英文名称为“Shield(盾、护罩)”。

  因为Arduino是开源硬件,有经验的玩家可以自己制作扩展板,而对大多数人和初学者来说,更简单的方法是直接购买成品。现在市面上可以买到各种功能的Arduino扩展板,其中为Arduino UNO配套设计的扩展板占了大多数,比如传感器扩展板、L298/L293电机驱动板、继电器扩展板、网络扩展板、音频扩展板等。我们所说的Arduino编程,其实就是用Arduino程序来驱动这些扩展板,达到控制各种外部设备的目的。

  全国“少年电子技师”科普活动之所以选中了Tick Tock扩展板作为Arduino智能控制入门项目,最大的原因是它既非成品,也不是需要自己采购各种零配件的DIY项目,而是以套件形式存在的。这块板子需要用户自己焊接,考验用户对基础电子元器件的辨识能力和硬件电路制作能力。当然,它在设计上保证了制作的成功率,只要遵照规程制作,一般不容易出现制作失败的情况。

  有了制作成功的Tick Tock扩展板,用户就可以通过它来了解Arduino IDE(集成开发环境)和程序了。Arduino IDE是为了降低软件开发难度而设计出来的一种程序,包含源代码编辑器和程序的编译(把源代码转换成机器码)、上传功能。

  Tick Tock扩展板配套的库自带了7个示例,一般来说只要焊接无误,打开IDE上传示例就可以让这块板子运转起来。比如上传RealTimeClock示例,你就得到了一块可以显示温度和时间的多功能智能闹钟。用单片机制作一块电子表,对初学者来说是一道比较高的门槛,掌握了这项技术,用单片机开发其他应用,基本上也就难不倒你了。但是先不要得意,因为Tick Tock的程序是现成的,你做的只是在IDE里打开和编译、上传而已,自己并没有编写任何程序。虽然Arduino是一块在软件编程方面做了简化的单片机,但也不要指望分分钟就能学会编程。

Tick Tock扩展板编程

  如果你之前没接触过编程,特别是对C语言(Arduino程序设计是以C语言为基础的,也有一些自己特有的风格)和面向对象编程没什么了解,在阅读Tick Tock自带的7个示例时会感到特别困难。这是因为原作者的编程风格是面向高手的,里面用到了大量的函数和类库。我的建议是充分利用Arduino简单、直观的优点,从最基础的蜂鸣器和LED的控制开始学习,暂时先不要急着研究官方示例。为了使本文起到承上启下的过渡作用,下面例子中的命名方式沿用了原作者的习惯,当你的编程水平达到一定程度时,可以对照官方示例进一步深入学习。

  Arduino的优点是软硬件关系非常透明,这样用户就可以很轻松地用程序指挥硬件。下面的例子全部按照这个思路展开。针对Arduino扩展板的编程大体上可以分为3步:理顺软硬件接口的对应关系、搞清楚协议,编写程序。参考Tick Tock扩展板官方wiki提供的数据,可以整理出部分接口对应关系(见表1)。

                 1 Tick Tock扩展板上的部分接口对应关系

屏幕快照 2017-06-21 下午4.12.38.png

  这几个元器件连接的都是Arduino的数字引脚,下面我们的任务是编写程序控制相应数字引脚上的电平和时序,最终实现对这些元器件的控制。

  作为输出设备,蜂鸣器和LED的“协议”比较简单。这里的协议之所以打引号,是因为这类设备的控制非常简单。也有一些设备会用到稍微复杂一点的协议,比如I2C和串口通信。套件上提供的蜂鸣器是有源蜂鸣器,元器件里面内置了一个振荡器,通电就可以发出“滴”音。扩展板上的LED串联了限流电阻,也是直接通电就可以点亮。用单片机的说法,只要给它们一个高电平信号就可以使其运转。

  按钮开关K3是一个输入设备,搞清它的“协议”稍微有点麻烦,需要对照电路图查看K3连接的是下拉模式还是上拉模式。因为本文主要介绍的是编程,硬件部分不作深入探讨,请读者自行查阅Tick Tock官方wiki提供的电路图。这里直接给出答案:K3跨接在ArduinoD11GND之间,启用Arduino内部的上拉电阻以后,按下开关,D11电平为低。

小标:1让蜂鸣器发出“滴”音

   有了上面这些信息,就可以为这些元器件编写程序了。先从让蜂鸣器发音开始,编写一个小程序。

   注:Arduino程序中,双斜杠后面的文字为注释,IDE不会对其进行编译。为便于初学者阅读理解,对关键语句做了比较详细的注释。除#definde末尾可以为空外,每行命令的末尾均要加分号。以上也是C语言的规则。

//设备初始化

void setup() {           

  pinMode(6,OUTPUT); //D6脚设置为输出

  digitalWrite(6,HIGH); //D6脚输出高电平

}

//主程序

void loop() {

}

  这段程序非常直观。主程序loop()部分是空的,因为我们只需要让D6脚输出高电平(+5V),初始化一下设备即可。首先告诉Arduino你要怎么使用D6脚,用pinMode()函数把D6脚设置为输出。单片机的I/O引脚既可以作为输入,也可以作为输出,但是你先要通过程序告诉它工作在哪个模式才能正常使用。接下来用digitalWrite()函数让D6脚输出一个高电平,因为setup()函数仅在单片机上电的时候运行一次,所以D6脚会始终保持在这个状态。效果就是蜂鸣器发出持续不断的“滴”音。这里用到的函数都是Arduino的内置函数,功能可以根据字面意思理解,简单易懂。由于篇幅所限,函数的详细说明请参考Arduino官网,这里不再赘言。

小标:2 让蜂鸣器发出有间隔的“滴滴”音

  你也许会认为这个例子太简单了,用一块电池和一根导线就可以让蜂鸣器发音。但是要知道,程序控制的意义远不止于此。程序不光可以让D6脚输出高电平奏响蜂鸣器,还可以让它输出低电平令蜂鸣器停止发音。引入延迟函数以后,更可以控制蜂鸣器发出精确而有节奏的声音。你可能会马上联想到生活中常见的闹钟,如果你是个“火腿族(HAM)”,还会联想到莫尔斯码的“嘀嘀嗒嗒”声。下面我们就分别编写一段模拟闹钟和莫尔斯码的小程序。

#define ALARM_BUZZER 6   //D6脚定义为蜂鸣器

//设备初始化

void setup() {

  pinMode(ALARM_BUZZER,OUTPUT);  //ALARM_BUZZER(即D6脚)设置为输出

}

//主程序

void loop() {

  digitalWrite(ALARM_BUZZER,HIGH);   //D6脚输出高电平,蜂鸣器鸣响

  delay(200);                           //延迟0.2s

  digitalWrite(ALARM_BUZZER,LOW);    //D6脚输出低电平,蜂鸣器息音

  delay(200);                            //延迟0.2s

  digitalWrite(ALARM_BUZZER,HIGH);   // 高电平鸣响

  delay(200);                            //延迟0.2s

  digitalWrite(ALARM_BUZZER,LOW);    //低电平息音

  delay(2000);                           //延迟2s

}

  这个程序的执行效果为蜂鸣器发出单音间隔为0.2s、“滴滴”间隔为2s的信号音。

  程序开头部分的“#define ALARM_BUZZER 6用宏定义的方法把蜂鸣器指定为D6脚,这样以后凡是用到D6脚时,都用ALARM_BUZZER(名字可以自己起)代替,防止出现当程序越来越复杂,使用到的引脚越来越多时,调整一个硬件引脚就要翻遍整个程序的情况。举一个例子说明用“#define”定义的好处:在D12脚增加了一个蜂鸣器,原来D6脚那个不用了,只要把6换成12即可,后面的程序不需要作任何修改。

  和前面的例子一样,用digitalWrite()函数让蜂鸣器获得高电平发出声音,同样的函数也可以发送低电平让蜂鸣器停止发音。延迟函数delay()的作用是让系统暂时挂起,什么也不做,它的单位是毫秒,delay(200)就是延迟200ms0.2s)。把这几行命令放在主程序loop()的大括号中循环执行,是为了让蜂鸣器不停地发出“滴滴”音。感兴趣的朋友可以做个试验,把这几行代码像上一个例子那样都放在setup()函数的大括号里,让主程序空着,看看会出现什么效果。

小标:例3 让蜂鸣器以莫尔斯码发出333短的SOS求救信号

#define ALARM_BUZZER 6    //D6脚定义为蜂鸣器

//设备初始化

void setup() {

pinMode(ALARM_BUZZER,OUTPUT);  //设置蜂鸣器输出脚

}

//主程序

void loop() {

  diDa(200);     //蜂鸣器发出3短音,即莫尔斯码的S

  diDa(200);

  diDa(200);

  delay(300);    //间隔0.3s

  diDa(500);    //蜂鸣器发出3长音,即莫尔斯码的O

  diDa(500);

  diDa(500);

  delay(300);   //间隔0.3s

  diDa(200);    //蜂鸣器发出3短音,即莫尔斯码的S

  diDa(200);

  diDa(200);

  delay(2000);  //SOS发送完毕,大间隔2s

 }

//自定义函数diDa()

void diDa(int microsecond)

{

  digitalWrite(ALARM_BUZZER,HIGH);

  delay(microsecond);

  digitalWrite(ALARM_BUZZER,LOW);

  delay(microsecond);

}

  让蜂鸣器发出SOS信号,意味着要在loop()主程序的一个循环里反复向蜂鸣器发送9组高低电平切换信号。如果按照例2的方法,要重复写很多行digitalWrite()delay(),看起来非常累赘。为此,我们把重复的部分写成一个自定义函数diDa(),放到主程序loop()的后面。

  一般来说,Arduino IDE对自定义函数的位置没有特殊要求,既可以放在loop()前,也可以放在loop()后。为了方便阅读和管理,还可以新建一个标签,把它保存成一个单独的文件。这个自定义函数前面的void表示它不返回数值,在主程序里调用一次,它就执行一次。小括号内名为microsecond的参数是一个整数型变量,控制蜂鸣器开启和关断的时间。举个例子,在主程序中调用diDa(200),程序执行的是下面这个过程:

  digitalWrite(ALARM_BUZZER,HIGH);

  delay(200);

  digitalWrite(ALARM_BUZZER,LOW);

  delay(200);

  程序最大的妙处是可以发挥出元器件的最大潜力,不需要对硬件进行修改。一个小小的蜂鸣器就可以在程序控制下呈现出无穷多种变化。除了让它发出厂家设置的标准“滴”音(取决于内置振荡器的频率),还可以用程序改变它的音调。有精力的读者可以研究一下官方示例changeThePitch里面的playTone()函数。

void playTone(int tone, int duration) {

  for (long i = 0; i < duration * 1000L; i += tone * 2) {

    digitalWrite(ALARM_BUZZER,HIGH);

    delayMicroseconds(tone);

    digitalWrite(ALARM_BUZZER,LOW);

    delayMicroseconds(tone);

  }

}

  你可以把这个函数放入例3,在主程序里调用它,给toneduration分配不同的数值,看看会出来什么效果。这个函数的功能有点类似用PWM给电机调速,只不过这里的受控对象换成了蜂鸣器。注意函数里面的“duration * 1000L后面的L是告诉编译器这是一个长整数(32bit)运算,防止溢出。比如duration300,300×1000=300000,这个结果大大超出了整数(16bit)的范围。

小标:4 4个板载LED显示流水灯

  有了声音,下面再加入一些灯光效果。掌握了前面的几个例子,编写LED的控制程序就简单多了。例4的功能是让扩展板上编号为D1~D4(注意此D4非彼D4,不要和Arduino的数字脚编号混淆)的4LED依次点亮再全部熄灭,形成一个流水灯效果。

#define LED1 2  //根据表1,依次指定LED对应的数字引脚

#define LED2 3

#define LED3 4

#define LED4 5 

//设备初始化

void setup() {

pinMode(LED1,OUTPUT); //把对应的数字引脚设置为输出

pinMode(LED2,OUTPUT);

pinMode(LED3,OUTPUT);

pinMode(LED4,OUTPUT);

}

//主程序

void loop() {

digitalWrite(LED1,HIGH);   //D2脚输出高电平,编号为D1LED点亮

delay(500);                 //延迟0.5s

digitalWrite(LED2,HIGH);    //D3脚输出高电平,编号为D2LED点亮

delay(500);                 //延迟0.5s

digitalWrite(LED3,HIGH);   //D4脚输出高电平,编号为D3LED点亮

delay(500);                 //延迟0.5s

digitalWrite(LED4,HIGH);    //D5脚输出高电平,编号为D4LED点亮

delay(500);                   //延迟0.5s

digitalWrite(LED1,LOW);      //D1~D4全部熄灭

digitalWrite(LED2,LOW);

digitalWrite(LED3,LOW);

digitalWrite(LED4,LOW);

delay(500);                  //延迟0.5s

}

  LED和蜂鸣器的控制方式差不多,区别是一个发光,一个发声。感兴趣的读者可以试着调整主程序中的代码,改变4LED的闪烁模式,比如让D1~D4呈现跑马灯效果,依次点亮。

  前面举的例子都是系统上电以后程序自己执行,不需要人为干预。接下来的例子我们要启用一个输入设备K3,使用户可以通过按钮开关控制程序的执行效果,进行最简单的人机交互。Arduino的编程方法除了在IDE中敲代码,还流行一种更友好的图形界面编程。下面的例子我们试着换一种方式编写程序。

图形化编程

  感谢开源和共享精神使现在的业余爱好者可以获得和使用大量超赞的软硬件工具,这对入门学习和简化工作都起到了极大帮助。为Arduino设计的第三方图形化编程工具有很多,比如MIT的Scratch、新车间的ArduBlock和北京师范大学的Mixly(米思齐)。这些工具比官方IDE更简单和直观,受到了很多初学者的喜爱。下面就以新车间的ArduBlock为例编写一个图形化程序,其他图形化编程工具的操作方法与此类似。

 

  首先安装ArduBlock。注意ArduBlock作为一个插件,需要通过Arduino IDE启动,且IDE版本不能低于1.5。我使用的是ArduBlock教育版1.0,把压缩包中的libraries和tools文件夹复制到Arduino的安装目录下就完成了安装。打开Arduino IDE以后,单击“工具”菜单里的“ArduBlock”选项即可启动图形化编程界面(见图1、图2)。

  图形化编程操作起来非常简单。根据设计需要,把左侧的模块拖放到右侧的工作区,像积木一样拼接在一起,设置好关键参数,就完成了编程 

 

  首先安装ArduBlock。注意ArduBlock作为一个插件,需要通过Arduino IDE启动,且IDE版本不能低于1.5。我使用的是ArduBlock教育版1.0,把压缩包中的libraries和tools文件夹复制到Arduino的安装目录下就完成了安装。打开Arduino IDE以后,单击“工具”菜单里的“ArduBlock”选项即可启动图形化编程界面(见图1、图2)。

  图形化编程操作起来非常简单。根据设计需要,把左侧的模块拖放到右侧的工作区,像积木一样拼接在一起,设置好关键参数,就完成了编程 

无标题3.png

1 安装成功后,可以在IDE的“工具”菜单里看到“ArduBlock”选项

图片3.png

2 ArduBlock图形化编程界面

小标:例5 带声光效果的莫尔斯码练习器

   接下来我们编写一个带声光效果的莫尔斯码练习器,需要用到扩展板上的3个设备:K3D1BUZ1。根据表1,可以知道它们对应的引脚分别是1126

  首先定义K3D1BUZ1的引脚和初始电平值。单击左侧菜单的控制选项,把第2个模块(带有程序、设定、循环字样的那个)拖放到右侧工作区(见图3)。这个模块转换成文字版,其实就是Arduino IDE启动后默认的setup()loop()    


图片3.png

3 把主程序模块拖放到右侧工作区

  然后单击“引脚”选项,把第1个“设定针脚数字值”的模块拖放到右侧工作区,与之前那个模块的“设定”右侧向右开口的凹槽对齐。对齐以后,松开鼠标会听到“咔哒”一声,表示模块已经连接。

  图形化编程中模块的连接有两个窍门:一是注意凹槽的形状,不同形状的模块是不匹配的,无法连接在一起,这个功能是一种非常有效的防错设计;二是注意对齐的方向,要把模块向左、向上与凹槽对齐,用鼠标抓着一个模块直接放在凹槽上,是不会出现任何变化的。

  接着再次单击“引脚”选项,把另外两个同样的模块拖放到之前那个模块的下方。最后设定这3个模块的参数,它们代表的是K3D1BUZ1,对应的引脚分别是1126,引脚初始电平为高、低和低(见图4)。此时系统上电以后,程序会先把D11看作一个输出脚,向它发送高电平信号,这个操作会启用Arduino的内部上拉电阻(见前面K3的说明)。默认状态下LED是熄灭的,蜂鸣器也处于静音状态。


图片4.png

4 设定K3D1BUZ1的初始状态

  注意模块化编程出于简化的考虑,无法像前面IDE中那样用“#define”来定义各个引脚功能。因此需要你牢牢记住哪个编号的引脚连接的是哪个设备。

  下面开始“编写”(拼接)核心程序。这个莫尔斯码练习器以K3作为输入设备,设计上是按下K3以后D1点亮,蜂鸣器发出“滴”音;松开K3LED熄灭,蜂鸣器静音。我们要用到一个条件判断模块,当K3被按下、电平为低时,激活LED和蜂鸣器,反之让它们熄灭和静音。单击左侧菜单的“控制”选项,把“如果/否则”模块拖放到主程序模块的“循环”凹槽内(见图5)。 

 

  注意模块化编程出于简化的考虑,无法像前面IDE中那样用“#define”来定义各个引脚功能。因此需要你牢牢记住哪个编号的引脚连接的是哪个设备。

  下面开始“编写”(拼接)核心程序。这个莫尔斯码练习器以K3作为输入设备,设计上是按下K3以后D1点亮,蜂鸣器发出“滴”音;松开K3LED熄灭,蜂鸣器静音。我们要用到一个条件判断模块,当K3被按下、电平为低时,激活LED和蜂鸣器,反之让它们熄灭和静音。单击左侧菜单的“控制”选项,把“如果/否则”模块拖放到主程序模块的“循环”凹槽内(见图5)。 

图片5.png

5 添加“如果/否则”判断模块

  接下来是最关键的一步,我们需要让程序判断开关是否被按下(11是否为低电平),很明显这是一个逻辑运算。单击左侧菜单的“逻辑”运算符选项,把第7个模块拖放到“条件满足”凹槽内。然后点击左侧菜单的引脚选项,把第1个“数字针脚”模块拖放到逻辑运算符双等于号左侧的凹槽内。这里你可以体会一下模块和凹槽的对齐操作。虽然第3个和第7个逻辑运算符的功能看似一样,但是它们的凹槽形状不同,只有形状一致和左上对齐才能把模块连接在一起。最后从“常量/变量”选项下拖放一个“低(数字)”模块,放入双等于号右侧的凹槽内。图6所示是组装完成的“条件满足”模块。

 

 

  注:这一步还有另外一层含义,判断11脚电平状态是对一个输入事件进行检测,因此程序又把ArduinoD11定义成了一个输入脚。这个小细节可以在后面ArduBlockIDE中生成的源代码里看到。

图片6.png

6 判断D11脚是否为低电平的“条件满足”模块

  接下来的“执行/否则”执行模块的组装就非常简单了,读者一定已经有了自己的答案。当D11脚为低时,执行让D2D6脚电平为高的操作,令LED点亮,蜂鸣器发音;否则就执行让它们低电平静默的操作。为了改善手感,我还加入了一个延迟模块,松开开关以后延迟0.2s再关断LED和蜂鸣器,产生一个“余烬”效果,使你不会觉得硬件状态的切换太过生硬(见图7)。

图片7.png

7 最终的莫尔斯码练习器程序(例5

   实际上我对这个系统非常满意,最后干脆从K3引出两根延长线,连接了一个老式发报机使用的重型电键(见图8)。因为Arduino可以连接计算机,可以再写一个串口通信程序把练习数据输入电脑,或者批量发送预先编制好的莫尔斯码。这个用Tick Tock扩展板改造的莫尔斯码练习器从功能和使用体验上比以前用NE555自制的电码练习器好多了,程序又一次获得了胜利。

无标题17.png

8 Tick Tock制作的电码练习器

   ArduBlock图形化编程的最后一步操作是单击编辑界面顶部的“上载到Arduino”按钮,程序会先在IDE中生成源代码,再编译、上传到Arduino控制板。这种自动生成源代码的设定对初学者非常有帮助,我们可以看一下ArduBlock图形化编程生成的莫尔斯码练习器源代码。

void setup()

{

  pinMode( 11, INPUT);

  pinMode( 11 , OUTPUT);

  pinMode( 2 , OUTPUT);

  pinMode( 6 , OUTPUT);

  digitalWrite( 11 , HIGH );

  digitalWrite( 2 , LOW );

  digitalWrite( 6 , LOW );

}

void loop()

{

  if (( ( digitalRead(11) ) == ( LOW ) ))

  {

    digitalWrite( 2 , HIGH );

    digitalWrite( 6 , HIGH );

    delay( 200 );

  }

  else

  {

    digitalWrite( 2 , LOW );

    digitalWrite( 6 , LOW );

  }

}

因为有了前面的基础,这段代码解读起来会很轻松。

启用Tick Tock上的传感器

  很多人会认为Tick Tock上只有两个传感器——1个光敏电阻和1个热敏电阻,其实3个按钮开关也是一种传感器,我认为系统里能感知外部事件的元器件都可以算作传感器,这样理解有助于拓宽设计思路。一个很好的例子是PVCBOT1号机,用两个微动开关让机器人感知前方的障碍。

  前面的例子以按钮开关K3为例介绍了这种最简单的传感器的用法。因为开关只有两个状态,只要把它们连接至单片机的数字输入引脚,检测电平高低即可。光敏电阻和热敏电阻就复杂多了,这类元器件的数值随外界环境(光照强度或温度)发生变化,单片机要处理的是在一定范围内变化的量,需要先把它们连接至单片机的模拟输入引脚,经过模数转换以后才能进一步处理。

小标:例6光线和温度传感器应用实例

  参考Tick Tock扩展板官方wiki提供的电路图,可以得知光敏电阻LDR1连接的是Arduino的A0脚,热敏电阻RT1连接的是A1脚。下面的例子想实现两个功能:光线变暗时点亮发光二极管D1,温度升高时令蜂鸣器发出报警信号。为此,我们先要定量确定两个传感器处于“临界状态”时的数值。先编写一段小程序,用Arduino的串口监视器查看传感器检测到的数值。

#define TEMPERATURE_SENSOR   A0  //指定热敏电阻对应的引脚为模拟A0

#define LIGHT_SENSOR A1      //指定光敏电阻对应的引脚为模拟A1

//系统初始化

void setup() {

Serial.begin(9600);   //启用串口通信,速率设置为9600bit/s

pinMode(TEMPERATURE_SENSOR, INPUT);  //A0脚设置为热敏电阻输入

pinMode(LIGHT_SENSOR, INPUT);        //A1脚设置为光敏电阻输入

}

//主程序

void loop() {

int temp= analogRead(TEMPERATURE_SENSOR);  //读取热敏电阻,数据存入整数型变量temp

Serial.print(" temp= ");  //PC串口监视器上打印“temp=传感器度数”

Serial.println(temp);

int light = analogRead(LIGHT_SENSOR);    //读取光敏电阻,数据存入整数型变量light

Serial.print("light = "); //PC串口监视器上打印“light=传感器度数”

Serial.println(light);

delay(2000);                             //延迟2s

}

  TEMPERATURE_SENSOR和LIGHT_SENSOR沿用了Tick Tock官方示例的命名方式,读者也可以使用其他更简单易懂的名称。主程序中的temp和light是两个变量,用来存储传感器数值,程序每循环一次,变量数值就刷新一次。变量前的int告诉编译器它们是整数型变量(16bit),可存储的数值范围是-32768~32767。比较关键的一行是delay(2000),告诉程序每隔2s刷新一次传感器数据,防止串口监视器上的信息显示过快,肉眼无法看清。

  上传程序以后,打开Arduino IDE工具菜单下的“串口监视器”选项,把波特率设置为9600(一般默认就是这个数值)。监视器窗口里会每隔2s返回一组温度和光线数值,数值取决于实际环境(写这篇文章时正值深夜,我的照明设备是一台3W节能灯,温度计上显示的室温是22.6℃)。当你用手掌遮住光敏电阻,模拟光线变暗的情况,把手指按在热敏电阻上面,模拟温度升高的情况时,可以看到数值出现了变化(见图9)。我们可以整理出下面两组数据,见表2和表3。

无标题19.png

9 传感器读数的变化

                  表2 光线测试数据

屏幕快照 2017-06-21 下午4.21.08.png

                  表3 温度测试数据

屏幕快照 2017-06-21 下午4.20.27.png

  因为每个人的测试环境各不相同,实际读数可能有少许出入。从表中可以看出光敏电阻的读数随光线变暗而降低,热敏电阻的读数随温度升高而升高。为了使系统触发得更灵敏,可以在程序中把数值稍微作一下调整,最后把光线阈值设置为130,温度阈值设置为520。

  注:串口监视器回显的传感器数据属于底层数据,仅供开发人员(也包括此时的你)使用。如果换成用户界面,就要把它们转换成大家都能看懂的单位,比如光照单位勒克斯(Lx)和温度单位摄氏度()。数据的转换需要根据传感器元器件的特性用到专门的算法,比如官方示例MeasureTemperature中用到的温度转换函数getTemperature(),它的功能是读取热敏电阻数值,把转换好的摄氏度值返回给主程序。大家看一下这个函数的内容,还是比较复杂的,但是不要害怕,随着知识的不断积累,很快你也可以编写这样的函数。

int8_t getTemperature()

{

    float temperature,resistance;

    int a;

    a = analogRead(TEMPERATURE_SENSOR);

    resistance   = (float)(1023-a)*RESISTOR_CONNECT_THERMISTOR/a; //计算热敏电阻阻值

    int B = 3975;

    /*按照下面的公式计算温度*/

    temperature  = 1/(log(resistance/RESISTOR_CONNECT_THERMISTOR)/B+1/298.15)-273.15;

    return (int8_t)temperature;//把温度值从浮点转换成8bit整数

}

最终程序基本是前面几个例子的组合。

#define TEMPERATURE_SENSOR   A0  //指定热敏电阻对应的引脚为模拟A0

#define LIGHT_SENSOR A1      //指定光敏电阻对应的引脚为模拟A1

#define LED1 2                //根据表1,依次指定LED对应的数字引脚

#define ALARM_BUZZER 6         //D6脚定义为蜂鸣器

int tempThreshold = 520;          //设置温度阈值,如果报警过于灵敏,可适当加大数值

int lightThreshold = 130;         //设置光线阈值

//系统初始化

void setup() {

  Serial.begin(9600);      //启用串口通信,速率设置为9600bit/s

  pinMode(TEMPERATURE_SENSOR, INPUT);  //A0脚设置为热敏电阻输入

  pinMode(LIGHT_SENSOR, INPUT);        //A1脚设置为光敏电阻输入

  pinMode(LED1, OUTPUT);                //D2脚设置为输出驱动发光二级管D1

  pinMode(ALARM_BUZZER, OUTPUT);        //D6脚设置为输出驱动蜂鸣器

}

//主程序

void loop() {

  int temp= analogRead(TEMPERATURE_SENSOR);  //读取热敏电阻,数据存入变量temp

  Serial.print(" temp= ");  //PC串口监视器上打印“temp=传感器度数”

  Serial.println(temp);

  if ( temp > tempThreshold ) //判断温度是否升高(接近人体体表温度)

    {

        digitalWrite(ALARM_BUZZER, HIGH);   //D6脚输出高电平,蜂鸣器鸣响

        delay(200);                           //延迟0.2s

        digitalWrite(ALARM_BUZZER, LOW);    // D6脚输出低电平,蜂鸣器息音

        delay(200);                            // 延迟0.2s

        digitalWrite(ALARM_BUZZER, HIGH);   // 高电平鸣响

        delay(200);                            // 延迟0.2s

        digitalWrite(ALARM_BUZZER, LOW);    // 低电平息音

        delay(200);                            // 延迟0.2s

      }

    else                              //否则

    {

      digitalWrite(ALARM_BUZZER, LOW); //低电平息音

    }

  int light = analogRead(LIGHT_SENSOR);    //读取光敏电阻,数据存入变量light

  Serial.print("light = "); //PC串口监视器上打印“light=传感器度数”

  Serial.println(light);

  if ( light < lightThreshold )  //判断光线是否降低(接近黑暗)

    {

      digitalWrite( LED1 , HIGH );  //点亮D1

    }

    else

    {

      digitalWrite( LED1 , LOW );   //熄灭D1

    }

  // delay(2000);                   //系统正常运转,就不需要延迟了

}

  图形化编程和Arduino IDE相比稍有变化,但大体思路是一样的,下面给出ArduBlock的程序设计供读者参考,如图10所示。顺便说一下,如果程序比较大,可以利用ArduBlock下方的“Save as image”把它保存成一张图片,对教学来说非常方便。大家还可以对比一下ArduBlockIDE中生成的源代码和自己写的有什么不同。 

 

图片9.png

图10 用ArduBlock编写的例6

  上传程序后,当你用手遮住光敏电阻(模拟光线变暗的情况)后,蓝色的D1会点亮;移开手掌,D1熄灭;当你把手指按在热敏电阻上(模拟温度升高的情况)时,蜂鸣器会发出“滴滴”警报;拿开手指,警报停止。你会发现一个小问题,就是遮光以后,LED会快速闪烁,而不是像我们期望的那样保持常亮。其原因是串口通信在一些情况下有可能会对单片机的时序造成干扰,可以试着提高通信速率解决,比如把速率从9600bit/s提高至57600bit/s。一般仅在调试时开启串口。

小标:7二进制计数器

  这里例子中我们来利用按键K3和4个LED制作一个二进制的计数器。

  通常计数器的功能就是当我们按下按键的时候,在显示区域会显示相应的按下按键的次数,按一次相应的数值就增加1。而二进制的计数器就是显示的结果是通过LED以二进制的形式来显示出来。

  二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”。当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的。在这里的显示当中可以用LED的亮表示1,灭表示0。

  在二进制中,11应该等于2,因为没有2的值,只能向上一个数位进一,就是采用满二进一的原则,这和十进制是采用满十进一原则完全相同。所以在二进制中

1+1=1010+1=1111+1=100100+1=101101+1=110110+1=111......

  由此可见在二进制中10表示二,100表示四,1000表示八,对应到这里的4LED,就是只有红色的D4亮时表示8,只有红色的D3亮时表示4,只有绿色的D2亮时表示2,只有蓝色的D1亮时表示1。如果4LED亮的状态是“亮亮灭灭”的话,则对应的值是1100,表示的数值就是8+4=12

  了解了以上的内容后,我们就来制作这个二进制的计数器。为了让Arduino能够记住现在按键按下的次数,您需要定义一个变量num,具体程序如下

#define LED1 2  //根据表1,依次指定LED对应的数字引脚

#define LED2 3

#define LED3 4

#define LED4 5 

int num = 0;

int temp;

//设备初始化

void setup() {

pinMode(LED1,OUTPUT); //把对应的数字引脚设置为输出

pinMode(LED2,OUTPUT);

pinMode(LED3,OUTPUT);

pinMode(LED4,OUTPUT);

pinMode( 11, INPUT_PULLUP);

}

//主程序

void loop() {

if (digitalRead(11) == LOW)

{

    delay(200);                                 //延时去抖

    num = num + 1;

}

temp = num % 16;

if(temp / 8 == 1)            //根据第4位的值决定是否点亮第4LED(从右望左数)

{

digitalWrite(LED4,HIGH);

}

else

{

digitalWrite(LED4,LOW);

}

temp = temp % 8;

if(temp / 4 == 1)            //根据第3位的值决定是否点亮第3LED

{

digitalWrite(LED3,HIGH);

}

else

{

digitalWrite(LED3,LOW);

}

temp = num % 4;

if(temp / 2 == 1)            //根据第2位的值决定是否点亮第2LED

{

digitalWrite(LED2,HIGH);

}

else

{

digitalWrite(LED2,LOW);

}

temp = temp % 2;

if(temp  == 1)            //根据第1位的值决定是否点亮第1LED

{

digitalWrite(LED1,HIGH);

}

else

{

digitalWrite(LED1,LOW);

}

}

  这里注意我们用了除和余两种运算符号,分别是/和%。对于Arduino来说,一个除法运算只能够得到结果的整数值,比如5/3得到的结果是1,4/3得到的结果也是1,而6/3得到的结果就是2。取余的运算能够得到除完之后余下的值,比如5%3得到的结果就是2,而4%3得到的结果是1。

  现在你手里的Tick Tock扩展板摇身一变,成为了一个名副其实的智能硬件。你可以用PC端的串口监视器查看传感器上的数据,可以根据需要在程序里设置合理的阈值,还可以把LED和蜂鸣器的信号引出来,通过继电器或可控硅控制房间内的灯具或风扇,打造一个属于自己的智能家居系统。

  希望这篇文章可以在你的学习道路上助一臂之力。

套件购买链接:

https://item.taobao.com/item.htm?spm=a1z10.5-c.w4002-15794170327.16.4lDdjH&id=553526460741

 

 

 

 

 

 

 

 

 


关键词:科普

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到:  分享到QQ空间
Google提供的广告
最新文章
热门文章
图片主题
推荐文章
关于“少年电子技师” - 联系我们 - 法律声明 - 友情链接 - 《无线电》杂志
Powered by qibosoft V7.0 Code © 2003-10 qibosoft
Copyright© 全国“少年电子技师”认定活动组织委员会 all rights reserved. 北京博趣出版有限责任公司北京八中亦庄分校技术支持 京ICP备10040526号