ЦАП — цифро-аналоговый преобразователь — это устройство, которое преобразует информацию из цифрового вида в аналоговые сигналы, при этом максимально точно и без искажений.
Собрать внешний ЦАП на компонентах предложных в статье не сложно, он содержит несколько недорогих компонентов и в настройке практически не нуждается.
Входной цифровой сигнал имеет формат S/PDIF (цифровой аудио интерфейс разработанный фирмами SONY/PHILIPS, предназначен для передачи цифрового сигнала между аудио устройствами), ЦАП AK4393 не поддерживает такой формат, для преобразования S/PDIF необходим ресивер на DIR9001 который преобразует S/PDIF в цифровой сигнал в формат I2S. С выхода ЦАП звуковой сигнал поступает на фильтр-сумматор собранный на ОУ NE5532, а с выхода фильтра на цифровой регулятор громкости PGA2311.
Внешний звуковой ЦАП содержит несколько основных компонента:
- Плата Arduino Nano
- Ресивер на DIR9001 (24 бит 96 кГц)
- ЦАП на AK4393 (24 бит 96 кГц)
- Цифровой регулятор громкости PGA2311
- Фильтр-сумматор на NE5532
- LCD1602
- Энкодер KY-040
Характеристики основных компонентов внешнего ЦАПа:
- DIR9001
- Входной формат — S/PDIF
- Выходной формат:
- 16-bit, MSB-first, right-justified
- 24-bit, MSB-first, right-justified
- 24-bit, MSB-first, left-justified
- 24-bit, MSB-first, I2S
- Напряжение питания
- Digital power supply … 3,3 В
- Digital power supply … 3,3 В
- Диапазон частот дискретизации входного сигнала 28 — 108 кГц
Так как вход ресивера DIR9001 имеет вход уровня TTL, сигнал S/PDIF подается через преобразователь интерфейса MAX485. Ресивер аппаратно настроен на выходной сигнал формата 24 bit / I2S / 96 kHz.
В схеме ресивера присутствует индикатор режима работы светодиод D1, при подключении коаксиального кабеля с источником цифрового сигнала светодиод гаснет, при отключении кабеля загорается.
- AK4393
- Входные форматы:
- 16bit LSB Justified
- 20bit LSB Justified
- 24bit MSB Justified
- I2S Compatible
- 24bit LSB Justified
- De-emphasis filter
- 38 кГц
- 44,1 кГц
- 48 кГц
- 96 кГц
- Напряжение питания
- Digital power supply … 5 В
- Digital power supply … 5 В
- Уровень гармонических искажений -100 дБ
- Динамический диапазон 120 дБ
- Отношение сигнал/шум 120 дБ
- Частота дискретизации до 108 кГц
- Выходной сигнал — дифференциальные аудиовыходы
- Входные форматы:
Управление ЦАП AK4393 программное, настроен на входной сигнал формата 24 bit / I2S / 96 kHz.
Схема фильтра сумматора
- PGA2311
- Диапазон регулировки громкости от -95,5 дБ до +31.5 дБ с шагом 0,5 дБ (255 ступеней)
- Динамический диапазон 120 дБ
- Коэффициент нелинейных искажений не более 0.0002%
- Межканальные перекрестные помехи не более -130 дБ
- Входное напряжение 2.5 Vrms
- Режим MUTE
Программно диапазон регулировки громкости немного уменьшен, добавлена регулировка баланса.
Схема стабилизаторов питания
Рекомендуется как можно ближе к микросхемам по цепи питания устанавливаются фильтрующие конденсаторы 0,1 и 22 мкФ.
Подключение к плате Arduino
Во внешнем ЦАПе использован энкодер KY-040 для управления громкостью, балансом, фильтром De-emphasis, дополнительно используется кнопка для активации режима MUTE. Информация о режимах работы внешнего ЦАП выводится на LCD дисплея LCD1602 на базе контроллера HD44780 совместно с модулем I2C. I2C модуль на базе микросхемы PCF8574 позволяют подключить символьный дисплей 1602 к плате Arduino всего по двум проводам SDA и SCL (А4 и А5), что дает возможность не использовать цифровые выходы Arduino при подключении дисплея.
Меню внешнего ЦАПа состоит из трех пунктов:
- Регулировка громкости от -86,0 до +14,0 дБ, шаг регулировки 0,5 дБ
- Регулировка баланса от -10 до +10 дБ, шаг регулировки 0,5 дБ
- Фильтр De-emphasis filter 38 кГц, 44,1 кГц, 48 кГц, 96 кГц
#define SCK 5 // CCLK AK4393 #define DATA 4 // CDTI AK4393 #define CS 3 // CSN AK4393 #define RES 2 // PDN AK4393 #define SCLK 10 // SCLK PGA2311 #define SDATAI 9 // SDI PGA2311 #define CSP 8 // CS PGA2311 #define MUTE 11 // MUTE PGA2311 #define B_MUTE 12 // BUTTON MUTE #define CLK A0 // CLK ENCODER #define DT A1 // DT ENCODER #define SW A2 // SW ENCODER // LCD1602 I2C A4 = SDA, A5 = SCL #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(CLK, DT); byte a1[8] = {0b00000,0b10101,0b10101,0b10101,0b10101,0b10101,0b10101,0b00000}; byte a2[8] = {0b00000,0b10100,0b10100,0b10100,0b10100,0b10100,0b10100,0b00000}; byte a3[8] = {0b00000,0b10000,0b10000,0b10000,0b10000,0b10000,0b10000,0b00000}; unsigned long times,oldPosition = -999,newPosition; int vol,vol_d,z,z0,z1,vol_old,balanc,emp; byte w,w1,www,mute,menu; void setup(){ pinMode(CS,OUTPUT); pinMode(SCK,OUTPUT); pinMode(DATA,OUTPUT); pinMode(RES,OUTPUT); pinMode(CSP,OUTPUT); pinMode(SCLK,OUTPUT); pinMode(SDATAI,OUTPUT); pinMode(MUTE,OUTPUT); pinMode(SW,INPUT); pinMode(B_MUTE,INPUT_PULLUP); Wire.begin();Serial.begin(9600); lcd.init();lcd.backlight(); MsTimer2::set(1, to_Timer);MsTimer2::start(); lcd.createChar(0,a1);lcd.createChar(1,a2);lcd.createChar(2,a3); lcd.setCursor(0,0);lcd.print(" DIR9001 AK4393 "); lcd.setCursor(0,1);lcd.print(" PGA2311 ");delay(2000);lcd.clear(); vol = EEPROM.read(0); balanc = EEPROM.read(1)-20; emp = EEPROM.read(2); digitalWrite(RES,HIGH); digitalWrite(MUTE,HIGH); WriteAK4393(0b01100000,0b00000110); // reset WriteAK4393(0b01100000,0b00000111); // 128fs | I2S switch(emp){ case 0: WriteAK4393(0b01100001,0b00001100);break; case 1: WriteAK4393(0b01100001,0b00000000);break; case 2: WriteAK4393(0b01100001,0b00001000);break; case 3: WriteAK4393(0b01100001,0b00001010);break; } WritePGA2311(vol+balanc,vol-balanc); } void loop(){ /// MENU /////////////////////////////////////// if(digitalRead(SW)==LOW){menu++;if(menu>2){menu=0;}lcd.clear();times=millis();w=1;w1=1;www=1;delay(200);} /// MUTE /////////////////////////////////////// if(digitalRead(B_MUTE)==LOW&&mute==0){mute=1;digitalWrite(MUTE,LOW);lcd.clear();lcd.setCursor(0,0);lcd.print(" MUTE ");delay(300);} if(digitalRead(B_MUTE)==LOW&&mute==1){mute=0;myEnc.write(0);newPosition=0;digitalWrite(MUTE,HIGH);lcd.clear();w1=1;delay(300);} /////////////// VOLUME ///////////////////////////////////////////////////////// if(menu==0&&mute==0){ if (newPosition != oldPosition&&mute==0){oldPosition = newPosition; vol=vol+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;vol_func();WritePGA2311(vol+balanc,vol-balanc);} vol_d=map(vol,20,220,0,45); if(w1==1){ for(z=0,z0=0,z1=0;z<=vol_d;z++,z1++){if(z1>2){z1=0;z0++;} if(z1==1){lcd.setCursor(z0,1);lcd.write((uint8_t)0);lcd.setCursor(z0+1,1);lcd.print(" ");}} if(z1==3){lcd.setCursor(z0,1);lcd.write((uint8_t)1);} if(z1==2){lcd.setCursor(z0,1);lcd.write((uint8_t)2);}w1=0;} lcd.setCursor(0,0);lcd.print("VOLUME "); if(vol*0.5-96>=0){lcd.print("+");} lcd.print(vol*0.5-96,1);lcd.print(" "); lcd.setCursor(14,0);lcd.print("dB"); } // menu == 0 /////////////// BALANCE +/- 10 dB ///////////////////////////////////////////////////////// if(menu==1&&mute==0){ if (newPosition != oldPosition&&mute==0){oldPosition = newPosition; balanc=balanc+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;balanc_func();WritePGA2311(vol+balanc,vol-balanc);} lcd.setCursor(0,0);lcd.print("BALANCE ");if(balanc>=0){lcd.print("+");}lcd.print(balanc*0.5,1);lcd.print(" ");lcd.setCursor(14,0);lcd.print("dB"); if(w1==1){ if(balanc<0){lcd.setCursor(balanc/4-2+7,1);lcd.print(" ");lcd.write((uint8_t)0);lcd.print(" ");} if(balanc==0){lcd.setCursor(balanc/4-2+7,1);lcd.print(" ");lcd.write((uint8_t)0);lcd.write((uint8_t)0);lcd.print(" ");} if(balanc>0){lcd.setCursor(balanc/4-2+8,1);lcd.print(" ");lcd.write((uint8_t)0);lcd.print(" ");} w1=0;} }// menu == 1 /////////////// De-emphasis filter ///////////////////////////////////////////////////////// if(menu==2&&mute==0){ if (newPosition != oldPosition&&mute==0){oldPosition = newPosition; emp=emp+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;emp_func();www=1;} lcd.setCursor(0,0);lcd.print("De-emphasis filt");lcd.setCursor(4,1); if(www==1){www=0; switch(emp){ case 0: lcd.print("32.0 kHz");WriteAK4393(0b01100001,0b00001100);break; case 1: lcd.print("44.1 kHz");WriteAK4393(0b01100001,0b00000000);break; case 2: lcd.print("48.0 kHz");WriteAK4393(0b01100001,0b00001000);break; case 3: lcd.print("96.0 kHz");WriteAK4393(0b01100001,0b00001010);break; }} }// menu == 2 //////// EEPROM ////////////////////////////////////////////////// if(millis()-times>10000 && w==1 && mute == 0){EEPROM.update(0,vol);EEPROM.update(1,balanc+20);EEPROM.update(2,emp);w=0;menu=0;w1=1;lcd.clear();} }// loop void WriteAK4393(byte reg, byte data){ // WRITE_REG SPI digitalWrite(SCK,HIGH);digitalWrite(CS,LOW); for(int i = 7; i >= 0; i--){ digitalWrite(SCK,LOW); digitalWrite(DATA, (reg >> i) & 0x01); digitalWrite(SCK,HIGH); } for(int i = 7; i >= 0; i--){ digitalWrite(SCK,LOW); digitalWrite(DATA, (data >> i) & 0x01); digitalWrite(SCK,HIGH); } digitalWrite(SCK,HIGH);digitalWrite(CS,HIGH); } void WritePGA2311(byte r_ch, byte l_ch){ // WRITE_REG SPI digitalWrite(SCLK,HIGH);digitalWrite(CSP,LOW); for(int i = 7; i >= 0; i--){ digitalWrite(SCLK,LOW); digitalWrite(SDATAI, (r_ch >> i) & 0x01); digitalWrite(SCLK,HIGH); } for(int i = 7; i >= 0; i--){ digitalWrite(SCLK,LOW); digitalWrite(SDATAI, (l_ch >> i) & 0x01); digitalWrite(SCLK,HIGH); } digitalWrite(CSP,HIGH); } void to_Timer(){newPosition = myEnc.read()/4;} void vol_func(){if(vol<20){vol=20;}if(vol>220){vol=220;}} void emp_func(){if(emp<0){emp=3;}if(emp>3){emp=0;}} void balanc_func(){if(balanc<-20){balanc=-20;}if(balanc>20){balanc=20;}}