干货|有趣好玩的音乐可视化系列小项目:OLED频谱灯
▲ 更多精彩活动 请点击上方蓝字关注我们吧!
OLED(Organic Light-Emitting Diode)
即有机发光二极管,在手机OLED上属于新型产品,被称誉为“梦幻显示器”。OLED显示技术与传统的LCD显示方式不同,无需背光灯,采用非常薄的有机材料涂层和玻璃基板(或柔性有机基板),当有电流通过时,这些有机材料就会发光。而且OLED显示屏幕可以做得更轻更薄,可视角度更大,并且能够显著的节省耗电量。
OLED技术特点
(1) OLED 器件的核心层厚度很薄,厚度可以小于 1mm,为液晶的 1/3。
(2) OLED 器件为全固态机构,无真空,液体物质,抗震性好,可以适应巨大的加速度,振动等恶劣环境。
(3) 主动发光的特性使 OLED 几乎没有视角限制,视角一般可达到 170 度,具有较宽的视角,从侧面也不会失真。
(4) OLED 显示屏的响应时间超过 TFT—LCD 液晶屏。TFT—LCD 的响应时间大约使几十毫秒,现在做得最好的 TFT—LCD 响应时间也只有 12 毫秒。而 OLED 显示屏的响应时间大约是几微秒到几十微秒。
(5) OLED 低温特性好,在零下 40 摄氏度都能正常显示,目前航天服上也使用OLED 作为显示屏。而 TFT—LCD 的响应速度随温度发生变化,低温下,其响应速度变慢,因此,液晶在低温下显示效果不好。
(6) OLED 采用有机发光原理,所需材料很少,制作上比采用液体发光的液晶工序少,液晶显示屏少 3 道工序,成本大幅降低。
(7) OLED 采用的二极管会自行发光,因此不需要背面光源,发光转化效率高,能耗比液晶低,OLED 能够在不同材质的基板上制造,厂家甚至可以将电路印刷在弹性材料上——做成能弯曲的柔软显示器。
(8) 低电压直流驱动,5V 以下,用电池就能点亮。高亮度,可达 300 明流以上。
主要的实验材料
MAX9814麦克风放大器模块
MAX9814是一款低成本高性能麦克风放大器,具有自动增益控制(AGC)和低噪声麦克风偏置。器件具有低噪声前端放大器、可变增益放大(VGA)、输出放大器、麦克风偏置电压发生器和AGC控制电路。
●自动增益控制(AGC)
●3种增益设置(40dB、50dB、60dB)
●可编程动作时间
●可编程动作和释放时间比
●电源电压范围2.7V~5.5V
●低THD:0.04% (典型值)
●低功耗关断模式
●内置2V低噪声麦克风偏置
MAX9814麦克风放大器模块电原理图
0.91寸OLED液晶屏显示模块参数
驱动芯片:SSD1306
支持接口:I2C
显示颜色:白色
高分辨率:128×32
可视角度:大于160°
工作电压:3.3V / 5V
模块大小:36 x 12.5(mm)
0.96寸OLED模块主要参数
电压:3V~5V DC
工作温度:-30℃~70℃
驾驶义务:1/64职责
高分辨率:128 * 64
面板尺寸:26.70 * 19.26 * 1.85mm / 1.03 * 0.76 * 0.07英寸(约)
有效面积:21.74 * 11.2mm /0.86*0.44英寸(约)
驱动IC:SSD1306
128 * 64 LED显示模块,支持多种控制芯片。
完全兼容51系列,MSP430系列,STM32 / 2,CSR IC等
超低功耗:全屏点亮0.08W
超高亮度和对比度可调
带嵌入式驱动/控制器
接口类型为IIC
音乐可视化系列小项目:OLED频谱灯
项目之一:使用MAX9814声音模块测试环境音乐的动态波形
实验开源代码:
/*
【花雕动手做】音乐可视化系列小项目(02)---OLED频谱灯
项目之一:使用MAX9814声音模块测试环境音乐的动态波形
实验接线:
MAX9814 Arduino
VCC 5V
GND GND
OUT A0
*/
const int sampleWindow = 50; // 以mS为单位的采样窗口宽度(50 mS = 20Hz)
unsigned int sample;
void setup() {
Serial.begin(9600);
pinMode(A0, INPUT);
}
void loop() {
unsigned long startMillis = millis(); // 样本窗口的开始
unsigned int peakToPeak = 0; // 峰峰值
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
// collect data for 50 mS
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(A0);
if (sample < 1024) // 抛出错误的读数
{
if (sample > signalMax)
{
signalMax = sample; // 只保存最大级别
}
else if (sample < signalMin)
{
signalMin = sample; // 仅保存最低级别
}
}
}
peakToPeak = signalMax - signalMin; // max-min =峰峰值幅度
double volts = (peakToPeak * 5.0) / 166;
Serial.println(volts);
}
实验串口返回情况
打开IDE的串口绘图器
实验场景图
实验场景图 动态图
音乐可视化系列小项目:OLED频谱灯
项目之二:0.91寸OLED液晶屏声音可视化频谱灯
实验开源代码:
/*
【花雕动手做】音乐可视化系列小项目(02)---OLED频谱灯
项目之二:0.91寸OLED液晶屏声音可视化频谱灯
实验接线: max9814接A0
oled模块 Ardunio Uno
GND---------GND接地线
VCC---------5V 接电源
SDA---------A4
SCL ------- A5
*/
#include "arduinoFFT.h"
#include
#include
#define SAMPLES 64 // power of 2
#define SAMPLING_FREQ 8000 // 12 kHz Fmax = sampleF /2
#define AMPLITUDE 100 // 灵敏度
#define FREQUENCY_BANDS 14
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define BARWIDTH 11
#define BARS 11
#define ANALOG_PIN A0
#define OLED_RESET -1 // 重置引脚 #(如果共享 Arduino 重置引脚,则为 -1)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
double vImag[SAMPLES];
double vReal[SAMPLES];
unsigned long sampling_period_us;
arduinoFFT fft = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
//调整参考以去除背景噪声
float reference = log10(60.0);
double coutoffFrequencies[FREQUENCY_BANDS];
void setup() {
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
for (;;); // Don't proceed, loop forever
}
// Setup display
display.clearDisplay();
display.display();
display.setRotation(0);
display.invertDisplay(false);
sampling_period_us = (1.0 / SAMPLING_FREQ ) * pow(10.0, 6);
// 计算截止频率,以对数标度为基数 POt
double basePot = pow(SAMPLING_FREQ / 2.0, 1.0 / FREQUENCY_BANDS);
coutoffFrequencies[0] = basePot;
for (int i = 1 ; i < FREQUENCY_BANDS; i++ ) {
coutoffFrequencies = basePot * coutoffFrequencies[i - 1];
}
// 绘制虚线以分离频段
for (int i = 0; i < BARS - 1 ; i++) {
for (int j = 0; j < SCREEN_HEIGHT ; j += 4) {
display.writePixel((i + 1)*BARWIDTH + 2 , j, SSD1306_WHITE );
}
}
display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
}
int oldHeight[20];
int oldMax[20];
double maxInFreq;
void loop() {
// 采样
for (int i = 0; i < SAMPLES; i++) {
unsigned long newTime = micros();
int value = analogRead(ANALOG_PIN);
vReal = value;
vImag = 0;
while (micros() < (newTime + sampling_period_us)) {
yield();
}
}
// 计算 FFT
fft.DCRemoval();
fft.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
fft.Compute(FFT_FORWARD);
fft.ComplexToMagnitude();
double median[20];
double max[20];
int index = 0;
double hzPerSample = (1.0 * SAMPLING_FREQ) / SAMPLES; //
double hz = 0;
double maxinband = 0;
double sum = 0;
int count = 0;
for (int i = 2; i < (SAMPLES / 2) ; i++) {
count++;
sum += vReal;
if (vReal > max[index] ) {
max[index] = vReal;
}
if (hz > coutoffFrequencies[index]) {
median[index] = sum / count;
sum = 0.0;
count = 0;
index++;
max[index] = 0;
median[index] = 0;
}
hz += hzPerSample;
}
// 计算每个频段的中值和最大值
if ( sum > 0.0) {
median[index] = sum / count;
if (median[index] > maxinband) {
maxinband = median[index];
}
}
int bar = 0;
for (int i = FREQUENCY_BANDS - 1; i >= 3; i--) {
int newHeight = 0;
int newMax = 0;
// 计算实际分贝
if (median > 0 && max > 0 ) {
newHeight = 20.0 * (log10(median ) - reference);
newMax = 20.0 * (log10(max ) - reference);
}
// 调整最小和最大级别
if (newHeight < 0 || newMax < 0) {
newHeight = 1;
newMax = 1;
}
if (newHeight >= SCREEN_HEIGHT - 2) {
newHeight = SCREEN_HEIGHT - 3;
}
if (newMax >= SCREEN_HEIGHT - 2) {
newMax = SCREEN_HEIGHT - 3;
}
int barX = bar * BARWIDTH + 5;
// 删除旧水平中位数
if (oldHeight > newHeight) {
display.fillRect(barX, newHeight + 1, 7, oldHeight, SSD1306_BLACK);
}
// 删除旧的最大级别
if ( oldMax > newHeight) {
for (int j = oldMax; j > newHeight; j -= 2) {
display.drawFastHLine(barX , j, 7, SSD1306_BLACK);
}
}
// 绘制新的最大级别
for (int j = newMax; j > newHeight; j -= 2) {
display.drawFastHLine(barX , j, 7, SSD1306_WHITE);
}
// 绘制新的级别中位数
display.fillRect(barX , 1, 7, newHeight, SSD1306_WHITE);
oldMax = newMax;
oldHeight = newHeight;
bar++;
}
display.drawFastHLine(0 , SCREEN_HEIGHT - 1, SCREEN_WIDTH, SSD1306_WHITE);
display.display();
}
实验场景图
实验场景图 动态图
实验场景图
实验场景图 动态图
音乐可视化系列小项目:OLED频谱灯
项目之三:32段分频0.91寸OLED液晶可视化细条频谱灯
实验开源代码:
/*
【花雕动手做】音乐可视化系列小项目(02)---OLED频谱灯
项目之三:32段分频0.91寸OLED液晶可视化细条频谱灯
实验接线: max9814接A0
oled模块 Ardunio Uno
GND---------GND接地线
VCC---------5V 接电源
SDA---------A4
SCL ------- A5
*/
#include
#include
#include
// These are user-adjustable
#define LOG_OUTPUT // Uncomment to enable logarithmic output (exchanges absolute resoluton for more readable output; may require different below params)
#define SAMPLING_FREQUENCY 15000 // Sampling frequency (Actual max measured frequency captured is half)
#define TIME_FACTOR 2 // Smoothing factor (lower is more dynamic, higher is smoother) ranging from 1 to 10+
#define SCALE_FACTOR 15 // Direct scaling factor (raise for higher bars, lower for shorter bars)
#ifdef LOG_OUTPUT
const float log_scale = 64. / log(64. / SCALE_FACTOR + 1.); // Attempts to create an equivalent to SCALE_FACTOR for log function
#endif
const float coeff = 1. / TIME_FACTOR; // Time smoothing coefficients (used to factor in previous data)
const float anti_coeff = (TIME_FACTOR - 1.) / TIME_FACTOR;
const unsigned int sampling_period_us = round(1000000 * (2.0 / SAMPLING_FREQUENCY)); // Sampling period (doubled to account for overclock)
int8_t data[64], buff[32]; // used to store FFT input/output and past data
unsigned long microseconds; // used for timekeeping
int summ, avg; // used for DC bias elimination
NanoEngine engine; // declares nanoengine
void setup()
{
OSCCAL = 240; // Overclocks the MCU to around 30 MHz, set lower if this causes instability, raise if you can/want
ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear ADC prescaler bits
ADCSRA |= bit (ADPS2); // sets ADC clock in excess of 10kHz
ADCSRA |= bit (ADPS0);
ssd1306_128x64_i2c_init(); // initializes OLED
ssd1306_clearScreen(); // clears OLED
engine.begin(); // inititalizes nanoengine
};
void loop()
{
summ = 0;
for (int i = 0; i < 64; i++) {
microseconds = micros();
data[i] = ((analogRead(A0)) >> 2) - 128; // Fitting analogRead data (range:0 - 1023) to int8_t array (range:-128 - 127)
summ += data[i];
while (micros() < (microseconds + sampling_period_us)) { // Timing out uC ADC to fulfill sampling frequency requirement
}
}
// Eliminating remaining DC component (produces usable data in FFT bin #0, which is usually swamped by DC bias)
avg = summ / 64;
for (int i = 0; i < 64; i++) {
data[i] -= avg;
}
fix_fftr(data, 6, 0); // Performing real FFT
// Time smoothing by user-determined factor and user-determined scaling
for (int count = 0; count < 32; count++) {
if (data[count] < 0) data[count] = 0; // Eliminating negative output of fix_fftr
#ifdef LOG_OUTPUT
else data[count] = log_scale * log((float)(data[count] + 1)); // Logarithmic function equivalent to SCALING_FACTOR*log2(x+1)
#else
else data[count] *= SCALE_FACTOR; // Linear scaling up according to SCALE_FACTOR
#endif
data[count] = (float)buff[count] * anti_coeff + (float)data[count] * coeff; // Smoothing by factoring in past data
buff[count] = data[count]; // Storing current output as next frame's past data
if (data[count] > 63) data[count] = 63; // Capping output at screen height
}
// Output to SSD1306 using nanoengine canvas from library
engine.refresh(); // Mark entire screen to be refreshed
engine.canvas.clear(); // Clear canvas as previous data
for (int i = 0; i < 8; i++) {
engine.canvas.drawVLine(i * 4, 31 - (data[i] + 1), 31); // Draw to canvas data for lower-leftest sector (FFT bins 0 - 7, lower half)
}
engine.canvas.blt(0, 32); // Outputs canvas to OLED with an offset (x pixels, y pixels)
engine.canvas.clear();
for (int i = 0; i < 8; i++) {
if (data[i] > 31) engine.canvas.drawVLine(i * 4, 31 - (data[i] - 31), 31); // Draw to canvas data for upper-leftest sector (FFT bins 0 - 7, upper half)
}
engine.canvas.blt(0, 0);
engine.canvas.clear();
for (int i = 8; i < 16; i++) {
engine.canvas.drawVLine((i - 8) * 4, 31 - (data[i] + 1), 31); // FFT bins 8 - 15, lower half
}
engine.canvas.blt(32, 32);
engine.canvas.clear();
for (int i = 8; i < 16; i++) {
if (data[i] > 31) engine.canvas.drawVLine((i - 8) * 4, 31 - (data[i] - 31), 31); // FFT bins 9 - 15, upper half
}
engine.canvas.blt(32, 0);
engine.canvas.clear();
for (int i = 16; i < 24; i++) {
engine.canvas.drawVLine((i - 16) * 4, 31 - (data[i] + 1), 31); // FFT bins 16 - 23, lower half
}
engine.canvas.blt(64, 32);
engine.canvas.clear();
for (int i = 16; i < 24; i++) {
if (data[i] > 31) engine.canvas.drawVLine((i - 16) * 4, 31 - (data[i] - 31), 31); // FFT bins 16 - 23, upper half
}
engine.canvas.blt(64, 0);
engine.canvas.clear();
for (int i = 24; i < 32; i++) {
engine.canvas.drawVLine((i - 24) * 4, 31 - (data[i] + 1), 31); // FFT bins 24 - 31, lower half
}
engine.canvas.blt(96, 32);
engine.canvas.clear();
for (int i = 24; i < 32; i++) {
if (data[i] > 31) engine.canvas.drawVLine((i - 24) * 4, 31 - (data[i] - 31), 31); // FFT bins 24 - 31, upper half
}
engine.canvas.blt(96, 0);
}
实验场景图
实验场景动态图
实验场景图
实验场景动态图
在公众号内回复您想搜索的任意内容,如问题关键字、技术名词、bug代码等,就能轻松获得与之相关的专业技术内容反馈。快去试试吧!
由于微信公众号近期改变了推送规则,如果您想经常看到我们的文章,可以在每次阅读后,在页面下方点一个「赞」或「在看」,这样每次推送的文章才会第一时间出现在您的订阅列表里。
或将我们的公众号设为星标。进入公众号主页后点击右上角「三个小点」,点击「设为星标」,我们公众号名称旁边就会出现一个黄色的五角星(Android 和 iOS 用户操作相同)。
推荐帖子
- 100分求wince上GIF动画显示解决办法,本人现在在用gif98a类在改写,但遇到两个问题,帮忙解决一样给分.问题内详!!!!
- 在wince下没有CreateDIBitmap函数,但好象可以通过使用CreateDIBSection函数来做到,但确实对这两个函数都不熟悉,谁能给一个修改好的函数呀.如果能说明一下两个函数的使用就更好了,分再给看见以前讨论的一篇帖子,关于wince上gif动画的显示,其实我就是在用gif89a在移植.但遇到几个问题,首先就是文件创建时的几个参数不支持FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,然后就是我上面提到的问题,有好的解决办法么?
- jerry_liu WindowsCE
- ADAS3022 ADC芯片问题
- 我要采用ADAS3022来设计同时支持8路差分模拟量+5路单端模拟量输入。看了数据手册,不知道如何用一颗芯片来同时实现8路差分和5路单端的输入。我有一个想法,不知道可以否:就是用2个8:1模拟开关来分别选折差分1~8路的正输入和负输入,然后送到ADAS3022的0和1通道。然后另外的5路单端用一颗8:1的模拟开关来选折送到ADAS3022的2通道,3通道直接接地。其他没有用到的输入通道全部通过一个100K电阻接到地。寄存器配置时就把整个ADAS3022的所有输入通道配制成差分输
- perhas ADI参考电路
- 蓝牙串口模块
- www.wavesen.comwww.linvor.com原厂供应性价比最高,价格最低的蓝牙串口模块包括AT指令等蓝牙串口模块
- oooooooooo 嵌入式系统
- TI的MSP432驱动修复记
- 前几天就收到TI寄来的MSP432,于是马上把CCS6更新了,然后把USB插上到板子上,出现了令人抓狂的情况,有2个驱动死活也装不上,手动指定到CCS6目录下的驱动安装包也不行。然后面临两种选择。第一种最激进的选择,把系统重装了,但这样太费时间了,装完系统后还有装回以前的软件。第二种是继续找出问题把它修复好。于是我就沉下心,用搜索工具开始找最佳的解决办法,翻了几个网页,找到解决的方法。主要是修改一个文件
- 强仔00001 微控制器 MCU
- 基于TMS320F28XX+UCD8K+HCPL-315J的两级逆变器
- 1引言随着DSP技术发展,数字控制在逆变电源控制中的应用也日益广泛,同时PWM专用数字控制器件种类繁多。本文对适用于开关电源控制的多个控制器件的性能进行比较,着重详细介绍TI的TMS320F28XX。逆变电源一般采用两级变换架构,即先采用隔离的DC/DC变换电路增大输入直流电压,再采用非隔离的DC/AC逆变电路输出正弦电压。为了达到高效、一体化管理,可利用单个DSP实现两个变换器的反馈控制。然而,如果同时对两个变换器进行反馈控制,但不能同时进行控制计算,必然会造成相互影响,从而影响最终的控
- 呱呱 微控制器 MCU
- dspic33用什么C编译器
- 请问高手,dspic33用什么C编译器?是和dspic30一样吗?谢谢!dspic33用什么C编译器以前用过Microchip自家的MPLABC30编译器,可以上其官网看看记得是用MAPLABXIDE(编辑器)+C30(编译器),或者去官网搜索芯片型号,会有需要的信息
- chenbingjy Microchip MCU