Добавлен код анализатора спектра
This commit is contained in:
		| @@ -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(); | ||||
| @@ -89,8 +128,20 @@ void setup() { | ||||
|     bluetooth = EEPROM.read(12); | ||||
|     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 | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Калинин Сергей Валерьевич
					Калинин Сергей Валерьевич