[ESP8266/ESP32]Firebeetle FFT VGA 显示器 精华

1846浏览
查看: 1846|回复: 2

[ESP8266/ESP32] Firebeetle FFT VGA 显示器

[复制链接]
之前基于 FireBeetle ESP32 和全向MEMS麦克风模块(SEN0487)制作过一个在OLED屏幕上显示当前环境声音频谱的装置【参考1】。这次制作的是能够输出 VGA 信号的频谱装置,这样,用户能够在显示器或者电视机上看到实时频谱输出。
具体的VGA 显示原理,可以在之前的介绍中看到【参考2】,这次的设计硬件部分与之类似。电路图如下:

Firebeetle FFT VGA 显示器图1

其中主控和VGA部分如下:VGA本质上还是模拟信号,这里使用电阻能够输出不同电平的模拟信号,三根GPIO能够实现2^3=16种组合,因此也意味着能够实现16种颜色.

Firebeetle FFT VGA 显示器图2

下面是用于连接全向MEMS麦克风模块的接口:

Firebeetle FFT VGA 显示器图3
板子上带有一个 USB 公头用于取电,另外还有一个 USB母头,如果你的显示设备没有 VGA接口只有HDMI接口,那么需要一个VGAHDMI线,而这种线通常使用USB公头取电,这种情况可以直接将它连接到这个 USB母头取电。

Firebeetle FFT VGA 显示器图4


同样的,为了便于从充电宝取电,还设计了一个负载消耗电路。

Firebeetle FFT VGA 显示器图5


PCB设计如下:

Firebeetle FFT VGA 显示器图6


3D预览如下:


Firebeetle FFT VGA 显示器图7

焊接后的实物如下:

Firebeetle FFT VGA 显示器图8


接下来就可以进行软件的设计了,。基本原理是:首先通过ADC进行采样,然后将采样结果进行 FFT ,最终得到的是采样期间每个频率的能量。我们将这个数值显示在 VGA上就得到了期望的结果:

  1. #include <ArduinoFFT.h>
  2. #include "fabgl.h"
  3. // VGA 显示
  4. fabgl::VGA16Controller DisplayController;
  5. Canvas cv(&DisplayController);
  6. //ZivDebug #define SAMPLES         1024          // Must be a power of 2
  7. #define SAMPLES         256          // Must be a power of 2
  8. #define SAMPLING_FREQ   40000         // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
  9. #define AMPLITUDE       1000          // Depending on your audio source level, you may need to alter this value. Can be used as a 'sensitivity' control.
  10. #define AUDIO_IN_PIN    A0            // Signal in on this pin
  11. #define NOISE           500           // Used as a crude noise filter, values below this are ignored
  12. const uint8_t kMatrixWidth = 16;      // Matrix width
  13. const uint8_t kMatrixHeight = 16;     // Matrix height
  14. #define NUM_BANDS       16            // To change this, you will need to change the bunch of if statements describing the mapping from bins to bands
  15. #define BAR_WIDTH      (kMatrixWidth  / (NUM_BANDS - 1))  // If width >= 8 light 1 LED width per bar, >= 16 light 2 LEDs width bar etc
  16. #define TOP            (kMatrixHeight - 0)                // Don't allow the bars to go offscreen
  17. // Sampling and FFT stuff
  18. unsigned int sampling_period_us;
  19. byte peak[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // The length of these arrays must be >= NUM_BANDS
  20. int oldBarHeights[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  21. int bandValues[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  22. double vReal[SAMPLES];
  23. double vImag[SAMPLES];
  24. unsigned long newTime;
  25. arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
  26. unsigned long int Elsp1;
  27. int h[16];
  28. int Height,Width;
  29. void setup() {
  30.   Serial.begin(115200);
  31.   sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQ));
  32.   DisplayController.begin();
  33.   // 设定分辨率
  34.   DisplayController.setResolution(VGA_640x400_60Hz);
  35.   Height=cv.getHeight();
  36.   Width=cv.getWidth();
  37.   cv.setBrushColor(Color::Red    );
  38.   
  39.   // get a font for about 40x14 text screen
  40.   cv.selectFont(&fabgl::FONT_8x8);
  41.   cv.setGlyphOptions(GlyphOptions().FillBackground(true));
  42.   
  43. }
  44. void loop() {
  45.   static int64_t stime  = esp_timer_get_time();
  46.   static int FPS        = 0;
  47.   static int FPSCounter = 0;
  48.   
  49.   // Reset bandValues[]
  50.   for (int i = 0; i < NUM_BANDS; i++) {
  51.     bandValues[i] = 0;
  52.   }
  53.   // Sample the audio pin
  54.   for (int i = 0; i < SAMPLES; i++) {
  55.     newTime = micros();
  56.     vReal[i] = analogRead(AUDIO_IN_PIN); // A conversion takes about 9.7uS on an ESP32
  57.     vImag[i] = 0;
  58.     while ((micros() - newTime) < sampling_period_us) {
  59.       /* chill */
  60.     }
  61.   }
  62.   // Compute FFT
  63.   FFT.DCRemoval();
  64.   FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  65.   FFT.Compute(FFT_FORWARD);
  66.   FFT.ComplexToMagnitude();
  67.   // Analyse FFT results
  68.   for (int i = 2; i < (SAMPLES / 2); i++) {    // Don't use sample 0 and only first SAMPLES/2 are usable. Each array element represents a frequency bin and its value the amplitude.
  69.     if (vReal[i] > NOISE) {                    // Add a crude noise filter
  70.       //16 bands, 12kHz top band
  71.       if (i <= 2 )           bandValues[0]  += (int)vReal[i];
  72.       if (i > 2   && i <= 3  ) bandValues[1]  += (int)vReal[i];
  73.       if (i > 3   && i <= 5  ) bandValues[2]  += (int)vReal[i];
  74.       if (i > 5   && i <= 7  ) bandValues[3]  += (int)vReal[i];
  75.       if (i > 7   && i <= 9  ) bandValues[4]  += (int)vReal[i];
  76.       if (i > 9   && i <= 13 ) bandValues[5]  += (int)vReal[i];
  77.       if (i > 13  && i <= 18 ) bandValues[6]  += (int)vReal[i];
  78.       if (i > 18  && i <= 25 ) bandValues[7]  += (int)vReal[i];
  79.       if (i > 25  && i <= 36 ) bandValues[8]  += (int)vReal[i];
  80.       if (i > 36  && i <= 50 ) bandValues[9]  += (int)vReal[i];
  81.       if (i > 50  && i <= 69 ) bandValues[10] += (int)vReal[i];
  82.       if (i > 69  && i <= 97 ) bandValues[11] += (int)vReal[i];
  83.       if (i > 97  && i <= 135) bandValues[12] += (int)vReal[i];
  84.       if (i > 135 && i <= 189) bandValues[13] += (int)vReal[i];
  85.       if (i > 189 && i <= 264) bandValues[14] += (int)vReal[i];
  86.       if (i > 264          ) bandValues[15] += (int)vReal[i];
  87.     }
  88.   }
  89.   // Process the FFT data into bar heights
  90.   for (byte band = 0; band < NUM_BANDS; band++) {
  91.     // Scale the bars for the display
  92.     int barHeight = bandValues[band] / AMPLITUDE;
  93.     if (barHeight > TOP) barHeight = TOP;
  94.     // Small amount of averaging between frames
  95.     barHeight = ((oldBarHeights[band] * 1) + barHeight) / 2;
  96.     // Move peak up
  97.     if (barHeight > peak[band]) {
  98.       peak[band] = min(TOP, barHeight);
  99.     }
  100.     h[band] = barHeight;
  101.     // Save oldBarHeights for averaging later
  102.     oldBarHeights[band] = barHeight;
  103.   }
  104.   if (millis() - Elsp1 > 10) {
  105.     for (byte band = 0; band < NUM_BANDS; band++)
  106.       if (peak[band] > 0) peak[band] -= 1;
  107.     cv.setBrushColor(Color::Black    );
  108.     cv.clear();
  109.     cv.setBrushColor(Color::Red    );
  110.     for (int i = 0; i < 16; i++) {
  111.       if (h[i] != 0) {
  112.         //cv.fillRectangle(cv.getWidth()*i / 16, 0, cv.getWidth() * (i + 1) / 16, cv.getHeight() *h[i] / 16);
  113.         cv.fillRectangle(Width*i / 16, Height -1 , Width * (i + 1) / 16-1, (Height-1) *(16-h[i]) / 16);
  114.         
  115.       }
  116.       Serial.print(h[i], HEX);
  117.       Serial.print("");
  118.     }
  119.     Serial.println("");
  120.     Elsp1 = millis();
  121.   }
  122.   
  123.       if (esp_timer_get_time() - stime > 1000000) {
  124.     // calculate FPS
  125.     FPS = FPSCounter;
  126.     stime = esp_timer_get_time();
  127.     FPSCounter = 0;
  128.   }
  129.   ++FPSCounter;
  130.   // display test state and FPS
  131.   cv.setPenColor(Color::Blue);
  132.   cv.setBrushColor(Color::Yellow);
  133.   cv.drawTextFmt(80, 5, "%d FPS ",  FPS);
  134. }
复制代码

这个项目焊接难度较低,演示效果非常好,有兴趣的朋友不妨尝试亲自实验,同时还可以修改代码来实现更多的效果。

参考:








zoologist  高级技匠
 楼主|

发表于 2023-6-15 09:32:15

回复

使用道具 举报

zoologist  高级技匠
 楼主|

发表于 2023-6-15 10:00:03

本文提到的电路图和 PCB 下载

下载附件VGAFFT_SCH&amp;PCH.zip

本文提到的代码下载:

下载附件FB_VGA_FFTV2.zip


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail