Raspberry Pi 4B / Zero / Zero 2 W / 3B+ 均采用 40-pin GPIO Header,引脚布局完全兼容。 引脚编号规则:USB 口朝下时, Pin 1 在左上角(方焊盘),按书页顺序编号。
来源核对:Raspberry Pi 官方文档 raspberrypi.com、 pinout.xyz、 Wevolver Pi 4 Pinout Guide、 Electronics for You GPIO 指南
协议信号BCM GPIO物理引脚关键说明I2C-1SDAGPIO2Pin 3默认 I2C 总线;板载 1.8kΩ 固定上拉 到 3.3VI2C-1SCLGPIO3Pin 5同上;同时是 唤醒引脚(拉低可唤醒关机状态的 Pi)SPI0MOSIGPIO10Pin 19主出从入SPI0MISOGPIO9Pin 21主入从出SPI0SCLKGPIO11Pin 23时钟SPI0CE0GPIO8Pin 24片选 0SPI0CE1GPIO7Pin 26片选 1UART0TXDGPIO14Pin 8默认串口控制台;如需他用需先禁用 consoleUART0RXDGPIO15Pin 10默认串口控制台I2C-0ID_SDGPIO0Pin 27保留给 HAT ID EEPROM, 不要占用****I2C-0ID_SCGPIO1Pin 28保留给 HAT ID EEPROM, 不要占用
物理引脚功能物理引脚功能13.3V25V45V6GND7GPIO49GND11GPIO1712GPIO18 (PWM0)13GPIO2714GND15GPIO2216GPIO23173.3V18GPIO2420GND22GPIO2525GND26GPIO7 (CE1)29GPIO530GND31GPIO632GPIO12 (PWM0)33GPIO13 (PWM1)34GND35GPIO19 (PCM_FS / SPI1)36GPIO16 (SPI1 CE2)37GPIO2638GPIO20 (PCM_DIN / SPI1)39GND40GPIO21 (PCM_DOUT / SPI1)
3.3V 逻辑电平:所有 GPIO 通信引脚(SDA/SCL/MOSI/MISO/TX/RX)均为 3.3V, 不兼容 5V。直接接 5V 会烧毁 GPIO。
GPIO2/GPIO3 固定上拉:这两个引脚有 ~1.8kΩ 板载上拉电阻 到 3.3V,因此:
3.3V 电源限制:总输出电流约 800mA,5V 引脚直通 USB 供电。
Zero 系列:Zero / Zero W / Zero 2 W 出厂 不焊排针,需自行焊接或使用带排针版本(WH)。
使用硬件 I2C 控制器,驱动由 Linux 内核管理。
bash
# 启用
sudo raspi-config # -> Interface Options -> I2C -> Enable
# 或修改 /boot/config.txt 添加:
# dtparam=i2c_arm=on
优点缺点稳定可靠,400kHz 标准速率只能使用固定的 GPIO2/GPIO3(pin3/5)内核管理时钟拉伸、仲裁无法启用内部上拉(树莓派已带物理上拉,通常够用)Python smbus2 / C ioctl 直接可用如需其他 GPIO 做 I2C,必须用软件方案性能瓶颈在 Linux I2C 子系统(~1.3ms/事务)不是 C 语言 vs Python 的差距
通过 GPIO 手动翻转电平模拟 I2C 时序,可使用任意 GPIO。
适用场景:
两种实现方式:
方式实现内部上拉支持性能推荐度内核 i2c-gpio overlaydtoverlay=i2c-gpio不支持(重大陷阱)一般不推荐用户态 gpiod 库Python gpiod / C libgpiod支持 GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP~50kHz (10us 延时)推荐
内核 overlay 陷阱: i2c-gpio 设备树叠加层无法启用 SoC 内部上拉电阻。如果外接传感器没有外部上拉,I2C 总线会处于浮空状态,导致设备扫描不到、通信失败。 必须用 gpiod 库在用户态实现。
以 BMI270 读取 12 字节(accel + gyro)为例:
方案单次读取耗时理论最高频率备注Python + smbus2 (标准 I2C)2-3ms400Hz内核 I2C 开销C + ioctl (标准 I2C)1.3ms700Hz与 Python 同数量级Python + gpiod bit-banging3-4ms250Hz10us/bit 延时C + gpiod bit-banging3-4ms250Hz10us/bit 延时
关键结论:
bash
# Python
pip3 install gpiod
# C
sudo apt install libgpiod-dev
用 gpiod 实现一个与 smbus2.SMBus 接口兼容的类,可 monkey-patch 替换原驱动中的 SMBus:
python
import gpiod
from gpiod.line import Direction, Value, Bias
class SMBus:
"""gpiod-based bit-banging I2C compatible with smbus2.SMBus"""
def __init__(self, bus=None):
self.chip = gpiod.Chip('/dev/gpiochip0')
self.scl_pin = 9 # GPIO9 (物理 pin21)
self.sda_pin = 8 # GPIO8 (物理 pin24)
self.addr = 0
def _delay(self):
import time
time.sleep(0.00001) # 10us = ~50kHz
def _start(self):
# SCL=1 时 SDA 从高变低
...
def _stop(self):
# SCL=1 时 SDA 从低变高
...
def write_byte_data(self, addr, reg, value):
self.addr = addr
self._start()
self._write_raw(addr << 1) # ADDR + W
self._write_raw(reg)
self._write_raw(value)
self._stop()
def read_i2c_block_data(self, addr, reg, length):
self.addr = addr
self._start()
self._write_raw(addr << 1) # ADDR + W
self._write_raw(reg)
self._start() # Repeated START
self._write_raw((addr << 1) | 1) # ADDR + R
data = [self._read_raw(ack=1) for _ in range(length-1)]
data.append(self._read_raw(ack=0)) # 最后字节发 NACK
self._stop()
return data
def write_i2c_block_data(self, addr, reg, data):
self.addr = addr
self._start()
self._write_raw(addr << 1)
self._write_raw(reg)
for b in data:
self._write_raw(b)
self._stop()
关键技巧:
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UPGPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UPc
#include <gpiod.h>
#define SDA_GPIO 8
#define SCL_GPIO 9
struct gpiod_chip *chip = gpiod_chip_open("/dev/gpiochip0");
struct gpiod_line *scl = gpiod_chip_get_line(chip, SCL_GPIO);
// SCL 固定为输出(带上拉)
gpiod_line_request_output_flags(scl, "i2c_driver",
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP, 1);
// SDA 读时切为输入(带上拉)
gpiod_line_request_input_flags(sda_in, "i2c_driver",
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
// SDA 写时切为输出(带上拉)
gpiod_line_request_output_flags(sda_out, "i2c_driver",
GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP, 1);
参数值I2C 地址0x68 (SDO 接 GND) / 0x69 (SDO 接 VCC)CHIP_ID0x24供电1.8V ~ 3.6V,树莓派 3.3V 可直接驱动量程Accel: ±2/4/8/16g;Gyro: ±125/250/500/1000/2000 dps
BMI270 每次上电或软复位后必须加载 8KB config 文件,否则无法工作。
text
1. 读 CHIP_ID (0x00) -> 应返回 0x24
2. 写 PWR_CONF (0x7C) = 0x00 # 禁用高级省电模式
3. 延时 1ms
4. 写 INIT_CTRL (0x59) = 0x00 # 准备加载 config
5. 延时 1ms
6. 【循环 256 次】每次写 32 字节到 INIT_DATA (0x5E)
- 写 INIT_ADDR_0 (0x5B) = 0x00
- 写 INIT_ADDR_1 (0x5C) = page (0~255)
- burst write: 0x5E + 32 bytes config data
- 每页延时 30us
7. 写 INIT_CTRL (0x59) = 0x01 # 完成加载
8. 延时 30ms
9. 轮询 INTERNAL_STATUS (0x21) 直到 == 0x01(最多等 2 秒)
10. 写 PWR_CTRL (0x7D) = 0x07 # 启用 Accel + Gyro + Temp
11. 配置 ODR 和量程
.bin, 8192 字节,由 Bosch 官方提供write(i2c_fd, buf, 33),buf[0]=0x5E, buf[1..32]=config data寄存器布局(从 0x0C 开始连续读取 12 字节):
字节内容说明0-1Accel Xlittle-endian int162-3Accel Ylittle-endian int164-5Accel Zlittle-endian int166-7Gyro Xlittle-endian int168-9Gyro Ylittle-endian int1610-11Gyro Zlittle-endian int16
转换公式:
c
// Accel (量程 ±16g, REG_ACC_RANGE=0x03)
float scale = 16.0f / 32768.0f; // ≈ 0.000488 g/LSB
// Gyro (量程 ±2000 dps, REG_GYR_RANGE=0x00)
float scale = 2000.0f / 32768.0f; // ≈ 0.061 dps/LSB
// 温度 (寄存器 0x22, 2字节)
// 实际驱动中常用简化公式:23.0 + (raw * 0.5f / 512.0f)
寄存器地址常用值含义ACC_CONF0x400x0AODR=200Hz, BWP=normalACC_RANGE0x410x03±16gGYR_CONF0x420x0AODR=200HzGYR_RANGE0x430x00±2000 dps
参数值I2C 地址0x48 ~ 0x4B(由 A0/A1/A2 引脚接地/接 VCC 决定)默认地址0x48(三个地址引脚全部接地)精度±0.1°C(典型值)分辨率0.00390625°C(16-bit)
python
import smbus
bus = smbus.SMBus(1)
data = bus.read_i2c_block_data(0x48, 0x00, 2) # 从寄存器 0x00 读 2 字节
raw = (data[0] << 8) | data[1]
if raw > 0x7FFF:
raw -= 0x10000
temp_c = raw / 256.0
C 语言版本:
c
int fd = open("/dev/i2c-1", O_RDWR);
ioctl(fd, I2C_SLAVE, 0x48);
uint8_t reg = 0x00;
write(fd, ®, 1);
uint8_t data[2];
read(fd, data, 2);
int16_t raw = (data[0] << 8) | data[1];
if (raw > 0x7FFF) raw -= 0x10000;
float temp = raw / 256.0f;
寄存器地址说明Temperature0x00温度值(只读,2 字节)Configuration0x01配置寄存器THYST0x02温度下限阈值TOS0x03温度上限阈值(中断用)
检查项方法I2C 是否启用?ls /dev/i2c-* 应有 /dev/i2c-1设备是否上电?万用表量 VCC/GND 之间是否有 3.3V地址是否正确?sudo i2cdetect -y 1 扫描总线接线是否松动?SDA/SCL 是否反接?上拉电阻?GPIO2/GPIO3 有板载上拉,其他 GPIO 需外接上拉或 gpiod 内部上拉设备是否被占用?lsof /dev/i2c-1 检查是否有其他进程占用
现象原因解决CHIP_ID = 0x00I2C 通信失败检查接线、上拉、地址Config timeout8KB config 未正确写入确保 burst write,检查每页延时Write failed at page NI2C 写入中断检查总线稳定性,减小每块大小INTERNAL_STATUS != 0x01Config 文件错误使用 Bosch 官方提供的 config bin 文件软复位后必须重新加载 config这是设计特性每次 POR/软复位后都要走完整加载流程
现象原因解决设备偶尔 ACK 失败SDA 切换输出/输入时序不对确保 SDA 在 SCL 低电平时切换完全无响应缺少上拉电阻gpiod 必须带 BIAS_PULL_UP 标志"Device or resource busy"GPIO 被其他进程占用sudo killall python3 或释放 gpiod line速度太慢usleep 粒度大树莓派 Linux 下 usleep(10) 实际约 60-100us
现象原因解决No module named 'bmi270'sudo 使用 root 的 Python path不用 sudo 运行 Flask,或设置 PYTHONPATHgpiod 权限不足用户不在 gpio 组sudo usermod -aG gpio ubuntuSMBus monkey-patch 不生效导入顺序问题必须先 patch sys.modules 再 import BMI270
本项目实际接线(BMI270 + MAX30205 共存):
传感器信号BCM GPIO物理引脚总线类型MAX30205SDAGPIO2Pin 3标准 I2C-1MAX30205SCLGPIO3Pin 5标准 I2C-1BMI270SDAGPIO8Pin 24软件 I2C (gpiod)BMI270SCLGPIO9Pin 21软件 I2C (gpiod)共地GND—Pin 6/9/14/20/25/30/34/39—供电3.3V—Pin 1/17—
注意:GPIO8/9 同时也是 SPI0 的 CE0/MISO。如果同时使用 SPI 设备,需避免冲突。
read_i2c_block_data(0x0C, 12) 比 6 次单字节读取快 5 倍以上