Тема: Регулятор громкости и тембра LC75342 на Atmega88 (Arduino IDE)
Основная статья - http://rcl-radio.ru/?p=120528
// ATMEGA88 12 MHz
#define CE PD0
#define DI PD1
#define CL PD2
#define DT PD5
#define CLK PD6
#define SW PD7
#define IN PB1
#define MUTE PB2
#include <avr/io.h>
#include <util/delay.h>
#include <Wire_low.h> // http://forum.rcl-radio.ru/viewtopic.php?pid=5521#p5521
#include <Lcd1602_i2c_low.h> // http://rcl-radio.ru/wp-content/uploads/2022/03/Lcd1602_i2c_low.zip
Lcd1602_i2c_low lcd(0x27);// адрес I2C
volatile uint8_t _prevValueAB = 0;
volatile uint8_t _currValueAB = 0;
volatile int16_t newPosition = 0;
int position = -999;
int menu,vol_reg,mute_reg,in_reg,vol_old,treb_reg,treb_print,bass_reg,bass_print,ball,chl,chr,i;
byte a[6],d1,d2,d3,d4,d5,d6,e1,e2,e3,w,w2,x,www;
int gain0,gain1,gain2,gain3,gain4,gain5,gain0_print;
unsigned long millis_times,times;
int main(){
cli();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 46874;
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS12);
TIMSK1 |= (1 << OCIE1A);
sei();
wire_set(12000000,100000); // тактовая частота контроллера, частота шины I2C
lcd.setInit();
lcd.Clear(); // очистка экрана
lcd.led(1); // включение и отключение подсветки экрана
lcd.Write(0, 0b00111,0b00111,0b00111,0b00111,0b00111,0b00111,0b00111,0b00111);
lcd.Write(1, 0b00111,0b00111,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000);
lcd.Write(2, 0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b11111,0b11111);
lcd.Write(3, 0b11111,0b11111,0b00000,0b00000,0b00000,0b00000,0b11111,0b11111);
lcd.Write(4, 0b11100,0b11100,0b00000,0b00000,0b00000,0b00000,0b11100,0b11100);
lcd.Write(5, 0b11100,0b11100,0b11100,0b11100,0b11100,0b11100,0b11100,0b11100);
lcd.Write(6, 0b00000,0b00000,0b00000,0b00000,0b00000,0b00000,0b00111,0b00111);
lcd.Write(7, 0b11111,0b11111,0b00000,0b00000,0b00000,0b00000,0b00000,0b00000);
DDRD |=(1<<CE)|(1<<DI)|(1<<CL);
PORTD &=~(1<<CE)|(1<<DI)|(1<<CL);
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT21)|(1 << PCINT22);
PORTB |=(1<<IN)|(1<<MUTE);
if(EEPROM_read(100)!=0){for(int i=0;i<101;i++){EEPROM_write(i,0);}}// очистка памяти при первом включении
vol_reg = EEPROM_read(0);treb_reg = EEPROM_read(1)-5;bass_reg = EEPROM_read(2)-10;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();
while(1){
/// BUTTON ///////////////////////////////////
if(mute_reg==0){
if(((PIND >> SW) & 1)==0){menu++;cl();w2=1;w=1;times=millis_times;if(menu>3){menu=0;}}
if(((PINB >> IN) & 1)==0){in_reg++;menu=4;cl();w=1;times=millis_times;if(in_reg>3){in_reg=0;}}
}
if((((PINB >> MUTE) & 1)==0)&&mute_reg==0){mute_reg=1;menu=100;cl();w=1;times=millis_times;vol_old=vol_reg;vol_reg=79;audio_R();audio_L();lcd.Curs(0,6);lcd.PrintString("MUTE");}
if((((PINB >> MUTE) & 1)==0)&&mute_reg==1){mute_reg=0;menu=0;cl();w=1;times=millis_times;vol_reg=vol_old;audio_R();audio_L();}
////////////// VOLUME ///////////////////////////////////////////////////////////////////
if(menu==0){
if(newPosition != position){position = newPosition;vol_reg = vol_reg+newPosition;newPosition=0;w=1;times=millis_times;vol_func();audio_R();audio_L();}
a[0]= (79-vol_reg)/10;a[1]=(79-vol_reg)%10;
for(x=0;x<2;x++){switch(x){case 0: e1=10,e2=11,e3=12;break;case 1: e1=13,e2=14,e3=15;break;}digit();}
if(mute_reg==0){lcd.Curs(0,0);lcd.PrintString("VOLUME");}else{lcd.Curs(0,0);lcd.PrintString("MUTE");}
lcd.Curs(1,0);lcd.PrintString("INPUT ");lcd.PrintInt(in_reg+1);
}
////////////// TREBLE ///////////////////////////////////////////////////////////////////
if(menu==1){
if(newPosition != position){position = newPosition;treb_reg = treb_reg-newPosition;newPosition=0;w=1;times=millis_times;treb_func();audio_R();audio_L();}
if(treb_reg<0){treb_print = (-treb_reg)*2;lcd.Curs(0,7);lcd.PrintChar(2);}else{treb_print = treb_reg*2;lcd.Curs(0,7);lcd.PrintString(" ");}
a[0]= treb_print/10;a[1]=treb_print%10;
for(x=0;x<2;x++){switch(x){case 0: e1=8,e2=9,e3=10;break;case 1: e1=11,e2=12,e3=13;break;}digit();}
lcd.Curs(0,0);lcd.PrintString("TREBLE");lcd.Curs(1,0);lcd.PrintString("CONTROL");lcd.Curs(0,14);lcd.PrintString("dB");
}
////////////// BASS ///////////////////////////////////////////////////////////////////
if(menu==2){
if(newPosition != position){position = newPosition;bass_reg = bass_reg-newPosition;newPosition=0;w=1;times=millis_times;bass_func();audio_R();audio_L();}
if(bass_reg<0){bass_print = (-bass_reg)*2;lcd.Curs(0,7);lcd.PrintChar(2);}else{bass_print = bass_reg*2;lcd.Curs(0,7);lcd.PrintString(" ");}
a[0]= bass_print/10;a[1]=bass_print%10;
for(x=0;x<2;x++){switch(x){case 0: e1=8,e2=9,e3=10;break;case 1: e1=11,e2=12,e3=13;break;}digit();}
lcd.Curs(0,0);lcd.PrintString("BASS");lcd.Curs(1,0);lcd.PrintString("CONTROL");lcd.Curs(0,14);lcd.PrintString("dB");
}
//////// BALANCE ///////////////////////////////////////////////////////////////
if(menu==3){
if(newPosition != position){position = newPosition;ball = ball-newPosition;newPosition=0;w=1;w2=1;times=millis_times;ball_fun();audio_R();audio_L();}
lcd.Curs(0,4);lcd.PrintString(" <> ");lcd.Curs(1,4);lcd.PrintString("CHL CHR");
chl=(4+ball)-4;chr=(4-ball)-4;
if(chl<0){lcd.Curs(0,12);chl=(-chl);lcd.PrintChar(2);}else{lcd.Curs(0,12);lcd.PrintString(" ");}
if(chr<0){lcd.Curs(0,0);chr=(-chr);lcd.PrintChar(2);}else{lcd.Curs(0,0);lcd.PrintString(" ");}
if(w2==1){w2=0;a[0]=chl;a[1]=chr;
for(i=0;i<2;i++){
switch(i){
case 0: e1=1,e2=2,e3=3;break;
case 1: e1=13,e2=14,e3=15;break;
}
switch(a[i]){
case 0: d1=0,d2=7,d3=5,d4=0,d5=2,d6=5;break;case 1: d1=32,d2=1,d3=5,d4=32,d5=32,d6=5;break;
case 2: d1=1,d2=7,d3=5,d4=0,d5=3,d6=4;break;case 3: d1=1,d2=3,d3=5,d4=6,d5=2,d6=5;break;
case 4: d1=0,d2=2,d3=5,d4=32,d5=32,d6=5;break;case 5: d1=0,d2=3,d3=4,d4=6,d5=2,d6=5;break;
case 6: d1=0,d2=3,d3=4,d4=0,d5=2,d6=5;break;case 7: d1=0,d2=7,d3=5,d4=32,d5=32,d6=5;break;
case 8: d1=0,d2=3,d3=5,d4=0,d5=2,d6=5;break;case 9: d1=0,d2=3,d3=5,d4=6,d5=2,d6=5;break;
}
char_lcd();
}}}
////////////// 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 != position){position = newPosition;gain0 = gain0-newPosition;newPosition=0;w=1;times=millis_times;www=1;gain_func();}
switch(in_reg){
case 0: gain1 = gain0;break;
case 1: gain2 = gain0;break;
case 2: gain3 = gain0;break;
case 3: gain4 = gain0;break;}
gain0_print = gain0*2;
a[0]= gain0_print/10;a[1]=gain0_print%10;
for(x=0;x<2;x++){switch(x){case 0: e1=8,e2=9,e3=10;break;case 1: e1=11,e2=12,e3=13;break;}digit();}
if(www==1){audio_R();audio_L();www=0;}
lcd.Curs(0,0);lcd.PrintString("IN GAIN");lcd.Curs(0,14);lcd.PrintString("dB");
lcd.Curs(1,0);lcd.PrintString("INPUT ");lcd.PrintInt(in_reg+1);}
////////////////// EEPROM //////////////////////////////////////////////////////////////
if(millis_times-times>10 && w==1 && mute_reg==0){
EEPROM_write(0,vol_reg);EEPROM_write(1,treb_reg+5);EEPROM_write(2,bass_reg+10);EEPROM_write(4,gain1);
EEPROM_write(5,gain2);EEPROM_write(6,gain3);EEPROM_write(7,gain4);EEPROM_write(8,gain5);
EEPROM_write(9,in_reg);EEPROM_write(10,ball+4);
if(menu!=0){lcd.Clear();menu=0;}w=0;}
}}// end while
ISR(TIMER1_COMPA_vect){millis_times++;}
void digit(){switch(a[x]){
case 0: d1=0,d2=7,d3=5,d4=0,d5=2,d6=5;break;case 1: d1=32,d2=1,d3=5,d4=32,d5=32,d6=5;break;
case 2: d1=1,d2=7,d3=5,d4=0,d5=3,d6=4;break;case 3: d1=1,d2=3,d3=5,d4=6,d5=2,d6=5;break;
case 4: d1=0,d2=2,d3=5,d4=32,d5=32,d6=5;break;case 5: d1=0,d2=3,d3=4,d4=6,d5=2,d6=5;break;
case 6: d1=0,d2=3,d3=4,d4=0,d5=2,d6=5;break;case 7: d1=0,d2=7,d3=5,d4=32,d5=32,d6=5;break;
case 8: d1=0,d2=3,d3=5,d4=0,d5=2,d6=5;break;case 9: d1=0,d2=3,d3=5,d4=6,d5=2,d6=5;break;}
char_lcd();}
void char_lcd(){
lcd.Curs(0,e1);lcd.PrintChar(d1);lcd.Curs(0,e2);lcd.PrintChar(d2);lcd.Curs(0,e3);lcd.PrintChar(d3);
lcd.Curs(1,e1); lcd.PrintChar(d4);lcd.Curs(1,e2);lcd.PrintChar(d5);lcd.Curs(1,e3);lcd.PrintChar(d6);
}
void vol_func(){if(vol_reg<4){vol_reg=4;}if(vol_reg>79){vol_reg=79;}}
void gain_func(){{if(gain0<0){gain0=0;}if(gain0>15){gain0=15;}}}
void ball_fun(){if(ball>4){ball=4;}if(ball<-4){ball=-4;}}
void bass_func(){if(bass_reg<-10){bass_reg=-10;}if(bass_reg>10){bass_reg=10;}}
void treb_func(){if(treb_reg<-5){treb_reg=-5;}if(treb_reg>5){treb_reg=5;}}
void cl(){_delay_ms(300);lcd.Clear();}
ISR(PCINT2_vect){
bool pinA = ((PIND >> DT) & 1);
bool pinB = ((PIND >> CLK) & 1);
_currValueAB = (pinA << 1) | pinB;
switch(_prevValueAB | _currValueAB){
case 0b0001: newPosition++;break;
case 0b0100: newPosition--;break;
}
_prevValueAB = _currValueAB << 2;
}
void addr(){
PORTD &=~(1<<CL)|(1<<CE);
byte addr = 0b01000001;
for(int i = 7; i >= 0; i--){
PORTD &=~(1<<CL);
if(((addr>>i)&0x01)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);}
PORTD |=(1<<CL);
}
PORTD |=(1<<CE);
}
void set_input(byte in){
for(int i = 0; i <= 3; i++){
PORTD &=~(1<<CL);
switch(i){
case 0: if(((in & 0b0001)>>0)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 1: if(((in & 0b0010)>>1)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 2: if(((in & 0b0100)>>2)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 3: if(((in & 0b1000)>>3)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
}
PORTD |=(1<<CL);
}
}
void set_gain(byte gain){
for(int i = 0; i <= 3; i++){
PORTD &=~(1<<CL);
switch(i){
case 0: if(((gain & 0b0001)>>0)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 1: if(((gain & 0b0010)>>1)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 2: if(((gain & 0b0100)>>2)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 3: if(((gain & 0b1000)>>3)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
}
PORTD |=(1<<CL);
}
}
void set_volume(byte vol){
for(int i = 0; i <= 7; i++){
PORTD &=~(1<<CL);
switch(i){
case 0: if(((vol & 0b00000001)>>0)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 1: if(((vol & 0b00000010)>>1)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 2: if(((vol & 0b00000100)>>2)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 3: if(((vol & 0b00001000)>>3)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 4: if(((vol & 0b00010000)>>4)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 5: if(((vol & 0b00100000)>>5)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 6: if(((vol & 0b01000000)>>6)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
case 7: if(((vol & 0b10000000)>>7)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);};break;
}
PORTD |=(1<<CL);
}
}
void set_treble(int treb){
switch(treb){
case 5: treb = 0b1010;break;//10dB
case 4: treb = 0b0010;break;//8dB
case 3: treb = 0b1100;break;//6dB
case 2: treb = 0b0100;break;//4dB
case 1: treb = 0b1000;break;//2dB
case 0: treb = 0b0000;break;//0dB
case -1: treb = 0b1001;break;//-2dB
case -2: treb = 0b0101;break;//-4dB
case -3: treb = 0b1101;break;//-6dB
case -4: treb = 0b0011;break;//-8dB
case -5: treb = 0b1011;break;//10dB
}
for(int i = 3; i >= 0; i--){
PORTD &=~(1<<CL);
if(((treb>>i)&0x01)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);}
PORTD |=(1<<CL);
}
}
void set_bass(int bass){
switch(bass){
case 10: bass = 0b010100;break;//20dB
case 9 : bass = 0b100100;break;//18dB
case 8 : bass = 0b000100;break;//16dB
case 7 : bass = 0b111000;break;//14dB
case 6 : bass = 0b011000;break;//12dB
case 5 : bass = 0b101000;break;//10dB
case 4 : bass = 0b001000;break;//8dB
case 3 : bass = 0b110000;break;//6dB
case 2 : bass = 0b010000;break;//4dB
case 1 : bass = 0b101000;break;//2dB
case 0 : bass = 0b000000;break;//0dB
case -1: bass = 0b100010;break;//-2dB
case -2: bass = 0b010010;break;//-4dB
case -3: bass = 0b110010;break;//-6dB
case -4: bass = 0b001010;break;//-8dB
case -5: bass = 0b101010;break;//-10dB
case -6: bass = 0b011010;break;//-12dB
case -7: bass = 0b111010;break;//-14dB
case -8: bass = 0b000110;break;//-16dB
case -9: bass = 0b100110;break;//-18dB
case -10: bass = 0b010110;break;//-20dB
}
for(int i = 5; i >= 0; i--){
PORTD &=~(1<<CL);
if(((bass>>i)&0x01)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);}
PORTD |=(1<<CL);
}
}
void set_ch(byte ch){
switch(ch){
case 1: ch = 0b01;break;
case 2: ch = 0b10;break;
case 3: ch = 0b11;break;
}
for(int i = 1; i >= 0; i--){
PORTD &=~(1<<CL);
if(((ch>>i)&0x01)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);}
PORTD |=(1<<CL);
}
}
void test(){
byte test = 0;
for(int i = 3; i >= 0; i--){
PORTD &=~(1<<CL);
if(((test>>i)&0x01)==1){PORTD |=(1<<DI);}else{PORTD &=~(1<<DI);}
PORTD |=(1<<CL);
}
PORTD &=~(1<<CL);
PORTD &=~(1<<CE);
}
void audio_L(){
addr();
set_input(0); // input 1...4 = byte 0...3 (byte 4...7 = All switches off)
set_gain(gain0); // gain 0...30 dB step 2 dB = byte 0...15
set_volume(vol_reg-ball); // volume 0...-79 dB = byte 0...79
set_treble(treb_reg); // treble 10...-10 dB step 2 dB = int 5...-5
set_bass(bass_reg); // bass 20...-20 dB step 2 dB = int 10...-10
set_ch(1); // Channel Selection RCH = byte 2, LCH = byte 1, Left and right together = byte 3
test();
}
void audio_R(){
addr();
set_input(0); // input 1...4 = byte 0...3 (byte 4...7 = All switches off)
set_gain(gain0); // gain 0...30 dB step 2 dB = byte 0...15
set_volume(vol_reg+ball); // volume 0...-79 dB = byte 0...79
set_treble(treb_reg); // treble 10...-10 dB step 2 dB = int 5...-5
set_bass(bass_reg); // bass 20...-20 dB step 2 dB = int 10...-10
set_ch(2); // Channel Selection RCH = byte 2, LCH = byte 1, Left and right together = byte 3
test();
}
unsigned char EEPROM_read(unsigned int uiAddress){
while(EECR & (1<<EEPE)); // проверка готовности EEPROM
EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H
EEARL = uiAddress & 0x0F; // регистр адреса L
EECR |= (1<<EERE);// чтение EEPROM
return EEDR; // вывод значения
}
void EEPROM_write(unsigned int uiAddress, unsigned char ucData){
while(EECR & (1<<EEPE)); // проверка готовности EEPROM
EEARH = ((uiAddress & 0xF0) << 2); // регистр адреса H
EEARL = uiAddress & 0x0F; // регистр адреса L
EEDR = ucData; // регистр данных
EECR |= (1<<EEMPE);// Разрешение записи в EEPROM
EECR |= (1<<EEPE); // Запись в EEPROM
}