Темброблок на TDA8425 (Arduino)

ИМС TDA8425 представляет собой двухканальный (стереофонический) регулятор громкости и тембра с микропроцессорным управлением.

Технические характеристики:

  • Производитель: Philips
  • Напряжение питания минимальное 7 В
  • Напряжение питания максимальное 13,2 В
  • Частотный диапазон 35…20000 Гц
  • Коэффициент гармоник 0,05%
  • Выходное напряжение максимальное 1 В
  • Регулировка громкости 48 уровней от -88 дБ до +6 дБ с шагом 2 дБ
  • Регулировка тембра НЧ  от -12 дБ до +15 дБ с шагом 3 дБ
  • Регулировка тембра ВЧ  от -12 дБ до +12 дБ с шагом 3 дБ
  • Регулировка баланса 8 дБ с шагом 2 дБ

 

Плата Arduino Nano аудиопроцессор TDA8425 обмениваются данными на шине I2C по линиям SDA (data — данные) и SCL (clock — синхронизация).

Темброблок имеет пять кнопок управления:

  • МЕНЮ — перебор параметров (громкость, баланс, тембр НЧ, тембр ВЧ, выбор канала)
  • ПЛЮС — прибавление громкости, тембра и др.
  • МИНУС  — уменьшение громкости, тембра и др.
  • MUTE — включение/выключение режима MUTE
  • — 20 дБ — при нажатии на кнопку громкость уменьшается на 20 дБ

Если кнопки регулировок «ПЛЮС» и «МИНУС» и кнопка «МЕНЮ» не были активны в течении 10 секунд, то происходит запись всех настроек в энергонезависимую память (функция EEPROM.update не перезаписывает ячейку памяти если значение переменной не менялось), так же происходит переход из режима регулировок тембра, баланса, выбора входа в режим регулировки громкости.

Если в режиме MUTE нет необходимости, то выход D9 необходимо подключить к GND.

  #include <Wire.h>
  #include <TDA8425.h>
  #include <LiquidCrystal.h>
  #include <EEPROM.h>//#include <EEPROMex.h>
  TDA8425 tda;
  LiquidCrystal lcd(7, 6, 2, 3, 4, 5);// RS,E,D4,D5,D6,D7
  byte a1[8]={0b00000,0b11011,0b11011,0b11011,0b11011,0b11011,0b11011,0b00000};
  byte a2[8]={0b00000,0b11000,0b11000,0b11000,0b11000,0b11000,0b11000,0b00000};
  int menu,vol_d,z,w,w1,bass_d,treb,treb_d,balans,db;
  byte vol,bass,in,mute=0;
  unsigned long time;
 
void setup(){
  Serial.begin(9600);
  lcd.begin(16, 2);
  pinMode(12,INPUT);// меню
  pinMode(11,INPUT);// плюс
  pinMode(10,INPUT);// минус
  pinMode(9,INPUT);// mute
  pinMode(8,INPUT);// -20дБ
  lcd.createChar(0,a1); lcd.createChar(1,a2); 
  vol = EEPROM.read(0);// vol eeprom
  bass = EEPROM.read(1);// bass eeprom
  treb = EEPROM.read(2);// treb eeprom
  in = EEPROM.read(3);// in eeprom
  balans = EEPROM.read(4)-4;// balans eeprom
   audio();
  lcd.setCursor(4,0);lcd.print("TDA8425");delay(500);
 }
 
void loop(){
   if(digitalRead(12)==HIGH){menu++;mute=0;audio();cl();time=millis();w1=1;if(menu>4){menu=0;}}// меню
 
 // индикация громкости + управление кнопками //////////////
   if(menu==0){
   if(digitalRead(11)==HIGH){vol++;w=1;if(vol>63){vol=63;}}// 63 максимальная громкость = +6 дБ 
   if(digitalRead(10)==HIGH){vol--;w=1;if(vol<=16){vol=16;}}// 16 минимум громкости = -88 дБ
  lcd.setCursor(0,0);lcd.print("Volume    ");
  vol_d=vol*2-120;
   if(vol_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(vol_d>-10){{lcd.print(" ");}}
  lcd.print(abs(vol_d));lcd.print(" dB   ");
 // ползунок громкости начинает работать с -56 дБ
   if(vol-32>0){for(z=0;z<=vol-33;z++){lcd.setCursor(z/2,1);lcd.write((uint8_t)0);}}
   if((vol-32)%2==0&&vol-32>=0){lcd.setCursor(z/2,1);lcd.write((uint8_t)1);}
   if(w==1){audio();cl();time=millis();w1=1;w=0;}
 }
 ///////////////////////////////////////////////////////////
 
 // индикация баланс + управление кнопками +\- 8 дБ ///////
   if(menu==1){
   if(digitalRead(11)==HIGH){balans++;w=1;if(balans>4){balans=4;}}
   if(digitalRead(10)==HIGH){balans--;w=1;if(balans<-4){balans=-4;}}
  lcd.setCursor(0,0);
   if(balans*2>=0){lcd.print("-");}else{lcd.print("+");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.print(" <>  ");
   if(balans*2>=0){lcd.print("+");}else{lcd.print("-");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.setCursor(0,1);lcd.print("R");
  lcd.setCursor(15,1);lcd.print("L");
   if(balans<0){lcd.setCursor(balans+7,1);lcd.write((uint8_t)0);}
   if(balans>0){lcd.setCursor(balans+8,1);lcd.write((uint8_t)0);}
   if(balans==0){lcd.setCursor(7,1);lcd.write((uint8_t)0);lcd.setCursor(8,1);lcd.write((uint8_t)0);}
   if(w==1){audio();cl();time=millis();w1=1;w=0;}
 }
 /////////////////////////////////////////////////////////////
 
 // индикация тембр НЧ + управление кнопками //////////////
   if(menu==2){
   if(digitalRead(11)==HIGH){bass++;w=1;if(bass>11){bass=11;}}// 11 максимум тембр = +15 дБ 
   if(digitalRead(10)==HIGH){bass--;w=1;if(bass<2||bass==255){bass=2;}}// 2 минимум тембр = -12 дБ
  lcd.setCursor(0,0);lcd.print("Bass      ");
  bass_d=bass*3-18;
   if(bass_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(bass_d>-10&&bass_d<=0||bass_d<10&&bass_d>0){{lcd.print(" ");}}
  lcd.print(abs(bass_d));lcd.print(" dB   ");
   if(bass>1){for(z=3;z<=bass+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
   if(w==1){audio();cl();time=millis();w1=1;w=0;}
 }
 ///////////////////////////////////////////////////////////
 
 // индикация тембр ВЧ + управление кнопками //////////////
   if(menu==3){
   if(digitalRead(11)==HIGH){treb++;w=1;if(treb>10){treb=10;}}// 10 максимум тембр = +12 дБ 
   if(digitalRead(10)==HIGH){treb--;w=1;if(treb<2||treb==255){treb=2;}}// 2 минимум тембр = -12 дБ
  lcd.setCursor(0,0);lcd.print("Treble    ");
  treb_d=treb*3-18;
   if(treb_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(treb_d>-10&&treb_d<=0||treb_d<10&&treb_d>0){{lcd.print(" ");}}
  lcd.print(abs(treb_d));lcd.print(" dB   ");
   if(treb>1){for(z=3;z<=treb+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
   if(w==1){audio();cl();time=millis();w1=1;w=0;}
 }
 ///////////////////////////////////////////////////////////
 
 /////////// input /////////////////////////////////////////
   if(menu==4){
   if(digitalRead(11)==HIGH){in++;w=1;if(in>1&&in<255){in=0;}}
   if(digitalRead(10)==HIGH){in--;w=1;if(in>2){in=1;}}
  lcd.setCursor(0,0);lcd.print("Source selector");
  lcd.setCursor(0,1);lcd.print("Input: ");lcd.print(in);
   if(w==1){audio();cl();time=millis();w1=1;w=0;}
 }
////////////////////////////////////////////////////////////
 
////////////////// mute //////////////////////////////////
   if(digitalRead(9)==HIGH){mute++;if(mute>1){mute=0;}
   if(mute==1){menu=-1;}else{menu=0;}
  audio();cl();
  lcd.setCursor(6,0);lcd.print("MUTE");
 }
///////////////////////////////////////////////////////////
 
///////////////////  -20 dB ///////////////////////////////
   if(digitalRead(8)==HIGH&&db==0){vol=vol-10;db=1;delay(500);audio();cl();}
   if(digitalRead(8)==HIGH&&db==1){vol=vol+10;db=0;delay(500);audio();cl();}
//////////////////////////////////////////////////////////
 
// запись всех настроек в EEPROM если кнопки + и - не нажимались в течении 10 сек
// если настройки тембра, баланса и номер входа не менялись в течении 10 сек, переход в рег. Громкости
   if(millis()-time>10000 && w1==1){
     EEPROM.update(0,vol);
     EEPROM.update(4,balans+4);
     EEPROM.update(1,bass);
     EEPROM.update(2,treb);
     EEPROM.update(3,in);
  menu=0;w1=0;cl();
 }
}
 
void cl(){
  delay(200);
  lcd.clear();
 }
 
void audio(){
  tda.setVolumeL(vol+balans);
  tda.setVolumeR(vol-balans);
  tda.setBass(bass);
  tda.setTreble(treb);
  tda.setMute(mute);
  tda.setSource(in);
 }

Библиотеки

TDA8425.ZIP 

MsTimer2.ZIP

Encoder.zip


Следующий вариант темброблока аналогичен первому, только вместо кнопок управления «ПЛЮС» и «МИНУС» и  «МЕНЮ» применен энкодер.

В данном варианте регулятора тембра и громкости, меню разделено на две части, первое меню это настройка громкости и тембра, второе меню это настройка баланса и выбор источника сигнала. При нажатии на кнопку энкодера происходит перебор основных параметров (громкость и тембр), для того чтобы попасть во второе меню необходимо нажать и удерживать кнопку энкодера, при быстром пролистывании основных параметров появится меню настройки баланса, при повторном нажатии кнопки энкодера меню выбора источника сигнала.

Если энкодер не активен в течении 10 секунд происходит переход на регулировку громкости и запись всех настроек в энергонезависимую память.

  #include <Wire.h>
  #include <TDA8425.h>
  #include <Encoder.h>
  #include <LiquidCrystal.h>
  #include <EEPROM.h>//
 // #include <EEPROMex.h>
  #include <MsTimer2.h>
   Encoder myEnc(11, 10);//CLK, DT
   TDA8425 tda;
   LiquidCrystal lcd(7, 6, 2, 3, 4, 5);// RS,E,D4,D5,D6,D7
   byte a1[8]={0b00000,0b11011,0b11011,0b11011,0b11011,0b11011,0b11011,0b00000};
   byte a2[8]={0b00000,0b11000,0b11000,0b11000,0b11000,0b11000,0b11000,0b00000};
   int z,vol,vol_d,balans,bass,bass_d,treb,treb_d,in,mute,sss,db;
   byte menu,w1;
   long oldPosition  = -999,newPosition;
   unsigned long time;
 
   void to_Timer(){ //функция таймера
 newPosition = myEnc.read()/4;}
 
void setup() {
  Serial.begin(9600);
   lcd.begin(16, 2);
   pinMode(12,INPUT);// меню кнопка энкодера SW
   lcd.createChar(0,a1); lcd.createChar(1,a2); 
   vol = EEPROM.read(0);// vol eeprom
   bass = EEPROM.read(1);// bass eeprom
   treb = EEPROM.read(2);// treb eeprom
   in = EEPROM.read(3);// in eeprom
   balans = EEPROM.read(4)-4;// balans eeprom
   audio();
   lcd.setCursor(4,0);lcd.print("TDA8425");delay(500);
   MsTimer2::set(1, to_Timer);MsTimer2::start();
}
 
void loop() {
   if(digitalRead(12)==LOW&&menu<3){sss++;menu++;myEnc.write(0);oldPosition  = -999;delay(300);time=millis();w1=1;if(menu>2){menu=0;}if(sss>2){menu=3;delay(300);}}// меню
   if(digitalRead(12)==HIGH){sss=0;}
   if(menu==3&&digitalRead(12)==LOW&&sss==0){menu=4;oldPosition  = -999;delay(300);time=millis();w1=1;lcd.clear();}
   if(menu==4&&digitalRead(12)==LOW){menu=0;delay(300);lcd.clear();}
   ///////////////////////// громкость /////////////////////////////
   if(menu==0){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    vol=vol+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(vol>63){vol=63;}if(vol<16){vol=16;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Volume    ");
  vol_d=vol*2-120;
   if(vol_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(vol_d>-10){{lcd.print(" ");}}
  lcd.print(abs(vol_d));lcd.print(" dB   ");
 // ползунок громкости начинает работать с -56 дБ
   if(vol-32>0){for(z=0;z<=vol-33;z++){lcd.setCursor(z/2,1);lcd.write((uint8_t)0);}}
   if((vol-32)%2==0&&vol-32>=0){lcd.setCursor(z/2,1);lcd.write((uint8_t)1);}
  }
   ///////////////////////// баланс /////////////////////////////
  if(menu==3){
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    balans=balans+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(balans>4){balans=4;}if(balans<-4){balans=-4;}audio();}
 
  lcd.setCursor(0,0);
   if(balans*2>=0){lcd.print("-");}else{lcd.print("+");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.print(" <>  ");
   if(balans*2>=0){lcd.print("+");}else{lcd.print("-");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.setCursor(0,1);lcd.print("L");
  lcd.setCursor(15,1);lcd.print("R");
   if(balans<0){lcd.setCursor(balans+7,1);lcd.write((uint8_t)0);}
   if(balans>0){lcd.setCursor(balans+8,1);lcd.write((uint8_t)0);}
   if(balans==0){lcd.setCursor(7,1);lcd.write((uint8_t)0);lcd.setCursor(8,1);lcd.write((uint8_t)0);}
}
  ///////////////////////// тембр нч /////////////////////////////
  if(menu==1){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    bass=bass+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(bass>11){bass=11;}if(bass<2){bass=2;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Bass      ");
  bass_d=bass*3-18;
   if(bass_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(bass_d>-10&&bass_d<=0||bass_d<10&&bass_d>0){{lcd.print(" ");}}
  lcd.print(abs(bass_d));lcd.print(" dB   ");
   if(bass>1){for(z=3;z<=bass+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
 }
   ///////////////////////// тембр вч /////////////////////////////
  if(menu==2){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    treb=treb+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(treb>11){treb=11;}if(treb<2){treb=2;}audio();}
 
   if(treb>10){treb=10;}if(treb<2||treb==255){treb=2;}
  lcd.setCursor(0,0);lcd.print("Treble    ");
  treb_d=treb*3-18;
   if(treb_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(treb_d>-10&&treb_d<=0||treb_d<10&&treb_d>0){{lcd.print(" ");}}
  lcd.print(abs(treb_d));lcd.print(" dB   ");
   if(treb>1){for(z=3;z<=treb+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
 }
  ///////////////////////// input /////////////////////////////
  if(menu==4){
if (newPosition != oldPosition) {
    oldPosition = newPosition;
    in=in+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(in>1){in=0;}if(in<0){in=1;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Source selector");
  lcd.setCursor(0,1);lcd.print("Input: ");lcd.print(in);
 }
////////////////// mute //////////////////////////////////
   if(digitalRead(9)==HIGH){mute++;if(mute>1){mute=0;}
   if(mute==1){menu=-1;}else{oldPosition  = -999;menu=0;}
  audio(); delay(300);lcd.clear();
  lcd.setCursor(6,0);lcd.print("MUTE");
 }
///////////////////////////////////////////////////////////
 
///////////////////  -20 dB ///////////////////////////////
    if(digitalRead(8)==HIGH&&db==0){vol=vol-10;db=1;delay(500);audio();myEnc.write(0);menu=0;lcd.clear();}
    if(digitalRead(8)==HIGH&&db==1){vol=vol+10;db=0;delay(500);audio();myEnc.write(0);menu=0;lcd.clear();}
//////////////////////////////////////////////////////////
 
 
   if(millis()-time>10000 && w1==1){// сохранение всех настроек в eeprom через 60 сек неактивности
     myEnc.write(0);
     EEPROM.update(0,vol);
     EEPROM.update(4,balans+4);
     EEPROM.update(1,bass);
     EEPROM.update(2,treb);
     EEPROM.update(3,in);
     w1=0;menu=0;lcd.clear(); }
 
 }// loop
 
void audio(){
  tda.setVolumeL(vol+balans);
  tda.setVolumeR(vol-balans);
  tda.setBass(bass);
  tda.setTreble(treb);
  tda.setMute(mute);
  tda.setSource(in);
 }

Видео


Следующий скетч позволяет добавить анализатор спектра, при этом линейный входной сигнал необходимо  подать на вход А0. При неактивности кнопок управления и энкодера через 10 секунд происходит запись всех измененных параметров в энергонезависимую память и активация анализатора спектра, если нажать кнопку энкодера или покрутить его ручку анализатор спектра прекращает работать, и осуществляется переход в основное меню (громкость).

Дополнительно во второе меню (нажать и удерживать кнопку энкодера) добавлены настройки анализатора спектра:

  • AUTO_GAIN — автонастройка по громкости (0…1)
  • VOL_THR — порог тишины, ниже него отображения на экране не будет, это значение всегда должно быть быть меньше DEF_GAIN (10…100) (25-30 оптимально), параметр активен когда AUTO_GAIN = 1
  • LOW_PASS — нижний порог чувствительности шумов, нет скачков при отсутствии звука, это значение должно быть меньше DEF_GAIN (10..100) (35-40 оптимально)
  • DEF_GAIN — максимальный порог по умолчанию, это значение всегда должно быть выше VOL_THR и LOW_PASS (30…200)(80-120 оптимально), параметр активен когда AUTO_GAIN = 0
  • GRAPH_ATT — аттенюатор чувствительности (0,5…1,5), параметр активен когда AUTO_GAIN = 1
  • GRAPHICS — изменение вида графических полос (0…4)
  • Conv. speed — скорость преобразования для аналогового входа (чем больше значение тем более узкий частотный диапазон):
    • 0 — CLK/4
    • 1 — CLK/8 (оптимально)
    • 2  — CLK/16
    • 3  — CLK/32
    • 4  — CLK/64
    • 5  — CLK/128

Для перехода по параметрам настроек анализатора спектра необходимо нажимать кнопку MUTE.

Скетч не тестировался, возможно требуется правка кода.

Дополнительная библиотека FHT.ZIP

  #define FHT_N 256          // ширина спектра х2
  #define LOG_OUT 1
  #include <FHT.h>          
  #include <LiquidCrystal.h>
  #include <Wire.h>
  #include <TDA8425.h>
  #include <Encoder.h>
  #include <EEPROM.h>
 // #include <EEPROMex.h>
  #include <MsTimer2.h>
   LiquidCrystal lcd(7, 6, 2, 3, 4, 5);// RS,E,D4,D5,D6,D7
   Encoder myEnc(11, 10);//CLK, DT
   TDA8425 tda;
  // вручную забитый массив тонов, сначала плавно, потом круче
   byte posOffset[16] = {2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
   int AUTO_GAIN;    // автонастройка по громкости (экспериментальная функция)
   int VOL_THR;       // порог тишины (ниже него отображения на экране не будет)
   int LOW_PASS;        // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
   int DEF_GAIN;     // максимальный порог по умолчанию (при GAIN_CONTROL игнорируется)
   unsigned long gainTimer,time;
   byte maxValue, maxValue_f,www,www1,spek,menu,w1,w2,y;
   float k = 0.1,k_usil;
   int z,vol,vol_d,balans,bass,bass_d,treb,treb_d,in,mute,sss,i,sp,graf,k_us,gain,i2,sped,db;
   long oldPosition  = -999,newPosition;
 
    void to_Timer(){ //функция таймера
    newPosition = myEnc.read()/4;}
 
void setup() { Serial.begin(9600);
  lcd.begin(16, 2);// LCD 16X2
  ADMUX  = 0b11100000; 
  //ADCSRA = 0b11110011;
   if(EEPROM.read(0)>100){ // используется только один раз при первом запуске, если ячейка памяти для громкости вне диапазона регулирования
     EEPROM.update(0,40);  // установка всех настроек по умолчанию
     EEPROM.update(4,0);
     EEPROM.update(1,0);
     EEPROM.update(2,0);
     EEPROM.update(3,0);
     EEPROM.update(5,1);
     EEPROM.update(6,30);
     EEPROM.update(7,35);
     EEPROM.update(8,80);
     EEPROM.update(9,0);
     EEPROM.update(10,10);
     EEPROM.update(11,0);
   }
   pinMode(12,INPUT);// меню кнопка энкодера SW
   vol = EEPROM.read(0);// vol eeprom
   bass = EEPROM.read(1);// bass eeprom
   treb = EEPROM.read(2);// treb eeprom
   in = EEPROM.read(3);// in eeprom
   balans = EEPROM.read(4)-4;// balans eeprom
   AUTO_GAIN = EEPROM.read(5);
   VOL_THR = EEPROM.read(6);
   LOW_PASS = EEPROM.read(7);
   DEF_GAIN = EEPROM.read(8);
   graf = EEPROM.read(9);
   k_us = EEPROM.read(10);
   sped = EEPROM.read(11);
   audio();
   lcd.setCursor(4,0);lcd.print("TDA8425");delay(500);
   MsTimer2::set(1, to_Timer);MsTimer2::start();
}
 
void loop() {
  if(spek==1){
 
  if(www==1){
             if(graf==0){
             byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11011};
             byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11011, 0b11011};
             byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11011, 0b11011, 0b11011};
             byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11011, 0b11011, 0b11011, 0b11011};
             byte v5[8] = {0b00000, 0b00000, 0b00000, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011};
             byte v6[8] = {0b00000, 0b00000, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011};
             byte v7[8] = {0b00000, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011};
             byte v8[8] = {0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011, 0b11011};
             lcd.createChar(0, v1);lcd.createChar(1, v2);lcd.createChar(2, v3);lcd.createChar(3, v4);
             lcd.createChar(4, v5);lcd.createChar(5, v6);lcd.createChar(6, v7);lcd.createChar(7, v8);
         }
             if(graf==1){
             byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01110};
             byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01110, 0b01110};
             byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01110, 0b01110, 0b01110};
             byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b01110, 0b01110, 0b01110, 0b01110};
             byte v5[8] = {0b00000, 0b00000, 0b00000, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110};
             byte v6[8] = {0b00000, 0b00000, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110};
             byte v7[8] = {0b00000, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110};
             byte v8[8] = {0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110, 0b01110};
             lcd.createChar(0, v1);lcd.createChar(1, v2);lcd.createChar(2, v3);lcd.createChar(3, v4);
             lcd.createChar(4, v5);lcd.createChar(5, v6);lcd.createChar(6, v7);lcd.createChar(7, v8);}
 
             if(graf==2){
             byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01010};
             byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01010, 0b01010};
             byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01010, 0b01010, 0b01010};
             byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b01010, 0b01010, 0b01010, 0b01010};
             byte v5[8] = {0b00000, 0b00000, 0b00000, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010};
             byte v6[8] = {0b00000, 0b00000, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010};
             byte v7[8] = {0b00000, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010};
             byte v8[8] = {0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010};
             lcd.createChar(0, v1);lcd.createChar(1, v2);lcd.createChar(2, v3);lcd.createChar(3, v4);
             lcd.createChar(4, v5);lcd.createChar(5, v6);lcd.createChar(6, v7);lcd.createChar(7, v8);}
 
             if(graf==3){
             byte v1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b10101};
             byte v2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b10101, 0b10101};
             byte v3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b10101, 0b10101, 0b10101};
             byte v4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b10101, 0b10101, 0b10101, 0b10101};
             byte v5[8] = {0b00000, 0b00000, 0b00000, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101};
             byte v6[8] = {0b00000, 0b00000, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101};
             byte v7[8] = {0b00000, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101};
             byte v8[8] = {0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101, 0b10101};
             lcd.createChar(0, v1);lcd.createChar(1, v2);lcd.createChar(2, v3);lcd.createChar(3, v4);
             lcd.createChar(4, v5);lcd.createChar(5, v6);lcd.createChar(6, v7);lcd.createChar(7, v8);}
 
             if(graf==4){
             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};
             lcd.createChar(0, v1);lcd.createChar(1, v2);lcd.createChar(2, v3);lcd.createChar(3, v4);
             lcd.createChar(4, v5);lcd.createChar(5, v6);lcd.createChar(6, v7);lcd.createChar(7, v8);}
  www=0;
  }
  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]];
 
    lcd.setCursor(pos, 0);
 
    // преобразовать значение величины спектра в диапазон 0..15 с учётом настроек
    int posLevel = map(fht_log_out[posOffset[pos]], LOW_PASS, gain, 0, 15);
    posLevel = constrain(posLevel, 0, 15);
  while(y<3){y++;
    if (posLevel > 7) {         // если значение больше 7 (значит нижний квадратик будет полный)
      lcd.write((uint8_t)posLevel - 8);    // верхний квадратик залить тем что осталось
      lcd.setCursor(pos, 1);          // перейти на нижний квадратик
      lcd.write((uint8_t)7);        // залить его полностью
  } 
    else {                             // если значение меньше 8
      lcd.print(" ");                 // верхний квадратик пустой
      lcd.setCursor(pos, 1);          // нижний квадратик
      lcd.write((uint8_t)posLevel);        // залить полосками
    }} y=0;
}
 
 
   k_usil=k_us;k_usil=k_usil*0.1;
  if (AUTO_GAIN==1) {
    maxValue_f = maxValue*k_usil * k + maxValue_f * (1 - k);
   if (millis() - gainTimer > 1500) {      // каждые 1500 мс
      // если максимальное значение больше порога, взять его как максимум для отображения
      if (maxValue_f > VOL_THR) gain = maxValue_f;
      // если нет, то взять порог побольше, чтобы шумы вообще не проходили
      else gain = 100;
      gainTimer = millis();
    }}else{gain=DEF_GAIN;
  }// spektr
  } 
 
   if(digitalRead(12)==LOW&&menu<3){sss++;if(spek==1){menu==0;}else{menu++;}www1=0;spek=0;myEnc.write(0);oldPosition  = -999;delay(300);time=millis();w1=1;if(menu>2){menu=0;}if(sss>2){menu=3;delay(300);}}// меню
   if(digitalRead(12)==HIGH){sss=0;}
   if(menu==3&&digitalRead(12)==LOW&&sss==0){menu=4;oldPosition  = -999;delay(300);time=millis();w1=1;lcd.clear();}
   if(menu==4&&digitalRead(12)==LOW&&sss==0){menu=5;oldPosition  = -999;delay(300);time=millis();w1=1;lcd.clear();}
   if(menu==5&&digitalRead(12)==LOW){menu=0;time=millis();w1=1;delay(300);lcd.clear();}
   if (newPosition != oldPosition&&spek==1) {oldPosition = newPosition;menu=0,www1=0;spek=0;w1=1;time=millis();lcd.clear();}
 
    if(spek==0){
      if(www1==0){byte a1[8]={0b00000,0b11011,0b11011,0b11011,0b11011,0b11011,0b11011,0b00000};
                  byte a2[8]={0b00000,0b11000,0b11000,0b11000,0b11000,0b11000,0b11000,0b00000};
                  lcd.createChar(0,a1); lcd.createChar(1,a2); www1=1; }
 
   ///////////////////////// громкость /////////////////////////////
   if(menu==0){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    vol=vol+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(vol>63){vol=63;}if(vol<16){vol=16;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Volume    ");
  vol_d=vol*2-120;
   if(vol_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(vol_d>-10){{lcd.print(" ");}}
  lcd.print(abs(vol_d));lcd.print(" dB   ");
 // ползунок громкости начинает работать с -56 дБ
   if(vol-32>0){for(z=0;z<=vol-33;z++){lcd.setCursor(z/2,1);lcd.write((uint8_t)0);}}
   if((vol-32)%2==0&&vol-32>=0){lcd.setCursor(z/2,1);lcd.write((uint8_t)1);}
  }
   ///////////////////////// баланс /////////////////////////////
  if(menu==3){
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    balans=balans+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(balans>4){balans=4;}if(balans<-4){balans=-4;}audio();}
 
  lcd.setCursor(0,0);
   if(balans*2>=0){lcd.print("-");}else{lcd.print("+");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.print(" <>  ");
   if(balans*2>=0){lcd.print("+");}else{lcd.print("-");}
  lcd.print(abs(balans*2));lcd.print(" dB ");
  lcd.setCursor(0,1);lcd.print("L");
  lcd.setCursor(15,1);lcd.print("R");
   if(balans<0){lcd.setCursor(balans+7,1);lcd.write((uint8_t)0);}
   if(balans>0){lcd.setCursor(balans+8,1);lcd.write((uint8_t)0);}
   if(balans==0){lcd.setCursor(7,1);lcd.write((uint8_t)0);lcd.setCursor(8,1);lcd.write((uint8_t)0);}
}
  ///////////////////////// тембр нч /////////////////////////////
  if(menu==1){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    bass=bass+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(bass>11){bass=11;}if(bass<2){bass=2;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Bass      ");
  bass_d=bass*3-18;
   if(bass_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(bass_d>-10&&bass_d<=0||bass_d<10&&bass_d>0){{lcd.print(" ");}}
  lcd.print(abs(bass_d));lcd.print(" dB   ");
   if(bass>1){for(z=3;z<=bass+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
 }
   ///////////////////////// тембр вч /////////////////////////////
  if(menu==2){
   if (newPosition != oldPosition) {
    oldPosition = newPosition;
    treb=treb+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(treb>11){treb=11;}if(treb<2){treb=2;}audio();}
 
   if(treb>10){treb=10;}if(treb<2||treb==255){treb=2;}
  lcd.setCursor(0,0);lcd.print("Treble    ");
  treb_d=treb*3-18;
   if(treb_d>=0){lcd.print("+");}else{lcd.print("-");}
   if(treb_d>-10&&treb_d<=0||treb_d<10&&treb_d>0){{lcd.print(" ");}}
  lcd.print(abs(treb_d));lcd.print(" dB   ");
   if(treb>1){for(z=3;z<=treb+1;z++){lcd.setCursor(z,1);lcd.write((uint8_t)0);}}
 }
  ///////////////////////// input /////////////////////////////
  if(menu==4){
if (newPosition != oldPosition) {
    oldPosition = newPosition;
    in=in+newPosition;myEnc.write(0);newPosition=0;lcd.clear();time=millis();w1=1;if(in>1){in=0;}if(in<0){in=1;}audio();}
 
  lcd.setCursor(0,0);lcd.print("Source selector");
  lcd.setCursor(0,1);lcd.print("Input: ");lcd.print(in);
 }
///////////////////////////////////////////////////////////
 
  ///////////////////////// spectrum analyzer /////////////////////////////
  if(menu==5){  
    if(digitalRead(9)==HIGH){sp++;myEnc.write(0);oldPosition  = -999;delay(300);time=millis();if(sp>6){sp=0;}}
     lcd.setCursor(0,0);lcd.print("Spectrum analyzer");
if (newPosition != oldPosition) {
    oldPosition = newPosition;
    switch(sp){ w1=0;
    case 0: AUTO_GAIN=AUTO_GAIN+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(AUTO_GAIN>1){AUTO_GAIN=0;}if(AUTO_GAIN<0){AUTO_GAIN=1;};
    lcd.setCursor(0,1);lcd.print("AUTO_GAIN ");lcd.print(AUTO_GAIN);break;
    case 1: VOL_THR=VOL_THR+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(VOL_THR>100){VOL_THR=100;}if(VOL_THR<10){VOL_THR=10;};
    lcd.setCursor(0,1);lcd.print("VOL_THR ");lcd.print(VOL_THR);break;
    case 2: LOW_PASS=LOW_PASS+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(LOW_PASS>100){LOW_PASS=100;}if(LOW_PASS<10){LOW_PASS=10;};
    lcd.setCursor(0,1);lcd.print("LOW_PASS ");lcd.print(LOW_PASS);break;
    case 3: DEF_GAIN=DEF_GAIN+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(DEF_GAIN>200){DEF_GAIN=200;}if(DEF_GAIN<30){DEF_GAIN=30;};
    lcd.setCursor(0,1);lcd.print("DEF_GAIN ");lcd.print(DEF_GAIN);break;
    case 4: k_us=k_us+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(k_us>15){k_us=15;}if(k_us<5){k_us=5;}; k_usil=k_us;k_usil=k_usil*0.1;
    lcd.setCursor(0,1);lcd.print("GRAPH_ATT ");lcd.print(k_usil,1);break;
    case 5: graf=graf+newPosition;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(graf>4){graf=4;}if(graf<0){graf=0;};
    lcd.setCursor(0,1);lcd.print("GRAPHICS ");lcd.print(graf);break;
    case 6: sped=sped+newPosition;w2=0;myEnc.write(0);newPosition=0;audio();lcd.clear();time=millis();w1=1;if(sped>5){sped=5;}if(sped<0){sped=0;};
    lcd.setCursor(0,1);lcd.print("Conv. speed ");lcd.print(sped);break;
 
  }}}
 /////////////////////////////////////////////////////////////////////////
    }// spek = 0
  if(w2==0){
    switch(sped){
      case 0: ADCSRA = 0b11110010;break;
      case 1: ADCSRA = 0b11110011;break;
      case 2: ADCSRA = 0b11110100;break;
      case 3: ADCSRA = 0b11110101;break;
      case 4: ADCSRA = 0b11110110;break;
      case 5: ADCSRA = 0b11110111;break;
    }w2=1;}
 
////////////////// mute //////////////////////////////////
   if(digitalRead(9)==HIGH&&menu!=5){mute++;if(mute>1){mute=0;}
   if(mute==1){menu=-1;w1=0;spek=0;www1=0;}else{oldPosition  = -999;menu=0;}
  audio();delay(300);lcd.clear();
  lcd.setCursor(6,0);lcd.print("MUTE");
 }
///////////////////////////////////////////////////////////
 
///////////////////  -20 dB ///////////////////////////////
 if(digitalRead(8)==HIGH&&db==0){vol=vol-10;db=1;delay(500);audio();myEnc.write(0);menu=0;lcd.clear();}
 if(digitalRead(8)==HIGH&&db==1){vol=vol+10;db=0;delay(500);audio();myEnc.write(0);menu=0;lcd.clear();}
//////////////////////////////////////////////////////////
 
 
   if(millis()-time>10000 && w1==1){// сохранение всех настроек в eeprom через 60 сек неактивности
     //myEnc.write(0);
     EEPROM.update(0,vol);
     EEPROM.update(4,balans+4);
     EEPROM.update(1,bass);
     EEPROM.update(2,treb);
     EEPROM.update(3,in);
     EEPROM.update(5,AUTO_GAIN);
     EEPROM.update(6,VOL_THR);
     EEPROM.update(7,LOW_PASS);
     EEPROM.update(8,DEF_GAIN);
     EEPROM.update(9,graf);
     EEPROM.update(10,k_us);
     EEPROM.update(11,sped);
     w1=0;menu=0;lcd.clear();spek=1;www=1; sp=0;}
 
 }// loop
 
void audio(){
  tda.setVolumeL(vol+balans);
  tda.setVolumeR(vol-balans);
  tda.setBass(bass);
  tda.setTreble(treb);
  tda.setMute(mute);
  tda.setSource(in);  }
 
void analyzeAudio() {  
   cli(); 
 while( i < FHT_N ) {  i++; 
    do{ADCSRA |= (1 << ADSC);} 
    while((ADCSRA & (1 << ADIF)) == 0);
    fht_input[i] = (ADCL|ADCH << 8); 
    }i=0;
    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
   sei();
}

  • tda8425_01.zip — основное меню: громкость, тембр, баланс, выбор входа, в дополнительном меню настройки только анализатора спектра.


TDA8425 + энкодер + пульт + STANDBY — http://forum.rcl-radio.ru/viewtopic.php?id=57


TDA8425 + энкодер + пульт + STANDBY + часы(DS3231) — http://forum.rcl-radio.ru/viewtopic.php?id=59


Дополнительные материалы:

Comments

    1. Походу ни как. Надо использовать второй аналоговый вход для второго канала и переключать его в зависимости какой INPUT работает.

      ADMUX = 0b11100000; = активен А0
      ADMUX = 0b11100001; = активен А1

      1. Если добавить в обработчик переключения входа (последние 4 строки), будет работать?

        ///////////////////////// input /////////////////////////////
        if(menu==4){
        if (newPosition != oldPosition) {
        oldPosition = newPosition;
        in=in+newPosition;
        myEnc.write(0);
        newPosition=0;
        audio();
        lcd.clear();
        time=millis();
        w1=1;

        if(in>1){in=0;}
        if(in<0){in=1;}

        if(in==1)
        {ADMUX = 0b11100001;}
        else
        {ADMUX = 0b11100000;}
        }

  1. Проблемка возникла, переключает все меню в произвольном порядке, то есть ничего не трогая он листает настройки, как бороться с этим?

Добавить комментарий

Войти с помощью: