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
相关产品推荐
相关产品推荐

