1

Тема: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Требуется тестирование

http://forum.rcl-radio.ru/uploads/images/2026/01/39c130535431edf3113d822a2087fa31.png

http://forum.rcl-radio.ru/uploads/images/2026/01/e41a3a89d5fc21bcbbe09fffeaf88d9a.png


#define AUTO_GAIN 1       // автонастройка по громкости (экспериментальная функция)
#define VOL_THR 25        // порог тишины (ниже него отображения на матрице не будет)
#define LOW_PASS 20       // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
#define DEF_GAIN 80       // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
#define FHT_N 256         // ширина спектра х2
#define LOG_OUT 1
#define PIK_DELAY 400    // время зависания пика

// вручную забитый массив тонов, сначала плавно, потом круче
byte posOffset[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};//1500 Hz
//byte posOffset[16] = {1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 25, 30, 35, 40, 45, 50};//4000 Hz

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

#include <Wire.h>
#include <U8glib.h>  // http://rcl-radio.ru/wp-content/uploads/2023/04/U8glib.zip
#include <FHT.h>     // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=297&download=1

U8GLIB_SH1106_128X64 myOLED(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST);  // Dev 0, Fast I2C / TWI
 
  byte gain = DEF_GAIN;   
  unsigned long gainTimer,times, times0;
  byte maxValue, maxValue_f;
  float k = 0.1;
  byte ur[16],urr[16];
  unsigned long peakTimings[16]; 


  
void setup() {
  delay(100); 
  sbi(ADCSRA, ADPS2);
  cbi(ADCSRA, ADPS1);
  sbi(ADCSRA, ADPS0);
  Serial.begin(9600);
  Wire.begin();Wire.setClock(800000L);
  myOLED.begin();
 // myOLED.setRot180();
  myOLED.setFont(u8g_font_profont11r);
  analogReference(INTERNAL); // Устанавливаем внутреннее опорное напряжение 1.1 В
  pinMode(A0,INPUT); // INPUT AUDIO
}
 

void loop() {
  analyzeAudio(); 

  myOLED.firstPage();  
  do {
    for (int pos = 0; pos < 128; pos += 8) {
      // Определяем уровень сигнала на конкретной частоте
      int posLevel = map(fht_log_out[posOffset[pos / 8]], LOW_PASS, gain, 0, 60);
      posLevel = constrain(posLevel, 0, 60);

      // Обновляем текущий уровень сигнала
      urr[pos] = posLevel;

      // Индекс текущей полосы (для массива пиков)
      int index = pos / 8;

      // Обрабатываем пики
      if (urr[pos] < ur[index]) {
        // Если пик установлен давно, начинается его плавное уменьшение
        if (millis() - peakTimings[index] > PIK_DELAY) {
          ur[index] -= 1; // Плавное уменьшение пиков
        }
      } else {
        // Установка нового пикового значения и фиксация времени
        ur[index] = posLevel;
        peakTimings[index] = millis(); // Запомним время фиксации текущего пика
      }

      // Ждем для стабильности
      delayMicroseconds(200);

      // Рисуем текущий уровень сигнала
      for (int v_pos = 0; v_pos < urr[pos] + 4; v_pos += 4) {
        myOLED.drawBox(pos, 61 - v_pos, 6, 2);
      }

      // Если пик существовал раньше, рисуем пиковый индикатор
      if (ur[index] > urr[pos]) {
        myOLED.drawBox(pos, 61 - ur[index], 6, 2); // Пиковый индикатор
      }
    }
  } while(myOLED.nextPage());

  // Автонастройка усиления
  if (AUTO_GAIN) {
    maxValue_f = maxValue * k + maxValue_f * (1 - k);
    if (millis() - gainTimer > 1500) {
      if (maxValue_f > VOL_THR) gain = maxValue_f;
      else gain = 100;
      gainTimer = millis();
    }
  }
}
 
void analyzeAudio() {
  for (int i = 0 ; i < FHT_N ; i++) {
    int sample = analogRead(A0);
    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
}

2

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

При нулевом сигнале пики лежат на полосах

#define AUTO_GAIN 1       // автонастройка по громкости (экспериментальная функция)
#define VOL_THR 25        // порог тишины (ниже него отображения на матрице не будет)
#define LOW_PASS 20       // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
#define DEF_GAIN 80       // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
#define FHT_N 256         // ширина спектра х2
#define LOG_OUT 1
#define PIK_DELAY 400    // время зависания пика

// вручную забитый массив тонов, сначала плавно, потом круче
byte posOffset[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};//1500 Hz
//byte posOffset[16] = {1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 25, 30, 35, 40, 45, 50};//4000 Hz

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

#include <Wire.h>
#include <U8glib.h>  // http://rcl-radio.ru/wp-content/uploads/2023/04/U8glib.zip
#include <FHT.h>     // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=297&download=1

U8GLIB_SH1106_128X64 myOLED(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST);  // Dev 0, Fast I2C / TWI
 
  byte gain = DEF_GAIN;   
  unsigned long gainTimer,times, times0;
  byte maxValue, maxValue_f;
  float k = 0.1;
  byte ur[16],urr[16];
  unsigned long peakTimings[16]; 


  
void setup() {
  delay(100); 
  sbi(ADCSRA, ADPS2);
  cbi(ADCSRA, ADPS1);
  sbi(ADCSRA, ADPS0);
  Serial.begin(9600);
  Wire.begin();Wire.setClock(800000L);
  myOLED.begin();
 // myOLED.setRot180();
  myOLED.setFont(u8g_font_profont11r);
   //analogReference(INTERNAL); // Устанавливаем внутреннее опорное напряжение 1.1 В
  pinMode(A0,INPUT); // INPUT AUDIO 
  
  // Инициализация массива пиков минимальным значением
  for(int i = 0; i < sizeof(ur)/sizeof(ur[0]); i++) {
    ur[i] = 3; // Устанавливаем все пики на минимум 
  }
}
 

 

void loop() {
  analyzeAudio(); 

  myOLED.firstPage();  
  do {
    for (int pos = 0; pos < 128; pos += 8) {
      // Уровень сигнала
      int posLevel = map(fht_log_out[posOffset[pos / 8]], LOW_PASS, gain, 0, 60);
      posLevel = constrain(posLevel, 0, 60);

      // Обновление текущего уровня сигнала
      urr[pos] = posLevel;

      // Индекс текущей полосы
      int index = pos / 8;

      // Обработка пиков
      if (urr[pos] < ur[index]) {
        // Уменьшение пиков
        if (millis() - peakTimings[index] > PIK_DELAY) {
          ur[index] -= 1;
          if (ur[index] < 3) {
            ur[index] = 3; // Минимальная высота пиков
          }
        }
      } else {
        // Новая максимальная величина
        ur[index] = posLevel;
        peakTimings[index] = millis();
      }

      // Маленькая задержка
      delayMicroseconds(200);

      // Рисуем текущий уровень сигнала
      for (int v_pos = 0; v_pos < urr[pos] + 4; v_pos += 4) {
        myOLED.drawBox(pos, 61 - v_pos, 6, 2);
      }

      // Показываем пиковые линии
      if (ur[index] > 0) {
        myOLED.drawBox(pos, 61 - ur[index], 6, 2); // Пиковый индикатор
      }
    }
  } while(myOLED.nextPage());

  // Автоматическое усиление
  if (AUTO_GAIN) {
    maxValue_f = maxValue * k + maxValue_f * (1 - k);
    if (millis() - gainTimer > 1500) {
      if (maxValue_f > VOL_THR) gain = maxValue_f;
      else gain = 100;
      gainTimer = millis();
    }
  }
}
 
void analyzeAudio() {
  for (int i = 0 ; i < FHT_N ; i++) {
    int sample = analogRead(A0);
    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
}

3

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Основные полосы плавно опускаются и быстро подымаются

#define AUTO_GAIN 1       // автонастройка по громкости (экспериментальная функция)
#define VOL_THR 25        // порог тишины (ниже него отображения на матрице не будет)
#define LOW_PASS 20       // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
#define DEF_GAIN 80       // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
#define FHT_N 256         // ширина спектра х2
#define LOG_OUT 1
#define PIK_DELAY 400    // время зависания пика

// вручную забитый массив тонов, сначала плавно, потом круче
byte posOffset[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};//1500 Hz
//byte posOffset[16] = {1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 25, 30, 35, 40, 45, 50};//4000 Hz

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

#include <Wire.h>
#include <U8glib.h>  // http://rcl-radio.ru/wp-content/uploads/2023/04/U8glib.zip
#include <FHT.h>     // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=297&download=1

U8GLIB_SH1106_128X64 myOLED(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST);  // Dev 0, Fast I2C / TWI
 
  byte gain = DEF_GAIN;   
  unsigned long gainTimer,times, times0;
  byte maxValue, maxValue_f;
  float k = 0.1;
  byte ur[16],urr[16];
  unsigned long peakTimings[16]; 


  
void setup() {
  delay(100); 
  sbi(ADCSRA, ADPS2);
  cbi(ADCSRA, ADPS1);
  sbi(ADCSRA, ADPS0);
  Serial.begin(9600);
  Wire.begin();Wire.setClock(800000L);
  myOLED.begin();
 // myOLED.setRot180();
  myOLED.setFont(u8g_font_profont11r);
  analogReference(INTERNAL); // Устанавливаем внутреннее опорное напряжение 1.1 В
  pinMode(A0,INPUT); // INPUT AUDIO 
  
  // Инициализация массива пиков минимальным значением
  for(int i = 0; i < sizeof(ur)/sizeof(ur[0]); i++) {
    ur[i] = 3; // Устанавливаем все пики на минимум 
  }
}
 

 

void loop() {
  analyzeAudio(); 

  myOLED.firstPage();  
  do {
    // Первая фаза: рисуем только текущие полосы сигнала
    for (int pos = 0; pos < 128; pos += 8) {
      // Текущий уровень сигнала
      int posLevel = map(fht_log_out[posOffset[pos / 8]], LOW_PASS, gain, 0, 60);
      posLevel = constrain(posLevel, 0, 60);

      // Обновляем текущий уровень сигнала
      if (posLevel > urr[pos]) {
        urr[pos] = posLevel;     // Быстро увеличиваем уровень сигнала
      } else {
        // Медленно уменьшаем уровень сигнала
        if (posLevel < urr[pos]) {
          urr[pos]--;       // Постепенное убывание уровня сигнала
        }
        
        // Минимальный уровень сигнала для отображения полос
        if (urr[pos] < 1) {
          urr[pos] = 1;     // Сохраняем базовую высоту полосы
        }
      }

 delayMicroseconds(100);

      // Рисуем текущий уровень сигнала
      for (int v_pos = 0; v_pos < urr[pos]; v_pos += 4) {
        myOLED.drawBox(pos, 61 - v_pos, 6, 2); // Базовые полосы сигнала
      }
    }

    // Вторая фаза: рисуем пиковые индикаторы отдельно от полос
    for (int pos = 0; pos < 128; pos += 8) {
      // Индекс текущей полосы
      int index = pos / 8;

      // Обработка пиков
      if (urr[pos] < ur[index]) {
        // Уменьшение пиков
        if (millis() - peakTimings[index] > PIK_DELAY) {
          ur[index] -= 1;
          if (ur[index] < 3) {
            ur[index] = 3; // Минимальная высота пиков
          }
        }
      } else {
        // Новая максимальная величина
        ur[index] = urr[pos];
        peakTimings[index] = millis();
      }

      // Рисуем пиковые индикаторы выше уровня сигнала
      if (ur[index] > urr[pos]) {
        myOLED.drawBox(pos, 61 - ur[index] , 6, 2); // Пик находится выше уровня сигнала
      }
    }
  } while(myOLED.nextPage());

  // Автонастройка усиления
  if (AUTO_GAIN) {
    maxValue_f = maxValue * k + maxValue_f * (1 - k);
    if (millis() - gainTimer > 1500) {
      if (maxValue_f > VOL_THR) gain = maxValue_f;
      else gain = 100;
      gainTimer = millis();
    }
  }
}
 
void analyzeAudio() {
  for (int i = 0 ; i < FHT_N ; i++) {
    int sample = analogRead(A0);
    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
}

4

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Александр,вопрос . OLED I2C может поддерживать 800кгц ? вроде заявленная скорость порта I2C OLED 400кгц.

5

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

да, может

Wire.begin();Wire.setClock(800000L);

6

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

liman324 пишет:

да, может

Wire.begin();Wire.setClock(800000L);

Про эту обьявленную скорость я и спрашиваю.
Я в свое время на нано когда делал ,баловался со скоростью порта i2c дисплея ,то на 800кгц дисплей начинает подтупливать(не сильно ,но присмотревшись видно),поэтому вернулся к 400кгц.

7

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Вроде как пишут что ATmega328 не поддерживает 800 кГц, а LGT8F328 поддерживает

8

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Скорее всего Wire.setClock(800000L); не будет работать на ATmega328, команда не будет активна.

9

Re: Анализатор спектра с пиковыми индикаторами на OLED1.3 (Arduino Nano)

Пробовал и на нано и меге и esp32 ,поднятие скорости к лучшим показателям не приходит,что толку поднимать скорость порта i2c на проце ,если дисплей не волокет такую скорость. К примеру на esp32(снизил скорость до 40мгц) при установленной скорости порта 400000L отлично справляется с двумя дисплеями I2C (тут важна сама скорость процессора).Снизил до 20мгц и уже два дисплея начинают притормаживать.