Skocz do zawartości
ethanak

ESP8266 + MAX98357A = coś dziwnego :(

Pomocna odpowiedź

Napisano (edytowany)

Za chwilę mnie coś strzeli...

Program najprostszy chyba z możliwych (bez żadnych bibliotek):

#include <Arduino.h>
#include <i2s_reg.h>
#include <i2s.h>


int g_samrate = 8000;
uint32_t last_micro_call;
uint32_t idle_micros = 100000;

void init_audio()
{
    i2s_begin();
    i2s_set_rate(g_samrate);
    last_micro_call = micros();
}

void put_sample(int16_t sample)
{
    uint32_t usample;
    usample = sample & 0x0000ffff;
    usample |= usample << 16;
    if (micros() - last_micro_call > idle_micros) {
        last_micro_call = micros();
        yield();
    }
    // wersja 1 najprostsza - działa źle
    i2s_write_sample(usample);
    
    /* // wersja 2 - to samo
    while (i2s_is_full()) {
        delay(1);
        last_micro_call = micros();
    }*/
    
    
    /* // wersja 3 - to samo
    while (!i2s_write_sample_nb(usample)) {
        delay(1);
        last_micro_call = micros();
    }*/
}

void finalize_audio()
{
    for (int i=0; i<1000 && !i2s_is_full(); i++) i2s_write_sample_nb(0);
    i2s_end();
    
}

void setup(void)
{
}

void loop(void)
{
    int i;
    init_audio();
    for (i=0;i<4000;i++) {
        float a = i * (3.141592/10);
        int16_t s = sin(a) * 4096;
        put_sample(s);
    }
    finalize_audio();
    delay(500);
}

Efekt działania: co prawda pika sobie bardzo ładnie, ale mniej więcej połowa (całkowicie losowo) "piknięć" jest głośniejsza i zniekształcona - tak jakby wejściowa próbka przesunęła się o bit w lewo.

Miałem podobny efekt kiedyś gdy próbowałem coś zrobić z TDA1543 ale uznałem, że to pewnie wina połączeń na paszczatej płytce stykowej albo 8kHz to dla niego za mało...

Co ciekawsze - podobny program, ale używający hacku PWM działa ślicznie...

Ktoś coś... jakieś pomysły? Bo mi się skończyły 😞

Po sprawdzeniu: zamiana na bibliotekę ESP8266Audio i użycie AudioOutputI2S nic nie zmienia.

 

Edytowano przez ethanak

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Piszę z dużą niechęcią i lekką obawą, ale jeżeli @SOYER jest odporny, to ja powinienem być 2xbardziej.

Możliwe, że dla Ciebie 

1 godzinę temu, ethanak napisał:

Program najprostszy chyba z możliwych (bez żadnych bibliotek):

Cóż, ja widzę, nawet pomijając Arduino.h, jeszcze dwie inne. No i używanie przerwań [i yeld()] to dla Ciebie pewnie codzienność?

Zauważyłem też, że raz użyłeś zmiennej unsigned, a w funkcji, jako parametru używasz zmiennej signed. Funkcja sin(a) [* ] przyjmuje czasami wartość ujemną?

 

?Dziwne [naprawdę coś dziwnego 😉 ], chyba wyedytowałeś, więc nie chce mi się dalej analizować, jestem na to za stary i za wolny 😉

Edytowano przez narbej

Udostępnij ten post


Link to post
Share on other sites
Przed chwilą, narbej napisał:

Cóż, ja widzę, nawet pomijając Arduino.h, jeszcze dwie inne.

To nie są dodatkowe biblioteki tylko te, które przychodzą razem z Arduino dla ESP. Używanie yield() to konieczność. No - ale nie wszyscy muszą przecież o tym wiedzieć, prawda?

Owszem , wyedytowałem aby nie było niepotrzebnych podejrzeń o wpływ signed/unsigned.

Poza tym gdzie w tym kawałku programu widzisz przerwania?

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Tak Arduino jest standardową wbudowaną biblioteką, a esp8266 nie używałem, więc nie wiedziałem.

Czy yield() nie jest używane [do powrotu] np gdy korzystamy z przerwań? Piszesz, że z PWM, działa - a pwm działa przecież na przerwaniach? Używasz dokładniejszego pomiaru czasu - mikros(), ale bez przerwań to i tak może za mało dokładne? Dlatego sądziłem, że jednak w swoim korzystasz [z przerwań]. Więc może tu jest problem? Nie wiem też, jaki wpływ na dźwięk ma [nie]dokładność odmierzenia 0.5 sek [delay(500)]. Wyobrażam sobie, że po odtworzeniu kawałka sampla, [jest finalize_audio] i od początku znowu to samo? Tzn, niedokładność odtwarzania jest [pojawia się] w jednym takim kawałku? Czy init_audio(), nie mogłoby być w setup()?

2 godziny temu, ethanak napisał:

void put_sample(int16_t sample) { uint32_t usample; usample = sample & 0x0000ffff;

Na zewnątrz zmieniłeś na signed, ale w środku funkcji jednak rzutujesz [dokładniej odcinasz 4 młodsze bity] taką zmienną na unsigned, czy to [zawsze] dobrze?. Wprawdzie jedna zmienna jest 16, a druga 32, ale ..... ? Nie wiem.

Edytowano przez narbej

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

To nie tak. yield() to inaczej delay(0) - umożliwienie esp-kowi robienia jakichś własnych magii (typu wifi, tcp/ip i tak dalej). W tym przypadku użyte dlatego, aby watchdog nie zareagował na to że za długo siędzę w ptli loop() i nie zrobił mi resetu.

To nie tak że "działa z PWM" tylko "działa z PWM hack" - w przypadku i2s jest to użycie wyjścia danych jako jednobitowego przetwornika (delta-sigma). Osiąga się wtedy dość niską jakość dźwięku (5 bitów zamiast 16) ale nie jest potrzebny żaden układ DAC I2S, po prostu karmi się DMA spreparowanymi pseudo-samplami (w najprostszym przypadku wystarczy jeden opornik i jeden tranzystor, wypróbowana metoda, działa mi bez zarzutu w gadającym omomierzu).

init_audio teoretycznie mogłoby być w setup(), ale wtedy dźwięk byłby albo cały czas prawidłowy, albo cały czas zniekształcony (zależnie od humoru układu przy włączeniu).

500 milisekund nie ma tu żadnego znaczenia, po prostu arbitralnie dobrany czas między dwoma piknęciami (taki sam jak długość piknięcia, 4000 sampli przy 8000 sps).

Pomiar czasu za pomocą micros() też nie ma znaczenia - w programie i tak użyłem 400 msec (co jest więcej niż wystarczające aby watchdog nie nabroił).

Całość jest uproszczonym fragmentem większego programu, stąd takie a nie inne rozwiązania.

Edytowano przez ethanak

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

W czasie, gdy odpowiadałeś, ja trochę wyedytowałem swój ostatni post. Sorry, mam nadzieję, że jednak z tego powodu nic Cię nie strzeli.

2 godziny temu, ethanak napisał:

usample = sample & 0x0000ffff;

Powyższy kawałek, mógłby równie dobrze wyglądać:

usample = sample & 0xffff;

Możliwe, że się mylę, ale jeżeli sample jest signet i jest ujemne, to nie zależnie jakiej formy użyjesz .... nie chce mi się jednak dzisiaj myśleć, sorry 😉  

 

PS

"Jakiej formy" - miałem jeszcze na myśli:

usample = sample;

ale to jednak raczej zły pomysł.

Edytowano przez narbej

Udostępnij ten post


Link to post
Share on other sites

Mógłby... ale jakoś się przyzwyczaiłem do pisania wszystkich cyferek w liczbach szesnastkowych.

Poza tym tak, tak trzeba zrobić, do i2s podajesz 32-bitową paczkę (dwa 16-bitowe sample lewego i prawego kanału) którą DMA wysyła na pin. Równie dobrze można by było zrobić coś w stylu:

union {
   uint32_t usample;
   int16_t samples[2];
} usample;

usample.samples[0] = usample.samples[1] = sample;
i2s_write_sample(usample.usample);

Przy okazji - dlatego mój początkowy błąd w deklaracji sample jako unsigned nie powodował żadnych skutków 🙂

Udostępnij ten post


Link to post
Share on other sites

union{} ? A miało być prosto i przyjemnie? 😉 [dla mnie jest ok, ale przecież tutaj są też dzieci] ;-).

Ok, nie mam pojęcia, czy o to Ci chodziło? Jeżeli nie to sorry, że Ci zabrałem czas.

void setup() {
  Serial.begin(115200);
  delay(1000);
  int16_t sample = -4096;
  uint32_t usample;
  for (int i = 0; i < 25; ++i){
     usample = sample & 0x0000FFFF;
     Serial.print(sample);
     Serial.print('\t');
     Serial.println(usample);
     sample += 200; 
  }
}

void loop() {    
}
-4096	61440
-3896	61640
-3696	61840
-3496	62040
-3296	62240
-3096	62440
-2896	62640
-2696	62840
-2496	63040
-2296	63240
-2096	63440
-1896	63640
-1696	63840
-1496	64040
-1296	64240
-1096	64440
-896	64640
-696	64840
-496	65040
-296	65240
-96	65440
104	104
304	304
504	504
704	704

POwyżej wynik na moim monitorze, z mojego arduino [bez esp8266] ... Więcej pomysłu[ów] nie mam.

 

Miłego popołudnia.

Udostępnij ten post


Link to post
Share on other sites

Niestety - zupełnie nie o to... chciałem po prostu wiedzieć, dlaczego mimo podania prawidłowych danych dla DMA dźwięk jest odtwarzany czasem prawidłowo, a czasem nie. Ale dzięki przynajmniej za życzenia miłego popołudnia - nawzajem 😉

(uprzedzając delikatne sugestie kolegów: tak, analizator stanów właśnie zamówiłem, ale przyjdzie najwcześniej we wtorek).

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Wiem, że w tym fragmencie swojego kodu, samplujesz funkcję sinus. Funkcja sinus [o wartościach z przedziału -1, 1] jest funkcją gładką, więc po zsamplowaniu, też powinna taka pozostać [z dokładnością do częstotliwości próbkowania]. Chciałem tylko Ci pokazać, na przykładzie zwykłej funkcji liniowej i ograniczonego przedziału, że użycie w tym kodzie usample = sample & 0x0000FFFF;, "zrywa" gładkość samplowania:

-296 65240
-96 65440 
104 104  <--- TUTAJ

A te wartości właśnie będą z przedziału {-1*4096, +1*4096}.

Ja pewnie ztablicował bym wcześniej wartości funkcji sinus i wprowadził jeszcze parę drobnych zmian, ale tobie może wcale nie o to chodzi. Napisałeś, że to fragment większej całości i może masz zamiar samplować jakieś inne kawałki dźwięków, a na funkcji sinus tylko testujesz.  

Edytowano przez narbej
  • Nie zgadzam się! 1

Udostępnij ten post


Link to post
Share on other sites

Nic nie zrywa.

Popatrz sobie jak wygląda w 16 bitach np. -296 i 65240 i zastanów się o czym piszesz. Przepraszam, nie obrażając... temat jest o i2s i esp8266, sam przyznałeś że nie znasz esp8266, nie wiesz co to i2s... ja nie bardzo rozumiem co chcesz swoimi wypowiedziami pokazać.

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

-2 & 0x0000ffff = 65534

-1 & 0x0000ffff = 65535

0 & 0x0000ffff = 0

1 & 0x0000ffff = 1

Edytowano przez narbej

Udostępnij ten post


Link to post
Share on other sites

O co Ci chodzi? Co chciałeś przez to powiedzieć? Bo tym razem w ogóle wyskoczyłeś poza tematem...

Udostępnij ten post


Link to post
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »

×