40Pin 扩展
Quectel Pi H1 单板电脑提供了标准的 40PIN GPIO 扩展接口,支持 GPIO、I2C、SPI、UART、PWM 等多种外设接口。下面将介绍如何测试这些接口功能。
引脚定义


接口配置说明
40PIN 中部分低速接口(i2c9/spi10/uart12_2w/i2c13/spi14)的配置文件位于 /etc/quecpi_config/quecpi_config.ini。
配置步骤:
- 修改
quecpi_config.ini配置文件 - 应用配置
quecpi_config 40pin set
- 重启系统使配置生效
GPIO 测试
本节以测试 40PIN 的 pin3 引脚为例,演示如何使用 GPIO 功能。pin3 对应的 gpio_num 为 36。
硬件连接
连接方法一:万用表测量电压
将 pin3(GPIO_36)引脚接万用表正极,pin6(GND)引脚接万用表负极。通过万用表可以测量引脚输出的电压值,验证 GPIO 功能是否正常。
连接方法二:使用 GPIO 扩展板指示灯
也可以通过接插 树莓派4B/3B GPIO 扩展板 到 40-Pin 扩展接口来测试 GPIO 高低电平。该扩展板将各 GPIO 引脚引出并配有对应的指示灯,可将待测 GPIO 引脚配置为输出并接到扩展板上,当 GPIO 输出高电平时小灯点亮,输出低电平时小灯熄灭,从而直观观察 GPIO 电平变化是否符合预期。
关于该扩展板的更多信息,可参考配件章节中的 GPIO terminal expansion board 条目(参见:支持配件)。
方法一:使用 SHELL 命令控制
系统已默认启用 lgpiod 服务,依次执行如下命令:
执行 step1: 打开 GPIO 设备
rgs c 999 go 4
使用 go 命令打开文件 /dev/gpiochip4
执行 step2: 设置 GPIO 模式
rgs c 999 gso 0 36
使用 gso 命令设置 gpio 36 为输出模式,这条命令里的 0 是上一条命令的返回内容,根据实际情况修改。
执行 step3: 设置低电平
rgs c 999 gw 0 36 0
设置 gpio 36 为低电平,此时测试引脚电压值为 0V。
执行 step4: 设置高电平
rgs c 999 gw 0 36 1
设置 gpio 36 为高电平,此时测试引脚电压值为 3.3V。
方法二:使用 Python/C 代码控制
如需使用编程语言进行 GPIO 控制,请参考以下文档:
- Python GPIO 开发 - 使用 python-periphery 库进行 GPIO 开发
- C/C++ GPIO 开发 - 使用 lgpio 库进行 GPIO 开发
这些文档提供了完整的代码示例、编译方法和测试步骤。
I2C 测试
40PIN 的 pin3 和 pin5 默认是 I2C 的 data 和 clock 引脚,对应设备节点 /dev/i2c9。
若设备节点没有出现,可以先使用本章开头部分的 quecpi_config 进行配置使能。
测试准备
本次测试使用微雪环境传感器扩展板(BME280),通过 40PIN 接口进行连接。
硬件连接示意图:
微雪环境传感器扩展板
QuecPi 40PIN引脚
插上环境传感器扩展板的Quectel Pi H1
使用 C 代码控制
如果系统中没有 lgpio 库,请参考 C/C++ GPIO 开发 文档安装 lgpio 库。
执行 step1: 创建源文件
新建 envtest.c 文件,内容如下:
点击展开/折叠完整代码
#include <stdio.h>
#include <lgpio.h>
#define I2C_DEV_NUM 9
#define BME280_ADDR 0x76
int32_t digT[3],digP[9],digH[6];
int32_t t_fine = 0.0;
double compensate_P(int32_t adc_P)
{
double pressure = 0.0;
double v1,v2;
v1 = (t_fine / 2.0) - 64000.0;
v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5];
v2 = v2 + ((v1 * digP[4]) * 2.0);
v2 = (v2 / 4.0) + (digP[3] * 65536.0);
v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8) + ((digP[1] * v1) / 2.0)) / 262144;
v1 = ((32768 + v1) * digP[0]) / 32768;
if(v1 == 0)
return 0;
pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125;
if (pressure < 0x80000000)
pressure = (pressure * 2.0) / v1;
else
pressure = (pressure / v1) * 2;
v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096;
v2 = ((pressure / 4.0) * digP[7]) / 8192.0;
pressure = pressure + ((v1 + v2 + digP[6]) / 16.0) ;
return (pressure/100);
}
double compensate_T(int32_t adc_T)
{
double temperature = 0.0;
double v1,v2;
v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1];
v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2];
t_fine = v1 + v2;
temperature = t_fine / 5120.0;
return temperature;
}
double compensate_H(int32_t adc_H)
{
double var_h = t_fine - 76800.0;
if (var_h == 0)
return 0;
var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) *
(digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h)));
var_h = var_h * (1.0 - digH[0] * var_h / 524288.0);
if (var_h > 100.0)
var_h = 100.0;
else if (var_h < 0.0)
var_h = 0.0;
return var_h;
}
void get_calib_param(int handle)
{
uint8_t calib[32];
for(int i=0;i<24;i++) {
calib[i] = lgI2cReadByteData(handle, 0x88 + i);
}
calib[24] = lgI2cReadByteData(handle, 0xA1);
for(int i=25,o=0;i<32;i++,o++) {
calib[i] = lgI2cReadByteData(handle, 0xE1 + o);
}
digT[0] = (calib[1] << 8) | calib[0];
digT[1] = (calib[3] << 8) | calib[2];
digT[2] = (calib[5] << 8) | calib[4];
digP[0] = (calib[7] << 8) | calib[6];
digP[1] = (calib[9] << 8) | calib[8];
digP[2] = (calib[11] << 8) | calib[10];
digP[3] = (calib[13] << 8) | calib[12];
digP[4] = (calib[15] << 8) | calib[14];
digP[5] = (calib[17] << 8) | calib[16];
digP[6] = (calib[19] << 8) | calib[18];
digP[7] = (calib[21] << 8) | calib[20];
digP[8] = (calib[23] << 8) | calib[22];
digH[0] = calib[24];
digH[1] = (calib[26] << 8) | calib[25];
digH[2] = calib[27];
digH[3] = (calib[28] << 4) | (0x0f & calib[29]);
digH[4] = (calib[30] << 4) | ((calib[29] >> 4) & 0x0f);
digH[5] = calib[31];
for(int i=1;i<2;i++)
if((digT[i] & 0x8000) != 0) digT[i] = (-digT[i] ^ 0xFFFF) + 1;
for(int i=1;i<8;i++)
if ((digP[i] & 0x8000) != 0) digP[i]=(-digP[i] ^ 0xFFFF) + 1 ;
for(int i=0;i<6;i++)
if ((digH[i] & 0x8000) != 0) digH[i] = (-digH[i] ^ 0xFFFF) + 1;
}
int main(int argc, char **argv)
{
uint8_t data[8];
double value[3];
int handle = lgI2cOpen(I2C_DEV_NUM, BME280_ADDR, 0);
lgI2cWriteByteData(handle, 0xF2, 0x01);
lgI2cWriteByteData(handle, 0xF4, 0x27);
lgI2cWriteByteData(handle, 0xF5, 0xA0);
get_calib_param(handle);
for(int i=0;i<8;i++) {
data[i] = lgI2cReadByteData(handle, 0xF7 + i);
}
value[0] = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
value[1] = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
value[2] = (data[6] << 8) | data[7];
value[0] = compensate_P(value[0]);
value[1] = compensate_T(value[1]);
value[2] = compensate_H(value[2]);
printf("pressure: %7.2f hPa\n", value[0]);
printf("temperature: %7.2f C\n" , value[1]);
printf("humidity: %7.2f %\n" , value[2]);
lgI2cClose(handle);
}
执行 step2: 编译程序
gcc -o envtest envtest.c -llgpio
执行 step3: 运行测试
sudo ./envtest
如果出现 "Permission denied" 错误,需要使用
sudo 运行程序。
运行成功后,输出内容包含采集的气压、温度和湿度信息:
pressure: 1013.25 hPa
temperature: 25.36 C
humidity: 45.67 %
SPI 测试
40PIN 中的 SPI 功能对应设备节点 /dev/spi10。
若设备节点没有出现,可以先使用本章开头部分的 quecpi_config 进行配置使能。
SPI 回环测试
本测试通过 SHELL 命令控制 SPI 接口,验证数据收发功能。
执行 step1: 打开 SPI 设备
rgs c 999 SPIO 10 0 10000000 0
使用 SPIO 命令打开 /dev/spidev10.0,波特率为 10000000。命令返回文件句柄(通常为 0)。
执行 step2: 进行 SPI 数据传输测试
短数据测试:
rgs c 999 SPIX 0 1 2 3 4
长数据测试:
rgs c 999 SPIX 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
使用 SPIX 命令进行 SPI 数据传输,其中 0 是上一条命令返回的文件句柄,后面的数字是要发送的数据。
测试结果:
- MISO 和 MOSI 短接时(回环模式),返回发送的数据:
4 1 2 3 4
第一个数字 4 表示接收到的数据字节数。
- MISO 和 MOSI 不短接时,返回全 255(表示未接收到有效数据):
4 255 255 255 255
UART测试
40PIN 的 pin8 和 pin10 默认配置为 uart 功能,对应设备节点 /dev/ttyHS2,若节点没出来可以先使用本章开头部分的 quecpi_config 进行配置使能。
可以使用以下命令查看系统中所有的串口设备:
ls /dev/tty*
UART 回环测试
本测试通过将 pin8 和 pin10 短接,验证串口收发功能是否正常。
硬件连接: 将 40PIN 的 pin8(TX)和 pin10(RX)短接。
需要安装 python3-serial 库:
apt-get update
apt-get install python3-serial
执行 step1: 创建测试脚本
新建 uart_loop.py 文件,内容如下:
点击展开/折叠完整代码
import serial
import time
def serial_loopback_test(port='/dev/ttyHS2', baudrate=115200, timeout=1):
try:
# 打开串口
ser = serial.Serial(
port=port,
baudrate=baudrate,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=timeout
)
if not ser.is_open:
print(f"无法打开串口 {port}")
return
print(f"串口 {port} 已打开,开始回环测试(按Ctrl+C退出)...")
test_data = b"Hello, Serial Loopback!" # 测试数据
while True:
# 发送数据
ser.write(test_data)
print(f"发送: {test_data.decode('utf-8')}")
# 读取回环数据
time.sleep(0.1) # 等待数据接收
received = ser.read(len(test_data))
# 验证结果
if received == test_data:
print(f"接收: {received.decode('utf-8')} → 测试通过\n")
else:
print(f"接收异常: 发送[{len(test_data)}] vs 接收[{len(received)}] → 测试失败\n")
time.sleep(1) # 间隔1秒重复测试
except serial.SerialException as e:
print(f"串口错误: {e}")
except KeyboardInterrupt:
print("\n用户中断测试")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
print(f"串口 {port} 已关闭")
if __name__ == "__main__":
# 可根据实际情况修改串口和波特率
serial_loopback_test(port='/dev/ttyHS2', baudrate=115200)
执行 step2: 运行测试
python3 uart_loop.py
测试结果:
当 pin8 和 pin10 正确短接时,程序会持续发送数据并验证接收的数据是否一致:
串口 /dev/ttyHS2 已打开,开始回环测试(按Ctrl+C退出)...
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
发送: Hello, Serial Loopback!
接收: Hello, Serial Loopback! → 测试通过
按 Ctrl+C 可退出测试程序。
PWM 测试
40Pin 的 pin33 默认配置为 PWM 功能,这里选择微雪的 4pin PWM 协议的调速风扇作为测试设备。
硬件连接
PWM 风扇接线定义:
| Pin | Function | Wire colour | 40PIN 连接 |
|---|---|---|---|
| 1 | +5V | Red | pin2 或 pin4 (5V) |
| 2 | PWM | Blue | pin33 (GPIO_78) |
| 3 | GND | Black | pin6/9/14/20/25/30/34/39 (任意GND) |
| 4 | Tach | Yellow | pin32 (GPIO_76) |
连接示意图:
风扇连接到40PIN
PWM风扇接口定义
- Tach 引脚用于读取风扇转速,是可选连接
- 如果不需要读取转速信息,Tach 引脚可以不连接
使用 C 代码控制
创建 pwm.c 文件,文件内容如下:
#include <stdio.h> #include <lgpio.h> int main(int argc, char **argv) { int h; int gpio = 78; float pwmFrequency = 1000; float pwmDutyCycle = 50; h = lgGpiochipOpen(4); // 打开 /dev/gpiochip4 设备 if (h < 0) { printf("ERROR: %s (%d)\n", lguErrorText(h), h); return 1; } int e = lgGpioClaimOutput(h, 0, gpio, 0); if (e < 0) { printf("ERROR: %s (%d)\n", lguErrorText(e), e); return 1; } e = lgTxPwm(h, gpio, pwmFrequency, pwmDutyCycle, 0, 0); if (e < 0) { printf("ERROR: %s (%d)\n", lguErrorText(e), e); return 1; } lguSleep(5); lgGpioFree(h, gpio); lgGpiochipClose(h); return 0; }编译:
gcc pwm.c -o pwm -llgpio执行:
sudo ./pwm风扇会以中等转速运转,可以调整代码中的
pwmDutyCycle值(范围 0-100)来调整风扇转速。