Các hàm, biến và kiểu dữ liệu trong Arduino

Khi bắt đầu lập trình Arduino, việc hiểu rõ về hàm, biếnkiểu dữ liệu là cực kỳ quan trọng. Chúng là những viên gạch cơ bản để xây dựng bất kỳ chương trình nào, giúp bạn điều khiển phần cứng, xử lý thông tin và tạo ra các dự án thú vị. Hãy cùng đi sâu vào từng khái niệm nhé!

1. Biến (Variables) trong Arduino

Trong lập trình, biến có thể hình dung như những "chiếc hộp" trong bộ nhớ máy tính, dùng để lưu trữ dữ liệu. Dữ liệu này có thể thay đổi trong suốt quá trình chương trình chạy.

Khai báo biến

Trước khi sử dụng một biến, bạn cần "khai báo" nó. Việc này cho Arduino biết tên của biến và loại dữ liệu mà nó sẽ lưu trữ (kiểu dữ liệu).

Cú pháp khai báo biến:

kieu_du_lieu ten_bien;

Hoặc bạn có thể gán giá trị ban đầu ngay khi khai báo:

kieu_du_lieu ten_bien = gia_tri_khoi_tao;

Ví dụ:

int soLanNhanNut; // Khai báo một biến kiểu số nguyên
float nhietDoC = 25.5; // Khai báo và gán giá trị ban đầu cho biến nhiệt độ

Phạm vi biến (Variable Scope)

Phạm vi của biến quy định nơi mà biến đó có thể được truy cập và sử dụng trong chương trình. Có hai loại chính:

  • Biến cục bộ (Local Variables): Được khai báo bên trong một hàm hoặc một khối lệnh ({...}). Biến này chỉ tồn tại và có thể sử dụng được trong chính hàm hoặc khối lệnh đó. Sau khi hàm/khối lệnh kết thúc, biến cục bộ sẽ bị hủy.
    void setup() {
      int denPin = 13; // denPin là biến cục bộ của hàm setup()
    }
    
    void loop() {
      // denPin không thể được truy cập ở đây
    }
  • Biến toàn cục (Global Variables): Được khai báo bên ngoài tất cả các hàm, thường là ở đầu chương trình. Biến toàn cục có thể được truy cập và sử dụng từ bất kỳ đâu trong chương trình.
    int soLanNhiepAnh = 0; // soLanNhiepAnh là biến toàn cục
    
    void setup() {
      // Có thể truy cập soLanNhiepAnh ở đây
    }
    
    void loop() {
      soLanNhiepAnh++; // Có thể truy cập và thay đổi soLanNhiepAnh ở đây
    }
    Lưu ý: Mặc dù tiện lợi, việc sử dụng quá nhiều biến toàn cục có thể khiến chương trình khó quản lý và dễ gây lỗi hơn.

2. Kiểu Dữ liệu (Data Types) trong Arduino

Kiểu dữ liệu xác định loại giá trị mà một biến có thể lưu trữ và lượng bộ nhớ mà biến đó chiếm dụng. Arduino (dựa trên C++) cung cấp nhiều kiểu dữ liệu khác nhau:

Kiểu Dữ liệu Kích thước (byte) Phạm vi giá trị (xấp xỉ) Mô tả Ví dụ
boolean 1 true hoặc false Giá trị logic (đúng/sai) boolean trangThai = true;
byte 1 0 đến 255 Số nguyên không dấu (0 hoặc dương) byte giaTriCamBien = 150;
char 1 -128 đến 127 Ký tự ASCII (hoặc số nguyên nhỏ) char kyTu = 'A';
unsigned char 1 0 đến 255 Tương tự byte, dùng cho ký tự không dấu unsigned char duLieu = 200;
int 2 -32,768 đến 32,767 Số nguyên có dấu int tocDo = 1000;
unsigned int 2 0 đến 65,535 Số nguyên không dấu unsigned int thoiGian = 50000;
word 2 0 đến 65,535 Giống unsigned int word chuKy = 1024;
long 4 -2,147,483,648 đến 2,147,483,647 Số nguyên dài có dấu long soLuotDem = 100000;
unsigned long 4 0 đến 4,294,967,295 Số nguyên dài không dấu unsigned long milisTuKhiKhoiDong = 123456789;
float 4 $\pm 3.4028235E+38$ Số thập phân (số thực) float nhietDo = 27.5;
double 4 Giống float trên Arduino Uno Số thập phân (chính xác hơn trên các board khác) double pi = 3.14159265;
void 0 Không có giá trị Chỉ ra rằng một hàm không trả về giá trị void setup() { ... }
Lưu ý: Kích thước và phạm vi giá trị của một số kiểu có thể thay đổi tùy thuộc vào kiến trúc của vi điều khiển (ví dụ: trên ESP32 hoặc Teensy, intlong có thể là 4 byte). Trên các board Arduino AVR (Uno, Nano, Mega), bảng trên là chính xác.

3. Hàm (Functions) trong Arduino

Hàm là một khối mã độc lập được thiết kế để thực hiện một nhiệm vụ cụ thể. Việc sử dụng hàm giúp chương trình của bạn:

  • Tổ chức: Chia chương trình lớn thành các phần nhỏ, dễ quản lý hơn.
  • Tái sử dụng: Viết mã một lần và gọi nó nhiều lần ở các vị trí khác nhau trong chương trình.
  • Dễ đọc: Làm cho mã nguồn rõ ràng và dễ hiểu hơn.
  • Dễ bảo trì: Khi có lỗi hoặc cần thay đổi, bạn chỉ cần sửa ở một chỗ duy nhất.

Cấu trúc của một hàm

Một hàm cơ bản có cấu trúc như sau:

kieu_du_lieu_tra_ve ten_ham(kieu_tham_so1 ten_tham_so1, kieu_tham_so2 ten_tham_so2, ...) {
  // Các câu lệnh trong hàm
  return gia_tri_tra_ve; // Chỉ có nếu kieu_du_lieu_tra_ve không phải là void
}
  • kieu_du_lieu_tra_ve: Kiểu dữ liệu mà hàm sẽ trả về sau khi thực hiện xong (ví dụ: int, float, boolean). Nếu hàm không trả về bất kỳ giá trị nào, bạn sẽ dùng từ khóa void.
  • ten_ham: Tên của hàm, bạn tự đặt (nên đặt tên có ý nghĩa).
  • (tham_so1, tham_so2, ...): Danh sách các tham số. Đây là những giá trị mà bạn "truyền" vào hàm để hàm xử lý. Mỗi tham số bao gồm kiểu dữ liệu và tên biến. Nếu hàm không cần tham số nào, bạn để trống dấu ngoặc đơn ().
  • return gia_tri_tra_ve;: Câu lệnh này dùng để trả về một giá trị từ hàm. Nó phải phù hợp với kieu_du_lieu_tra_ve đã khai báo. Chỉ dùng khi kieu_du_lieu_tra_ve không phải là void.

Các loại hàm phổ biến

1. Hàm không có tham số, không có giá trị trả về (void)

Thường dùng cho các tác vụ đơn giản, không cần đầu vào hay trả về kết quả.

void nhayDen() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  nhayDen(); // Gọi hàm nhayDen()
}

2. Hàm có tham số, không có giá trị trả về (void)

Cho phép bạn truyền dữ liệu vào hàm để hàm thực hiện tác vụ dựa trên dữ liệu đó.

void dieuKhienDen(int pinDen, boolean trangThai) {
  digitalWrite(pinDen, trangThai);
}

void setup() {
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
}

void loop() {
  dieuKhienDen(13, HIGH); // Bật đèn ở chân 13
  delay(1000);
  dieuKhienDen(12, LOW);  // Tắt đèn ở chân 12
  delay(1000);
}

3. Hàm không có tham số, có giá trị trả về

Thực hiện một tác vụ và trả về một kết quả.

int docGiaTriCamBienA0() {
  int giaTri = analogRead(A0);
  return giaTri;
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  int duLieuDocDuoc = docGiaTriCamBienA0();
  Serial.print("Gia tri cam bien: ");
  Serial.println(duLieuDocDuoc);
  delay(100);
}

4. Hàm có tham số, có giá trị trả về

Loại hàm linh hoạt nhất, nhận đầu vào và trả về kết quả.

float chuyenDoiNhietDo(int docTho) {
  // Công thức chuyển đổi từ giá trị thô sang độ C (ví dụ)
  float dienAp = docTho * (5.0 / 1023.0);
  float nhietDo = (dienAp - 0.5) * 100;
  return nhietDo;
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  int giaTriTho = analogRead(A0);
  float nhietDoHienTai = chuyenDoiNhietDo(giaTriTho);
  Serial.print("Nhiet do: ");
  Serial.print(nhietDoHienTai);
  Serial.println(" C");
  delay(1000);
}

Hai hàm đặc biệt trong Arduino: setup()loop()

Mọi chương trình Arduino đều bắt buộc phải có hai hàm này:

  • void setup(): Hàm này chỉ chạy một lần duy nhất khi board Arduino được bật nguồn hoặc khởi động lại. Đây là nơi bạn cấu hình các chân (pin) là đầu vào/đầu ra, khởi tạo giao tiếp Serial, LCD, hoặc các cảm biến/module khác.
    void setup() {
      pinMode(LED_BUILTIN, OUTPUT); // Đặt chân LED tích hợp là đầu ra
      Serial.begin(9600);          // Khởi tạo giao tiếp Serial ở tốc độ 9600 baud
    }
  • void loop(): Hàm này chạy lặp đi lặp lại vô tận sau khi hàm setup() hoàn thành. Đây là nơi bạn đặt mã điều khiển chính của dự án, chẳng hạn như đọc cảm biến, điều khiển động cơ, cập nhật hiển thị, v.v.
    void loop() {
      digitalWrite(LED_BUILTIN, HIGH); // Bật LED
      delay(1000);                     // Đợi 1 giây
      digitalWrite(LED_BUILTIN, LOW);  // Tắt LED
      delay(1000);                     // Đợi 1 giây
    }

Tổng kết

Việc nắm vững các khái niệm về biến, kiểu dữ liệuhàm là nền tảng vững chắc để bạn tiến xa hơn trong lập trình Arduino. Hãy thực hành thật nhiều với các ví dụ khác nhau để biến kiến thức lý thuyết thành kỹ năng thực tế nhé!

Nhận xét

Mới hơn Cũ hơn