| Ваш IP: 3.239.233.139 | Online(43) - гости: 34, боты: 8 | Загрузка сервера: 0.75 ::::::::::::


DAC CS8416 + AK4396 — 24bit 192kHz (Arduino)

Внешний звуковой ЦАП собран на недорогих компонентах, в качестве ресивера используется CS8416, ЦАП на AK4396. Микросхема ресивера и ЦАПа имеют полностью микроконтроллерное управление на базе платформы Arduino.

Входной цифровой сигнал для внешнего ЦАПа имеет формат S/PDIF (цифровой аудио интерфейс разработанный фирмами SONY/PHILIPS, предназначен для передачи цифрового сигнала между аудио устройствами), ресивер на CS8416 преобразует его в формат I2S 24 бит с частотой дискретизации 192 кГц. Цифровой сигнала I2S поступает на ЦАП AK4396, далее с выхода AK4396 звуковой сигнал поступает на фильтр-сумматор собранный на ОУ NE5532.

Для нормальной работы ресивер и ЦАП должный иметь одинаковый формат передачи цифровых данных, в данном случае это 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
  • Разрядность 24 бит
  • Частота дискредитации 192 кГц
  • Фильтр De-emphasis:
    • No De-emphasis
    • 32 kHz
    • 48 kHz
    • 44.1 kHz
  • Кол-во входов: 8
  • Управление: I2C

Параметры ЦАП на AK4396:

  • Напряжение питания:
    • +5 V Analog Supply
    • +5 V Digital Supply
  • Входной формат данных: I2S (PCM)
  • Разрядность 24 бит
  • Частота дискредитации 192 кГц
  • Фильтр De-emphasis:
    • No De-emphasis
    • 32 kHz
    • 48 kHz
    • 44.1 kHz
  • Roll-off Filter
    • Sharp roll-off filter
    • Slow roll-off filter
  • Аттенюатор 75 дБ (256 ступеней — линейная зависимость)
  • Режим MUTE
  • Отношение сигнал\шум 120 дБ
  • Динамический диапазон 120 дБ
  • Уровень нелинейных искажений -100 дБ

Внешний ЦАП состоит из нескольких компонентов:

  • Ресивер на CS8416
  • ЦАП на AK4396
  • ФНЧ на NE5532
  • Источник питания
  • Плата Arduino Nano
  • Индикатор LCD1602 с модулем I2C
  • Энкодер KY-040
  • Кнопки — MUTE, INPUT

Ресивер программно сконфигурирован на работу с 4-я входами (можно увеличить до 8), управление внешним ЦАПом осуществляется при помощи энкодера, кнопка энкодера позволяет переключаться между меню «VOLUME»,  «De-emphasis SEL» и «Roll-off Filter», поворот ручки энкодера меняет настройки выбранного параметра. Дополнительно используются кнопки MUTE и IN. Вся информация выводится на индикатор LCD1602 ( с модулем I2C). Громкость имеет 37 шагов (логарифмическая зависимость).

Схема ресивера на CS8416

Схема ЦАП на AK4396

Схема ФНЧ на NE5532

Схема управления

Для питания внешнего ЦАПа рекомендуется использовать три независимых источника питания, который состоит из одного трансформатора с тремя независимыми вторичными обмотками,  одна из которых имеет отвод от середины для двух полярного источника питания. Каждый источник питания имеет свой диодный мост. При этом аналоговая и цифровая земля объединяются через дроссель. Дополнительно как можно ближе к микросхемам по цепи питания устанавливаются фильтрующие конденсаторы 0,1 и 10 мкФ.

Схема источника питания (стабилизаторы)

Упрощенная схема источника питания (стабилизаторы)

#define CS    10 // CSN  AK4396
#define MOSI  11 // CDTI AK4396
#define SCK   13 // CCLK AK4396
#define RES    2 // PDN  AK4396
 
#include <SPI.h>
#include <EEPROM.h>
#include <MsTimer2.h>                    // https://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip
#include <Encoder.h>                     // https://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(8, 9);//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,oldPosition  = -999,newPosition,times1;
int w,vol,mute,code_mute,in,w2,a[3],menu,dem,err,err_old,roll;
int db[38]={0,1,2,3,4,5,6,7,8,9,10,11,13,15,17,19,22,25,28,32,36,40,45,51,57,64,72,80,90,100,112,126,142,159,178,200,224,255};
byte i,d1,d2,d3,d4,d5,d6,e1,e2,e3;
 
 
void setup(){
 Wire.begin();Serial.begin(9600);SPI.begin();Wire.setClock(31000L);
 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);
 MsTimer2::set(1, to_Timer);MsTimer2::start();
 if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении 
 lcd.setCursor(5,0);lcd.print("CS8416");
 lcd.setCursor(5,1);lcd.print("AK4396");
 pinMode(CS,OUTPUT);
 pinMode(RES,OUTPUT);
 pinMode(7,INPUT); // кнопка SW энкодера MENU
 pinMode(6,INPUT_PULLUP); // кнопка IN
 pinMode(5,INPUT_PULLUP); // кнопка MUTE
 vol = EEPROM.read(0);in = EEPROM.read(1);dem = EEPROM.read(2); roll = EEPROM.read(3); 
 // Write 8416 I2C
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
  Wire.write (0b00000000);
  Wire.write (0b10000010);// 128Fs
  Wire.write (0b00000000);
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000101);// I2S
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();
  digitalWrite(RES,LOW);delay(100);// AK4396 OFF
  // Write AK4396 SPI 16 bit
  WriteAK4396(0b0010000000000111);  //  control 00H
  WriteAK4396(0b0010000100010010);  //  control 01H // [3:4]=10 120-216kHz
  WriteAK4396(0b0010001000000000);  //  control 02H
  WriteAK4396(0b0010001100000000 + db[vol]); // volume left 03H
  WriteAK4396(0b0010010000000000 + db[vol]); // volume right 04H
  delay(500);digitalWrite(RES,HIGH);lcd.clear(); // AK4396 ON
 }// setup
 
void loop(){
 /// MENU //////////////////////////////
  if(digitalRead(7)==LOW){menu++;if(menu>2){menu=0;};delay(200);lcd.clear();w=1;times=millis();w2=1;}
 /// INPUT /////////////////////////////
  if(digitalRead(6)==LOW){in++;if(in>3){in=0;}delay(200);w=1;times=millis();w2=1;
   // Write 8416
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
  Wire.write (0b00000000);
  Wire.write (0b10000010);// 128Fs
  Wire.write (0b00000000);
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000101);
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();
  delay(10);
  }
 /// MUTE ///////////////////
 if(digitalRead(5)==LOW&&mute==0){mute=1;delay(200);WriteAK4396(0b0010001100000000);WriteAK4396(0b0010010000000000);}
 if(digitalRead(5)==LOW&&mute==1){mute=0;delay(200);WriteAK4396(0b0010001100000000 + db[vol]);WriteAK4396(0b0010010000000000 + db[vol]);} 
 
  /// 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>37){vol=37;}if(vol<0){vol=0;}
  WriteAK4396(0b0010000000000111);  //  control 00H
  WriteAK4396(0b0010000100010010 + (dem << 1) + (roll << 5));  //  control 01H -- [4:3]=10 120-216kHz -- [2:1]=dem off/44.1/32/48 -- [5]=roll
  WriteAK4396(0b0010001000000000);  //  control 02H
  WriteAK4396(0b0010001100000000 + db[vol]); // volume left  03H
  WriteAK4396(0b0010010000000000 + db[vol]); // volume right 04H
       }
 
      lcd.setCursor(0,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>0&&mute==0){ lcd.setCursor(0,0);lcd.print("ERROR  ");}
      lcd.setCursor(0,1);lcd.print("COAX ");lcd.print(in+1);
 
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;}
      WriteAK4396(0b0010000100010010 + (dem << 1) + (roll << 5));  //  control 01H -- [4:3]=10 120-216kHz -- [2:1]=dem off/44.1/32/48 -- [5]=roll
      }
  if(w2==1){w2=0;
  lcd.setCursor(0,0);lcd.print("DE-EMPHASIS SEL");  
  lcd.setCursor(0,1);
  switch(dem){
  case 1: lcd.print("No De-emphasis");break;
  case 0: lcd.print("44.1 kHz      ");break;
  case 3: lcd.print("32.0 kHz      ");break;
  case 2: lcd.print("48.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;}
      WriteAK4396(0b0010000100010010 + (dem << 1) + (roll << 5));  //  control 01H -- [4:3]=10 120-216kHz -- [2:1]=dem off/44.1/32/48 -- [5]=roll
      }
  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;
  }}}  
 
delay(100);
 
////////// cs8416 read  
  if(millis()-times1>1000){times1=millis();err = wireRead(0x10,0x0C);delay(10);}
  if(err!=err_old){err_old = err;w2=1;}
 
 
  if(millis()-times>5000 && w==1){w=0;EEPROM.update(0,vol);EEPROM.update(1,in);EEPROM.update(2,dem);EEPROM.update(3,roll);menu=0;lcd.clear();w2=1;}
  }// loop
 
 
void WriteAK4396(uint16_t Data){
  for(int p=0;p<2;p++){
  SPI.beginTransaction(SPISettings(SPI_CLOCK_DIV16, MSBFIRST, SPI_MODE2));
  digitalWrite(CS, LOW);
  delayMicroseconds(10);
  SPI.transfer16(Data);
  digitalWrite(CS, HIGH);
  SPI.endTransaction();
}}  
 
byte wireRead(int addr, int reg){
  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;}

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

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

Случайные статьи

  • Автомобильный адаптер USB

    На рисунке показана схема универсального USB-адаптера, который питается от 12 вольтовой аккумуляторной батареи автомобиля. Выходное напряжение адаптера 5В. Адаптер может питать любое уст-во использующее для этого USB разъем с питающим напряжением 5В.  Напряжение питания на адаптер подается от гнезда прикуривателя автомобиля. Постоянное напряжение от аккумуляторной батареи автомобиля подается на стабилизатор …Подробнее...
  • Маркировка SMD компонентов

    Маркировка SMD компонентов

    Компоненты для поверхностного монтажа (SMD) очень малы и размещение на их корпусе полноценной маркировки затруднительна. Поэтому используется специальная система маркировки — на корпус SMD прибора наносится код, состоящий из двух, трех и более символов.Подробнее...
  • Простой аналоговый частотомер

    Простой аналоговый частотомер

    Простой аналоговый частотомер можно собрать по схеме, показанной на рисунке, он позволяет измерять частоту периодических сигналов напряжением 1,8… 5 В в диапазоне частот 20… 20 000 Гц. Его основой является триггер Шмитта, собранный на элементах DD1.1 и DD1.2, и ждущий мультивибратор на элементах DD1.3 и DD1.4. На вход частотомера через …Подробнее...
  • Значения ТЭДС для термопары типа R (родий / платина) ТПП

    Значения ТЭДС для термопары типа R (родий / платина) ТПП

    Термопара (термоэлектрический преобразователь) — устройство, применяемое в промышленности, научных исследованиях, медицине, в системах автоматики. Применяется в основном для измерения температуры. Принцип действия основан на эффекте Зеебека или, иначе, термоэлектрическом эффекте. Между соединёнными проводниками имеется контактная разность потенциалов; если стыки связанных в кольцо проводников находятся при одинаковой температуре, сумма таких разностей …Подробнее...
  • wi-fi модуль NodeMcu v3 с чипом ESP8266 (ESP-12e)(Умный дом)

    wi-fi модуль NodeMcu v3 с чипом ESP8266 (ESP-12e)(Умный дом)

    На базе wi-fi модуля NodeMcu v3 с чипом ESP8266 (ESP-12e) можно сделать терморегулятор с удаленным управлением (по локальной сети или Интернет), помимо терморегулятора можно управлять несколькими цифровыми выходами. Предлагаемое для повторения уст-во содержит в себе несколько компонентов: wi-fi модуль NodeMcu v3 с чипом ESP8266 (ESP-12e) 18B20 — цифровой датчик температуры, для …Подробнее...