40-Pin扩展

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


Quectel Pi H1智能主控板配备了标准的40-pin GPIO扩展接口,支持GPIO、I2C、SPI、UART、PWM等多种外设接口。下面将介绍如何测试这些接口的功能。

引脚定义

../../_images/image_DI7hbqQmRokgZExVb1fcAYdOnIf.webp ../../_images/image_TYqebBrtloyjP8xwMMscyMqtnpd.webp

接口配置说明

40-pin中部分低速接口(I2C9/SPI10/UART12_2W/I2C13/SPI14)的配置文件位于 /etc/qpi-config/qpi-config.ini。

配置步骤:

  1. 修改 qpi-config.ini 配置文件

  2. 应用配置

qpi-config 40pin set
  1. 重启系统使配置生效

GPIO测试

本节以测试40-pin接口的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控制,请参考以下文档:

这些文档提供了完整的代码示例、编译方法和测试步骤。

I2C测试

40-pin接口的pin3和pin5默认是I2C的data和clock引脚,对应设备节点 /dev/i2c9。

提示
若设备节点没有出现,可以先使用本章开头部分的 qpi-config 进行配置使能。

测试准备

本次测试使用微雪环境传感器扩展板(BME280),通过40-pin接口进行连接。

硬件连接示意图:

../../_images/image_A9mXbAhNOoxzXmxRQ5Qc7WAknkc.webp
微雪环境传感器扩展板
../../_images/image_KrPJbr3AboDxwzxXPaAcMNcHnRc.webp
Quectel Pi H1 40-Pin接口
../../_images/image_VoS8bfbvPogqhLxeH9Jcs8sAnTb.webp
插上环境传感器扩展板的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测试

40-pin接口中的SPI功能对应设备节点 /dev/spi10。

提示
若设备节点没有出现,可以先使用本章开头部分的 qpi-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测试

40-pin接口 的 pin8 和 pin10 默认配置为 UART 功能,对应设备节点 /dev/ttyHS2,若设备节点没有出现,可以先使用本章开头部分的 qpi-config 进行配置使能。

查看串口设备
你可以使用以下命令查看系统中所有的串口设备:

ls /dev/tty*

UART回环测试

本测试通过将 pin8 和 pin10 短接,验证串口收发功能是否正常。

硬件连接: 将40-pin接口的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测试

40-pin接口 的 pin33 默认配置为 PWM 功能,这里选择微雪的 4pin PWM 协议的调速风扇作为测试设备。

硬件连接

PWM风扇接线定义:

Pin

Function

Wire colour

40-Pin连接

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)

连接示意图:

../../_images/image_H5sxbIgjiovyqxxDkM4cejsZnDb.webp
风扇连接到40-Pin接口
../../_images/image_Sq9SbpyEQo4HBbxhivvcfG9qnTd.webp
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)来调整风扇转速。