ESP32 điều khiển động cơ DC với driver L298N: đảo chiều, chỉnh tốc độ bằng PWM

Hướng dẫn nối ESP32 với driver L298N và động cơ DC giảm tốc: cầu H, đảo chiều, phanh, chỉnh tốc độ bằng PWM, điều khiển bằng lệnh trên Serial Monitor và thí nghiệm STEM.

Động cơ DC giảm tốc là bộ phận quen thuộc trong xe robot, băng tải mini và các mô hình STEM. Chân GPIO của ESP32 chỉ cấp được vài chục miliampe ở 3,3 V, trong khi động cơ cần hàng trăm miliampe ở 6–12 V. Vì vậy cần một driver động cơ đứng giữa. Module L298N là driver phổ biến nhất trong các bộ kit học tập.

Bài viết giải thích cầu H bên trong L298N, cách nối ESP32 với module và động cơ, hai chương trình mẫu (tự chạy tiến, lùi, phanh; điều khiển bằng lệnh gõ trên Serial Monitor) và gợi ý thí nghiệm cho giờ học STEM.

1. Vì sao không nối động cơ thẳng vào ESP32

  • Dòng điện: động cơ giảm tốc loại nhỏ cần khoảng 100–250 mA khi chạy không tải và lớn hơn nhiều lúc khởi động. Chân GPIO của ESP32 chỉ nên cấp vài chục miliampe.
  • Điện áp: GPIO chỉ có 3,3 V, trong khi động cơ thường chạy ở 3–12 V.
  • Đảo chiều: muốn quay ngược phải đổi cực điện áp trên hai đầu động cơ, việc này cần một cầu H.
  • Sức điện động ngược: khi ngắt điện, cuộn dây động cơ sinh xung điện áp ngược có thể làm hỏng chân vi điều khiển.

2. L298N và cầu H

Hình minh họa cầu H của L298N với bốn khóa S1 đến S4: đóng S1 S4 để tiến, đóng S2 S3 để lùi, đóng S3 S4 để phanh, kèm bảng trạng thái ENA IN1 IN2
Hình minh họa: cầu H và bảng trạng thái các chân ENA, IN1, IN2.

Mỗi kênh của L298N là một cầu H gồm bốn transistor xếp thành chữ H, động cơ nằm ở thanh ngang. Đóng cặp chéo S1–S4 thì dòng điện chạy qua động cơ theo một chiều, đóng cặp S2–S3 thì chạy theo chiều ngược lại. Module có hai kênh nên điều khiển được hai động cơ DC.

  • IN1, IN2 chọn chiều quay của kênh A (OUT1, OUT2). IN3, IN4 làm việc tương tự cho kênh B (OUT3, OUT4).
  • ENA bật hoặc tắt kênh A. Cấp xung PWM vào ENA thì chỉnh được tốc độ. Muốn dùng PWM phải rút jumper đang cắm trên chân ENA.
  • Phanh và thả trôi: khi ENA ở mức cao và IN1 bằng IN2, hai đầu động cơ bị nối với nhau, động cơ dừng nhanh. Khi ENA ở mức thấp, động cơ bị ngắt điện và quay trơn rồi mới dừng.
Thông số chính của L298N (theo datasheet của chip)
Thông sốGiá trị
Số kênh2 kênh cầu H (2 động cơ DC hoặc 1 động cơ bước hai pha)
Dòng mỗi kênh2 A liên tục, cần tản nhiệt khi chạy lâu
Sụt áp trên driverkhoảng 1,8–3,2 V ở 1 A (transistor lưỡng cực)
Mức logicngưỡng mức cao 2,3 V, nên nhận được tín hiệu 3,3 V của ESP32
Ổn áp 5 V trên moduledùng khi giữ jumper 5V và nguồn động cơ không quá 12 V

Vì sụt áp lớn, với nguồn 7,4 V từ hai pin 18650 thì động cơ chỉ nhận khoảng 5–5,5 V khi chạy hết tốc độ. Như vậy vừa với động cơ giảm tốc 3–6 V trong bộ kit.

3. Linh kiện cần chuẩn bị

4. Sơ đồ nối dây

Sơ đồ nối ESP32 DevKit V1 với module L298N: ENA vào D27, IN1 vào D26, IN2 vào D25, GND chung; động cơ DC nối OUT1 OUT2; khay hai pin 18650 nối cọc 12V và GND
Hình minh họa: sơ đồ nối ESP32, L298N, động cơ và khay pin.
Bảng nối dây
L298NNối tớiGhi chú
ENAD27 (GPIO27)Rút jumper ENA trước khi nối
IN1D26 (GPIO26)
IN2D25 (GPIO25)
GNDGND của ESP32 và cực − của pinBắt buộc nối chung
12VCực + của khay pinDùng được nguồn 6–12 V
OUT1, OUT2Hai chân động cơĐổi chỗ hai dây thì đổi chiều quay
  • Khi làm thí nghiệm, cấp nguồn cho ESP32 qua cổng USB của máy tính để xem Serial Monitor.
  • Khi lắp lên xe chạy pin, có thể lấy 5 V từ cọc 5V của L298N (giữ jumper 5V) cấp vào chân VIN của ESP32. Ổn định hơn là dùng mạch hạ áp LM2596 hoặc MP1584. Không cắm USB và cấp VIN từ nguồn ngoài cùng lúc.

5. Chương trình 1: tăng tốc, phanh, lùi, thả trôi

Chương trình tự chạy một vòng: tăng tốc từ 0 lên 100 % trong khoảng 3 giây, phanh, lùi 60 %, rồi thả trôi. Gói esp32 phiên bản 3.x có sẵn các hàm PWM, không cần cài thêm thư viện.

/*
  PYWORLD.VN - ESP32 điều khiển động cơ DC qua L298N: tăng tốc, phanh, lùi, thả trôi
  L298N: ENA -> GPIO27 (PWM, đã bỏ jumper ENA), IN1 -> GPIO26, IN2 -> GPIO25, GND L298N -> GND ESP32
  Gói esp32 phiên bản 3.x, không cần cài thêm thư viện
*/

#define ENA_PIN 27        // Chân PWM chỉnh tốc độ
#define IN1_PIN 26        // Hai chân chọn chiều quay
#define IN2_PIN 25

#define PWM_TAN_SO 1000   // Tần số PWM (Hz)
#define PWM_BIT    10     // Độ phân giải 10 bit: duty từ 0 đến 1023
const uint32_t DUTY_MAX = (1 << PWM_BIT) - 1;

// tocDo từ -100 đến 100 (%): dương là tiến, âm là lùi, 0 là thả trôi
void quay(int tocDo) {
  tocDo = constrain(tocDo, -100, 100);
  if (tocDo > 0) {
    digitalWrite(IN1_PIN, HIGH);
    digitalWrite(IN2_PIN, LOW);
  } else if (tocDo < 0) {
    digitalWrite(IN1_PIN, LOW);
    digitalWrite(IN2_PIN, HIGH);
  }
  // tocDo = 0: ENA ở mức thấp, hai đầu ra bị ngắt, động cơ quay trơn rồi tự dừng
  ledcWrite(ENA_PIN, (uint32_t)abs(tocDo) * DUTY_MAX / 100);
}

// Phanh: hai đầu động cơ cùng nối về 0 V, động cơ dừng nhanh
void phanh() {
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, LOW);
  ledcWrite(ENA_PIN, DUTY_MAX);
}

void setup() {
  Serial.begin(115200);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);
  if (!ledcAttach(ENA_PIN, PWM_TAN_SO, PWM_BIT)) {
    Serial.println("Khong cau hinh duoc PWM tren chan ENA!");
    while (true) delay(1000);
  }
  quay(0);
  Serial.println("ESP32 + L298N: bat dau sau 2 giay");
  delay(2000);
}

void loop() {
  Serial.println("Tien: tang toc tu 0 den 100 %");
  for (int v = 0; v <= 100; v += 10) {
    quay(v);
    Serial.printf("  toc do %3d %%\n", v);
    delay(300);
  }
  delay(2000);

  Serial.println("Phanh");
  phanh();
  delay(1000);

  Serial.println("Lui 60 %");
  quay(-60);
  delay(3000);

  Serial.println("Tha troi (ngat dien, dong co tu dung)");
  quay(0);
  delay(3000);
}

6. Chương trình 2: điều khiển bằng lệnh trên Serial Monitor

Chương trình thứ hai cho phép gõ lệnh để đổi chiều, tốc độ và tần số PWM. Đây là cách tiện để làm thí nghiệm mà không cần thêm nút nhấn hay biến trở. Trong Serial Monitor chọn tốc độ 115200 baud và kiểu xuống dòng "New Line".

Các lệnh
LệnhTác dụng
t 50Tiến với 50 % tốc độ (0–100)
l 30Lùi với 30 % tốc độ; nếu đang tiến thì phanh 0,2 giây trước khi đổi chiều
dThả trôi
pPhanh
f 5000Đổi tần số PWM (100–20000 Hz)
qQuét duty từ 0 lên 100 %, mỗi bước 5 %, giữ 1 giây
/*
  PYWORLD.VN - Điều khiển động cơ DC bằng lệnh gõ trên Serial Monitor (ESP32 + L298N)
  Lệnh: t <0-100> tiến, l <0-100> lùi, d thả trôi, p phanh, f <100-20000> đổi tần số PWM, q quét duty
  Nối dây như chương trình DC_L298N_TIEN_LUI; Serial Monitor chọn 115200 baud và "New Line"
*/

#define ENA_PIN 27
#define IN1_PIN 26
#define IN2_PIN 25

#define PWM_BIT 10
const uint32_t DUTY_MAX = (1 << PWM_BIT) - 1;

uint32_t tanSo = 1000;     // Tần số PWM hiện tại (Hz)
int tocDoHienTai = 0;      // -100..100 %

void quay(int tocDo) {
  tocDo = constrain(tocDo, -100, 100);
  if (tocDo > 0) {
    digitalWrite(IN1_PIN, HIGH);
    digitalWrite(IN2_PIN, LOW);
  } else if (tocDo < 0) {
    digitalWrite(IN1_PIN, LOW);
    digitalWrite(IN2_PIN, HIGH);
  }
  ledcWrite(ENA_PIN, (uint32_t)abs(tocDo) * DUTY_MAX / 100);
}

void phanh() {
  digitalWrite(IN1_PIN, LOW);
  digitalWrite(IN2_PIN, LOW);
  ledcWrite(ENA_PIN, DUTY_MAX);
}

// Đổi tốc độ; nếu đổi chiều thì phanh 0,2 giây trước để dòng điện không tăng vọt
void datTocDo(int moi) {
  moi = constrain(moi, -100, 100);
  if ((moi > 0 && tocDoHienTai < 0) || (moi < 0 && tocDoHienTai > 0)) {
    phanh();
    delay(200);
  }
  quay(moi);
  tocDoHienTai = moi;
}

// Thí nghiệm: tăng duty từng bước 5 % để tìm mức nhỏ nhất làm động cơ bắt đầu quay
void quetDuty() {
  Serial.println("Quet duty: 0 -> 100 %, moi buoc 5 %, giu 1 giay");
  for (int v = 0; v <= 100; v += 5) {
    datTocDo(v);
    Serial.printf("  duty %3d %%  (gia tri %4u / %u)\n", v, (unsigned)(v * DUTY_MAX / 100), (unsigned)DUTY_MAX);
    delay(1000);
  }
  datTocDo(0);
  Serial.println("Xong. Ghi lai duty nho nhat lam dong co bat dau quay.");
}

void inHuongDan() {
  Serial.println("Lenh: t 50 = tien 50 %, l 30 = lui 30 %, d = tha troi, p = phanh,");
  Serial.println("      f 5000 = doi tan so PWM, q = quet duty tu 0 den 100 %");
}

void xuLyLenh(String lenh) {
  lenh.trim();
  if (lenh.length() == 0) return;
  char c = tolower(lenh.charAt(0));
  int so = lenh.substring(1).toInt();
  switch (c) {
    case 't': datTocDo(so); break;
    case 'l': datTocDo(-so); break;
    case 'd': datTocDo(0); break;
    case 'p': phanh(); tocDoHienTai = 0; break;
    case 'f':
      if (so < 100 || so > 20000) {
        Serial.println("Tan so nen trong khoang 100 - 20000 Hz");
        return;
      }
      tanSo = ledcChangeFrequency(ENA_PIN, so, PWM_BIT);
      quay(tocDoHienTai);   // ghi lại duty sau khi đổi tần số
      break;
    case 'q': quetDuty(); return;
    default:
      Serial.println("Lenh khong hop le");
      inHuongDan();
      return;
  }
  Serial.printf("Toc do: %d %% | tan so PWM: %u Hz\n", tocDoHienTai, (unsigned)tanSo);
}

void setup() {
  Serial.begin(115200);
  pinMode(IN1_PIN, OUTPUT);
  pinMode(IN2_PIN, OUTPUT);
  ledcAttach(ENA_PIN, tanSo, PWM_BIT);
  quay(0);
  delay(500);
  Serial.println("ESP32 + L298N: dieu khien dong co bang lenh");
  inHuongDan();
}

void loop() {
  if (Serial.available()) {
    xuLyLenh(Serial.readStringUntil('\n'));
  }
}
Hình minh họa Serial Monitor khi gõ các lệnh t 50, l 30, f 5000 và q: in tốc độ, tần số PWM và giá trị duty từng bước
Hình minh họa: kết quả in ra khi gõ lần lượt các lệnh.

7. Giải thích các lệnh chính

  • ledcAttach(ENA_PIN, 1000, 10): gắn chân ENA vào bộ PWM của ESP32 với tần số 1000 Hz, độ phân giải 10 bit (duty 0–1023).
  • ledcWrite(ENA_PIN, duty): đặt độ rộng xung. Phần trăm tốc độ được đổi ra giá trị duty theo công thức phần trăm × 1023 / 100.
  • digitalWrite(IN1_PIN, ...), digitalWrite(IN2_PIN, ...): chọn chiều quay theo bảng trạng thái ở mục 2.
  • phanh(): đặt IN1 = IN2 = 0 và ENA ở mức cao. Hàm quay(0) chỉ đưa ENA về 0, nên động cơ thả trôi.
  • ledcChangeFrequency(): đổi tần số PWM khi chương trình đang chạy, rồi ghi lại duty.
  • Đổi chiều đột ngột ở tốc độ cao làm dòng điện tăng vọt. Chương trình phanh 0,2 giây trước khi đổi chiều để giảm dòng.

8. Chọn tần số PWM cho L298N

  • Tần số thấp (vài trăm Hz) cho mô-men tốt ở tốc độ chậm nhưng động cơ rung và kêu ù.
  • Khoảng 1–5 kHz là mức thường dùng với L298N: động cơ chạy êm vừa phải, driver không nóng thêm nhiều.
  • Trên 20 kHz thì tai người không còn nghe tiếng rít. Tuy vậy L298N chuyển mạch chậm, ở tần số cao driver nóng hơn và tốc độ thấp kém đi.

Lệnh f trong chương trình 2 cho phép tự nghe và so sánh các tần số này.

9. Gợi ý dùng trong giờ học STEM

Mục tiêu: học sinh hiểu vì sao động cơ cần driver, cầu H đổi chiều quay thế nào, và điện áp trung bình của PWM quyết định tốc độ ra sao.

  1. Tìm duty khởi động. Gõ lệnh q và ghi lại duty nhỏ nhất làm động cơ bắt đầu quay. Với L298N và động cơ giảm tốc nhỏ, duty này thường khá cao vì driver sụt áp và động cơ cần đủ lực để thắng ma sát. Thảo luận: tại sao dưới mức đó động cơ chỉ kêu mà không quay?
  2. Đo tốc độ theo duty. Dán một mẩu băng dính lên bánh xe, đặt duty 40, 60, 80, 100 %, đếm số vòng trong 10 giây. Vẽ đồ thị số vòng mỗi phút theo duty và nhận xét đồ thị có thẳng không.
  3. So sánh phanh và thả trôi. Cho bánh xe quay 100 %, lần lượt dùng lệnh p và d, quan sát bánh xe quay thêm bao lâu mới dừng. Giải thích bằng hình cầu H ở mục 2.
  4. Đo sụt áp trên driver. Dùng đồng hồ vạn năng đo điện áp pin và điện áp giữa OUT1, OUT2 ở 100 %. Hiệu hai số là phần điện áp L298N giữ lại.

Các thí nghiệm này gắn với kiến thức về điện và từ trong môn Khoa học tự nhiên, Vật lý (động cơ điện một chiều, công suất), kỹ năng lập bảng và vẽ đồ thị trong Toán, và lập trình trong Tin học.

10. Xử lý lỗi thường gặp

Lỗi thường gặp
Hiện tượngNguyên nhân thường gặpCách xử lý
Động cơ không quay, đèn module sángChưa nối GND chung; jumper ENA còn cắm nhưng chân ENA lại nối GPIO; duty quá thấpNối GND của ESP32 với GND của L298N; rút jumper ENA; thử t 100
Chỉ quay được một chiềuLỏng dây IN1 hoặc IN2Kiểm tra lại hai dây chọn chiều
ESP32 khởi động lại khi động cơ chạyNguồn sụt khi động cơ khởi động, nhiễu từ động cơDùng pin còn đầy; cấp ESP32 bằng nguồn riêng hoặc mạch hạ áp; hàn tụ 100 nF giữa hai chân động cơ
Tốc độ thấp không quay, chỉ kêuDuty dưới mức khởi độngĐặt duty tối thiểu cao hơn mức tìm được ở thí nghiệm 1
Module nóngĐộng cơ kéo dòng lớn hoặc bị kẹtGiảm tải; cho nghỉ; dùng driver MOSFET khi cần dòng lớn
Động cơ kêu rítTần số PWM nằm trong vùng nghe đượcThử f 2000 đến f 5000 và chọn mức êm nhất

11. Xem thêm

Các bài tiếp theo trong loạt ESP32 điều khiển động cơ: đo tốc độ bằng encoder, giữ tốc độ ổn định bằng PID và xe robot điều khiển qua Wi-Fi.


Tin tức liên quan

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code
Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, cài driver, chọn Board và Processor, nạp Blink, in ra Serial Monitor, quét I2C trên A4/A5, cấp nguồn và xử lý lỗi thường gặp.

Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác
Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác

Làm cánh tay robot 4 servo với ESP32: điều khiển từng khớp bằng biến trở, nhấn nút ghi tư thế, phát lại chuỗi động tác mượt, lưu vào flash khi mất điện và gợi ý STEM.

Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny
Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny

Hướng dẫn cài driver, cài Thonny và nạp firmware MicroPython cho mạch ESP32 Lite, dùng REPL, nháy LED, lưu main.py, đọc nút nhấn, quét I2C và xử lý lỗi thường gặp.

ESP32 điều khiển động cơ bước 28BYJ-48 với ULN2003: nửa bước, quay đúng góc, kim giây
ESP32 điều khiển động cơ bước 28BYJ-48 với ULN2003: nửa bước, quay đúng góc, kim giây

Hướng dẫn điều khiển động cơ bước 28BYJ-48 bằng ESP32 qua mạch ULN2003: chế độ nửa bước, 4076 bước một vòng, quay đúng góc theo lệnh, kim giây đồng hồ và thí nghiệm STEM.

Hướng dẫn dùng ngắt GPIO trên ESP32: đọc nút nhấn, chống dội và đếm xung
Hướng dẫn dùng ngắt GPIO trên ESP32: đọc nút nhấn, chống dội và đếm xung

Hướng dẫn dùng ngắt GPIO trên ESP32 với Arduino IDE: attachInterrupt, các kiểu kích ngắt, chống dội nút nhấn, đếm xung đo tần số và quy tắc viết hàm ngắt an toàn.

Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái
Điều khiển relay từ xa bằng ESP-NOW: remote ESP32 một nút nhấn có phản hồi trạng thái

Hướng dẫn làm bộ điều khiển relay từ xa bằng hai mạch ESP32 và ESP-NOW hai chiều: remote gửi lệnh, mạch relay báo lại trạng thái, lọc theo địa chỉ MAC và bật mã hóa.

Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266
Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266

Hướng dẫn nối OLED SSD1306 với ESP8266, quét địa chỉ I2C, cài thư viện và hiển thị thời gian bằng mã Arduino mẫu.

Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32
Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32

So sánh mạch hạ áp LM2596 và MP1584, cách chỉnh điện áp ra bằng đồng hồ vạn năng trước khi nối tải, cấp nguồn 5 V cho ESP32 DevKit V1 từ nguồn 12 V và xử lý lỗi thường gặp.


Bình luận
Đã thêm vào giỏ hàng