回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【Arduino 动手做】MAX7219 图形音频频谱分析仪 简单

头像 驴友花雕 2025.07.10 9 0

具有大量设置和功能的 Graph Spectrum Analyzer
光谱输出开启:
显示器 1602
4 个块的矩阵 8x8 (MAX7219)
WS2812 可寻址矩阵
亮度调整
色域调整(适用于 WS2812)
调整增益和杂色减少
调整动画平滑度
音量设置:
固定
来自电位计
自动
最高积分
开 关
挂起时间
坠落速度
按频率手动采样

算法
频谱分析,在输出端,我们有一个光谱波段值数组(128 个波段)
按每个波段的较低值(128 个波段)进行筛选
从 128 个通道过渡到 16 个通道,同时保持基于线性的带间值
求最大值以校正矩阵上条形的高度
将带材的净“重量”转换为模具的高度
将条带发送到矩阵
计算最高积分的位置并将其发送到 mtaritsa
顺便过滤较高的峰,从体积中校正柱的高度,等等

00.jpg
0-.jpg
01.jpg
02.jpg
03.jpg
04.jpg
05.jpg
8x32.png

项目代码

代码
// -------------------------------- НАСТРОЙКИ --------------------------------
// матрица
#define BRIGHTNESS 15     // яркость матрицы (0 - 15)

// сигнал
#define INPUT_GAIN 1.5    // коэффициент усиления входного сигнала
#define LOW_PASS 35       // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
#define MAX_COEF 1.1      // коэффициент, который делает "максимальные" пики чуть меньше максимума, для более приятного восприятия
#define NORMALIZE 0       // нормализовать пики (столбики низких и высоких частот будут одинаковой длины при одинаковой громкости) (1 вкл, 0 выкл)

// анимация
#define SMOOTH 0.4        // плавность движения столбиков (0 - 1)
#define DELAY 4           // задержка между обновлениями матрицы (периодичность основного цикла), миллиисекунды

// громкость
#define DEF_GAIN 100       // максимальный порог по умолчанию (при MANUAL_GAIN или AUTO_GAIN игнорируется)
#define MANUAL_GAIN 0     // ручная настройка потенциометром на громкость (1 вкл, 0 выкл)
#define AUTO_GAIN 1       // автонастройка по громкости (экспериментальная функция) (1 вкл, 0 выкл)

// точки максимума
#define MAX_DOTS 1        // включить/выключить отрисовку точек максимума (1 вкл, 0 выкл)
#define FALL_DELAY 50     // скорость падения точек максимума (задержка, миллисекунды)
#define FALL_PAUSE 700    // пауза перед падением точек максимума, миллисекунды

// массив тонов, расположены примерно по параболе. От 80 Гц до 16 кГц
byte posOffset[33] = {
  2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 25, 32, 36, 40, 44, 48, 52, 57, 62, 67, 72, 78, 84, 90, 96, 102, 108, 120
};
// -------------------------------- НАСТРОЙКИ --------------------------------

// ---------------------- ПИНЫ ----------------------
// для увеличения точности уменьшаем опорное напряжение,
// выставив EXTERNAL и подключив Aref к выходу 3.3V на плате через делитель
// GND ---[10-20 кОм] --- REF --- [10 кОм] --- 3V3

// Матрица: CS -> 10 / CLK -> 13 / DIN -> 11
#define AUDIO_IN 0         // пин, куда подключен звук
#define POT_PIN 7          // пин потенциометра настройки (если нужен MANUAL_GAIN)
// ---------------------- ПИНЫ ----------------------

// --------------- ДЛЯ РАЗРАБОТЧИКОВ ---------------
#define MATR_NUM 4        // количество матриц последовательно
#define FHT_N 256         // ширина спектра х2
#define LOG_OUT 1
#include <FHT.h>           // преобразование Хартли
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
Max72xxPanel matrix = Max72xxPanel(10, 4, 1);

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

int gain = DEF_GAIN;   // усиление по умолчанию
unsigned long gainTimer, fallTimer;
byte maxValue;
float k = 0.05, maxValue_f = 0.0;
int maxLevel[32];
byte posLevel_old[32];
unsigned long timeLevel[32], mainDelay;
boolean fallFlag;
// --------------- ДЛЯ РАЗРАБОТЧИКОВ ---------------

void setup() {
  // поднимаем частоту опроса аналогового порта до 38.4 кГц, по теореме
  // Котельникова (Найквиста) максимальная частота дискретизации будет 19 кГц
  // http://yaab-arduino.blogspot.ru/2015/02/fast-sampling-from-analog-input.html
  sbi(ADCSRA, ADPS2);
  cbi(ADCSRA, ADPS1);
  sbi(ADCSRA, ADPS0);

  analogReference(EXTERNAL);

  Serial.begin(9600);
  matrix.setIntensity(BRIGHTNESS);
  for (byte i = 0; i < MATR_NUM; i++) {
    // матрицы расположены криво, здесь поворачиваем
    matrix.setRotation(i, 1);
  }
  matrix.fillScreen(LOW);
  matrix.write();
}

void loop() {
  if (millis() - mainDelay > DELAY - 2) {     // итерация главного цикла
    mainDelay = millis();

    analyzeAudio();   // функция FHT, забивает массив fht_log_out[] величинами по спектру

    for (int i = 0; i < 128; i ++) {
      // вот здесь сразу фильтруем весь спектр по минимальному LOW_PASS
      if (fht_log_out[i] < LOW_PASS) fht_log_out[i] = 0;

      // усиляем сигнал
      fht_log_out[i] = (float)fht_log_out[i] * INPUT_GAIN;

      // уменьшаем громкость высоких частот (пропорционально частоте) если включено
      if (NORMALIZE) fht_log_out[i] = (float)fht_log_out[i] / ((float)1 + (float)i / 128);
    }
    maxValue = 0;
    matrix.fillScreen(LOW);
    delay(2);///////////////////СУКАДИЛЕЙ//////БЕЗ НЕГО МАТРИЦА НАЧИНАЕТ КОСОЖОПИТЬ///////////
    for (byte pos = 0; pos < MATR_NUM * 8; pos++) {    // для кажого столбца матрицы
      int posLevel = fht_log_out[posOffset[pos]];
      byte linesBetween;
      if (pos > 0 && pos < MATR_NUM * 8) {
        linesBetween = posOffset[pos] - posOffset[pos - 1];
        for (byte i = 0; i < linesBetween; i++) {  // от предыдущей полосы до текущей
          posLevel += (float) ((float)i / linesBetween) * fht_log_out[posOffset[pos] - linesBetween + i];
        }
        linesBetween = posOffset[pos + 1] - posOffset[pos];
        for (byte i = 0; i < linesBetween; i++) {  // от предыдущей полосы до текущей
          posLevel += (float) ((float)i / linesBetween) * fht_log_out[posOffset[pos] + linesBetween - i];
        }
      }
      // найти максимум из пачки тонов
      if (posLevel > maxValue) maxValue = posLevel;

      // фильтрация длины столбиков, для их плавного движения
      posLevel = posLevel_old[pos] * SMOOTH + posLevel * (1 - SMOOTH);
      posLevel_old[pos] = posLevel;

      // преобразовать значение величины спектра в диапазон 0..7 с учётом настроек
      posLevel = map(posLevel, LOW_PASS, gain, 0, 7);
      posLevel = constrain(posLevel, 0, 7);

      // если столбик больше нуля, то начать линию с координаты (pos, 7) и рисовать в (pos, 7 - значение)
      if (posLevel > 0) matrix.drawLine(pos, 7, pos, 7 - posLevel, HIGH);

      if (posLevel > 0 && posLevel > maxLevel[pos]) {    // если для этой полосы есть максимум, который больше предыдущего
        maxLevel[pos] = posLevel;                        // запомнить его
        timeLevel[pos] = millis();                       // запомнить время
      }

      // если точка максимума выше нуля (или равна ему) - включить пиксель
      if (MAX_DOTS && maxLevel[pos] >= 0) matrix.drawPixel(pos, 7 - maxLevel[pos], HIGH);

      if (fallFlag) {                                           // если падаем на шаг
        if ((long)millis() - timeLevel[pos] > FALL_PAUSE) {     // если максимум держался на своей высоте дольше FALL_PAUSE
          if (maxLevel[pos] >= 0) maxLevel[pos]--;              // уменьшить высоту точки на 1
          // внимание! Принимает минимальное значение -1 !
        }
      }
    }
    matrix.write();  // отправить на матрицу

    fallFlag = 0;                                 // сбросить флаг падения
    if (millis() - fallTimer > FALL_DELAY) {      // если настало время следующего падения
      fallFlag = 1;                               // поднять флаг
      fallTimer = millis();
    }

    // если разрешена ручная настройка уровня громкости
    if (MANUAL_GAIN) gain = map(analogRead(POT_PIN), 0, 1023, 0, 150);

    // если разрешена авто настройка уровня громкости
    if (AUTO_GAIN) {
      if (millis() - gainTimer > 10) {      // каждые 10 мс
        maxValue_f = maxValue * k + maxValue_f * (1 - k);
        // если максимальное значение больше порога, взять его как максимум для отображения
        if (maxValue_f > LOW_PASS) gain = (float) MAX_COEF * maxValue_f;
        // если нет, то взять порог побольше, чтобы шумы вообще не проходили
        else gain = 100;
        gainTimer = millis();
      }
    }
  }
}

void analyzeAudio() {
  for (int i = 0 ; i < FHT_N ; i++) {
    int sample = analogRead(AUDIO_IN);
    fht_input[i] = sample; // put real data into bins
  }
  fht_window();  // window the data for better frequency response
  fht_reorder(); // reorder the data before doing the fht
  fht_run();     // process the data in the fht
  fht_mag_log(); // take the output of the fht
}

/*
   Алгоритм работы:
   Анализ спектра, на выходе имеем массив величин полос спектра (128 полос)
   Фильтрация по нижним значениям для каждой полосы (128 полос)
   Переход от 128 полос к 16 полосам с сохранением межполосных значений по линейной зависимости
   Поиск максимумов для коррекции высоты столбиков на матрице
   Перевод чистого "веса" полосы к высоте матрицы
   Отправка полос на матрицу
   Расчёт позиций точек максимума и отправка их на мтарицу

   Мимоходом фильтрация верхних пиков, коррекция высоты столбиков от громкости и прочее
*/

【Arduino 动手做】MAX7219 图形音频频谱分析仪
项目链接:https://alexgyver.ru/fhtspectrumanalyzer/
项目作者:alexgyver
项目参考:https://alexgyver.ru/arduino-first/#%E2%96%B6%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B_AlexGyver%E2%97%80
项目视频 :https://www.youtube.com/watch?v=3b66UxMQoiw
项目代码:
https://github.com/AlexGyver/FHTSpectrumAnalyzer/blob/master/Firmware/spertrumMatrix_MAX7219/spertrumMatrix_MAX7219.ino
https://github.com/AlexGyver/FHTSpectrumAnalyzer/archive/master.zip

00186.gif

评论

user-avatar
icon 他的勋章
    展开更多