Atlas Robotics
Kiến thức nền tảngCơ bản

Bit, Byte, hệ nhị phân và thập lục phân: ngôn ngữ gốc của vi điều khiển

Atlasbot · 20/8/2026 · 10 phút đọc

Bit, Byte, hệ nhị phân và thập lục phân: ngôn ngữ gốc của vi điều khiển
Bit là 1 chữ số nhị phân, Byte là 8 bit, hex là cách viết gọn binary

Mở bất kỳ datasheet vi điều khiển nào, bạn sẽ thấy vô số con số dạng 0x1F, 0b1010, "thanh ghi 8-bit", "địa chỉ 16-bit" — không phải ngẫu nhiên mà lập trình nhúng gắn chặt với hệ nhị phân và thập lục phân: phần cứng máy tính vận hành hoàn toàn bằng tín hiệu điện 2 mức (có/không có điện), nên mọi thứ máy tính "hiểu" cuối cùng đều quy về những con số 0 và 1 này.

Bit và Byte là gì

Bit (binary digit) là đơn vị dữ liệu nhỏ nhất, chỉ nhận 1 trong 2 giá trị: 0 hoặc 1 — tương ứng với mức điện áp thấp/cao trong mạch điện tử. Byte là một nhóm 8 bit, đơn vị dữ liệu cơ bản mà hầu hết vi điều khiển xử lý cùng lúc (dù nhiều MCU hiện đại xử lý 32-bit — 4 byte — cùng lúc trong mỗi lệnh).

Với 8 bit, một byte biểu diễn được 2⁸ = 256 giá trị khác nhau (từ 0 đến 255 nếu không dùng dấu) — đây là lý do kiểu dữ liệu uint8_t trong C chỉ chứa được tối đa 255, và vì sao PWM 8-bit trên Arduino chỉ có 256 mức (0-255) chứ không mịn hơn.

Hệ nhị phân (binary)

Hệ nhị phân chỉ dùng 2 chữ số (0, 1), khác hệ thập phân quen thuộc dùng 10 chữ số (0-9). Cách đọc giá trị giống hệt thập phân, chỉ khác cơ số — mỗi vị trí là luỹ thừa của 2 thay vì luỹ thừa của 10:

Nhị phân:  1  0  1  1
Vị trí:    2³ 2² 2¹ 2⁰  =  8  4  2  1

Giá trị = 1×8 + 0×4 + 1×2 + 1×1 = 11 (thập phân)

Vì sao máy tính dùng hệ nhị phân

Mạch điện tử số chỉ cần phân biệt 2 trạng thái (có điện / không điện, hoặc điện áp cao / thấp) — dễ chế tạo, ít nhiễu, ổn định hơn nhiều so với việc phải phân biệt chính xác 10 mức điện áp khác nhau như hệ thập phân sẽ yêu cầu. Đây là lý do nền tảng khiến toàn bộ máy tính hiện đại xây dựng trên hệ nhị phân, không phải lựa chọn tuỳ ý.

Hệ thập lục phân (hexadecimal)

Viết số nhị phân dài (VD: 10110100) rất khó đọc và dễ gõ nhầm — hệ thập lục phân (hex), dùng 16 chữ số (0-9 và A-F), giải quyết vấn đề này. Vì 16 = 2⁴, mỗi 1 chữ số hex tương ứng chính xác 4 bit — một byte (8 bit) luôn viết gọn thành đúng 2 chữ số hex:

Nhị phân (4 bit)HexNhị phân (4 bit)Hex
0000010008
0001110019
001021010A
001131011B
010041100C
010151101D
011061110E
011171111F

Ví dụ: byte nhị phân 10110100 tách thành 2 nhóm 4 bit 1011 và 0100, tra bảng ra B và 4 — viết gọn thành 0xB4 (tiền tố 0x là quy ước báo đây là số hex trong hầu hết ngôn ngữ lập trình, kể cả C/C++ dùng cho vi điều khiển).

Tóm lại: Binary là ngôn ngữ thật của phần cứng; hex chỉ là cách viết gọn binary cho con người dễ đọc — 1 chữ số hex luôn bằng đúng 4 bit, không phải quy đổi phức tạp.

Kiến thức kỹ thuật

Công thức

Với N bit, số giá trị khả dụng (không dấu) là 2^N, từ 0 đến 2^N − 1:

2^N giá trị, dải giá trị: 0 đến (2^N − 1)

VD: N = 8  → 2⁸ = 256 giá trị → 0-255
    N = 16 → 2¹⁶ = 65536 giá trị → 0-65535

Thông số

Kích thước và dải giá trị của các kiểu dữ liệu C phổ biến trong lập trình nhúng:

Kiểu dữ liệuSố bitDải giá trị (không dấu)
uint8_t80 – 255
int8_t8-128 – 127
uint16_t160 – 65.535
uint32_t320 – 4.294.967.295

Register / Bit / Byte

Thanh ghi GPIOx_IDR (Input Data Register) trên STM32 là ví dụ điển hình: thanh ghi 32-bit nhưng chỉ 16 bit thấp có ý nghĩa (mỗi bit ứng với 1 chân từ Pin0 đến Pin15). Đọc giá trị 0x0005 (nhị phân 0000 0000 0000 0101) cho biết ngay Pin0 và Pin2 đang ở mức HIGH, các chân còn lại LOW — đọc 1 lần ra trạng thái của 16 chân cùng lúc, thay vì phải đọc từng chân riêng lẻ.

Ứng dụng thực tế

Địa chỉ thiết bị I2C luôn viết dạng hex trong datasheet lẫn code (VD: cảm biến MPU6050 có địa chỉ 0x68) — đối chiếu đúng địa chỉ hex này là bước đầu tiên khi debug lỗi "không quét thấy thiết bị I2C". Ngoài ra, khi robot có nhiều công tắc hành trình (limit switch) nối vào cùng 1 port GPIO, đọc toàn bộ thanh ghi 1 lần (thay vì đọc từng chân bằng digitalRead() riêng lẻ) giúp lấy được trạng thái của tất cả công tắc tại đúng cùng 1 thời điểm — tránh sai lệch nhỏ về thời gian giữa các lần đọc riêng lẻ.

Ví dụ thực tế: đọc thanh ghi trong lập trình nhúng

// STM32 — cấu hình thanh ghi GPIO trực tiếp bằng hex
GPIOA->MODER |= 0x00000001;   // set bit 0 = 1, các bit khác giữ nguyên

// Arduino — set nhiều bit cùng lúc bằng phép OR
DDRB |= 0b00000101;   // set bit 0 và bit 2 của thanh ghi DDRB thành OUTPUT

Datasheet vi điều khiển mô tả từng thanh ghi (register) là một chuỗi bit có ý nghĩa riêng ở từng vị trí (VD: bit 0 bật/tắt tính năng A, bit 1 bật/tắt tính năng B) — viết hằng số dạng hex hoặc binary trong code giúp đối chiếu trực tiếp với bảng bit trong datasheet, thay vì phải tự quy đổi từ số thập phân mỗi lần đọc.

Lỗi thường gặp

  • Nhầm 0x10 với số 10 thập phân — 0x10 trong hex bằng 16 thập phân, không phải 10. Luôn để ý tiền tố 0x khi đọc code người khác hoặc datasheet.
  • Tràn số (overflow) khi kiểu dữ liệu không đủ bit — uint8_t x = 255; x = x + 1; cho kết quả 0 (không phải 256), vì 8 bit chỉ chứa được tối đa 255, cộng thêm 1 sẽ "quay vòng" về 0 — lỗi kinh điển gây ra hành vi khó hiểu nếu không hiểu giới hạn bit.
  • Quên rằng phép dịch bit phụ thuộc kiểu dữ liệu có dấu hay không — dịch phải (>>) trên số có dấu (signed) có thể giữ nguyên bit dấu (arithmetic shift), khác hành vi với số không dấu (unsigned) — dễ gây lỗi logic khó phát hiện khi thao tác bit trực tiếp trên thanh ghi.
  • Viết nhầm số bit khi thao tác thanh ghi 16/32-bit — thanh ghi trên STM32 thường là 32-bit, nhưng nếu chỉ nhớ mẫu 8-bit từ Arduino có thể tính sai vị trí bit cần set.

Kết luận

Bit, byte, nhị phân và hex không phải kiến thức hàn lâm xa vời — đây là ngôn ngữ trực tiếp để đọc datasheet, cấu hình thanh ghi, và hiểu vì sao kiểu dữ liệu (uint8_t, uint16_t...) lại giới hạn giá trị như vậy. Bài tiếp theo trong chuyên mục nói về điện áp, dòng điện và công suất — nền tảng vật lý để hiểu vì sao mạch điện tử hoạt động được.

Bit, Byte, hệ nhị phân và thập lục phân: ngôn ngữ gốc của vi điều khiển — Atlasbot