DAC PCM1796 + CS8416 (Arduino)

Внешний ЦАП собран за базе ЦАП 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;
  }      

Comments

      1. 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

Добавить комментарий

Войти с помощью: