【互动交通信号灯】基于Mind+ Ardunio入门教程03
本帖最后由 DFEDU 于 2019-11-27 11:01 编辑有没有试着做上面那个课后作业呢?做出来的话,说明你已经基本掌握上面所学的东西了,如果不会也没关系,我相信,看完这个章节,前面那个问题就不攻自破了!我们这回就基于上面这个交通灯来进行一个拓展,增加一种行人按键请求通过马路的功能。当按钮被按下时,Arduino会自动反应,改变交通灯的状态,让车停下,允许行人通过。
这个项目中,我们开始要实现Arduino的互动了,也会在代码学习中掌握如何创建自己的函数。这次的代码相对长一点,耐下心来,等看完这一章,相信你能收获颇丰!
所需元件■ 2×5mm LED灯 https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144918e0oxbh11mommykyv.png■ 2×5mm LED灯 https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144918a2trerc2knrr9zna.png■ 1×5mm LED灯 https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144919l7wgg55w2gwig2g0.png■ 6×220欧电阻 https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144919nutyw5rutwzdpbb4.png■ 1×按钮 https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144919d39sfycs9i2jdz2u.png
*我们之后在所需元件中将不再重复罗列以下三样,UNO、扩展板+面包板、面包线。但是,每次都还是需要用到的。
这里5个LED灯,为什么会用到了6个电阻呢?我们知道5个电阻是LED的限流电阻。还有一个电阻是给按钮的,它叫做下拉电阻(我们后面会解释)。
硬件连接
按图3-1的连线图连接你的电路。特别要注意的是,这次连线比较多,注意不要插错。下图中,面包板上标出淡绿色的不是跳线,只是为了说明纵向的孔导通,避免你插错。给Arduino上电前认真检查你的接线是否正确。在连线时,保持电源是断开的状态,也就是没有插USB线。
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144919cvwciwwb5yiiovhi.png
图3-1 互动交通信号灯连线图图形化编程 在本项目中,需要用到变量和函数,因此我们先来学习一下如何在Mind+中新建变量以及定义函数。
变量的新建
STEP1:点击“新建数字类型变量”。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/144919acsk3xhkphmf3vsc.png
STEP2:输入合适的变量名。给变量命名时,可以用字母、数字、下划线开头,Mind+里面特别开发了中文变量,输入中文时在自动生成的代码中,会自动转换成汉语拼音字母。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813ayw6a4n8gasnrcfq.png
STEP3:新变量https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813o4qg44uer44lc4c7.png已经建立好,以备之后使用。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/151820cmmlgzj4nrrdv4md.png
*因为Mind+为我们的变量加了前缀以防冲突,所以在自动生成代码中的变量名和自己起的会不同,加上了mind_n_ 的前缀。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813qbzbbga0am0ozhgh.png https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813tmb1quug61gss6uz.png
定义新函数
STEP1:点击“自定义模块”。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813bzcy0snv0g8ssycn.png
STEP2:输入合适的函数名。命名规则等同变量,一样可以输入汉字,Mind+会进行转化。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813wbq9vmqfv9o91bjb.png
STEP3:此时在指令区出现了https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813it1gsxuxzowsb173.png,我们可以在他的下方进行编程,定义此函数的内容。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813rdoc7qp1n5dnnodz.png
STEP4:在程序中调用函数,将https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112813xzhldj744d6hhgli.png拖入到程序中,即实现了函数的调用。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112814ehegehzy8euurug3.png
样例程序的输入
连接主板和电脑,打开Mind+载入扩展库,输入样例程序3-1所示程序。
需要建立三个新变量,你有发现是哪三个吗?如在编写的时候遇到困难,可以先行阅读指令学习,了解本项目中所使用的新指令。本项目中涉及的新指令较多,需要静下心来细致地消化和吸收。
样例程序 3-1:
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112817lex2emxmt891jix3.png
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112817mgb6d4xafn93gy9z.png
输入完毕后,检查是否处于https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112817hjh4xhpts5uthzgu.png点击https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112817mn7jn74n66n5p2p1.png,给Arduino下载程序。
下载完成后,可以尝试按下按钮。看看是什么样的效果?我们可以看到整个变化过程是这样的——开始时,汽车灯为绿灯,行人灯为红灯,代表车行人停。一旦行人,也就是你,按下按钮,请求过马路,那么汽车灯由绿变黄、变红,行人灯就开始由红变绿。在行人通行的过程中,设置了一个过马路的时间crossTime,一旦到点,行人绿灯开始闪烁,提醒行人快速过马路。闪烁完毕,最终,又回到了开始的状态,汽车灯为绿灯,行人灯为红灯。
图形化指令学习
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112818rpv9pphtj88ji9cm.png
乍一看指令似乎很复杂,其实理清一下思路并不难,代码学习中将带着你一步步理解每一条指令起到了什么作用,相信你一定会很快理解整个程序是如何运行的了!
代码学习
// 动态变量
volatile float mind_n_crossTime, mind_n_changeTime, mind_n_ButtonState;
// 函数声明
void DF_changelight();
// 主程序开始
void setup() {
mind_n_crossTime = 5;
mind_n_changeTime = 0;
digitalWrite(10, HIGH);
digitalWrite(8, HIGH);
}
void loop() {
mind_n_ButtonState = digitalRead(9);
if (((mind_n_ButtonState==1) && ((millis()-mind_n_changeTime)>5000))) {
DF_changelight();
}
}
// 自定义函数
void DF_changelight() {
digitalWrite(10, LOW);
digitalWrite(11, HIGH);
delay(2000);
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
delay(1000);
digitalWrite(8, LOW);
digitalWrite(7, HIGH);
delay(mind_n_crossTime * 1000);
for (int index = 0; index < 10; index++) {
digitalWrite(7, HIGH);
delay(250);
digitalWrite(7, LOW);
delay(250);
}
digitalWrite(8, HIGH);
delay(500);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
delay(1000);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
mind_n_changeTime = millis();
}
我们来看第一行代码// 动态变量
这是代码中的说明文字,可以叫做注释。
注释
这里的注释是以”//”开始,这个符号所在行之后的文字将不被编译器编译。注释在代码中是非常有用的,它可以帮助你理解代码,如果项目比较复杂,自然而然,代码也会随之非常的长,而此时注释就会发挥很大作用,可以快速帮你回忆起这段代码的功能。同样,当把你的代码分享给别人的时候,别人也会很快理解你的代码。
还有另外一种写注释的方式,用“/*…*/”,这个符号的作用是可以注释多行,这也是与上一种注释方式的区别之处。在/*和*/中间的所有内容都将被编译器忽略,不进行编译。 例如以下文字: /* 在这两个符号之间的文字, 都将被注释掉,编译器自动不进行编译, 注释掉的文字将会呈现灰色 */
Mind+将自动把注释的文字颜色变为灰色。
变量的内容较多,我们先来看第三、第四行// 函数声明
void DF_changelight();
函数声明
本课图形化编程时,在使用函数前,我们需要点击按钮,新建并命名一个新的函数。在代码编程时也是如此,需要在定义、调用函数前务必先声明!否则程序中的这一函数都无法执行。本课程中大多使用没有输入、输出的函数。它的申明格式如下:
void FunctionName ();
其他包含输入、输出的函数声明格式,在之后的项目中遇到了会具体分析。
何为变量?
volatile float mind_n_crossTime, mind_n_changeTime, mind_n_ButtonState; 第二行代码的作用是声明变量,每次使用一个新的变量前,我们都需要去对其声明,对应指令区的:https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112818lz9qizoijixmojqn.png
但变量是什么呢?
我们做个这样的比方,变量好比一个盒子,盒子的空间用来存放东西的,想要放的东西一定要比盒子小,那样才放的下,否则会溢出。变量也是一样,你存储的数据一定要在变量的范围内,否则会出现溢出。
之所以叫变量,是因为程序运行过程中,可以改变它的值。程序中,有时候会对变量值进行数字计算,变量的值也会随之发生变化。在以后的项目中,我们会有深入的了解。
在之前学习如何新建变量中,我们已经知道在给变量起名时,由于Mind+可以帮你直接转化变量名从而不产生冲突,但是在代码编程中,变量的命名有很多规则,比如在c语言中,变量名必须以一个字母开头,之后可以包含字母、数字、下划线。注意C语言认为大小写字母是不同的。C语言中还有一些特定的名称也是不能使用的,比如main,if,while等。为了避免这些特定名称作为变量名,所有这些名称在程序中显示为高亮色。
变量这个盒子无限大吗?
之前举例了变量像个盒子,那变量是不是也像盒子一般有不同的大小呢?就像生活中的收纳盒,小的用来装文具,大的可以放下书本,而有的专门用来存放衣物。
答案是有的!变量这个盒子有很多类型和尺寸,具体可以阅读表3-1。
表3-1程序中可能用到的变量数据类型https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112818sk9hvl29kvl9blvp.png
从上面表格可以看到,变量的类型有很多,不同的数对应不同的变量,int和long是针对整数变量,char是针对字符型变量,而float,double是针对含有小数点的变量。
那又有人问,设置不同大小的盒子干嘛呢?一样大不就行啦,都设置的大一点。理论上没有什么不可以的,可是我们不能忽略一个问题,那就是微控制器的内部存储容量是有限定的。电脑有内存,我们的微控制器同样有内存。像Arduino UNO板上的用的主芯片Atmega328最大内存是32K。
在Mind+中使用图形化编程新建数字类型变量指令时,为简化使用,都采用了float型的变量并加入了volatile修饰符,这是为了兼顾使用小数、整数及在中断中使用变量,在我们手动编程时则可以根据需要选择变量类型和修饰词。
全局变量和局部变量
我们这里要引用一个新的概念,是全局变量和局部变量。
全局变量,例如在第二行中所声明的变量,它能在整个程序中起作用,但条件是,必须在setup()、loop()函数外声明。volatile float mind_n_crossTime, mind_n_changeTime, mind_n_ButtonState; 局部变量,例如32行中的indexfor (int index = 0; index < 10; index++) {} 这类变量只在自己的代码内起作用。就像我们这里for循环中的变量index,它就是个局部变量。因此在项目二中每个for循环中都有一个变量index,但它们不冲突的原因,就是它们只在自己的循环中执行。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112818hg9waff03nd3p966.png
我们接着往下看代码,会在14行发现一个新的函数 digitalRead()mind_n_ButtonState = digitalRead(9);■ digitalRead() https://mc.dfrobot.com.cn/data/attachment/album/201911/25/114445roeoo72mm1ee2meo.png 这个函数是用来读取数字引脚状态,HIGH还是LOW(其实HIGH还有一种表达就是“1”,LOW是“0”,只是HIGH/LOW更直观)。函数需要一个传递参数——pin,这里需要读取的是按键信号,按键所在引脚是数字引脚9。
并且把读到的信号传递给变量mind_n_ButtonState,用于后面进行判断。ButtonState为 HIGH或者说为1时,说明按键被按下了。ButtonState为LOW或者0,表明按键没被按下。
*本章之前已经讲解了Mind+在代码中给变量命名时会增加前缀,行文中在提到变量时以命名时输入的变量名代替。
所以,可以直接检查state的值来判断按钮是否被按下: if (((mind_n_ButtonState==1) && ((millis()-mind_n_changeTime)>5000))) {
changelight();
} 这里涉及新的语句——if语句。
■ if语句
它是一种条件判断的语句,判断是否满足括号内的条件,如满足则执行花括号内的语句,如不满足则跳出if语句。
if语句格式如下:
if(表达式){ 语句; }
表达式是指我们的判断条件,通常为一些关系式或逻辑式,也可是直接表示某一数值。如果if表达式条件为真,则执行if中的语句。表达式条件为假,则跳出if语句。
我们代码中,第一个条件是ButtonState变量为HIGH。如果按键被按下,ButtonState就会变为HIGH。第二个条件是millis()的值减changeTime的值大于5000。这两个条件之间有个“&&”符号。这是一种逻辑运算符,表示的含义是两者同时满足。
在if的判断条件中,我们可以发现这(millis()-mind_n_changeTime)>5000中有一个新的函数。
■ millis()
millis()是一个函数,该函数是Arduino语言自有的函数,它返回值是一个时间,Arduino开始运行到执行到当前的时间,也称之为机器时间,就像一个隐形时钟,从控制器开始运行的那一刻起开始计时,以毫秒为单位。变量changeTime初始化时,不存储任何数值,只有在Arduino运行之后,将millis()值赋给它,它才开始有数值,并且随着millis()值变化而变化。通过millis()函数不断记录时间,判断两次按键之间的时间是不是大于5秒,如果在5秒之内不予反应。这样做能防止重复按键而导致的运行错误。
逻辑运算符
指令学习中已经提到了逻辑运算符,在if语句的判断条件中&&也是其中之一,常用的逻辑运算符有:
&&—— 逻辑与(两者同时满足) || —— 逻辑或(两者其中一个满足) ! —— 逻辑非(取反,相反的情况)
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/112819kj0ns0sea1fikifs.png
表中的1、0,既能够分别代表着高(1)、低电平(0),也能够代表着判断后的真(1)、假(0)
例如 (10 > 8) && (9<5)这一句的返回值是多少呢?
10>8我们判断后这是正确的,那么这个比较语句的返回值为1。9<5 显然是假的,返回值为0。整个语句可以简化为1 && 0, 再由上方的真值表可以得出,(10 > 8)&&(9<5 )的返回值为0。
if语句内只有一个函数:
■ changeLights();
这是一个函数调用的例子。该函数单独写在了loop()函数之外。我们需要使用的时,直接写出函数名就可以实现调用了。该函数是void型且后面括号内为空,所以是无返回值、无传递参数的函数。当函数被调用时,程序也就自动跳到它的函数中运行。运行完之后,再跳回主函数。需要特别注意的:函数调用时,函数名后面的括号不能省,要和所写的函数保持一致。changeLights() 函数内部程序请自行理解,这里就不做说明了。
硬件回顾
按键开关
按键一共有4个引脚,图3-2分别显示了正面与背面。而图3-3则说明了按键的工作原理。一旦按下后,左右两侧就被导通了,而上下两端始终导通。
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/141705y4hanhplgpgcuhns.png图 3-2 按钮结构图
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142445gzj7qrvc7jjvq1ue.png
图 3-3 按钮原理图
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142445yfeu22upu77pezwp.png
图 3-4 按钮示意图
图3-4传达的意思是,按钮就是起到一个通断的作用。在我们这个项目中,按钮控制数字引脚是否接高电平(接5V)。按下的话,数字引脚9就能检测到为高电平。否则就是保持一个低电平的状态(接GND)。
什么是下拉电阻?
下拉电阻这个名词可能比较抽象,就从字的含义着手,“下拉”我们就理解为把电压往下拉,降低电压。https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142445afe6n6f2kd4ck6kj.png
图 3-5 未接下拉电阻
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142446tcotfjddzfgf6znh.png图 3-6 有下拉电阻 按键作为开关。当输入电路状态为HIGH的时候,电压要尽可能接近5V。输入电路状态为LOW的时候,电压要尽可能接近0V。如果不能确保状态接近所需电压,这部分电路就会产生电压浮动。所以,我们在按钮那里接了一个电阻来确保一定达到LOW,这个电阻就是所谓下拉电阻。可以从上面两张图看到,第一张是未接下拉电阻的电路,按键没被按下时, input引脚就处于一个悬空状态。空气会使该引脚电压产生浮动,不能确保是0V。然而第二张是接了下拉电阻的电路,当没被按下时,输入引脚通过电阻接地,确保为0V,不会产生电压浮动现象。
课后作业
1. 本项目内容较多,但还是希望同学们能够花点时间自己动手输入代码,不荒废每次练习的机会。*仅编译后出现报错的可能原因(2):https://mc.dfrobot.com.cn/data/attachment/album/201911/25/141706rk2ekzrzrkm5yrr7.png
这一问题经常出现于自己声明函数,在写完函数内容后缺失了 } 这一花括号
2. 任意颜色LED 6个,做一个流水灯的效果,6盏灯从左至右依次点亮,然后再从右至左依次熄灭
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142446grttjtj4y8tatjtt.png
3. 如果上面那个你已经完成了的话,可以尝试一下,先从中间的灯开始亮起,依次向两边扩开。下图是个变换过程的示意图。
https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142447yis7ai8oo0q17ysd.png
4. 再比如,从左至右,依次亮起1个,2个,3个…… https://mc.dfrobot.com.cn/data/attachment/album/201911/25/142447ge443hb61y1eyz34.png
5. 再结合按钮,用按键开关和LED互动。 用一个按键,按一下控制灯亮,再按一下控制灯灭。 又或者用两个按键,一个控制灯亮,另一个控制灯灭。 玩儿法有很多,就全靠你的想象了!
谢分享资料 DFEDU 发表于 2019-11-27 10:55
有没有试着做上面那个课后作业呢?做出来的话,说明你已经基本掌握上面所学的东西了,如果不会也没 ...
我在if的前面加了一个自动换灯的程序,删除了计数,if句不变。但是实际程序之执行了自动换灯,当我想按下按钮的换灯的时候没有反应,是不是我漏了什么?或者是那个int state要放到哪里才能自动换灯和if都能运行。 好详细啊 怎么在nano板子上插线呢
页:
[1]