Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[MSP430][CCS4] Kłopocik z PWM-em jako DAC

27 Sty 2010 09:36 8871 38
  • Poziom 39  
    Witam.

    Natrafiłem wczoraj na problem z użyciem PWMa opartego o TIMER_A3 jako DAC. W czym rzecz?
    Układzik, który aktualnie znajduje się na tapecie ma mieć możliwość wypowiadania krótkich sentencji głosowych za pomocą głośnika 50R podłączonego bez zbędnych elementów - najlepiej wprost na nogi procesora. Ponieważ już coś podobnego robiłem na AVRku wszystko szło sprawnie. Do czasu pierwszego uruchomienia dźwięku...

    Okazało się bowiem, że DAC z PWMa co prawda odtwarza dźwięk PCM z poprawną częstotliwością próbkowania to w treści dźwiękowej pojawiają się dziwne wysokoczęstotliwościowe zakłócenia. Nie jest to szum kwantyzacji tylko jakieś niezidentyfikowane szpilki. Zatem ponownie sprawdziłem próbkę dźwiękową w CoolEdit czy nie wkradły się tam jakieś przekłamania podczas konwersji do danych RAW. Nic takiego nie występuje. Postanowiłem więc na szybko zaimportować program do AVR i tam... tą samą metodą próbka odtwarzana jest poprawnie.
    Przypuszczam zatem, iż winę za całe zamieszanie ponosi brak układu buforowania rejestrów porównania TACCRx, co powoduje iż zapis do tych rejestrów w czasie trwania cyklu licznika generuje tzw. glitch-e (fałszywe wypełnienia) w przebiegu z PWM.

    Czy rzeczywiście liczniki z MSP nie posiadają możliwości sprzętowego buforowania lub synchronizacji zapisu nowej wartości zawsze na początku pojedynczego cyklu liczenia? Gdyż w dokumentacji nie ma słowa na temat tego, iż PWMy z liczników są glitch free (o czym poniekąd chwali się Atmel).

    Próbowałem już skorzystać z flagi TAIFG i poczekać z zapisem do TACCRx aż zostanie ona ustawiona, lecz efekt jest wtedy jeszcze gorszy - zniekształcenia się nasilają. Zamiast gongu jest garnek :D

    Czy ma ktoś jakieś pomysły jak wyeliminować tą przypadłość? Robienie oversamplingu jak to pokazano w nocie aplikacyjnej MSP jest tu bardziej skomplikowane gdyż różnica pomiędzy nośną a próbką jest o wiele większa niż 4 razy, co znacznie wydłuży przerwanie.

    Kodzik wygląda tak (dane próbki podcięto):

    Code:
    #include  "msp430x21x2.h"
    

    #define TEST       BIT1        // P2
    #define BUZZ       BIT3        // P2
    #define BUZZH      BIT4        // P2

    #define DONGLEN 4530
    const unsigned char dong[] = {
      2, 2, 244, 252, 24, 3, 218, 0, 52, 251, (itd...)
    };

    unsigned int  volatile buf_pointer = 0;           // wskaźnik w buforze
    unsigned int  volatile play_sample = 0;           // stan odgrywania
    unsigned char one_byte;                           // próbka do obróbki

    // ##################################################################
    void do_config(void)
    {
      DCOCTL  = CALDCO_8MHZ;                    // DCO 8MHz
      BCSCTL1 = CALBC1_8MHZ;
      WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog
    // P2 = mix
      P2DIR |=  BUZZ | BUZZH;                   // wejścia(0)-wyjścia(1)
      P2OUT  =  BUZZ;                           // stany początkowe (buzzer off)
      P2SEL  =  0x18;          // 0b00011000    // funkcja podstawowa, P2.3-P2.4 timer
    // ustawienie pracy TIMER_A0 - generator dźwięku 
      TA0CCR0  = 0x00FF;                        // pełne wychylenie licznika
      TA0CCR1  = 0x00FF;                        // rejestry porównania zero wypełnienia
      TA0CCR2  = 0x0000;
      TA0CCTL1 = CM_0 | CCIS_2 | OUTMOD_2;      // ustaw PWM - wysoki->niski
      TA0CCTL2 = CM_0 | CCIS_2 | OUTMOD_6;      //             niski->wysoki
      TA0CTL   = TASSEL_2 | ID_0 | MC_1;        // szybki PWM 31kHz
    // ustawienie pracy TIMER_A1 - generator czasu
      TA1CCR0 = 1500;                         
      TA1CTL  = TASSEL_2 | ID_1 | MC_1 | TAIE;  // próbkowanie ok. 2kHz
    }

    // ##################################################################
    int main(void)
    {
      do_config();                              // przeprowadź niezbędne konfiguracje peryferii
      __enable_interrupt();                     // przerwania włącz
      while(1)
      {
        if ((P2IN & TEST) == TEST)               // czy wejście aktywne?
          play_sample = 1;
      }
    }

    // #######################################################################
    // ISR Timer1_A1 Interrupt Vector (TAIV)
    #pragma vector=TIMER1_A1_VECTOR
    __interrupt void Timer1_A1(void)
    {
      switch(TA1IV)
      {
        case 0x0A :                             // TA1OVF przepełnienie
          if(play_sample == 1)
          {     
            one_byte = dong[buf_pointer++];     // odczytaj jeden bajt z bufora
            if(buf_pointer == DONGLEN)
            {
              buf_pointer = 0;
              play_sample = 0;
              TA0CCR1  = 0x00FF;                // zeruj przebieg z DAC
              TA0CCR2  = 0x0000;         
            }   
            one_byte = one_byte + 127;          // korekcja danych
            TA0CCR1  = one_byte;                // i do DAC
            TA0CCR2  = one_byte;
          }
          break;
      }
    }


    ps1. Próbkowanie jest 2kHz. Wydawać by się mogło, że to za mało, ale dźwięk składa się głównie z przebiegu 500Hz-600Hz - taki gong. Zatem te 2 kilo wystarcza w zupełności.
    ps2. Głośnik jest podpięty bezpośrednio do dwóch nóg procesora i sterowany jest przeciwsobnie.
  • Poziom 31  
    A czy częstotliwość PWM jest rzeczywiście równa 31kHz? Nie znam procesora, ale przy problemach z brakiem sprzętowe synchronizacji pomiędzy "update'em" PWM i chwilami przełączeń starałbym oba zdarzenia jakoś połączyć logicznie (choćby na zasadzie zapętlenia na zewnątrz i użycia przerwania-karkołomne, ale...) .
    Czy w AVR'ach też częstotliwość PWM wynosi 31KHz? Sprawdził bym też działanie tego układu ze stałym wsp. wypełnienia. Dostaniesz jeden ciągły "pisk" lub "pisk posiekany" co może świadczyć o innej przyczynie powstawania zakłóceń.
    A poza tym to raczej trzeba by spojrzeć na to na oscyloskopie.
  • Poziom 39  
    kemot55 napisał:
    A czy częstotliwość PWM jest rzeczywiście równa 31kHz? Nie znam procesora, ale przy problemach z brakiem sprzętowe synchronizacji pomiędzy "update'em" PWM i chwilami przełączeń starałbym oba zdarzenia jakoś połączyć logicznie (choćby na zasadzie zapętlenia na zewnątrz i użycia przerwania-karkołomne, ale...) .

    Częstotliwość nośnej jest dokładna. Wypuściłem ją na pin i pomiar oscyloskopowy wskazuje piękny i stabilny przebieg. Oscyloskop wykazuje również idealnie wyliczone 31kHz z groszami.
    Dodałem drugie przerwanie z tajmera generującego DAC (w momencie przepełnienia), a w nim znajduje się tylko zapis do rejestrów porównania. Dźwięk jest lepszy ale trochę zakłóceń pozostało - w głównej mierze w głośniejszej partii. Na mowie to będzie bardziej przeszkadzać niż na sygnale typu gong.

    kemot55 napisał:
    Czy w AVR'ach też częstotliwość PWM wynosi 31KHz?

    Tak. Częstotliwość jest ta sama bo i w MSP, i w AVR taktowanie to 8MHz, a PWM ustawiony tak samo: FastPWM, 8bit, bez podziału częstotliwości.

    kemot55 napisał:
    Sprawdził bym też działanie tego układu ze stałym wsp. wypełnienia. Dostaniesz jeden ciągły "pisk" lub "pisk posiekany" co może świadczyć o innej przyczynie powstawania zakłóceń.

    Stałe wypełnienie to stałe wychylenie membrany. Pisku nie uświadczysz, gdyż nie tak to działa.

    kemot55 napisał:
    A poza tym to raczej trzeba by spojrzeć na to na oscyloskopie.

    To jest przebieg PWM. Zobaczysz sieczkę.

    Tak wygląda odtwarzana próbka.
  • Poziom 31  
    Wydaje mi się, że nie będzie do końca tak jak napisałeś. Przy cyklicznym wpisywaniu tej samej wartości współczynnika wypełnienia powinna być cisza, ale tylko w poprawnie działającym układzie. Jest tak? (co do określenia "piski" może źle się wyraziłem raczej powinno tu się pojawić określenie zgrzyt lub coś takiego oczywiście przy niewłaściwej pracy układu)
    A co do oscyloskopu. Wbrew pozorom przy złapaniu fragmentu fali powinno być widać ewentualne szpilki. Poza tym sprawdź czy oscyloskop przy generowaniu dzwięku da sie synchronizować?
    Zawsze też możesz podać znany, krótki wzorzec (kilka wsp. PWM powtarzających się cyklicznie) i wtedy na oscyloskopie zobaczysz wynik.
    Osobiście kiedyś też zrobiłem coś takiego na 8052 z tym, że na wyjściu był filtr RC. Ponieważ nie wychodziło rewelacyjnie całość skończyła się na układzie ISD.
  • Poziom 39  
    kemot55 napisał:
    Wydaje mi się, że nie będzie do końca tak jak napisałeś. Przy cyklicznym wpisywaniu tej samej wartości współczynnika wypełnienia powinna być cisza, ale tylko w poprawnie działającym układzie. Jest tak?

    I niestety taka cisza występuje. Na zdjęciu (niestety jakość mocno komórkowa) widać dwa przebiegi z wyjść P2.3 i P2.4 które pełnią teraz funkcję wyjść komparatorów z licznika. Oba rejestry załadowano wartością 128. Przebieg w kanale 2 jest nieco poszarpany, ale to wina sondy, która ma problemy z masą.

    [MSP430][CCS4] Kłopocik z PWM-em jako DAC

    kemot55 napisał:
    A co do oscyloskopu. Wbrew pozorom przy złapaniu fragmentu fali powinno być widać ewentualne szpilki. Poza tym sprawdź czy oscyloskop przy generowaniu dzwięku da sie synchronizować?

    Na oscyloskopie widać, że podczas generowania dźwięku wypełnienie zmienia się. Nie ma tam nic nadzwyczajnego w formie szpilek. Niestety aby wychwycić czy PWMy chodzą synchronicznie i dokładnie tak samo ale symetrycznie, musiałbym nagrać obraz z oscyloskopu podczas odtwarzania próbki i później analizować go poklatkowo. "Na oko" wydaje mi się, że czasem przeleci jakieś fałszywe wypełnienie.

    ----

    kemot55 napisał:
    Osobiście kiedyś też zrobiłem coś takiego na 8052 z tym, że na wyjściu był filtr RC. Ponieważ nie wychodziło rewelacyjnie całość skończyła się na układzie ISD.

    Nagrałem krótką sentencję mowy próbkowaną 11kHz i puściłem z AVR-ka metodą jak wyżej. Brzmi rewelacyjnie nawet bez filtrowania. Patrz załącznik. Jedno nagranie to próbka męska 6kHz, drugie to próbka żeńska 11kHz.
  • Poziom 31  
    Zaraz zaraz. Napisałeś,że oba rejestry są załadowane i już. A co się dzieje jak te rejestry są załadowywane z określoną częśtotliwością (tak jakby szły kolejne próbki tylko zawsze z tą samą wartością współczynnika)?
  • Poziom 39  
    One są właśnie cyklicznie w przerwaniu ładowane tą samą wartością. Fakt. Miałem napisać "ładowano". Sorry.

    Tak jak pisałem wcześniej dodano drugie przerwanie z licznika pracującego jako DAC
    Code:

    // #######################################################################
    #pragma vector=TIMER0_A1_VECTOR
    __interrupt void Timer0_A1(void)
    {
      switch(TA0IV)
      {
        case 0x0A :                             // TA0OVF przepełnienie
          TA0CCR1  = one_byte;                  // rejestr danych DAC
          TA0CCR2  = one_byte;
          break;
      } // koniec switch

    a zmienna one_byte posiada teraz status volatile.
  • Poziom 31  
    Na tym oscyloskopie możesz robić sumowanie kanałów (Math). To w kwestii porównania obu portów on-line.
    Czy to co pokazałeś na zdjęciu z oscyloskopu to były przebiegi z portów z dołączonym głośnikiem?
    Ponieważ zastanawiam się jeszcze nad wydajnością (i możliwością pracy) portów. Nie znalazłem na ten temat nic w dokumentacji od MSP. Porty w AVR'ach są dość wydajne prądowo. Może pojawia się jakieś ograniczenie.
    I ostatnia "abstrakcja".Przez chwilę pomyślałem, że może problemem może być obciążenie indukcyjne dla portu tego procesora . Ale to jest mało prawdopodobne.
  • Poziom 43  
    Ja też bym obstawiał na małą wydajność pinów procesora. Szczególnie że to są procesory po przekątnej do AVR. MPS430 teoretycznie mogły by mieć bardzo małą wydajność prądową bo są projektowane do bardzo energooszczędnych aplikacji. Ale do datasheeta nie zaglądałem, więc nie wiem.
    Spróbuj dać prosty filtr RC (10k 100nF) i podłącz się do jakiegoś wzmacniacza audio. Oczywiście w takim przypadku podłącz się pod jeden kanał.

    Dodano po 7 [minuty]:

    A to to co?
    Code:

            if(buf_pointer == DONGLEN)
            {
              buf_pointer = 0;
              play_sample = 0;
              TA0CCR1  = 0x00FF;                // zeruj przebieg z DAC
              TA0CCR2  = 0x0000;         
            } 

    ?

    Ja raczej zerowanie przeprowadzał bym wpisując do obydwu rejestrów wartość 128.
    Po za tym. Ustawiasz zmienną play_sample na 0. Ale zatrzymanie odtwarzania nastąpi dopiero w kolejnym cyklu. W tym mimo wszystko wykona się jeszcze ten fragment kodu:
    Code:

            one_byte = one_byte + 127;          // korekcja danych
            TA0CCR1  = one_byte;                // i do DAC
            TA0CCR2  = one_byte;

    i zamaże Ci to co dopiero wpisałeś do tych rejestrów.
    To teraz nie ma znaczenia, ale to tak na przyszłość.
  • Poziom 39  
    kemot55 napisał:
    Czy to co pokazałeś na zdjęciu z oscyloskopu to były przebiegi z portów z dołączonym głośnikiem?

    kemot55 napisał:
    I ostatnia "abstrakcja".Przez chwilę pomyślałem, że może problemem może być obciążenie indukcyjne dla portu tego procesora . Ale to jest mało prawdopodobne.

    Nie był podpięty. Jak głośnik jest podpięty to przebieg wygląda inaczej. Zbocza narastające są podniesione w górę - indukcja dodaje. A podczas stabilnego stanu napięcie opada krzywą paraboliczną (jakbyś ją przerzucił na oś X).

    kemot55 napisał:
    Ponieważ zastanawiam się jeszcze nad wydajnością (i możliwością pracy) portów. Nie znalazłem na ten temat nic w dokumentacji od MSP. Porty w AVR'ach są dość wydajne prądowo. Może pojawia się jakieś ograniczenie.

    No niestety nie jest ona dobra, z membraną piezo - obciążenie pojemnościowe -przebieg jest tylko nieco pofałdowany. Wiadomo inaczej niż to przedstawiłem wyżej i bardzo łagodnie. Przy głośniku 50R jest to tak jak opisałem. Przy głośniku 8R (mocne przeciążenie) to z PWMa robią się tylko krótkie impulsy. Ale pomimo to efekty "zakłóceniowe" się subiektywnie nie pogarszają. Dlatego dalej obstawiam, że to jednak błąd glitch. Albo oba efekty naraz...??

    Dodano po 9 [minuty]:

    atom1477 napisał:
    Spróbuj dać prosty filtr RC (10k 100nF) i podłącz się do jakiegoś wzmacniacza audio. Oczywiście w takim przypadku podłącz się pod jeden kanał.

    Prościej będzie nagrać przez line-in w laptopie. Jakoś w pracy nie mamy na półce żadnego audio. Nie ta branża.

    atom1477 napisał:
    A to to co?

    A to jest właśnie ustawienie przy, którym oba PWMy generują taki sam stan. Zauważ jaka jest konfiguracja: jeden jest inwersyjny.

    atom1477 napisał:
    Po za tym. Ustawiasz zmienną play_sample na 0. Ale zatrzymanie odtwarzania nastąpi dopiero w kolejnym cyklu.

    Wiem, ale to co tu jest to dopiero 1/10 programu jaki muszę stworzyć. Zatem gdy ta głosowa funkcja będzie już działać poprawnie to dopiero będę ją próbował "dopieścić".
  • Poziom 31  
    Cytat:
    Jak głośnik jest podpięty to przebieg wygląda inaczej. Zbocza narastające są podniesione w górę - indukcja dodaje. A podczas stabilnego stanu napięcie opada krzywą paraboliczną (jakbyś ją przerzucił na oś X).

    A czy przebiegi przy zboczu narastającym i opadającym są symetryczne?
    Możesz odczytać coś z TDS'a? Np. ściągnąć możliwie najdłuższą próbkę dźwięku z zakłóceniem i podesłać mi? (dwa przebiegi, podłączenie w tych samych punktach: jeden z podpiętym głośnikiem, drugi bez, zapisane w CSV albo w innym formacie, ale tekstowo. Przy czym nie istotne jest czy będą to dokładnie te same fragmenty dźwięku).
    Myślę o zrobieniu na szybko FFT. Jeżeli w obu przypadkach widmo będzie podobne to jednak trzeba będzie szukać bardziej w oprogramowaniu (a właściwie nie tyle w programie co w konfiguracji procesora).
    Tak a'propos słonia - mam wrażenie, że korzystasz z IAR'a. A masz włączoną w kompilatorze optymalizację kodu? Dla pewności wyłączyłbym to w czasie prób.
  • Poziom 43  
    ZbeeGin napisał:
    atom1477 napisał:
    A to to co?

    A to jest właśnie ustawienie przy, którym oba PWMy generują taki sam stan. Zauważ jaka jest konfiguracja: jeden jest inwersyjny.

    Tak, ale to powoduje skok z wartości średniej do GND albo do VCC co da trzask. Lepiej by było zamrozić próbki na 1/2 VCC czyli na wartości 128.
  • Poziom 39  
    kemot55 napisał:
    A czy przebiegi przy zboczu narastającym i opadającym są symetryczne?

    Kiedy dokładnie?

    kemot55 napisał:
    Możesz odczytać coś z TDS'a? Np. ściągnąć możliwie najdłuższą próbkę dźwięku z zakłóceniem i podesłać mi?

    Niestety nie mamy modułu rejestratora do niego. :cry:

    kemot55 napisał:
    Tak a'propos słonia - mam wrażenie, że korzystasz z IAR'a. A masz włączoną w kompilatorze optymalizację kodu? Dla pewności wyłączyłbym to w czasie prób.

    Nie korzystam z IAR-a. Nie przypada mi do gustu to środowisko. Używam Code Composera v4 w najnowszej edycji i FET-a z USB który też zasila układ podczas prób.

    Dodano po 6 [minuty]:

    atom1477 napisał:
    Tak, ale to powoduje skok z wartości średniej do GND albo do VCC co da trzask. Lepiej by było zamrozić próbki na 1/2 VCC czyli na wartości 128.

    On będzie nieistotny gdyż będzie tylko jeden na początku i na końcu odtworzenia próbki. Mnie chodzi o dziwne trzaski w trakcie trwania odtwarzania.

    Ponadto jak ustawisz wartość 128 w obu kanałach to dostaniesz taki przebieg jak na zdjęciu. Zatem głośnik niepotrzebnie będzie próbował odtwarzać nośną 31250Hz!
  • Poziom 43  
    ZbeeGin napisał:
    Mnie chodzi o dziwne trzaski w trakcie trwania odtwarzania.

    Wiem i dlatego napisałem że to przyda się później.

    ZbeeGin napisał:
    Ponadto jak ustawisz wartość 128 w obu kanałach to dostaniesz taki przebieg jak na zdjęciu. Zatem głośnik niepotrzebnie będzie próbował odtwarzać nośną 31250Hz!

    Chm, no właśnie. Chyba masz zły przebieg. Bo w takim układzie nie da się uzyskać normalnego poziomu „zero”. Ja rozumiem że podczas zatrzymania odtwarzania brak poziomu „zero” Ci nie przeszkadza, ale podczas normalnego odtwarzania takie „zero” też musi czasami występować. Po prostu sygnał czasami powinien przechodzić przez zero i tyle.
    Chyba trzeba to zrobić inaczej. Bo ustawienie obydwu kanałów na noninvet i programowe odwracanie wypełnienia da inny efekt niż ustawienie jednego kanału na noninvert a drugiego na invert.
    Wiesz o co mi chodzi?
    No chyba że na AVR takie coś działało zupełnie identycznie i było dobrze. Ale mimo to to co masz teraz to jest zły sposób sterowania. A przynajmniej tak mi się wydaje.
  • Poziom 39  
    atom1477 napisał:
    Chm, no właśnie. Chyba masz zły przebieg. Bo w takim układzie nie da się uzyskać normalnego poziomu „zero”. Ja rozumiem że podczas zatrzymania odtwarzania brak poziomu „zero” Ci nie przeszkadza, ale podczas normalnego odtwarzania takie „zero” też musi czasami występować. Po prostu sygnał czasami powinien przechodzić przez zero i tyle.

    I przez to zero przechodzi! Jak byś zobaczył przebieg na oscyloskopie to wypełnienie oscyluje wokół 50% podczas odtwarzania próbki.

    atom1477 napisał:
    Chyba trzeba to zrobić inaczej. Bo ustawienie obydwu kanałów na noninvet i programowe odwracanie wypełnienia da inny efekt niż ustawienie jednego kanału na noninvert a drugiego na invert.

    Przeczytaj komentarze w programie, zobacz screen z TDSa i treść Moich wypowiedzi. Jeszcze raz powtórzę: Oba kanały PWM chodzą przeciwsobnie. Sprzętowo!
  • Poziom 43  
    ZbeeGin napisał:
    atom1477 napisał:
    Chm, no właśnie. Chyba masz zły przebieg. Bo w takim układzie nie da się uzyskać normalnego poziomu „zero”. Ja rozumiem że podczas zatrzymania odtwarzania brak poziomu „zero” Ci nie przeszkadza, ale podczas normalnego odtwarzania takie „zero” też musi czasami występować. Po prostu sygnał czasami powinien przechodzić przez zero i tyle.

    I przez to zero przechodzi! Jak byś zobaczył przebieg na oscyloskopie to wypełnienie oscyluje wokół 50% podczas odtwarzania próbki.

    atom1477 napisał:
    Chyba trzeba to zrobić inaczej. Bo ustawienie obydwu kanałów na noninvet i programowe odwracanie wypełnienia da inny efekt niż ustawienie jednego kanału na noninvert a drugiego na invert.

    Przeczytaj komentarze w programie, zobacz screen z TDSa i treść Moich wypowiedzi. Jeszcze raz powtórzę: Oba kanały PWM chodzą przeciwsobnie.


    Wszystko dokładnie przeczytałem.
    ZbeeGin napisał:
    Oba kanały PWM chodzą przeciwsobnie.

    I własnie w tym rzecz.

    Dodano po 54 [minuty]:

    Zobacz jak to wygląda:
    [MSP430][CCS4] Kłopocik z PWM-em jako DAC

    Pierwszy oscylogram na górze.
    Oba przebiegi (1 i 2 po lewej) w fazie. Wypełnienie 50% (czyli wartości 128). Napięcie różnicowe (przebieg 3 po lewej) równe 0.
    I tutaj wyjaśnia się dlaczego ja w celu wyciszenia podawał bym wartość 128 do obydwu kanałów. Bo mimo generowania nośnych, na głośniku napięcie różnicowe będzie równe 0.
    Oscylogram na dole. Wypełnienie na jednym kanale wzrosło a na drugim zmalało. Napięcie różnicowe teraz zawiera impulsy dodatnie. Będzie lekkie wychylenie membrany.
    Przy skrajnych wypełnieniach (0 i 100%) na pinach pojawi się po prostu VCC i GND i membrana wychyli się na maxa w którąś stronę. Jest Git.

    A teraz jak jest u Ciebie.
    Oscylogram po prawej stronie na górze. Oba wypełnienia 50%. Ale w przeciwfazie. Wynik? Ogromny sygnał różnicowy na głośniku. Po pierwsze duży pobór prądu bo cewka nawet jak ograniczy prąd to całkiem go nie wyeliminuje. Po drugie nie wiadomo co jeszcze. Pewnie nic ale jak piny miały by niesymetryczne charakterystyki to membrana nie była by na środku tylko była by trochę wychylona w którąś stronę.
    Oscylogram na dole. Próbka wzrosła. No i tutaj mam problem bo nie wiem co się stanie z wypełnieniem na tym odwróconym kanale. Załóżmy że wypełnienie też wzrośnie. No bo w końcu ustawiłeś tylko odwracanie przebiegu a nie odwracanie wypełnienia.
    Efekt? Ząbkowanie przebiegu. Co ciekawe nawet składowa stała się nie zmieniła. Więc dźwięku nie powinno być. A jak jest to z powodu niesymetrycznej charakterystyki pinów.
    Chyba że i odwracanie wypełnienia też jest realizowane. W taki przypadku składowa stała zmieni się. Więc dźwięk będzie. Ale ząbkowanie i ogromny niepotrzebny przebieg 30kHz też będzie.

    Więc moja rada taka żeby przebiegi nie były odwrócone w fazie. Po prostu jednemu podawaj normalne wypełnienie a drugiemu odwrócone ((PWM) i (255-PWM))


    PS. Przebiegi 3 narysowałem dla niekorygowanego PWMa. Dla PWM Phase Correct (przebiegi 4) będzie podobnie, tyle że impulsów będzie dwa razy więcej i ich wypełnienie będzie dwa razy mniejsze (W przypadku mojego przebiegu).
    U Ciebie podobnie. Ząbki będą już z dwóch stron przebiegu i będą miały dwukrotnie mniejszą szerokość. Ale jako takie i tak będą i ten niepotrzebny a wręcz szkodliwy przebieg 30kHz też.

    To co teraz stworzyłeś to jest klasyczny PWM (mówię o przebiegu różnicowym) który celu zasilenia głośnika WYMAGA odfiltrowania go filtrem LC.
    A to moje to jest tak zwany PWM jednokierunkowy albo jakoś tak. To już nie wymaga filtru LC. Prądy są jednokierunkowe a ich kierunek zależy od tego czy wypełnienie jest mniejsze czy większe od 50%. Jakaś firma chwali się takim PWMem. Ale prawda jest taka że Ameryki nie odkryli bo takie coś jest stosowane już od kilkudziesięciu lat ;)
    Więc nawet jeżeli nie to jest powodem kiepskiej jakości dźwięku u Ciebie, to powinieneś albo dodać filtr, albo przejść na PWM jednokierunkowy bo to co masz teraz kłóci się ze sztuką.
  • Poziom 39  
    atom1477 napisał:
    Dodano po 54 [minuty]:

    Wszystko co napisałeś po 54 minucie myślę, że dałoby się obalić jednym zdaniem (Uwaga! Długie i zawiłe):
    Procesor AVR ATmega32L taktowany 8MHz, gdzie przerwanie z Timer0 odpowiada za podawanie kolejnych próbek do OCR1A i OCR1B w równych odstępach czasu; gdzie Timer1 jest podwójnym synchronicznym generatorem PWM o f=31250Hz, w którym to jeden kanał jest normalny, a drugi inwersyjny; odtwarza próbki przygotowane tą samą metodą i za pomocą dwóch portów OC1A i OC1B prawidłowo, a dowód na to znajduje się w nagraniach z mikrofonu przyłożonego do głośnika, które to nagrania jako archiwum ZIP możesz pobrać i odtworzyć za pomocą systemowego odtwarzacza audio.
    Jedno zasadnicze pytanie jest takie: Dlaczego ten sposób nie działa tak samo w MSP430?

    Odpowiedź Moja: Nie wiem dokładnie, szukam 100% odpowiedzi.
    Odpowiedź Twoja: [......................................] (wpisać maszynowo lub ręcznie)

    Moje przypuszczenie: Brak opcji glitch free w MSP430, wypełnienie się rozjeżdża.
    Twoje przypuszczenie: Masz nośne w przeciwfazach.

    ps. Sorry za tą ironię, ale właśnie obejrzałem House-a. Mogę go z premedytacją naśladować.

    ----

    Ok. Żeby nie było. Program dla AVR dla próbki męskiej 6kHz:
    Code:
    #include <avr/io.h>
    
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h>
    #include <util/delay.h>

    uint8_t  volatile play    = 0;
    uint16_t volatile pointer = 0;
    uint16_t volatile counter = 0;

    #include "isospeak.h"

    // dane początków i rozmiarów próbek w isospeak.h
    const uint16_t wavetab[][2] = {
        {     0,  6060 },
        {  2125,  7105 },
        {  9369,  5469 },    // słaba bateria
        { 12295,  5538 },
       { 18634,  3045 }     // spadaj
    };

    //==================================================
    ISR(INT0_vect)
    {
      if(bit_is_set(PIND, 2))
      {
        cli();
       pointer = wavetab[0][0];
       counter = wavetab[0][1];
       play = 1;
       sei();
      }
      else
      {
        cli();
       pointer = wavetab[1][0];
       counter = wavetab[1][1];
       play = 1;
       sei();
      }
    }

    //==================================================
    ISR(INT2_vect)
    {
       cli();
       pointer = wavetab[2][0];
       counter = wavetab[2][1];
       play = 1;
       sei();
    }
       
    //==================================================
    ISR(INT1_vect)
    {
        cli();
       pointer = wavetab[4][0];
       counter = wavetab[4][1];
       play = 1;
       sei();
    }

    //==================================================
    ISR(TIMER0_COMP_vect)
    {
       uint8_t temp;                                      // zmienna tymczasowa
       
       if(play)                                           // jak play = 1
       {
          if(counter--)                                   // i counter nie wyzerowany
         {
           temp = pgm_read_byte(&wave[pointer++]);      // odczytaj bajt z próbki pod adresem pointer
           temp = temp + 127;                           // korekcja próbki
           OCR1A = temp;                                // niech DAC przetworzy
           OCR1B = temp;
          }
         else
         {
            OCR1A = 255;                                    // DAC wygaś
          OCR1B = 0;
          play = 0;                                     // i koniec gadania
         }
       }
    }

    //----------------------------------------------------
    void config(void)
    {
       // konfiguruj porty
       //
       DDRD  = (1<<PD5)|(1<<PD4);
               // OC1A/OC1B outputs
       PORTD = (1<<PD2)|(1<<PD3);
               // INT0/1 inputs high
       PORTB = (1<<PB2);
               // INT2 input high
     
       // konfiguracja PWMa (Timer1) nośna 30kHz
       //
       OCR1A = 255;
       OCR1B = 0;
       TCCR1A = (1<<WGM10)|(1<<COM1A1)|(0<<COM1A0)|(1<<COM1B1)|(1<<COM1B0);
       TCCR1B = (0<<WGM12)|(1<<CS10);
                // mode 1 fastpwm 8bit | oc1a clear | oc1b set | prescaler 1
       
       // konfiguracja licznika
       // generuj przerwania o częstotliwości 6kHz (160us)
       //
       OCR0 = 20;
       TCCR0 = (1<<WGM01)|(1<<CS01)|(1<<CS00);
       TIMSK = (1<<OCIE0);
               // mode 2 ctc | prescaler 64 | 20*8us | int enable
       
       // konfiguracja przerwań
       //
       MCUCR = (1<<ISC10)|(1<<ISC00);
               // int any change
       GICR  = (1<<INT1)|(1<<INT0)|(1<<INT2);
               // INT0/INT1/INT2 enable
    }

    //----------------------------------------------------
    int main(void)
    {
       config();
       sei();
       
       while(1);
     
       return 0;
    }
  • Poziom 43  
    No właśnie myślałem że na AVR zrobiłeś to inaczej. Wiem, pisałeś że tak samo, ale nie podałeś kodu.
    No trudno, bo gdyby to była prawda to miał byś rozwiązany problem, a tak nie masz.
  • Poziom 39  
    atom1477 napisał:
    No właśnie myślałem że na AVR zrobiłeś to inaczej. Wiem, pisałeś że tak samo, ale nie podałeś kodu.
    No trudno, bo gdyby to była prawda to miał byś rozwiązany problem, a tak nie masz.

    Kod dla AVR nie jest istotą problemu, dlatego jego podanie nie miało sensu. Teraz postawiłeś "zarzut", zatem Ja wystawiłem "świadka koronnego". :P

    A ha! (C) Copyright by Zbigniew "ZbeeGin" G. 2010
  • Pomocny post
    Poziom 43  
    Nie czaję ;)

    Dodano po 10 [minuty]:

    A próbowałeś wszystko wrzucić do przerwania od Timera0?
    Code:

    #pragma vector=TIMER0_A1_VECTOR
    __interrupt void Timer0_A1(void)
    {
      switch(TA0IV)
      {
        case 0x0A :                             // TA0OVF przepełnienie
          Timer0_state++;
          if (Timer0_state >= 12)
              {
              Timer0_state = 0;

              if(play_sample == 1)
                  {     
                  one_byte = dong[buf_pointer++];     // odczytaj jeden bajt z bufora
                  if(buf_pointer == DONGLEN)
                      {
                      buf_pointer = 0;
                      play_sample = 0;
                      TA0CCR1  = 0x00FF;                // zeruj przebieg z DAC
                      TA0CCR2  = 0x0000;         
                      }   
                  one_byte = one_byte + 127;          // korekcja danych
                  TA0CCR1  = one_byte;                // i do DAC
                  TA0CCR2  = one_byte;
                  }
              }
          break;
      }
    }


    Teraz obstawiam że może okres przerwania Timera1 nie jest dokładną wielokrotnością okresu Timera0. Wrzucenie tego do jednego przerwania pozwoli to sprawdzić.
    Napisałeś że przebieg jest: „piękny i stabilny”, ale piękny i stabilny nie oznacz zsynchronizowany. Z tego co napisałeś wynika że sprawdziłeś tylko czy przebieg jest ładny i jaka ma częstotliwość. A czy jest dokładnie zsynchronizowany z częstotliwością aktualizowania próbek to już nie.
    Nie wiem jak Tobie, ale mi w 99% zdarza się pomyłka o 1 przy wyznaczaniu okresu Timera i zawsze muszę to później sprawdzać ;) Pewnie Ty policzyłeś dobrze, ale nie zaszkodzi sprawdzić.
    Dodatkowo spróbuj zmniejszyć głośność dźwięku. Powiedzmy do 50%. Czyli próbki mają mieć wartości od 64 do 192. Dzięki temu ewentualny glitch nie wystąpi nawet jak by chciał wystąpić, bo wartość PWMa będzie zawsze aktualizowana przy stabilnym stanie na pinie (zanim Timer zdąży doliczyć do 64). Jaką próbkę byś nie wpisywał do TA0CCRx, to będzie ona zawsze większa od 64 a więc stan na pinie nie zmieni się. Zmieni się dopiero później, ale już w prawidłowym momencie bez wytwarzania impulsu glitch.
  • Poziom 39  
    atom1477 napisał:
    A próbowałeś wszystko wrzucić do przerwania od Timera0?

    Tak. Była taka próba po podpowiedziach osoby, która z MSP430 jest dobrze obeznana.
    Niestety efekt jest ten sam. Próbkę wzorcową już przedstawiłem na forum, teraz przedstawię nagranie z głośnika podłączonego do MSP430 dla aktualnego programu z jednym przerwaniem.

    http://zbeegin.republika.pl/mspplay.wav

    Co więcej. Znalazłem też film, w którym odtwarzanie próbki z karty SD przez MSP430 też jest obarczone "śmieciami" z PWMa. Jest ich mniej, gdyż to tylko jeden kanał. Tam problem może być jednak rozwiązany przez filtrację, gdyż wyjście jest podłączone do wzmacniacza. Pokazuje on jednak, że coś nie pozwala cieszyć się dobrą jakością dźwięku jaką można uzyskać z AVRa. :(

    http://www.youtube.com/watch?v=uf82rc57YCA (Program 3)

    atom1477 napisał:
    Nie wiem jak Tobie, ale mi w 99% zdarza się pomyłka o 1 przy wyznaczaniu okresu Timera i zawsze muszę to później sprawdzać ;) Pewnie Ty policzyłeś dobrze, ale nie zaszkodzi sprawdzić.

    Zmiana częstości przerwań podsyłania próbek wpływa tylko na prędkość ich odtwarzania. Zaśmiecanie jest ciągle w takim samym stopniu.

    atom1477 napisał:
    Dodatkowo spróbuj zmniejszyć głośność dźwięku. Powiedzmy do 50%. Czyli próbki mają mieć wartości od 64 do 192. Dzięki temu ewentualny glitch nie wystąpi nawet jak by chciał wystąpić, bo wartość PWMa będzie zawsze aktualizowana przy stabilnym stanie na pinie (zanim Timer zdąży doliczyć do 64). Jaką próbkę byś nie wpisywał do TA0CCRx, to będzie ona zawsze większa od 64 a więc stan na pinie nie zmieni się. Zmieni się dopiero później, ale już w prawidłowym momencie bez wytwarzania impulsu glitch.

    Niestety ma to działać głośno tak jak tylko potrafi. :cry: I taka opcja nie przejdzie.
  • Poziom 43  
    ZbeeGin napisał:

    Niestety ma to działać głośno tak jak tylko potrafi. I taka opcja nie przejdzie.

    No ale dla samego testu można by to zrobić.
    Można by ale widzę nie że nie trzeba bo zakłócenia są nawet dla cichych dźwięków.
    Szkoda że w pliku jest ogromny przydźwięk sieci bo to utrudnia analizę.
    Ale przefiltruję to i zobaczę.
  • Pomocny post
    Poziom 31  
    Rzeczywiście sieć zniekształca znacznie przebieg. Prążki wyższych harmonicznych znacząco występują dla 734Hz (maksymalnie) i 2106Hz (37% 50Hz, 100% 734Hz, 95% 2106Hz, 24% 2800Hz, 37% 3573Hz oraz 41% 4946Hz (przy czym udział procentowy jest odniesiony do harmonicznej 734Hz)). Ma się to nijak do częstotliwości nośnej PWM'a. Można to ewentualnie nałożyć na źródło.
    W przebiegu WAV widać wyraźnie oscylacje po przełączeniu i pewnie z tego wynika cały kłopot. Możesz ta samą próbkę zarejestrować dla AVR'a?
    A to co teraz usłyszałem jest podobne z tym co sam robiłem. Teraz rozumiesz dlaczego mnie to nie zachwyciło. Ja próbowałem się z takim generatorem jakieś 6 lat temu na 89S8252 (zauważ, że wydajność portu tego procesora też jest słaba dla wysokiego poziomu logicznego)
  • Poziom 43  
    A to 734Hz to czasem nie jest główny przebieg?
  • Poziom 15  
    Przydatna by była informacja którego dokładnie procesora kolega używa, stawiam na MSP430F2131 lub MSP430F2132.
    Code:

      TA0CCTL1 = CM_0 | CCIS_2 | OUTMOD_2;      // ustaw PWM - wysoki->niski
      TA0CCTL2 = CM_0 | CCIS_2 | OUTMOD_6;      //             niski->wysoki

    Użyłbym raczej odpowiednio:
    OUTMOD_3 i OUTMOD_7 tj. set/reset i reset/set
    (zamiast OUTMOD_2,OUTMOD_6 toggle/reset toggle/set.)

    Przydatna też bywa errata dla tych układów: http://focus.ti.com/lit/er/slaz041c/slaz041c.pdf moduły timerów mają kilka problemów.
  • Poziom 39  
    tek-no-logical napisał:
    Przydatna by była informacja którego dokładnie procesora kolega używa, stawiam na MSP430F2131 lub MSP430F2132.

    MCU to MSP430F2132.
    Tak jak się spodziewałem zmiana trybu wyjść nie miała znaczenia, z przebiegów z noty katalogowej wynika, że akurat w tym trybie są one tożsame.


    Próbkę z AVR zarejestruje dziś wieczorem po powrocie do domu.

    p.s. Wczoraj podłączyłem bezpośrednio pod AVR-a GDN20/40 8Ohm w obudowie zamkniętej i było dobrze, a zarazem głośno. Na szczęście sąsiedzi byli wyrozumiali. :D

    --edit later--

    A oto i nagrana próbka z AVR. Ten sam mikrofon, ten sam głośnik, tylko procesor i eval board inny.

    http://zbeegin.republika.pl/avrplay.wav
  • Poziom 15  
    Sugestia odnośnie uproszczenia sobie życia - zamiast dość skomplikowanego przebiegu jakim jest gong - użyć sinusoidy lub piły o stałej znanej częstotliwości.

    Jeszcze ciekawi mnie dlaczego :
    Code:
    unsigned int  volatile play_sample = 0;           // stan odgrywania 


    a już
    Code:
    unsigned char one_byte;                           // próbka do obróbki 


    Rejestry CCRx są 16-sto bitowe - wypadałoby użyć:
    Code:
    unsigned int one_byte;                           // próbka do obróbki 


    Wspomniane buforowanie z AVR w rodzinie MSP430 też występuje - można wczytywać nowe wartości synchronicznie z przepełnieniem licznika, wybrać długość licznika itp., jednak to wszystko w modułach TimerB, niedostępnych w użytym układzie. Mam jakąś płytkę z MSP430F2274, spróbuję w wolnej chwili przetestować podany program.
    Może dałoby się w ramach testu uaktualniać rejestry PWM po każdym cyklu? ( nawet, jeżeli wartość się nie zmienia )
  • Poziom 39  
    tek-no-logical napisał:
    Sugestia odnośnie uproszczenia sobie życia - zamiast dość skomplikowanego przebiegu jakim jest gong - użyć sinusoidy lub piły o stałej znanej częstotliwości.

    Taki wymóg. Nic nie poradzę.

    tek-no-logical napisał:
    Jeszcze ciekawi mnie dlaczego :
    Code:
    unsigned int  volatile play_sample = 0;           // stan odgrywania 

    Chodzi ci o typ zmiennej czy to, że ma status ulotnej zmienianej też w przerwaniu?

    tek-no-logical napisał:
    a już
    Code:
    unsigned char one_byte;                           // próbka do obróbki 


    Rejestry CCRx są 16-sto bitowe - wypadałoby użyć:
    Code:
    unsigned int one_byte;                           // próbka do obróbki 

    Próbki dźwiękowe są 8-bitowe. Poza tym jak zauważysz licznik jest skracany do 8 bitów.

    tek-no-logical napisał:
    Może dałoby się w ramach testu uaktualniać rejestry PWM po każdym cyklu? (nawet, jeżeli wartość się nie zmienia)

    Też to przerabiałem. Poprawy żadnej, a nawet gorzej.
  • Poziom 31  
    Wynik dla dźwięku z AVR'a. (50Hz) 3%!, (549Hz) 100%, (1575, 2673, 3700Hz) 20% oraz (4800Hz) <10%
    A zasilanie sprawdzałeś (oscyloskopem)? Jakoś ta duża zawartość harmonicznych sieci jest mocno zastanawiająca (dla MSP oczywiście). Nie obciąża ci się całość za bardzo?
  • Poziom 43  
    ZbeeGin napisał:
    Taki wymóg. Nic nie poradzę.

    Masz wymóg rozwiązania tego problemu przy użyciu skomplikowanego przebiegu?