ESP32 điều khiển động cơ bước NEMA 17 với A4988: vi bước, chỉnh dòng VREF, tăng tốc mượt
Hướng dẫn điều khiển động cơ bước NEMA 17 bằng ESP32 qua driver A4988: nối dây, chỉnh dòng VREF, chọn vi bước 1/16, tăng tốc giảm tốc hình thang và thí nghiệm STEM.
Động cơ bước NEMA 17 với driver A4988 là bộ đôi có mặt trong máy in 3D, máy CNC mini, máy vẽ và máy khắc laser loại nhỏ. So với động cơ 28BYJ-48, NEMA 17 khỏe và nhanh hơn nhiều, còn A4988 cho phép chia mỗi bước thành 16 vi bước để động cơ chạy êm. ESP32 chỉ cần gửi hai tín hiệu: STEP (mỗi xung là một bước) và DIR (chiều quay).
Bài viết hướng dẫn nối dây, chỉnh dòng điện bằng VREF, chọn vi bước, chương trình tăng tốc và giảm tốc hình thang để động cơ không bị trượt bước, cùng gợi ý thí nghiệm STEM.
1. A4988 và động cơ NEMA 17
| Thông số | Giá trị |
|---|---|
| Điện áp động cơ (VMOT) của A4988 | 8–35 V, thường dùng 12 V |
| Dòng mỗi cuộn | khoảng 1 A khi không tản nhiệt, tới 2 A khi có tản nhiệt tốt |
| Logic (VDD) | 3–5,5 V, dùng 3,3 V của ESP32 |
| Vi bước | bước đầy, 1/2, 1/4, 1/8, 1/16 |
| Động cơ NEMA 17 thông dụng | 1,8° mỗi bước (200 bước một vòng), mặt bích 42 × 42 mm, hai cuộn dây, 4 dây ra |
- Bốn dây của động cơ chia làm hai cặp: 1A–1B là cuộn A, 2A–2B là cuộn B. Hai dây cùng cuộn đo được điện trở vài ohm; hai dây khác cuộn thì không thông mạch.
- Chân EN ở mức thấp thì driver cấp điện cho cuộn dây. Ở mức cao thì trục quay tự do.
- Chân RST phải nối với SLP. Để hở RST thì driver không chạy.
2. Vi bước, tăng tốc và chỉnh dòng VREF

- Vi bước: ở chế độ 1/16, một vòng trục cần 3200 xung STEP. Động cơ chạy êm và mịn hơn nhưng ESP32 phải phát nhiều xung hơn cho cùng tốc độ.
- Tăng tốc hình thang: động cơ bước không thể đang đứng yên mà nhảy ngay lên tốc độ cao; làm vậy rôto không theo kịp và bị trượt bước. Chương trình tăng tốc dần, chạy đều, rồi giảm tốc trước khi dừng.
- Chỉnh dòng VREF: dòng tối đa mỗi cuộn bằng
VREF / (8 × Rs). Rs là điện trở cảm biến dòng in trên mạch: R100 là 0,1 Ω, R068 là 0,068 Ω. Đặt khoảng 70–80 % dòng định mức của động cơ.
Cách đo VREF: cấp nguồn logic (VDD) cho A4988, để que đen của đồng hồ vạn năng ở GND, que đỏ chạm vào con chạy của biến trở nhỏ trên mạch. Dùng tua vít nhựa vặn từ từ đến đúng giá trị.
3. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân.
- Module A4988 kèm tản nhiệt và động cơ bước NEMA 17.
- Nguồn 12 V, 2 A trở lên; tụ hóa 100 µF, 25 V trở lên.
- Breadboard 830 lỗ và dây cắm test board.
4. Sơ đồ nối dây

- Cắm A4988 lên breadboard theo đúng chiều in trên mạch. Các mạch A4988 cùng thứ tự chân, nhưng chiều biến trở và tản nhiệt có thể khác.
- Tụ 100 µF đặt sát chân VMOT và GND để chặn xung điện áp khi cấp nguồn. Thiếu tụ có thể làm hỏng driver.
- Không cắm hoặc rút dây động cơ khi driver đang có điện. Cuộn dây sinh điện áp cao làm cháy A4988.
- GND của nguồn 12 V và GND của ESP32 phải nối chung. Hai chân GND trên A4988 đã nối với nhau bên trong mạch.
5. Chương trình: vi bước và tăng tốc hình thang
Chương trình nhận lệnh trên Serial Monitor (115200 baud, kiểu xuống dòng "New Line"):
| Lệnh | Tác dụng |
|---|---|
q 1 | Quay 1 vòng; q -0.5 quay ngược nửa vòng |
m 16 | Chọn vi bước 1/16 (nhận 1, 2, 4, 8, 16) |
v 2 | Tốc độ tối đa 2 vòng/s |
a 4 | Gia tốc 4 vòng/s² |
e 0, e 1 | Tắt driver (thả trục), bật lại |
/*
PYWORLD.VN - ESP32 điều khiển động cơ bước NEMA 17 qua A4988: chọn vi bước, tăng tốc và giảm tốc hình thang
A4988: EN -> GPIO21, MS1 -> GPIO19, MS2 -> GPIO18, MS3 -> GPIO5, STEP -> GPIO17, DIR -> GPIO16, RST nối SLP
Lệnh (115200, New Line): q 1 = quay 1 vòng (âm là quay ngược), m 16 = vi bước 1/16, v 2 = tốc độ 2 vòng/s, a 4 = gia tốc 4 vòng/s2, e 0 / e 1
*/
#define EN_PIN 21 // mức thấp: driver cấp điện cho cuộn dây
#define MS1_PIN 19
#define MS2_PIN 18
#define MS3_PIN 5
#define STEP_PIN 17 // mỗi xung lên là một bước (hoặc một vi bước)
#define DIR_PIN 16 // chiều quay
const int BUOC_DAY_MOI_VONG = 200; // động cơ 1,8 độ mỗi bước
int viBuoc = 16; // 1, 2, 4, 8 hoặc 16
float vMaxVong = 2.0; // tốc độ tối đa, vòng/s
float giaTocVong = 4.0; // gia tốc, vòng/s2
long buocMoiVong() {
return (long)BUOC_DAY_MOI_VONG * viBuoc;
}
// Bảng chọn vi bước của A4988: MS1 MS2 MS3
bool chonViBuoc(int n) {
int ms1, ms2, ms3;
switch (n) {
case 1: ms1 = 0; ms2 = 0; ms3 = 0; break;
case 2: ms1 = 1; ms2 = 0; ms3 = 0; break;
case 4: ms1 = 0; ms2 = 1; ms3 = 0; break;
case 8: ms1 = 1; ms2 = 1; ms3 = 0; break;
case 16: ms1 = 1; ms2 = 1; ms3 = 1; break;
default: return false;
}
digitalWrite(MS1_PIN, ms1);
digitalWrite(MS2_PIN, ms2);
digitalWrite(MS3_PIN, ms3);
viBuoc = n;
return true;
}
// Chạy soBuoc bước. Tốc độ tại mỗi bước tính theo v = căn(2 x gia tốc x quãng đường),
// lấy theo đầu gần hơn (đầu tăng tốc hoặc đầu giảm tốc) và không vượt vMax: được hình thang.
void chay(long soBuoc, float vMax, float giaToc) {
digitalWrite(DIR_PIN, soBuoc >= 0 ? HIGH : LOW);
delayMicroseconds(5);
long n = labs(soBuoc);
unsigned long t = micros();
for (long i = 0; i < n; i++) {
long k = min(i + 1, n - i);
float v = sqrtf(2.0f * giaToc * k);
if (v > vMax) v = vMax;
if (v < 20.0f) v = 20.0f;
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(3); // A4988 cần xung rộng tối thiểu 1 micro giây
digitalWrite(STEP_PIN, LOW);
t += (unsigned long)(1000000.0f / v);
while ((long)(micros() - t) < 0) {
}
}
}
// Thời gian lý thuyết của chuyển động hình thang (hoặc tam giác nếu quãng ngắn)
float thoiGian(long n, float v, float a) {
float sTang = v * v / (2 * a);
if (2 * sTang <= n) return n / v + v / a;
return 2 * sqrtf(n / a);
}
void inTrangThai() {
Serial.printf("Vi buoc 1/%d (%ld buoc/vong), vmax %.1f vong/s, gia toc %.1f vong/s2\n",
viBuoc, buocMoiVong(), vMaxVong, giaTocVong);
}
void setup() {
Serial.begin(115200);
int chan[] = {EN_PIN, MS1_PIN, MS2_PIN, MS3_PIN, STEP_PIN, DIR_PIN};
for (int p : chan) pinMode(p, OUTPUT);
digitalWrite(STEP_PIN, LOW);
digitalWrite(EN_PIN, LOW);
chonViBuoc(16);
delay(500);
Serial.println("A4988: q <vong>, m <1|2|4|8|16>, v <vong/s>, a <vong/s2>, e <0|1>");
inTrangThai();
}
void loop() {
if (!Serial.available()) return;
String s = Serial.readStringUntil('\n');
s.trim();
if (s.length() < 1) return;
char c = tolower(s.charAt(0));
float so = s.substring(1).toFloat();
if (c == 'q') {
long n = lroundf(so * buocMoiVong());
float v = vMaxVong * buocMoiVong();
float a = giaTocVong * buocMoiVong();
Serial.printf("Quay %.2f vong = %ld buoc, du kien %.2f s\n", so, n, thoiGian(labs(n), v, a));
unsigned long t0 = millis();
chay(n, v, a);
Serial.printf("Xong sau %lu ms\n", millis() - t0);
} else if (c == 'm') {
if (!chonViBuoc((int)so)) Serial.println("Chi nhan 1, 2, 4, 8, 16");
inTrangThai();
} else if (c == 'v' && so > 0) {
vMaxVong = so;
inTrangThai();
} else if (c == 'a' && so > 0) {
giaTocVong = so;
inTrangThai();
} else if (c == 'e') {
digitalWrite(EN_PIN, so > 0 ? LOW : HIGH); // e 1: bật giữ lực, e 0: thả trục
Serial.println(so > 0 ? "Driver bat" : "Driver tat, truc quay tu do");
} else {
Serial.println("Lenh khong hop le");
}
}

6. Giải thích các lệnh chính
chonViBuoc()đặt MS1, MS2, MS3 theo bảng ở mục 2. Đổi vi bước khi động cơ đang đứng yên.chay()phát từng xung STEP. Tốc độ ở mỗi bước tính theov = √(2 × a × s), vớislà số bước tính từ đầu gần hơn (đầu tăng tốc hoặc đầu giảm tốc). Tốc độ không vượtvMax. Kết quả là đồ thị hình thang, hoặc hình tam giác khi quãng đường ngắn.- Khoảng cách giữa hai xung là
1 000 000 / vmicro giây. Biếntcộng dồn thời điểm đích, nên thời gian chạy lệnhdigitalWritekhông làm chậm dần chuyển động. thoiGian()tính thời gian theo lý thuyết để so với thời gian đo được sau mỗi lần quay.
Hàm chay() chiếm CPU trong lúc quay. Muốn vừa quay vừa làm việc khác (đọc nút, Wi-Fi), có thể phát xung bằng timer phần cứng hoặc chạy hàm trên lõi thứ hai của ESP32 bằng FreeRTOS.
7. Gợi ý dùng trong giờ học STEM
Mục tiêu: học sinh hiểu điều khiển vị trí bằng đếm bước, quan hệ giữa vận tốc, gia tốc và quãng đường, và giới hạn của động cơ thật.
- So sánh vi bước. Quay 1 vòng ở chế độ bước đầy rồi ở 1/16, cùng tốc độ. Nghe tiếng ồn, sờ độ rung. Vì sao vi bước êm hơn?
- Tìm gia tốc lớn nhất. Gắn một đĩa nặng lên trục. Tăng dần
atới khi động cơ trượt bước (kêu, không về đúng vạch đánh dấu sau 10 vòng). Nhận xét: tải nặng hơn thì gia tốc tối đa thay đổi ra sao? - Kiểm chứng công thức chuyển động. So thời gian dự kiến với thời gian đo được, rồi giải thích chênh lệch. Đây là bài tập về chuyển động biến đổi đều trong Vật lý.
- Trục vít me. Gắn động cơ với trục vít bước 8 mm. Tính số bước để bàn trượt đi đúng 10 mm, đo bằng thước kẹp.
8. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Động cơ không quay, không giữ lực | RST để hở; EN ở mức cao; chưa có nguồn VMOT | Nối RST với SLP; gõ e 1; kiểm tra nguồn 12 V |
| Động cơ rung tại chỗ, không quay | Sai cặp cuộn dây; VREF quá thấp | Đo điện trở để tìm đúng cặp; tăng VREF |
| Chạy được một lúc thì trượt bước, kêu lạch cạch | Gia tốc hoặc tốc độ quá cao so với tải | Giảm a, v; tăng dòng (VREF) trong giới hạn |
| A4988 rất nóng, tự ngắt | Dòng đặt quá cao, thiếu tản nhiệt | Giảm VREF; gắn tản nhiệt, thêm quạt |
| Quay ngược chiều | Đảo cặp dây cuộn A hoặc cuộn B | Đổi dấu số vòng hoặc đảo hai dây của một cuộn khi đã tắt nguồn |
9. Xem thêm
- Ngắt GPIO trên ESP32: đọc nút nhấn, chống dội (thêm công tắc hành trình về gốc)
- PWM trên ESP32
- Mức logic 3,3 V và 5 V
Cùng loạt: động cơ bước 28BYJ-48 với ULN2003, động cơ DC với L298N.
Xem thêm