Cách làm đồng hồ đếm ngược, và bật mí cách làm nó để tỏ tình!

Nội dung chính, cần nắm

Hôm nay chúng ta sẽ cùng tham gia làm một dự án nho nhỏ cùng với tôi để thiết kế một mạch đồng hồ đếm ngươc nhé! Vậy mạch này dùng để làm gì nhỉ? Có nhiều ứng dụng lắm, ví dụ như bạn có thể nâng cấp để ghi âm lại lời thoại của mình vào một thẻ nhớ, sau đó dùng module thẻ nhớ (sẽ có hướng dẫn sau) để làm một mạch tự động phát ra lời tỏ tình với người mà bạn yêu thương !

Và nếu có một tâm hồn devil, bạn sẽ có thể làm một mạch tự động phát ra tiếng kêu beep beep thật to để phá một ai đó (trong lúc họ đang ngủ,...)! Đùa thôi, không nên quậy như vậy!

Phần cứng

Lắp mạch

Hôm nay, sẽ là một ngày vất vả đấy, vì vậy các bạn nên chọn thời gian rãnh rỗi (khoảng 1 tiếng 30 phút để làm nhé). Bây giờ, còn chần chừ gì nữa, tiến hành cùng với tôi thôi! heart

Thứ nhất: Bạn hãy xem hôm nay chúng ta sẽ làm gì!

Đây là chiếc đồng hồ của tôi, bạn sẽ cải tiến nó đẹp hơn chứ smiley?

Các bạn thấy đấy, mô hình của tôi chưa đẹp, vậy sau khi làm xong ví dụ mẫu này, hãy hàn mạch và gửi bài về cho chúng tôi nhé! (bạn không biết gửi bài? hãy chú ý đến menu đang chạy theo con chuột của bạn và kích vào mục Đăng bài nhé)

Bây giờ, tôi sẽ phân tích bộ đồng hồ có những phần (sau này trong các bài viết sau tôi sẽ gọi là module) gì:

  1. Module 4 LED số 8 tôi dùng để hiển thị thời gian, tôi chỉ cho đặt tối đa là 59 phút 59 giây. (vì nhiều quá thì cũng không biết làm gì, các bạn có thể đặt cho cao hơn, nhưng cũng chỉ tối đa là 99 phút 59 giây mà thôi wink)
  2. 1 biến trở có nhiệm vụ sẽ thay đổi độ sáng của Module 4 LED nói trên
  3. 1 cái loa để phát ra tiếng báo động là đã hết "Quỹ thời gian"
  4. 3 cái button nho nhỏ để điều chỉnh cái đồng hồ

Thứ hai: Thực hiện Module 4 LED số 8 (khoảng 45 phút)

Đây là module tốn thời gian nhất, vì bạn biết đấy, module này nhiều chân nhất devil và nó cùng có khá là mới mẻ với những bạn nào chưa từng làm, còn những module khác trong bài, chúng ta có thể dễ dàng tham khảo trên arduino.vn đúng không nào! Tuy nhiên, Cộng đồng Arduino Việt Nam của chúng ta luôn có những suy nghĩ cực kỳ đơn giản cho những việc khó, vì vậy bạn chỉ cần làm theo từng bước dưới đây để hoàn thiện module này. Và tất nhiên, bạn sẽ hiểu được cách để sử dụng chúng sau này, cho những dự án khác của bạn smiley.

  1. Tải về thư viện SegSev ( sau đó giải nén và chép vào thư mục libraries trong thư mục cài đặt Arduino IDE của bạn)
  2. Mở Arduino IDE và dán vào đoạn mã sau:
#include "SevSeg.h" //Include thư viện SevSeg

SevSeg myDisplay; // Khai báo biến myDisplay là đối tượng của thư viện SegSev

void setup() { //Hàm Setup

    int displayType = COMMON_ANODE; // CÁI NÀY VÔ CÙNG QUAN TRỌNG, nếu người ta bán cho bạn một Module LED 8 đoạn chung cực dương thì bạn không cần thay đổi dòng này. Còn nếu họ bán một Module LED 8 đoạn chung cực âm cho các bạn thì bạn cần thay đổi là COMMON_CATHODE. Và nếu người bán không nói cho bạn hoặc bạn quên không hỏi thì cũng không sao, cứ đặt như thế này và lắp mạch nếu không chạy đúng như những gì đoạn code này cần thực hiện thì bạn chỉ cần thay nó là COMMON_CATHODE thôi!
    int digit1 = 8; //Pin 12 led 7 đoạn
    int digit2 = 12; //Pin 9 led 7 đoạn
    int digit3 = 13; //Pin 8 led 7 đoạn 
    int digit4 = 2; //Pin 6 led 7 đoạn

    int segA = 9; //Pin 11 led 7 đoạn
    int segB = 11; //Pin 7 led 7 đoạn
    int segC = 4; //Pin 4 led 7 đoạn
    int segD = 6; //Pin 2 led 7 đoạn
    int segE = 7; //Pin 1 led 7 đoạn
    int segF = 10; //Pin 10 led 7 đoạn
    int segG = 3; //Pin 5 led 7 đoạn
    int segDP= 5; //Pin 3 led 7 đoạn

    int numberOfDigits = 4; // số dấu chấm

    myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP); // Bắt đầu cho phép giao tiếp Module LED 7 đoạn với Arduino
    
    myDisplay.SetBrightness(100); //điều chỉnh độ sáng của Module LED

 
}

void loop(){

    myDisplay.DisplayString("abcd", 0b00001000); // Thể hiện chữ AbcD ra bảng LED, và dãy số 0b00001000 là vị trí dấu chấm. Bạn hãy thử thay những số 0 bằng số 1 hoặc ngược lại để kiểm nghiệm
    
}

Bây giờ, bạn hãy lắp mạch như sau (kích vào hình để xem ảnh lớn)

Nếu không rõ, bạn có thể thực hiện theo từng bước như sau:

Bạn gắn Module 4 LED số 8 vào bread board, những số màu đỏ là thứ tự chân của Module LED

Bây giờ, bạn sẽ thực hiện lắp ghép chân của module này như hướng dẫn sau:

  1. Chân số 12 --> điện trở 560 Ohm --> Digital 8
  2. Chân số 11 --> Digital 9
  3. Chân số 10 --> Digital 10
  4. Chấn số 9 --> điện trở 560 Ohm --> Digital 12
  5. Chân số 8 --> điện trở 560 Ohm --> Digital 13
  6. Chân số 7 --> Digital 11
  7. Chân số 6 --> điện trở 560 Ohm --> Digital 2
  8. Chân số 5, 4, 3, 2, 1 lần lượt nối với chân Digital 3, 4, 5, 6, 7
Sau khi upload code trên, bạn sẽ được thành quả như thế này. Thật đáng 45 phút bạn chứ?

Thứ ba: Dùng biến trở để chỉnh độ sáng của Module đèn LED (khoảng 15 phút)

Bạn hãy lắp thêm một biến trở vào chân A0 nhé, để biết cách sử dụng biến trở, xem bài hướng dẫn sau.

Hãy theo từng bước của tôi, bạn nhé.

Đầu tiên, bạn cần khai báo biến bientro là A0 để sử dụng trong việc đọc giá trị biến trở, bạn đặt dòng code này trước hàm setup:

int bientro = A0;

Sau đó, chúng ta cần khởi tạo một cổng Serial để xem giá trị của biến trở, và sau này là để xem giá trị của các nút bấm (button) bạn thêm vào trong hàm setup những dòng code sau:

Serial.begin(9600);//Bật cổng Serial ở mức Baudrate là 9600
Serial.println("Ready");

Khi sử dụng hàm analogRead, nó sẽ trả về một giá trị trong khoảng từ 0 đến 1023, mà giá trị độ sáng của đèn (được thiết đặt tại myDisplay.SetBrightness(100);) chỉ nằm trong khoảng từ 0 đến 100. Vì vậy, chúng ta sẽ sử dụng hàm map để đổi thang đo. Vì vậy, ta sẽ xây dựng một hàm kiểu int để trả về giá trị độ sáng màn hình! Bạn thêm đoạn code này nằm ngoài hàm loop hoặc hàm setup.

int getBrightness() { // Hàm đọc giá trị độ sáng từ biến trở
  int value = analogRead(bientro); // Đọc giá trị biến trở và đưa vào biến value
  value = map(value,0,1023,0,100); // Chuyển đổi giá trị trong thang đo 0-1023 về thang đo 0 - 180
  return value; // Trả về giá trị độ sáng
}

Để thay đổi độ sáng của Module 4 LED số 8 thời gian thực (ngay lập tức) chúng ta cần thêm dòng này vào hàm loop, lúc này độ sáng sẽ được cập nhập liên tục:

myDisplay.SetBrightness(getBrightness()); //độ sáng

Lúc bấy giờ, toàn bộ chương trình của ta sẽ như thế này:

#include "SevSeg.h" //Include thư viện SevSeg

SevSeg myDisplay; // Khai báo biến myDisplay là đối tượng của thư viện SegSev

int bientro = A0;
void setup() { //Hàm Setup
    Serial.begin(9600);//Bật cổng Serial ở mức Baudrate là 9600
    Serial.println("Ready");
    int displayType = COMMON_ANODE; // CÁI NÀY VÔ CÙNG QUAN TRỌNG, nếu người ta bán cho bạn một Module LED 8 đoạn chung cực dương thì bạn không cần thay đổi dòng này. Còn nếu họ bán một Module LED 8 đoạn chung cực âm cho các bạn thì bạn cần thay đổi là COMMON_CATHODE. Và nếu người bán không nói cho bạn hoặc bạn quên không hỏi thì cũng không sao, cứ đặt như thế này và lắp mạch nếu không chạy đúng như những gì đoạn code này cần thực hiện thì bạn chỉ cần thay nó là COMMON_CATHODE thôi!
    int digit1 = 8; //Pin 12 led 7 đoạn
    int digit2 = 12; //Pin 9 led 7 đoạn
    int digit3 = 13; //Pin 8 led 7 đoạn 
    int digit4 = 2; //Pin 6 led 7 đoạn

    int segA = 9; //Pin 11 led 7 đoạn
    int segB = 11; //Pin 7 led 7 đoạn
    int segC = 4; //Pin 4 led 7 đoạn
    int segD = 6; //Pin 2 led 7 đoạn
    int segE = 7; //Pin 1 led 7 đoạn
    int segF = 10; //Pin 10 led 7 đoạn
    int segG = 3; //Pin 5 led 7 đoạn
    int segDP= 5; //Pin 3 led 7 đoạn

    int numberOfDigits = 4; // số dấu chấm

    myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP); // Bắt đầu cho phép giao tiếp Module LED 7 đoạn với Arduino
    
    myDisplay.SetBrightness(100); //điều chỉnh độ sáng của Module LED

 
}

void loop(){

    myDisplay.DisplayString("abcd", 0b00001000); // Thể hiện chữ AbcD ra bảng LED, và dãy số 0b00001000 là vị trí dấu chấm. Bạn hãy thử thay những số 0 bằng số 1 hoặc ngược lại để kiểm nghiệm
    myDisplay.SetBrightness(getBrightness()); //độ sáng
}

int getBrightness() { // Hàm đọc giá trị độ sáng từ biến trở
    int value = analogRead(bientro); // Đọc giá trị biến trở và đưa vào biến value
    value = map(value,0,1023,0,100); // Chuyển đổi giá trị trong thang đo 0-1023 về thang đo 0 - 180
    return value; // Trả về giá trị độ sáng
}

Và sau khi upload đoạn code mới lên, mạch của bạn sẽ như thế này.

Thứ tư: Xây dựng bộ đếm ngược thời gian với các button và phát ra loa (khoảng 30 phút)

Để xây dựng một bộ đếm thời gian thì chúng ta cần hiểu được quy trình nó như thế nào, và trước tiên phải đề xuất ra những ý tưởng, với tôi, tôi có 2 ý tưởng cho việc này:

Gọi biến time là Quỹ thời gian còn lại của đồng hồ, ta có 2 cách:

  1. time = time - 1 ( time-- ) --> xuất ra đồng hồ --> delay(1000) --> quay trở về (time--) cho đến khi nào mà time = 0
  2. Nếu thời điểm kể từ lúc bắt đầu chạy chương trình đến bây giờ trừ đi THỜI ĐIỂM VỪA THỰC HIỆN LỆNH XUẤT RA ĐỒNG HỒ mà lớn hơn 1 giây thì
    • time = time - 1 ( time-- )
    • Cập thời THỜI ĐIỂM VỪA THỰC HEIẸN LỆNH XUẤT RA ĐỒNG HỒ lời thời điểm hiện tại

Qua quá trình so sánh giữa 2 cách, tôi nhận thấy. Cách 2 có nhiều ưu điểm vượt trội, như là:

  • Chương trình không bị dừng hẳn (delay) nên vẫn tiếp tục tính toán được ==> tôi có thể gắn được các button
  • Cập nhập thời gian liên tục không gián đoạn
  • Còn cách 1 làm cho đồng hồ bị dừng (sử dụng hàm delay) làm cho việc gia công các button là không thể thực hiện được

Bây giờ, bạn hãy lắp thêm 3 button như sau bạn nhé:

 

Chúng ta sẽ mắc 3 điện trở button kiểu PULL DOWN và pinMode cho chúng là INPUT. Thứ tự của 3 button từ trái qua phải lần lượt là 1, 2, 3 và 3 button này được mắc lần lượt với chân Analog A3, Analog A2, Analog A1. Tiếp theo chúng ta sẽ gắn loa theo sơ đồ sau, nếu các bạn không phân biệt được cực âm cực dưong của loa thì cũng không sao cả, chúng ta chỉ việc chọn đại một chân nào đó để làm cực âm thôi (chân còn lại nối vào Analog A4).

Cuối cùng, bạn upload đoạn code sau đây và "enjoy" dự án đồng hồ của mình thôi! Trong đoạn code mình đã chủ thích khá rõ ràng, bạn hãy đọc trong đó để hiểu hơn nhé! Để debug xem các button và biến trở có hoạt động hay không thì sau khi upload thành công bạn nhấn tổ hợp phím Ctrl+Shift+M nhé.

#include "SevSeg.h" //Include thư viện SevSeg

SevSeg myDisplay; // Khai báo biến myDisplay là đối tượng của thư viện SegSev

int bientro = A0;
/* NEW */

int maxTime = 60 * 60 - 1; // 59 phút 59 giây.
int time; // Biến "Quỹ thời gian".
int defaultTime = 90; // Mặc định tôi đặt là 90 giây
int second, minute; // Biến giây và biến phút sẽ được tính từ biến "Quỹ thời gian"
unsigned long milli, milli2; // Biến milli dùng để lưu lại THỜI ĐIỂM VỪA THỰC HIỆN LỆNH XUẤT RA ĐỒNG HỒ. Biến milli2 dùng để lưu lại THỜI ĐIỂM CUỐI CÙNG NHẤN NÚT BẤM
boolean startCounting = false; // Biến này dùng để. Mặc định là chưa đếm
char buf[5]; //Chuỗi buf có độ dài tối đa là 4 (xem bài char để rõ). Chuỗi buf có nhiệm vụ là nhận giá trị cuối cùng (Đã được định dạng thời gian) trước khi chuyển cho đối tượng SegSev để xuất ra Module 4 LED số 8

int buttonPin[3] = { A3, A2, A1 }; // Mảng lưu chân các button. Do 12 chân digital đã được sử dụng để xử lý Module 4 LED nên mình mới sử dụng chân Analog, ngoài ra bạn có thể sử dụng chân Digital cho việc này
int button[3]; // Mảng lưu giá trị các button (HIGH, LOW)

int speakerPin = A4; // Chân nối tới 1 chân của loa. Do 12 chân digital đã được sử dụng để xử lý Module 4 LED nên mình mới sử dụng chân Analog, ngoài ra bạn có thể sử dụng chân Digital cho việc này
/* Hết NEW */
void setup()
{ //Hàm Setup
    Serial.begin(9600); //Bật cổng Serial ở mức Baudrate là 9600
    Serial.println("Ready");
    int displayType = COMMON_ANODE; // CÁI NÀY VÔ CÙNG QUAN TRỌNG, nếu người ta bán cho bạn một Module LED 8 đoạn chung cực dương thì bạn không cần thay đổi dòng này. Còn nếu họ bán một Module LED 8 đoạn chung cực âm cho các bạn thì bạn cần thay đổi là COMMON_CATHODE. Và nếu người bán không nói cho bạn hoặc bạn quên không hỏi thì cũng không sao, cứ đặt như thế này và lắp mạch nếu không chạy đúng như những gì đoạn code này cần thực hiện thì bạn chỉ cần thay nó là COMMON_CATHODE thôi!
    int digit1 = 8; //Pin 12 led 7 đoạn
    int digit2 = 12; //Pin 9 led 7 đoạn
    int digit3 = 13; //Pin 8 led 7 đoạn
    int digit4 = 2; //Pin 6 led 7 đoạn

    int segA = 9; //Pin 11 led 7 đoạn
    int segB = 11; //Pin 7 led 7 đoạn
    int segC = 4; //Pin 4 led 7 đoạn
    int segD = 6; //Pin 2 led 7 đoạn
    int segE = 7; //Pin 1 led 7 đoạn
    int segF = 10; //Pin 10 led 7 đoạn
    int segG = 3; //Pin 5 led 7 đoạn
    int segDP = 5; //Pin 3 led 7 đoạn

    int numberOfDigits = 4; // số dấu chấm

    myDisplay.Begin(displayType, numberOfDigits, digit1, digit2, digit3, digit4, segA, segB, segC, segD, segE, segF, segG, segDP); // Bắt đầu cho phép giao tiếp Module LED 7 đoạn với Arduino

    myDisplay.SetBrightness(100); //điều chỉnh độ sáng của Module LED

    /* NEW */
    // pinMode các chân của button là INPUT
    for (int i = 0; i < 3; i++) { //Chạy từ 0 --> 2
        pinMode(buttonPin[i], INPUT);
    }
    milli = milli2 = millis(); // Cập nhập cả 2 thời điểm
    time = defaultTime;
    /* hết NEW */
}

void loop()
{
    myDisplay.SetBrightness(getBrightness()); //độ sáng

    /* NEW */
    if (millis() - milli2 > 300) { //Cứ sau mỗi 300 mili giây là đọc tất cả giá trị button
        for (int i = 0; i < 3; i++) {
            button[i] = digitalRead(buttonPin[i]);
            Serial.print(button[i]); //Debug
        }
        Serial.println();
        /*
            Bây giờ chúng ta sẽ thiết đặt các lệnh, gợi ý, các bạn đặt những lệnh nào sử dụng nhiều nút bấm lên trước
                Tôi đặt mặc định như sua:
            - Nhấn cả 2 phím button[0] và button[2] sẽ ngừng/tiếp tục đếm ngược --> delay(500) miligiây để tạo hiệu ứng
            - Nhấn button[1] sẽ reset về giá trị ban đầu và ngừng đếm ngược --> delay(500) miligiây để tạo hiệu ứng
            - Nhấn button[0] để giảm "Quỹ thời gian" đi 10 giây
            - Nhấn button[2] để tăng "Quỹ thời gian" thêm 10 giây
    */
        if (button[0] && button[2]) {
            startCounting = !startCounting; //Thay đổi trạng thái kiểu toogle giữa HIGH và LOW
            delay(500);
        }
        else if (button[1]) {
            time = defaultTime; // Đặt mặc định
            startCounting = false;
            delay(500);
        }
        else if (button[0]) {
            time = max(0, time - 10);
        }
        else if (button[2]) {
            time = min(maxTime, time + 10);
        }

        milli2 = millis(); //Cập nhập lại thời điểm
    }
    second = time % 60;
    minute = time / 60;
    sprintf(buf, "%02d%02d", minute, second); // Hàm sprintf là một hàm của C, bạn tham khảo địa chỉ http://www.cplusplus.com/reference/cstdio/sprintf/ Tôi dùng nó để định dạng lại giá trị trước khi xuất ra màn hình. Bởi vì các biến minute và second là các biến kiểu số nguyên => không thể xuất được. Vì vậy tôi định dạng lại nó để có được mạng chuỗi dạng (mmss) (mm = minute, ss = second) để xuất ra được Module 4 LED số 8
    myDisplay.DisplayString(buf, 0b00000010); // Thể hiện chữ AbcD ra bảng LED, và dãy số 0b00001000 là vị trí dấu chấm. Bạn hãy thử thay những số 0 bằng số 1 hoặc ngược lại để kiểm nghiệm
    if (startCounting && time > 0 && (millis() - milli) > 1000) {
        /*
            Còn đếm ngược khi:
        1. Được phép đếm
        2. Quỹ thời gian lớn hơn 0
        3. Cách 1 giây từ THỜI ĐIỂM VỪA THỰC HIỆN LỆNH XUẤT RA ĐỒNG HỒ
    */
        milli = millis();
        time -= 1;
    }

    if (time == 0) { // Quỹ thời gian đã cạn, bây giờ boom sẽ nổ! (đùa thôi, loa sẽ kêu)
        tone(speakerPin, 3200); //Phát ra âm thanh ở tần số 3200 Hz, bạn có thể thay đổi nó, miễn nó nằm trong ngưỡng nghe được của tai.
    }
    else {
        noTone(speakerPin); // Tắt loa vì Quỹ thời gian vẫn còn
    }

    delay(5);
    /* hết NEW */
}

int getBrightness()
{ // Hàm đọc giá trị độ sáng từ biến trở
    int value = analogRead(bientro); // Đọc giá trị biến trở và đưa vào biến value
    value = map(value, 0, 1023, 0, 100); // Chuyển đổi giá trị trong thang đo 0-1023 về thang đo 0 - 180
    /* NEW */
    Serial.println(value);
    /* hết NEW*/
    return value; // Trả về giá trị độ sáng
}

Ngoài ra, bạn có thể download đoạn code trên tại đây.

Lời kết

Bạn và tôi đã trải qua 1h30 phút căng thẳng đúng không nào? Vậy bây giờ ra ngoài tiệm tạp hóa gần nhất mua một chai Mountain Dew để thưởng thức vị ngọt đậm đà và lên kế hoạch cho cái đồng hồ chứ devil khàkhà

Và trong bài sau, tôi sẽ hướng dẫn bạn sử dụng module thẻ nhớ để lưu lại những gì mình muốn nói và phát ra lời bạn muốn tỏ tình hehe!

Thôi, không phải đợi lâu, bạn chỉ cần làm như bài hướng dẫn sau thôi, hehe. Bài 12: Phát nhạc bằng Arduino với một cái loa hoặc buzzer

lên
20 thành viên đã đánh giá bài viết này hữu ích.
Chuyên mục: 
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

Tiết kiệm RAM trong Arduino?

Như đã nói ở bài trước Cách lưu trữ các biến số, mảng, chuỗi trong Arduino, chúng ta đã biết rằng các loại biến trong Arduino được lưu ở những vùng nhớ khác nhau trong RAM, và khi hết RAM thì chương trình của bạn sẽ die một cách bất ngờ - vì lỗi không nằm trong code.

Vì vậy, hôm nay, chúng ta sẽ tìm cách giải quyết vấn đề "làm thế nào để giảm thiểu việc sử dụng RAM trong một sketch Arduino?".

lên
11 thành viên đã đánh giá bài viết này hữu ích.