Ранее в статье рассматривался пример создания на основе аудиопроцессора LC75341 регулятора громкости и тембра с использованием дисплея LCD1602 + I2C, в этой статье аналогичный пример, но с использованием дисплея 0.96′ I2C 128X64 OLED.
В OLED дисплее отсутствует дополнительный слой подсветки всей поверхности экрана. Каждый пиксел, формирующий изображение, испускает самостоятельное свечение. При этом картинка получается яркой и контрастной.
Управление OLED дисплеем в данном примере осуществляется при помощи шины I2C.
Параметры дисплея SSD1306:
- Технология дисплея: OLED
- Разрешение дисплея: 128 на 64 точки
- Диагональ дисплея: 0,96 дюйма
- Угол обзора: 160°
- Напряжение питания: 2.8 В ~ 5.5 В
- Мощность: 0,08 Вт
- Габариты: 27.3 мм х 27.8 мм х 3.7 мм
Регулятор громкости содержит два основных блока, первый блок микроконтроллерный (Arduino Nano) с органами управления и индикации, второй блок плата аудиопроцессора.
Основные параметры регулятора громкости и тембра на LC75341:
- Регулировка громкости от -79 до 0 дБ (шаг 1 дБ)
- Независимая регулировка громкости (баланс)
- Входной предварительный усилитель входа от 0 до +30 дБ (шаг 2 дБ)
- 4-х канальный коммутатор входов
- Регулировка тембра BASS от 0 до +20 дБ (шаг 2 дБ)
- Регулировка тембра TREBLE от -10 до +10 дБ (шаг 2 дБ)
- Напряжение питания от 5 до 10 В
- Управление цифровое 3-Wire (CL, DI, CE) (макс. тактовая частота до 500 кГц)
- Входное сопротивление 50 кОм
- Коэффициент нелинейных искажений 0,01% (макс.)
Основное управление параметрами аудиопроцессора будет осуществляться при помощи энкодера (KY-040) и 2-х кнопок, так же будет применен ИК пульт который будет дублировать энкодер и кнопки управления.
#define IR2 0x33B820DF // button encoder #define IR3 0x33B8946B // mute #define IR4 0x33B810EF // >>> #define IR5 0x33B8E01F // <<< #define IR6 0x33B844BB // INPUT #define POWER 0x33B800FF // STANDBY #include <LC75341.h> // http://rcl-radio.ru/wp-content/uploads/2022/01/lc75341.zip #include <Wire.h> #include <MsTimer2.h> // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip #include <OLED_I2C.h> // http://rcl-radio.ru/wp-content/uploads/2022/01/OLED_I2C.zip #include <Encoder.h> // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip #include <EEPROM.h> #include <boarddefs.h> // входит в состав библиотеки IRremote #include <IRremote.h> // http://rcl-radio.ru/wp-content/uploads/2019/06/IRremote.zip Encoder myEnc(9, 8);// DT, CLK OLED myOLED(SDA, SCL, 8); LC75341 lc(3,4,2); // CE,DI,CL IRrecv irrecv(12); // указываем вывод модуля IR приемника decode_results ir; extern uint8_t SmallFont[],BigNumbers[]; long times,oldPosition = -999,newPosition; int vol_reg,menu,mute_reg,power,treb_reg,gain0,gain1,gain2,gain3,gain4,gain5,bass_reg,in_reg,ball,vol_old; bool w,w2,gr1,gr2,www; void setup(){ Wire.begin();Wire.setClock(400000L); irrecv.enableIRIn(); myOLED.begin(); myOLED.setBrightness(100); Serial.begin(9600); MsTimer2::set(1, to_Timer);MsTimer2::start(); pinMode(10,INPUT); // encoder SW pinMode(7,INPUT_PULLUP); // mute button pinMode(11,INPUT_PULLUP); // input button vol_reg = EEPROM.read(0);treb_reg = EEPROM.read(1)-5;bass_reg = EEPROM.read(2);gain1 = EEPROM.read(4); gain2 = EEPROM.read(5);gain3 = EEPROM.read(6);gain4 = EEPROM.read(7);gain5 = EEPROM.read(8); in_reg = EEPROM.read(9);ball = EEPROM.read(10)-4; switch(in_reg){ case 0: gain0 = gain1;break; case 1: gain0 = gain2;break; case 2: gain0 = gain3;break; case 3: gain0 = gain4;break; } audio_L(); audio_R(); } void loop(){ /// IR //////////////////////////////////////// if ( irrecv.decode( &ir )) {Serial.print("0x");Serial.println( ir.value,HEX);irrecv.resume();times=millis();w=1;w2=1;}// IR приемник - чтение, в мониторе порта отображаются коды кнопок if(ir.value==0){gr1=0;gr2=0;}// запрет нажатий не активных кнопок пульта //////// BUTTON ///////////////////////////////////////////////////////////////////////////////////////////////////////////// if(mute_reg==0){ if(digitalRead(10)==LOW||ir.value==IR2){menu++;gr1=0;gr2=0;cl();times=millis();w=1;w2=1;if(menu>3){menu=0;}} if((digitalRead(11)==LOW||ir.value==IR6)){in_reg++;menu=4;cl();times=millis();w=1;w2=1;www=1;if(in_reg>3){in_reg=0;}} } if((digitalRead(7)==LOW||ir.value==IR3)&&mute_reg==0){mute_reg=1;menu=100;cl();vol_old=vol_reg;vol_reg=79;audio_R();audio_L();myOLED.clrScr();myOLED.setFont(SmallFont);myOLED.print(F("MUTE"), CENTER, 25);myOLED.update();} if((digitalRead(7)==LOW||ir.value==IR3)&&mute_reg==1){mute_reg=0;w=1;menu=0;cl();vol_reg=vol_old;audio_R();audio_L();} //////// VOLUME ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(menu==0){ if(newPosition != oldPosition){oldPosition = newPosition;if(newPosition>1){newPosition=1;}if(newPosition<-1){newPosition=-1;} vol_reg=vol_reg+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w2=1;vol_func();audio_R();audio_L();} if(ir.value==IR5){vol_reg++;gr1=1;gr2=0;cl();times=millis();w=1;w2=1;vol_func();audio_R();audio_L();}// кнопка > if(ir.value==0xFFFFFFFF and gr1==1){vol_reg++;gr2=0;cl();times=millis();w=1;w2=1;vol_func();audio_R();audio_L();}// кнопка >>>>>> if(ir.value==IR4){vol_reg--;gr1=0;gr2=1;cl();times=millis();w=1;w2=1;vol_func();audio_R();audio_L();}// кнопка < if(ir.value==0xFFFFFFFF and gr2==1){vol_reg--;gr1=0;cl();times=millis();w=1;w2=1;vol_func();audio_R();audio_L();}// кнопка <<<<<< if(w==1){w=0; myOLED.clrScr(); myOLED.setFont(SmallFont);myOLED.print(F("VOLUME"), LEFT, 0);myOLED.setFont(BigNumbers);myOLED.printNumI(79-vol_reg, 65, 0); myOLED.setFont(SmallFont);myOLED.print(F("IN"), LEFT, 10); myOLED.printNumI(in_reg+1, 20, 10);myOLED.print(F("GAIN"), LEFT, 20);myOLED.printNumI(gain0*2, 30, 20); myOLED.drawLine(0, 30, 128, 30); myOLED.setFont(SmallFont);myOLED.print(F("TREBLE"), LEFT, 35);myOLED.printNumI(treb_reg*2, 70, 35);myOLED.print(F("dB"), RIGHT, 35); myOLED.setFont(SmallFont);myOLED.print(F("BASS"), LEFT, 45);myOLED.printNumI(bass_reg*2, 70, 45);myOLED.print(F("dB"), RIGHT, 45); myOLED.setFont(SmallFont);myOLED.print(F("BALANCE"), LEFT, 55);myOLED.printNumI(ball, 70, 55);myOLED.print(F("dB"), RIGHT, 55); myOLED.update(); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// TREBLE ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(menu==1){ if(newPosition != oldPosition){oldPosition = newPosition;if(newPosition>1){newPosition=1;}if(newPosition<-1){newPosition=-1;} treb_reg=treb_reg-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w2=1;treb_func();audio_R();audio_L();} if(ir.value==IR4){treb_reg++;gr1=1;gr2=0;cl();times=millis();w=1;w2=1;treb_func();audio_R();audio_L();}// кнопка > if(ir.value==IR5){treb_reg--;gr1=0;gr2=1;cl();times=millis();w=1;w2=1;treb_func();audio_R();audio_L();}// кнопка < if(w==1){w=0; myOLED.clrScr(); myOLED.setFont(SmallFont);myOLED.print(F("TREBLE"), LEFT, 0);;myOLED.setFont(BigNumbers);myOLED.printNumI(treb_reg*2, 65, 0); myOLED.setFont(SmallFont);myOLED.print(F("dB"), RIGHT, 0); myOLED.drawLine(0, 30, 128, 30); myOLED.setFont(SmallFont);myOLED.print(F("BASS"), LEFT, 35);myOLED.printNumI(bass_reg*2, 70, 35);myOLED.print(F("dB"), RIGHT, 35); myOLED.setFont(SmallFont);myOLED.print(F("BALANCE"), LEFT, 45);myOLED.printNumI(ball, 70, 45);myOLED.print(F("dB"), RIGHT, 45); myOLED.setFont(SmallFont);myOLED.print(F("VOLUME"), LEFT, 55);myOLED.printNumI(79-vol_reg, 70, 55); myOLED.update(); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// BASS ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(menu==2){ if(newPosition != oldPosition){oldPosition = newPosition;if(newPosition>1){newPosition=1;}if(newPosition<-1){newPosition=-1;} bass_reg=bass_reg-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w2=1;bass_func();audio_R();audio_L();} if(ir.value==IR4){bass_reg++;gr1=1;gr2=0;cl();times=millis();w=1;w2=1;bass_func();audio_R();audio_L();}// кнопка > if(ir.value==IR5){bass_reg--;gr1=0;gr2=1;cl();times=millis();w=1;w2=1;bass_func();audio_R();audio_L();}// кнопка < if(w==1){w=0; myOLED.clrScr(); myOLED.setFont(SmallFont);myOLED.print(F("BASS"), LEFT, 0);;myOLED.setFont(BigNumbers);myOLED.printNumI(bass_reg*2, 65, 0); myOLED.setFont(SmallFont);myOLED.print(F("dB"), RIGHT, 0); myOLED.drawLine(0, 30, 128, 30); myOLED.setFont(SmallFont);myOLED.print(F("BALANCE"), LEFT, 35);myOLED.printNumI(ball, 70, 35);myOLED.print(F("dB"), RIGHT, 35); myOLED.setFont(SmallFont);myOLED.print(F("VOLUME"), LEFT, 45);myOLED.printNumI(79-vol_reg, 70, 45); myOLED.setFont(SmallFont);myOLED.print(F("TREBLE"), LEFT, 55);myOLED.printNumI(treb_reg*2, 70, 55);myOLED.print(F("dB"), RIGHT, 55); myOLED.update(); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// BALANCE ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(menu==3){ if(newPosition != oldPosition){oldPosition = newPosition;if(newPosition>1){newPosition=1;}if(newPosition<-1){newPosition=-1;} ball=ball-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w2=1;ball_func();audio_R();audio_L();} if(ir.value==IR4){ball++;gr1=1;gr2=0;cl();times=millis();w=1;w2=1;ball_func();audio_R();audio_L();}// кнопка > if(ir.value==IR5){ball--;gr1=0;gr2=1;cl();times=millis();w=1;w2=1;ball_func();audio_R();audio_L();}// кнопка < if(w==1){w=0; myOLED.clrScr(); myOLED.setFont(SmallFont);myOLED.print(F("BALANCE"), LEFT, 0);;myOLED.setFont(BigNumbers);myOLED.printNumI(abs(ball), 65, 0); myOLED.setFont(SmallFont);myOLED.print(F("dB"), RIGHT, 0); if(ball<=0){myOLED.print(F("LCH"), 20, 20);} if(ball>=0){myOLED.print(F("RCH"), RIGHT, 20);} myOLED.drawLine(0, 30, 128, 30); myOLED.setFont(SmallFont);myOLED.print(F("VOLUME"), LEFT, 35);myOLED.printNumI(79-vol_reg, 70, 35); myOLED.setFont(SmallFont);myOLED.print(F("TREBLE"), LEFT, 45);myOLED.printNumI(treb_reg*2, 70, 45);myOLED.print(F("dB"), RIGHT, 45); myOLED.setFont(SmallFont);myOLED.print(F("BASS"), LEFT, 55);myOLED.printNumI(bass_reg*2, 70, 55);myOLED.print(F("dB"), RIGHT, 55); myOLED.update(); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////// INPUT GAIN /////////////////////////////////////////////////////////////////// if(menu==4){ switch(in_reg){ case 0: gain0 = gain1;break; case 1: gain0 = gain2;break; case 2: gain0 = gain3;break; case 3: gain0 = gain4;break; } if (newPosition != oldPosition){oldPosition = newPosition;gain0=gain0-newPosition;myEnc.write(0);newPosition=0;times=millis();www=1;w=1;w2=1;gain_func();} if(ir.value==IR4){gain0++;gr1=1;gr2=0;cl();times=millis();w=1;w2=1;gain_func();audio_R();audio_L();}// кнопка > if(ir.value==IR5){gain0--;gr1=0;gr2=1;cl();times=millis();w=1;w2=1;gain_func();audio_R();audio_L();}// кнопка < switch(in_reg){ case 0: gain1 = gain0;break; case 1: gain2 = gain0;break; case 2: gain3 = gain0;break; case 3: gain4 = gain0;break; } if(www==1){audio_R();audio_L();www=0;} if(w==1){w=0; myOLED.clrScr(); myOLED.setFont(SmallFont);myOLED.print(F("INPUT"), LEFT, 0);myOLED.setFont(BigNumbers);myOLED.printNumI(in_reg+1, 65, 0); myOLED.drawLine(0, 30, 128, 30); myOLED.setFont(SmallFont);myOLED.print(F("GAIN"), LEFT, 35);myOLED.setFont(BigNumbers);myOLED.printNumI(gain0*2, 65, 35); myOLED.setFont(SmallFont);myOLED.print(F("dB"), RIGHT, 35); myOLED.update(); }} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////// EEPROM ////////////////////////////////////////////////////////////// if(millis()-times>10000 && w2==1 && mute_reg==0 && power==0){ EEPROM.update(0,vol_reg);EEPROM.update(1,treb_reg+5);EEPROM.update(2,bass_reg);EEPROM.update(4,gain1); EEPROM.update(5,gain2);EEPROM.update(6,gain3);EEPROM.update(7,gain4);EEPROM.update(8,gain5); EEPROM.update(9,in_reg);EEPROM.update(10,ball+4); if(menu!=0){menu=0;}w=1;w2=0;} }// end loop void treb_func(){if(treb_reg<-5){treb_reg=-5;}if(treb_reg>5){treb_reg=5;}} void gain_func(){{if(gain0<0){gain0=0;}if(gain0>15){gain0=15;}}} void ball_func(){if(ball>4){ball=4;}if(ball<-4){ball=-4;}} void bass_func(){if(bass_reg<0){bass_reg=0;}if(bass_reg>10){bass_reg=10;}} void vol_func(){if(vol_reg<4){vol_reg=4;}if(vol_reg>79){vol_reg=79;}} void to_Timer(){newPosition = myEnc.read()/4;} void cl(){myOLED.clrScr();ir.value=0;delay(300);} void audio_L(){ lc.addr(); lc.set_input(in_reg); // input 1...4 = byte 0...3 (byte 4...7 = All switches off) lc.set_gain(gain0); // gain 0...30 dB step 2 dB = byte 0...15 lc.set_volume(vol_reg-ball); // volume 0...-79 dB = byte 0...79 lc.set_treble(treb_reg); // treble -10...10 dB step 2 dB = int -5...5 lc.set_bass(bass_reg); // bass 0...20 dB step 2 dB = int 0...10 lc.set_ch(1); // Channel Selection RCH = byte 2, LCH = byte 1, Left and right together = byte 3 lc.test(); } void audio_R(){ lc.addr(); lc.set_input(in_reg); // input 1...4 = byte 0...3 (byte 4...7 = All switches off) lc.set_gain(gain0); // gain 0...30 dB step 2 dB = byte 0...15 lc.set_volume(vol_reg+ball); // volume 0...-79 dB = byte 0...79 lc.set_treble(treb_reg); // treble -10...10 dB step 2 dB = int -5...5 lc.set_bass(bass_reg); // bass 0...20 dB step 2 dB = int 0...10 lc.set_ch(2); // Channel Selection RCH = byte 2, LCH = byte 1, Left and right together = byte 3 lc.test(); }
ИК пульт дублирует работу энкодера и кнопок. Для управления регулятором подойдет практически любой пульт ИК, для поддержки Вашего пульта необходимо прописать коды кнопок в скетч:
#define IR2 0x33B820DF // button encoder #define IR3 0x33B8946B // mute #define IR4 0x33B810EF // >>> #define IR5 0x33B8E01F // <<< #define IR6 0x33B844BB // INPUT
Для получения кодов кнопок Вашего пульта загрузите скетч и откройте монитор порта, при нажатии кнопки пульта его код будет отображен в мониторе порта.
Наконец то появился аудио с графическим дисплеем!!!
Жалко что тема затухла : http://rcl-radio.ru/?p=57658
Очень интересно ее развить
Подскажите пожалуйста, есть у Вас под рукой данная сборка? Я собрал, но при упирании в пределы, неважно, баланс это, или регулировка громкости и продолжении вращать энкодер появляются громкие и сильные щелчки, с пульта также происходит, также если очень быстро изменять громкость энкодером. Это особенность микросхемы или ардуино? Может в схеме что-то недосказанно? Надоело просто уже деньги на эксперименты тратить. На ТДА 8425 щелчки вообще при изменении любого параметра, громче всего при переходе в режим mute и выкручивании громкости в 0, и с ней щелчки не важно, кнопками что-то регулируется, с пульта или энкодером. Перепробовал 6 микросхем с китая, 5 из местных магазинов, и одну с телевизора снятую, все ведут себя идентично, работают, но щелчки жутко выбешивают. В итоге ни одного нормального аудиопроца не получил, может я что-то не знаю?
Если снизить громкость на 10 дБ от максимальной громкости и покрутить в упор тембр или баланс щелчки есть?
Крайне был невнимателен, нашел свою ошибку, у меня дважды шел вызов установки настроек LC-шки (2 раза отправка данных происходила), щелчки ушли. Но вот на 8425 к сожалению никак не повлияло, получается сами микросхемы капризные.
Еще хотел спросить, на счет еще одной проблемы, а именно, на минимальной громкости, а также в режиме mute звук полностью не исчезает, также, если подается сигнал допустим на вход 1, а выбран вход 2, может есть предположения как это можно побороть? Проблема я так понимаю в земле, т.к. она общая как у входов/выходов так и питания аудиопроцессора.
У меня нет решения данной проблемы, хотелось только отметить что у аудиопроцессора нет аппаратного MUTE, оно программное — просто в этом режиме минимальная громкость.
Посмотрел различные предложения готовых устройств, предлагаемых китайскими чудо-инженерами, а также пару мануалов самсунговких музыкальных центров, увидел наличие резисторных делеителей по входам микросхемы, возможно, это поможет решить данную проблему.