Тема: Анализатор спектра с пиковыми индикаторами на 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
}
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
}