Внешний ЦАП собран за базе ЦАП PCM1796 который обладает весьма качественными характеристиками. Управление параметрами ЦАП PCM1796 микроконтроллерное, осуществляется при помощи платы Arduino Nano, позволяющее изменять громкость, активировать режим MUTE, применять цифровые фильтры Rolloff и De-Emphasis. Ресивер S/PDIF собран на CS8416.
Для нормальной работы ресивер и ЦАП должный иметь одинаковый формат передачи цифровых данных, в данном случае это I2S 24 бит с частотой дискретизации 192 кГц.
Параметры ресивера 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 (аппаратно настроен)
- Разрядность 16 — 24 бит (аппаратно настроен на 24 бит)
- Частота дискредитации 32 — 192 кГц (аппаратно настроен на 192 кГц)
- Фильтр De-emphasis:
- No De-emphasis (аппаратно настроен)
- 32 kHz
- 48 kHz
- 44.1 kHz
- Кол-во входов: 8 (аппаратно поддерживает 4, настроен на 1 канал)
Параметры ЦАП на PCM1796
- Напряжение питания
- 5-V Analog
- 3.3-V Digital
- Входной формат данных:
- Left-Justified
- Right-Justified
- I2S (программно настроен)
- Частота дискредитации: 10 кГц to 200 кГц
- System Clock: 128, 192, 256, 384, 512, or 768 fS With Autodetect
- Коэффициент гармоник с учетом шума (THD+N): 0.0005%
- Динамический диапазон 123 дБ
- Отношение сигнал.шум 123 дБ
- Разделение каналов 117 дБ
- Диапазон регулировки громкости от -120 до 0 дБ (программно ограничен от -50 до 0 дБ)
- Фильтры:
- Digital De-Emphasis (OFF, 32,0 44,1 48,0 кГц ) (программно поддерживается)
- Digital Filter Rolloff: Sharp or Slow (программно поддерживается)
- Soft Mute (программно поддерживается)
- Zero Flag for Each Output (программно поддерживается)
- Управление: SPI
Внешний ЦАП состоит из нескольких компонентов:
- Ресивер на CS8416
- ЦАП на PCM1796
- ФНЧ на NE5532
- Источник питания
- Плата Arduino Nano
- Индикатор LCD1602 с модулем I2C
- Энкодер KY-040
- Кнопка — MUTE
Схема ресивера на CS8416
Выше показана схема ресивера на CS8416, он имеет один активный вход, но можно использовать все 4-е доступные входы. Коммутация входами осуществляется при помощи пинов RSEL1 и RSEL0 микросхемы ресивера , при этом лог. 1 подается с питания VL (+5 В), а логический ноль с DGND.
INPUT | RSEL0 | RSEL1 |
RXP0 | 0 | 0 |
RXP1 | 1 | 0 |
RXP2 | 0 | 1 |
RXP3 | 1 | 1 |
Ресивер работает независимо от Arduino, в схеме присутствует индикатор режима работы светодиод D1, при подключении коаксиального кабеля с источником цифрового сигнала светодиод гаснет, при отключении кабеля загорается.
Схема ЦАП на PCM1796
Схема ФНЧ на NE5532
Блок управления на Arduino Nano
Регулировка громкости в диапазоне от -50 до 0 дБ осуществляется при помощи энкодера KY-040, нажатие кнопки энкодера переключает пункты меню, в первом меню находятся настройка уровня громкости, а так же выводятся данные о состоянии фильтров, второе меню отвечает за фильтр De-Emphasis, третье за фильтр Rolloff. Дополнительно в блоке управления используется кнопка для активации режима MUTE.
Схема стабилизаторов питания
Для питания внешнего ЦАПа рекомендуется использовать три независимых источника питания (для питания ресивера, ЦАП и ФНЧ), который состоит из одного трансформатора с тремя независимыми вторичными обмотками, одна из которых имеет отвод от середины для двух полярного источника питания. Каждый источник питания имеет свой диодный мост. При этом аналоговая и цифровая земля объединяются через дроссель. Дополнительно как можно ближе к микросхемам по цепи питания устанавливаются фильтрующие конденсаторы.
Упрощенная схема стабилизаторов
#define MS 6 // PCM1796 #define MDI 5 // PCM1796 #define MC 4 // PCM1796 #define MDO 3 // PCM1796 #define RST 2 // PCM1796 #define CLK 9 // ENCODER #define DT 8 // ENCODER #define SW 10 // ENCODER #define MUTE 7 // BUTTON MUTE // LCD1602 - I2C | A4=SDA A5=SCL #include <EEPROM.h> #include <MsTimer2.h> // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip #include <Encoder.h> // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip #include <LiquidCrystal_I2C.h> // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1 Encoder myEnc(CLK, DT); LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей 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}; unsigned long times,times1,oldPosition = -999,newPosition; int menu,w,w2,vol,mute,a[3],dem,roll,zero; byte i,d1,d2,d3,d4,d5,d6,e1,e2,e3; void setup() { Serial.begin(9600);MsTimer2::set(1, to_Timer);MsTimer2::start(); pinMode(RST,OUTPUT); pinMode(MDI,OUTPUT); pinMode(MC,OUTPUT); pinMode(MS,OUTPUT); pinMode(MDO,INPUT); pinMode(SW,INPUT); pinMode(MUTE,INPUT_PULLUP); digitalWrite(RST,LOW);digitalWrite(MC,LOW);digitalWrite(MS,HIGH); 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); lcd.setCursor(5,0);lcd.print("CS 8416"); lcd.setCursor(5,1);lcd.print("PCM1796"); delay(1000);lcd.clear(); if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении vol = EEPROM.read(0);dem = EEPROM.read(1);roll = EEPROM.read(2); digitalWrite(RST,HIGH); audio(); } void loop() { /// MENU ////////////////////////////// if(digitalRead(SW)==LOW){menu++;if(menu>2){menu=0;};delay(200);lcd.clear();w=1;times=millis();w2=1;} /// MUTE /////////////////// if(digitalRead(MUTE)==LOW&&mute==0){mute=1;audio();delay(200);} if(digitalRead(MUTE)==LOW&&mute==1){mute=0;audio();delay(200);} /// VOLUME /////////////////////////////////////////////////// if(menu==0){ if (newPosition != oldPosition){oldPosition = newPosition; vol=vol+newPosition;myEnc.write(0);newPosition=0;w=1;times=millis();w2=1; if(vol>99){vol=99;}if(vol<0){vol=0;} audio();} lcd.setCursor(0,0); if(mute==1){lcd.print("MUTE ");} if(mute==0&&zero>0){lcd.print("No Sound");} if(mute==0&&zero==0){lcd.print("VOLUME ");} lcd.setCursor(0,1); switch(roll){ case 0: lcd.print("SH ");break; case 1: lcd.print("SL ");break; } switch(dem){ case 0: lcd.print("OFF ");break; case 1: lcd.print("48.0");break; case 2: lcd.print("44.1");break; case 3: lcd.print("32.0");break; } if(w2==1){w2=0; a[0]=vol/10;a[1]=vol%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); }} } /// DE-EMPHASIS ////////////////////////////////// if(menu==1){ if (newPosition != oldPosition){oldPosition = newPosition; dem=dem+newPosition;myEnc.write(0);newPosition=0;w=1;times=millis();w2=1; if(dem<0){dem=3;}if(dem>3){dem=0;} audio();} if(w2==1){w2=0; lcd.setCursor(0,0);lcd.print("DE-EMPHASIS SEL"); lcd.setCursor(0,1); switch(dem){ case 0: lcd.print("No De-emphasis");break; case 1: lcd.print("48.0 kHz ");break; case 2: lcd.print("44.1 kHz ");break; case 3: lcd.print("32.0 kHz ");break; }}} /// Roll-off Filter ////////////////////////////////// if(menu==2){ if (newPosition != oldPosition){oldPosition = newPosition; roll=roll+newPosition;myEnc.write(0);newPosition=0;w=1;times=millis();w2=1; if(roll<0){roll=1;}if(roll>1){roll=0;} audio();} if(w2==1){w2=0; lcd.setCursor(0,0);lcd.print("Roll-off Filter"); lcd.setCursor(0,1); switch(roll){ case 0: lcd.print("Sharp");break; case 1: lcd.print("Slow ");break; }}} //// EEPROM /////////////////////////////////////// if(millis()-times>5000 && w==1){w=0;EEPROM.update(0,vol);EEPROM.update(1,dem);EEPROM.update(2,roll);menu=0;lcd.clear();w2=1;} delay(100); /// READ REGISTER if(millis()-times1>1000){ zero=ReadPCM1796(0b10010110); Serial.print("Zero-Detection Flag ");Serial.println(zero,BIN); // REG_22 | Zero-Detection Flag Serial.print("Device ID ");Serial.println(ReadPCM1796(0b10010111),BIN); // REG_23 | Device ID Serial.println();times1=millis();} } void to_Timer(){newPosition = myEnc.read()/4;} void audio(){ WritePCM1796(0b00010000, vol+156); // REG_16 WritePCM1796(0b00010001, vol+156); // REG_17 WritePCM1796(0b00010010, 0b11010010 + mute + (dem << 2)); // REG_18 WritePCM1796(0b00010011, 0b00000000 + (roll << 1)); // REG_19 WritePCM1796(0b00010100, 0b00000000); // REG_20 WritePCM1796(0b00010101, 0b00000001); // REG_21 } void WritePCM1796(byte reg, byte mdi){ // WRITE_REG digitalWrite(MC,LOW);digitalWrite(MS,LOW); for(int i = 7; i >= 0; i--){ digitalWrite(MC,LOW); digitalWrite(MDI, (reg >> i) & 0x01); digitalWrite(MC,HIGH); } for(int i = 7; i >= 0; i--){ digitalWrite(MC,LOW); digitalWrite(MDI, (mdi >> i) & 0x01); digitalWrite(MC,HIGH); } digitalWrite(MC,LOW);digitalWrite(MS,HIGH);delay(1); } byte ReadPCM1796(byte reg){ // READ_REG byte dat[8],data = 0; digitalWrite(MC,LOW);digitalWrite(MS,LOW); for(int i = 7; i >= 0; i--){ digitalWrite(MC,LOW); digitalWrite(MDI, (reg >> i) & 0x01); digitalWrite(MC,HIGH); } for(int i = 7; i >= 0; i--){ digitalWrite(MC,LOW); dat[i] = digitalRead(MDO); data = data + (dat[i] << i); digitalWrite(MC,HIGH); } digitalWrite(MC,LOW);digitalWrite(MS,HIGH);delay(1); return data; }
The Arduino code doesn’t work it gives many errors can someone fix it?
код ошибок?
Hi Liman thanks forr the response and this fantatic project. When I copy paste the code into arduino IDE it gives all these errors when complile.
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:19:23: error: hexadecimal floating constants require an exponent
byte v1 [ 8 ] = { 0x07.0x07.0x07.0x07.0x07.0x07.0x07.0x07 } ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:20:23: error: hexadecimal floating constants require an exponent
byte v2 [ 8 ] = { 0x07.0x07.0x00.0x00.0x00.0x00.0x00.0x00 } ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:21:23: error: hexadecimal floating constants require an exponent
byte v3 [ 8 ] = { 0x00.0x00.0x00.0x00.0x00.0x00.0x1F,0x1F } ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:22:33: error: hexadecimal floating constants require an exponent
byte v4 [ 8 ] = { 0x1F,0x1F,0x00.0x00.0x00.0x00.0x1F,0x1F } ;
^~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:23:33: error: hexadecimal floating constants require an exponent
byte v5 [ 8 ] = { 0x1C,0x1C,0x00.0x00.0x00.0x00.0x1C,0x1C } ;
^~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:25:23: error: hexadecimal floating constants require an exponent
byte v7 [ 8 ] = { 0x00.0x00.0x00.0x00.0x00.0x00.0x07.0x07 } ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:26:33: error: hexadecimal floating constants require an exponent
byte v8 [ 8 ] = { 0x1F,0x1F,0x00.0x00.0x00.0x00.0x00.0x00 } ;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino: In function ‘void setup()’:
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:46:29: error: expected ‘)’ before ‘!’ token
if ( EEPROM. read ( 100 ) ! = 0 ) { for ( int i= 0 ;i = 0 ; i— ) {
^
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:166:28: error: expected primary-expression before ‘=’ token
for ( int i = 7 ; i > = 0 ; i— ) {
^
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino: In function ‘byte ReadPCM1796(byte)’:
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:177:28: error: expected primary-expression before ‘=’ token
for ( int i = 7 ; i > = 0 ; i— ) {
^
D:\Temp\.arduinoIDE-unsaved2023920-8648-4dr3vi.i2uk6\sketch_oct20a\sketch_oct20a.ino:182:28: error: expected primary-expression before ‘=’ token
for ( int i = 7 ; i > = 0 ; i— ) {
^
exit status 1
Compilation error: hexadecimal floating constants require an exponent
Измените язык сайта на русский и скопируйте скетч по новой.
That worked, thank you Liman I can now finish this project, thank you for sharing it 🙂