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编程要点:
- 输出控制:使用DigitalOutput控制LED、继电器等设备
- 输入读取:使用DigitalInput读取按钮、传感器状态
- 事件监听:使用addListener实现中断式响应
- 消抖处理:配置debounce参数消除机械抖动
- 上下拉电阻:根据电路配置合适的上下拉模式
下一章我们将学习I2C通信协议,实现与I2C设备的交互。
最后更新:2026-03-27