GPIO编程 #

一、GPIO基础概念 #

1.1 GPIO工作模式 #

GPIO引脚可以配置为两种基本模式:

text
┌─────────────────────────────────────────────────────┐
│                   GPIO工作模式                       │
├─────────────────────────────────────────────────────┤
│                                                     │
│   输出模式 (OUTPUT)          输入模式 (INPUT)       │
│   ┌─────────────┐           ┌─────────────┐        │
│   │   控制器    │           │   控制器    │        │
│   └──────┬──────┘           └──────┬──────┘        │
│          │                         │               │
│          ▼                         ▼               │
│      ┌───────┐               ┌───────┐            │
│      │ HIGH  │               │ 读取  │            │
│      │ LOW   │               │ HIGH  │            │
│      └───────┘               │ LOW   │            │
│          │                   └───────┘            │
│          ▼                         │               │
│      外部设备                    外部信号           │
│      (LED/继电器)              (按钮/传感器)        │
│                                                     │
└─────────────────────────────────────────────────────┘

1.2 电气特性 #

特性 说明 典型值
逻辑高电平 HIGH 3.3V
逻辑低电平 LOW 0V
输出电流 单引脚最大 16mA
总输出电流 所有引脚总和 50mA
输入阻抗 高阻抗模式 >1MΩ

1.3 上拉/下拉电阻 #

text
上拉电阻 (PULL_UP)              下拉电阻 (PULL_DOWN)
    3.3V                           3.3V
      │                               │
     ┌┴┐                             ┌┴┐
     │ │ R                           │ │ R
     └┬┘                             └┬┘
      │                               │
      ├──────── GPIO                  ├──────── GPIO
      │                               │
     ┌┴┐                             ─┴─ 按钮
     │ │ 按钮                         │
     └┬┘                             ─┬─
      │                               │
     ═┴═ GND                         ═┴═ GND

按下按钮: GPIO = LOW              按下按钮: GPIO = HIGH
松开按钮: GPIO = HIGH             松开按钮: GPIO = LOW

二、数字输出 #

2.1 基本输出操作 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;

public class DigitalOutputDemo {

    private static final int PIN_LED = 17;
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        var led = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id("led")
                .name("LED Output")
                .address(PIN_LED)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        
        System.out.println("数字输出演示");
        
        led.high();
        System.out.println("LED状态: " + led.state());
        Thread.sleep(1000);
        
        led.low();
        System.out.println("LED状态: " + led.state());
        Thread.sleep(1000);
        
        led.toggle();
        System.out.println("LED状态: " + led.state());
        Thread.sleep(1000);
        
        led.pulse(2000, DigitalState.HIGH);
        System.out.println("LED脉冲2秒");
        
        pi4j.shutdown();
    }
}

2.2 多输出控制 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;
import java.util.HashMap;
import java.util.Map;

public class MultiOutputController {

    private final Map<String, DigitalOutput> outputs = new HashMap<>();
    private final var pi4j;
    
    public MultiOutputController() {
        this.pi4j = Pi4J.newAutoContext();
    }
    
    public void addOutput(String name, int pin) {
        var output = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                .id(name)
                .name(name)
                .address(pin)
                .shutdown(DigitalState.LOW)
                .initial(DigitalState.LOW)
                .provider("pigpio-digital-output"));
        outputs.put(name, output);
    }
    
    public void setHigh(String name) {
        if (outputs.containsKey(name)) {
            outputs.get(name).high();
        }
    }
    
    public void setLow(String name) {
        if (outputs.containsKey(name)) {
            outputs.get(name).low();
        }
    }
    
    public void toggle(String name) {
        if (outputs.containsKey(name)) {
            outputs.get(name).toggle();
        }
    }
    
    public void setAllHigh() {
        outputs.values().forEach(DigitalOutput::high);
    }
    
    public void setAllLow() {
        outputs.values().forEach(DigitalOutput::low);
    }
    
    public DigitalState getState(String name) {
        if (outputs.containsKey(name)) {
            return outputs.get(name).state();
        }
        return null;
    }
    
    public void shutdown() {
        pi4j.shutdown();
    }
    
    public static void main(String[] args) throws InterruptedException {
        var controller = new MultiOutputController();
        
        controller.addOutput("led1", 17);
        controller.addOutput("led2", 18);
        controller.addOutput("led3", 22);
        
        System.out.println("流水灯效果");
        for (int i = 0; i < 5; i++) {
            controller.setHigh("led1");
            Thread.sleep(200);
            controller.setLow("led1");
            
            controller.setHigh("led2");
            Thread.sleep(200);
            controller.setLow("led2");
            
            controller.setHigh("led3");
            Thread.sleep(200);
            controller.setLow("led3");
        }
        
        controller.shutdown();
    }
}

三、数字输入 #

3.1 基本输入操作 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalInput;
import com.pi4j.io.gpio.digital.DigitalState;
import com.pi4j.io.gpio.digital.PullResistance;

public class DigitalInputDemo {

    private static final int PIN_BUTTON = 18;
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        var button = pi4j.create(DigitalInput.newConfigBuilder(pi4j)
                .id("button")
                .name("Button Input")
                .address(PIN_BUTTON)
                .pull(PullResistance.PULL_UP)
                .debounce(3000L)
                .provider("pigpio-digital-input"));
        
        System.out.println("数字输入演示 - 按钮检测");
        System.out.println("按下按钮查看状态变化(Ctrl+C退出)");
        
        while (true) {
            DigitalState state = button.state();
            
            if (state == DigitalState.LOW) {
                System.out.println("按钮被按下!");
            } else {
                System.out.println("按钮未按下");
            }
            
            Thread.sleep(100);
        }
    }
}

3.2 输入状态轮询 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalInput;
import com.pi4j.io.gpio.digital.DigitalState;
import com.pi4j.io.gpio.digital.PullResistance;

import java.util.function.Consumer;

public class InputPoller {

    private final DigitalInput input;
    private volatile boolean running = true;
    private DigitalState lastState;
    private final Consumer<DigitalState> stateChangeListener;
    
    public InputPoller(DigitalInput input, Consumer<DigitalState> listener) {
        this.input = input;
        this.stateChangeListener = listener;
        this.lastState = input.state();
    }
    
    public void startPolling(int intervalMs) {
        Thread pollThread = new Thread(() -> {
            while (running) {
                DigitalState currentState = input.state();
                if (currentState != lastState) {
                    lastState = currentState;
                    stateChangeListener.accept(currentState);
                }
                try {
                    Thread.sleep(intervalMs);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        pollThread.setDaemon(true);
        pollThread.start();
    }
    
    public void stop() {
        running = false;
    }
    
    public static void main(String[] args) {
        var pi4j = Pi4J.newAutoContext();
        
        var button = pi4j.create(DigitalInput.newConfigBuilder(pi4j)
                .id("button")
                .name("Button")
                .address(18)
                .pull(PullResistance.PULL_UP)
                .provider("piggpio-digital-input"));
        
        InputPoller poller = new InputPoller(button, state -> {
            if (state == DigitalState.LOW) {
                System.out.println("[" + System.currentTimeMillis() + "] 按钮按下");
            } else {
                System.out.println("[" + System.currentTimeMillis() + "] 按钮释放");
            }
        });
        
        poller.startPolling(50);
        
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        poller.stop();
        pi4j.shutdown();
    }
}

四、事件监听与中断 #

4.1 边缘触发事件 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalInput;
import com.pi4j.io.gpio.digital.DigitalState;
import com.pi4j.io.gpio.digital.DigitalInputEventListener;
import com.pi4j.io.gpio.digital.PullResistance;
import com.pi4j.io.gpio.digital.DigitalInputConfig;

public class EdgeTriggerDemo {

    private static final int PIN_BUTTON = 18;
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        var button = pi4j.create(DigitalInput.newConfigBuilder(pi4j)
                .id("button")
                .name("Button")
                .address(PIN_BUTTON)
                .pull(PullResistance.PULL_UP)
                .debounce(3000L)
                .provider("pigpio-digital-input"));
        
        button.addListener(event -> {
            System.out.printf("事件触发: %s -> %s%n", 
                event.source().id(),
                event.state());
            
            if (event.state() == DigitalState.LOW) {
                System.out.println("按钮按下事件!");
            } else {
                System.out.println("按钮释放事件!");
            }
        });
        
        System.out.println("事件监听已启动,按下按钮测试...");
        System.out.println("按Ctrl+C退出");
        
        Thread.sleep(Long.MAX_VALUE);
    }
}

4.2 高级事件处理 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class AdvancedButtonHandler {

    public static class ButtonEvent {
        public final Instant timestamp;
        public final DigitalState state;
        public final long durationMs;
        
        public ButtonEvent(Instant timestamp, DigitalState state, long durationMs) {
            this.timestamp = timestamp;
            this.state = state;
            this.durationMs = durationMs;
        }
    }
    
    public static class ButtonConfig {
        public final int pin;
        public final PullResistance pull;
        public final long debounceMs;
        public final long longPressMs;
        public final long doubleClickMs;
        
        public ButtonConfig(int pin) {
            this.pin = pin;
            this.pull = PullResistance.PULL_UP;
            this.debounceMs = 50;
            this.longPressMs = 1000;
            this.doubleClickMs = 300;
        }
    }
    
    private final DigitalInput button;
    private final ButtonConfig config;
    private Instant pressTime;
    private Instant lastReleaseTime;
    private int clickCount = 0;
    
    private final List<Consumer<ButtonEvent>> onPressListeners = new ArrayList<>();
    private final List<Consumer<ButtonEvent>> onReleaseListeners = new ArrayList<>();
    private final List<Consumer<ButtonEvent>> onLongPressListeners = new ArrayList<>();
    private final List<Consumer<Integer>> onDoubleClickListeners = new ArrayList<>();
    
    public AdvancedButtonHandler(var pi4j, ButtonConfig config) {
        this.config = config;
        
        this.button = pi4j.create(DigitalInput.newConfigBuilder(pi4j)
                .id("button-" + config.pin)
                .name("Button")
                .address(config.pin)
                .pull(config.pull)
                .debounce(config.debounceMs * 1000L)
                .provider("pigpio-digital-input"));
        
        setupListener();
    }
    
    private void setupListener() {
        button.addListener(event -> {
            DigitalState state = event.state();
            Instant now = Instant.now();
            
            if (state == DigitalState.LOW) {
                handlePress(now);
            } else {
                handleRelease(now);
            }
        });
    }
    
    private void handlePress(Instant now) {
        pressTime = now;
        
        ButtonEvent pressEvent = new ButtonEvent(now, DigitalState.LOW, 0);
        onPressListeners.forEach(listener -> listener.accept(pressEvent));
    }
    
    private void handleRelease(Instant now) {
        if (pressTime == null) return;
        
        long duration = now.toEpochMilli() - pressTime.toEpochMilli();
        ButtonEvent releaseEvent = new ButtonEvent(now, DigitalState.HIGH, duration);
        onReleaseListeners.forEach(listener -> listener.accept(releaseEvent));
        
        if (duration >= config.longPressMs) {
            onLongPressListeners.forEach(listener -> listener.accept(releaseEvent));
        }
        
        if (lastReleaseTime != null && 
            (now.toEpochMilli() - lastReleaseTime.toEpochMilli()) < config.doubleClickMs) {
            clickCount++;
            if (clickCount == 2) {
                onDoubleClickListeners.forEach(listener -> listener.accept(2));
                clickCount = 0;
            }
        } else {
            clickCount = 1;
        }
        
        lastReleaseTime = now;
    }
    
    public AdvancedButtonHandler onPress(Consumer<ButtonEvent> listener) {
        onPressListeners.add(listener);
        return this;
    }
    
    public AdvancedButtonHandler onRelease(Consumer<ButtonEvent> listener) {
        onReleaseListeners.add(listener);
        return this;
    }
    
    public AdvancedButtonHandler onLongPress(Consumer<ButtonEvent> listener) {
        onLongPressListeners.add(listener);
        return this;
    }
    
    public AdvancedButtonHandler onDoubleClick(Consumer<Integer> listener) {
        onDoubleClickListeners.add(listener);
        return this;
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        var config = new ButtonConfig(18);
        config.longPressMs = 1500;
        config.doubleClickMs = 400;
        
        var handler = new AdvancedButtonHandler(pi4j, config);
        
        handler.onPress(e -> System.out.println("按钮按下"));
        handler.onRelease(e -> System.out.println("按钮释放,持续时间: " + e.durationMs + "ms"));
        handler.onLongPress(e -> System.out.println("长按触发!持续时间: " + e.durationMs + "ms"));
        handler.onDoubleClick(count -> System.out.println("双击触发!"));
        
        System.out.println("高级按钮处理已启动");
        System.out.println("测试:单击、双击、长按");
        System.out.println("按Ctrl+C退出");
        
        Thread.sleep(Long.MAX_VALUE);
    }
}

五、实际应用示例 #

5.1 LED控制面板 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;
import java.util.Scanner;

public class LedControlPanel {

    private static final int[] LED_PINS = {17, 18, 22, 23};
    private static final int BUTTON_PIN = 27;
    
    private final var pi4j;
    private final DigitalOutput[] leds;
    private final DigitalInput modeButton;
    private int currentMode = 0;
    private volatile boolean running = true;
    
    public LedControlPanel() {
        this.pi4j = Pi4J.newAutoContext();
        this.leds = new DigitalOutput[LED_PINS.length];
        
        for (int i = 0; i < LED_PINS.length; i++) {
            leds[i] = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                    .id("led-" + i)
                    .name("LED " + i)
                    .address(LED_PINS[i])
                    .shutdown(DigitalState.LOW)
                    .initial(DigitalState.LOW)
                    .provider("pigpio-digital-output"));
        }
        
        this.modeButton = pi4j.create(DigitalInput.newConfigBuilder(pi4j)
                .id("mode-button")
                .name("Mode Button")
                .address(BUTTON_PIN)
                .pull(PullResistance.PULL_UP)
                .debounce(50000L)
                .provider("pigpio-digital-input"));
        
        modeButton.addListener(event -> {
            if (event.state() == DigitalState.LOW) {
                nextMode();
            }
        });
    }
    
    private void nextMode() {
        allOff();
        currentMode = (currentMode + 1) % 5;
        System.out.println("切换到模式: " + currentMode);
    }
    
    private void allOff() {
        for (DigitalOutput led : leds) {
            led.low();
        }
    }
    
    public void run() {
        System.out.println("LED控制面板启动");
        System.out.println("按下按钮切换模式");
        System.out.println("模式: 0-关闭, 1-全开, 2-流水灯, 3-闪烁, 4-二进制计数");
        
        Thread modeThread = new Thread(() -> {
            int counter = 0;
            while (running) {
                try {
                    switch (currentMode) {
                        case 0:
                            allOff();
                            Thread.sleep(500);
                            break;
                        case 1:
                            for (DigitalOutput led : leds) led.high();
                            Thread.sleep(500);
                            break;
                        case 2:
                            runningLight();
                            break;
                        case 3:
                            blinkAll();
                            break;
                        case 4:
                            binaryCount(counter++);
                            Thread.sleep(500);
                            break;
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        modeThread.start();
        
        Scanner scanner = new Scanner(System.in);
        while (running) {
            String cmd = scanner.nextLine();
            if ("exit".equals(cmd)) {
                running = false;
            }
        }
        
        allOff();
        pi4j.shutdown();
    }
    
    private void runningLight() throws InterruptedException {
        for (DigitalOutput led : leds) {
            led.high();
            Thread.sleep(150);
            led.low();
        }
    }
    
    private void blinkAll() throws InterruptedException {
        for (DigitalOutput led : leds) led.high();
        Thread.sleep(300);
        for (DigitalOutput led : leds) led.low();
        Thread.sleep(300);
    }
    
    private void binaryCount(int value) {
        for (int i = 0; i < leds.length; i++) {
            if (((value >> i) & 1) == 1) {
                leds[i].high();
            } else {
                leds[i].low();
            }
        }
    }
    
    public static void main(String[] args) {
        new LedControlPanel().run();
    }
}

5.2 继电器控制 #

java
package com.example.gpio;

import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.*;

public class RelayController {

    public static class Relay {
        private final DigitalOutput output;
        private final String name;
        private final boolean activeLow;
        
        public Relay(var pi4j, String name, int pin, boolean activeLow) {
            this.name = name;
            this.activeLow = activeLow;
            
            this.output = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
                    .id("relay-" + name)
                    .name(name)
                    .address(pin)
                    .shutdown(activeLow ? DigitalState.HIGH : DigitalState.LOW)
                    .initial(activeLow ? DigitalState.HIGH : DigitalState.LOW)
                    .provider("pigpio-digital-output"));
        }
        
        public void on() {
            output.state(activeLow ? DigitalState.LOW : DigitalState.HIGH);
            System.out.println(name + ": ON");
        }
        
        public void off() {
            output.state(activeLow ? DigitalState.HIGH : DigitalState.LOW);
            System.out.println(name + ": OFF");
        }
        
        public void toggle() {
            DigitalState currentState = output.state();
            if (currentState == DigitalState.HIGH) {
                if (activeLow) on(); else off();
            } else {
                if (activeLow) off(); else on();
            }
        }
        
        public boolean isOn() {
            DigitalState state = output.state();
            return activeLow ? state == DigitalState.LOW : state == DigitalState.HIGH;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        var pi4j = Pi4J.newAutoContext();
        
        Relay light = new Relay(pi4j, "Light", 17, true);
        Relay fan = new Relay(pi4j, "Fan", 18, true);
        
        System.out.println("继电器控制演示");
        
        light.on();
        Thread.sleep(2000);
        light.off();
        
        fan.on();
        Thread.sleep(2000);
        fan.off();
        
        pi4j.shutdown();
    }
}

六、GPIO调试技巧 #

6.1 命令行调试 #

bash
# 导出GPIO
echo 17 > /sys/class/gpio/export

# 设置方向
echo out > /sys/class/gpio/gpio17/direction

# 设置值
echo 1 > /sys/class/gpio/gpio17/value
echo 0 > /sys/class/gpio/gpio17/value

# 读取值
cat /sys/class/gpio/gpio17/value

# 释放GPIO
echo 17 > /sys/class/gpio/unexport

6.2 使用gpio命令 #

bash
# 读取所有GPIO状态
gpio readall

# 设置GPIO模式
gpio mode 17 out

# 写入GPIO
gpio write 17 1
gpio write 17 0

# 读取GPIO
gpio read 17

6.3 常见问题排查 #

问题 可能原因 解决方案
GPIO无响应 权限不足 添加用户到gpio组
状态不稳定 缺少上拉/下拉 配置内部上拉/下拉
按键抖动 机械抖动 添加软件消抖
输出电流不足 负载过重 使用驱动电路

七、总结 #

GPIO编程要点:

  1. 输出控制:使用DigitalOutput控制LED、继电器等设备
  2. 输入读取:使用DigitalInput读取按钮、传感器状态
  3. 事件监听:使用addListener实现中断式响应
  4. 消抖处理:配置debounce参数消除机械抖动
  5. 上下拉电阻:根据电路配置合适的上下拉模式

下一章我们将学习I2C通信协议,实现与I2C设备的交互。

最后更新:2026-03-27