第一个嵌入式程序 #
一、项目目标 #
本章我们将完成嵌入式开发的"Hello World"——让LED灯闪烁。通过这个简单的项目,你将学习:
- GPIO基本概念
- 硬件电路连接
- Pi4J库的使用
- 嵌入式程序的基本结构
二、GPIO基础 #
2.1 什么是GPIO #
GPIO(General Purpose Input/Output,通用输入输出)是嵌入式系统与外部世界交互的基本接口。
text
┌─────────────────────────────────────────┐
│ GPIO引脚功能 │
├─────────────────────────────────────────┤
│ 输出模式:控制LED、继电器、蜂鸣器等 │
│ 输入模式:读取按键、传感器信号等 │
│ 特殊功能:PWM、I2C、SPI、UART等 │
└─────────────────────────────────────────┘
2.2 Raspberry Pi GPIO引脚图 #
text
3.3V ──[01] [02]── 5V
GPIO2 ──[03] [04]── 5V
GPIO3 ──[05] [06]── GND
GPIO4 ──[07] [08]── GPIO14
GND ──[09] [10]── GPIO15
GPIO17 ──[11] [12]── GPIO18
GPIO27 ──[13] [14]── GND
GPIO22 ──[15] [16]── GPIO23
3.3V ──[17] [18]── GPIO24
GPIO10 ──[19] [20]── GND
GPIO9 ──[21] [22]── GPIO25
GPIO11 ──[23] [24]── GPIO8
GND ──[25] [26]── GPIO7
GPIO0 ──[27] [28]── GPIO1
GPIO5 ──[29] [30]── GND
GPIO6 ──[31] [32]── GPIO12
GPIO13 ──[33] [34]── GND
GPIO19 ──[35] [36]── GPIO16
GPIO26 ──[37] [38]── GPIO20
GND ──[39] [40]── GPIO21
2.3 GPIO编号方式 #
| 编号方式 | 说明 | 示例 |
|---|---|---|
| BCM编号 | Broadcom芯片引脚号 | GPIO17 |
| 物理编号 | 板上引脚位置编号 | Pin 11 |
| WiringPi | WiringPi库编号 | Pin 0 |
本教程使用BCM编号方式。
三、硬件准备 #
3.1 所需材料 #
| 材料 | 数量 | 说明 |
|---|---|---|
| Raspberry Pi | 1 | 任意型号 |
| LED | 1 | 任意颜色 |
| 电阻 | 1 | 220Ω-330Ω |
| 面包板 | 1 | 用于连接 |
| 杜邦线 | 2 | 公对母 |
3.2 电路连接 #
text
Raspberry Pi
┌─────────────┐
│ │
GPIO17 ────────┤ Pin 11 │
│ │
│ GND ├───────┐
│ │ │
└─────────────┘ │
│
┌─────────────────────────────────────┘
│
│ ┌───────┐ ┌───────┐
└───┤ LED ├──────┤ 220Ω ├───┐
└───────┘ └───────┘ │
│
电路说明: │
GPIO17 ── LED正极 ── LED负极 ── 电阻 ── GND
连接步骤:
- 将LED长脚(正极)连接到GPIO17(Pin 11)
- 将LED短脚(负极)连接到电阻一端
- 将电阻另一端连接到GND(Pin 6或Pin 9)
四、Pi4J库介绍 #
4.1 Pi4J概述 #
Pi4J是Raspberry Pi上最流行的Java硬件控制库,提供:
- GPIO数字输入输出
- PWM输出
- I2C/SPI/UART通信
- 事件监听机制
4.2 添加依赖 #
xml
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-plugin-raspberrypi</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-plugin-pigpio</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
五、编写程序 #
5.1 基础版本:LED闪烁 #
java
package com.example.led;
import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;
public class LedBlink {
private static final int PIN_LED = 17;
public static void main(String[] args) throws InterruptedException {
System.out.println("LED闪烁程序启动...");
var pi4j = Pi4J.newAutoContext();
var led = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
.id("led")
.name("LED Flasher")
.address(PIN_LED)
.shutdown(DigitalState.LOW)
.initial(DigitalState.LOW)
.provider("pigpio-digital-output"));
System.out.println("LED控制程序运行中,按Ctrl+C退出");
while (true) {
led.high();
System.out.println("LED: ON");
Thread.sleep(1000);
led.low();
System.out.println("LED: OFF");
Thread.sleep(1000);
}
}
}
5.2 进阶版本:可控闪烁 #
java
package com.example.led;
import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;
import java.util.Scanner;
public class ControlledBlink {
private static final int PIN_LED = 17;
private static volatile boolean running = true;
public static void main(String[] args) {
System.out.println("=== 可控LED闪烁程序 ===");
System.out.println("命令: start | stop | speed <毫秒> | exit");
var pi4j = Pi4J.newAutoContext();
var led = pi4j.create(DigitalOutput.newConfigBuilder(pi4j)
.id("led")
.name("LED Controller")
.address(PIN_LED)
.shutdown(DigitalState.LOW)
.initial(DigitalState.LOW)
.provider("pigpio-digital-output"));
BlinkController controller = new BlinkController(led);
Thread blinkThread = new Thread(controller);
blinkThread.start();
Scanner scanner = new Scanner(System.in);
while (running) {
String input = scanner.nextLine().trim().toLowerCase();
String[] parts = input.split("\\s+");
switch (parts[0]) {
case "start":
controller.start();
System.out.println("闪烁已启动");
break;
case "stop":
controller.stop();
led.low();
System.out.println("闪烁已停止");
break;
case "speed":
if (parts.length > 1) {
try {
int speed = Integer.parseInt(parts[1]);
controller.setSpeed(speed);
System.out.println("闪烁速度设置为 " + speed + "ms");
} catch (NumberFormatException e) {
System.out.println("无效的速度值");
}
}
break;
case "exit":
running = false;
controller.stop();
led.low();
System.out.println("程序退出");
break;
default:
System.out.println("未知命令");
}
}
pi4j.shutdown();
scanner.close();
}
static class BlinkController implements Runnable {
private final DigitalOutput led;
private volatile boolean blinking = false;
private int speedMs = 500;
public BlinkController(DigitalOutput led) {
this.led = led;
}
@Override
public void run() {
while (running) {
if (blinking) {
led.toggle();
}
try {
Thread.sleep(speedMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public void start() {
blinking = true;
}
public void stop() {
blinking = false;
}
public void setSpeed(int speedMs) {
this.speedMs = Math.max(50, speedMs);
}
}
}
5.3 完整版本:多LED控制 #
java
package com.example.led;
import com.pi4j.Pi4J;
import com.pi4j.io.gpio.digital.DigitalOutput;
import com.pi4j.io.gpio.digital.DigitalState;
import com.pi4j.io.gpio.digital.DigitalOutputConfigBuilder;
import java.util.ArrayList;
import java.util.List;
public class MultiLedDemo {
private static final int[] LED_PINS = {17, 18, 22, 23};
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 多LED演示程序 ===\n");
var pi4j = Pi4J.newAutoContext();
List<DigitalOutput> leds = new ArrayList<>();
for (int i = 0; i < LED_PINS.length; i++) {
var led = 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"));
leds.add(led);
}
System.out.println("1. 流水灯效果");
runningLight(leds, 3);
System.out.println("\n2. 来回闪烁效果");
pingPong(leds, 3);
System.out.println("\n3. 二进制计数效果");
binaryCounter(leds, 16);
System.out.println("\n4. 呼吸灯效果(模拟)");
breathingEffect(leds.get(0), 5);
for (DigitalOutput led : leds) {
led.low();
}
System.out.println("\n演示完成!");
pi4j.shutdown();
}
private static void runningLight(List<DigitalOutput> leds, int cycles)
throws InterruptedException {
for (int cycle = 0; cycle < cycles; cycle++) {
for (DigitalOutput led : leds) {
led.high();
Thread.sleep(150);
led.low();
}
}
}
private static void pingPong(List<DigitalOutput> leds, int cycles)
throws InterruptedException {
for (int cycle = 0; cycle < cycles; cycle++) {
for (int i = 0; i < leds.size(); i++) {
leds.get(i).high();
Thread.sleep(100);
leds.get(i).low();
}
for (int i = leds.size() - 2; i > 0; i--) {
leds.get(i).high();
Thread.sleep(100);
leds.get(i).low();
}
}
}
private static void binaryCounter(List<DigitalOutput> leds, int maxCount)
throws InterruptedException {
for (int count = 0; count < maxCount; count++) {
System.out.print("计数: " + count + " -> ");
for (int i = 0; i < leds.size(); i++) {
boolean bit = ((count >> i) & 1) == 1;
if (bit) {
leds.get(i).high();
System.out.print("1");
} else {
leds.get(i).low();
System.out.print("0");
}
}
System.out.println();
Thread.sleep(500);
}
}
private static void breathingEffect(DigitalOutput led, int cycles)
throws InterruptedException {
for (int cycle = 0; cycle < cycles; cycle++) {
for (int i = 0; i < 10; i++) {
led.high();
Thread.sleep(i * 2);
led.low();
Thread.sleep((10 - i) * 2);
}
for (int i = 10; i > 0; i--) {
led.high();
Thread.sleep(i * 2);
led.low();
Thread.sleep((10 - i) * 2);
}
}
led.low();
}
}
六、编译与运行 #
6.1 编译项目 #
bash
# 在开发主机上编译
mvn clean package
# 生成的JAR文件位于 target/ 目录
6.2 部署到设备 #
bash
# 使用SCP传输
scp target/led-blink-1.0-SNAPSHOT.jar pi@192.168.1.100:/home/pi/
# 或使用rsync
rsync -avz target/led-blink-1.0-SNAPSHOT.jar pi@192.168.1.100:/home/pi/
6.3 运行程序 #
bash
# SSH登录到设备
ssh pi@192.168.1.100
# 运行程序
java -jar led-blink-1.0-SNAPSHOT.jar
# 后台运行
nohup java -jar led-blink-1.0-SNAPSHOT.jar > led.log 2>&1 &
# 查看日志
tail -f led.log
6.4 开机自启动 #
创建systemd服务:
bash
# 创建服务文件
sudo nano /etc/systemd/system/led-blink.service
ini
[Unit]
Description=LED Blink Service
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi
ExecStart=/usr/bin/java -jar /home/pi/led-blink-1.0-SNAPSHOT.jar
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
bash
# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable led-blink.service
sudo systemctl start led-blink.service
# 查看状态
sudo systemctl status led-blink.service
七、常见问题 #
7.1 LED不亮 #
检查清单:
- LED极性是否正确(长脚为正极)
- 电阻阻值是否合适
- GPIO引脚号是否正确
- 程序是否正确运行
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
7.2 权限错误 #
bash
# 确保用户在正确的组
groups pi
# 添加到gpio组
sudo usermod -a -G gpio pi
# 重新登录生效
logout
7.3 pigpio服务问题 #
bash
# 检查pigpio服务状态
sudo systemctl status pigpiod
# 重启服务
sudo systemctl restart pigpiod
# 查看日志
journalctl -u pigpiod -f
八、代码结构说明 #
8.1 Pi4J核心对象 #
java
// Pi4J上下文 - 主入口点
var pi4j = Pi4J.newAutoContext();
// 数字输出配置
DigitalOutput.newConfigBuilder(pi4j)
.id("unique-id") // 唯一标识符
.name("Friendly Name") // 友好名称
.address(17) // GPIO引脚号
.shutdown(DigitalState.LOW)// 关闭时状态
.initial(DigitalState.LOW) // 初始状态
.provider("pigpio-digital-output"); // 提供者
// 常用方法
led.high(); // 输出高电平
led.low(); // 输出低电平
led.toggle(); // 切换状态
led.state(); // 获取当前状态
8.2 程序生命周期 #
text
初始化 ──▶ 创建Pi4J上下文 ──▶ 配置GPIO ──▶ 主循环 ──▶ 清理资源
九、总结 #
通过本章的学习,你已经掌握了:
- GPIO的基本概念和使用方法
- 硬件电路的连接技巧
- Pi4J库的基本使用
- LED控制的多种实现方式
关键要点:
- 安全第一:连接电路前断电,使用合适的电阻保护GPIO
- 正确编号:使用BCM编号方式,避免引脚混淆
- 资源管理:程序退出时正确关闭GPIO和Pi4J上下文
- 异常处理:添加适当的异常处理和日志记录
下一章我们将学习JVM在嵌入式环境下的优化技巧,让Java程序在资源受限的设备上运行得更高效。
最后更新:2026-03-27