You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Pi4J v2结合GrovePi遇I2C写入异常求助

问题

在树莓派3(DietPi系统)上使用Pi4J v2库驱动Grove RGB LCD显示屏,严格遵循官方文档操作后,Java代码无法通过Pi4J写入I2C端口。运行Jar文件无控制台报错,日志显示执行了写入操作,但显示屏无任何反应。DietPi已启用I2C端口,相同硬件下Python测试代码可正常运行。

测试代码

Java测试代码

(注:GrovePi4J类是Grove库提供的预配置类,I2C输入/输出配置与Python示例完全一致)

import java.io.IOException;
import java.util.Random;

import com.github.yafna.raspberry.grovepi.GrovePi;
import com.github.yafna.raspberry.grovepi.devices.GroveRgbLcd;
import com.github.yafna.raspberry.grovepi.pi4j.GrovePi4J;
import com.pi4j.Pi4J;
import com.pi4j.context.Context;


public class LcdService {
    
    public void exec() throws IOException, InterruptedException {
        Context pi4j = Pi4J.newAutoContext();
        GrovePi grovePi = new GrovePi4J(pi4j);
        GroveRgbLcd lcd = grovePi.getLCD();

        String[] phrases = new String[]{
          "Hi! ALL!",
          "This should work just fine",
          "A message\nA response",
          "More data that you can handle for sure!",
          "Welcome to the internet of things with java",
          "Short\nMessage"
        };
        int[][] colors = new int[][]{
          {50, 255, 30},
          {15, 88, 245},
          {248, 52, 100},
          {48, 56, 190},
          {178, 25, 180},
          {210, 210, 210}
        };
        while (true) {
          try {
            String text = phrases[new Random().nextInt(phrases.length)];
            int[] color = colors[new Random().nextInt(colors.length)];
            lcd.setRGB(color[0], color[1], color[2]);
            Thread.sleep(100);
            lcd.setText(text);
            Thread.sleep(2000);
          } catch (IOException io) {
              // 原代码空捕获,无法排查异常
          }
        }
    }
}

Python测试代码

import time,sys

if sys.platform == 'uwp':
    import winrt_smbus as smbus
    bus = smbus.SMBus(1)
else:
    import smbus
    import RPi.GPIO as GPIO
    rev = GPIO.RPI_REVISION
    if rev == 2 or rev == 3:
        bus = smbus.SMBus(1)
    else:
        bus = smbus.SMBus(0)

# 设备拥有两个I2C地址
DISPLAY_RGB_ADDR = 0x62
DISPLAY_TEXT_ADDR = 0x3e

# 设置背光RGB值(0-255)
def setRGB(r,g,b):
    bus.write_byte_data(DISPLAY_RGB_ADDR,0,0)
    bus.write_byte_data(DISPLAY_RGB_ADDR,1,0)
    bus.write_byte_data(DISPLAY_RGB_ADDR,0x08,0xaa)
    bus.write_byte_data(DISPLAY_RGB_ADDR,4,r)
    bus.write_byte_data(DISPLAY_RGB_ADDR,3,g)
    bus.write_byte_data(DISPLAY_RGB_ADDR,2,b)

# 向显示屏发送命令(无需外部调用)
def textCommand(cmd):
    bus.write_byte_data(DISPLAY_TEXT_ADDR,0x80,cmd)

# 设置显示文本,\n代表换行或自动换行
def setText(text):
    textCommand(0x01) # 清屏
    time.sleep(.05)
    textCommand(0x08 | 0x04) # 显示开启,无光标
    textCommand(0x28) # 两行模式
    time.sleep(.05)
    count = 0
    row = 0
    for c in text:
        if c == '\n' or count == 16:
            count = 0
            row += 1
            if row == 2:
                break
            textCommand(0xc0)
            if c == '\n':
                continue
        count += 1
        bus.write_byte_data(DISPLAY_TEXT_ADDR,0x40,ord(c))

# 更新显示内容不清屏
def setText_norefresh(text):
    textCommand(0x02) # 返回首页
    time.sleep(.05)
    textCommand(0x08 | 0x04) # 显示开启,无光标
    textCommand(0x28) # 两行模式
    time.sleep(.05)
    count = 0
    row = 0
    while len(text) < 32: # 清空屏幕剩余区域
        text += ' '
    for c in text:
        if c == '\n' or count == 16:
            count = 0
            row += 1
            if row == 2:
                break
            textCommand(0xc0)
            if c == '\n':
                continue
        count += 1
        bus.write_byte_data(DISPLAY_TEXT_ADDR,0x40,ord(c))

# 创建自定义字符(传入位置和行模式数组)
def create_char(location, pattern):
    """
    向LCD CGRAM写入位模式

    参数:
    location -- 整数,0-7中的一个位置
    pattern -- 包含位模式的字节数组
    """
    location &= 0x07 # 确保位置在0-7范围内
    textCommand(0x40 | (location << 3))
    bus.write_i2c_block_data(DISPLAY_TEXT_ADDR, 0x40, pattern)

# 示例代码
if __name__=="__main__":
    setText("Hello world\nThis is an LCD test")
    setRGB(0,128,64)
    time.sleep(2)
    for c in range(0,255):
        setText_norefresh("Going to sleep in {}...".format(str(c)))
        setRGB(c,255-c,0)
        time.sleep(0.1)
    setRGB(0,255,0)
    setText("Bye bye, this should wrap onto next line")

排查与解决方案

1. 修复异常捕获,暴露真实问题

原Java代码中IOException被空捕获,完全无法得知执行时是否有错误。修改捕获块,打印异常信息:

catch (IOException io) {
    io.printStackTrace();
}

运行后就能看到具体的I2C操作错误,比如地址不存在、总线访问失败等。

2. 显式指定Pi4J的I2C总线

树莓派3默认使用I2C总线1,但Pi4J v2的自动上下文可能未正确识别,手动指定总线创建实例:

// 替换原有Context初始化代码
Context pi4j = Pi4J.newContextBuilder()
        .add(new I2CProviderImpl())
        .build();

// 手动指定总线1创建GrovePi4J实例
GrovePi grovePi = new GrovePi4J(pi4j, 1);

3. 验证GrovePi4J库的地址映射

对比Python代码中的两个I2C地址(0x62用于RGB背光,0x3e用于文本),确认GroveRgbLcd类是否正确使用了这两个地址。如果封装库默认地址不符,手动指定:

// 若库支持,手动指定地址
GroveRgbLcd lcd = grovePi.getLCD(0x3e, 0x62);

4. 确保运行权限

Java程序访问I2C设备文件需要root权限,运行Jar时加上sudo:

sudo java -jar your-lcd-app.jar

5. 绕开封装库,直接用Pi4J原生API测试

如果以上方法无效,直接用Pi4J的I2C API模拟Python的操作逻辑,排除封装库的问题:

import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.i2c.I2C;
import com.pi4j.io.i2c.I2CConfig;

public class DirectI2CLcdTest {
    private static final int DISPLAY_RGB_ADDR = 0x62;
    private static final int DISPLAY_TEXT_ADDR = 0x3e;

    public static void main(String[] args) throws Exception {
        Context pi4j = Pi4J.newAutoContext();

        // 创建RGB背光I2C设备
        I2C rgbI2c = pi4j.create(I2C.newConfigBuilder(pi4j)
                .id("rgb-backlight")
                .bus(1)
                .device(DISPLAY_RGB_ADDR)
                .build());

        // 创建文本显示I2C设备
        I2C textI2c = pi4j.create(I2C.newConfigBuilder(pi4j)
                .id("text-display")
                .bus(1)
                .device(DISPLAY_TEXT_ADDR)
                .build());

        // 执行Python中的setRGB逻辑
        rgbI2c.writeRegister(0, (byte)0);
        rgbI2c.writeRegister(1, (byte)0);
        rgbI2c.writeRegister(0x08, (byte)0xaa);
        rgbI2c.writeRegister(4, (byte)0);
        rgbI2c.writeRegister(3, (byte)128);
        rgbI2c.writeRegister(2, (byte)64);

        // 执行Python中的setText逻辑
        textCommand(textI2c, 0x01);
        Thread.sleep(50);
        textCommand(textI2c, 0x08 | 0x04);
        textCommand(textI2c, 0x28);
        Thread.sleep(50);

        writeText(textI2c, "Hello from Pi4J!");
        Thread.sleep(5000);

        pi4j.shutdown();
    }

    private static void textCommand(I2C i2c, int cmd) throws Exception {
        i2c.writeRegister(0x80, (byte)cmd);
    }

    private static void writeText(I2C i2c, String text) throws Exception {
        for (char c : text.toCharArray()) {
            i2c.writeRegister(0x40, (byte)c);
        }
    }
}

内容的提问来源于stack exchange,提问作者Lucas Girard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:55:55