2014-1-7 18:48:20 只看该作者
161371浏览
查看: 161371|回复: 82
打印 上一主题 下一主题

[入门教程] Arduino教程 04 互动交通信号灯「DFR0100」

  [复制链接]

项目三 互动交通信号灯

有没有试着做上面那个课后作业呢?做出来的话,说明你已经基本掌握上面所学的东西了,如果不会也没关系,我 相信,看完这个章节,前面那个问题就不攻自破了!我们这回就基于上面这个交通灯来进行一个拓展,增加一种行 人按键请求通过马路的功能。当按钮被按下时,Arduino会自动反应,改变交通灯的状态,让车停下,允许行人通 过。

这个项目中,我们开始要实现Arduino的互动了,也会在代码学习到如何创建自己的函数。这次的代码相对长一 点,耐下心来,等看完这一章,相信你能收获不少

所需元件

Arduino教程 04 互动交通信号灯「DFR0100」图1

这里5个LED灯,为什么会用到了6个电阻呢?我们知道5个电阻是LED的限流 电阻。还有一个电阻是给按钮的,它叫做下拉电阻(我们后面会解释)。

硬件连接

Arduino教程 04 互动交通信号灯「DFR0100」图2
按图3-1的连线图连接你的电路。特别要注意的是,这次连线比较多,注意 不要插错。下图中,面包板上标出淡绿色的不是跳线,只是为了说明纵向 的孔导通,避免你插错。给Arduino上电前认真检查你的接线是否正确。在 连线时,保持电源是断开的状态,也就是没有插USB线。

输入代码

输入下面的样例代码3-1,这段代码引自《beginning-arduino》一书。

样例代码 3-1:

//项目三 -- 互动交通信号灯
int carRed = 12; //设置汽车灯
int carYellow = 11;
int carGreen = 10;
int button = 9; //按钮引脚
int pedRed = 8; //设置行人灯
int pedGreen = 7;
int crossTime = 5000;//允许行人通过的时间
unsigned long changeTime;//按钮按下后的时间

void setup() {
        //所有LED设置为输出模式
                pinMode(carRed, OUTPUT);
                pinMode(carYellow, OUTPUT);
                pinMode(carGreen, OUTPUT);
                pinMode(pedRed, OUTPUT);
        pinMode(pedGreen, OUTPUT);
        pinMode(button, INPUT); //按钮设置为输入模式
        digitalWrite(carGreen, HIGH); //开始时,汽车灯绿灯
                digitalWrite(pedRed, LOW);  //行人灯为红灯
}

void loop() {
                int state = digitalRead(button);
                //检测按钮是否被按下,并且是否距上次按下后有5秒的等待时间
if(state == HIGH && (millis() - changeTime)> 5000){
          //调用变灯函数
          changeLights();
}
}

void changeLights() {
                digitalWrite(carGreen, LOW); //汽车绿灯灭
                digitalWrite(carYellow, HIGH); //汽车黄灯亮
                delay(2000); //等待2秒

                digitalWrite(carYellow, LOW); //汽车黄灯灭
                digitalWrite(carRed, HIGH); //汽车红灯亮
        delay(1000); //为安全考虑等待1秒

                digitalWrite(pedRed, LOW); //行人红灯灭
                digitalWrite(pedGreen, HIGH); //行人绿灯亮

                delay(crossTime); //等待一个通过时间

                //闪烁行人灯绿灯,提示可过马路时间快到
                for (int x=0; x<10; x++) {
                                digitalWrite(pedGreen, HIGH);
                                delay(250);
                                digitalWrite(pedGreen, LOW);
                                delay(250);
}                        
                digitalWrite(pedRed, HIGH);//行人红灯亮
                delay(500);

                digitalWrite(carRed, LOW); //汽车红灯灭
                digitalWrite(carYellow, HIGH); //汽车黄灯亮        
                delay(1000);
                digitalWrite(carYellow, LOW); //汽车黄灯灭
digitalWrite(carGreen, HIGH); //汽车绿灯亮

                        changeTime = millis(); //记录自上一次灯变化的时间
                        //返回到主函数循环中
}

下载完成后,可以尝试按下按钮。看看是个什么的效果?我们可以看到整个变化过程是这样的——开始时,汽车灯为绿灯,行人灯为红灯,代表车行人停。一旦行人,也就是你,按下按钮,请求过马路,那么行人灯就开始由红变绿,汽车灯由绿变黄,变红。在行人通行的过程中,设置了一个过马路的时间 crossTime,一旦到点,行人绿灯开始闪烁,提醒行人快速过马路。 闪烁完毕,最终,又回到了开始的状态,汽车灯为绿灯,行人灯为红灯。

整段代码看起来很复杂,其实理清一下思路并不难。如果你还是没有办法理不清里面变化关系的话,可以试着画一个示意图,像项目2的课后作业那样,这样一来可能会方便你理解程序。

代码回顾

通过前面两个项目,你应该能够理解这个代码的大部分内容。代码开始是一串的变量的声明,在声明中,出现了一个新名词。这里就解释一下这个新名词:

unsigned long changeTime;

这是一个新的变量类型。我们之前,只创建过int整型变量,它可以存放一个-32768到32767之间的整数。这次要创建的是一个long的变量类型,它可以存放一个-2147483648到2147483647之间的整数。而unsigned long数据类型,则不存储负数,所以存储的范围就从0到4294967295.

如果我们使用一个int型的话,信号灯状态变化的时间,它只能存储最大32秒(32768毫秒约为32秒),一旦出现变量溢出就会造成程序运行出现错误,所以,为了避免这样的情况,要选用能存储更大数的一个变量,并且不为负,我们就可以考虑使用unsigned long型。你可以用笔算下,这个变量最大能存储的数,时间可达49天。

随即进入setup()函数,对LED和按钮进行一些设置,在设置时,需要注意到的是:

pinMode(button, INPUT);

pinMode()函数我们已经很熟悉了,在项目一的时候就介绍过,只是和LED有所不同的是,按钮要设置为INPUT。

在setup()函数中,先给定行人灯和汽车灯的一个初始状态:

digitalWrite(carGreen, HIGH); //开始时,汽车灯绿灯digitalWrite(pedRed, LOW); //行人灯为红灯
进入到的主程序中的第一句,就是来检测button(引脚9)的状态的:

int state = digitalRead(button);

变量这个盒子无限大吗?

那么,有人会问为什么有些变量类型可以存储很大的数,而有些变量类型不行呢?这是由变量类型所占的存储空间决定的。就拿我们前面讲变量的时候举过得例子,变量好像好比用来放东西的盒子,那不同类型的变量想象成不同大小的盒子,int的盒子比unsigned long的盒子小,所以放的东西当然少啦!这样解释是不是比较容易理解这个概念呢?

那又有人问,设置不同大小的盒子干嘛呢?一样大不就行啦,都设置的大一点。理论上没有什么不可以的,可是我们不能忽略一个问题,那就是微控制器的内部存储容量是有限定的。电脑有内存,我们的微控制器同样有内存。像Arduino UNO板上的用的主芯片Atmega328最大内存是32K。所以,我们要尽量的少用存储空间,能不用则不用。

下表列出了程序中可能用到的变量数据类型:

数据类型 RAM 范围
boolean (布尔型) 1 byte 0 ~ 1(True 或False)
char(字符型) 1 byte -128 ~ 127
unsigned char(无符号字符型) 1 byte 0~255
int(整型) 2 byte -32768 ~ 32768
unsigned int(整型) 2 byte 0 ~ 65535
long(长整型) 4 byte -2147483648 ~ 2147483647
unsigned long(无符号长整型) 4 byte 0 ~ 4294967295
boolean (布尔型) 1 byte 0 ~ 1(True 或False)
float(单精度浮点型) 4 byte -3.4028235E38 ~ 3.4028235E38
double(单精度浮点型) 4 byte -3.4028235E38 ~ 3.4028235E38

从上面表格可以看到,变量的类型有很多,不同的数对应不同的变量,int和long是针对整数变 量,char是针对字符型变量,而float,double是针对含有小数点的变量。

此时,一个新函数出现——digitalRead()!
Arduino教程 04 互动交通信号灯「DFR0100」图3

这个函数是用来读取数字引脚状态,HIGH还是LOW(其实HIGH还有一种表达就是“1”,LOW是 “0”,只是HIGH/LOW更直观)。函数需要一个传递参数 --pin,这里需要读取是按键信号,按键所在引脚是数字引脚9,由于前面做了声明,所以这里用 button。

并且把读到的信号传递给变量state,用于后面进行判断。state为 HIGH或者说为1时,说明按键被按下了。state为LOW或者0,表明按键没被按下。

所以,可以直接检查state的值来判断按钮是否被按下:

if(state == HIGH && (millis() - changeTime)> 5000) {
     //调用变灯函数
     changeLights();

这里涉及新的语句-- if语句。

if语句是一种条件判断的语句,判断是否满足括号内的条件,如满足则执行花括号内的语句,如不满足则跳出if语句。

if语句格式如下:

if(表达式){
    语句;
}

表达式是指我们的判断条件,通常为一些关系式或逻辑式,也可是直接表示某一数值。如果if表达式条件为真,则执行if中的语句。表达式条件为假,则跳出if语句。

我们代码中,第一个条件是state变量为HIGH。如果按键被按下,state就会变为HIGH。第二个条件是millis()的值减changeTime的值大于5000。这两个条件之间有个“&&”符号。这是一种逻辑运算符,表示的含义是两者同时满足。

(millis() - changeTime)> 5000)

millis()是一个函数,该函数是Arduino语言自有的函数,它返回值是一个时间,Arduino开始运行到执行到当前的时间,也称之为机器时间,就像一个隐形时钟,从控制器开始运行的那一刻起开始计时,以毫秒为单位。变量changeTime初始化时,不存储任何数值,只有在Arduino运行之后,将millis()值赋给它,它才开始有数值,并且随着millis()值变化而变化。通过millis()函数不断记录时间,判断两次按键之间的时间是不是大于5秒,如果在5秒之内不予反应。这样做的目的是,防止重复按键而导致的运行错误。

if语句内只有一个函数:

changeLights();

这是一个函数调用的例子。该函数单独写在了loop()函数之外。我们需要使用的时,直接写出函数名就可以实现调用了。该函数是void型,所以是无返回值、无传递参数的函数。当函数被调用时,程序也就自动跳到它的函数中运行。运行完之后,再跳回主函数。需要特别注意的:函数调用时,函数名后面的括号不能省,要和所写的函数保持一致。changeLights() 函数内部就不做说明了。

逻辑运算符

前面说到的&&是一个逻辑运算符,常用的逻辑运算符有:

  • &&  —— 逻辑与  (两者同时满足)
  • ||  —— 逻辑或  (两者其中一个满足)
  • !  —— 逻辑非  (取反,相反的情况)

硬件回顾

按键开关

按键一共有4个引脚,图3-2分别显示了正面与背面。而图3-3则说明了按键的工作原理。一旦按下后,左右两侧就被导通了,而上下两端始终导通。
Arduino教程 04 互动交通信号灯「DFR0100」图5
图3-4传达的意思是,按钮就是起到一个通断的作用。在我们这个项目中,按钮控制数字引脚是否接高(接5V)。按下的话,数字引脚9就能检测到为高电平。否则就是保持一个低电平的状态(接GND)。
Arduino教程 04 互动交通信号灯「DFR0100」图4

什么是下拉电阻?

下拉电阻这个名词可能比较抽象,就从字的含义着手,“下拉”我们就理解为把电压往下拉,降低电压。

按键作为开关。当输入电路状态为HIGH的时候,电压要尽可能接近5V。输入电路状态为LOW的时候,电压要尽可能接近0V。如果不能确保状态接近所需电压,这部分电路就会产生电压浮动。所以,我们在按钮那里接了一个电阻来确保一定达到LOW,这个电阻就是所谓下拉电阻。
Arduino教程 04 互动交通信号灯「DFR0100」图6Arduino教程 04 互动交通信号灯「DFR0100」图7
可以从上面两张图看到,第一张是未接下拉电阻的电路,按键没被按下时, input引脚就处于一个悬空状态。空气会使该引脚电压产生浮动,不能确保是0V。然而第二张是接了下拉电阻的电路,当没被按下时,输入引脚通过电阻接地,确保为0V,不会产生电压浮动现象。

课后作业

  1. 选择任意颜色LED 6个,做一个流水灯的效果,6盏灯从左至右依次点亮,然后再从右至左依次熄灭。
    Arduino教程 04 互动交通信号灯「DFR0100」图10
  2. 如果上面那个你已经完成了的话,可以尝试一下,先从中间的灯开始亮起,依次向两边扩开。下图是个变换过程的示意图。
    Arduino教程 04 互动交通信号灯「DFR0100」图9
  3. 再比如,从左至右,依次亮起1 个,2个,3个......
    Arduino教程 04 互动交通信号灯「DFR0100」图8
  4. 再结合按钮,用按键开关和LED互动。(提供供参考教程)
    用两个按键,一个控制灯亮,另一个控制灯灭。 http://learn.adafruit.com/adafruit-arduino-lesson-6-digital-inputs?view=all
    玩儿法有很多,就全靠你的想象了!

DF创客社区版权所有,欢迎转载。
转载请务必标注来源: DF创客社区+作者姓名+原文网址。

推荐

雨峰  学徒

发表于 2022-10-11 11:03:22

C:\Program Files (x86)\Mind+\Arduino\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10804 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I C:\Program Files (x86)\Mind+\Arduino\hardware\arduino\avr\cores\arduino -I C:\Program Files (x86)\Mind+\Arduino\hardware\arduino\avr\variants\eightanaloginputs -I C:\Program Files (x86)\Mind+\Arduino\hardware\arduino\avr\cores\arduino -I C:\Program Files (x86)\Mind+\Arduino\hardware\arduino\avr\variants\standard C:\Users\Administrator\AppData\Local\DFScratch\cache\dfrobot.ino.cpp -o C:\Users\Administrator\AppData\Local\DFScratch\cache\dfrobot.ino.cpp.o
C:\Users\Administrator\AppData\Local\DFScratch\cache\dfrobot.ino.cpp: In function 'void loop()':
C:\Users\Administrator\AppData\Local\DFScratch\cache\dfrobot.ino.cpp:29:24: error: 'changeLights' was not declared in this scope
           changeLights();
                        ^
编译失败

楼主帮看一下,我直接复制代码,就有错误,不知道哪里有问题

回复

使用道具 举报

推荐

猪爱吃面  学徒

发表于 2023-7-11 21:29:32

楼主  我按你的程序做出来的灯 指挥交通把人撞死了  
开始汽车通过时  行人灯不亮 很多乱穿马路的

后来按了按钮   汽车不能走路  行人灯也不能走
然后三次准备后 行人汽车都是绿灯  大家同时抢行  压死不少

现在正在处理交通事故 一会律师会给您来电 请解释下
回复

使用道具 举报

推荐

莫莫  见习技师

发表于 2020-11-16 20:10:02

1115332213 发表于 2015-7-12 23:19
我想问下楼主,为什么我的这段代码不能实现间隔5秒才可改变红灯的颜色啊。因为只要机动车的灯一亮红,我立 ...

你的flag=low在设置函数里,所以后面loop函数里的if语句,不管条件是不是满足,不管执不执行if里的( flag = LOW;)你的flag都等于low,所以不管什么情况都会执行下一个if语句,会一直调用函数changeState
回复

使用道具 举报

5#

Holiday  初级技匠

发表于 2014-1-8 13:41:59

:D好用心的发帖、。
回复

使用道具 举报

6#

xj123306351  学徒

发表于 2014-1-11 20:12:40

谢谢分享!:)
回复

使用道具 举报

7#

lvyangyang1314  学徒

发表于 2014-6-10 11:31:48

:P
回复

使用道具 举报

8#

htp  高级技匠

发表于 2014-6-23 11:24:00

非常详细,有空试一下。
回复

使用道具 举报

9#

青云山馒头神  学徒

发表于 2014-6-24 20:07:39

程序有个地方弄反了,刚开始行人灯的红灯应该是HIGH,楼主写的是LOW
回复

使用道具 举报

10#

Jane  高级技匠
 楼主|

发表于 2014-6-25 10:32:36

青云山馒头神 发表于 2014-6-24 20:07
程序有个地方弄反了,刚开始行人灯的红灯应该是HIGH,楼主写的是LOW

谢谢提醒~:D
回复

使用道具 举报

11#

翻滚的魔方  初级技师

发表于 2014-8-29 21:46:03

为什么按键开关要接地
回复

使用道具 举报

12#

Jane  高级技匠
 楼主|

发表于 2014-9-1 09:59:55

翻滚的魔方 发表于 2014-8-29 21:46
为什么按键开关要接地

是呀 常态接地,按下接5V
回复

使用道具 举报

13#

midpoint  见习技师

发表于 2014-12-20 11:04:59

谢谢分享!
回复

使用道具 举报

14#

Jane  高级技匠
 楼主|

发表于 2014-12-28 15:45:14

回复

使用道具 举报

15#

Youyou  初级技匠

发表于 2014-12-28 18:14:57

Jane老师的帖子都是非常用心去写的,怒赞!
回复

使用道具 举报

16#

半水  见习技师

发表于 2015-1-27 20:25:38

Jane 发表于 2014-9-1 09:59
是呀 常态接地,按下接5V

按下接5V为什么啊?
回复

使用道具 举报

17#

Jane  高级技匠
 楼主|

发表于 2015-1-28 10:12:46

半水 发表于 2015-1-27 20:25
按下接5V为什么啊?

按下和5V导通,测得的就是高电平。没有按下的时候,接地,测得是低电平
回复

使用道具 举报

18#

半水  见习技师

发表于 2015-1-29 10:01:59

Jane 发表于 2014-9-1 09:59
是呀 常态接地,按下接5V

接引脚不行吗?引脚常态也是5V~
回复

使用道具 举报

19#

Jane  高级技匠
 楼主|

发表于 2015-1-29 10:10:50

半水 发表于 2015-1-29 10:01
接引脚不行吗?引脚常态也是5V~

你是直接让引脚输出高电平,是这个意思吗?
回复

使用道具 举报

20#

Jane  高级技匠
 楼主|

发表于 2015-1-29 10:11:20

半水 发表于 2015-1-29 10:01
接引脚不行吗?引脚常态也是5V~

你怎么接线的,拍张照片看得清楚点
回复

使用道具 举报

21#

半水  见习技师

发表于 2015-1-29 20:58:43

Jane 发表于 2015-1-29 10:10
你是直接让引脚输出高电平,是这个意思吗?

是的,直接让引脚输入高电平。因为我记得入门教程里讲到给LED灯接的引脚就是5V。
但我自己试验了一下,给按钮使用引脚输入,是不行的。不知道为什么,同样都是5V
回复

使用道具 举报

22#

Jane  高级技匠
 楼主|

发表于 2015-1-30 11:09:44

半水 发表于 2015-1-29 20:58
是的,直接让引脚输入高电平。因为我记得入门教程里讲到给LED灯接的引脚就是5V。
但我自己试验了一下,给 ...

那可能是电流不够,虽然有电压,电流不够也是不行的。
回复

使用道具 举报

23#

Mr.why  见习技师

发表于 2015-2-1 11:28:21

Q1:“(millis()-changeTime)”这句判断连续按两次按钮时间必须大于5秒的解释不准确,而是信号灯重新变绿后至少要亮5s(这期间连续按键时间小于5s不予反应),之后再继续调用changeLights函数。
Q2:如果不按按钮,信号灯应该正常工作写个else分支,由绿变红变黄——绿变红变黄——。。。如何在进入else语句后判断btn情况,再break掉,进入changeLight函数。
回复

使用道具 举报

24#

a920496044  见习技师

发表于 2015-2-12 21:21:15

本帖最后由 a920496044 于 2015-2-12 21:31 编辑

顶一个
回复

使用道具 举报

1234下一页
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail