Ранее в http://rcl-radio.ru/?p=80541 рассматривался пример создания регулятора громкости на аудиопроцессоре PT2258 с использованием четырех разрядного семисегментного индикатора. На этой странице аналогичный проект, но в нем будет использован индикатор LCD2004 c модулем I2C.
ИМС PT2258 — шести канальный регулятор громкости с микроконтроллерным управлением. Управление PT2258 осуществляется при помощи шины I2C. Регулятор громкости обладает низким уровнем шума и малым коэффициентом нелинейных искажений. Регулировка громкости может быть как независимая для каждого канала, так и общая для всех каналов.
Основные характеристики PT2258:
- Напряжение питания от 5 до 10 В, 9 В — рекомендуемое
- Ток потребления 8 мА
- Входное сопротивление 30 кОм
- Максимальное входное напряжение 2,8 Vrms
- Разделение каналов 100 дБ
- Коэффициент нелинейных искажений 0,005%
- Диапазон регулировки громкости от -79 до 0 дБ
- Шаг регулировки громкости 1 дБ
- Режим MUTE
- Отношение сигнал/шум 105 дБ
Схема регулятора громкости достаточно проста, выводы 4 и 17 (CODE2 и CODE1) в зависимости от подключения к GND или VCC позволяют изменять адрес микросхемы при работе с I2C шиной, что дает возможность подключения других утс-в на шину I2С, у которых нет возможности изменить адрес:
- CODE1 = GND, CODE2 = GND 80H
- CODE1 = GND, CODE2 = VCC 84H
- CODE1 = VCC, CODE2 = GND 88H
- CODE1 = VCC, CODE2 = VCC 8CH
Библиотека PT2258 поддерживает адрес 0х88, если возникнет необходимость изменить адрес шины I2C, то потребуется корректировка адреса в библиотеке:
Файл — PT2258.h
В платформе Arduino адрес I2C 7 бит, поэтому вместо 0х88 (0B10001000), указывается 0х44 (0B1000100).
Регулятор громкости на PT2258 содержит следующие компоненты:
- Плата Arduino Nano
- Дисплей LCD2004 с модулем I2C
- Часы реального времени DS3231
- Энкодер KY-040
- Три тактовые кнопки
- ИК-датчик VS1838B
Схема регулятора громкости
Основные функциональные возможности регулятора громкости:
- Основное меню:
- Регулировка громкости 64 уровня (64 дБ), осуществляется при помощи энкодера и ИК пульта
- Вывод даты и времени
- Второе меню (для перехода в меню нажать кнопку SET, нажимая на кнопку энкодера можно перебирать пункты меню)
- По канальная регулировка аттенюаторов выходов в диапазоне от 0 до 15 дБ
- Регулировка яркости подсветки дисплея в обычном режиме и в режиме STANDBY
- Меню коррекции времени
- Режим установки-корректировки времени:
Нажать и удерживать кнопку энкодера, далее нажать кнопку SET, после появится меню корректировки времени, параметры перебираются кнопкой энкодера, ручка энкодера меняет выбранный параметр.
- Режим установки-корректировки времени:
- Режим MUTE, активируется при помощи кнопки MUTE или ИК пульта
- Режим STANDBY, активируется при помощи кнопки или ИК пульта
Так как предусмотрена управляемая яркость подсветки дисплея, то можно установить яркость подсветки в основном режиме и в режиме STANDBY (как правило с пониженной яркостью подсветки), регулировка яркости подсветки осуществляется в меню №2.
Подсветка — убрать перемычку с модуля I2C PCF8574 и подключить вывод модуля к цифровому выходу Arduino D6. Перед подключением замерить ток подсветки который не должен превышать 20 мА (у моего модуля ток не более 15 мА, замер производить между контактами перемычки).
Максимальный выходной ток одного выхода Arduino Nano не должен превышать 40 мА.
ИК пульт дублирует работу энкодера и кнопок. Для управления регулятором подойдет практически любой пульт ИК, для поддержки Вашего пульта необходимо прописать коды кнопок в скетч:
#define POWER 0x33B800FF #define VOLUME_UP 0x33B8E01F #define VOLUME_DW 0x33B810EF #define MUT 0x33B8946B
Для получения кодов кнопок Вашего пульта загрузите скетч и откройте монитор порта, при нажатии кнопки пульта его код будет отображен в мониторе порта.
#define POWER 0x33B800FF #define VOLUME_UP 0x33B8E01F #define VOLUME_DW 0x33B810EF #define MUT 0x33B8946B #include <Wire.h> #include <LiquidCrystal_I2C.h> // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1 #include <Encoder.h> // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip #include <EEPROM.h> #include <MsTimer2.h> // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip #include <boarddefs.h> // входит в состав библиотеки IRremote #include <IRremote.h> // http://rcl-radio.ru/wp-content/uploads/2019/06/IRremote.zip #include <PT2258.h> // https://github.com/liman324/PT2258.git #include <DS3231.h> PT2258 pt; LiquidCrystal_I2C lcd(0x27,20,4); // Устанавливаем дисплей Encoder myEnc(9, 8);// DT, CLK IRrecv irrecv(12); // указываем вывод модуля IR приемника decode_results ir; DS3231 clock;RTCDateTime DateTime; byte v1[8] = {0b00111,0b00111,0b00111,0b00111,0b00111,0b00111,0b00111,0b00111}; byte v2[8] = {0b00111,0b00111,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000}; byte v3[8] = {0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b11111,0b11111}; byte v4[8] = {0b11111,0b11111,0b00000,0b00000,0b00000,0b00000,0b11111,0b11111}; byte v5[8] = {0b11100,0b11100,0b00000,0b00000,0b00000,0b00000,0b11100,0b11100}; byte v6[8] = {0b11100,0b11100,0b11100,0b11100,0b11100,0b11100,0b11100,0b11100}; byte v7[8] = {0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b00111,0b00111}; byte v8[8] = {0b11111,0b11111,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000}; unsigned long times,oldPosition = -999,newPosition; int vol,ch1,ch2,ch3,ch4,ch5,ch6,menu,menu1,menu2,brig,brig0=10,brig1,temp0,i; bool mute,power,w,w1,gr1,gr2; byte a[6],d1,d2,d3,d4,d5,d6,e1,e2,e3,x,www,tim_w; int hour,minut,secon,data,mon,year,time_temp; byte mesto2[8]={0,10,0,10,0,10,0,10}; byte mesto3[8]={0,0,1,1,2,2,3,3}; int hh,mm,ss,dd,mn,gg; void setup(){ irrecv.enableIRIn();lcd.init();lcd.backlight();Wire.begin();clock.begin();Serial.begin(9600); MsTimer2::set(1, to_Timer);MsTimer2::start(); lcd.createChar(1, v1);lcd.createChar(2, v2);lcd.createChar(3, v3);lcd.createChar(4, v4); lcd.createChar(5, v5);lcd.createChar(6, v6);lcd.createChar(7, v7);lcd.createChar(8, v8); if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении pinMode(10,INPUT); // МЕНЮ КНОПКА SW энкодера pinMode(6,OUTPUT); // ВЫХОД УПРАВЛЕНИЯ ПОДСВЕТКОЙ pinMode(2,INPUT_PULLUP); // КНОПКА SET pinMode(3,INPUT_PULLUP); // КНОПКА MUTE pinMode(4,INPUT_PULLUP); // КНОПКА POWER pinMode(5,OUTPUT); // Выход управления STANDBY усилителя // clock.setDateTime(__DATE__, __TIME__); // Устанавливаем время на часах, основываясь на времени компиляции скетча vol = EEPROM.read(0);ch1 = EEPROM.read(1);ch2 = EEPROM.read(2);ch3 = EEPROM.read(3); ch4 = EEPROM.read(4);ch5 = EEPROM.read(5);ch6 = EEPROM.read(6); brig0 = EEPROM.read(7);brig1 = EEPROM.read(8); analogWrite(6, brig0*25); delay(500); audio(); } void loop(){ /// READ TIME ///////////////////////////////// DateTime = clock.getDateTime();hour = DateTime.hour;minut = DateTime.minute;secon = DateTime.second; data = DateTime.day;mon = DateTime.month;year = DateTime.year; /// BUTTON //////////////////////////////////// if(power==0){ if(digitalRead(10)==LOW&&menu==1){menu1++;delay(200);times=millis();w=1;w1=1;if(menu1>7){menu1=0;}} if(digitalRead(10)==LOW&&menu==2){menu2++;delay(200);times=millis();w=1;w1=1;if(menu2>5){menu2=0;}} if(digitalRead(2)==LOW&&digitalRead(10)==HIGH){menu++;delay(200);times=millis();cl();w=1;w1=1;menu1=0;if(menu>1){menu=0;}} // SET if(digitalRead(2)==LOW&&digitalRead(10)==LOW){menu=2;delay(200);times=millis();cl();w=1;w1=1;lcd.setCursor(2,1);lcd.print("TIME CORRECTION");delay(1000);} // SET time if((digitalRead(3)==LOW||ir.value==MUT)&&mute==0){mute=1;cl();audio();menu=100;lcd.setCursor(8,1);lcd.print("MUTE");delay(500);}// mute on if((digitalRead(3)==LOW||ir.value==MUT)&&mute==1){mute=0;cl();times=millis();w=1;w1=1;menu=0;menu1=0;audio();} // mute off } if((digitalRead(4)==LOW||ir.value==POWER)&&power==0){power=1;cl();times=millis();w=1;w1=1;menu=100;menu1=0;mute=1;audio();brig = brig1;analogWrite(6, brig*25);} // power off if((digitalRead(4)==LOW||ir.value==POWER)&&power==1){power=0;cl();times=millis();w=1;w1=1;menu=0;menu1=0;mute=0;audio();brig = brig0;analogWrite(6, brig*25);} // power on if(power==1){digitalWrite(5,LOW);}else{digitalWrite(5,HIGH);} // output standby /// IR //////////////////////////////////////// if ( irrecv.decode( &ir )) {Serial.print("0x");Serial.println( ir.value,HEX);irrecv.resume();times=millis();w=1;w1=1;}// IR приемник - чтение, в мониторе порта отображаются коды кнопок if(ir.value==0){gr1=0;gr2=0;}// запрет нажатий не активных кнопок пульта ////////////// VOLUME /////////////////////////////////////////////////////////////////// if(menu==0){ if(ir.value==VOLUME_UP){vol++;gr1=1;gr2=0;cl1();times=millis();w=1;vol_func();audio();}// кнопка > if(ir.value==0xFFFFFFFF and gr1==1){vol++;gr2=0;cl1();times=millis();;w=1;vol_func();audio();}// кнопка >>>>>> if(ir.value==VOLUME_DW){vol--;gr1=0;gr2=1;cl1();times=millis();;w=1;vol_func();audio();}// кнопка < if(ir.value==0xFFFFFFFF and gr2==1){vol--;gr1=0;cl1();times=millis();w=1;vol_func();audio();}// кнопка <<<<<< if (newPosition != oldPosition){oldPosition = newPosition;vol=vol+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;vol_func();audio();} a[0]= (64-vol)/10;a[1]=(64-vol)%10; for(x=0;x<2;x++){switch(x){case 0: e1=11,e2=12,e3=13;break;case 1: e1=14,e2=15,e3=16;break;}digit();} lcd.setCursor(0,0);lcd.print("VOLUME"); lcd.setCursor(0,1);lcd.print("CONTROL"); lcd.setCursor(0,2);lcd.print("--------------------"); lcd.setCursor(0,3); lcd.print(hour/10);lcd.print(hour%10);lcd.print(":"); lcd.print(minut/10);lcd.print(minut%10);lcd.print(":"); lcd.print(secon/10);lcd.print(secon%10);lcd.print(" "); lcd.print(data/10);lcd.print(data%10);lcd.print("-"); lcd.print(mon/10);lcd.print(mon%10);lcd.print("-"); lcd.print(year); brig=brig0; analogWrite(6, brig*25); } ///////// ATT ////////////////////////////////////////////////////////////////////////////// if(menu==1){ switch(menu1){ case 0: temp0 = ch1;break; case 1: temp0 = ch2;break; case 2: temp0 = ch3;break; case 3: temp0 = ch4;break; case 4: temp0 = ch5;break; case 5: temp0 = ch6;break; case 6: temp0 = brig0;break; case 7: temp0 = brig1;break; } if (newPosition != oldPosition){oldPosition = newPosition; temp0=temp0-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;www=1;} switch(menu1){ case 0: ch1 = temp0;if(ch1<0){ch1=0;}if(ch1>15){ch1=15;}break; case 1: ch2 = temp0;if(ch2<0){ch2=0;}if(ch2>15){ch2=15;}break; case 2: ch3 = temp0;if(ch3<0){ch3=0;}if(ch3>15){ch3=15;}break; case 3: ch4 = temp0;if(ch4<0){ch4=0;}if(ch4>15){ch4=15;}break; case 4: ch5 = temp0;if(ch5<0){ch5=0;}if(ch5>15){ch5=15;}break; case 5: ch6 = temp0;if(ch6<0){ch6=0;}if(ch6>15){ch6=15;}break; case 6: brig0 = temp0;brig0_func();break; case 7: brig1 = temp0;brig1_func();break; } au(); if(menu1<7){brig=brig0;} if(menu1==7){brig=brig1;} analogWrite(6, brig*25); for(i=0;i<8;i++){if(menu1==i){lcd.setCursor(mesto2[i],mesto3[i]);lcd.print(">");}else{lcd.setCursor(mesto2[i],mesto3[i]);lcd.print(" ");}} lcd.setCursor(1,0);lcd.print("CH1 ");lcd.print(ch1);lcd.print(" ");lcd.setCursor(11,0);lcd.print("CH2 ");lcd.print(ch2);lcd.print(" "); lcd.setCursor(1,1);lcd.print("CH3 ");lcd.print(ch3);lcd.print(" ");lcd.setCursor(11,1);lcd.print("CH4 ");lcd.print(ch4);lcd.print(" "); lcd.setCursor(1,2);lcd.print("CH5 ");lcd.print(ch5);lcd.print(" ");lcd.setCursor(11,2);lcd.print("CH6 ");lcd.print(ch6);lcd.print(" "); lcd.setCursor(1,3);lcd.print("BR_1 ");lcd.print(brig0);lcd.print(" ");lcd.setCursor(11,3);lcd.print("BR_2 ");lcd.print(brig1);lcd.print(" "); } //////// menu set time ///////////////////////////////////// if(menu==2){ hh = DateTime.hour;mm = DateTime.minute;ss = DateTime.second;dd = DateTime.day;mn = DateTime.month;gg = DateTime.year; switch(menu2){ case 0: time_temp = hh;break; case 1: time_temp = mm;break; case 2: time_temp = ss;break; case 3: time_temp = dd;break; case 4: time_temp = mn;break; case 5: time_temp = gg;break; } if (newPosition != oldPosition){oldPosition = newPosition; time_temp=time_temp-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;tim_w=1;} switch(menu2){ case 0: hh = time_temp;if(hh<0){hh=0;}if(hh>23){hh=23;}break; case 1: mm = time_temp;if(mm<0){mm=0;}if(mm>59){mm=59;}break; case 2: ss = time_temp;if(ss<0){ss=0;}if(ss>59){ss=59;}break; case 3: dd = time_temp;if(dd<1){dd=1;}if(dd>31){dd=31;}break; case 4: mn = time_temp;if(mn<1){mn=1;}if(mn>12){mn=12;}break; case 5: gg = time_temp;if(gg<2022){gg=2022;}if(gg>2100){gg=2100;}break; } lcd.setCursor(0,0);lcd.print("TIME CORRECTION"); lcd.setCursor(0,1);lcd.print("--------------------"); lcd.setCursor(0,2); if(menu2==0){lcd.print("set hour ");} if(menu2==1){lcd.print("set minute ");} if(menu2==2){lcd.print("set seconds");} if(menu2==3){lcd.print("set date ");} if(menu2==4){lcd.print("set month ");} if(menu2==5){lcd.print("set year ");} lcd.setCursor(0,3); lcd.print(hour/10);lcd.print(hour%10);lcd.print(":"); lcd.print(minut/10);lcd.print(minut%10);lcd.print(":"); lcd.print(secon/10);lcd.print(secon%10);lcd.print(" "); lcd.print(data/10);lcd.print(data%10);lcd.print("-"); lcd.print(mon/10);lcd.print(mon%10);lcd.print("-"); lcd.print(year); if(tim_w==1){tim_w=0; clock.setDateTime(gg, mn, dd, hh, mm, ss);delay(100);} } /////// EEPROM ////////////////////////////////////// if(millis()-times>10000 && w1==1 && mute==0 && power==0){w1=0; EEPROM.update(0,vol);EEPROM.update(1,ch1);EEPROM.update(2,ch2);EEPROM.update(3,ch3); EEPROM.update(4,ch4);EEPROM.update(5,ch5);EEPROM.update(6,ch6); EEPROM.update(7,brig0);EEPROM.update(8,brig1); menu=0;cl(); } if(power==1){digitalWrite(7,LOW); a[0]=DateTime.hour/10; a[1]=DateTime.hour%10; a[2]=DateTime.minute/10; a[3]=DateTime.minute%10; a[4]=DateTime.second/10; a[5]=DateTime.second%10; for(i=0;i<6;i++){ switch(i){ case 0: e1=0,e2=1,e3=2;break; case 1: e1=3,e2=4,e3=5;break; case 2: e1=7,e2=8,e3=9;break; case 3: e1=10,e2=11,e3=12;break; case 4: e1=14,e2=15,e3=16;break; case 5: e1=17,e2=18,e3=19;break; } switch(a[i]){ case 0: d1=1,d2=8,d3=6,d4=1,d5=3,d6=6;break; case 1: d1=32,d2=2,d3=6,d4=32,d5=32,d6=6;break; case 2: d1=2,d2=8,d3=6,d4=1,d5=4,d6=5;break; case 3: d1=2,d2=4,d3=6,d4=7,d5=3,d6=6;break; case 4: d1=1,d2=3,d3=6,d4=32,d5=32,d6=6;break; case 5: d1=1,d2=4,d3=5,d4=7,d5=3,d6=6;break; case 6: d1=1,d2=4,d3=5,d4=1,d5=3,d6=6;break; case 7: d1=1,d2=8,d3=6,d4=32,d5=32,d6=6;break; case 8: d1=1,d2=4,d3=6,d4=1,d5=3,d6=6;break; case 9: d1=1,d2=4,d3=6,d4=7,d5=3,d6=6;break; } lcd.setCursor(e1,0);lcd.write((uint8_t)d1);lcd.setCursor(e2,0);lcd.write((uint8_t)d2);lcd.setCursor(e3,0);lcd.write((uint8_t)d3); lcd.setCursor(e1,1);lcd.write((uint8_t)d4);lcd.setCursor(e2,1);lcd.write((uint8_t)d5);lcd.setCursor(e3,1);lcd.write((uint8_t)d6); } lcd.setCursor(6,0);lcd.print(".");lcd.setCursor(13,0);lcd.print(".");lcd.setCursor(6,1);lcd.print(".");lcd.setCursor(13,1);lcd.print("."); lcd.setCursor(0,2);lcd.print("--------------------"); lcd.setCursor(5,3);lcd.print("POWER OFF"); } } /// END LOOP void au(){if(www==1){audio();www=0;}} void cl(){ir.value=0;delay(300);lcd.clear();} void cl1(){ir.value=0;delay(200);} void brig1_func(){if(brig1>10){brig1=10;}if(brig1<0){brig1=0;}} void brig0_func(){if(brig0>10){brig0=10;}if(brig0<0){brig0=0;}} void audio(){ pt.clear(); // pt.setVolume(75); // int 0...79 pt.setVol1(79-(vol+ch1)); // int 0...79 pt.setVol2(79-(vol+ch2)); // int 0...79 pt.setVol3(79-(vol+ch3)); // int 0...79 pt.setVol4(79-(vol+ch4)); // int 0...79 pt.setVol5(79-(vol+ch5)); // int 0...79 pt.setVol6(79-(vol+ch6)); // int 0...79 pt.setMute(mute); // mute 0 - off / 1 - on } void to_Timer(){newPosition = myEnc.read()/4;} void vol_func(){if(vol<0){vol=0;}if(vol>64){vol=64;}} void digit(){switch(a[x]){ case 0: d1=1,d2=8,d3=6,d4=1,d5=3,d6=6;break;case 1: d1=32,d2=2,d3=6,d4=32,d5=32,d6=6;break; case 2: d1=2,d2=8,d3=6,d4=1,d5=4,d6=5;break;case 3: d1=2,d2=4,d3=6,d4=7,d5=3,d6=6;break; case 4: d1=1,d2=3,d3=6,d4=32,d5=32,d6=6;break;case 5: d1=1,d2=4,d3=5,d4=7,d5=3,d6=6;break; case 6: d1=1,d2=4,d3=5,d4=1,d5=3,d6=6;break;case 7: d1=1,d2=8,d3=6,d4=32,d5=32,d6=6;break; case 8: d1=1,d2=4,d3=6,d4=1,d5=3,d6=6;break;case 9: d1=1,d2=4,d3=6,d4=7,d5=3,d6=6;break;} lcd.setCursor(e1,0);lcd.write((uint8_t)d1);lcd.setCursor(e2,0);lcd.write((uint8_t)d2);lcd.setCursor(e3,0);lcd.write((uint8_t)d3); lcd.setCursor(e1,1);lcd.write((uint8_t)d4);lcd.setCursor(e2,1);lcd.write((uint8_t)d5);lcd.setCursor(e3,1);lcd.write((uint8_t)d6);}
Форум — http://forum.rcl-radio.ru/viewtopic.php?pid=6321#p6321
PT2258 + OLED 0.96 + encoder + IR
D10 — MUTE КНОПКА SW энкодера
D2 — КНОПКА SET
D4 — КНОПКА POWER
D5 — Выход управления STANDBY усилителя
Скетч — http://forum.rcl-radio.ru/viewtopic.php?id=525
PT2258 + OLED 0.96 + encoder + IR + часы
Время отображается в режиме POWER OFF.
Модуль часов DS3231 или DS1307.
Для коррекции времени перейти в режим STANDBY (POWER OFF) нажать и удерживать кнопку энкодера, для корректировки часов нажать SET, для минут POWER.
D10 — MUTE КНОПКА SW энкодера
D2 — КНОПКА SET
D4 — КНОПКА POWER
D5 — Выход управления STANDBY усилителя
Скетч — http://forum.rcl-radio.ru/viewtopic.php?pid=6985#p6985