Ранее на странице DAC PCM1796 + CS8416 (Arduino) рассматривался пример создания внешнего ЦАПа на базе PCM1796 с ресивером CS8416, на этой странице будет рассмотрен аналогичный пример, но с использованием ресивера на базе WM8804.
Входной цифровой сигнал для внешнего ЦАПа имеет формат S/PDIF (цифровой аудио интерфейс разработанный фирмами SONY/PHILIPS, предназначен для передачи цифрового сигнала между аудио устройствами), ресивер на WM8804 преобразует его в формат I2S 24 бит с частотой дискретизации 192 кГц. Цифровой сигнала I2S поступает на ЦАП PCM1796, далее с выхода PCM1796 звуковой сигнал поступает на фильтр-сумматор собранный на ОУ NE5532.
Для нормальной работы ресивер и ЦАП должный иметь одинаковый формат передачи цифровых данных, в данном случае это I2S 24 бит с частотой дискретизации 192 кГц.
Параметры ресивера WM8804:
- Напряжение питания:
- +3.3 V DVDD
- +3.3 V PVDD
- Входной формат данных: S/PDIF
- Выходной формат данных:
- I2S (аппаратно настроен)
- Left Justified
- Right Justified
- Разрядность 16/20/24 бит (аппаратно настроен на 24 бит)
- Частота дискредитации 32 to 192 кГц (аппаратно настроен на 192 кГц)
- MCLK rate of 512fs, 256fs, 128fs и 64fs
Параметры ЦАП на 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
Внешний ЦАП состоит из нескольких компонентов:
- Ресивер на WM8804
- ЦАП на PCM1796
- ФНЧ на NE5532
- Источник питания
- Плата Arduino Nano
- Индикатор LCD1602 с модулем I2C
- Энкодер KY-040
- Кнопка — MUTE
Схема ресивера на WM8804
Схема ресивера содержит выход ERROR, который меняет свое логические состояние при отключении и подключении коаксиального кабеля ко входу внешнего ЦАПа. Состояние выхода ERROR выводится на LCD1602 в виде сообщения «No sound».
Схема ЦАП на 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 RST 2 // PCM1796 #define CLK 9 // ENCODER #define DT 8 // ENCODER #define SW 10 // ENCODER #define MUTE 7 // BUTTON MUTE #define ERR 11 // PIN 5 WM8804 // 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,err; 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(SW,INPUT); pinMode(ERR,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() { if(digitalRead(ERR)==HIGH){err=1;}else{err=0;} /// 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&&err==1){lcd.print("No Sound");} if(mute==0&&err==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); } 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); }
Добрый день! Я правильно понимаю, что если замкнуть 10,11,12 ноги PCM на землю, а 14 через резистор 1k на +3.3В, выкинуть ардуино, то мы получаем простой конвертер spdif в аудио? Это будет самая простая схема такого конвертера? Надо ли будет произвести еще какие-то изменения, кроме вышеописанных, чтобы схема работала на 192Khz?
Да все верно, но на вход RST (14) лучше подать 3,3в через резистор 10К и подключить конденсатор 10 мкФ.