| Ваш IP: 3.237.105.210 | Online(22) - гости: 11, боты: 11 | Загрузка сервера: 2.93 ::::::::::::

DAC CS8416 + CS4398 (Arduino)(2)

Ранее в https://rcl-radio.ru/?p=83845 рассматривался вариант создания внешнего ЦАП на основе ресивера CS8416 и ЦАП на CS4398. В этой статье аналогичный проект, главное отличие его от предыдущего проекта это полностью программное управление ресивером и ЦАП. Управление осуществляется при помощи платы Arduino через шину I2C.

Основное преимущество в программном управлении в отличии от аппаратного, это расширение функциональных возможностей. Например при программном управлении кол-во входов ресивера увеличивается до 8-и, появляется возможность менять выходной формат данных (Left-Justified, Right-Justified, I2S).

Основные параметры ресивера 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

Основные характеристики CS4398:

  • отношение сигнал/шум 120 дБ
  • динамический диапазон 120 дБ
  • уровень гармонических искажений -107 дБ
  • взаимное проникновение каналов 120 дБ
  • входной формат: Left-Justified, Right-Justified, I2S
  • разрядность 24 бит
  • частота дискретизации 192 кГц

С выхода ЦАП звуковой сигнал поступает на фильтр-сумматор собранный на ОУ NE5532:

В состав внешнего ЦАП под управлением Arduino входит:

  • Ресивер CS8416
  • ЦАП CS4398
  • Плата Arduino Nano
  • LCD1602 с модулем I2C
  • Энкодер KY-040

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

Управление внешним ЦАП разделено на несколько меню, навигация по меню осуществляется при помощи кнопки энкодера, поворот ручки энкодера меняет выбранный параметр, дополнительно используется одна кнопка для функции MUTE.

  • Меню громкости (99 шагов), индикация выбранного входа, индикатор MUTE и ERROR
  • Меню выбора входа

  • Меню фильтра De-emphasis
  • Меню выбора выходного формата

 

#include <Wire.h> 
#include <EEPROM.h>
#include <Encoder.h>  // https://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip
#include <MsTimer2.h> // https://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(9, 8);//CLK, DT
      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};
      int a[3],vol,vol_d,menu,in,dem,form;
      byte i,d1,d2,d3,d4,d5,d6,e1,e2,e3;
      unsigned long times,times1,oldPosition  = -999,newPosition;
      bool w,w1,www=1,mute;
      byte dem_dac,dem_res,for_dac,for_res,err,err_old;
 
void setup(){
   Wire.begin();
   delay(100);
   //Serial.begin(9600);
   MsTimer2::set(3, to_Timer);MsTimer2::start();
   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(0,0);lcd.print("CS8416  00");lcd.print(wireRead(0x10,0x7F),BIN);
   lcd.setCursor(0,1);lcd.print("CS4398  0");lcd.print(wireRead(0x4C,0x01),BIN);
   delay(2000);
   pinMode(10,INPUT);// SW 
   pinMode(11,INPUT_PULLUP); // кнопка MUTE
  if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении 
  vol = EEPROM.read(0);in = EEPROM.read(1);dem_dac = EEPROM.read(2);dem_res = EEPROM.read(3);for_dac = EEPROM.read(4);for_res = EEPROM.read(5);
// Write 8416
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
//-------------------//
  Wire.write (0b00000000);
  Wire.write (0b10000010);// 128Fs
  Wire.write (0b00000000 + (dem_res << 4));
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000000 + (for_res));
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();
// End Write ////////////////////////////////////
delay(10);
// Write 4398 
  wireWrite(0x4C, 0x02, 0b00000010 + (dem_dac << 2) + (for_dac << 4));
  wireWrite(0x4C, 0x03, 0b00001001);// default
  wireWrite(0x4C, 0x04, 0b11000000);// default
  wireWrite(0x4C, 0x05, vol);
  wireWrite(0x4C, 0x06, vol);
  wireWrite(0x4C, 0x07, 0b10110000);// default
  wireWrite(0x4C, 0x08, 0b01000000);// default
  wireWrite(0x4C, 0x09, 0b00001000);// default
// End Write ////////////////////////////////////
  lcd.clear();
  }
 
void loop(){ 
  /// MENU //////////////////////////////
  if(digitalRead(10)==LOW){menu++;if(menu>3){menu=0;};delay(200);lcd.clear();www=1;}
 
 /// MUTE /////////////////////////////
 if(digitalRead(11)==LOW && mute==0){mute=1;wireWrite(0x4C, 0x04, 0b11011000);delay(300);www=1;} 
 if(digitalRead(11)==LOW && mute==1){mute=0;wireWrite(0x4C, 0x04, 0b11000000);delay(300);www=1;}
 
 /// VOLUME ////////////////////////////
  if(menu==0){
  if (newPosition != oldPosition){oldPosition = newPosition;www=0;
     vol=vol+newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;
      if(vol<0){vol=0;}if(vol>99){vol=99;}
      wireWrite(0x4C, 0x05, vol);
      wireWrite(0x4C, 0x06, vol);
  }
  if(www==1||w==1){www=0;w=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);
     vol_d=99-vol;a[0]=vol_d/10;a[1]=vol_d%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);
 }}
 }
 //////// INPUT ////////////////////////////////////  
  if(menu==1){
    if (newPosition != oldPosition){oldPosition = newPosition;
     in=in-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;
      if(in<0){in=0;}if(in>7){in=7;}
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
  Wire.write (0b00000000);
  Wire.write (0b10000010);
  Wire.write (0b00000000 + (dem_res << 4));
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000000 + (for_res));
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();}
  delay(10);
  if(www==1||w==1){www=0;w=0;
  lcd.setCursor(0,0);lcd.print("INPUT SELECTOR");  
  lcd.setCursor(0,1);lcd.print("COAXIAL "); lcd.print(in);lcd.print(" ");
  }
  }
 
  /// DE-EMPHASIS //////////////////////////////////
  if(menu==2){
    if (newPosition != oldPosition){oldPosition = newPosition;
     dem=dem-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;
      if(dem<0){dem=0;}if(dem>3){dem=3;}
 
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
  Wire.write (0b00000000);
  Wire.write (0b10000010);
  Wire.write (0b00000000 + (dem_res << 4));
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000000 + (for_res));
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();
  delay(10);
  wireWrite(0x4C, 0x02, 0b00000010 + (dem_dac << 2) + (for_dac << 4));
  delay(10);
  }
  if(www==1||w==1){www=0;w=0;
  lcd.setCursor(0,0);lcd.print("DE-EMPHASIS SEL");  
  lcd.setCursor(0,1);
  switch(dem){
  case 0: lcd.print("No De-emphasis");dem_dac=0;dem_res=0;break;
  case 1: lcd.print("44.1 kHz      ");dem_dac=1;dem_res=2;break;
  case 2: lcd.print("48.0 kHz      ");dem_dac=2;dem_res=3;break;
  case 3: lcd.print("32.0 kHz      ");dem_dac=3;dem_res=1;break;
  }
  }
  }
 
  /// Serial Format Select //////////////////////////////////
  if(menu==3){
    if (newPosition != oldPosition){oldPosition = newPosition;
     form=form-newPosition;myEnc.write(0);newPosition=0;times=millis();w=1;w1=1;
      if(form<0){form=0;}if(form>2){form=2;}
 
  Wire.beginTransmission(0x10);
  Wire.write (0x00);
  Wire.write (0b00000000);
  Wire.write (0b10000010);
  Wire.write (0b00000000 + (dem_res << 4));
  Wire.write (0b00000000);
  Wire.write (0b10000000 + (in << 3));
  Wire.write (0b10000000 + (for_res));
  Wire.write (0xFF);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.write (0x00);
  Wire.endTransmission();
  delay(10);
  wireWrite(0x4C, 0x02, 0b00000010 + (dem_dac << 2) + (for_dac << 4));
  delay(10);
  }
  if(www==1||w==1){www=0;w=0;
  lcd.setCursor(0,0);lcd.print("FORMAT SELECT");  
  lcd.setCursor(0,1);
  switch(form){
  case 0: lcd.print("L-Just 24 bit");for_dac=0;for_res=0;break;
  case 1: lcd.print("I2S    24 bit");for_dac=1;for_res=5;break;
  case 2: lcd.print("R-Just 24 bit");for_dac=3;for_res=8;break;
  }
  }
  }
  ////////////////////////////////////////////////////////
 
  delay(100);
 
  if(millis()-times1>1000){times1=millis();err = wireRead(0x10,0x0C);delay(10);}
  if(err!=err_old){err_old = err;www=1;}
 
  if(millis()-times>10000 && w1==1){w1=0;EEPROM.update(0,vol);EEPROM.update(1,in);
  EEPROM.update(2,dem_dac);EEPROM.update(3,dem_res);EEPROM.update(4,for_dac);EEPROM.update(5,for_res);
  menu=0;www=1;lcd.clear();}
 
  }  
 
void wireWrite(byte addr, byte reg, byte data){
  Wire.beginTransmission(addr);
  Wire.write (reg);
  Wire.write (data);
  Wire.endTransmission();
  }
 
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;}

Комментарии

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

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

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

  • Запись String в энергонезависимую память EEPROM

    Запись String в энергонезависимую память EEPROM

    EEPROM.put()  функция записывает данные любого стандартного типа или произвольную структуру в энергонезависимую память EEPROM, иначе говоря если размер данных превышает 1 байт, нужно использовать функцию EEPROM.put(). При записи данных в EEPROM размер которых превышает 1 байт, необходимо корректный расчет адресов по которым будет производится запись, для расчета адресов используется функция sizeof(). Для чтения …Подробнее...
  • Узел задержки включения громкоговорителей

    Узел задержки включения громкоговорителей

    На рисунке показана схема узла задержки включения громкоговорителей. Часто бывает, что при включении усилителя НЧ в динамиках слышен громкий щелчок в момент включения. Чтобы устранить этот дефект необходимо акустическую систему подключать к усилителю через узел задержки включения громкоговорителей. В момент включения питания усилителя акустика будет отключена при помощи реле, после …Подробнее...
  • TDA1560Q — УМЗЧ класса Н

    TDA1560Q — УМЗЧ класса Н

    TDA1560Q — усилитель мощности звуковой частоты класса Н, развивает выходную мощность 40 Вт на нагрузке 8 Ом при напряжении питания 14,4 В. Типовая схема ее включения показана на рисунке. Микросхема имеет функции управления режимами (включена, режим ожидания, режим приглушения звука, работа в режиме B, работа в режиме H). Основные характеристики микросхемы: …Подробнее...
  • Радиомикрофон

    Схема радиомикрофона показана на рисунке. Радиомикрофон работает на частоте 87,9 МГц и представляет собой автогенератор с емкостной обратной связью и дополнительной емкостью С4 в индуктивной ветви (схема Клаппа). Дополнительная емкость необходима, во-первых, для развязки по постоянному току цепей питания и смещения. Во2вторых, она обеспечивает дополнительную степень свободы для получения оптимального …Подробнее...
  • USB светодиодный фонарик

    На рисунке показана схема простого светодиодного фонарика, зарядка аккумуляторов фонарика осуществляется от USB порта. Напряжение 5В от USB  порта поступает через токоограничивающий резистор R2 на транзистор VT1, база транзистора заземлена через R1, заземление обеспечивает постоянное напряжение смещения для транзистора. Диод D1 предотвращает обратное протекание тока от батареи. Примечания. USB порт …Подробнее...