Skocz do zawartości

Przebieg PWM 3 - fazowy na AVR


slawek7

Pomocna odpowiedź

CZeść.

Próbuję zrozumieć jak przy pomocy AVRa generowany jest przebieg sinusoidalny 3 fazowy.

Poczytałem parę not na ten temat ale zamiast rozjaśnić jeszcze bardziej mi zamieszały.

Czy ktoś z Was próbował to rozgryźć i może mi pomóc to zrozumieć?

Link do komentarza
Share on other sites

To może tak:

- Sygnały analogowe w tego typu aplikacjach generujesz przez "wyprodukowanie" prostokątnego sygnału PWM a potem odfiltrowanie dolnoprzepustowe.

- Zacznij od jednego przebiegu przy założeniach: masz sygnał PWM z okresem powiedzmy 8kHz a chcesz mieć sinusoidę 100Hz. Na jeden jej okres przypada zatem 80 okresów sygnału PWM - tylko w tych momentach możesz i musisz wyznaczać poziom kolejnej próbki. Podziel sobie zatem cały generowany sygnał na 80 kawałków i co 125us wysyłaj kolejną wartość - coś świta? A teraz zrób to samo z 3 wyjściami PWM i 3 sinusoidami przesuniętymi w fazie. W takim schemacie zwykle częstotlwość PWM jest stała a zmieniasz "krok" po jakim skaczesz po swojej wewnętrznej sinusoidzie (czy co tam chcesz wygenerować) wzorcowej. Jeżeli krokiem będzie 2*Pi/80 to dostaniesz 100Hz, jeżeli 2*Pi/120 to na wyjściu masz sinusoidę 66.6Hz a jeśli wyznaczysz krok na 2*Pi/8 to usłyszysz 1kHz. Proste?

  • Lubię! 1
Link do komentarza
Share on other sites

Nie świta, bo...

Dlaczego mając sygnał PWM zmiana indeksu tablicy z próbkami PWM następuje po każdym cyklu PWM czyli co przepełnienie timera odpowiedzialnego za generowanie PWM.

Jak w takim razie generowany jest przebieg sinusa jesli chciałbym zmienić częstotliwość przebiegu sinus mając dalej tą samą tablicę wartości.

Zobacz jeszcze na tą stronę

http://www.avislab.com/blog/stm32-pmsm_ru/

dlaczego ta tablica sinusa ma taki dziwny przebieg (te dwa garby - niebieski przebieg na dole).

Trochę poszukałem na ten tema, nikt nie tłumaczy dlaczego tak robią jak na tej stronie. Po prostu wzorują się na kilku notach katalogowych od microchipa i dawnego atmela

Link do komentarza
Share on other sites

OK, to REDO FROM START, jak mawiały stare komputery (tłumaczenie ZACZNIJ Z POCZĄTKU wydaje mi się równie fajne).

Zatem przez chwilę przestań myśleć o PWM. Zamiast tego załóżmy, że na wyjściu z procesora masz DAC czyli przetwornik cyfrowo-analogowy. Jak wyślesz do niego liczbę 0 to dostajesz 0V a gdy wyślesz powiedzmy 255 to dostajesz jego pełny zakres czyli np. 5V. Wygląda, że Twój DAC jest 8-bitowy 🙂

Żeby nie trzeba było wciąż liczyć kosztownych funkcji trygonometrycznych, w pamięci masz stablicowany jeden okres sinusa (albo innego przebiegu, który chcesz wygenerować, to może być trójkąt, piła, impuls Gaussa, cokolwiek). Powiedzmy, że zrobiłeś tablicę o długości 64 próbki, czyli policzoną z krokiem 2*PI/64. Ponieważ byłeś sprytny, to wartości  tablicy (gołe sinusy czyli zakres -1..+1) od razu pomnożone są przez 127 i do wszystkich dodałeś 128. Dzięki temu liczby jakie z niej czytasz pasują wprost do Twojego DAC-a:

  • 1 to najmniejsza możliwa wartość, bo (-1*127)+128 = 1,
  • 128 to poziom odniesienia, wartość "spoczynkowa" - to byś wysyłał do DACa gdybyś nie generował żadnego przebiegu, (0*127)+128 = 128
  • 255 to max tego co DAC może przyjąć, bo (+1*127)+128 = 255.

No a teraz zrobiłeś licznik i zaczynasz w pętli wysłać kolejne wartości odczytane z tablicy do DACa. Program napisałeś tak, że np. dostajesz przerwanie od jakiegoś timera co 100us czyli z częstotliwością 10kHz. Cały okres sinusa wyśle się zatem po 64 przerwaniach (i zliczeniu licznika od 0 do 63), a zatem po 64*100us=6.4ms. Wyjściowy sygnał będzie miał zatem częstotliwość 1/6.4ms=156.25Hz. Na razie jest prosto, prawda? Zatem chcesz zmienić częstotliwość. Oczywistym rozwiązaniem wydaje się zmiana częstotliwości przerwań od timera, ale to zły pomysł, bo systemy lubią stałe taktowanie. Być może na tym przerwaniu robisz też coś innego (zliczasz czas? generujesz inne przebiegi dla innego DACa? itp) a wierz mi, są jeszcze inne powody. Wypadałoby więc trochę szybciej albo trochę wolniej przemiatać tablicę. Jeżeli zamiast jednego kroku na raz robiłbyś ich więcej, np. do licznika dodawałbyś za każdym razem 2 to wtedy całą tablicę "przejechałbyś" w zaledwie 32 przerwania a sygnał wyjściowy miałby już 1/3.2ms = 312.5Hz. To jednak jest ułomne, bo od razu skoczyłeś o sporą wartość. Widać wyraźnie, że wypadałoby mieć licznik liczący ułamki albo inaczej liczby niecałkowite, prawda?. Wtedy można skakać w tablicy np. o 1.25 kroku i mieć delikatną zmianę częstotliwości zamiast dwukrotnej. I tak się właśnie robi 🙂 Oczywiście są tu pewne sztuczki, bo przecież tablicy nie możesz odczytywać z indeksu ułamkowego, np. 27.75, musisz ją adresować liczbą całkowitą, ale na szczęście ani używanie zmiennych typu float/double ani adresowanie "ułamkowe" nie są to potrzebne.

Zacznijmy od licznika. Do tej pory wystarczał licznik zrobiony na jednym bajcie, bo potrzebny zakres indeksów tablicy to 0..63. Teraz też tyle będzie wystarczało, bo tablica przecież się nie wydłużyła, ale potrzebujemy bitów "po przecinku". Zmieniamy zatem licznik np. na 16-bitowy int bez znaku i ustalamy, że nasz przecinek leży (dla uproszczenia) między bitami 7 a 8:

NNCCCCCC.UUUUUUUU

To co powyżej to 16-bitowe słowo licznika rozpisane na bity:

  • N - to bity nieużywane
  • C - to część całkowita naszego licznika/indeksu tablicy, 6 bitów wystarcza do zaadresowania naszych 64 pozycji
  • U - to część ułamkowa licznika

Oczywiście procesor o tym nic nie wie i nadal traktuje nasz licznik jako zwykłą zmienną całkowitą bez znaku. Pamiętając, że dla adresowania tablicy sinusów korzystamy tylko z bitów C zobaczmy co się będzie z nimi działo, gdy do licznika będziemy dodawać w każdym przerwaniu liczbę 0x0100:

0x0000, 0x0100, 0x0200, 0x0300 itd..

Ponieważ nasze bity C są na starszym bajcie to od razu widać, że zliczamy jakbyśmy do licznika dodawali 1. Tuż przez zaadresowaniem tablicy przesuwamy licznik o 8 bitów w prawo gubiąc niepotrzebną część ułamkową, obcinamy nadmiarowe bity N i mamy indeks całkowity w zakresie 0..63:

DAC = tablica[(licznik >>8) & 0x3F];

No to teraz spróbujmy dodawać coś ciekawszego, np. 0.75, czyli w naszej notacji 0x00C0 🙂:

0x0000, 0x00C0, 0x0180, 0x0240, 0x0300, 0x03C0, 0x0480 itd..

Zgodnie z oczekiwaniami mamy zatem sekwencję adresów: 0,0,1,2,3,3,4 itd.. Tablica adresowana w ten sposób będzie "przemiatana " wolniej zatem częstotliwość wyjściowego sinusa będzie mniejsza. W drugą stronę także się da: jeśli do licznika będziesz dodawał liczbę 0x0357 to dostaniesz na wyjściu sinus 521.85Hz (rozpisz to sobie i zastanów się dlaczego akurat tyle). Z resztą zostawiam Ci wyprowadzenie wzoru na to jaką liczbę dodawać by otrzymać wymaganą częstotliwość sygnału wyjściowego przy określonych parametrach syntezera (długość tablicy, postać licznika) 🙂 Wystarczy tylko wspomnieć, że w takiej konfiguracji jednostkowym krokiem tego systemu będzie ok. 0.61Hz. Teraz możesz zatem zupełnie spokojnie wygenerować częstotliwość 937.5Hz jak i 145.18Hz bez żadnej zmiany taktowania a jedynie przez dobór wartości dodawanej do licznika.Tak działają wszystkie tzw. DDS-y czyli programowane syntezery częstotliwości spotykane czasem w generatorach. Co prawda zamiast taktowania 10kHz masz tam 25MHz a licznik zamiast 14 bitów ma ich np. 24, ale zasada jest ta sama.

No i na koniec, jeśli w tych rozważaniach zastąpisz DACa timerem/generatorem PWM, to każdy okres PWMa zamienia się na jedną próbkę z DACa. Za każdym razem gdy generator PWM zgłasza Ci, że właśnie skończył poprzedni okres Ty wysyłasz mu nową próbkę sinusa z tablicy zaadresowanej licznikiem tak jak do DACa. Po odfiltrowaniu low-pass (to konieczne, inaczej całość nie ma sensu) dostajesz sygnał analogowy o żądanym kształcie. Czy teraz świta bardziej?

Acha, i jeszcze faza: jeśli chcesz produkować kilka sygnałów o tej samej częstotliwości, ale przesuniętych w fazie, to wystarczy tylko jeden licznik. Przykład dla trzech wyjść: bezpośrednio bitami C licznika adresujesz tablicę dla pierwszego sygnału, dodajesz do tego 1/3 długości tablicy (modulo jej długość) i dostajesz adres próbki dla drugiego wyjścia i po kolejnym dodaniu 1/3 długości tablicy masz indeks próbki dla trzeciego DACa/PWMa. Fajne?

  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Jesteś dobry, muszę przyznać. W końcu zrozumiałem DDS. Tam to zwykle nazywają akumulatorem fazy. Czy coś stoi na przeszkodzie aby zrozumieć to tak. Skoro z pewnych względów częstotliwość przewiania wysyłającego próbki do DAC musi być stała to chcąc wydłużyć okres generowanego przebiegu zwyczajnie w przerwaniu powtarzamy odczyt tablicy próbek tj  przerwanie co stały okres a odczyt tablicy tak: 0,0,0,1,1,1,2,2,2,3,3,3... itd. Inaczej mówić powtarzamy co x razy każdą próbkę?

 

Link do komentarza
Share on other sites

Tak, tylko to jest znów pewien przypadek szczególny w którym liczby jakie dodajesz do ułamkowego akumulatora fazy (tak, to jest właśnie on 🙂 ) będą miały postać 1/2, 1/3, 1/4, 1/5 itd. Wtedy rzeczywiście będziesz odczytywał tę samą próbkę 2, 3, 4, 5 itd razy, ale po co się ograniczać? Ani to  w niczym nie upraszcza programu ani nie umożliwia generowania częstotliwości w szerokim zakresie z małym rastrem. A jeśli chcesz mieć krok 0.625 (czyli 5/8) albo 2.828125 (czyli 2 i 53/64)? Obie te liczby mają skończone rozwinięcie dwójkowe na mniej niż 8 bitach i zaaplikowane do naszego DDSa z bezwzględną precyzją dadzą odpowiednio 97.65625Hz i 441.89453125Hz. A przecież to prosty programik z jedną, 16-bitową zmienną licznika gdzie za pomoca trywialnego dodawania niecałkowitych kroków dostajesz zakres od 0.61Hz (0x0001) do 2.5kHz (0x1000) z rastrem 0.61Hz. Pomysł jest tak prosty i efektywny, że żal go nie wykorzystać, prawda? Jak rozumiem Twój pomysł polega na tym, że po prostu odliczamy co ile próbek wysłać kolejną pozycję z tablicy. Zadziała, ale nie wiem co to daje, bo ani to prostsze ani lepsze. Akumulator fazy robi to, i wiele więcej. Wszystko zależy od liczby jaką dodajesz.

Bardzo istotnym elementem DDSa jest analogowy filtr wyjściowy. Musi to być porządnie zrobiony układ tnący ostro trochę poniżej połowy częstotlwości próbkowania. W prostych zabawkach można stosować jakieś kondensatorki i oporniki, w lepszych urządzeniach dawać filtry aktywne a w tych najbardziej wypasionych trzeba sięgać po scalone filtry z przełączanymi pojemnościami. Takie coś kosztuje z 10 USD, ale za to jest filtrem Czebyszewa np. 8 rzędu więc tnie ostro, a jego częstotlwość ustalasz zegarem cyfrowym. Bez takiego stopnia wyjściowego DDS produkuje przebiegi bardzo zniekształcone i zawierające sporo harmonicznych, których zawartość jest niestety różna w zależności od ustawionego kroku. Tak więc niektóre częstotliwości z DDSa będą ładniejszą sinusoidą a niektóre będą poszarpane, co jest chyba zgodne z intuicją. Filtr analogowy to wycina i każdemu daje równą szansę 🙂

Przy okazji: puszczając kilka DDSów równolegle, korzystających oczywiście wspólnie z tego samego przerwania od  jednego timera i tym samym jednej, wspólnej częstoliwości próbkowania oraz sumując ich sygnały wyjściowe jeszcze w procesorze, można zupełnie spokojnie zrobić polifoniczne organki np. na Arduino mogące grać rozbudowane akordy a nie tylko "Wlazł kotek na płotek" jednym palcem. A mając kilka tablic różnych sygnałów, można łatwo przełączać "brzmienia", bo jednak czysta sinusoida jest akustycznie mało ciekawa..

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Możesz mi jeszcze coś uświadomić? 🤔

Jeśli przebieg generujemy przy pomocy PWM to dlaczego ta zmiana wystawionej liczby z tablicy przebiegu jest wykonywana za każdym cyklem PWM (czyli de facto po przepełnieniu licznika odpowiedzialnego za przebieg PWM), a nie np z wykorzystaniem innego licznika którego okres jest inny niż PWM. Wtedy taka zmiana wystawionej próbki byłaby co jakiś n-ty okres. Jest jakieś uzasadnienie tego?

I druga sprawa. Jeśli mam przebieg np sinusa w tablicy i niech to będzie jego pełny okres w tej tablicy to wystawiając po kolei elementy tablicy dostanę max częstotliwość przebiegu, czy tak?

Jak teraz zrobić prosty akumulator fazy?

 

Link do komentarza
Share on other sites

1 godzinę temu, slawek7 napisał:

dlaczego ta zmiana wystawionej liczby z tablicy przebiegu jest wykonywana za każdym cyklem PWM

Dlatego, żeby było prościej. Robisz akumulację fazy czyli dodajesz zadany offset do akumulatora, wybierasz z niego najstarsze bity i nimi adresujesz tablicę. Nie wiesz czy te bity się zmieniły, bo przecież nie muszą zmieniać się z każdą wysyłaną próbką i nic Cię to nie obchodzi. W zależności od szybkości "przejazdu" przez tablicę wysyłasz kilka razy tę samą wartość (czyli pozycję z tablicy) a czasem krok jest większy niż 1 i wtedy czasem niektóre wartości/próbki w ogóle pomijasz. Przyjrzyj się jeszcze raz mojemu przykładowi działania DDSa z czynnikiem 0.75. Przecież sekwencja adresów/indeksów tablicy nie jest prostą inkrementacją. Pokażę ją raz jeszcze, ale przedłużoną:

0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8..

Tego nie zrobisz zwykłym licznikiem a dzięki takiej sekwencji posuwasz się przez tablicę z prędkościa średnią równą 0.75 standardowej, tj. tej z offsetem 1. A skoki/nieciągłości rozłożone są średnio najlepiej jak można. Jeśłi w tym kontekście weźmiesz pod uwagę długi czas wielu okresów sinusa wyjściowego to okaże się, że jego częstoliwość jest równa 0.75 * (10kHz/64). Offset ustawiony dokładnie na 1 daje sekwencję adresów/numerów próbek jak zwykłe zliczanie, ale jest to tylko przypadek szczególny:

0, 1, 2, 3, 4, 5, ,6 ,7 ,8..

A gdy dodawana liczba jest większa niż 1, to w sekwencji adresów pojawiają się czasem przeskoki o więcej niż jeden adres, bo faza akumuluje się szybciej niż 1/64 okresu za każdą próbką, bo DDS musi "zdążyć" przejechać tablicę szybciej. Przykładowo przy dodawaniu wartości 1.875 (czyli 0x01E0) dającej częstotliwość ok. 293Hz (bo 1.875*10kHz/64) masz sekwencję (liczby szesnastkowe):

0, 1, 3, 5, 7, 9, B, D, F, 10, 12, 14, 16, 18, 1A, 1C, 1E, 1F..

Ponieważ offset jest tu prawie równy 2 to kroki też są prawie równe 2. A ponieważ mogą być tylko całkowite, to w większości są rzeczywiście 2, ale czasami zdarza się o 1 tak, by średnio za długi czas wyszło nasze 1.875. Tego też nie zrobisz zwykłym licznikiem, prawda? Wszystko polega na tym, ze akumulator fazy ma bity ułamkowe, "schowane" na prawo od bitów adresu tablicy. Coś tam się w nich dzieje podczas kolejnych dodawaś offsetu i w końcu, gdy wyjdzie z nich przeniesienie (na skutek akumulacji części ułamkowych), mamy dodatkowy przeskok o 1.

1 godzinę temu, slawek7 napisał:

Jeśli mam przebieg np sinusa w tablicy i niech to będzie jego pełny okres w tej tablicy to wystawiając po kolei elementy tablicy dostanę max częstotliwość przebiegu, czy tak?

Nie. Czytając kolejne próbki w trywialnej sekwencji 0, 1, 2, 3 dostaniesz przebieg o częstotliwości jakby "podstawowej" dla tego DDSa, ale nie jest to ani najmniejsza największa częstotlwość wyjściowa. Najmniejsza będzie wtedy, gdy do akumulatora będziesz dodawał najmniejszy możliwy ułamek. W naszym przypadku będzie to liczba 0x0001. A ponieważ przyjęliśmy 8 bitów w części ułamkowej, to najmłodszy bit "waży" 1/256 i tyle możesz dodawać najmniej i tak wolno będzie poruszał się indeks naszej tablicy z każdym okresem PWM. Podstawiając ten minimalny offset do wzoru: 1/256 * (10kHz/64) dostajesz ok. 0.61Hz. Mam nadzieję, ze już rozumiesz dlaczego. Musisz dodać aż 256 liczb 0x0001 aby zmienił się o 1 starszy bajt licznika - a to przecież on adresuje tablicę 🙂 

Z kolei w drugą stronę sprawa jest trochę trudniejsza. Teoretycznie największym krokiem jest taki, który wyśle conajmniej dwie próbki sinusa na wyjście w każdym jego okresie - to wynika z teorii o próbkowaniu. Tak więc dodając liczbę o wartości będącej połową długości tablicy (oraz startując z odpowiedniej fazy / indeksu tablicy) masz najwyższą częstotliwość: 32*(10kHz/64) = 5kHz. Sekwencja adresów tablicy powinna być wtedy taka: 16, 48, 16, 48, itd.. co na wyjściu DACa/PWMa wygląda jakoś tak: 1, 255, 1, 255 itd.. Mimo, że jest to ewidentnie protokąt, nawet z czegoś takiego idealny, analogowy filtr wyjściowy (pamiętasz o nim? teraz widać dlaczego jest niezbędny) odtworzy poprawną sinusoidę. Niestety nic nie jest idealne a filtry analogowe w szczególności. Dlatego w moim opisie (gdzieś wyżej) założyłem, że maksymalną częstotliwością powinna być raczej ta, przy której wysyłasz 4 próbki z tablicy na jeden jej "przejazd". Wynika z tego offset 64/4 czyli 16, czyli liczba 0x1000. Wtedy sekwencja adresów wygląda tak: 0, 16, 32, 48, 0, 16, 32, 48, 0, itd.. a wysyłane wartości to 128, 255, 128, 1, 128, 255, 128, 1, itd Narysuj to sobie. Z czegoś takiego nawet nieidealny (ale wciąż dobry) filtr analogowy zaprojektowany na 5kHz spokojnie odtworzy sinusoidę 2.5kHz. Oczywiście im filtr będzie gorszy, tym więcej "schodków" pojawi się w sygnale wyjściowym, tym "gorsza" będzie sinusoida i tym więcej będzie zawierała harmonicznych. W szczególności składowych sygnału zegarowego - tutaj 10kHz, ale nie tylko.  Wniosek: im gorszy/prostszy/słabszy analogowy filtr wyjściowy tym niższa jest maksymalna częstotlwość sygnału z DDSa przy zachowaniu założonych parametrów czystości widmowej.

Czy to jakoś rozjaśnia temat? Bo mam wrażenie, że zadając pytania o te sekwencje generowane przez proste liczniki mimo wszystko jeszcze nie do końca czaisz bazę. DDS nie musi wysłać każdej próbki z tablicy w każdym okresie sygnału wyjściowego i nie musi także wysłać jej jednokrotnie. Niezależnie od tego czy krok (offset) jest mniejszy od 1.00 (wtedy wysyłamy czasem kilka takich samych próbek pod rząd, ale wcale nie jest powiedziane, że zawsze tyle samo powtórzeń) czy większy od 1.00 (wtedy niektóre pozycje tablicy pomijamy, ale uwaga: w każdym okresie sinusa być może inne), całkowity indeks tablicy jest przesuwany tak, by średnio w dłuższym czasie krok był dokładnie taki jak sobie życzysz 🙂 

EDIT: Może spróbuj zaprezentować jakiś kawałek kodu realizujący choćby tego DDSa jaki pokazałem. Wydaje się prosty w implementacji więc do roboty. Przynajmniej rozmowa będzie bardziej konkretna. Na razie nie wnikaj w szczegóły timerów, przerwań itp. Chodzi o główną funkcję syntezera przy założeniu, że jakoś jest wywoływana okresowo co 100us i wysyła próbki do jakiegoś wyimaginowanewgo, 8-bitowego DACa.

Edytowano przez marek1707
  • Lubię! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.