Học

An Arduino traffic light, with a crossing button that works

Trung bình

Hoặc xem mọi bài học.

  • 503linh kiện trên bàn làm mạch
  • 44bo mạch đang chạy
  • 1.00xthời gian thực, trên mọi bo mạch
Arduino Uno: đèn qua đường có nút nhấnlive0.000 s 0.00x
Nhấn nút để xin sang đường
Ví dụ traffic, chạy trên ATmega328P mô phỏng. Nhấn nút trên breadboard để xin sang đường.

Đèn giao thông là dự án mà gần như khóa học Arduino nào cũng dùng vào tuần thứ hai, vì nó chỉ cần ba LED và vài lệnh delay mà vẫn trông ra dáng. Phiên bản ở trên tiến thêm một bước so với phần lớn: đó là đèn qua đường cho người đi bộ, có đèn cho người đi bộ, một nút xin sang đường, và quy tắc rằng đèn xanh không bao giờ kết thúc sớm hơn mức nó cần. Nó đang chạy trên một Arduino Uno mô phỏng ngay lúc này, với đúng sketch mà bạn có thể đọc ở phía dưới trang này.

Nhấn nút trên breadboard và xem điều gì xảy ra. Đèn WAIT màu xanh dương sáng ngay khi tiếp điểm đóng, đèn xanh lá giữ cho đến khi đủ thời gian tối thiểu, rồi đến vàng, rồi đỏ cùng đèn đi bộ màu trắng, rồi pha nhấp nháy, và trở lại xanh lá. Mở nó trong trình soạn thảo để đổi bất kỳ khoảng thời gian nào rồi chạy lại.

What you need

  • Một Arduino Uno
  • Năm LED: đỏ, vàng, xanh lá, trắng và xanh dương
  • Năm điện trở 220 ohm, mỗi LED một cái
  • Một nút nhấn
  • Một IC đảo 74HC04, cho đèn WAIT
  • Một breadboard và dây cắm

Wiring

Linh kiện và chânNối tớiGhi chú
LED đỏ, qua điện trở 220 ohmChân 8Đèn đỏ giao thông
LED vàng, qua điện trở 220 ohmChân 9Đèn vàng giao thông
LED xanh lá, qua điện trở 220 ohmChân 10Đèn xanh giao thông
LED trắng, qua điện trở 220 ohmChân 11Đèn đi bộ
Nút nhấn, một bênChân 2Bên kia nối GND; sketch bật điện trở kéo lên bên trong
Chân input của 74HC04Đường tín hiệu nút nhấnChân output của nó điều khiển đèn WAIT màu xanh dương, không cần code
Chân 14 và chân 7 của 74HC045 V và GNDCon chip không làm gì cho đến khi nối cả hai

Trình tự được xây dựng thế nào

Một chỗ qua đường có một trạng thái nghỉ, đèn xanh cho xe cộ, và nó chỉ rời trạng thái đó khi có người yêu cầu. Sketch viết điều đó thành một vòng lặp gồm bốn pha: xanh cho đến khi có yêu cầu (nhưng không bao giờ ít hơn ba giây và không bao giờ quá chín giây), vàng trong một giây rưỡi, đỏ cùng đèn đi bộ trong bốn giây, rồi mười hai lần nháy nửa giây của đèn vàng và đèn đi bộ cùng lúc, đúng như một đèn qua đường thật làm ở cuối pha đi bộ.

Mỗi pha là một lời gọi tới hàm phụ nhỏ lights(), hàm đặt đỏ, vàng và xanh trong một lần. Luôn ghi cả ba đèn cùng lúc, mọi lần, là cách ngăn lỗi đèn giao thông hay gặp nhất, khi một pha quên tắt đèn của pha trước và hai màu cùng sáng một lúc.

Bốn pha, trích từ ví dụ traffic (đã rút gọn)
void loop() {
  lights(0, 0, 1);          // green
  waitForRequest();

  lights(0, 1, 0);          // amber
  delay(AMBER_MS);

  lights(1, 0, 0);          // red, and walk
  digitalWrite(WALK, HIGH);
  delay(WALK_MS);

  for (int i = 0; i < 12; i++) {   // the flashing phase
    int on = (i & 1) ? LOW : HIGH;
    digitalWrite(WALK, on);
    lights(0, on == HIGH, 0);
    delay(500);
  }
  digitalWrite(WALK, LOW);
}

Vì sao nút nhấn dùng ngắt

Sketch qua đường đầu tiên mà phần lớn mọi người viết kiểm tra nút nhấn bằng digitalRead() ở đâu đó trong loop(). Nó chạy được khi bạn thử bằng cách giữ nút, và hỏng ngay khi có người chỉ nhấn nhẹ một cái, vì phần lớn thời gian của chu kỳ, bo mạch đang nằm trong một lệnh delay() và hoàn toàn không nhìn vào chân. Một lần nhấn nhanh trong pha đỏ đơn giản là không bao giờ được thấy.

Sketch này đặt nút nhấn ở chân 2, chân INT0 của Uno, và gắn một ngắt vào cạnh xuống của nó. Hàm xử lý chỉ làm một việc: đặt một cờ tên là requested. Dù vòng lặp chính đang làm gì, kể cả đang chờ trong delay, lần nhấn vẫn được bắt. Sau đó vòng lặp đọc cờ trong lúc tạm chặn ngắt, để không bao giờ đọc phải một giá trị đang ghi dở, và xóa cờ ở cuối chu kỳ.

Cờ được khai báo volatile, từ khóa báo cho trình biên dịch biết rằng có thứ gì đó nằm ngoài luồng chạy bình thường của chương trình có thể thay đổi nó. Bỏ từ đó đi thì một trình biên dịch có tối ưu được phép đọc biến một lần rồi không bao giờ nhìn lại.

Yêu cầu được bắt, dù loop() đang làm gì
volatile bool requested = false;

static void onPress() { requested = true; }

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON), onPress, FALLING);
}

Chiếc đèn không cần code

Đèn WAIT màu xanh dương hoàn toàn không do Uno điều khiển. Nó nối vào một cổng của IC đảo 74HC04, cổng này nối thẳng vào đường tín hiệu nút nhấn: đường này ở mức cao nhờ điện trở kéo lên, nút nhấn kéo nó xuống thấp, cổng đảo biến mức thấp đó thành mức cao, và đèn sáng. Nó phản ứng trong vài nano giây, trước khi sketch kịp nhận ra bất cứ điều gì.

Nó có mặt trong mạch để nhắc rằng không phải việc gì cũng thuộc về vi điều khiển. Nó cũng cho thấy một điều về trình mô phỏng: 74HC04 ở đây là một con chip 14 chân thật với nguồn cấp ở chân 14 và chân 7, và nếu bạn xóa một trong hai dây nguồn trong trình soạn thảo thì đèn ngừng hoạt động, y như trên bàn làm việc.

Thử trong trình soạn thảo

Mở mạch trong trình soạn thảo và đổi MIN_GREEN từ 3000 thành 500. Nhấn nút ngay khi đèn xanh vừa bật và giờ đèn qua đường sẽ chặn xe gần như ngay lập tức, đó chính là lý do đèn qua đường thật có thời gian tối thiểu.

Rồi cố tình làm hỏng nó. Thay ngắt bằng một lệnh digitalRead() chân 2 đơn giản bên trong waitForRequest() và nhấn nhanh nút trong pha đỏ. Lần nhấn bị mất. Đặt ngắt trở lại thì lần nào cũng được bắt.

Cuối cùng, kéo một máy phân tích logic từ hộp linh kiện ra và kẹp bốn kênh của nó vào chân 8, 9, 10 và 11. Toàn bộ trình tự hiện ra thành một giản đồ thời gian, với pha nhấp nháy là một hàng xung gọn gàng, cách kiểm tra thời gian nhanh hơn nhiều so với ngồi nhìn đèn.

Common mistakes

Không có điện trở cho LED. Mỗi đèn ở đây có điện trở 220 ohm riêng, giữ dòng điện ở khoảng 14 miliampe từ một chân 5 volt. Một LED nối thẳng vào chân sẽ cố lấy dòng điện lớn hơn nhiều so với mức chân đó nên cấp.

Chỉ đọc nút nhấn giữa các lệnh delay, như đã nói ở trên. Nếu có lúc lần nhấn bị bỏ qua, gần như chắc chắn là vì lý do này.

Tưởng nút nhấn đọc HIGH khi được nhấn. Với INPUT_PULLUP, chân nằm ở mức cao khi nghỉ và lần nhấn kéo nó xuống GND, nên khi nhấn là LOW. Ngắt đặt ở cạnh xuống cũng vì lý do đó.

Hai màu sáng cùng lúc, vì một pha bật một đèn mà pha sau không bao giờ tắt nó. Đặt cả ba đèn cùng lúc trong một hàm sẽ loại bỏ hẳn kiểu lỗi này.

Câu hỏi

Có làm được đèn giao thông đơn giản không cần nút nhấn không?

Được. Xóa nút nhấn và lời gọi waitForRequest(), rồi cho đèn xanh một delay() cố định riêng. Kết quả là trình tự ba LED kinh điển. Bản có nút nhấn đáng để viết thêm vài dòng vì đó là cách đèn qua đường thật hoạt động.

Vì sao nút nhấn ở chân 2?

Trên Uno, chỉ chân 2 và 3 có ngắt ngoài riêng, là INT0 và INT1. Chân nào cũng đọc được bằng digitalRead(), nhưng chỉ hai chân đó mới gọi được một hàm ngay khi chúng thay đổi.

Code này có chạy trên Arduino Uno thật mà không cần sửa không?

Sketch chỉ dùng các lệnh Arduino chuẩn: pinMode, digitalWrite, delay, millis và attachInterrupt. Đó chính là code bạn sẽ nạp từ Arduino IDE.

Làm sao biến nó thành ngã tư?

Thêm một bộ ba đèn thứ hai trên ba chân nữa và cho mỗi pha hai lời gọi, mỗi đường một lời gọi. Ngã tư còn có một pha đỏ và vàng cùng sáng trước khi sang xanh, điều mà đèn qua đường cho người đi bộ không có.

Lắp mạch này ngoài đời

Mở trình soạn thảo, đổi một giá trị và xem con số thay đổi theo. Không cần cài gì, không cần tài khoản.