Ранее в статьях http://rcl-radio.ru/?p=112670 и http://rcl-radio.ru/?p=112468 рассматривались примеры создания регуляторов громкости на аудиопроцессоре LC75341 с использованием дисплеяLCD1602 и OLED 0.96″, в этом примере будет показан пример создания регулятора громкости с использованием дисплея OLED 1.3″.
OLED 1.3″ I2C 128х64 дисплей обладает высокой контрастностью, высоким разрешением и низким энергопотреблением. В OLED дисплее отсутствует дополнительный слой подсветки всей поверхности экрана. Каждый пиксел, формирующий изображение, испускает самостоятельное свечение.
Чип драйвера — SH1106, который обеспечивает связь I2C.
Параметры дисплея SH1106:
- Технология дисплея: OLED
- Разрешение дисплея: 128 на 64 точки
- Диагональ дисплея: 1,3 дюйма
- Угол обзора > 170°
- Напряжение питания: 3,3 В ~ 5.0 В
- Мощность: 0,08 Вт
- Габариты: 35.7 х 30.9 х 4.0 мм
- Вес: 7 грамм
Подключение OLDE дисплея к плате Arduino Nano (Uno):
- VCC > 5V
- GND > GND
- SCL > A5 (I2C)
- SDA > A4 (I2C)
Для использования дисплея OLED 1.3″ Вам необходима библиотека U8glib , ее можно установить с источника — https://github.com/olikraus/u8glib/ или установить при помощи менеджера библиотек как показано в — http://rcl-radio.ru/?p=130369.
Регулятор громкости содержит два основных блока, первый блок микроконтроллерный (Arduino Nano или плата LGT8F328P-LQFP32 MiniEVB) с органами управления и индикации, второй блок плата аудиопроцессора.
Основные параметры регулятора громкости и тембра на 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) и 3-х кнопок, так же будет применен ИК пульт который будет дублировать энкодер и кнопки управления.
В регуляторе громкости на аудиопроцессоре LC75341 реализованы следующие функции:
- Регулировка громкости от 0 до 75 шагов (1 шаг равен 1 дБ, регулировка от -75 до 0 дБ)
- Регулировка тембра ВЧ от -10 до +10 дБ
- Регулировка тембра НЧ от 0 до 20 дБ
- Регулировка баланса -4…+4 дБ
- Входной предварительный усилитель с регулировкой от 0 до +30 дБ раздельно для каждого входа.
- 4-е стерео входа
- Режим MUTE
- Режим STANDBY с выходом управления ST-BY
- 4-е лог. выхода для управления источниками входных сигналов
- Гашение дисплея при не активности органов управления в течении 30 сек (для активации режима гашения дисплея необходимо в режиме POWER OFF нажать и удерживать кнопку энкодера, далее нажать кнопку INPUT. При активации режима на дисплее появится надпись OLED_OFF, при отключении режима отключения дисплея появится надпись OLED_ON).
- Сохранение всех настроек в энергонезависимой памяти
Схема регулятора громкости
#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 <U8glib.h> // https://github.com/olikraus/u8glib/ #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(8, 9);// DT, CLK U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST); // Dev 0, Fast I2C / TWI LC75341 lc(3,4,2); // CE,DI,CL IRrecv irrecv(12); // указываем вывод модуля IR приемника decode_results ir; long times,oldPosition = -999,newPosition, times1; 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,oled_off; void setup(){ Wire.begin(); irrecv.enableIRIn(); 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 pinMode(5,OUTPUT); // OUT STBY pinMode(6,INPUT_PULLUP); // POWER BUTTON // input_out_pin pinMode(A0,OUTPUT); pinMode(A1,OUTPUT); pinMode(A2,OUTPUT); pinMode(A3,OUTPUT); 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;oled_off = EEPROM.read(11); if(oled_off>1){oled_off=0;} switch(in_reg){ case 0: gain0 = gain1;out_in(1,0,0,0);break; case 1: gain0 = gain2;out_in(0,1,0,0);break; case 2: gain0 = gain3;out_in(0,0,1,0);break; case 3: gain0 = gain4;out_in(0,0,0,1);break; } audio_L(); audio_R(); } void loop(){ // Serial.println(menu); /// IR //////////////////////////////////////// if ( irrecv.decode( &ir )) {Serial.print("0x");Serial.println( ir.value,HEX);irrecv.resume();times=millis();w=1;w2=1;times1=millis();}// IR приемник - чтение, в мониторе порта отображаются коды кнопок if(ir.value==0){gr1=0;gr2=0;}// запрет нажатий не активных кнопок пульта //////// BUTTON ///////////////////////////////////////////////////////////////////////////////////////////////////////////// if(power==0){ if(mute_reg==0){ if(digitalRead(10)==LOW||ir.value==IR2){menu++;times1=millis();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();times1=millis();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=71;audio_R();audio_L(); u8g.firstPage();do {u8g.setFont(u8g_font_profont12r);u8g.drawStr(53,30,"MUTE");} while( u8g.nextPage() );} if((digitalRead(7)==LOW||ir.value==IR3)&&mute_reg==1){mute_reg=0;times1=millis();w=1;menu=0;cl();vol_reg=vol_old;audio_R();audio_L();} } ////////// POWER /////////////////// if((digitalRead(6)==LOW||ir.value==POWER)&&power==0){power=1;menu=100;cl();vol_old=vol_reg;vol_reg=71;audio_R();audio_L(); u8g.firstPage();do {u8g.setFont(u8g_font_profont12r);u8g.drawStr(40,30,"POWER OFF");} while( u8g.nextPage() ); delay(1500); u8g.firstPage();do {} while( u8g.nextPage() );} if((digitalRead(6)==LOW||ir.value==POWER)&&power==1){power=0;times1=millis(); u8g.firstPage();do {u8g.setFont(u8g_font_profont12r);u8g.drawStr(42,30,"POWER ON");} while( u8g.nextPage() );delay(1500); w=1;menu=0;cl();vol_reg=vol_old;audio_R();audio_L();} if(power==0){digitalWrite(5,HIGH);}else{digitalWrite(5,LOW);} if(digitalRead(10)==LOW && digitalRead(11)==LOW && power==1 && oled_off==0){oled_off=1;EEPROM.update(11,oled_off); u8g.firstPage();do {u8g.setFont(u8g_font_profont12r);u8g.drawStr(42,30,"OLED OFF");} while( u8g.nextPage() ); delay(2000);u8g.firstPage();do {} while( u8g.nextPage() );} if(digitalRead(10)==LOW && digitalRead(11)==LOW && power==1 && oled_off==1){oled_off=0;EEPROM.update(11,oled_off); u8g.firstPage();do {u8g.setFont(u8g_font_profont12r);u8g.drawStr(42,30,"OLED ON");} while( u8g.nextPage() ); delay(2000);u8g.firstPage();do {} while( u8g.nextPage() );} //////// 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;times1=millis();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; u8g.firstPage(); do { u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,8,"VOLUME");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 22);u8g.print(71-vol_reg); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,18,"IN"); u8g.setPrintPos(20, 18);u8g.print(in_reg+1);u8g.drawStr(0,28,"GAIN");u8g.setPrintPos(30, 28);u8g.print(gain0*2); u8g.drawLine(0, 32, 128, 32); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,44,"TREBLE");u8g.setPrintPos(80, 44);u8g.print(treb_reg*2);u8g.drawStr(95,44,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,54,"BASS");u8g.setPrintPos(80, 54);u8g.print(bass_reg*2);u8g.drawStr(95,54,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,64,"BALANCE");u8g.setPrintPos(80, 64);u8g.print(ball);u8g.drawStr(95,64,"dB"); } while( u8g.nextPage() ); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// 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;times1=millis();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; u8g.firstPage(); do { u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,8,"TREBLE");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 22);u8g.print(treb_reg*2); u8g.setFont(u8g_font_profont12r);//u8g.drawStr(115,8,"dB"); u8g.drawLine(0, 32, 128, 32); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,44,"BASS");u8g.setPrintPos(80, 44);u8g.print(bass_reg*2);u8g.drawStr(95,44,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,54,"BALANCE");u8g.setPrintPos(80, 54);u8g.print(ball);u8g.drawStr(95,54,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,64,"VOLUME");u8g.setPrintPos(80, 64);u8g.print(79-vol_reg);//u8g.drawStr(95,64,"dB"); } while( u8g.nextPage() ); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// 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;times1=millis();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; u8g.firstPage(); do { u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,8,"BASS");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 22);u8g.print(bass_reg*2); u8g.setFont(u8g_font_profont12r);//u8g.drawStr(115,8,"dB"); u8g.drawLine(0, 32, 128, 32); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,44,"BALANCE");u8g.setPrintPos(80, 44);u8g.print(ball);u8g.drawStr(95,44,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,54,"VOLUME");u8g.setPrintPos(80, 54);u8g.print(79-vol_reg);//u8g.drawStr(95,54,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,64,"TREBLE");u8g.setPrintPos(80, 64);u8g.print(treb_reg*2);u8g.drawStr(95,64,"dB"); } while( u8g.nextPage() ); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// 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;times1=millis();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; u8g.firstPage(); do { u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,8,"BALANCE");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 22);u8g.print(abs(ball)); u8g.setFont(u8g_font_profont12r);//u8g.drawStr(115,8,"dB"); if(ball<=0){u8g.drawStr(45,28,"LCH");} if(ball>=0){u8g.drawStr(105,28,"RCH");} u8g.drawLine(0, 32, 128, 32); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,44,"VOLUME");u8g.setPrintPos(80, 44);u8g.print(79-vol_reg);//u8g.drawStr(95,44,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,54,"TREBLE");u8g.setPrintPos(80, 54);u8g.print(treb_reg*2);u8g.drawStr(95,54,"dB"); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,64,"BASS");u8g.setPrintPos(80, 64);u8g.print(bass_reg*2);u8g.drawStr(95,64,"dB"); } while( u8g.nextPage() ); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////// INPUT GAIN /////////////////////////////////////////////////////////////////// if(menu==4){ switch(in_reg){ case 0: gain0 = gain1;out_in(1,0,0,0);break; case 1: gain0 = gain2;out_in(0,1,0,0);break; case 2: gain0 = gain3;out_in(0,0,1,0);break; case 3: gain0 = gain4;out_in(0,0,0,1);break; } if (newPosition != oldPosition){oldPosition = newPosition;gain0=gain0-newPosition;myEnc.write(0);newPosition=0;times1=millis();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; u8g.firstPage(); do { u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,8,"INPUT");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 22);u8g.print(in_reg+1); u8g.setFont(u8g_font_profont12r);//u8g.drawStr(115,8,"dB"); u8g.drawLine(0, 32, 128, 32); u8g.setFont(u8g_font_profont12r);u8g.drawStr(0,44,"GAIN");u8g.setFont(u8g_font_profont29r); u8g.setPrintPos(78, 60);u8g.print(gain0*2); } while( u8g.nextPage() ); }} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////// 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;} if(millis()-times1>30000 && mute_reg==0 && oled_off==1){ u8g.firstPage(); do {} while( u8g.nextPage() );} }// 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>71){vol_reg=71;}} void to_Timer(){newPosition = myEnc.read()/4;} void cl(){ir.value=0;delay(300);} void out_in(bool o1, bool o2, bool o3, bool o4){digitalWrite(A0,o1);digitalWrite(A1,o2);digitalWrite(A2,o3);digitalWrite(A3,o4);} 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+4-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+4+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 #define POWER 0x33B800FF // STANDBY
Для получения кодов кнопок Вашего пульта загрузите скетч и откройте монитор порта, при нажатии кнопки пульта его код будет отображен в мониторе порта.
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=8112#p8112
Приветствую. Можете подсказать, зачем пины OUT_IN на ардуинке? Нужно ли их как-то использовать?
4-е лог. выхода для управления источниками входных сигналов
Например для подачи питания для платы блютуз когда активен этот вход
или просто подключить светодиоды (через 330 Ом) для индикации активного входа
Использование этих выходов не обязательно
Спасибо!
Вот так получилось. Спасибо за ваши разработки.