前几天刚在某宝上买了个树莓派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代码,照抄,具体含义不清楚,待研究,这篇博客的目的不是理解原理,而是实现目的。

补充:github上还是有好东西的,还是自己太懒,CSDN上也有好几篇关于java的操作博客了:https://github.com/sharetop/max7219-java

说明:本篇仅介绍了基于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命令,你会看到:

留下来备用,拿起你的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;}}@Overridepublic 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给LEDgetPoints(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造字程序。

文字平移和滚动

基于上述的一个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;}@Datapublic 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是有问题的,我这里使用了netty做了一个统一的服务,每个需要点亮led的都去申请服务授权,每个申请者有都需要表明自己的优先级,优先级高的优先被展示,优先级低的将不再展示,直到优先级高的释放了自己的连接后才会展示优先级低的数据,就类似于桌面上一个窗口遮住了另一个窗口。

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

Java控制树莓派8x8矩阵LED相关推荐

  1. android客户端控制树莓派GPIO点亮LED灯

    首先需要android客户端与树莓派进行连接,树莓派与android客户端利用wifi连接并进行socket通信请参考我的另一片文章:https://mp.csdn.net/postedit/7991 ...

  2. 基于8086简易电子琴系统仿真设计-基于8086红外自动门控制系统设计-基于单片机8x8矩阵键盘两机串口通信-基于单片机8层电梯控制系统仿真设计-基于单片机ADC0809和DAC0832模数和数模设计

    1189基于8086简易电子琴系统仿真设计-设计资料 编写一实现电子琴的程序,并实现若干扩展功能. 基本功能:用键盘输入对应的七个音阶,通过实验箱的喇叭发出声音,并通过七段数码管显示输入音阶: 扩展功 ...

  3. java gpio_Java控制树莓派GPIO口-Pi4J

    Pi4J(http://pi4j.com/)是专门用来控制树莓派GPIO口以及通信的Java库.它有如下功能: Export & unexport GPIO pins Configure GP ...

  4. 树莓派c语言led,用树莓派实现RGB LED的颜色控制——C语言版本号

    用树莓派实现RGB LED的颜色控制 RGB色彩模式是工业界的一种颜色标准.是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代 表红.绿. ...

  5. python rgb led控件_Raspberry Pi-用树莓派实现RGB LED的颜色控制——Python版本-电路城论坛 - 电子工程师学习交流园地...

    用树莓派实现RGB LED的颜色控制 RGB色彩模式是工业界的一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代 表红.绿. ...

  6. 微信小程序 阿里云服务器 非物联网平台自建MQTT代理服务器控制树莓派LED

    微信小程序 阿里云服务器 非物联网平台自建MQTT代理服务器控制树莓派LED 本人大三,临近毕业季,日后希望从事物联网和嵌入式相关工作,所以自己构想了一个项目来练手,之前做大创的时候学了一点微信小程序 ...

  7. 树莓派java 控制摄像头_【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件...

    已更新JavaCV1.5.3版本:https://blog.csdn.net/ap114/article/details/105531743 做毕设要用到摄像头,可是树莓派上的摄像头就那么几种,jmf ...

  8. Arduino智能闹钟设计(8x8矩阵键盘+LCD显示)

    Arduino智能闹钟设计(8x8矩阵键盘+LCD显示+蜂鸣器歌曲) 作者:STCode(公众号同名) 1.效果描述: 本设计基于Arduino Nano控制器,主要功能为定时闹钟和环境温湿度显示,使 ...

  9. python控制摄像头拍照_microbit使用蓝牙控制树莓派摄像头拍照

    1.项目概览 本文旨在实现这样一个功能:用MicroBit配合DFRobot出品的gamePad拓展板通过蓝牙控制树莓派上的摄像头进行拍摄,并可以通过gamePad方向键控制云台.主要涉及的知识点包括 ...

最新文章

  1. sqlrelay mysql_数据库连接池SQL Relay安装使用-Java架构师必看
  2. php 写入文件 格式,在使用php写入文件时如何保证用户的单一
  3. 06 Jquery 基础
  4. 解决Ubuntu上的phpMyAdmin 404 错误
  5. 通俗易懂,java8 .stream().map().collect()用法
  6. java怎样获取线程的进度_java中的多线程——进度2
  7. c++服务器笔试题编程_C++服务器开发面试题
  8. 一款好用且开源的图像处理软件----imageJ
  9. 地铁票务管理系统_[地铁票务管理论文] 地铁票务系统 场景法
  10. android 输入法判断,Android如何检测输入法键盘是否显示
  11. 实现发送xml格式的请求
  12. 干货 | 携程Dynamo风格存储的落地实践
  13. 直播系统开发之ios对接腾讯云直播sdk
  14. 螺旋线java_java怎么画布画阿基米螺旋线
  15. python pip 豆瓣镜像
  16. SLAM视觉里程计的理解
  17. 本地配置多个git账户(公司、GitHub)
  18. 网络钓鱼攻击类型,载体及其技术途径
  19. 淘宝的字体也改变了(今天)
  20. 3,uniapp功能之—蓝牙秤,连接蓝牙秤获取重量实时显示在页面上(坤宏的蓝牙秤)

热门文章

  1. 常用的一些LDO芯片及使用
  2. python--len函数的用法
  3. 2022浙江最新八大员之(安全员)模拟考试试题及答案
  4. Unity 相机的移动旋转以及拉近拉远的原理
  5. C#入门学习-----图书阅读器(WPF 用户控件技术)
  6. TJISE-APP 自动签到打卡
  7. 浅谈IC卡密码的破解方法
  8. Android(小米miui)如何判断当前应用是否允许NFC权限
  9. 继续魔改TCP BBR
  10. html5水涟漪动画,CSS3水波涟漪动画定位样式制作教程