Внешний звуковой ЦАП под управлением платы 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> // https://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip #include <MsTimer2.h> // https://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;}
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.