Добавлен код анализатора спектра
This commit is contained in:
parent
524de58d3a
commit
40de9299d1
|
@ -2,6 +2,7 @@
|
|||
//https://www.instructables.com/Simple-LCD-MENU-Using-Arduino/
|
||||
// https://alexgyver.ru/lessons/
|
||||
// https://www.hackster.io/mircemk/diy-arduino-vfd-display-20x2-vu-volume-unit-meter-37898f
|
||||
// https://github.com/AlexGyver/FHTSpectrumAnalyzer/blob/master/Firmware/spertrum1602/spertrum1602.ino
|
||||
|
||||
#define GAIN 5 // усиление 0...50
|
||||
#define STEP 20 // плавность полос 0...20
|
||||
|
@ -21,27 +22,65 @@
|
|||
#define INIT_ADDR 1023 // номер резервной ячейки
|
||||
#define INIT_KEY 1 // ключ первого запуска
|
||||
|
||||
//#include <LiquidCrystal.h>
|
||||
// ---------------- НАСТРОЙКИ ----------------
|
||||
#define DRIVER_VERSION 0 // 0 - маркировка драйвера кончается на 4АТ, 1 - на 4Т
|
||||
#define GAIN_CONTROL 0 // ручная настройка потенциометром на громкость (1 - вкл, 0 - выкл)
|
||||
|
||||
#define AUTO_GAIN 0 // автонастройка по громкости (экспериментальная функция)
|
||||
#define VOL_THR 35 // порог тишины (ниже него отображения на матрице не будет)
|
||||
|
||||
#define LOW_PASS 30 // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
|
||||
#define DEF_GAIN 80 // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
|
||||
|
||||
#define FHT_N 256 // ширина спектра х2
|
||||
// вручную забитый массив тонов, сначала плавно, потом круче
|
||||
byte posOffset[16] = {2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 25, 30, 35, 60, 80, 100};
|
||||
// ---------------- НАСТРОЙКИ ----------------
|
||||
|
||||
// ---------------------- ПИНЫ ----------------------
|
||||
#define AUDIO_IN_L 0 // пин, куда подключен звук
|
||||
#define AUDIO_IN_R 1 // пин, куда подключен звук
|
||||
#define POT_PIN 7 // пин потенциометра настройки
|
||||
// ---------------------- ПИНЫ ----------------------
|
||||
|
||||
|
||||
// --------------- БИБЛИОТЕКИ ---------------
|
||||
#define LOG_OUT 1
|
||||
#include <FHT.h> // преобразование Хартли
|
||||
#include <Wire.h>
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
#define printByte(args) write(args);
|
||||
double prevVolts = 100.0;
|
||||
// --------------- БИБЛИОТЕКИ ---------------
|
||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
// LiquidCrystal lcdVuMeter(2, 3, 4, 5, 6, 7);// RS,E,D4,D5,D6,D7
|
||||
LiquidCrystal_I2C lcdVuMeter(0x27, 16, 2);
|
||||
LiquidCrystal_I2C lcdService(0x26, 16, 2);
|
||||
|
||||
byte z,z0,z1,w,www=1;
|
||||
int ur,ul,urr,ull,urrr,ulll;
|
||||
int x,i,u_maxr,u_maxl;
|
||||
int u_l0[20],u_r0[20];
|
||||
bool usb, bluetooth, mp3;
|
||||
int menu = 1;
|
||||
bool menuShow = false;
|
||||
|
||||
// ------------------------------------- ПОЛОСОЧКИ -------------------------------------
|
||||
byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111};
|
||||
byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111};
|
||||
byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111};
|
||||
byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111};
|
||||
byte v5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
|
||||
byte v6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
|
||||
byte v7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
|
||||
byte v8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
|
||||
// ------------------------------------- ПОЛОСОЧКИ -------------------------------------
|
||||
byte gain = DEF_GAIN; // усиление по умолчанию
|
||||
unsigned long gainTimer;
|
||||
byte maxValue, maxValue_f;
|
||||
float k = 0.1;
|
||||
|
||||
void setup() {
|
||||
pinMode(8,INPUT_PULLUP);
|
||||
byte znak_r[8]={0b00000,0b00000,0b11111,0b00101,0b00101,0b11010,0b00000,0b00000};
|
||||
byte znak_l[8]={0b00000,0b00000,0b11111,0b10000,0b10000,0b10000,0b00000,0b00000};
|
||||
lcdVuMeter.init();
|
||||
lcdVuMeter.backlight();
|
||||
lcdVuMeter.clear();
|
||||
|
@ -90,7 +129,19 @@ void setup() {
|
|||
mp3 = EEPROM.read(14);
|
||||
}
|
||||
|
||||
w = EEPROM.read(0);
|
||||
// поднимаем частоту опроса аналогового порта до 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);
|
||||
|
||||
// для увеличения точности уменьшаем опорное напряжение,
|
||||
// выставив EXTERNAL и подключив Aref к выходу 3.3V на плате через делитель
|
||||
// GND ---[2х10 кОм] --- REF --- [10 кОм] --- 3V3
|
||||
analogReference(EXTERNAL);
|
||||
|
||||
|
||||
if (usb) {
|
||||
digitalWrite(RELAY_POWER_BT,HIGH);
|
||||
digitalWrite(RELAY_POWER_MP3,HIGH);
|
||||
|
@ -115,82 +166,22 @@ void setup() {
|
|||
}
|
||||
if (mp3) {
|
||||
}
|
||||
lcdChars(); // подхватить коды полосочек
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
void vu_metter() {
|
||||
|
||||
// if(digitalRead(8)==LOW){w++;www=1;if(w>4){w=0;}delay(200);EEPROM.update(0,w);}
|
||||
|
||||
if(w==0&&www==1){www=0;
|
||||
byte a1[8] = {0b10101,0b10101,0b10101,0b10101,0b10101,0b10101,0b10101,0b10101};
|
||||
byte a2[8] = {0b10100,0b10100,0b10100,0b10100,0b10100,0b10100,0b10100,0b10100};
|
||||
byte a3[8] = {0b10000,0b10000,0b10000,0b10000,0b10000,0b10000,0b10000,0b10000};
|
||||
byte a4[8] = {0b00011,0b00011,0b00011,0b0011,0b0011,0b0011,0b0011,0b0011};//
|
||||
lcdVuMeter.createChar(0,a1);lcdVuMeter.createChar(1,a2);lcdVuMeter.createChar(2,a3);lcdVuMeter.createChar(6,a4);}
|
||||
if(w==1&&www==1){www=0;
|
||||
byte a1[8] = {0b00000,0b10101,0b10101,0b10101,0b10101,0b10101,0b10101,0b00000};
|
||||
byte a2[8] = {0b00000,0b10100,0b10100,0b10100,0b10100,0b10100,0b10100,0b00000};
|
||||
byte a3[8] = {0b00000,0b10000,0b10000,0b10000,0b10000,0b10000,0b10000,0b00000};
|
||||
byte a4[8] = {0b00000,0b0011,0b0011,0b0011,0b0011,0b0011,0b0011,0b00000};//
|
||||
lcdVuMeter.createChar(0,a1);lcdVuMeter.createChar(1,a2);lcdVuMeter.createChar(2,a3);lcdVuMeter.createChar(6,a4);}
|
||||
if(w==2&&www==1){www=0;
|
||||
byte a1[8] = {0b10101,0b10101,0b10101,0b00000,0b00000,0b10101,0b10101,0b10101};
|
||||
byte a2[8] = {0b10100,0b10100,0b10100,0b00000,0b00000,0b10100,0b10100,0b10100};
|
||||
byte a3[8] = {0b10000,0b10000,0b10000,0b00000,0b00000,0b10000,0b10000,0b10000};
|
||||
byte a4[8] = {0b0011,0b0011,0b0011,0b0011,0b0011,0b0011,0b0011,0b0011};
|
||||
lcdVuMeter.createChar(0,a1);lcdVuMeter.createChar(1,a2);lcdVuMeter.createChar(2,a3);lcdVuMeter.createChar(6,a4);}
|
||||
if(w==3&&www==1){www=0;
|
||||
byte a1[8] = {0b00000,0b10101,0b10101,0b00000,0b00000,0b10101,0b10101,0b00000};
|
||||
byte a2[8] = {0b00000,0b10100,0b10100,0b00000,0b00000,0b10100,0b10100,0b00000};
|
||||
byte a3[8] = {0b00000,0b10000,0b10000,0b00000,0b00000,0b10000,0b10000,0b00000};
|
||||
byte a4[8] = {0b00000,0b0011,0b0011,0b0011,0b0011,0b0011,0b0011,0b00000};
|
||||
lcdVuMeter.createChar(0,a1);lcdVuMeter.createChar(1,a2);lcdVuMeter.createChar(2,a3);lcdVuMeter.createChar(6,a4);}
|
||||
if(w==4&&www==1){www=0;
|
||||
byte a1[8] = {0b00000,0b00000,0b10101,0b10101,0b10101,0b10101,0b00000,0b00000};
|
||||
byte a2[8] = {0b00000,0b00000,0b10100,0b10100,0b10100,0b10100,0b00000,0b00000};
|
||||
byte a3[8] = {0b00000,0b00000,0b10000,0b10000,0b10000,0b10000,0b00000,0b00000};
|
||||
byte a4[8] = {0b00000,0b00000,0b0011,0b0011,0b0011,0b0011,0b00000,0b00000};
|
||||
lcdVuMeter.createChar(0,a1);lcdVuMeter.createChar(1,a2);lcdVuMeter.createChar(2,a3);lcdVuMeter.createChar(6,a4);}
|
||||
|
||||
urr = log(analogRead(0))*GAIN;if(urr>41){urr=41;}
|
||||
ull = log(analogRead(1))*GAIN;if(ull>41){ull=41;}
|
||||
|
||||
if(RL==0){lcdVuMeter.setCursor(0,0);lcdVuMeter.write((uint8_t)4);lcdVuMeter.setCursor(0,1);lcdVuMeter.write((uint8_t)5);}
|
||||
if(RL==1){lcdVuMeter.setCursor(0,1);lcdVuMeter.print("L");lcdVuMeter.setCursor(0,0);lcdVuMeter.print("R");}
|
||||
|
||||
if(urr<ur){ur=ur-1;delay(STEP);}else{ur = urr;}
|
||||
for(z=0,z0=0,z1=0;z<=ur;z++,z1++){if(z1>2){z1=0;z0++;}
|
||||
if(z1==1){lcdVuMeter.setCursor(z0+1,1);lcdVuMeter.write((uint8_t)0);lcdVuMeter.setCursor(z0+2,1);if(ur<39){lcdVuMeter.print(" ");}}}
|
||||
if(z1==3){lcdVuMeter.setCursor(z0+1,1);lcdVuMeter.write((uint8_t)1);}
|
||||
if(z1==2){lcdVuMeter.setCursor(z0+1,1);lcdVuMeter.write((uint8_t)2);}
|
||||
|
||||
if(ull<ul){ul=ul-1;delay(STEP);}else{ul = ull;}
|
||||
for(z=0,z0=0,z1=0;z<=ul;z++,z1++){if(z1>2){z1=0;z0++;}
|
||||
if(z1==1){lcdVuMeter.setCursor(z0+1,0);lcdVuMeter.write((uint8_t)0);lcdVuMeter.setCursor(z0+2,0);if(ul<39){lcdVuMeter.print(" ");}}}
|
||||
if(z1==3){lcdVuMeter.setCursor(z0+1,0);lcdVuMeter.write((uint8_t)1);}
|
||||
if(z1==2){lcdVuMeter.setCursor(z0+1,0);lcdVuMeter.write((uint8_t)2);}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
i++;if(i<19){u_l0[i]=abs(ull);u_r0[i]=abs(urr);}else{i=1;}
|
||||
if(i==18){u_maxr=0;u_maxl=0;
|
||||
for(x=1;x<=15;x++){
|
||||
u_maxl=max(u_maxl,u_l0[x]);
|
||||
u_maxr=max(u_maxr,u_r0[x]);}}
|
||||
|
||||
if(u_maxl<=ull){u_maxl=ull+1;}
|
||||
if(u_maxr<=urr){u_maxr=urr+1;}
|
||||
|
||||
if(u_maxl<ulll){ulll=ulll-1;delay(STEP);}else{ulll = u_maxl;}
|
||||
if(u_maxr<urrr){urrr=urrr-1;delay(STEP);}else{urrr = u_maxr;}
|
||||
|
||||
|
||||
lcdVuMeter.setCursor(ulll/3+1,0);if(ulll/3>2){lcdVuMeter.write((uint8_t)6);}lcdVuMeter.print(" ");
|
||||
lcdVuMeter.setCursor(urrr/3+1,1);if(urrr/3>2){lcdVuMeter.write((uint8_t)6);}lcdVuMeter.print(" ");
|
||||
delay(3);
|
||||
void lcdChars() {
|
||||
lcdVuMeter.createChar(0, v1);
|
||||
lcdVuMeter.createChar(1, v2);
|
||||
lcdVuMeter.createChar(2, v3);
|
||||
lcdVuMeter.createChar(3, v4);
|
||||
lcdVuMeter.createChar(4, v5);
|
||||
lcdVuMeter.createChar(5, v6);
|
||||
lcdVuMeter.createChar(6, v7);
|
||||
lcdVuMeter.createChar(7, v8);
|
||||
}
|
||||
|
||||
|
||||
String setUSBinput() {
|
||||
return "Input: USB";
|
||||
}
|
||||
|
@ -314,6 +305,7 @@ void actionMP3() {
|
|||
delay(1500);
|
||||
}
|
||||
|
||||
|
||||
uint32_t btnTimer = 0;
|
||||
|
||||
void loop() {
|
||||
|
@ -368,6 +360,50 @@ void loop() {
|
|||
// }
|
||||
// lcdService.setCursor(11,1);
|
||||
// lcdService.print(digitalRead(8), digitalRead(9));
|
||||
if (GAIN_CONTROL) gain = map(analogRead(POT_PIN), 0, 1023, 0, 150);
|
||||
|
||||
vu_metter();
|
||||
analyzeAudio(); // функция FHT, забивает массив fht_log_out[] величинами по спектру
|
||||
|
||||
for (int pos = 0; pos < 16; pos++) { // для окошек дисплея с 0 по 15
|
||||
// найти максимум из пачки тонов
|
||||
if (fht_log_out[posOffset[pos]] > maxValue) maxValue = fht_log_out[posOffset[pos]];
|
||||
|
||||
lcdVuMeter.setCursor(pos, 0);
|
||||
|
||||
// преобразовать значение величины спектра в диапазон 0..15 с учётом настроек
|
||||
int posLevel = map(fht_log_out[posOffset[pos]], LOW_PASS, gain, 0, 15);
|
||||
posLevel = constrain(posLevel, 0, 15);
|
||||
|
||||
if (posLevel > 7) { // если значение больше 7 (значит нижний квадратик будет полный)
|
||||
lcdVuMeter.printByte(posLevel - 8); // верхний квадратик залить тем что осталось
|
||||
lcdVuMeter.setCursor(pos, 1); // перейти на нижний квадратик
|
||||
lcdVuMeter.printByte(7); // залить его полностью
|
||||
} else { // если значение меньше 8
|
||||
lcdVuMeter.print(" "); // верхний квадратик пустой
|
||||
lcdVuMeter.setCursor(pos, 1); // нижний квадратик
|
||||
lcdVuMeter.printByte(posLevel); // залить полосками
|
||||
}
|
||||
}
|
||||
|
||||
if (AUTO_GAIN) {
|
||||
maxValue_f = maxValue * k + maxValue_f * (1 - k);
|
||||
if (millis() - gainTimer > 1500) { // каждые 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(AUDIO_IN_L) + analogRead(AUDIO_IN_R)) / 2;
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user