干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈


為做這個項目,特意準備了四種規格的OLED有機屏幕
 
干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖1

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖2
干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖3

OLED(Organic Light-Emitting Diode

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖4

 

即有機發光二極管,在手機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 明流以上。

主要的實驗材料

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖5


干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖6

 

MAX9814麥克風放大器模塊
MAX9814是一款低成本高性能麥克風放大器,具有自動增益控制(AGC)和低噪聲麥克風偏置。器件具有低噪聲前端放大器、可變增益放大(VGA)、輸出放大器、麥克風偏置電壓發生器和AGC控制電路。
●自動增益控制(AGC)  
●3種增益設置(40dB、50dB、60dB)  
●可編程動作時間  
●可編程動作和釋放時間比  
●電源電壓范圍2.7V~5.5V   
●低THD:0.04% (典型值)  
●低功耗關斷模式  
●內置2V低噪聲麥克風偏置

MAX9814麥克風放大器模塊電原理圖


干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖7


干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖8

0.91寸OLED液晶屏顯示模塊參數
驅動芯片:SSD1306
支持接口:I2C
顯示顏色:白色
高分辨率:128×32
可視角度:大于160°
工作電壓:3.3V / 5V
模塊大小:36 x 12.5(mm)

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖9


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頻譜燈的圖10


音樂可視化系列小項目: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);

  • }


實驗串口返回情況

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖11

打開IDE的串口繪圖器

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖12


干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖13

實驗場景圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖14


實驗場景圖 動態圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖15

音樂可視化系列小項目: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頻譜燈的圖16


實驗場景圖  動態圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖17


實驗場景圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖18


實驗場景圖  動態圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖19


音樂可視化系列小項目: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);

  • }

實驗場景圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖20


實驗場景動態圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖21


實驗場景圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖22


實驗場景動態圖

 

干貨|有趣好玩的音樂可視化系列小項目:OLED頻譜燈的圖23

登錄后免費查看全文
立即登錄
App下載
技術鄰APP
工程師必備
  • 項目客服
  • 培訓客服
  • 平臺客服

TOP