Môi trường phần cứng: Raspberry Pi 4 Model B Rev 1.5 · Debian 12 Bookworm · Nhân 6.12.25
Ngày kiểm thử: Tháng 4 năm 2026
Tác giả: zeruns (https://blog.zeruns.com/)
Nội dung này do AI viết, chỉ mang tính chất tham khảo (Hermes Agent + DeepSeek V4 Flash)
Mục lục
- Khái niệm cơ bản về GPIO
- Chuẩn bị môi trường và công cụ
- GPIO Input/Output (bằng Shell)
- GPIO Input/Output (bằng Python)
- Giao tiếp I2C
- Giao tiếp SPI
- Ví dụ tổng hợp
- Lưu ý và xử lý sự cố
1. Khái niệm cơ bản về GPIO
1.1 GPIO là gì?
GPIO (General Purpose Input/Output - Ngõ vào/ra mục đích chung) là một nhóm chân có thể lập trình được trên bộ vi xử lý. Thông qua phần mềm, bạn có thể cấu hình mỗi chân thành:
- Chế độ xuất (Output): Phát mức cao (3.3V) hoặc thấp (0V), dùng để điều khiển LED, rơle, còi, v.v.
- Chế độ nhập (Input): Đọc trạng thái điện áp bên ngoài (cao/thấp), dùng để đọc tín hiệu từ nút nhấn, cảm biến
- Chức năng thay thế (Alternate function): Các giao thức truyền thông chuyên dụng như I2C, SPI, UART, PWM, v.v.
1.2 Bố trí chân của Raspberry Pi 4B
Raspberry Pi 4B sử dụng thanh chân mở rộng 40 chân, có ba cách đánh số chân:
| Cách đánh số | Mô tả | Thư viện mẫu |
|---|---|---|
| Chân vật lý (Board) | Đánh số từ 1 đến 40, bắt đầu từ góc trái trên là 1 | RPi.GPIO setmode(GPIO.BOARD) |
| Số BCM (Broadcom) | Số theo chân GPIO trên chip, ví dụ GPIO17 | RPi.GPIO setmode(GPIO.BCM), libgpiod |
| Số wiringPi | Số do wiringPi tự định nghĩa (đã lỗi thời) | Thư viện wiringPi |
Bảng chức năng 40 chân
┌─────────────────────────────┐
│ 🥝 Sơ đồ chân Raspberry Pi 4B 40 chân │
3.3V (01) ──● ●── (02) 5V │
GPIO2 (03) ──● ●── (04) 5V │
GPIO3 (05) ──● ●── (06) GND │
GPIO4 (07) ──● ●── (08) GPIO14 (UART TX) │
GND (09) ──● ●── (10) GPIO15 (UART RX) │
GPIO17 (11) ──● ●── (12) GPIO18 (PWM0) │
GPIO27 (13) ──● ●── (14) GND │
GPIO22 (15) ──● ●── (16) GPIO23 │
3.3V (17) ──● ●── (18) GPIO24 │
GPIO10 (19) ──● ●── (20) GND (SPI_MOSI) │
GPIO9 (21) ──● ●── (22) GPIO25 (SPI_MISO) │
GPIO11 (23) ──● ●── (24) GPIO8 (SPI_SCLK) │
GND (25) ──● ●── (26) GPIO7 (SPI_CE0) │
GPIO0 (27) ──● ●── (28) GPIO1 (ID_SDA / I2C) │
GPIO5 (29) ──● ●── (30) GND │
GPIO6 (31) ──● ●── (32) GPIO12 (PWM0) │
GPIO13 (33) ──● ●── (34) GND │
GPIO19 (35) ──● ●── (36) GPIO16 │
GPIO26 (37) ──● ●── (38) GPIO20 │
GND (39) ──● ●── (40) GPIO21 │
└─────────────────────────────┘
Bảng chức năng chính của Raspberry Pi 4B
| Chân vật lý | BCM | Chức năng | Chân vật lý | BCM | Chức năng |
|---|---|---|---|---|---|
| 1 | — | 3.3V | 2 | — | 5V |
| 3 | GPIO2 | I2C1 SDA | 4 | — | 5V |
| 5 | GPIO3 | I2C1 SCL | 6 | — | GND |
| 7 | GPIO4 | — | 8 | GPIO14 | UART TX |
| 9 | — | GND | 10 | GPIO15 | UART RX |
| 11 | GPIO17 | — | 12 | GPIO18 | PCM_CLK / PWM0 |
| 13 | GPIO27 | — | 14 | — | GND |
| 15 | GPIO22 | — | 16 | GPIO23 | — |
| 17 | — | 3.3V | 18 | GPIO24 | — |
| 19 | GPIO10 | SPI0 MOSI | 20 | — | GND |
| 21 | GPIO9 | SPI0 MISO | 22 | GPIO25 | — |
| 23 | GPIO11 | SPI0 SCLK | 24 | GPIO8 | SPI0 CE0 |
| 25 | — | GND | 26 | GPIO7 | SPI0 CE1 |
| 27 | GPIO0 | ID_SDA (EEPROM) | 28 | GPIO1 | ID_SCL (EEPROM) |
| 29 | GPIO5 | — | 30 | — | GND |
| 31 | GPIO6 | — | 32 | GPIO12 | PWM0 |
| 33 | GPIO13 | PWM1 | 34 | — | GND |
| 35 | GPIO19 | PCM_FS / PWM1 | 36 | GPIO16 | — |
| 37 | GPIO26 | — | 38 | GPIO20 | PCM_DIN |
| 39 | — | GND | 40 | GPIO21 | PCM_DOUT |
1.3 Lưu ý quan trọng
- Mức logic: GPIO của Raspberry Pi là mức 3.3V, không thể nối trực tiếp với tín hiệu 5V
- Giới hạn dòng điện: Mỗi chân GPIO xuất tối đa khoảng 16mA, tổng tất cả các chân không quá 50mA
- Trạng thái mặc định: Hầu hết các chân GPIO mặc định ở chế độ nhập, một số có điện trở kéo lên/kéo xuống trong
- Chân 3.3V (01/17): Dòng điện xuất tối đa khoảng 500mA
- Chân 5V (02/04): Lấy điện trực tiếp từ cổng USB-C, dòng điện phụ thuộc vào bộ nguồn
2. Chuẩn bị môi trường và công cụ
2.1 Danh sách công cụ đã cài đặt
Hướng dẫn này được kiểm thử trên hệ thống Debian 12 Bookworm, các công cụ sau đã được cài sẵn:
| Công cụ/Thư viện | Phiên bản | Mục đích |
|---|---|---|
| libgpiod / gpioset / gpioget | 1.6.3 | Điều khiển GPIO bằng Shell |
| python3-libgpiod | 1.6.3 | Liên kết Python cho libgpiod |
| RPi.GPIO | 0.7.2 | Thư viện GPIO Python cổ điển |
| smbus2 | Đã cài | Giao tiếp I2C bằng Python |
| spidev | Đã cài | Giao tiếp SPI bằng Python |
| pigpiod | 1.79 | Dịch vụ GPIO (PWM/điều khiển từ xa) |
| i2c-tools | 4.3 | Quét thiết bị I2C |
2.2 Cài đặt công cụ cần thiết
Nếu thiếu một số công cụ, có thể cài theo lệnh sau:
# Công cụ GPIO cơ bản
sudo apt install gpiod libgpiod-dev python3-libgpiod
# Công cụ I2C
sudo apt install i2c-tools
# Thư viện Python
pip install RPi.GPIO smbus2 spidev gpiozero
# Dịch vụ pigpio (PWM phần cứng, khuyến nghị)
sudo apt install pigpio pigpiod
sudo systemctl enable pigpiod --now
2.3 Bật giao diện I2C / SPI
Sử dụng raspi-config để bật:
sudo raspi-config
Đường dẫn menu:
Interface Options → I2C → Enable → Yes
Interface Options → SPI → Enable → Yes
Hoặc trực tiếp sửa tệp /boot/firmware/config.txt (Debian 12):
# Thêm thủ công
sudo tee -a /boot/firmware/config.txt <<EOF
dtparam=i2c_arm=on
dtparam=spi=on
EOF
# Khởi động lại để áp dụng
sudo reboot
Kiểm tra việc bật thành công:
# Kiểm tra I2C
ls /dev/i2c*
# Đầu ra: /dev/i2c-20 /dev/i2c-21
# Kiểm tra SPI (sau khi bật)
ls /dev/spi*
# Đầu ra: /dev/spidev0.0 /dev/spidev0.1
2.4 Xem tất cả gpiochip
$ gpioinfo
gpiochip0 - 58 lines:
line 0: "ID_SDA" unused input active-high
line 1: "ID_SCL" unused input active-high
line 2: "GPIO2" unused input active-high
...
Con chip BCM2711 trên Raspberry Pi 4B cung cấp một gpiochip0, tổng cộng 58 chân GPIO, nhưng chỉ có GPIO0-GPIO27 được đưa ra ngoài thông qua thanh chân 40Pin.
3. Nhập/xuất GPIO (bằng Shell)
3.1 Bộ công cụ libgpiod
Nên sử dụng các công cụ của libgpiod, vì nó không phụ thuộc wiringPi và là giải pháp phổ biến hiện nay.
Các lệnh chính:
| Lệnh | Chức năng | Ví dụ |
|---|---|---|
gpioinfo |
Xem trạng thái tất cả GPIO | gpioinfo |
gpioset |
Thiết lập mức điện áp ngõ ra GPIO | gpioset 0 17=1 |
gpioget |
Đọc mức điện áp ngõ vào GPIO | gpioget 0 17 |
gpiomon |
Theo dõi sự kiện GPIO | gpiomon 0 17 |
Giải thích định dạng:
gpioset <chip> <pin >= <va>chipthường là 0 (gpiochip0),pin` là số theo định danh BCM
3.2 Đầu ra: Bật đèn LED
Nối cực dương của đèn LED (chân dài) qua điện trở 330Ω đến GPIO17 (chân vật lý 11), cực âm (chân ngắn) nối đến GND (chân vật lý 9).
# Đặt mức cao cho GPIO17 → Bật LED
gpioset 0 17=1
# Đặt mức thấp cho GPIO17 → Tắt LED
gpioset 0 17=0
3.3 Đầu vào: Đọc trạng thái nút nhấn
Nối một đầu của nút nhấn vào GPIO18 (chân vật lý 12), đầu còn lại nối GND (chân vật lý 14).
# Đọc mức điện áp GPIO18 (yêu cầu kéo lên ngoài hoặc kích hoạt kéo lên nội)
gpioget 0 18
# Đầu ra: 1 (chưa nhấn, mức cao)
# Đầu ra: 0 (khi nhấn, mức thấp)
3.4 Xem trạng thái tất cả chân
# Xem tất cả các chân
gpioinfo
# Xem chân cụ thể (BCM 17)
gpioinfo | grep "GPIO17"
# Đầu ra: line 17: "GPIO17" "myapp" output active-high
3.5 Dùng gpioset thiết lập kéo lên/kéo xuống
libgpiod phiên bản 1.6 hỗ trợ thiết lập thiên vị (bias) khi yêu cầu:
# Đặt GPIO17 thành đầu ra, mức cao ban đầu, có kéo lên
gpioset --bias=enable 0 17=1
4. GPIO Đầu vào/Đầu ra (bằng Python)
4.1 Sử dụng RPi.GPIO (thư viện cổ điển)
RPi.GPIO là thư viện GPIO bằng Python được dùng phổ biến nhất, đơn giản và dễ hiểu.
4.1.1 Đầu ra: LED nhấp nháy
import RPi.GPIO as GPIO
import time
# Dùng định danh theo BCM
GPIO.setmode(GPIO.BCM)
# Đặt GPIO17 làm đầu ra
GPIO.setup(17, GPIO.OUT)
# Nhấp nháy LED 5 lần
for _ in range(5):
GPIO.output(17, GPIO.HIGH) # Bật
time.sleep(0.5)
GPIO.output(17, GPIO.LOW) # Tắt
time.sleep(0.5)
# Dọn dẹp tài nguyên
GPIO.cleanup()
4.1.2 Đầu vào: Đọc nút nhấn
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
# GPIO18 làm đầu vào, bật điện trở kéo lên nội
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
try:
while True:
if GPIO.input(18) == GPIO.LOW:
print("Nút được nhấn")
else:
print("Nút được nhả")
time.sleep(0.1)
except KeyboardInterrupt:
GPIO.cleanup()
GPIO.PUD_UP= kéo lên nội (mặc định mức cao, nhấn xuống mức thấp)
GPIO.PUD_DOWN= kéo xuống nội (mặc định mức thấp, nhấn lên mức cao)
4.1.3 Đầu ra PWM: Đèn kiểu thở
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT) # GPIO18 hỗ trợ PWM phần cứng
pwm = GPIO.PWM(18, 1000) # Tần số 1kHz
pwm.start(0) # Chu kỳ hoạt động 0% ban đầu
try:
while True:
# Dần sáng lên
for duty in range(0, 101, 5):
pwm.ChangeDutyCycle(duty)
time.sleep(0.05)
# Dần mờ đi
for duty in range(100, -1, -5):
pwm.ChangeDutyCycle(duty)
time.sleep(0.05)
except KeyboardInterrupt:
pwm.stop()
GPIO.cleanup()
4.1.4 Phát hiện sự kiện: Đọc nút bằng ngắt
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def button_callback(channel):
print(f"Nút được nhấn! (chân {channel})")
# Kích hoạt ngắt theo cạnh xuống
GPIO.add_event_detect(18, GPIO.FALLING,
callback=button_callback,
bouncetime=200)
try:
print("Đang chờ nhấn nút...")
time.sleep(60)
except KeyboardInterrupt:
GPIO.cleanup()
4.2 Sử dụng gpiozero (tầng bao cao cấp)
gpiozero cung cấp lớp bao hướng đối tượng cao cấp, phù hợp để phát triển nhanh:
# Cài đặt
pip install gpiozero
from gpiozero import LED, Button, Buzzer
from signal import pause
# Đèn LED: Tự động nhấp nháy
led = LED(17)
led.blink()
# Nút nhấn: Khi nhấn, bật đèn LED
btn = Button(18, pull_up=True)
# Cách 1: Gán trực tiếp
btn.when_pressed = led.on
btn.when_released = led.off
# Cách 2: Hàm gọi lại tùy chỉnh
btn.when_pressed = lambda: print("Nhấn rồi!")
# Giữ chương trình chạy
pause()
gpiozero có các thành phần tích hợp:
| Thành phần | Tên lớp | Mô tả |
|---|---|---|
| LED | LED(pin) |
Điều khiển bật/tắt, nhấp nháy, hiệu ứng thở |
| Nút | Button(pin, pull_up=True) |
Đọc trạng thái nút, phát hiện nhấn giữ |
| Buzzer | Buzzer(pin) |
Điều khiển còi tích cực |
| Cảm biến khoảng cách | DistanceSensor(echo, trigger) |
Cảm biến siêu âm HC-SR04 |
| Động cơ servo | Servo(pin) |
Điều khiển góc quay servo |
| Động cơ | Motor(forward, backward) |
Điều khiển động cơ một chiều |
4.3 Sử dụng liên kết Python của libgpiod (thao tác cấp thấp)
import gpiod
import time
# Mở gpiochip0
chip = gpiod.Chip('gpiochip0')
# Lấy dòng GPIO17 và yêu cầu làm đầu ra
line = chip.get_line(17)
line.request(consumer='myapp', type=gpiod.LINE_REQ_DIR_OUT)
# Đặt mức cao
line.set_value(1)
time.sleep(1)
# Đặt mức thấp
line.set_value(0)
# Giải phóng
line.release()
Chế độ đầu vào:
import gpiod
chip = gpiod.Chip('gpiochip0')
line = chip.get_line(18)
# Yêu cầu làm đầu vào, kéo lên nội
line.request(consumer='myapp',
type=gpiod.LINE_REQ_DIR_IN,
flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP)
value = line.get_value() # 0 hoặc 1
print(f"GPIO18 = {value}")
line.release()
5. Giao tiếp I2C
5.1 Nguyên lý cơ bản I2C
I2C (Inter-Integrated Circuit) là một giao diện nối tiếp sử dụng hai đường dây:
| Tín hiệu | Chân | Chức năng |
|---|---|---|
| SCL | GPIO3 (chân vật lý 5) | Đường đồng hồ |
| SDA | GPIO2 (chân vật lý 3) | Đường dữ liệu |
Đặc điểm:
- Cấu trúc chủ-tớ: Raspberry Pi là thiết bị chủ (Master), cảm biến là thiết bị tớ (Slave)
- Địa chỉ 7 bit: Mỗi thiết bị tớ có địa chỉ duy nhất (ví dụ: 0x44)
- Chia sẻ bus: Nhiều thiết bị có thể nối trên một bus I2C
- Tốc độ: 100kHz (chuẩn), 400kHz (nhanh)
5.2 Xem bus I2C
# Xem danh sách bus I2C
$ i2cdetect -l
i2c-20 i2c bcm2711_i2c1 I2C adapter
i2c-21 i2c bcm2711_i2c0 I2C adapter
i2c-20: Ứng với chân 3(SDA)/5(SCL) — bus I2C chínhi2c-21: Ứng với ID_EEPROM (chân 27/28), thông thường không dùng cho thiết bị ngoài
5.3 Quét các thiết bị I2C
# Quét thiết bị trên i2c-20
$ i2cdetect -y 20
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 44 -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Có thiết bị tại địa chỉ
0x44— đó chính là cảm biến nhiệt độ/độ ẩm SHT30 (địa chỉ I2C 0x44).
Nếu không thấy thiết bị nào (toàn hiển thị --), nghĩa là bus chưa nối thiết bị.
5.4 Đọc/ghi I2C bằng smbus2
smbus2 là thư viện I2C bằng Python phổ biến nhất.
5.4.1 Đọc cảm biến nhiệt độ/độ ẩm SHT30
import smbus2
import time
# Địa chỉ I2C của SHT30
SHT30_ADDR = 0x44
# Mở bus I2C 20
bus = smbus2.SMBus(20)
# Gửi lệnh đo: chế độ độ chính xác cao
bus.write_i2c_block_data(SHT30_ADDR, 0x2C, [0x06])
time.sleep(0.015) # Chờ phép đo hoàn tất (15ms)
# Đọc 6 byte dữ liệu
data = bus.read_i2c_block_data(SHT30_ADDR, 0x00, 6)
# Tính nhiệt độ và độ ẩm
temp_raw = ((data[0] << 8) | data[1])
temp = -45 + 175 * temp_raw / 65535.0
hum_raw = ((data[3] << 8) | data[4])
hum = 100 * hum_raw / 65535.0
print(f"Nhiệt độ: {temp:.2f}°C")
print(f"Độ ẩm: {hum:.2f}%")
5.4.2 Đóng gói thành hàm
import smbus2
import time
class SHT30:
def __init__(self, bus_id=20, addr=0x44):
self.bus = smbus2.SMBus(bus_id)
self.addr = addr
def read(self):
"""Đọc nhiệt độ, độ ẩm, trả về (nhiệt độ °C, độ ẩm %)"""
self.bus.write_i2c_block_data(self.addr, 0x2C, [0x06])
time.sleep(0.015)
data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
temp_raw = (data[0] << 8) | data[1]
temp = -45 + 175 * temp_raw / 65535.0
hum_raw = (data[3] << 8) | data[4]
hum = 100 * hum_raw / 65535.0
return round(temp, 2), round(hum, 2)
# Sử dụng
sensor = SHT30()
t, h = sensor.read()
print(f"{t}°C, {h}%RH")
5.4.3 Các thao tác đọc/ghi I2C tổng quát
import smbus2
bus = smbus2.SMBus(20)
addr = 0x44 # Địa chỉ I2C thiết bị
# Ghi một byte vào thanh ghi
bus.write_byte_data(addr, 0x2C, 0x06)
# Đọc một byte
value = bus.read_byte_data(addr, 0x00)
# Đọc nhiều byte
data = bus.read_i2c_block_data(addr, 0x00, 6)
# Ghi nhiều byte
bus.write_i2c_block_data(addr, 0x2C, [0x06])
# Ghi một byte (không có địa chỉ thanh ghi)
bus.write_byte(addr, 0x06)
# Đọc một byte (không có địa chỉ thanh ghi)
value = bus.read_byte(addr)
bus.close()
5.5 Dùng lệnh i2cget / i2cset từ dòng lệnh
# Đọc thanh ghi trạng thái của SHT30 (1 byte)
$ i2cget -y 20 0x44 0x00
0x65
# Đọc 6 byte dữ liệu gốc
$ i2cget -y 20 0x44 0x00 b 6
6. Giao tiếp SPI
6.1 Nguyên lý cơ bản SPI
SPI (Serial Peripheral Interface) là một bus nối tiếp đồng bộ song công, dùng bốn dây:
| Tín hiệu | Chân Raspberry Pi | Chức năng |
|---|---|---|
| MOSI | GPIO10 (chân vật lý 19) | Chủ xuất, tớ nhận |
| MISO | GPIO9 (chân vật lý 21) | Chủ nhận, tớ xuất |
| SCLK | GPIO11 (chân vật lý 23) | Tín hiệu đồng hồ |
| CE0 | GPIO8 (chân vật lý 24) | Chọn thiết bị 0 |
| CE1 | GPIO7 (chân vật lý 26) | Chọn thiết bị 1 |
Đặc điểm:
- Song công: Đồng thời gửi và nhận
- Chủ-tớ: Raspberry Pi làm chủ, có thể nối nhiều tớ (mỗi tớ một chân chọn)
- Tốc độ cao: Có thể đạt hàng chục MHz
- Không có địa chỉ thiết bị: Chọn thiết bị qua chân CE (Chip Enable)
6.2 Bật SPI và kiểm tra
# Sau khi đã bật SPI
$ ls /dev/spi*
/dev/spidev0.0 /dev/spidev0.1
/dev/spidev0.0: bus SPI0, CE0/dev/spidev0.1: bus SPI0, CE1
6.3 Đọc/ghi SPI bằng spidev
pip install spidev
6.3.1 Đọc ADC MCP3008 (ADC 8 kênh)
import spidev
# Mở SPI
spi = spidev.Spi
#### 6.3.2 Truyền và nhận dữ liệu SPI phổ dụng
```python
import spidev
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 500000
spi.mode = 0
# Gửi và nhận (song công, gửi 0xFF đồng thời đọc)
data_out = [0xFF, 0x00, 0x55]
data_in = spi.xfer2(data_out) # Gửi 3 byte, nhận 3 byte
print(f"Gửi: {[hex(x) for x in data_out]}")
print(f"Nhận: {[hex(x) for x in data_in]}")
# Chỉ gửi (bỏ qua kết quả nhận)
spi.writebytes([0x01, 0x02, 0x03])
spi.close()
7. Ví dụ tổng hợp
7.1 Ví dụ 1: Đèn LED hiệu ứng thở + điều khiển bằng nút bấm
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
LED_PIN = 17 # BCM 17
BTN_PIN = 18 # BCM 18
GPIO.setup(LED_PIN, GPIO.OUT)
GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
pwm = GPIO.PWM(LED_PIN, 500)
pwm.start(0)
running = True # Trạng thái LED
try:
while True:
if GPIO.input(BTN_PIN) == GPIO.LOW:
running = not running
time.sleep(0.3) # Khử nhiễu
if running:
# Hiệu ứng thở
for duty in range(0, 101, 2):
pwm.ChangeDutyCycle(duty)
time.sleep(0.01)
for duty in range(100, -1, -2):
pwm.ChangeDutyCycle(duty)
time.sleep(0.01)
else:
pwm.ChangeDutyCycle(0)
except KeyboardInterrupt:
pwm.stop()
GPIO.cleanup()
7.2 Ví dụ 2: Đọc cảm biến nhiệt độ và độ ẩm SHT30 + ghi dữ liệu tự động
import smbus2
import time
import csv
from datetime import datetime
class SHT30:
def __init__(self, bus_id=20, addr=0x44):
self.bus = smbus2.SMBus(bus_id)
self.addr = addr
def read(self):
self.bus.write_i2c_block_data(self.addr, 0x2C, [0x06])
time.sleep(0.015)
data = self.bus.read_i2c_block_data(self.addr, 0x00, 6)
t = -45 + 175 * ((data[0] \u003c\u003c 8) | data[1]) / 65535.0
h = 100 * ((data[3] \u003c\u003c 8) | data[4]) / 65535.0
return round(t, 2), round(h, 2)
sensor = SHT30()
# Kiểm tra đọc dữ liệu
t, h = sensor.read()
print(f"[{datetime.now()}] Nhiệt độ: {t}°C, Độ ẩm: {h}%")
# Ghi dữ liệu liên tục vào CSV (mỗi phút một lần)
with open('sensor_log.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Thời gian', 'Nhiệt độ (C)', 'Độ ẩm (%)'])
for i in range(10): # Ghi 10 lần
t, h = sensor.read()
now = datetime.now().strftime('%H:%M:%S')
writer.writerow([now, t, h])
print(f"[{now}] {t}°C, {h}%")
time.sleep(60)
7.3 Ví dụ 3: gpiozero - một dòng hoàn tất
from gpiozero import LED, Button
from signal import pause
import time
led = LED(17)
btn = Button(18, pull_up=True)
# Ấn xuống bật, nhả ra tắt
btn.when_pressed = led.on
btn.when_released = led.off
# Nhấn kép chuyển sang chế độ nhấp nháy
press_count = 0
def double_click():
global press_count
press_count += 1
time.sleep(0.3)
if press_count == 2:
led.blink()
print("Nhấn kép! LED chuyển sang chế độ nhấp nháy")
press_count = 0
btn.when_pressed = double_click
pause()
8. Lưu ý và xử lý sự cố
8.1 Chuyển đổi mức điện áp (3.3V so với 5V)
\u003e
GPIO Raspberry Pi sử dụng mức logic 3.3V, cấm kết nối trực tiếp với thiết bị 5V!
| Tình huống | Cách xử lý |
|---|---|
| Ngõ ra cảm biến 5V → Ngõ vào Raspberry Pi | Sử dụng mạch chuyển đổi mức (ví dụ: TXS0108E) hoặc phân áp bằng điện trở |
| Ngõ ra Raspberry Pi → Ngõ vào thiết bị 5V | Một số thiết bị 5V có thể nhận 3.3V là mức cao (ví dụ: WS2812B) |
| Cảm biến 3.3V → Raspberry Pi | Có thể kết nối trực tiếp |
Phân áp (5V → 3.3V):
5V ──┬── R1 (1.8kΩ) ──┬── GPIO Raspberry Pi
│ │
GND ── R2 (3.3kΩ) ─┘
8.2 Điện trở kéo lên/kéo xuống
- Kéo lên nội bộ: Dùng
pull_up_down=GPIO.PUD_UPtrong RPi.GPIO - Kéo xuống nội bộ: Dùng
pull_up_down=GPIO.PUD_DOWNtrong RPi.GPIO - libgpiod:
gpiod.LINE_REQ_FLAG_BIAS_PULL_UP - Kéo lên ngoài: Dùng điện trở 4.7kΩ ~ 10kΩ nối với 3.3V
8.3 Giới hạn dòng điện
| Thông số | Giới hạn |
|---|---|
| Dòng tối đa mỗi chân GPIO | 16mA |
| Tổng dòng tất cả các chân GPIO | 50mA |
| Tổng dòng các chân 3.3V | ~500mA |
| Tổng dòng các chân 5V | Tùy thuộc nguồn cấp |
\u003e
Khi cấp trực tiếp cho LED phải nối tiếp điện trở giới hạn dòng 220Ω ~ 330Ω!
8.4 Các lỗi thường gặp
| Lỗi | Nguyên nhân | Cách khắc phục |
|---|---|---|
RuntimeError: No access to /dev/mem |
Thiếu quyền | sudo python3 script.py |
RuntimeError: Pin already in use |
Chân đã bị tiến trình khác chiếm dụng | Kiểm tra /sys/class/gpio/, giải phóng hoặc khởi động lại |
[Errno 13] Permission denied + i2c |
Chưa bật I2C hoặc thiếu quyền | Thêm người dùng vào nhóm i2c |
Could not open /dev/spidev0.0 |
Chưa bật SPI | Kiểm tra /boot/config.txt |
| LED không sáng | Đấu ngược cực | Chân dài nối GPIO, chân ngắn nối GND |
| Nút bấm kích hoạt sai | Chưa khử nhiễu | Thêm bouncetime trong code hoặc dùng độ trễ khử rung |
8.5 Thiết lập quyền
# Thêm người dùng hiện tại vào nhóm i2c, gpio
sudo usermod -aG i2c,gpio $USER
# Đăng nhập lại để có hiệu lực
exec su - $USER
8.6 Tài liệu tham khảo trực tuyến
\u003e Tác giả: zeruns
\u003e Hướng dẫn này được kiểm tra và viết dựa trên Raspberry Pi 4B + Debian 12 Bookworm
