【花雕动手做】看见声音,基于Arduino系列音乐可视器(32)
2022-11-222.7万
AI 快速预览详细
本文介绍了基于Arduino的音乐频谱可视器项目。从最简单的开始,逐步积累音乐频谱可视化的资料,涉及FFT和FHT等复杂算法。通过实际动手试验,形成实用好玩的音乐可视器项目。适合STEM教育和创客爱好者。
偶然心血来潮,想要做一个音乐可视化的系列专题。这个专题的难度有点高,涉及面也比较广泛,相关的FFT和FHT等算法也相当复杂,不过还是打算从最简单的开始,实际动手做做试验,耐心尝试一下各种方案,逐步积累些有用的音乐频谱可视化的资料,也会争取成型一些实用好玩的音乐可视器项目。![]() ![]() |






[align=left]https://v.youku.com/v_show/id_XNTg3MTM3MTYxNg==.html?spm=a2hcb.playlsit.page.1[/align]
[align=left][media=x,500,375]https://v.youku.com/v_show/id_XNTg3MTM3MTYxNg==.html?spm=a2hcb.playlsit.page.1[/media][/align]
项目程序之二:Freetronics DMD库的综合测试
[code]/*
【花雕动手做】有趣好玩的音乐可视化系列项目(31)--LCD1602液晶屏
项目程序之二:Freetronics DMD库的综合测试
*/
#include
#include
#include
#include "SystemFont5x7.h"
#include "Arial_black_16.h"
//以 dmd 的身份启动 DMD 库
#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1
DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
/*-------------------------------------------------------------------
Timer1 (TimerOne) 驱动的 DMD 刷新扫描的中断处理程序,这得到
在 Timer1.initialize() 中设置的周期调用;
-------------------------------------------------------------------*/
void ScanDMD() {
dmd.scanDisplayBySPI();
}
/*-------------------------------------------------------------------
设置
在主循环开始之前由 Arduino 架构调用
-------------------------------------------------------------------*/
void setup(void) {
//初始化TimerOne用于扫描和刷新显示的中断/CPU使用率
Timer1.initialize( 5000 ); //调用 ScanDMD 的周期(以微秒为单位)。任何超过 5000 (5ms) 的时间都可以看到闪烁。
Timer1.attachInterrupt( ScanDMD ); //将 Timer1 中断附加到 ScanDMD 到 dmd.scanDisplayBySPI()
//清除/初始化内存中的DMD像素
dmd.clearScreen( true ); //true 为正常(所有像素关闭),false 为负(所有像素打开)
}
/*-------------------------------------------------------------------
环形
Arduino架构主循环
-------------------------------------------------------------------*/
void loop(void) {
byte b;
// 10 x 14 字体时钟,包括OR 和 NOR 模式演示,以便可以覆盖闪烁的冒号
dmd.clearScreen( true );
dmd.selectFont(Arial_Black_16);
dmd.drawChar( 0, 3, '2', GRAPHICS_NORMAL );
dmd.drawChar( 7, 3, '3', GRAPHICS_NORMAL );
dmd.drawChar( 17, 3, '4', GRAPHICS_NORMAL );
dmd.drawChar( 25, 3, '5', GRAPHICS_NORMAL );
dmd.drawChar( 15, 3, ':', GRAPHICS_OR ); //时钟冒号覆盖
delay( 1000 );
dmd.drawChar( 15, 3, ':', GRAPHICS_NOR ); //时钟冒号覆盖关闭
delay( 1000 );
dmd.drawChar( 15, 3, ':', GRAPHICS_OR ); //时钟冒号覆盖
delay( 1000 );
dmd.drawChar( 15, 3, ':', GRAPHICS_NOR ); //时钟冒号覆盖关闭
delay( 1000 );
dmd.drawChar( 15, 3, ':', GRAPHICS_OR ); //时钟冒号覆盖
delay( 1000 );
//滚动文本
dmd.drawMarquee("Scrolling Text", 14, (32 * DISPLAYS_ACROSS) - 1, 0);
long start = millis();
long timer = start;
boolean ret = false;
while (!ret) {
if ((timer + 30) < millis()) {
ret = dmd.stepMarquee(-1, 0);
timer = millis();
}
}
// 一半像素
dmd.drawTestPattern( PATTERN_ALT_0 );
delay( 1000 );
// 另一半打开
dmd.drawTestPattern( PATTERN_ALT_1 );
delay( 1000 );
// 显示一些文本
dmd.clearScreen( true );
dmd.selectFont(System5x7);
for (byte x = 0; x < DISPLAYS_ACROSS; x++) {
for (byte y = 0; y < DISPLAYS_DOWN; y++) {
dmd.drawString( 2 + (32 * x), 1 + (16 * y), "freet", 5, GRAPHICS_NORMAL );
dmd.drawString( 2 + (32 * x), 9 + (16 * y), "ronic", 5, GRAPHICS_NORMAL );
}
}
delay( 2000 );
// 在显示器外面画一个边框
dmd.clearScreen( true );
dmd.drawBox( 0, 0, (32 * DISPLAYS_ACROSS) - 1, (16 * DISPLAYS_DOWN) - 1, GRAPHICS_NORMAL );
delay( 1000 );
for (byte y = 0; y < DISPLAYS_DOWN; y++) {
for (byte x = 0; x < DISPLAYS_ACROSS; x++) {
// draw an X
int ix = 32 * x;
int iy = 16 * y;
dmd.drawLine( 0 + ix, 0 + iy, 11 + ix, 15 + iy, GRAPHICS_NORMAL );
dmd.drawLine( 0 + ix, 15 + iy, 11 + ix, 0 + iy, GRAPHICS_NORMAL );
delay( 1000 );
//画一个圆
dmd.drawCircle( 16 + ix, 8 + iy, 5, GRAPHICS_NORMAL );
delay( 1000 );
// 绘制一个填充框
dmd.drawFilledBox( 24 + ix, 3 + iy, 29 + ix, 13 + iy, GRAPHICS_NORMAL );
delay( 1000 );
}
}
// 条纹追逐者
for ( b = 0 ; b < 20 ; b++ )
{
dmd.drawTestPattern( (b & 1) + PATTERN_STRIPE_0 );
delay( 200 );
}
delay( 200 );
}[/code]
[align=left][font=Tahoma, "][font=Lato][size=1]包括所有必需的字体库,在我们的例子中,我们使用“Arial Black 字体”进行显示。[/font][/size][/font][/align][align=left][font=Tahoma, "][font=Lato][size=1]
[/font][/size][/font][/align][align=left][font=Tahoma, "][backcolor=rgb(247, 247, 247)]#include
[align=left][font=Tahoma, "][font=Lato][size=1]在下一步中,定义 LED 显示板的行数和列数。在我们的例子中,我们只使用一个模块,因此 ROW 值和 COLUMN 值将为 1。然后为显示板上滚动的文本定义字体名称 - Arial_Black_16 。[/font][/size][/font][/align][font=Tahoma, "]#define ROW 1 [/font][font=Tahoma, "][size=1]#define COLUMN 1 [/size][/font]
[font=Tahoma, "][size=1]#define FONT Arial_Black_16 DMD led_module(ROW, COLUMN);[/size][/font][font=Tahoma, "][size=1]
[align=left][font=Lato]函数scan_module()通过 SPI 终端检查来自 Arduino 端的任何传入数据。如果是,那么它将触发一个中断引脚来执行某些事件。[/font][/align][align=left][font=Lato]
[/font][/align]void scan_module() {
led_module.scanDisplayBySPI();
}
[align=left][font=Lato]在setup()中,初始化定时器并将中断附加到函数scan_module。函数clearScreen(true)用于设置所有像素最初都关闭以清除显示板。[/font][/align][align=left][font=Lato]
[/font][/align]void setup() {
Timer1.initialize(2000);
Timer1.attachInterrupt(scan_module);
led_module.clearScreen( true );
}
[align=left][font=Lato]然后,要在模块中显示字符串,请使用 selectFont() 函数选择字体并使用drawMarquee()函数在显示屏上打印字符串消息[/font][/align][align=left][font=Lato] 。[/font][/align][align=left][font=Lato]
[/font][/align]led_module.selectFont(FONT);
led_module.drawMarquee("Hello World 1234567890", 25, (32 * ROW), 0);
[align=left][font=Lato]最后,要滚动 LED 显示板上的文本,请使用一定的时间段将整个消息从右向左移动。[/font][/align][align=left][font=Lato]
[/font][/align]long start = millis();
long timming = start;
boolean flag = false;
while (!flag)
{
if ((timming + 20) < millis())
{
flag = led_module.stepMarquee(-1, 0);
timming = millis();
}
}
[/size][/font]
优酷:https://v.youku.com/v_show/id_XNTg3MjI2MjE0MA==.html?spm=a2hcb.playlsit.page.1
[media=x,500,375]https://v.youku.com/v_show/id_XNTg3MjI2MjE0MA==.html?spm=a2hcb.playlsit.page.1[/media]
[backcolor=rgb(255, 255, 255)][font=宋体]
[/font][/backcolor]
项目程序之一:点亮屏幕滚动显示“Hello World 1234567890”
[code]/*
【花雕动手做】有趣好玩的音乐可视化系列项目(31)--LCD1602液晶屏
项目程序之一:点亮屏幕滚动显示“Hello World 1234567890”
*/
#include
#include
#include
#include "SystemFont5x7.h"
#include "Arial_black_16.h"
#define ROW 1
#define COLUMN 1
#define FONT Arial_Black_16//字体16
DMD led_module(ROW, COLUMN);//行、列
void scan_module() {
led_module.scanDisplayBySPI();//模块扫描显示 By SPI
}
void setup() {
Timer1.initialize(2000);
Timer1.attachInterrupt(scan_module);
led_module.clearScreen( true );
}
void loop() {
led_module.selectFont(FONT);//设置字体
led_module.drawMarquee("Hello World 1234567890", 25, (32 * ROW), 0);
long start = millis();
long timming = start;
boolean flag = false;
while (!flag)
{
if ((timming + 20) < millis())
{
flag = led_module.stepMarquee(-1, 0);
timming = millis();
delay(50);//通过延时时间来调整字幕滚动快慢
}
}
}[/code]
DMD.h 下载链接:https://github.com/freetronics/DMD
TimerOne.h 下载链接:https://github.com/PaulStoffregen/TimerOne
[align=left][font=Tahoma, "]以Proto Shield [font=宋体]原型[/font][font=宋体]扩展[/font][font=宋体]板[/font][font=宋体]为主,加装二只[/font][font=Calibri]P16[/font][font=宋体]插座,便于做实验[/font][/font][/align][align=left][font=Tahoma, "][font=宋体]
[/font][/font][/align][align=left][font=Tahoma, "]
[/font][/align]
P10显示模块的特点:
¼ duty scan type [模块上四分之一的LED由IC一次驱动]
亮度:3500nits 至 4500nits
最大功耗:20W
DC 5V电压输入
IP65防水
高对比度和视角
1W像素配置
P10显示模块规格:
输入电压:5V
最大电流输入:4A
平均功耗:8W-10W
颜色:红色
像素间距:10mm
LED像素尺寸:32X16 = 512
[list]
[*]EN:此引脚用于通过向其提供 PWM 脉冲来控制 LED 面板的亮度。
[*]A、B:这些称为多路复用选择引脚。他们采用数字输入来选择任何多路复用行
[*]移位时钟 (CLK)、存储时钟 (SCLK):这些是常规移位寄存器控制引脚
[*]DATA:此引脚用于 PWM 形式的数据输入。
[/list]