分享

干货|有趣好玩的音乐可视化系列小项目:OLED频谱灯

 知芯世界 2021-11-11
为做这个项目,特意准备了四种规格的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 <Adafruit_GFX.h>

  • #include <Adafruit_SSD1306.h>

  • #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 <fix_fft.h>

  • #include <ssd1306.h>

  • #include <nano_engine.h>

  • // 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<TILE_32x32_MONO> 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);

  • }

实验场景图

实验场景动态图

实验场景图

实验场景动态图

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多