在玩树莓派的时候写了个程序,用来动态控制4位数码管。但是遇到了一个问题,就是数码管的刷新频率太慢,结果就是闪烁。
于是写了一篇测试程序,最后结果是,用Java + Pi4j,大概每秒钟可以把GPIO置高置低1400次左右。这也太慢了。
网上有一篇文章,http://codeandlife.com/2012/07/03/benchmarking-raspberry-pi-gpio-speed/ ,里面对比了各个开发库操作GPIO的效率。结果是Python Perl这样的都比我得到的结果快得多。(每秒40k)
也没别的办法,就下载了Pi4j的源代码,最后发现,原来Pi4j的实现有一点问题。
在每次调用high()或者low()的时候,都要调用一个com.pi4j.wiringpi.GpioUtil.isPinSupported函数。这个是个JNI函数,而且比较慢。调用层次为
at com.pi4j.wiringpi.GpioUtil.isPinSupported(GpioUtil.java:291)
at com.pi4j.io.gpio.RaspiGpioProvider.hasPin(RaspiGpioProvider.java:61)
at com.pi4j.io.gpio.GpioProviderBase.setState(GpioProviderBase.java:172)
at com.pi4j.io.gpio.RaspiGpioProvider.setState(RaspiGpioProvider.java:154)
at com.pi4j.io.gpio.impl.GpioPinImpl.setState(GpioPinImpl.java:325)
at com.pi4j.io.gpio.impl.GpioPinImpl.low(GpioPinImpl.java:209)
at TestSpeed.test(TestSpeed.java:23)
at TestSpeed.main(TestSpeed.java:14)
考虑到不需要每次都判断一个端口是否存在,就把这里加了一个Cache,改为
private static ConcurrentHashMap<Pin, Boolean> hasPinMap = new ConcurrentHashMap(); @Override public boolean hasPin(Pin pin) { Boolean r = hasPinMap.get(pin); if (r != null) { return r.booleanValue(); } boolean realValue = (com.pi4j.wiringpi.GpioUtil.isPinSupported(pin.getAddress()) > 0); hasPinMap.putIfAbsent(pin, realValue); return realValue; }
再次进行测试,结果从之前的1400次每秒上升为158000次每秒!!!重新进行数码管的测试,数码管的闪烁现象消失。
后来和Pi4j的开发者沟通了下,他们说这个bug已经在1.1 Snapshot下修正了,测试了下果然如此。