audiohobby.ru audiohobby.ru

Документация к ES9039Q2M

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

ES9039Q2M могут использовать множество входных интерфейсов, включая TDM, I2S, LJ, RJ, DSD, DoP и S/PDIF. Они также могут быть запрограммированы с помощью нескольких методов ввода, включая I2C, SPI, а для облегчения программирования — новый режим с аппаратной поддержкой. ES9039Q2M также имеют программируемые FIR-фильтры для самых взыскательных аудиоэнтузиастов и профессионалов, выпускаются в 32-контактном корпусе QFN.

Вложение:
es9039q2m.pdf 3 Мбскачан 108 раз
+40
0
Alexei256 Alexei256 4 месяца назад #
буду ждать проект!
0
muxa muxa 4 месяца назад #

смысл? сам производитель позиционирует этот чипс для носимых устройств. но конечно для самодельщиков большой ++ это наличие HW конфигурирования с помощью ног микрухи. в остальном ни чего выдающегося.

+2
admin admin 4 месяца назад #

А вы его хоть слушали в нормальном исполнении? Пока на мой взгляд этот мобильный чип король бюджетных сборок, при минимальных вложениях получаем максимум результата.

Недавно собрал довольно простой макет аналогичный AH-D6 Pro, для оценки потенциала чипа, ничего особого не ждал. Тем более, что был опыт работы с предыдущим поколением в виде ES9028Pro, который я забросил и забыл, как страшный сон.

Но чип приятно удивил, макет выдал взрослый звук, тому же ak4493 тягаться с ES9039Q2M очень сложно.

0
zeus zeus 4 месяца назад #

У LYNXа появился очередной «свежий пирожок» на ES9038PRO. Пишет, что звучит очень хорошо. Правда совершенно не понятно, на сколько хорошо.

0
caleb caleb 4 месяца назад #

А то, что он так пишет, лучше не брать во внимание.

0
m910 m910 4 месяца назад #

Вопрос что собирать следующим решился, ждёмс..

0
caleb caleb 4 месяца назад #

Лично с ним по поводу этого пирожка разговаривал, мне он не так красочно рассказывал. Лучше не тратить на него время, не имея полного мануала и опыта работы с этими цап ничего путного не выйдет, будет пустая трата денег и времени.

Мануал у него есть, вот с каким трудом линкс его заполучил, нам не получить никогда, причем в нем есть куча ошибок, а информации в 15 раз больше чем в выложенном мануале официально. Там одних настроек больше сотни. А ASRC там не отключаемый! это он выяснил точно.

+1
caleb caleb 4 месяца назад #

Вот 9039 возможно какого-то внимания и заслуживает, надо расспросить.

0
m910 m910 4 месяца назад #

Собственно про ES9039Q2M речь и была. С inet-слухов чип достойный, что подтверждается словами Admin «ak4493 тягаться с ES9039Q2M очень сложно». И «ждемс..» относиться к проекту от Андрея, То что в нормальной обвязке потенциал у ES9039Q2M есть сомнений нет. Посмотрел что али в виде плат на этом чипе предлагает, имхо выброшенные деньги и время. Так что, надеюсь что появиться проект над которым можно будет приятно провести зимой время и в итоге получить удовольствие от звучания повторенной разработки Андрея.

0
caleb caleb 4 месяца назад #

Обвязка еще не все, вот на сколько достоверен и полон мануал на него....

0
m910 m910 4 месяца назад #

Есть подозрение что даташит «кривой» ?

0
caleb caleb 4 месяца назад #

у ess пока не было мануалов без ошибок, скорее всего сделанных специально, тот же линкс много раз их там находил, да и он сам в своих мануалах и схемах точно так же делал специально ошибки.

0
silentFREAK silentFREAK 3 месяца назад #

«так же делал специально ошибки» — Дмитрий говорит, что это «защита от пиратского копирования» :)

+5
admin admin 4 месяца назад #

У ES9039Q2M как раз вся прелесть, что есть HW режим, которого для основных задач более чем достаточно, те не надо совсем морочится с прошивкой. Аппаратно тоже неплохо поработали, пресловутого ESS IMD Hump я не наблюдаю, спектры довольно чистые 2 (небольшая) и 3 гармоники (THD при полной шкале сразу получился < 0.00008% и это на AH-F3 Superb, который снял с D19 и быстро подогнал под ES9039Q2M, никаких тонких настроек и оптимизаций), чего никак не удавалось добиться на ES9028 Pro, на котором был длинный хвост гармоник и какой-то мусор в ВЧ области, от которого мне никак не удавалось избавиться, и это точно был не фильтр (перепробовал разных более 10-ка), полагаю, что где-то с софтом по режимам пролетел, может чип такой попался (хотя стоит отдать должное цифры получались неплохие под 0.0001% так как уровень гармоник у всех был небольшой -128Дб и ниже). Ну и аппаратно, что просто добило проект на ES9028 Pro (на счет ES9038 Pro не знаю, выбрал 28-й из за меньшего выходного тока в надежде обойтись малой кровью с фильтром), то, что ess объединили все питания 1.2В аналоговое и цифровое внутри чипа, ну хоть бы заикнулись об это в даташите. Зато было забавно видеть у одного из брендовых аппаратов тоже россыпь стабов на 1.2в!!! Ладно бы я, но похоже они даже брендам не заморачиваются сообщать от таких нюансах в полной документации. Проекты D18 на ES9028 Pro и D17 на ak4497 Pro делал примерно в одно и то же время и просто в сравнении по бюджету + трудозатратам и итоговому результату все сложилось сильно не в пользу ES9028 Pro, а от ES9039Q2M я ничего не ждал, проект платы вообще слепил на коленке за пару дней, на прошивку время не тратил, запаял инженерную версию в довольно бюджетном исполнении, а результат сильно превзошел ожидания.

Ну и теперь главное о звуке ES9039Q2M, удивительно, но это первая сябра, которая меня не утомляет.

0
caleb caleb 4 месяца назад #

Похоже поняли свои косяки и перепилили чип по новому. Если не утомляет, наверное можно обратить внимание.

Есть мысль, что это старший чип с урезанным количеством каналов и немного упрощенный для более широкого использования.

+1
muxa muxa 3 месяца назад #

непохоже на старший. в ES9039Q2M оказывается есть выход для соединения в цепочку. он понимает TDM причем их там оказывается можно чуть ли не 16 каналов набирать. конечно это все через регистры, и там этих регистров более 200, при этом большое количество имеет статус reserved, вот в этом моменте похоже что digital core у них в цапах одинаковое. скорее всего тупо сэкономили на производственных затратах, но в регистры заложена просто уйма моментов для приданию звуку окраски. просто удивительно что они туда эквалайзер и еще пару улучшайзеров не встроили.

З.Ы, не совсем понятно как китайцы на своих платах делают SE выхлоп всего на двух восьминожках и это не диффампы.

P.P.S. на сайте сабры выложен более свежий даташит чем в этой теме.

0
ИгорьF ИгорьF 2 месяца назад #

Взял алишную плату на 9039q2m, даже в стоке звук очень и очень порадовал.

0
bavtec bavtec 2 месяца назад #

Не поделитесь ссылкой на плату?

0
ИгорьF ИгорьF 2 месяца назад #

https://sl.aliexpress.ru/p?key=odCe3gU

https://sl.aliexpress.ru/p?key=xFCe3Pm

Вариант с двумя цапами похоже поинтереснее реализовали.

0
muxa muxa 2 месяца назад #

скорее ближе к даташиту/классике, I-V на ора1612 и сумматор xlr-rca на ора1611. когда ОУ меньше то китайцы явно в каком то месте сэкономили.

0
rucrim rucrim 2 месяца назад #

Понятное дело — барахлом подменяют, ценой подкупают. Хочешь сделать хорошо, сделай сам.

0
rucrim rucrim 2 месяца назад #
0
Rasmus Rasmus 2 месяца назад #

Хороший малыш:

https://aliexpress.ru/item/1005008496280554.html

А в паре с Luckfox Pico Max через USB вообще звучит замечательно :)

0
Rasmus Rasmus 2 месяца назад #

Вот только ИОН для AVCC там стоит ADP150… фильтруется конечно, но можно попробовать что-то более малошумящего

0
muxa muxa 2 месяца назад #

у ADР150 шум 9мкВ, для этих целей вполне, конечно есть что то бОлее малошумящее, но это уже совсем другая история.

0
Rasmus Rasmus 2 месяца назад #

Прямая замена на LP5907. У ADP150 не очень по 1/f шумам, сильно растут ниже 100hz. A вот LP5907 держится до 10hz

Romires Romires 2 месяца назад #
Комментарий удален
+1
Romires Romires 2 месяца назад #

Коллеги, если кто освоил софтовое управление ES9039Q2M подскажите — при каких условиях на аналоговых выходах появляется 1/2 аналогового питания? Микра в целом отзывается, пишу и читаю регистры, управляю GPIO, но аналоговую секцию включить не удается, на выходах 0 вольт. На бит «DAC enable» в 0-м регистре не реагирует. Тактирование асинхронное, 49 Мег.

0
muxa muxa 2 месяца назад #

у пиндосов видел такую инициализацию для i2s


writeRegister(0x00, 0x80); // Register 0 Bit 7 to 1 Reset digital core (all settings are set to default) — on the off-chance

writeRegister(0x39, 0x01); // INPUT SELECTION [0] 1'b1: Automatically determine the input data format

writeRegister(0x2A, 0x00); // I2S IN ACTIVE

writeRegister(0x00, 0x02); // Register 0 Bit 1=1 Set DAC_MODE to enable DAC analog output

0
Romires Romires 2 месяца назад #

Благодарю, опробую такую последовательность.

0
Alexei256 Alexei256 2 месяца назад #

присоединяюсь к вопросу. поделитесь управлением кто освоил. купил пару цапов на опыты

0
Le81 Le81 18 дней назад #

Неделю мучений и чат джипити запустил es9039q2m на ардуино! Строки с пиндоса реально помогли.

0
Pushok62 Pushok62 17 дней назад #

Подéлитесь описанием конструкции и исходниками?

+3
Le81 Le81 17 дней назад #

Да конечно, скейч написан под ардуино нано, думаю без труда можно перепесать под свой контролер, сейчас работает с меню, громкость баланс, фазовый захват, полярность сигнала, авто формат, авто вход, настройка стерео и двойное моно, тоесть сам ставишь какой чип будет правый а какой левый, все фильтры, ещё дописал настройки по звуку, но не обкатал их, завтра займусь, короче заставил для эксперемента включить в меню вообще всё что умеет чип, допилю и скину. А так была плата с Китая, там блютуз и два ципа es9039q2m, Китай не стал заморачиватся с двойным моно и просто включил параллельно выходы, на плате был процессор который я так понял только включал в софт es9039q2m, но звук мне не понравился, короче выкинул проц, за одно спалил обе es9039q2m, пока игрался в хард режиме, заказал новый но он больше не запел в плате, скорее всего второго чипа не хватало, проц ждал два адреса от es9039q2m, короче пришлось играться с ардуино, звук на много лучше сейчас чем был с родным проц, сейчас в настройках появилась функция по фильтру гармоник, то есть подавляя вторую или 3 меняет восприятия.

0
Pushok62 Pushok62 17 дней назад #

Спасибо!

0
Le81 Le81 17 дней назад #

Куда скинуть?

0
Pushok62 Pushok62 17 дней назад #

Можно на vo_pushkov@mail.ru, а лучше оформить здесь на сайте статьёй, чтобы другие могли воспользоваться этой ценной информацией.

+1
zirkony zirkony 17 дней назад #

… или временно загрузить на пастебин, там можно указать срок хранение

Иначе вот такое получается, как на картинке

+5
Le81 Le81 17 дней назад #

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <EEPROM.h>

/* =========================

ПОДКЛЮЧЕНИЯ / АДРЕСА

========================= */

#define DAC_ADDR 0x49

#define LCD_ADDR 0x27

LiquidCrystal_I2C lcd(LCD_ADDR, 16, 2);

#define ENC_A 2

#define ENC_B 3

#define BTN 4

#define CHIP_EN_PIN 7 // HIGH=ON, LOW=RESET

/* =========================

РЕГИСТРЫ ES9039Q2M (DEC)

========================= */

#define REG_SYS 0x00

#define REG_IFACE_POL 0x03

#define REG_CLKCFG 0x04

#define REG_PLL_BW 0x05

#define REG_AUTO_INPUT 57

#define REG_SLOT_L 64

#define REG_SLOT_R 65

#define REG_VOL_L 74

#define REG_VOL_R 75

#define REG_FILTER 88

#define REG_PATH 90

#define REG_THD_C2_LSB 91

#define REG_THD_C3_LSB 107

#define REG_AM_ENABLE 123

#define REG_AM_TIME_L 124

#define REG_S1 0x1C

#define REG_S2 0x1D

#define REG_ID 225

/* =========================

ПРОТОТИПЫ

========================= */

bool wr(uint8_t r, uint8_t v);

bool wr16(uint8_t addrLSB, uint16_t val);

bool rd(uint8_t r, uint8_t &v);

bool pingDAC();

void chipOn();

void chipOff();

void chipReset(uint16_t ms=400);

bool lockStatus();

uint32_t fsFromS2();

void volWriteLR(uint8_t vL,uint8_t vR);

void setFilter(uint8_t f);

void writePathConfig();

void applySafe_VolBal();

void applySafe_Filter();

void applySafe_Path();

void applyAutoMute();

void applyTHD_C2();

void applyTHD_C3();

void queueApply(bool softR=false);

void runPending();

void showStatus();

void drawMenu();

void doAction(uint8_t idx);

void encISR();

void pollBtn();

void onChangeSafe(uint8_t code);

void onChangeDanger(uint8_t what);

/* =========================

СОСТОЯНИЕ / МЕНЮ

========================= */

uint8_t g_volBase=0; // 0..255 (0=0 dB)

int8_t g_balance=0; // -24..+24 (≈±12 dБ)

uint8_t g_dfilt=0; // 0..7

uint8_t g_path=0; // 0=ST,1=MonoL,2=MonoR,3=Swap

// bypass биты REG_PATH

bool g_bypIIR=false;

bool g_bypFIR4=false;

bool g_bypFIR2=false;

// интерфейс / клок

uint8_t g_pol=0; // 0..3

uint8_t g_pll=0x04; // 0x02..0x08 обычно

bool g_autoClk=true;

bool g_autoIn =true;

bool g_hardtrue; // «опасные» не писать

uint16_t g_settle=700; // мс после «опасных»

// AutoMute

bool g_amCh1=true;

bool g_amCh2=true;

bool g_amRampGND=true; // bit11

uint16_t g_amTime=0x00F; // [10:0]

// THD (signed 16-bit)

int16_t g_c2_L=0, g_c2_R=0;

int16_t g_c3_L=0, g_c3_R=0;

/* ===== Планировщик «опасных» ===== */

struct Pending { bool polarity=false, pll=false, autoclk=false, autoin=false, softReset=false; } pend;

/* ===== EEPROM / пресеты ===== */

#define CFG_VERSION 0x39 // меняй при правке структуры

struct Config {

uint8_t version;

uint8_t volBase; int8_t balance; uint8_t dfilt; uint8_t path;

bool bypIIR, bypFIR4, bypFIR2;

uint8_t pol, pll; bool autoClk, autoIn;

bool hardOnly; uint16_t settle;

bool amCh1, amCh2, amRamp; uint16_t amTime;

int16_t c2L, c2R, c3L, c3R;

uint8_t autosave;

uint16_t crc;

};

uint16_t crc16(const uint8_t* p, size_t n){

uint16_t c=0xFFFF;

for(size_t i=0;i<n;i++){ c ^= p[i]; for(uint8_t b=0;b<8;b++){ c = (c&1)?(c>>1)^0xA001:(c>>1); } }

return c;

}

#define EE_ADDR_LAST 0

#define EE_ADDR_PRE1 128

#define EE_ADDR_PRE2 256

#define EE_ADDR_PRE3 384

bool g_autosave=true;

uint8_t g_presetSel=1; // 1..3

/* ===== UI ===== */

volatile int16_t enc_delta=0;

bool btn_short=false, btn_lfalse, btn_dbl=false;

uint8_t menu_idx=0; bool edit=false;

const char* MENU[]={

// базовые

«Status»,«Volume»,«Balance»,«Filter»,«Path»,

// bypass

«Bypass IIR»,«Bypass FIR4»,«Bypass FIR2»,

// AutoMute

«AM Ch1»,«AM Ch2»,«AM RampGND»,«AM Time»,

// THD

«THD C2 L»,«THD C2 R»,«THD C3 L»,«THD C3 R»,

// интерфейс/PLL

«Polarity»,«DPLL BW»,«AutoClock»,«AutoInput»,

// пресеты

«Preset Load»,«Preset Save»,«Autosave»,

// прочее

«HardOnly»,«Reset»

};

const uint8_t MCNT = sizeof(MENU)/sizeof(MENU[0]);

/* =========================

I2C helpers

========================= */

bool wr(uint8_t r, uint8_t v){

Wire.beginTransmission(DAC_ADDR);

Wire.write®; Wire.write(v);

return (Wire.endTransmission()==0);

}

bool wr16(uint8_t addrLSB, uint16_t val){

return wr(addrLSB, (uint8_t)(val & 0xFF))

&& wr(addrLSB+1, (uint8_t)((val>>8)&0xFF));

}

bool rd(uint8_t r, uint8_t &v){

Wire.beginTransmission(DAC_ADDR); Wire.write®;

if(Wire.endTransmission(false)!=0) return false;

if(Wire.requestFrom((int)DAC_ADDR,1)!=1) return false;

v=Wire.read(); return true;

}

bool pingDAC(){ Wire.beginTransmission(DAC_ADDR); return (Wire.endTransmission()==0); }

/* =========================

ЧИП / СТАТУС

========================= */

void chipOn(){ pinMode(CHIP_EN_PIN,OUTPUT); digitalWrite(CHIP_EN_PIN,HIGH); }

void chipOff(){ digitalWrite(CHIP_EN_PIN,LOW); }

void chipReset(uint16_t ms){ chipOff(); delay(ms); chipOn(); delay(150); }

bool lockStatus(){ uint8_t s1=0; if(!rd(REG_S1,s1)) return false; return (s1&0x80)!=0; }

uint32_t fsFromS2(){

uint8_t s2=0; if(!rd(REG_S2,s2)) return 0; switch(s2&0x0F){

case 0: return 44100; case 1: return 48000; case 2: return 88200; case 3: return 96000;

case 4: return 176400; case 5: return 192000; case 6: return 384000; default: return 0; }

}

/* =========================

ЗАПИСИ В ЦАП (без mute)

========================= */

void volWriteLR(uint8_t vL,uint8_t vR){ wr(REG_VOL_L,vL); wr(REG_VOL_R,vR); }

void setFilter(uint8_t f){ wr(REG_FILTER, f & 0x07); }

void writePathConfig(){

uint8_t v=0;

if(g_bypIIR) v|=0x04;

if(g_bypFIR4) v|=0x02;

if(g_bypFIR2) v|=0x01;

wr(REG_PATH, v);

switch(g_path){

default:

case 0: wr(REG_SLOT_L,0x00); wr(REG_SLOT_R,0x01); break;

case 1: wr(REG_SLOT_L,0x00); wr(REG_SLOT_R,0x00); break;

case 2: wr(REG_SLOT_L,0x01); wr(REG_SLOT_R,0x01); break;

case 3: wr(REG_SLOT_L,0x01); wr(REG_SLOT_R,0x00); break;

}

}

void applySafe_VolBal(){

int16_t vL=g_volBase, vR=g_volBase;

if(g_balance>0) vL+=g_balance; else if(g_balance<0) vR+=(-g_balance);

if(vL<0)vL=0; if(vL>255)vL=255; if(vR<0)vR=0; if(vR>255)vR=255;

volWriteLR((uint8_t)vL,(uint8_t)vR);

}

void applySafe_Filter(){ setFilter(g_dfilt); }

void applySafe_Path(){ writePathConfig(); }

void applyAutoMute(){

uint8_t en = (g_amCh1?1:0) | (g_amCh2?2:0);

wr(REG_AM_ENABLE, en);

uint16_t t = ((g_amRampGND?1:0)<<11) | (g_amTime & 0x07FF);

wr16(REG_AM_TIME_L, t);

}

// THD: LSB->MSB, [15:0]=L, [31:16]=R

void applyTHD_C2(){

uint16_t wL=(uint16_t)g_c2_L, wR=(uint16_t)g_c2_R;

wr(REG_THD_C2_LSB+0,(uint8_t)(wL&0xFF));

wr(REG_THD_C2_LSB+1,(uint8_t)((wL>>8)&0xFF));

wr(REG_THD_C2_LSB+2,(uint8_t)(wR&0xFF));

wr(REG_THD_C2_LSB+3,(uint8_t)((wR>>8)&0xFF));

}

void applyTHD_C3(){

uint16_t wL=(uint16_t)g_c3_L, wR=(uint16_t)g_c3_R;

wr(REG_THD_C3_LSB+0,(uint8_t)(wL&0xFF));

wr(REG_THD_C3_LSB+1,(uint8_t)((wL>>8)&0xFF));

wr(REG_THD_C3_LSB+2,(uint8_t)(wR&0xFF));

wr(REG_THD_C3_LSB+3,(uint8_t)((wR>>8)&0xFF));

}

/* =========================

ОПАСНЫЕ — ПЛАНИРОВЩИК

========================= */

void queueApply(bool softR){ if(!g_hardOnly) pend.softReset |= softR; }

void runPending(){

static bool busy=false;

if(g_hardOnly){ pend=Pending(); busy=false; return; }

if(busy) return;

if(!(pend.polarity||pend.pll||pend.autoclk||pend.autoin||pend.softReset)) return;

busy=true;

if(pend.softReset){ wr(REG_SYS,0x80); delay(6); }

wr(REG_SYS,0x02); // аналог включен

if(pend.polarity) wr(REG_IFACE_POL,(g_pol&0x03));

if(pend.autoclk) wr(REG_CLKCFG, g_autoClk?0x40:0x00);

if(pend.pll) wr(REG_PLL_BW, g_pll);

if(pend.autoin) wr(REG_AUTO_INPUT,g_autoIn?0x01:0x00);

writePathConfig();

applySafe_VolBal();

setFilter(g_dfilt);

delay(g_settle);

pend=Pending();

busy=false;

}

/* =========================

EEPROM / PRESETS

========================= */

void cfgFromGlobals(Config &c){

c.version=CFG_VERSION;

c.volBase=g_volBase; c.balance=g_balance; c.dfilt=g_dfilt; c.path=g_path;

c.bypIIR=g_bypIIR; c.bypFIR4=g_bypFIR4; c.bypFIR2=g_bypFIR2;

c.pol=g_pol; c.pll=g_pll; c.autoClk=g_autoClk; c.autoIn=g_autoIn;

c.hardg_hardOnly; c.settle=g_settle;

c.amCh1=g_amCh1; c.amCh2=g_amCh2; c.amRamp=g_amRampGND; c.amTime=g_amTime;

c.c2L=g_c2_L; c.c2R=g_c2_R; c.c3L=g_c3_L; c.c3R=g_c3_R;

c.autosave=g_autosave?1:0;

c.crc=0;

c.crc=crc16((uint8_t*)&c, sizeof(Config)-2);

}

void globalsFromCfg(const Config &c){

g_volBase=c.volBase; g_balance=c.balance; g_dfilt=c.dfilt; g_path=c.path;

g_bypIIR=c.bypIIR; g_bypFIR4=c.bypFIR4; g_bypFIR2=c.bypFIR2;

g_pol=c.pol; g_pll=c.pll; g_autoClk=c.autoClk; g_autoIn=c.autoIn;

g_hardc.hardOnly; g_settle=c.settle;

g_amCh1=c.amCh1; g_amCh2=c.amCh2; g_amRampGND=c.amRamp; g_amTime=c.amTime;

g_c2_L=c.c2L; g_c2_R=c.c2R; g_c3_L=c.c3L; g_c3_R=c.c3R;

g_autosave=(c.autosave!=0);

}

bool eepromWrite(int base, const Config &c){

Config tmp=c;

for(unsigned i=0;i<sizeof(Config);++i) EEPROM.update(base+i, ((uint8_t*)&tmp)[i]);

return true;

}

bool eepromRead(int base, Config &c){

for(unsigned i=0;i<sizeof(Config);++i) ((uint8_t*)&c)[i]=EEPROM.read(base+i);

if(c.version!=CFG_VERSION) return false;

uint16_t calc=crc16((uint8_t*)&c, sizeof(Config)-2);

return (calc==c.crc);

}

void loadSlot(uint8_t slot){

int addr = (slot==1)?EE_ADDR_PRE1: (slot==2)?EE_ADDR_PRE2: EE_ADDR_PRE3;

Config c; if(eepromRead(addr,c)){ globalsFromCfg©; }

}

void saveSlot(uint8_t slot){

int addr = (slot==1)?EE_ADDR_PRE1: (slot==2)?EE_ADDR_PRE2: EE_ADDR_PRE3;

Config c; cfgFromGlobals©; eepromWrite(addr,c);

}

void autosaveWrite(){

if(!g_autosave) return;

static uint32_t last=0;

uint32_t now=millis();

if(now-last<1500) return; // экономим ресурс EEPROM

last=now;

Config c; cfgFromGlobals©; eepromWrite(EE_ADDR_LAST,c);

}

void autosaveLoadOnBoot(){

Config c; if(eepromRead(EE_ADDR_LAST,c)){ globalsFromCfg©; }

}

/* =========================

ЭКРАН / МЕНЮ

========================= */

void showStatus(){

lcd.clear();

lcd.print(lockStatus()? «Lock:Y »:«Lock:N „);

lcd.print(“P:»); lcd.print(g_pol);

lcd.setCursor(0,1);

uint32_t fs=fsFromS2();

if(fs){ lcd.print(«Fs:»); lcd.print(fs/1000); lcd.print(«k „); }

else { lcd.print(“Fs:? „); }

}

void drawMenu(){

lcd.clear();

lcd.print(“[»); lcd.print(menu_idx+1); lcd.print("/"); lcd.print(MCNT); lcd.print("] ");

lcd.print(MENU[menu_idx]);

lcd.setCursor(0,1);

switch(menu_idx){

case 0: lcd.print(«Status»); break;

case 1: lcd.print(«Vol: „); lcd.print(g_volBase); break;

case 2: lcd.print(“Bal: „); lcd.print((int)g_balance); break;

case 3: lcd.print(“Filt: „); lcd.print(g_dfilt); break;

case 4: lcd.print(“Path: „); lcd.print(g_path); break;

case 5: lcd.print(“Byp IIR: „); lcd.print(g_bypIIR ?“Y»:«N»); break;

case 6: lcd.print(«Byp FIR4: „); lcd.print(g_bypFIR4?“Y»:«N»); break;

case 7: lcd.print(«Byp FIR2: „); lcd.print(g_bypFIR2?“Y»:«N»); break;

case 8: lcd.print(«AM Ch1: „); lcd.print(g_amCh1?“ON»:«OFF»); break;

case 9: lcd.print(«AM Ch2: „); lcd.print(g_amCh2?“ON»:«OFF»); break;

case 10: lcd.print(«AM RampGND: „); lcd.print(g_amRampGND?“Y»:«N»); break;

case 11: lcd.print(«AM Time: „); lcd.print(g_amTime); break;

case 12: lcd.print(“THD C2 L: „); lcd.print(g_c2_L); break;

case 13: lcd.print(“THD C2 R: „); lcd.print(g_c2_R); break;

case 14: lcd.print(“THD C3 L: „); lcd.print(g_c3_L); break;

case 15: lcd.print(“THD C3 R: „); lcd.print(g_c3_R); break;

case 16: lcd.print(“Pol: „); lcd.print(g_pol); break;

case 17: lcd.print(“DPLL:0x»); lcd.print(g_pll,HEX); break;

case 18: lcd.print(«AutoClk: „); lcd.print(g_autoClk?“ON»:«OFF»); break;

case 19: lcd.print(«AutoIn: „); lcd.print(g_autoIn ?“ON»:«OFF»); break;

case 20: lcd.print(«Load P»); lcd.print(g_presetSel); break;

case 21: lcd.print(«Save P»); lcd.print(g_presetSel); break;

case 22: lcd.print(«Autosave: „); lcd.print(g_autosave?“ON»:«OFF»); break;

case 23: lcd.print(«HardOnly: „);lcd.print(g_hardOnly?“YES»:«NO»); break;

case 24: lcd.print(«Reset CHIP_EN»); break;

}

}

/* =========================

ВВОД / ОБРАБОТКА

========================= */

void encISR(){

static uint8_t prev=0;

uint8_t cur=(digitalRead(ENC_A)<<1)|digitalRead(ENC_B);

static const int8_t tab[4][4]={{0,-1,+1,0},{+1,0,0,-1},{-1,0,0,+1},{0,+1,-1,0}};

enc_delta+=tab[prev][cur]; prev=cur;

}

void pollBtn(){

static uint32_t t0=0,down=0; static bool last=false;

static uint32_t lastUp=0; static bool clickArmed=false;

const uint16_t DEB=20,L900, DBL=350;

bool raw=(digitalRead(BTN)==LOW); uint32_t now=millis();

if(raw!=last && (now-t0)>DEB){

t0=now; last=raw;

if(raw){ down=now; }

else{

uint32_t dur=now-down;

if(dur<600){

if(clickArmed && (now-lastUp)<DBL){ btn_dbl=true; clickArmed=false; }

else { btn_short=true; clickArmed=true; lastUp=now; }

}else if(dur>=LONG){ btn_ltrue; clickArmed=false; }

}

}

if(clickArmed && (now-lastUp)>=DBL) clickArmed=false;

}

void onChangeSafe(uint8_t code){

switch(code){

case 1: applySafe_VolBal(); autosaveWrite(); break;

case 2: applySafe_Filter(); autosaveWrite(); break;

case 3: applySafe_Path(); autosaveWrite(); break;

case 4: applyAutoMute(); autosaveWrite(); break;

case 5: applyTHD_C2(); autosaveWrite(); break;

case 6: applyTHD_C3(); autosaveWrite(); break;

}

}

void onChangeDanger(uint8_t what){

if(g_hardOnly) return;

if(what==10) pend.polarity=true;

if(what==11) pend.pll=true;

if(what==12) pend.autoclk=true;

if(what==13) pend.autoin=true;

queueApply(false);

autosaveWrite();

}

void doAction(uint8_t idx){

if(idx==0){ showStatus(); return; }

if(idx==20){ // Load preset

loadSlot(g_presetSel);

writePathConfig(); setFilter(g_dfilt); applySafe_VolBal();

applyAutoMute(); applyTHD_C2(); applyTHD_C3();

drawMenu(); return;

}

if(idx==21){ // Save preset

saveSlot(g_presetSel);

drawMenu(); return;

}

if(idx==24){

chipReset();

wr(REG_SYS,0x02);

writePathConfig(); setFilter(g_dfilt); applySafe_VolBal();

applyAutoMute(); applyTHD_C2(); applyTHD_C3();

drawMenu(); return;

}

}

/* =========================

SETUP / LOOP

========================= */

void setup(){

chipOn();

Wire.begin();

lcd.init(); lcd.backlight();

pinMode(ENC_A,INPUT_PULLUP); pinMode(ENC_B,INPUT_PULLUP); pinMode(BTN,INPUT_PULLUP);

attachInterrupt(digitalPinToInterrupt(ENC_A), encISR, CHANGE);

attachInterrupt(digitalPinToInterrupt(ENC_B), encISR, CHANGE);

lcd.print(«ES9039Q2M +EEPROM»);

lcd.setCursor(0,1);

if(pingDAC()){ uint8_t id=0; rd(REG_ID,id); lcd.print(«I2C OK ID:»); lcd.print(id,HEX); }

else lcd.print(«I2C ERR»);

// загрузить автосохранённое состояние (если валидно)

autosaveLoadOnBoot();

// аналог ON, применить конфиг в железо

wr(REG_SYS,0x02);

writePathConfig(); setFilter(g_dfilt); applySafe_VolBal();

applyAutoMute(); applyTHD_C2(); applyTHD_C3();

drawMenu();

}

void loop(){

static uint32_t tPoll=0;

static uint32_t tStat=0;

if(millis()-tPoll>5){ pollBtn(); tPoll=millis(); }

int16_t d=enc_delta; enc_delta=0;

// редактирование

if(edit && d){

switch(menu_idx){

// базовые

case 1:{ int n=g_volBase+(d>0?+1:-1); if(n<0)n=0; if(n>255)n=255; g_volBase=n; onChangeSafe(1);}break;

case 2:{ int n=g_balance+(d>0?+1:-1); if(n<-24)n=-24; if(n>24)n=24; g_balance=n; onChangeSafe(1);}break;

case 3:{ int n=g_dfilt +(d>0?+1:-1); if(n<0)n=7; if(n>7)n=0; g_dfilt=n; onChangeSafe(2);}break;

case 4:{ int n=g_path +(d>0?+1:-1); if(n<0)n=3; if(n>3)n=0; g_path =n; onChangeSafe(3);}break;

// bypass

case 5: g_bypIIR = !g_bypIIR; onChangeSafe(3); break;

case 6: g_bypFIR4 = !g_bypFIR4; onChangeSafe(3); break;

case 7: g_bypFIR2 = !g_bypFIR2; onChangeSafe(3); break;

// AutoMute

case 8: g_amCh1 = !g_amCh1; onChangeSafe(4); break;

case 9: g_amCh2 = !g_amCh2; onChangeSafe(4); break;

case 10: g_amRampGND = !g_amRampGND; onChangeSafe(4); break;

case 11:{ int n=g_amTime+(d>0?+1:-1); if(n<0)n=0; if(n>0x07FF)n=0x07FF; g_amTime=(uint16_t)n; onChangeSafe(4);}break;

// THD

case 12:{ int n=g_c2_L+(d>0?+1:-1); if(n<-32768)n=-32768; if(n>32767)n=32767; g_c2_L=(int16_t)n; onChangeSafe(5);}break;

case 13:{ int n=g_c2_R+(d>0?+1:-1); if(n<-32768)n=-32768; if(n>32767)n=32767; g_c2_R=(int16_t)n; onChangeSafe(5);}break;

case 14:{ int n=g_c3_L+(d>0?+1:-1); if(n<-32768)n=-32768; if(n>32767)n=32767; g_c3_L=(int16_t)n; onChangeSafe(6);}break;

case 15:{ int n=g_c3_R+(d>0?+1:-1); if(n<-32768)n=-32768; if(n>32767)n=32767; g_c3_R=(int16_t)n; onChangeSafe(6);}break;

// интерфейс/PLL

case 16:{ int n=g_pol+(d>0?+1:-1); if(n<0)n=3; if(n>3)n=0; g_pol=n; onChangeDanger(10);}break;

case 17:{ int n=g_pll+(d>0?+1:-1); if(n<0x01)n=0x01; if(n>0x10)n=0x10; g_pll=n; onChangeDanger(11);}break;

case 18: g_autoClk=!g_autoClk; onChangeDanger(12); break;

case 19: g_autoIn =!g_autoIn; onChangeDanger(13); break;

// пресеты

case 20:{ int n=g_presetSel+(d>0?+1:-1); if(n<1)n=1; if(n>3)n=3; g_presetSel=n; }break;

case 21:{ int n=g_presetSel+(d>0?+1:-1); if(n<1)n=1; if(n>3)n=3; g_presetSel=n; }break;

case 22: g_autosave=!g_autosave; autosaveWrite(); break;

case 23: g_hard!g_hardOnly; autosaveWrite(); break;

}

drawMenu();

}

// навигация

if(!edit && d){

int n = (int)menu_idx + (d>0?+1:-1);

if(n<0) n=MCNT-1; if(n>=MCNT) n=0;

menu_idx=(uint8_t)n; drawMenu();

}

// клики

if(btn_short){ btn_short=false; if(menu_idx==0) showStatus(); else doAction(menu_idx); }

if(btn_dbl){ btn_dbl=false; if(!g_hardOnly){ pend.softReset=false; runPending(); showStatus(); } }

if(btn_long){ btn_lfalse; edit=!edit; lcd.setCursor(15,0); lcd.print(edit?"*":" "); }

// фон

runPending();

if(menu_idx==0 && millis()-tStat>800){ showStatus(); tStat=millis(); }

}

+1
Le81 Le81 13 дней назад #

Естьполный скетч, старт — сразу Volume; кнопка — второе меню с Path, Bypass IIR/FIR2/FIR4, ASRC On/Off, THD C2, THD C3, Save/Load/Reset. THD — сразу применяется при вращении; одно значение пишется в оба канала

Если кому-то нужно!

0
muxa muxa 11 дней назад #

ASRC On/Off = емнип на 9039 ASRC не отключаемый. он работает всегда просто на отдельных сочетаниях коэфф круглые. на забугорном diyaudio (опять емнип) разбирали этот момент .

0
Le81 Le81 11 дней назад #

При отключении если клок не стабильный я слышу мелкий треск, для эксперимента проверял при вкючении все чисто

0
caleb caleb 10 дней назад #

Вообще от нестабильности клока треска не будет если все тактируется от одного генератора.

0
caleb caleb 11 дней назад #

Есть один товарищ довольно хорошо известный, оно тут зарегистрирован но заходил давно, так вот он на 9038 pro потратил несколько лет, при этом собранный цап он без сожаления продал. У 9038 pro как он говорил ASRC как раз не отключается. Если ничего не попутал и не забыл. Мануал у него есть полный, а мануал на 9039 от полного мануала составляет процентов 60. 9039 и подобные раскритиковал.

0
Le81 Le81 10 дней назад #

Ну это как с женщиной, каждому своя! То что es9039q2m хорошо справляется со звуком, а её инженеры многое проработали это факт и на практике проверил, на мой слух при отключении asrc немного сцена становится шире и это только если сигнал идёт достаточно чистый, я не про формат, а про формирование этого i2s сигнала.