偶然心血来潮,想要做一个音乐可视化的系列专题。这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累些有用的音乐频谱可视化的资料,也会争取成型一些实用好玩的音乐可视器项目。
找到二片P6 LED单元板,它们有 512 个明亮的 RGB LED,排列在正面的 16x32 网格中。
室内P6全彩LED显示单元板主要参数
规格像素:32X16
像素密度:27777点/㎡,
点间距: 6mm ;
亮度 ≥1200cd/㎡;
最佳视角(H/V):水平120度,垂直120度;
峰值功率:1000W/㎡;
运行功率:500W/㎡;
盲点率:小于万分之一;
换帧频率: ≥60帧/秒;
工作电压:5V 稳压电源输入,
工作电流: 2A
连续工作时间(小时):≥7×24hrs ,支持连续不间断显示;
平均无故障时间(小时):≥10000;
使用寿命(小时):≥10万;工作环境温度(度):-20~+50 ;
工作环境湿度:10%~90%
单元板尺寸:192 毫米 x 96 毫米 x 12 毫米(7.6 英寸 x 3.8 英寸 x 0.5 英寸)
带 IDC 电缆和电源线的面板重量:250 g
数据逻辑电平输入:5V
面板接口:12 个数字引脚(6 位数据、6 位控制),全彩HUB75接口
扫描速率:1/8
室内显示:150度可视性
显示器是“可链接的” - 将一个输出连接到下一个输入。
要做好这个实验,比较重要的一件事,就是先要了解单元板上的数据接口,初步查到,是全彩HUB75接口
HUB75 是 LED 单元板通信接口之一,国内也有叫全彩 75、直接简称 75 口等等。有一些变体,不过大同小异。
其中A、B、C、D、E是行信号,其中E信号在小于等于16扫时不需要;CLK(有的板也写作SCLK)是时钟信号;LE(有的板也写作LAT)是数据锁存信号);OE是使能信号;R1、G1、B1和R2、G2、B2是两组数据信号。查到的HUB75接口,如下图。
单元板的驱动原理
LED 单元板就是大量单色或三色 LED 的点阵,扫描式驱动,一般是行线共阴、单列单色 LED 共阳,采用专用芯片进行恒流驱动。所谓扫描式驱动,就是每一列的数据线共享、单元板的同一时刻只有一行被选中并点亮,通过快速的扫描加上人眼视觉暂留来实现整屏显示。有些做的比较好的单元板提供内置的余晖、消隐功能,在扫描其他行时之前点亮的行仍会继续点亮、重新扫描当前行时会强制熄灭不应点亮的像素,以降低屏幕的闪烁、提升画面的显示效果。
HUB75 的行选择信号与存储器的地址线运作方式相同,使用多条地址线,用高低电平组合出二进制的各位数字,然后通过译码器将 n 条地址线的组合信号变为 2^n 选 1 的行选择信号。HUB75 接口的多种变体(或曰实现)基本上就是从这里派生。一般情况 HUB75 的插座使用 16P 简易牛角座,下面是接口示意:
注意 9、10、11 三个引脚,名称分别为 A、B、C,这三根线就是 3 位地址线的 A0、A1、A2 三位,单元板的内部实现中,会使用一个 3-8 译码器将这 3 根输入地址线高低电平的二进制组合转化为 8 根输出片选线中其中一根的选中状态。例如当这四位的电平值分别为 H、L、L 时,代表了当前行地址为 001b,所以 1 号地址线(即代表第二行)为选中状态,其他为非选中状态。由于厂商具体内部设计的差异,行选择激活状态可能为高电平,也可能为低电平,所以这里不能确定具体电平的高低,只能确定哪条线处于激活状态。由于同一时刻 8 条行选择中只有一条被激活,即同一时刻有 1/8 的行正在被点亮,所以这种扫描方式被称作 1/8 扫描,行业简称“8 扫”。
变体由此产生。有些单元板的行数不足 8 行,不需要如此多的地址线,所以会把不需要的地址线空置或接地,比如 1/4 扫描的单元板会将 11 号 C 引脚空置,仅使用 2 条地址线。1/32 扫描的单元板会将 12 号、6 号引脚作为 D、E 信号即行地址二进制的第四、五位,使用 5-32 译码器来提供 32 条行地址线来进行更大范围的扫描,此时形成了 HUB75 最常用的变体——HUB75E。
关于LED点阵屏单元板的通信接口——HUB75的坑
因为 HUB75 接口标准缺乏明确规范,导致一些厂商并没有完全按照上面的格式传输数据。就拿我买到的第一块单元板为例,虽然号称 1/32 扫的 HUB75,但它的行选择信号并不是标准的5位地址线,而是将每半场的 32 条行选择线接入了一个串入并出移位寄存器,使用 ABC 三条信号线作为移位寄存器的数据、时钟、锁存信号,相当于直接把上面的简化版行选择直接集成进了单元板,这直接导致了我找到的所有关于 HUB75 的资料全都跑不通。最后我根据芯片型号找到手册、用万用表一个一个焊点试验摸出走线才把这块单元板的驱动原理摸通。询问卖家有没有资料,卖家只说不兼容市面上的控制器(所有卖家都这么说,不知道是不是为了让你买他们家的控制器),完全不提供任何技术支持,这个就是最大的坑——单元板拿到手之前,你永远不知道它到底该怎么用。
参考资料
https://github.com/hzeller/rpi-rgb-led-matrix/issues/956
https://bikerglen.com/projects/lighting/led-panel-1up/
https://www.sparkfun.com/news/2650
可供参考的HUB75接口定义
找到一张P6 32X16单元板的参考电原理图
电源连接器
尽管 LED 是非常高效的光源,但在一个地方获得足够多的 LED,电流确实会增加。单个 32x16或32x32 RGB 矩阵,全倾斜运行(所有像素设置为白色),可能需要近 4 安培的电流!对于64x32矩阵,该数字翻倍。不过,平均而言,显示典型的图形和动画,这些面板将使用更少的... 2A电源通常足以用于单个 32x16 或 32x32 面板,或 4A 用于 64x32 面板。使用额定电流更大的更大电源(例如 10A 电源)没有害处,但切勿 使用电压更高的电源(使用 5V,周期)!在这些面板上,电源连接与数据连接是分开的。让我们从连接 5V 电源开始……
面板具有 Molex 式接头,只需插入随附的电源线,注意正确的极性。如果您的电源线在此电源线的另一端带有铲子,则可以将它们拧入 2.1 毫米接线端子适配器。效果很好!不要让暴露的连接器接触金属……你应该用热缩管或电工胶带覆盖它。
自制HUB75接口的ArduinoUNO扩展板
以Proto Shield 原型扩展板为主,加装羊角P16插座,方便于做实验
做好的是这样,可以直接插在Arduino uno开发板上
HUB75接口扩展板接线
UNO HUB75
D2 R1
D3 G1
D4 B1
D5 R2
D6 G2
D7 B2
D8 CLK
D9 OE
A0 A
A1 B
A2 C
A3 STB
GND GND
点击下载库文件,并安装
https://github.com/adafruit/Adafruit-GFX-Library
https://github.com/adafruit/RGB-matrix-Panel
https://github.com/adafruit/Adafruit_BusIO
【花雕动手做】有趣好玩的音乐可视化系列项目(30)--P6LED单元板
项目程序之一:点亮P6单元板——点与线的简单控制
/*
【花雕动手做】有趣好玩的音乐可视化系列项目(30)--P6LED单元板
项目程序之一:点亮P6单元板——点与线的简单控制
*/
#include <Adafruit_GFX.h> //核心图形库
#include <RGBmatrixPanel.h> //硬件特定库
#define CLK 8 //必须在这个端口上! (在Mega上使用PIN 11)
#define LAT A3
#define OE 9
#define A A0
#define B A1
#define C A2
//最后一个参数='false'禁用双屏障
RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);
void setup() {
matrix.begin(); //矩阵开始(启动)
}
void loop() {
//用红色在0,0处画一个像素
matrix.drawPixel(0, 0, matrix.Color333(7, 0, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
//用绿色在31,0处画一个像素
matrix.drawPixel(31, 0, matrix.Color333(0, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
//用蓝色在31,15处画一个像素
matrix.drawPixel(31, 15, matrix.Color333(0, 0, 7));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
//用白色在0,15处画一个像素
matrix.drawPixel(0, 15, matrix.Color333(5, 5, 5));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
//用四种颜色在单元板四角各画一个像素
matrix.drawPixel(0, 0, matrix.Color333(7, 0, 0));
matrix.drawPixel(31, 0, matrix.Color333(0, 7, 0));
matrix.drawPixel(31, 15, matrix.Color333(0, 0, 7));
matrix.drawPixel(0, 15, matrix.Color333(5, 5, 5));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(7, 0, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(31, 0, 31, 15, matrix.Color333(0, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 15, 31, 15, matrix.Color333(0, 0, 7));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 0, 15, matrix.Color333(5, 5, 5));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(7, 0, 0));
matrix.drawLine(31, 0, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(0, 15, 31, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(0, 0, 0, 15, matrix.Color333(5, 5, 5));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 15, matrix.Color333(4, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(31, 0, 0, 15, matrix.Color333(4, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 15, matrix.Color333(0, 7, 4));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(0, 7, 4));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(7, 0, 0));
matrix.drawLine(31, 0, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(0, 15, 31, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(0, 0, 0, 15, matrix.Color333(5, 5, 5));
matrix.drawLine(0, 0, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(0, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(7, 0, 0));
matrix.drawLine(31, 0, 31, 15, matrix.Color333(7, 0, 0));
matrix.drawLine(0, 15, 31, 15, matrix.Color333(7, 0, 0));
matrix.drawLine(0, 0, 0, 15, matrix.Color333(7, 0, 0));
matrix.drawLine(0, 0, 31, 15, matrix.Color333(7, 0, 0));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(7, 0, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(0, 7, 0));
matrix.drawLine(31, 0, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(0, 15, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(0, 0, 0, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(0, 0, 31, 15, matrix.Color333(0, 7, 0));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(0, 7, 0));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
matrix.drawLine(0, 0, 31, 0, matrix.Color333(0, 0, 7));
matrix.drawLine(31, 0, 31, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(0, 15, 31, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(0, 0, 0, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(0, 0, 31, 15, matrix.Color333(0, 0, 7));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(0, 0, 7));
delay(500);
matrix.fillScreen(matrix.Color333(0, 0, 0));
delay(500);
}
实验场景图
实验场景图 动态图
实验接线图
HUB75接口,是个兼容大部分市面LED单元板的通用型接口,具体接线定义有些不同,比如HUB75B\HUB75E等。
这次自制的扩展板,只是专门匹配Arduino UNO开发板的专用板,由于性能限制,最多只能使用32x16,或者32x32的简单控制。
下一张扩展板,准备尝试搭配ESP32开发板,以适应大部分P5\P4\P3的单元板模组,主要规格可能有32x32\64x32\64x64,还有128x64等。
【花雕动手做】有趣好玩的音乐可视化系列项目(30)--P6LED单元板
项目程序之二:点亮P6单元板——显示图形与文字
/*
【花雕动手做】有趣好玩的音乐可视化系列项目(30)--P6LED单元板
项目程序之二:点亮P6单元板——显示图形与文字
*/
#include <Adafruit_GFX.h>
#include <RGBmatrixPanel.h>
#define CLK 8
#define LAT A3
#define OE 9
#define A A0
#define B A1
#define C A2
RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);
void setup() {
matrix.begin();
}
void loop() {
// draw a pixel in solid white
matrix.drawPixel(0, 0, matrix.Color333(7, 7, 7));
delay(500);
// fix the screen with green
matrix.fillRect(0, 0, 32, 16, matrix.Color333(0, 7, 0));
delay(500);
// draw a box in yellow
matrix.drawRect(0, 0, 32, 16, matrix.Color333(7, 7, 0));
delay(500);
// draw an 'X' in red
matrix.drawLine(0, 0, 31, 15, matrix.Color333(7, 0, 0));
matrix.drawLine(31, 0, 0, 15, matrix.Color333(7, 0, 0));
delay(500);
// draw a blue circle
matrix.drawCircle(7, 7, 7, matrix.Color333(0, 0, 7));
delay(500);
// fill a violet circle
matrix.fillCircle(23, 7, 7, matrix.Color333(7, 0, 7));
delay(500);
// fill the screen with 'black'
matrix.fillScreen(matrix.Color333(0, 0, 0));
// draw some text!
matrix.setCursor(1, 0);
matrix.setTextSize(1);
// print each letter with a rainbow color
matrix.setTextColor(matrix.Color333(7, 0, 0));
matrix.print('1');
matrix.setTextColor(matrix.Color333(7, 4, 0));
matrix.print('6');
matrix.setTextColor(matrix.Color333(7, 7, 0));
matrix.print('x');
matrix.setTextColor(matrix.Color333(4, 7, 0));
matrix.print('3');
matrix.setTextColor(matrix.Color333(0, 7, 0));
matrix.print('2');
matrix.setCursor(1, 9); // next line
matrix.setTextColor(matrix.Color333(0, 7, 7));
matrix.print('*');
matrix.setTextColor(matrix.Color333(0, 4, 7));
matrix.print('R');
matrix.setTextColor(matrix.Color333(0, 0, 7));
matrix.print('G');
matrix.setTextColor(matrix.Color333(4, 0, 7));
matrix.print('B');
matrix.setTextColor(matrix.Color333(7, 0, 4));
matrix.print("*");
delay(5000);
}
实验场景图
实验场景图 动态图
LED模组会轮流显示:一个白点、全屏绿色、全屏黄色、一个黄色的矩形框、一个红色的X、一个蓝色的圆、一个填充紫圆、英文字。
待续
花生编程2023.01.25
赞
花生编程2023.01.25
厉害
三春牛-创客2023.01.05
做这么多了啊
三春牛-创客2023.01.05
厉害厉害
auzn2022.11.18
佩服 ,比自己做PCB 板 买LED贴 便宜太多了
驴友花雕2022.12.04
是在咸鱼上淘的二手货