1

Тема: DAC WM8804 + AK4396 — 24bit 192kHz (Arduino) - доработка

Основная статья - http://rcl-radio.ru/?p=92642

Изменен вывод состояния фильтров на экран

http://forum.rcl-radio.ru/uploads/images/2021/02/58836bfe1f6ba8554102a69d275f1e45.png


#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>                    // 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 
 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};
      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 u,d1,d2,d3,d4,d5,d6,e1,e2,e3;


void setup(){
 Wire.begin();SPI.begin();
 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("WM8804");
 lcd.setCursor(5,1);lcd.print("AK4396");
 pinMode(CS,OUTPUT);
 pinMode(RES,OUTPUT);
 pinMode(5,INPUT_PULLUP); // кнопка MUTE
 pinMode(7,INPUT); // кнопка SW энкодера MENU
 vol = EEPROM.read(0);dem = EEPROM.read(2); roll = EEPROM.read(3);
 delay(1000);
 digitalWrite(RES,HIGH);ak();lcd.clear(); // AK4396 ON
 }// setup
 
void loop(){
/// MENU //////////////////////////////
 if(digitalRead(7)==LOW){menu++;if(menu>2){menu=0;};delay(100);lcd.clear();w=1;times=millis();w2=1;}  

/// MUTE ///////////////////
 if(digitalRead(5)==LOW&&mute==0&&menu==0){mute=1;delay(200);WriteAK4396(0b0010001100000000);WriteAK4396(0b0010010000000000);}
 if(digitalRead(5)==LOW&&mute==1&&menu==0){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;w=1;mute=0;
     if(vol>37){vol=37;}if(vol<0){vol=0;}
     ak();}
 
      lcd.setCursor(0,0);
      if(mute==1){lcd.print("MUTE   ");}else{lcd.print("VOLUME ");}
      lcd.setCursor(0,1);
      switch(roll){
        case 0: lcd.print("SH ");break;
        case 1: lcd.print("SL ");break;}
      switch(dem){
        case 1: lcd.print("    ");break;
        case 0: lcd.print("44.1");break;
        case 3: lcd.print("32.0");break;
        case 2: lcd.print("48.0");break;}
 
if(w2==1){w2=0;
     a[0]=vol/10;a[1]=vol%10;
      for(u=0;u<2;u++){
      switch(u){
        case 0: e1=9,e2=10,e3=11;break;
        case 1: e1=12,e2=13,e3=14;break;
        }
      switch(a[u]){
        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);
 }} 
 } // volume end

  /// 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;}
      ak();
      }
  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;}
      ak();
      }
  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;
  }}}  

 if(millis()-times>5000 && w==1){w=0;EEPROM.update(0,vol);EEPROM.update(2,dem);EEPROM.update(3,roll);menu=0;lcd.clear();w2=1;}
 delay(100);
  }// loop
 
 
void WriteAK4396(uint16_t Data){
  for(int p=0;p<2;p++){
  SPI.beginTransaction(SPISettings(SPI_CLOCK_DIV16, MSBFIRST, SPI_MODE0));
  digitalWrite(CS, LOW);
  delayMicroseconds(10);
  SPI.transfer16(Data);
  digitalWrite(CS, HIGH);
  SPI.endTransaction();
}} 

void ak(){
  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
  }
void to_Timer(){newPosition = myEnc.read()/4;} 

2

Re: DAC WM8804 + AK4396 — 24bit 192kHz (Arduino) - доработка

Добавление индикатора ERROR
Если коаксиальный кабель не подключен к WM8804 или на входе S/PDIF нет цифрового сигнала, то на дисплее будет высвечиваться надпись "ERROR"

http://forum.rcl-radio.ru/uploads/images/2021/02/0d301b1afb111e1c41526cd3c35fc60b.png

Изменения в схемах

http://forum.rcl-radio.ru/uploads/images/2021/02/d51e23184f97b1d7fa65475b6969de22.png

http://forum.rcl-radio.ru/uploads/images/2021/02/b0c0f76f6a3e2e36d3cf6089fdbbff32.png

#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>                    // 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 
 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};
      unsigned long times,oldPosition  = -999,newPosition,times1;
      int w,vol,mute,code_mute,in,w2,a[3],menu,dem,err,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 u,d1,d2,d3,d4,d5,d6,e1,e2,e3;


void setup(){Serial.begin(9600);
 Wire.begin();SPI.begin();
 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("WM8804");
 lcd.setCursor(5,1);lcd.print("AK4396");
 pinMode(CS,OUTPUT);
 pinMode(RES,OUTPUT);
 pinMode(5,INPUT_PULLUP); // кнопка MUTE
 pinMode(7,INPUT); // кнопка SW энкодера MENU
 pinMode(6,INPUT); // ERROR
 vol = EEPROM.read(0);dem = EEPROM.read(2); roll = EEPROM.read(3);
 delay(1000);
 digitalWrite(RES,HIGH);ak();lcd.clear(); // AK4396 ON
 }// setup
 
void loop(){
 err = digitalRead(6);  
/// MENU //////////////////////////////
 if(digitalRead(7)==LOW){menu++;if(menu>2){menu=0;};delay(100);lcd.clear();w=1;times=millis();w2=1;}  

/// MUTE ///////////////////
 if(digitalRead(5)==LOW&&mute==0&&menu==0){mute=1;delay(200);WriteAK4396(0b0010001100000000);WriteAK4396(0b0010010000000000);}
 if(digitalRead(5)==LOW&&mute==1&&menu==0){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;w=1;mute=0;
     if(vol>37){vol=37;}if(vol<0){vol=0;}
     ak();}
 
      lcd.setCursor(0,0);
      if(mute==1&&err==0){lcd.print("MUTE   ");}
      else if(mute==0&&err==0){lcd.print("VOLUME ");}
      else{lcd.print("ERROR  ");}
      
      lcd.setCursor(0,1);
      switch(roll){
        case 0: lcd.print("SH ");break;
        case 1: lcd.print("SL ");break;}
      switch(dem){
        case 1: lcd.print("    ");break;
        case 0: lcd.print("44.1");break;
        case 3: lcd.print("32.0");break;
        case 2: lcd.print("48.0");break;}
 
if(w2==1){w2=0;
     a[0]=vol/10;a[1]=vol%10;
      for(u=0;u<2;u++){
      switch(u){
        case 0: e1=9,e2=10,e3=11;break;
        case 1: e1=12,e2=13,e3=14;break;
        }
      switch(a[u]){
        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);
 }} 
 } // volume end

  /// 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;}
      ak();
      }
  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;}
      ak();
      }
  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;
  }}}  

 if(millis()-times>5000 && w==1){w=0;EEPROM.update(0,vol);EEPROM.update(2,dem);EEPROM.update(3,roll);menu=0;lcd.clear();w2=1;}
 delay(100);
  }// loop
 
 
void WriteAK4396(uint16_t Data){
  for(int p=0;p<2;p++){
  SPI.beginTransaction(SPISettings(SPI_CLOCK_DIV16, MSBFIRST, SPI_MODE0));
  digitalWrite(CS, LOW);
  delayMicroseconds(10);
  SPI.transfer16(Data);
  digitalWrite(CS, HIGH);
  SPI.endTransaction();
}} 

void ak(){
  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
  }
void to_Timer(){newPosition = myEnc.read()/4;}