前几天刚在某宝上买了个树莓派4b 4G内存版,附带了外壳,读卡器,16G TF卡,风扇,散热片,3.5寸电阻屏,几个RGB LED和一个8x8矩阵。我对点亮8x8矩阵这块兴趣较高,所以先拿来研究,由于是java出身,对java熟悉,而且听说过有个叫pi4j的东东,本着想做个自定义报时钟,需要音乐播放器,同时希望显示播放音乐的电平特效,本人之前用java实现过,所以首当其冲选用java。开始设想过用golang,但是golang除了服务外的其他方面太弱,python研究成本较高,因为我对它不是很熟悉。

言归正传,一开始在查阅pi4j时就遇到了一些阻力,因为有人说最新的pi4j v-1对树莓派4支持的不够友好,看了官网确实最新版仅支持3b+,pi4j v-2正在开发,为支持JDK11和其他一些新功能,当然4b也在其中,我也一直担心不兼容问题,不过经过测试,对于本例而言这些担心是多余的。之前看了很多博客,没有一篇是完整的从接线到代码,零零散散,我甚至不知道如何接线,这次把这些详细的都记下来,方便不时之需。

注:向硬件发送数据部分代码是借鉴了其他博客以及卖家给的python代码,照抄,具体含义不清楚,待研究,这篇博客的目的不是理解原理,而是实现目的。

说明:本篇仅介绍了基于GNIO最基础操作,8x8LED操作,可以在控制台上查看模拟效果

准备材料

 

  • 树莓派4b
  • 5根母对母杜邦线
  • 一个MAX7219 LED 8x8矩阵(其他类型的比如74HC595啥的自行研究吧,不清楚)

准备软件

  • JDK 8
  • XShell(为Windows准备,Mac或Linux自带终端就够了)
  • 一个可编辑java的工具(txt记事本/node++/vs code/netbeans/eclipse/idea随便)

首先使用XShell连接上树莓派(这个有屏无屏连接方式有很多,百度即可),推荐安装lrzsz命令,方便上传和下载,比linux自带的scp舒服,mac用lrzsz需要费点力气,或安装ftp服务,XManager可图形化管理。然后输入gpio readall命令,你会看到:

java led显示屏 java控制led屏_树莓派

留下来备用,拿起你的LED矩阵,查看针脚,然后做对比。python有GPIO的setModel函数做映射切换,java的我没找到,默认使用的是wringPi编号,所以对比wPi这一列,给出对应关系(方便查找):

  • [LED]CLK -> [NAME]SCLK ->[BCM]11 ->[WPI]14 -> [BOARD] 23
  • [LED]DIN -> [NAME]MOSI -> [BCM]10 ->[WPI]12 -> [BOARD] 19
  • [LED]CS -> [NAME]CE1 -> [BCM]7 -> [WPI]11 -> [BOARD] 26
  • VCC 电源5v+
  • GND或- 对应GND(地线或负极,图上为0v)
  • 其他针脚不用管(如果有的话)

java里真正编程时候用到的也就是WPI这一列的编号了,BCM对应图中的BCM,BOARD对应图中的Physical(物理地址)。拿起树莓派,将针脚接口面对自己,USB/网卡接口朝下(或者说也是朝自己)看针脚,针脚和图中的编号一一对应,右边的第一,二个是电源供电出口,这里我的有一组接了散热风扇,占用了右侧的第二个和第三个口,只剩下第一个口可用,剩下的自行对照,一个一个的数哈,一定要对准了把杜邦线往上插就是了。

然后上代码,从其他博客上和卖家给的代码结合扒过来的代码,这部分代码入口仅提供了一个byte[8]的数组,我这里将数组的下标设定为y坐标,每个数组元素控制着x坐标,也就是说数组第一个元素控制第一行灯的亮与灭,以此类推。

import static com.pi4j.wiringpi.Gpio.*;

/**
 * 脚编号使用wip
 * 使用直接操作,未使用提供的细化api
 * 全局仅一个对象,不可同时操作
 * max7219 单色8x8
 *
 * CLK - > SCLK bcm:11 wpi:14 board:23
 * DIN - > MOSI bcm:10 wpi:12 board:19
 * CS -> CE1 bcm:7 wpi:11 board:26
 * 树莓派4b
 * 参阅com.shisan.pi.tools.PointToSPI,得到sendPoints方法参数
 */
public class Pi4b8x8 {
    private static final int CS = 11,CLK = 14,DIN = 12;
    private static final byte []NONE_POINT = new byte[]{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
    private volatile static boolean hasInit = false;

    /**
     * 该byte数组len不大于8。
     * 从0-7分别代表各个y坐标。
     * @param xs 表示x坐标
     */
    public static void sendPoints(byte []xs){
        if(!hasInit){
            synchronized (Pi4b8x8.class){
                if(!hasInit){
                    init();
                    hasInit = true;
                }
            }
        }
        for (int i = 0; i < xs.length; i++) {
            writeWord(i+1,xs[i]&0xff);
        }
    }

    /**
     * 使用前必须先init
     */
    private static void init(){
        wiringPiSetup();
        pinMode(DIN,OUTPUT);
        pinMode(CS,OUTPUT);
        pinMode(CLK,OUTPUT);
        writeWord(0x09,0x00);
        writeWord(0x0a,0x03);
        writeWord(0x0b,0x07);
        //这个是shutdown命令了,如果需要关闭led设置为 writeWord(0x0c,0x00)
        writeWord(0x0c,0x01);
        //显示测试?应该是要不要区别意义不大吧
        writeWord(0xff,0x00);
    }

    /**
     * 使用完毕后clear(服务退出关闭led)。
     */
    public static void clear(){
        sendPoints(NONE_POINT);
    }

    private static void writeWord(int pin,int num){
        digitalWrite(CS,HIGH);
        digitalWrite(CS,LOW);
        digitalWrite(CLK,LOW);
        sendNum(pin);
        sendNum(num);
        digitalWrite(CS,HIGH);
    }
    private static void sendNum(int n){
        int tmp;
        for (int i = 0; i < 8; i++) {
            tmp = n&0x80;
            if(tmp > 0){
                digitalWrite(DIN,HIGH);
            }else{
                digitalWrite(DIN,LOW);
            }
            n = n <<1;
            digitalWrite(CLK,HIGH);
            digitalWrite(CLK,LOW);
        }
    }
}

在人的世界观里,(x,y)这样的坐标看着更舒服,所以我又封装了一个转换类:

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Shisan
 * @version V1.0.0
 * @Package com
 * @ClassName: Test1
 * @Description:
 * @date 2019年11月22日 10:16
 */
public class PointToSPI {

    public static void main(String[] args) throws Exception{
        PointToSPI t = new PointToSPI();
        List<Point> list = new ArrayList<>();
        list.add(new Point(1,1));
        list.add(new Point(1,2));
        list.add(new Point(2,1));
        list.add(new Point(4,5));
        list.add(new Point(3,3));
        list.add(new Point(0,7));
        list.add(new Point(1,7));
        list.add(new Point(5,5));
        list.stream().forEach(e->t.add((byte)e.getX(),(byte)e.getY()));
        t.remove((byte)1,(byte)1);
        System.out.println(t.toString());
    }

    private byte[] result = new byte[8];
    private byte[] result2;

    /**
     * 加入一个坐标点
     * @param x
     * @param y
     */
    public void add(byte x,byte y){
        if(x > 0x7 || y > 0x7){
            return;
        }
        result2 = null;
        result[y] |= 1 << 7 - x;
    }

    public void remove(byte x,byte y){
        if(x > 0x7 || y > 0x7){
            return;
        }
        result2 = null;
        result[y] &= ~(1 << 7 - x);
    }

    public byte[] get(){
        return result;
    }

    public byte[] get2(){
        if(result2 == null){
            result2 = new byte[result.length];
            for (int i = 0; i < result.length; i++) {
                result2[i] = (byte)~result[i];
            }
        }
        return result2;
    }

    public void clear(){
        for (int i = 0; i < result.length; i++) {
            result[i] = 0;
        }
    }

    @Override
    public String toString(){
        StringBuilder builder = new StringBuilder();
        builder.append("   0 1 2 3 4 5 6 7\n");
        builder.append("  |-|-|-|-|-|-|-|-|\n");
        for (int i = 0; i < result.length; i++) {
            builder.append(i).append(":");
            for (int j = 0; j < 8; j++) {
                builder.append("|").append((1<<7-j&result[i])!=0?"@":" ");
            }
            builder.append("|\n");
        }
        builder.append("  |-|-|-|-|-|-|-|-|");
        return builder.toString();
    }
}

综上所述,代码可写为这样就能完成发送:

public static void main(String args[]){
  PointToSPI p = new PointToSPI();
  //点亮左上角第一个灯
  p.add((byte)0,(byte)0);
  //在控制台查看效果
  System.out.println(p.toString());
  //发送给LED矩阵显示
  Pi4b8x8.sendPoints(p.get());
}

如果你追求效率的话,就不推荐将点拆分为x,y的坐标了,还是合并起来更好。一个8x8 LED矩阵由8个1字节组成的数组组成,在java中,如果想用一个变量就能表示那long类型就再合适不过了,随后我对这个做了升级,摒弃了PointToSPI类。

以下这个类自带了一些效果图

import java.lang.reflect.Field;
import java.util.Random;

public class DefaultFont8x8 {

    /**
     * 笑脸娃娃
     */
    public static final long FACE_HAPPY = 4342214966986228284L;
    public static final long FACE_UNHAPPY = 4342214966785688124L;
    public static final long FACE_NORMAL = 4342214966382248508L;
    public static final long FACE_SHRPRISE = 4342214966784901692L;

    /**
     * 符号
     */
    public static final long SIGN_FALSE = -9132697443079273855L;
    public static final long SIGN_TRUE = 283966729879552L;
    public static final long SIGN_WARN = 1739555223478305279L;
    public static final long SIGN_LOADING = 9097873883144667774L;
    public static final long SIGN_LOADING_FILL = 9114788770027961982L;
    public static final long SIGN_EMBARRASS = -35565062528073217L;
    public static final long SIGN_LOVE = 7393082656524739608L;
    public static final long SIGN_LOVE_FILL = 7421932185898073112L;
    //?
    public static final long SIGN_UNKONW = 2027184998608011272L;
    //#
    public static final long SIGN_PAND = 10271792857752576L;


    /**
     * 数字
     */
    public static final long NUM_ZERO = 2027224744808555036L;
    public static final long NUM_ONE = 583224982331983900L;
    public static final long NUM_TWO = 2027184998608544318L;
    public static final long NUM_THREE = 2027185032866701852L;
    public static final long NUM_FOUR = 291630221331661828L;
    public static final long NUM_FIVE = 9097342148642226748L;
    public static final long NUM_SIX = 4342103893170340412L;
    public static final long NUM_SEVEN = 9079822015070736400L;
    public static final long NUM_EIGHT = 4342105817315689020L;
    public static final long NUM_NINE = 4342105824827671100L;

    /**
     * 双位数字显示
     */
    public static final long NUM_0_RIGHT = 145528082076599554L;
    public static final long NUM_1_LEFT= 4665799858655871200L;
    public static final long NUM_1_RIGHT = 145806245582995975L;
    public static final long NUM_2_LEFT = 4656757337594306784L;
    public static final long NUM_2_RIGHT = 145523666799822087L;
    public static final long NUM_3_LEFT = 4656757474490097728L;
    public static final long NUM_3_RIGHT = 145523671077815554L;
    public static final long NUM_4_LEFT = 2333041218733219872L;
    public static final long NUM_4_RIGHT = 145815076170826242L;
    public static final long NUM_5_LEFT = -2269672649533677376L;
    public static final long NUM_5_RIGHT = 505533482005496070L;
    public static final long NUM_6_LEFT = 4656863579518050368L;
    public static final long NUM_6_RIGHT = 145526986859939074L;
    public static final long NUM_7_LEFT = -2296800349626793856L;
    public static final long NUM_7_RIGHT = 504685741377586180L;
    public static final long NUM_8_LEFT = 4656898214134325312L;
    public static final long NUM_8_RIGHT = 145528069191697666L;
    public static final long NUM_9_LEFT = 4656898349417406528L;
    public static final long NUM_9_RIGHT = 145528073419293954L;


    /**
     * 大写字母
     */
    public static final long UPPER_A = 582127782826426689L;
    public static final long UPPER_B = 8953792110620983932L;
    public static final long UPPER_C = 4342103635438617148L;
    public static final long UPPER_D = 8953791861512880764L;
    public static final long UPPER_E = 9097342149686476926L;
    public static final long UPPER_F = 9097342149686476864L;
    public static final long UPPER_G = 4485656002443756094L;
    public static final long UPPER_H = 4774451665011098178L;
    public static final long UPPER_I = 2019873263463172124L;
    public static final long UPPER_J = 2019873263463180304L;
    public static final long UPPER_K = 2460135483748131362L;
    public static final long UPPER_L = 2314885530818453566L;
    public static final long UPPER_M = -4511668628444591799L;
    public static final long UPPER_N = -4367838175208782266L;
    public static final long UPPER_O = 4342105843085492796L;
    public static final long UPPER_P = 8953791862485827648L;
    public static final long UPPER_Q = 4342105843085623869L;
    public static final long UPPER_R = 8953791871008916034L;
    public static final long UPPER_S = 4342103617214497340L;
    public static final long UPPER_T = 9153575073218037768L;
    public static final long UPPER_U = 4774451407313060412L;
    public static final long UPPER_V = 4702077015947482120L;
    public static final long UPPER_W = 4702156215280079892L;
    public static final long UPPER_X = 4693335786401309249L;
    public static final long UPPER_Y = 4693335786400516104L;
    public static final long UPPER_Z = 9079824231409139838L;

    /**
     * 动态组合
     */
    public static final long[] GIF_CIRCLE_LOADING = new long[]{
            9123619555872702846L,
            9119115964835266942L,
            9116864165055136126L,
            9115738265148424574L,
            9115736066125170046L,
            9115736057535237502L,
            9115736057501687166L,
            9115736057501564286L,
            9115736057505757566L,
            9115736058579497342L,
            9115736333457400190L,
            9115806702201569662L,
            9124813901452116350L,
            9129317500005745022L,
            9131569024941523326L,
            9132624556104188286L,
    };
    public static final long[] GIF_LINE_LOADING = new long[]{
            35608723204439552L,
            35608481746746880L,
            35608361017900544L,
            35608300653477376L,
            35608542111170048L,
            35608662840016384L
    };

    /**
     * 获取SPI点
     * @param points
     * @return
     */
    public static byte[] getPoints(long points){
        byte ps[] = new byte[8];
        for (int i = 0; i < ps.length; i++) {
            ps[i] = (byte)(points >> (7 - i << 3) & 0xff);
        }
        return ps;
    }

    /**
     * 获取所有坐标点
     * @param points
     * @return
     */
    public static long[] getAllSingleOfPoints(long points){
        byte num = getNumOfPoints(points);
        long singleOfPoints[] = new long[num];
        long p;
        for (int i = 0,j =0; i < 64 && j<num; i++) {
            if(((p = 1L << i & points)) != 0){
                singleOfPoints[j] = p;
                j ++;
            }
        }
        return singleOfPoints;
    }

    public static long[] randomSinglePoints(long[] points){
        int r = 0;
        Random random = new Random();
        long newPoints[] = new long[points.length];
        System.arraycopy(points,0,newPoints,0,points.length);
        for (int i = 0; i < newPoints.length; i++) {
            r = random.nextInt(newPoints.length);
            if(newPoints[i] != newPoints[r]){
                newPoints[i] = newPoints[i] ^ newPoints[r];
                newPoints[r] = newPoints[i] ^ newPoints[r];
                newPoints[i] = newPoints[i] ^ newPoints[r];
            }
        }
        return newPoints;
    }

    /**
     * 获取点个数
     * @param points
     * @return
     */
    private static byte getNumOfPoints(long points){
        byte res = 0;
        while(points != 0){
            points = points & (points - 1);
            res ++;
        }
        return res;
    }

    public static void main(String[] args) throws Exception {
        //这里做演示,获取到一个byte[8]数组视为完成发送(伪发送)。
        //可以传递给PointToSPI在控制台上查看效果
        //发送12给LED
        getPoints(NUM_1_LEFT|NUM_2_RIGHT);





        //实现一个动态的碎片化到完整文字的特效
        long[] all = randomSinglePoints(getAllSingleOfPoints(SIGN_FALSE));
        long o = 0L;
        //为了查看效果而引入的。
        PointToSPI p = new PointToSPI();
        //反射获取值
        Field f = ReflectUtil.getFieldByName("result",p.getClass());
        f.setAccessible(true);
        for (int i = 0; i < all.length; i++) {
            byte []v = getPoints(o |= all[i]);
            f.set(p,v);
            //打印查看效果
            System.out.println(p.toString());
            Thread.sleep(100);
        }
        f.setAccessible(false);
    }
}

当然上面那么一大堆的东东不是我一个一个试验的,而是用javafx写了个简陋的8x8造字程序。

java led显示屏 java控制led屏_数组_02

文字平移和滚动

基于上述的一个long代表一个图案,然后将这个图案向左向右平移(上下平移自行实现吧,我这里没这样的需求)。

/**
 * @author Shisan
 * @version V1.0.0
 * @Package com.shisan.pi.clock.simple
 * @ClassName: PointLineTranslationTools
 * @Description:
 * @date 2019年12月23日 15:49
 */
public class PointLineTranslationTools {

    public static long toLeft(long point,byte px){
        if(px > 7){
            return 0;
        }
        point = point << px;
        byte x;
        for (int i = 0; i < 63; i+=8) {
            x = 0;
            for (; x < px ; x++) {
                point &= ~(1L << i+x);
            }
        }
        return point;
    }

    public static long toRight(long point,byte px){
        if(px > 7){
            return 0;
        }
        point = point >>> px;
        byte x;
        for (int i = 7; i < 63; i+=8) {
            x = 0;
            for(;x < px;x++){
                point &= ~(1L << i-x);
            }
        }
        return point;
    }

    /**
     *
     * @param front
     * @param after
     * @param offset 最大值7,最小值-7
     * @param space 间隔,不高于127,不小于0
     * @return
     */
    public static long follow(long front,long after,byte offset,byte space){
        if(space < 0){
            space = 0;
        }
        if(offset < 0){
            if(offset > -8 && front!=0){
                front = toLeft(front,(byte)(~offset+1));
            }else{
                front = 0;
            }
            offset = (byte)(8+offset+space);
            if(offset > 7 || after == 0){
                after = 0;
            }else{
                after = toRight(after,offset);
            }
        }else{
            after = 0;
            if(offset > 8 || front == 0){
                front = 0;
            }else{
                front = toRight(front,offset);
            }
        }
        return front|after;
    }

}

最后结合桢就可以实现平移动画,这里有一个现成的文字从右往左滚动特效:

import com.shisan.pi.tools.DefaultFont8x8;
import com.shisan.pi.tools.PointToSPI;
import com.shisan.pi.tools.ReflectUtil;

import java.lang.reflect.Field;
import java.util.LinkedList;

import static com.shisan.pi.clock.simple.PointLineTranslationTools.*;

/**
 * @author Shisan
 * @version V1.0.0
 * @Package com.shisan.pi.clock.simple
 * @ClassName: HLineAnimation
 * @Description: 横向滚动从左到右
 * @date 2019年12月23日 15:43
 */
public class HLineAnimation {

    private LinkedList<TextTools.TextProperty> datas;

    private volatile TextTools.TextProperty first;

    private volatile TextTools.TextProperty next;

    private volatile TextTools.TextProperty current;

    private volatile int offset;

    private volatile int space;

    private volatile int loopTimes = 0;

    /**
     * 1 - 结束后等全部消失再第二次重复
     * 0 - 自然重复
     */
    private volatile int state;


    public HLineAnimation(LinkedList<TextTools.TextProperty> datas){
        this.datas = datas;
        current = first = datas.poll();
        datas.add(first);
        next = datas.poll();
        datas.add(next);
        offset = 8;
        space = next.getSpace();
    }

    public int getLoopTimes(){
        return loopTimes;
    }

    public void setLastLoopState(int state){
        this.state = state;
    }

    public int getLastLoopState(){
        return this.state;
    }

    public long getNext(){
        long p;
        if(offset > -7){
            if(offset<0 && state > 0 && next == first){
                p = follow(current.getPoints(),0,(byte)offset,(byte)space);
            }else{
                p = follow(current.getPoints(),next.getPoints(),(byte)offset,(byte)space);
            }
            offset --;
        }else{
            offset = space + 1;
            if(next == first){
                loopTimes++;
                if(state > 0){
                    offset = 8;
                }
                System.out.println("结束一遍");
            }
            current = next;
            next = datas.poll();
            datas.add(next);
            space = next.getSpace();
            p = follow(current.getPoints(),next.getPoints(),(byte)offset,(byte)space);
            offset --;
        }
        return p;
    }

    public static void main(String[] args) throws Exception{
        //TextTools是一个文字映射到Default8x8的map,这里可以自行来几个long类型的形状添加进去,这里实现了一个ABCDE5个字母来回滚动效果。
        HLineAnimation h = new HLineAnimation(TextTools.textToPoints("ABCDE"));
        h.setLastLoopState(1);
        PointToSPI p = new PointToSPI();
        Field f = ReflectUtil.getFieldByName("result", p.getClass());
        while(true){
            f.setAccessible(true);
            f.set(p, DefaultFont8x8.getPoints(h.getNext()));
            f.setAccessible(false);
            System.out.println(p.toString());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
    }
}

为了代码的完整性,还是把TextTools贴出来吧:

import com.shisan.pi.tools.DefaultFont8x8;
import lombok.Data;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @author Shisan
 * @version V1.0.0
 * @Package com.shisan.pi.clock.simple
 * @ClassName: TextTools
 * @Description:
 * @date 2019年12月23日 15:06
 */
public class TextTools {

    private static final Map<Character,Long> MAPPING = new HashMap<Character,Long>(){
        {
            put('0', DefaultFont8x8.NUM_ZERO);
            put('1', DefaultFont8x8.NUM_ONE);
            put('2', DefaultFont8x8.NUM_TWO);
            put('3', DefaultFont8x8.NUM_THREE);
            put('4', DefaultFont8x8.NUM_FOUR);
            put('5', DefaultFont8x8.NUM_FIVE);
            put('6', DefaultFont8x8.NUM_SIX);
            put('7', DefaultFont8x8.NUM_SEVEN);
            put('8', DefaultFont8x8.NUM_EIGHT);
            put('9', DefaultFont8x8.NUM_NINE);
            put('A', DefaultFont8x8.UPPER_A);
            put('B', DefaultFont8x8.UPPER_B);
            put('C', DefaultFont8x8.UPPER_C);
            put('D', DefaultFont8x8.UPPER_D);
            put('E', DefaultFont8x8.UPPER_E);
            put('F', DefaultFont8x8.UPPER_F);
            put('G', DefaultFont8x8.UPPER_G);
            put('H', DefaultFont8x8.UPPER_H);
            put('I', DefaultFont8x8.UPPER_I);
            put('J', DefaultFont8x8.UPPER_J);
            put('K', DefaultFont8x8.UPPER_K);
            put('L', DefaultFont8x8.UPPER_L);
            put('M', DefaultFont8x8.UPPER_M);
            put('N', DefaultFont8x8.UPPER_N);
            put('O', DefaultFont8x8.UPPER_O);
            put('P', DefaultFont8x8.UPPER_P);
            put('Q', DefaultFont8x8.UPPER_Q);
            put('R', DefaultFont8x8.UPPER_R);
            put('S', DefaultFont8x8.UPPER_S);
            put('T', DefaultFont8x8.UPPER_T);
            put('U', DefaultFont8x8.UPPER_U);
            put('V', DefaultFont8x8.UPPER_V);
            put('W', DefaultFont8x8.UPPER_W);
            put('X', DefaultFont8x8.UPPER_X);
            put('Y', DefaultFont8x8.UPPER_Y);
            put('Z', DefaultFont8x8.UPPER_Z);
        }
    };

    public static LinkedList<TextProperty> textToPoints(String text){

        char strs[] = text.toUpperCase().toCharArray();
        LinkedList<TextProperty> ps = new LinkedList<>();
        TextProperty p = new TextProperty();
        char c;
        for (int i = 0; i < strs.length; i++) {
            c = strs[i];
            if(c == ' '){
                p.setSpace((byte)(p.getSpace()+1));
            }else{
                p.setPoints(MAPPING.getOrDefault(c,DefaultFont8x8.SIGN_UNKONW));
                ps.add(p);
                p = new TextProperty();
            }
        }
        return ps;
    }

    @Data
    public static class TextProperty{
        private long points;
        private byte space;
    }

    public static void main(String[] args) {
//        LinkedList<TextProperty> ps = textToPoints("a   bc d 你");
//        System.out.println(ps);

    }

}

 

利用Swing和AWT包完成文字提取(java对文字操作我不是很熟,曲线救国)

话说,8x8的提取还是了解了解就好,毕竟像素太低了,提取出来的都不及自己画出来的好看。利用Graphics和BufferedImage类可以轻松实现:

BufferedImage bi = new BufferedImage(size,size,BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g2d = bi.createGraphics();
g2d.setPaint(new Color(0x000000));
g2d.fillRect(0,0,size,size);
//g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(new Color((Integer) getOwnProperty(KEY_COLOR)));
g2d.setFont(new Font((String) getOwnProperty(KEY_FONT_FAMILY), fontBold,fontSize));
g2d.drawString(String.valueOf(text),0, fontSize-1);
for(int i=0;i<bi.getWidth();i++){
   for(int j=0;j<bi.getHeight();j++){
      int rgb = bi.getRGB(i,j);
      if((rgb&0x00ffffff)!=0){
        //i为x坐标,j为y坐标。
      }
   }
}

基于上面的原理,不光是文字,图片也是可以提取的,如果想看效果,就买上N个8x8 led拼起来看吧,对于多个led拼装的代码尚未研究,我手头也没硬件暂不考虑。可以先看下在手机上展示的效果,很有意思,这是我做的一个验证码效果图:

java led显示屏 java控制led屏_java led显示屏_03

java led显示屏 java控制led屏_数组_04

sdn在实际使用中,如果你写了多个java应用一起点亮led是有问题的,我这里使用了netty做了一个统一的服务,每个需要点亮led的都去申请服务授权,每个申请者有都需要表明自己的优先级,优先级高的优先被展示,优先级低的将不再展示,直到优先级高的释放了自己的连接后才会展示优先级低的数据,就类似于桌面上一个窗口遮住了另一个窗口。

好了就写这么多了,把自己研究的核心的内容基本都贴上去了。