Внешний звуковой ЦАП под управлением платы Arduino (Nano) состоит из нескольких компонентов:
- Ресивер CS8416
- ЦАП WM8740
- ФНЧ на NE5532
- Источника питания
- Платы Arduino (Nano)
- Индикатор LCD1602 (I2C)
- Органы регулировки — экнодер, кнопка
Параметры ресивера CS8416:
- Напряжение питания:
- +3.3 V Analog Supply (VA)
- +3.3 V Digital Supply (VD)
- +3.3 V or +5.0 V Digital Interface Supply (VL)
- Входной формат данных: S/PDIF
- Выходной формат данных:
- Left-Justified
- Right-Justified
- I2S
- Разрядность 24 бит
- Частота дискредитации 192 кГц
- Фильтр De-emphasis:
- No De-emphasis
- 32 kHz
- 48 kHz
- 44.1 kHz
- Кол-во входов: 8
- Управление: I2C
Параметры ЦАП на WM8740:
- Напряжение питания:
- +5 V Analog Supply (AVDD)
- +5 V Digital Supply (DVDD)
- Входной формат данных:
- Left-Justified
- Right-Justified
- I2S
- Разрядность 24 бит
- Частота дискредитации 192 кГц
- Уровень гармонических искажений THD+N
- -104 dB (0dB)
- -117 dB (-60 dB)
- Отношение сигнал/шум 107 dB
- Фильтр De-emphasis:
- No De-emphasis
- 32 kHz
- 48 kHz
- 44.1 kHz
- Фильтр ROLL-OFF:
- Sharp
- Slow
- Управление: SPI
Внешний ЦАП достаточно простой, состоит из недорогих компонентов. Ресивер CS8416 управляется при помощи шины I2C, содержит 8 входов S/PDIF (Toslink), работает по трем выходным форматам данных (Left-Justified, Right-Justified, I2S), содержит переключаемый фильтр De-emphasis, содержит систему опознавания ошибок. ЦАП на WM8740 так же работает на прием трех форматов данных (Left-Justified, Right-Justified, I2S), содержит переключаемые фильтры De-emphasis и ROLL-OFF, имеет систему MUTE, регулятор громкости на 255 шагов (127 дБ), каждый шаг 0,5 дБ, программно число шагов ограничено от 155 до 255 значений, что соответствует диапазону регулировки от -50 до 0 дБ.
Вся информация о текущих настройках внешнего ЦАП отображается на индикаторе LCD1602 ( с модулем I2C), регулировка всех параметров осуществляется при помощи энкодера KY-040, параметр MUTE активируется отдельной кнопкой.
Схема ресивера
Схема ЦАП
Схема ФНЧ
Схема источника питания (стабилизаторов)
Для питания внешнего ЦАПа рекомендуется использовать три независимых источника питания, который состоит из одного трансформатора с тремя независимыми вторичными обмотками, одна из которых имеет отвод от середины для двух полярного источника питания. Каждый источник питания имеет свой диодный мост. При этом аналоговая и цифровая земля объединяются через дроссель. Дополнительно как можно ближе к микросхемам по цепи питания устанавливаются фильтрующие конденсаторы 0,1 и 22 мкФ.
Arduino
- Меню громкости (99 шагов), индикация выбранного входа, индикатор MUTE и ERROR
- Меню выбора входа
- Меню фильтра De-emphasis
- Меню фильтра ROLL-OFF
- Меню выбора выходного формата
#define CS 2 // ML WM8740 #define CLK 3 // MC WM8740 #define DATA 4 // MD WM8740 #include <Wire.h> #include <EEPROM.h> #include <Encoder.h> // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip #include <MsTimer2.h> // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip #include <LiquidCrystal_I2C.h> //Библиотека - http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1 LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей Encoder myEnc(9, 8);//CLK, DT byte v1[8] = {0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}; byte v2[8] = {0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00}; byte v3[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F}; byte v4[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x1F,0x1F}; byte v5[8] = {0x1C,0x1C,0x00,0x00,0x00,0x00,0x1C,0x1C}; byte v6[8] = {0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C}; byte v7[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07}; byte v8[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00}; int a[3],vol,vol_d,menu,in,dem,form; byte i,d1,d2,d3,d4,d5,d6,e1,e2,e3; unsigned long times,times1,oldPosition = -999,newPosition; bool w,w1,www=1,mute; byte rol,dac_i2s,dac_iw,dem_res,for_dac,for_res,err,err_old; void setup(){ Wire.begin(); Serial.begin(9600); MsTimer2::set(1, to_Timer);MsTimer2::start(); lcd.init();lcd.backlight(); 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); pinMode(10,INPUT); // SW энкодер pinMode(11,INPUT_PULLUP); // кнопка MUTE pinMode(CS,OUTPUT); // ML WM8740 pinMode(CLK,OUTPUT); // MC WM8740 pinMode(DATA,OUTPUT);// MD WM8740 if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении vol = EEPROM.read(0);in = EEPROM.read(1);dem_res = EEPROM.read(3);for_dac = EEPROM.read(4);for_res = EEPROM.read(5); dac_i2s = EEPROM.read(6);dac_iw = EEPROM.read(7);rol = EEPROM.read(8); lcd.setCursor(0,0);lcd.print(" CS8416 WM8740 "); lcd.setCursor(0,1);lcd.print(" 24 bit 192kHz "); // Write WM8740 SPI wireWrite(0b000100000000);//reg0 wireWrite(0b001100000000);//reg1 wireWrite(0b010000000000 + (dac_iw << 3));//reg2 wireWrite(0b011000000000 + dac_i2s + (rol << 3));//reg3 wireWrite(0b110000000000);//reg4 // Write 8416 I2C Wire.beginTransmission(0x10); Wire.write (0x00); Wire.write (0b00000000); Wire.write (0b10000010); Wire.write (0b00000000 + (dem_res << 4)); Wire.write (0b00000000); Wire.write (0b10000000 + (in << 3)); Wire.write (0b10000000 + (for_res)); Wire.write (0xFF); Wire.write (0x00); Wire.write (0x00); Wire.write (0x00); Wire.endTransmission(); delay(2000);lcd.clear(); // Write WM8740 SPI wireWrite(0b000100000000 + vol);//reg0 wireWrite(0b001100000000 + vol);//reg1 } void loop(){ /// MENU ////////////////////////////// if(digitalRead(10)==LOW){menu++;if(menu>4){menu=0;};delay(200);lcd.clear();times=millis();w=1;w1=1;www=1;} /// MUTE ///////////////////////////// if(digitalRead(11)==LOW && mute==0){mute=1; wireWrite(0b010000000001 + (dac_iw << 3));delay(300);www=1;} if(digitalRead(11)==LOW && mute==1){mute=0; wireWrite(0b010000000000 + (dac_iw << 3));delay(300);www=1;} if(menu==0){ if (newPosition != oldPosition){oldPosition = newPosition;www=0; vol=vol-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1; if(vol<155){vol=155;}if(vol>255){vol=255;} wireWrite(0b000100000000 + vol);//reg0 wireWrite(0b001100000000 + vol);//reg1 Serial.println(vol);// vol = 155...255 step 0.5 dB } if(www==1||w==1){www=0;w=0; if(mute==1){ lcd.setCursor(0,0);lcd.print("MUTE ");} if(err==0&&mute==0){ lcd.setCursor(0,0);lcd.print("VOLUME ");} if(err==22&&mute==0){lcd.setCursor(0,0);lcd.print("ERROR ");} if(err==8&&mute==0){ lcd.setCursor(0,0);lcd.print("NO SOUND");} lcd.setCursor(0,1);lcd.print("COAX ");lcd.print(in); vol_d=vol-156;a[0]=vol_d/10;a[1]=vol_d%10; for(i=0;i<2;i++){ switch(i){ case 0: e1=9,e2=10,e3=11;break; case 1: e1=12,e2=13,e3=14;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); }}} /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////// INPUT //////////////////////////////////// if(menu==1){ if (newPosition != oldPosition){oldPosition = newPosition; in=in-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1; if(in<0){in=0;}if(in>7){in=7;} Wire.beginTransmission(0x10); Wire.write (0x00); Wire.write (0b00000000); Wire.write (0b10000010); Wire.write (0b00000000 + (dem_res << 4)); Wire.write (0b00000000); Wire.write (0b10000000 + (in << 3)); Wire.write (0b10000000 + (for_res)); Wire.write (0xFF); Wire.write (0x00); Wire.write (0x00); Wire.write (0x00); Wire.endTransmission();} delay(10); if(www==1||w==1){www=0;w=0; lcd.setCursor(0,0);lcd.print("INPUT SELECTOR"); lcd.setCursor(0,1);lcd.print("COAXIAL "); lcd.print(in);lcd.print(" "); }} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// DE-EMPHASIS ////////////////////////////////// if(menu==2){ if (newPosition != oldPosition){oldPosition = newPosition; dem=dem-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1; if(dem<0){dem=0;}if(dem>3){dem=3;} Wire.beginTransmission(0x10); Wire.write (0x00); Wire.write (0b00000000); Wire.write (0b10000010); Wire.write (0b00000000 + (dem_res << 4)); Wire.write (0b00000000); Wire.write (0b10000000 + (in << 3)); Wire.write (0b10000000 + (for_res)); Wire.write (0xFF); Wire.write (0x00); Wire.write (0x00); Wire.write (0x00); Wire.endTransmission(); delay(10); } if(www==1||w==1){www=0;w=0; lcd.setCursor(0,0);lcd.print("DE-EMPHASIS SEL"); lcd.setCursor(0,1); switch(dem){ case 0: lcd.print("No De-emphasis");dem_res=0;break; case 1: lcd.print("44.1 kHz ");dem_res=2;break; case 2: lcd.print("48.0 kHz ");dem_res=3;break; case 3: lcd.print("32.0 kHz ");dem_res=1;break; }}} //////////////////////////////////////////////////////////////////////////////////////////////////////////// /// FILTER ROLL-OFF ////////////////////////////////// if(menu==3){ if (newPosition != oldPosition){oldPosition = newPosition; rol=rol-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1; if(rol<0){rol=0;}if(rol>1){rol=1;} wireWrite(0b011000000000 + dac_i2s + (rol << 3));//reg3 delay(10); } if(www==1||w==1){www=0;w=0; lcd.setCursor(0,0);lcd.print("ROLL-OFF SELECT "); lcd.setCursor(0,1); switch(rol){ case 0: lcd.print("Sharp");break; case 1: lcd.print("Slow ");break; }}} //////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Serial Format Select ////////////////////////////////// if(menu==4){ if (newPosition != oldPosition){oldPosition = newPosition; form=form-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1; if(form<0){form=0;}if(form>2){form=2;} Wire.beginTransmission(0x10); Wire.write (0x00); Wire.write (0b00000000); Wire.write (0b10000010); Wire.write (0b00000000 + (dem_res << 4)); Wire.write (0b00000000); Wire.write (0b10000000 + (in << 3)); Wire.write (0b10000000 + (for_res)); Wire.write (0xFF); Wire.write (0x00); Wire.write (0x00); Wire.write (0x00); Wire.endTransmission(); delay(10); wireWrite(0b010000000000 + (dac_iw << 3));//reg2 wireWrite(0b011000000000 + dac_i2s + (rol << 3));//reg3 delay(10); } if(www==1||w==1){www=0;w=0; lcd.setCursor(0,0);lcd.print("FORMAT SELECT"); lcd.setCursor(0,1); switch(form){ case 0: lcd.print("L-Just 24 bit");dac_i2s=0;dac_iw=3;for_res=0;break; case 1: lcd.print("I2S 24 bit");dac_i2s=1;dac_iw=1;for_res=5;break; case 2: lcd.print("R-Just 24 bit");dac_i2s=0;dac_iw=2;for_res=8;break; }}} //////////////////////////////////////////////////////////////////////////////////////////////////////// /// read registr error if(millis()-times1>1000){times1=millis();err = wireRead(0x10,0x0C);delay(10);} if(err!=err_old){err_old = err;www=1;} /// EEPROM write if(millis()-times>10000 && w1==1){w1=0;EEPROM.update(0,vol);EEPROM.update(1,in); EEPROM.update(3,dem_res);EEPROM.update(4,for_dac);EEPROM.update(5,for_res); EEPROM.update(6,dac_i2s);EEPROM.update(7,dac_iw);EEPROM.update(8,rol); menu=0;www=1;if(menu!=0){lcd.clear();}} delay(100); }// loop void wireWrite(int data){ // SPI digitalWrite(CLK,HIGH);digitalWrite(CS,HIGH);delay(1); for(int i = 15; i >= 0; i--){ digitalWrite(CLK,LOW); digitalWrite(DATA, (data >> i) & 0x01);delay(1); digitalWrite(CLK,HIGH);delay(1);} digitalWrite(CS,LOW);delay(1);digitalWrite(CS,HIGH); } byte wireRead(int addr, int reg){ // I2C Wire.beginTransmission(addr); Wire.write (reg); Wire.endTransmission(); delay(10); Wire.requestFrom(addr,1); while(Wire.available()<1); byte value = Wire.read(); return value; } void to_Timer(){newPosition = myEnc.read()/4;}
Hello. Tried this software, but even with cs8416 connected for I2C comunication, only stays at volume 00/coax 0. Tried another software for use with diferent dac/receiver and worked perfectly. Any tip? Using arduino Nano.thanks
I didn’t understand the question, rephrase the question, I use machine translation.
When uploading this sketch to arduino, only stays at display showing volume, coax 0 and 00. No menu working or encoder. i tried other sketch of another project published here and worked fine!
the encoder must switch the input and adjust the volume
Still didn’t work, my CS8416 could be dmaged but the sketch should work even without being connected…