用于月球上的自助交通灯

网友投稿 2019-08-24 12:03

地球的直径只有月球的3.6倍,地球能拥有月球这么大的卫星确实很神奇,月球无时无刻不在保护着地球。当然,人类探索月球的最大动力,还是因为月球上没有蚊子。

https://cdn.china-scratch.com/timg/190826/12032319C-0.jpg

中国的探月飞船已经成功在月球上着陆,随着月球城市建设的兴起,月球城市自助交通灯的研究显得尤为重要。基于Arduino的月球自助交通灯需求如图1所示,除了机动车道有交通灯,人行道也要设有交通灯,并有自助过马路按钮。

https://cdn.china-scratch.com/timg/190826/12032451A-1.jpg

图1 自助交通灯

交通灯的初始状态为机动车道亮绿灯,人行道亮红灯。当有行人要过马路时,按下过马路按钮,交通灯开始变灯,机动车道的交通灯将由绿灯经黄灯变为红灯,同时人行道的交通灯由红灯变为绿灯,使汽车停下来,允许行人安全通过。30秒后变灯结束,交通灯恢复到初始状态,机动车道亮绿灯,人行道亮红灯。

为防止频繁变灯导致交通拥堵,规定连续触发变灯的时间间隔必须在3分钟以上,也就是说,从本次交通灯变灯结束恢复到初始状态(机动车道亮绿灯,人行道亮红灯),在3分钟之内如果再次按下过马路按钮,则操作无效。

在这个项目中,除了控制交通灯外,还需要检测用户行为,这是你第一次与Arduino互动,当Arduino检测到按钮状态改变时,它将产生相应的动作。在这个项目中你还将学习到如何在代码里创建自己的函数。

1. 需要的元件

交通灯用不同颜色的LED代替,行人用的按钮用按键代替,项目所需元件清单如下:

 绿色LED,2个

 黄色LED,1个

 红色LED,2个

 电阻(220),5个

 电阻(10k),1个

 按键,1个

自助交通灯的原理图如图2所示,Arduino Uno开发板的引脚2配置为为输入,用于检测按键是否按下,引脚5、6、1012配置为输出,控制人行道和机动车道的交通灯,其中人行道只需要红绿两种颜色的交通灯即可。按键K通过10k的上拉电阻接+5V,当按键K未被按下时,由于上拉电阻的作用,引脚2处为稳定的高电平;按下按键后,引脚2接地,输入变为低电平。交通灯用相应颜色的LED代替,采用共阴极接法,当控制交通灯的对应引脚输出高电平时,相应的LED被点亮。

https://cdn.china-scratch.com/timg/190826/1203246435-2.jpg

图2 自助交通灯原理图

2. 搭建电路

参照图2的自助交通灯原理图,在面包板上搭建电路,如图3所示。

https://cdn.china-scratch.com/timg/190826/12032421Q-3.jpg

图3 自助交通灯连线图

3.输入代码

打开Arduino IDE,输入以下程序代码:


// 项目 -- 自助交通灯

int carRed = 12;  //机动车交通灯

int carYellow = 11; 

int carGreen = 10; 

int perGreen = 5;  //行人交通灯

int perRed = 6;  

int button = 2;    //按键输入

int crossTime = 25000; //人行道绿灯常亮时间,25秒

unsigned long intervalTime =0; //按键按下的时间

void setup() {

  pinMode(button,INPUT);

  pinMode(carGreen,OUTPUT); 

  pinMode(carYellow,OUTPUT);

  pinMode(carRed,OUTPUT);

  pinMode(perGreen,OUTPUT);

  pinMode(perRed,OUTPUT);

  digitalWrite(carGreen,HIGH);  //默认机动车道亮绿灯

  digitalWrite(carYellow,LOW);

  digitalWrite(carRed,LOW);

  digitalWrite(perRed,HIGH);   //默认人行道亮红灯

  digitalWrite(perGreen,LOW);

}

void loop() {

  int state = digitalRead(button);     //读取按键状态

  if (state == LOW && (millis() - intervalTime) > 180000l)

      changeLights();  //转入变灯函数

}

void changeLights()

{

  digitalWrite(carGreen,LOW);  //机动车道绿灯灭

  digitalWrite(carYellow,HIGH); //机动车道黄灯亮

  delay(20000);  //等待2秒

  digitalWrite(carYellow,LOW); //机动车黄灯灭

  digitalWrite(carRed,HIGH);   //机动车道红灯亮

  digitalWrite(perRed,LOW);   //人行道红灯灭

  digitalWrite(perGreen,HIGH); //人行道绿灯亮

  delay(crossTime);  //等待行人通过,绿灯常亮25秒

//人行道绿灯闪烁4秒

  for (int x=0; x<8; x++)    

  {

     digitalWrite(perGreen,HIGH);

     delay(250);

     digitalWrite(perGreen,LOW);

     delay(250);

  }

  //人行道红灯亮,延迟一秒后机动车道绿灯亮

  digitalWrite(perRed,HIGH);

  delay(1000);

  digitalWrite(carRed,LOW);

  digitalWrite(carGreen,HIGH);

  //记录本次变灯的时间

  intervalTime = millis();

}


https://cdn.china-scratch.com/timg/190826/1203253S3-4.jpg

4. 代码详解

在setup()函数里,初始化引脚2为数字输入端口,引脚5、6、1012为数字输出端口,默认状态为机动车道的交通灯亮绿灯,允许车辆通行,人行道亮红灯,禁止行人过马路。

在loop()函数里监测按键button的状态,当行人按下按键时,程序判断当前时间与上一次变灯结束的时间间隔,为避免机动车道拥堵,如果当前按键时间距上次变灯结束间隔不足3分钟,则不响应按键,交通灯的状态不变。如果超过3分钟,则响应行人的按键请求,调用changeLights()函数,交通灯开始变灯。

changeLights()函数完成变灯过程,变灯结束后恢复到初始状态。机动车道的交通灯先由绿灯变为为黄灯;2秒钟后,红灯亮起,禁止车辆通行,同时人行道的交通灯变为绿灯;25秒后,人行道的绿灯开始闪烁,再过4秒,人行道的交通灯变为红灯,禁止行人通行;但为了安全起见,此时机动车道的交通灯依然为红灯,延迟1秒钟后才变为绿灯,允许机动车继续通行。

在程序的主循环(loop()函数)里用如下语句检查引脚2的状态:

  int state = digitalRead(button);

  if (state == LOW && (millis() - intervalTime) > 180000l) {

      changeLights();

  }

程序引入一个整数state,并设置state的数值为引脚2的状态。

if语句是程序控制语句的一种,它的目的是检查当前条件是否满足,如果满足,则执行代码块{…}中的代码。由于该代码块中只有一条语句,就是调用changeLights()函数,所以该代码块的一对大括号也可以去掉。

这里要检查两个条件是否同时满足要求。第一个条件是state变量的状态是否为LOW,state变量存放的是引脚2的状态,当按键按下时,state为LOW。第二个条件是millis()的值减去intervalTime是否大于180000毫秒,由于该数值已经超过了整型值所能表达的范围,为保证计算精度,在180000后面加了一个小写字母“l”,将该值转换为长整型。

前面已经提及,millis()函数是Arduino库函数,它返回以毫秒为单位的从Arduino板通电到当前的时间。变量intervalTime是无符号长整型,取值范围为0到4294967295,存放的是上次从millis()函数读取的值。将millis()-intervalTime放在括号里,保证比较的是state与本次计算的结果,而不是millis()函数的返回值。

在state==LOW和millis()-intervalTime之间有符号&&,这是一个逻辑运算符,意思是进行逻辑与运算,逻辑运算符有三种:

  • && -- 逻辑与

  • || -- 逻辑或

  • !-- 逻辑非

逻辑与运算(&&)的含义是当两个操作数都是真时,逻辑运算的结果为真,例如当x为5、y为10时,下面这个if语句的条件为真:

     if (x==5 && y==10) {…}

逻辑或运算(||)的含义是两个操作数中只要有一个为真,结果就为真,例如当x为5、y为6时,下面这个if语句的条件为真:

     if (x==5 || y==10) {…}

逻辑非运算(!)的含义是如果操作数为假,则结果为真,因此如果x是假(用0表示),下面这个if语句的条件为真:

     if (!x) {…}

可以将操作符放在括号中使用,以增加程序的可读性。

主程序loop()函数只是不断检测人行道的按钮是否被按下,如果按钮被按下并且符合变灯条件,则调用changeLights()函数,程序执行函数中的代码,函数运行完成后,返回if语句changeLights()函数的下一条代码继续执行。函数changeLights()是自动交通灯程序的核心代码,负责控制机动车道和人行道上的交通灯,禁止机动车通行,允许行人通行;函数执行完毕后恢复原来交通灯的设置,允许机动车通行,禁止行人通行。

函数是有名称的独立代码块,调用时可以给函数传递参数,函数也可以返回数值,但在这个例子里,没有传递给函数任何参数,也没有让函数返回任何数值。

在程序中引入函数的意义是可以在其他地方被多次调用,尤其当一个函数传递了参数,或者返回数值时,它的优点才更能凸显出来。这个程序完全可以不使用changeLights()函数,而是把这段代码都放在loop()函数里,引入changeLights()只是为了给出自定义函数的概念,并改善代码的可读性。


当你的才华还撑不起你的野心的时候,你就应该静下心来学习;当你的能力还驾驭不了你的目标时,就应该沉下心来历练;梦想,不是浮躁,而是沉淀和积累,只有拼出来的美丽,没有等出来的辉煌,机会永远是留给最渴望的那个人,学会与内心深处的你对话,问问自己,想要怎样的人生,静心学习,耐心沉淀。

--end--

声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com