Digital vs Analog: vì sao vi điều khiển cần ADC để "nghe" được thế giới thật
Atlasbot · 20/8/2026 · 10 phút đọc
Cảm biến ánh sáng (LDR), cảm biến nhiệt độ, hay micro đều tạo ra tín hiệu biến đổi liên tục theo thời gian — gọi là tín hiệu analog. Nhưng bộ xử lý bên trong vi điều khiển chỉ thực sự hiểu digital — chuỗi 0 và 1. Cầu nối giữa hai thế giới này — ADC (đọc analog vào) và PWM (giả lập analog ra) — là kiến thức nền tảng bắt buộc trước khi làm việc với bất kỳ cảm biến tương tự nào.
Digital là gì
Tín hiệu digital (số) chỉ có một số hữu hạn mức rời rạc — phổ biến nhất là 2 mức: HIGH (thường ~3.3V hoặc 5V) và LOW (~0V). Vi điều khiển phân biệt 2 mức này dựa trên một ngưỡng điện áp (threshold) — điện áp trên ngưỡng đọc là HIGH (1), dưới ngưỡng đọc là LOW (0), không quan tâm giá trị chính xác ở giữa.
Analog là gì
Tín hiệu analog (tương tự) biến đổi liên tục trong một khoảng giá trị, không nhảy cóc giữa các mức rời rạc — giống như núm vặn âm lượng xoay mượt, khác nút bấm chỉ có 2 trạng thái bật/tắt. Hầu hết đại lượng vật lý trong thế giới thật (ánh sáng, nhiệt độ, áp suất, âm thanh) vốn dĩ là analog.
Tóm lại: Digital = hữu hạn mức rời rạc (thường 2 mức: 0/1); Analog = biến đổi liên tục, vô hạn giá trị trong 1 khoảng.
ADC — cầu nối từ Analog vào Digital
ADC (Analog-to-Digital Converter) là bộ phận trong vi điều khiển đo điện áp analog và quy đổi thành một con số digital mà chương trình đọc được. Hai thông số quan trọng nhất của ADC:
- Độ phân giải (resolution) — số bit của kết quả, quyết định độ mịn. ADC 10-bit (phổ biến trên Arduino Uno) chia điện áp thành 2¹⁰ = 1024 mức; ADC 12-bit (STM32, ESP32) chia thành 4096 mức mịn hơn.
- Điện áp tham chiếu (reference voltage) — điện áp tương ứng với giá trị ADC tối đa. Trên Arduino Uno mặc định là 5V — nghĩa là giá trị ADC đọc được 1023 tương ứng đúng 5V đưa vào chân analog.
Điện áp đo được = (Giá trị ADC / Giá trị ADC tối đa) × Điện áp tham chiếu
VD: ADC 10-bit, Vref = 5V, đọc được giá trị 512
Điện áp = (512 / 1023) × 5V ≈ 2.5V
PWM — giả lập Analog ra bằng Digital
Ngược lại với ADC, nhiều vi điều khiển (đặc biệt các chân thường trên Arduino) không có DAC thật để xuất ra điện áp analog chính xác — thay vào đó dùng PWM (Pulse Width Modulation): bật/tắt chân ở tần số rất nhanh, thay đổi tỷ lệ thời gian bật (duty cycle) để tạo cảm giác như một mức điện áp trung bình khác nhau.
// Arduino — PWM 8-bit, giá trị 0-255
analogWrite(9, 128); // duty cycle 50% → điện áp trung bình ~2.5V (nếu nguồn 5V)
analogWrite(9, 255); // duty cycle 100% → gần như 5V liên tục
analogWrite(9, 0); // duty cycle 0% → 0V
PWM đủ nhanh khiến mắt người hoặc động cơ DC "cảm nhận" như một mức điện áp liên tục (VD: dùng để chỉnh độ sáng LED hoặc tốc độ động cơ) — nhưng về bản chất tín hiệu vẫn là digital bật/tắt liên tục, không phải analog thật.
Kiến thức kỹ thuật
Công thức
Điện áp đo được từ ADC (đã nêu ở trên) và duty cycle của PWM là 2 công thức dùng hàng ngày khi làm nhúng:
Điện áp ADC = (Giá trị ADC / Giá trị ADC tối đa) × Vref
Duty cycle (%) = (Thời gian mức HIGH / Chu kỳ) × 100%
VD: PWM 8-bit, analogWrite(9, 64)
Duty cycle = 64 / 255 × 100% ≈ 25%
→ điện áp trung bình ≈ 25% × 5V = 1.25V
Thông số
Độ phân giải ADC và điện áp tham chiếu mặc định trên các board phổ biến:
| Board/Chip | Độ phân giải ADC | Số mức | Vref mặc định |
|---|---|---|---|
| Arduino Uno | 10-bit | 1024 | 5V |
| STM32 (đa số dòng) | 12-bit | 4096 | 3.3V |
| ESP32 | 12-bit | 4096 | 3.3V |
Register / Bit / Byte
Sau khi ADC hoàn tất một lần chuyển đổi, kết quả nằm trong một thanh ghi dữ liệu chuyên dụng — trên STM32 là ADCx->DR (Data Register), trên AVR (Arduino Uno) kết quả 10-bit được chia thành 2 thanh ghi 8-bit ADCH và ADCL (byte cao và byte thấp) vì thanh ghi AVR chỉ rộng 8-bit. Hàm analogRead() thực chất là đọc và ghép 2 thanh ghi này lại thành 1 số 10-bit — hiểu điều này giải thích vì sao đọc trực tiếp thanh ghi ADC (thay vì gọi hàm thư viện) đôi khi nhanh hơn trong code cần tốc độ cao.
Ứng dụng thực tế
Robot dò line (line-following robot) đọc nhiều cảm biến hồng ngoại analog qua ADC để phân biệt vùng sáng/tối trên đường line — độ phân giải ADC càng cao, robot càng phân biệt được nhiều mức xám trung gian thay vì chỉ "có line/không có line". Tương tự, đo dòng điện qua cảm biến dòng analog (VD: ACS712) để giám sát mức tiêu thụ pin của robot AMR theo thời gian thực cũng dựa trên đúng nguyên lý ADC này — điện áp ra của cảm biến tỷ lệ thuận với dòng điện đang đo.
Ví dụ thực tế: đọc cảm biến ánh sáng qua ADC
// Arduino — đọc cảm biến quang trở (LDR) qua chân analog
int sensorValue = analogRead(A0); // trả về 0-1023 (ADC 10-bit)
float voltage = sensorValue * (5.0 / 1023.0);
Serial.println(voltage); // In ra điện áp thực tế đo được, VD: 2.45
Output mẫu:
2.45
2.41
1.83 ← ánh sáng thay đổi (VD: che cảm biến), điện áp giảm
Lỗi thường gặp
- Nhầm PWM với analog thật —
analogWrite()không tạo ra điện áp analog thật, chỉ là digital bật/tắt nhanh — nếu cần điện áp analog thật ổn định (không nhấp nháy), cần mạch lọc RC hoặc DAC chuyên dụng. - Đọc giá trị ADC nhưng quên quy đổi ra điện áp — giá trị
analogRead()trả về là số nguyên 0-1023 (hoặc 0-4095 tuỳ chip), không phải điện áp — phải nhân vớiVref/giá trị tối đamới ra đơn vị Volt. - Điện áp tham chiếu (Vref) không đúng như giả định — một số board cho phép đổi Vref (VD: dùng nguồn 3.3V thay vì 5V mặc định) — quên cập nhật công thức quy đổi khi đổi Vref dẫn tới tính sai điện áp thực tế.
- Độ phân giải ADC không đủ mịn cho ứng dụng cần độ chính xác cao — ADC 10-bit trên tầm đo 5V chỉ phân biệt được bước ~4.9mV — nếu cần đo tín hiệu biến đổi rất nhỏ, cần ADC độ phân giải cao hơn hoặc mạch khuếch đại tín hiệu trước khi đưa vào ADC.
Kết luận
ADC và PWM là hai công cụ đối lập nhưng bổ sung cho nhau — một chiều đọc analog vào thành digital, một chiều giả lập digital ra như thể analog. Hiểu rõ đây không phải "analog thật" giúp tránh nhầm lẫn phổ biến khi debug mạch cảm biến hoặc điều khiển tốc độ động cơ. Bài tiếp theo trong chuyên mục nói về GPIO — chân đa năng cơ bản nhất, nơi các khái niệm digital này được áp dụng trực tiếp.