wtorek, 29 marca 2011

FAST PWM - Sposób na DAC

Autor: drzasiek
Redakcja: dondu


Drzaśkowy pamiętnik: Spis treści


Mikrokontroler Atmega 8 oferuje dwie sprzętowe możliwości pomiaru wielkości analogowych bez dołączania specjalizowanych układów zewnętrznych. Tymi sposobami są:
  • pomiar napięcia wbudowanym  ADC  (przetwornikiem analogowo-cyfrowym)
  • pomiar i/lub porównywanie napięcia wbudowanym komparatorem analogowym

Jednak systemy które budujemy nie tylko „obserwują” i liczą ale również w zależności od założeń czasami muszą czymś sterować. Jeśli mają sterować urządzeniami posiadającymi interfejs do komunikacji cyfrowej to nie ma problemu, ale czasem trzeba sterować bezpośrednio prądem, napięciem lub mocą oddawaną do odbiornika.

Mikrokontroler jednak jako urządzenie cyfrowe bezpośrednio na swoich wyprowadzeniach cyfrowych może ustalać stan wysoki (czyli zbliżony do napięcia zasilania) lub stan niski (czyli zbliżony do masy zasilania) czy też stan wysokiej impedancji.

Tak wiec wyprowadzenia cyfrowe bezpośrednio nie mogą sterować napięciem lub prądem. Bardziej rozbudowane mikrokontrolery posiadają jeszcze jedno lub kilka wyjść analogowych dzięki wbudowanemu przetwornikowi cyfrowo analogowemu tzw. DAC (Digital to analog converter).

Mikrokontrolery takie jak Atmega nie posiadają jednak DAC . Co więc w takiej sytuacji uczynić gdy zaistnieje potrzeba regulacji prądu i/lub napięcia? Wymienić mikrokontroler na lepszy? Można, ale nie jest to konieczne.

Większość mikrokontrolerów posiada możliwość konfiguracji przynajmniej jednego Timera do pracy w trybie PWM.

Czym jest więc PWM?
PWM to skrót od Pulse Width Modulation – czyli jest to modulacja szerokości impulsu. Innymi słowy to powtarzający się ciąg impulsów o takiej samej bądź różnej szerokości.

Jednym z parametrów takiego ciągu impulsów jest Duty Cycle czyli współczynnik wypełnienia sygnału. Jest to stosunek czasu trwania impulsu do czasu trwania okresu sygnału okresowego.




Współczynnik wypełnienia oblicza się następująco:



Czyli np. jeśli czas impulsu trwa 60ms, a okres trwa 100ms, to współczynnik wypełnienia wynosi:



Ale co to ma wspólnego z regulacją prądu lub napięcia?

Otóż w czasie trwania impulsu 5V prąd płynie z/do odbiornika, natomiast w czasie gdy napięcie jest równe 0V prąd nie płynie. Zatem średnia wartość prądu jest równa prądowi maksymalnemu pomnożonemu przez współczynnik wypełnienia.

Dla przykładu załóżmy, że podłączyliśmy diodę LED przez szeregowo dołączony rezystor do wyjścia cyfrowego mikrokontrolera zasilanego napięciem 5V. Rezystor dobrany jest tak (możesz wykorzystać kalkulator LED), by przez diodę płyną prąd 10 mA. Jeśli teraz na wyjście uC ustawimy stan wysoki czyli 5V, to dioda będzie świecić i popłynie prąd 10mA.

A co się stanie jeśli na to wyjście nie podamy stanu wysokiego ale wygenerujemy sprzętowo sygnał prostokątny o wypełnieniu 50%? Średni prąd płynący przez diodę będzie równy 5mA, a więc dioda będzie świecić słabiej.

Czas więc teraz wypróbować, to w praktyce przy użyciu mikrokontrolera Atmega 8.

Zatem przeglądnąć trzeba dokumentację Atmega8. Posiada ona dwa timery, które mogą pracować w trybie PWM. Jest to 16 bitowy Timer/Counter 1



oraz 8 bitowy Timer/Counter2:




Nawet taki prosty mikrokontroler jak Atmega8 dysponuje kilkoma trybami pracy PWM. Ja zastosowałem jednak ten najczęściej stosowany w amatorskich konstrukcjach, czyli tryb Fast PWM dla Timera 1 (16 bit) ponieważ jest dokładniejszy (większa rozdzielczość) i posiada większe możliwości.

Timer/Counter1 posiada dwa rejestry porównania, a więc można wygenerować dwa sygnały o tej samej częstotliwości, ale o różnym współczynniku wypełnienia:


Rys. Timer1 w Atmega8 

Na powyższym schemacie zaznaczyłem dwa kanały, które podłączone są odpowiednio do pinów OC1A i OC1B.

Tryb szybki PWM (Fast PWM) posiada możliwość pracy przy największej częstotliwości oraz różni się tym od pozostałych trybów tym, że zmiana stanów następuje tylko na jednym zboczu zmiany wartości licznika.



Zasada działania jest prosta. Timer zlicza w górę impulsy zegarowe (z wybranego źródła podzielone przez  ustawiony preskaler) od początkowo zadanej wartości w rejestrze TCNT1. Jeśli wartość rejestru TCNT1 jest równa wartości w rejestrze porównania OCR1A, to wyjście OC1A zmienia stan. Tak samo dzieje się w przypadku OCR1B i OC1B.

O tym jaka to będzie zmiana decyduje programista ustawiając odpowiednie bity w rejestrze konfiguracyjnym:



Kolejna zmiana następuje przy wartości TOP(kiedy to licznik się zeruje).
Wartość TOP również zależnej od wybranego trybu pracy PWM:




Zatem spróbujmy poskładać układzik z dwiema naprzemiennie płynnie regulowanymi diodami LED.
Wykorzystałem do tego możliwość wygenerowania dwóch sygnałów z Timera2.

Ale na których nogach uC będę miał sygnały? Jak zwykle odpowiedź leży w dokumentacji:



A więc trzeba poszukać gdzie znajdują się nogi z możliwością pracy jako OC1A oraz OC1B:



Jak widać obudowa PDIP posiada te wyprowadzenia na nogach PB1 oraz PB2.

Pora więc narysować schemat:


Pobierz schemat: eagle_1.rar


Ustawienia mikrokontrolera
Atmegę ustawiłem do pracy z wewnętrznym oscylatorem 1MHz.
Timer1 skonfigurowany do pracy w trybie Fast PWM, tryb 14 z tabeli 16-5.
Timer2 – przerwanie od przepełnienie licznika tzw. overflow (zmiana współczynnika wypełnienia)

Program:

// ***********************************************************
// Atmega8 - PWM
// FCPU 1MHz
// http://www.mikrokontrolery.blogspot.com
// ***********************************************************
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h> 

#define LED1 (1<<PB1)
#define LED2 (1<<PB2)

volatile int8_t kierunek=1;

int main(void) 
{
   DDRB |= LED1 | LED2;   //Wyjścia LED
 
   //Inicjalizacja Timer1 (PWM)
   TCCR1A |= (1<<COM1A1) //Zmiana stanu wyjścia OC1A na niski przy porównaniu A
          |  (1<<COM1B1) //Zmiana stanu wyjścia OC1B na niski przy porównaniu B
          |  (1<<WGM11); //Tryb 14 (FAST PWM, TOP=ICR1)

   TCCR1B |= (1<<WGM13) | (1<<WGM12)  //Tryb 14 (FAST PWM, TOP=ICR1)
          | (1<<CS10);                //Brak preskalera

   ICR1 = 1000;  //Wartość maksymalna (dla trybu 14)
                 //a więc częstotliwość = CLK/ICR1 = 1kHz

   OCR1A=500;    //Wartość początkowa porównania A (Wyjście OC1A - PB1),
                 //wypełnenie = 50%

   OCR1B=500;    //Wartość początkowa porównania B (Wyjście OC1B - PBB), 
                 //wypełnenie = 50%

   //Inicjalizacja Timer0 (zmiana współczynnika wypełnienia) 
   TCCR0 |= (1<<CS01);    // źródłem CLK, preskaler 8
   TIMSK |= (1<<TOIE0);   //Przerwanie overflow (przepełnienie timera)  

   sei();    //Globalne uruchomienie przerwań  

   for(;;);  //Pętla nieskończona
} 

//Procedura obsługi przerwania od przepełnienia Timer0
ISR(TIMER0_OVF_vect)   
{
   //jeśli dojdzie do granicy to zmienia kierunek
   if(OCR1A==1000 || OCR1A==0) kierunek*=-1;

   OCR1A+=kierunek;   //zmienia współczynniki wypełnienia
   OCR1B-=kierunek;
}
Pobierz kod: pwmA.rar

Efekt uzyskany:






Dwukanałowy generator funkcyjny


Timer w trybie PWM można również wykorzystać jako prosty przetwornik cyfrowo-analogowy. Dodając na wyjścia OC1x proste pasywne filtry RC uzyskane napięcia za filtrem będą mniej więcej równe:


Takie założenie można wykorzystać do budowy prostego generatora funkcyjnego (należy jednak pamiętać o ograniczonej wydajności prądowej takiego źródła napięcia, a dla wzmocnienia prądowego można za filtrem dać wtórnik napięcia).


Pobierz schemat: eagle_2.rar

Aby przyspieszyć działanie generatora zmieniłem częstotliwość taktowania uC na 8 MHz z wewnętrznego generatora RC.

Timer1 - skonfigurowany do pracy w trybie Fast PWM, tryb 14 z tabeli 16-5.
Timer2 - przerwanie overflow (przepisywanie buforów do rejestrów OCR1A oraz OCR1B)

Generator na wyjściu OUTA generuje sygnał sinusoidalny, a na wyjściu OUTB sygnał piłokształtny.

Program:

// ***********************************************************
// Atmega8 - Generator Funkcyjny na PWM
// www.mikrokontrolery.blogspot.com
// FCPU _ 8MHz
// ***********************************************************
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h> 
#include <math.h> 

#define OUT1 (1<<PB1)
#define OUT2 (1<<PB2)

volatile uint8_t bufor_sinus[256];//bufor na wartości funkcji sinus
volatile uint8_t bufor_pila[256];//bufor na wartości funkcji piła
volatile uint8_t indeks = 0;  //indeks bufora do odczytu w przerwaniu

int main(void) 
{
   DDRB |= OUT1 | OUT2;//Wyjścia Generatora
 
   //Inicjalizacja Timer1 (PWM)
   TCCR1A |= (1<<COM1A1) //Zmiana stanu wyjścia OC1A na niski przy porównaniu A
          |  (1<<COM1B1) //Zmiana stanu wyjścia OC1B na niski przy porównaniu B
          |  (1<<WGM11); //Tryb 14 (FAST PWM, TOP=ICR1)

   TCCR1B |= (1<<WGM13) | (1<<WGM12)  //Tryb 14 (FAST PWM, TOP=ICR1)
          |  (1<<CS10);  //Brak preskalera

   ICR1 = 255; //Wartość maksymalna (dla trybu 14) 
               //a więc częstotliwość = CLK/ICR1=8kHz

   OCR1A=127;  //Wartość porównania A (Wyjście OC1A - PB1), wypełnenie = 33%
   OCR1B=127;  //Wartość porównania B (Wyjście OC1B - PBB), wypełnenie = 66%

   //Inicjalizacja Timer0 (zmiana współczynnika wypełnienia) 
   TCCR0 |= (1<<CS00);  // źródłem CLK, brak preskalera
   TIMSK |= (1<<TOIE0);    //Przerwanie overflow (przepełnienie timera)  

   //Tworzy bufor funkcji Sinus
   for(unsigned short x=0; x<256; x++) bufor_sinus[x]=128+126*sin(6.26/256*x);

   //Tworzy bufor funkcji piła
   for(unsigned short x=0; x<256; x++) bufor_pila[x]=x;

   sei(); //Globalne uruchomienie przerwań  

   for(;;); //Pętla nieskończona
} 

//Procedura obsługi przerwania od przepełnienia Timera0
ISR(TIMER0_OVF_vect)   
{
   OCR1A=bufor_sinus[indeks]; //przepisuje do rejestru porównia wartość funkcji
   OCR1B=bufor_pila[indeks];  //przepisuje do rejestru porównia wartość funkcji

   if(indeks<255)//inkrementuje indeks bufora 
      indeks++;
   else//jeśli koniec bufora to powrót na początek
      indeks=0;
} 
Pobierz kod: pwmB.rar

Otrzymany efekt na oscyloskopie:




Dlaczego przebieg piły nie jest idealny?
Ponieważ przebiegiem podstawowym jest sinus, a inne przebiegi, np. piła składa się z sinusa o tej samej częstotliwości oraz z szeregu harmonicznych sinusa.

Poniżej widmo piły:



Aby wygładzić przebieg z PWM przepuszcza się go przez filtr dolnoprzepustowy, który wycina wyższe harmoniczne. Zatem wycina on też wyższe harmoniczne przebiegu piły a co za tym idzie przebieg "ma ochotę" coraz bardziej upodobnić się do sinusa. Jeśli filtr będzie miał za małe pasmo, to wytnie praktycznie wszystkie harmoniczne i zostanie w widmie sam prążek sinusa:



(na rysunku linią czerwoną zaznaczyłem przykładową-dość optymistyczną-charakterystykę przejściową filtra) zatem w sygnale zostaje praktycznie sama składowa częstotliwości podstawowej a więc z sygnału zrobi się sygnał bardzo zbliżony do sinusa (a teoretycznie po wycięciu wszystkich wyższych składowych powstanie sinus).





Przykłady wykorzystania

Jednym z przykładów wykorzystania PWM i filtru RC jako przetwornika DAC jest generowanie dźwięku wraz z obwiednią ADSR. Przykładowe programy znajdziesz w temacie: Generowanie obwiedni ADSR za pomocą mikrokontrolera





40 komentarzy:

  1. Witam, Mam kwarc 12MHz, gdzie w tym gdzie ustawić, żeby na wyjściu sygnały miały 40kHz?

    OdpowiedzUsuń
  2. Mam pytanko odnośnie regulowania częstotliwości i amplitudy sygnału. Jeżeli się mylę to poprawcie mnie. Do regulowania częstotliwości służy bufor funkcji, natomiast do regulowania amplitudy i ofsetu służą rejestry ICR1 i OCR1. Oczywiście bufor wpływa równierz pośrednio na wartość amplitudy i ofsetu sygnału. Mam rację?

    OdpowiedzUsuń
  3. TIMER1 generuje 2 sygnały PWM z częstotliwością 8 kHz w tym przypadku. Do zmiany częstotliwości sygnału służy TIMER0, ponieważ w procedurze przerwania od TIMER0 przepisywane są kolejne próbki z buforów do "DAC" czyli jako wartość wypełnienia, co z kolei przekłada się na napięcie. Jeśli przerwanie będzie zgłaszane częściej, to nowe próbki będą częściej przepisywane do PWM, więc częstotliwość sygnału się zwiększy. Można też regulować częstotliwość za pomocą bufora, np w buforze umieścić dwa okresu sygnału zamiast jeden, więc częstotliwość sygnału się zwiększy 2 razy, ale kosztem rozdzielczości.

    OdpowiedzUsuń
  4. Mały błąd w komentarzu do programu z przykładu 1:
    OCR1B=500; //Wartość początkowa porównania B (Wyjście OC1B - PBB
    Zamiast PBB powinno być zdaje się PB2

    OdpowiedzUsuń
  5. Witam. Mały trick odnośnie drugiego przykładu. Nie ma konieczności sprawdzania wartości index w funkcji obsługującej przerwanie. Skoro index jest zmienną typu uint_8t (nieujemną, 8 bitową), to jej inkrementacja w momencie gdy ma ona już wartość 255 spowoduje automatyczne 'przekręcenie licznika', czyli jej wartość po tej operacji będzie wynosić zero.

    Pozdrawiam : )

    OdpowiedzUsuń
  6. W jaki sposób mogę ustalić konkretną częstotliwość i amplitudę przebiegu sinusoidalnego?
    Jak włączać i wyłączać jego generowanie w odpowiednim momencie?

    Jak dużym oporem można obciążyć wyjście takiego generatorka?

    OdpowiedzUsuń
    Odpowiedzi
    1. Możesz obciążyć bardzo dużym oporem, nawet 100 mega giga.

      Usuń
  7. O regulację częstotliwości już ktoś wyżej pytał. Wystarczy przeczytać komentarze i przeanalizować kod.

    Co do amplitudy, możesz ją między innymi regulować przez obciążenie, tworząc w ten sposób dzielnik napięcia z rezystorem filtra LC, jednak to nie jest dobre rozwiązanie.
    Można także wykonać dzielonik napięcia z rezystorów lub potencjometrem na wyjściu filtra LC czy też programowo po prostu wgrać do bufora odpowiednio mniejsze wartości próbek.

    Każde obiążenie pogorszy jakość sygnału wyjściowego i zmniejszy jego amplitudę. Więc jeśli chcesz uzyskać na obciążeniu taki sam sygnał, to warto zastosować wtórnik napięcia.

    OdpowiedzUsuń
  8. Mam jeszcze jedno pytanie.
    Co zrobić w przypadku, gdy projekt posiada dwie oddzielne masy - cyfrową i analogową? W moim przypadku osobną masę analogową posiada układ wyjścia/wejścia audio użytego modułu GSM. Ponieważ zachodzi konieczność wygenerowania sygnału przez PWM i skierowania go na tą samą słuchawkę (nie jednocześnie - przełączenie wejść realizuję kluczami CD4066) zastanawiam się jak układ zachowa się przy osobnej masie. Atmega pracuje na cyfrowej, ale sinusoidę (przez kondensator) muszę skierować do słuchawki, podłączonej do masy analogowej.

    OdpowiedzUsuń
  9. Mam jeszcze jedno pytanie:
    Dlaczego w drugim przykładzie zastosowano tryb 14, z maksymalną wartością wpisywaną do ICR1, skoro jest nią 255. Czy w takim przypadku nie powinno się zastosować ośmiobitowego trybu 5, gdzie TOP jest ustawiony na stałe właśnie na 0xFF?

    OdpowiedzUsuń
  10. Obciążalność wyjścia to około 15..20mA. Takie wartości mam w głowie. Więcej szczegółów znajduje się w dokumentacji.

    OdpowiedzUsuń
  11. Zamiast:
    "
    //Tworzy bufor funkcji Sinus
    for(unsigned short x=0; x<256; x++) bufor_sinus[x]=128+126*sin(6.26/256*x);

    //Tworzy bufor funkcji piła
    for(unsigned short x=0; x<256; x++) bufor_pila[x]=x;
    "

    Można:
    "
    //Tworzy bufor funkcji Sinus i piła
    for(unsigned short x=0; x<256; x++){
    bufor_sinus[x]=128+126*sin(6.26/256*x);
    bufor_pila[x]=x;
    }

    OdpowiedzUsuń
  12. Zastanawiam się czy nie lepiej zamiast wykonywania nieskończonej pętli "for(;;)" nie byłoby lepszym rozwiązaniem usypianie procesora w trybie "Power-save". Programuję jedynie amatorsko ale sądzę, że może to być całkiem sensowne rozwiązanie. Czy któryś z kolegów, który ma więcej doświadczenia, mógłby tą kwestię skomentować?

    OdpowiedzUsuń
  13. Pętla i tak musiała, by być ponieważ po wybudzeniu realizowałby program dalej ... a tutaj brak programu i co wtedy ... kompilator domyślnie wyłącza przerwania i dodaje na końcu pętlę główną - czyli pozorne zawieszenie się mikrokontrolera :)

    Czy opisałem zrozumiale?

    A propos Power Save, to jeszcze dzisiaj ukaże się cykl dot. projektowania urządzeń, i tam będziemy wykorzystywać ten tryb.

    OdpowiedzUsuń
  14. No tak, uśpienie powinno być po zakończeniu obsługi przerwania. Oczywiście to czysto teoretyczna dyskusja bo trzeba by najpierw policzyć czy się to opłaca robić.

    OdpowiedzUsuń
  15. WIELKIE DZIĘKI!!!!

    Tego szukałem - działa pięknie!!!
    Pozdrawiam.

    OdpowiedzUsuń
  16. Czy ktoś może mi objaśnić, bo jestem chyba zupełnie ciemny, dlaczego w generatorze funkcyjnym jest częstotliwość 8KHz? Z moich obliczeń wynika: 8MHz/1/255 = ok 30KHz.
    Po drugie, dlaczego dla OCR1A = 127 wypełnienie wynosi 33% a nie 50%, (analogia z pierwszego przykładu)? Gdzie są błędy w moim rozumowaniu?

    OdpowiedzUsuń
  17. Podpowie mi ktoś, jaki mikrokontroler ma DAC( albo jaka rodzina, bo AVR, chyba nie dadzą rady). Muszę sterować napięciem analogowym z zakresu 0 - 5V z krokiem 5mV, więc PWM mi nie pomaga. Z góry dziękuję za pomoc.

    OdpowiedzUsuń
    Odpowiedzi
    1. Na stronie producentów znajdziesz wyszukiwarki z możliwością wybrania tych mikrokontrolerów, które mają sprzętowy DAC. Także Atmel ma taką wyszukiwarkę na swojej stronie.

      Sposób opisany w artykule powyżej prawdopodobnie pozwoli Ci także osiągnąć rozdzielczość 5mV, a nawet mniej. Nie podałeś niestety dokładniejszych parametrów sygnału, który chcesz generować i do czego on ma służyć.

      Usuń
  18. Xmega to też avr, i mają DAC.

    OdpowiedzUsuń
  19. Jaką maksymalną częstotliwość (tylko) sinusa można wygenerować w ten sposób zakładając, że dysponujemy zegarem 16MHz? Jak to policzyć?
    Pozdrawiam

    OdpowiedzUsuń
  20. To zależy od tego, z jaką częstotliwością będzie się wykonywała funkcja obsługi przerwania.
    Możesz to policzyć, ile trwa wejście w przerwanie, wykonanie instrukcji w funkcji obsługi tego przerwania, wyjście z przerwania. Częstotliwość sinusa na wyjściu zależy od tego jak często aktualizujesz rejestr OCR1x oraz ile próbek tego sinusa masz w buforze (im mniej próbek masz tym mniej aktualizacji potrzeba by uzyskać jeden okres sinusa) ale tracisz wtedy na rozdzielczości.

    OdpowiedzUsuń
  21. No dobrze :)
    Chciałbym uzyskać 5kHz przy powiedzmy 128 próbkach. Da rade uzyskać taką częstotliwość?
    Jeśli tak to czy starczy jeszcze czasu na obsługę uarta ( jakieś proste zapytania co około 0.5 sekundy w przerwaniu ). Przykładowy sposób obliczania tych czasów bardzo by mi pomógł...
    Pozdrawiam

    OdpowiedzUsuń
    Odpowiedzi
    1. Raczej nie uda ci się to zrealizować (na M8). Chcesz sygnał o częstotliwości 5 kHz, na każdy okres składa się 128 próbek, a więc 128 próbek powtarzasz 5000 razy na sekundę. Czyli wysyłasz 640k próbek na sekundę. Teraz wszystko zależy od rozdzielczości z jaką określasz wartość próbek (rozdzielczości PWM). Zakładając, że interesuje cię rozdzielczość 8-bitowa, potrzebujesz taktowanie timera równe 640k*256, czyli 163,84 MHz. Oczywiście zwykły AVR z rodziny ATMega nie ma takiej możliwości. Ale... niektóre AVRy mają PLL (układ mnożący częstotliwość) dla timera (lub całego rdzenia). Są to nowsze układy ATTiny (zazwyczaj z 3-cyfrowym oznaczeniem) lub układy z rodziny XMEGA. Zwykle dysponują one możliwością taktowania timera w zakresie 128-256 MHz (256 MHz to 128 MHz wykorzystujące oba zbocza zegara). Co więc zrobić? Przerzucić się na ATTiny lub XMEGA, lub ograniczyć rozdzielczość lub liczbę próbek na jeden okres. Jeśli przerzucisz się na XMEGA to masz do dyspozycji po prostu DAC który przetwarza do 1 Msps. Jest jeszcze jeden problem, musisz przeładowywać rejestr timera 640k razy na sekundę, dla ATTiny taktowanego 20 MHz (max dla tej rodziny) daje ci to trochę ponad 31 instrukcji pomiędzy kolejnymi przeładowaniami. I powstaje problem jeśli procesor ma robić cokolwiek innego. Stąd też dla takich celów wykorzystanie XMEGA, gdzie przełądowywaniem rejestru timera może zajmować się układ DMA ma sens - napisanie aplikacji, która robi przy okazji coś więcej nie jest problemem, a procek praktycznie nie jest obciążony tym zadaniem. Przykłady takiego wykorzystania timera (generowanie właśnie przebiegów o zadanym kształcie) możesz pobrać za darmo - są w przykładach do mojej książki o XMEGA.

      Usuń
    2. No to się nazywa konkretna odpowiedz :)
      Dziękuję bardzo za wyczerpanie tematu - jak dla mnie.
      Myślałem że uda się to zrobić na małym avrku ale przesiadka na xmega jakoś mi nie odpowiada więc z dwojga złego zastosuję coś co trochę znam - STM32F407 i przy okazji chyba "przerzucę" połowę czarów na pcb "do softu".
      Dziękuję i pozdrawiam.

      Usuń
  22. Cześć!
    Wykorzystałem Twój kod do wykonania generatora sygnałów kwadraturowych (przesuniętych względem siebie o 90 st.). W moim przypadku wystarczył ATTiny 2313 (wyjścia PB3 i PB4, zamiast Math - wpisałem tabelę od razu do pamięci). Dziękuję i pozdrawiam.

    OdpowiedzUsuń
  23. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
  24. krótkie pytanie:
    FastPWM 8bit, zmiana wyjscia na niski przy porównaniu,
    ilu procentowe jest wypełnienie sygnału (stanu wysokiego) dla wartości:
    a) OCR0A=0 ?
    b) OCR0A=255 ?

    OdpowiedzUsuń
  25. Witam.
    Mam problem z generatorem sinusa i piły. Na wyjściu OC1A/B wpiąłem diody led, jedyne co mogę zaobserwować to zmianę jasności świecenie- jest ona stała a chodzi mi o płynne "sinusoidalne" przejścia.
    Pomoże ktoś ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Witam. Nie wiem czy dobrze zrozumiałem, ale chodzi Tobie chyba o korekcję świecenia diody w taki sposób, by dla ludzkiego oka zmieniała sie ona płynnie. Jeśli tak, to ten temat omawiamy w artykule: LED vs ludzkie oko (korekcja gamma)

      Usuń
  26. Witam. W jaki sposób mógłbym zbudować zaprogramować układ generatora z niezależną regulacją częstotliwości i wypełnienia? Chciałbym regulować częstotliwość 2 przyciskami( + i -) z krokiem 100Hz od 1kHz do 30KHz i wypełnienie od 1 do 99% również dwoma przyciskami (+ i -) z krokiem 1%. Jakie wartości regulować tymi przyciskami i jak ogólnie za to się zabrać?

    OdpowiedzUsuń
  27. zawsze znajdę tutaj pomocne informacje, dziekuje za czas poswiecony!

    OdpowiedzUsuń
  28. Czemu w drugi przykładzie współczynniki wypełnienia nie są równe(po 50%)?
    OCR1A=127; //Wartość porównania A (Wyjście OC1A - PB1), wypełnenie = 33%
    OCR1B=127; //Wartość porównania B (Wyjście OC1B - PBB), wypełnenie = 66%

    OdpowiedzUsuń
    Odpowiedzi
    1. Niee wiem to i tak się zmienia potem w kodzie . Nie rozumiem wzoru na sinus.

      Usuń
  29. Witam! Chciałem sobie uruchomić PWM w Atmedze8 8bitowy działał bezproblemu dioda LED zmieniała jasność ale kiedy chciałem użyć trybu 14 TOP ICR1 zero reakcji nawet oscyloskopem sprawdzałem nic a kod jest dobry tak samo ustawiłem jak w katalogu porównałem z twoim te same bity i rejestry na bank dobrze a jednak nie działa. O co może chodzić?

    OdpowiedzUsuń
  30. Czytając pana artykuł moją uwagę przykuł "dziwny" kształt przebiegu piłokształtnego. Niby widziałem podstawowe przebiegi komercyjnych syntezatorów i bywały dalekie od ideału, ale pana wytłumaczenie takiego stanu rzeczy mnie nie przekonało. Winą za tak niesymetryczny kształt obarczył pan filtr na wyjściu sugerując, że ograniczenie górnej części widma tak właśnie wpłynie na sygnał. I tu pojawiło się pierwsze pytanie: skoro ograniczyliśmy wysokie częstotliwości to czemu sygnał "zaokrąglił się" tylko z "jednej strony"? Bawiłem się filtrami na różnych przebiegach patrząc w oscyloskop i czegoś takiego nie zaobserwowałem. Wówczas uświadomiłem sobie, że przez większą część artykułu pisał pan o konieczności zastosowania sprzętowego filtra dolnoprzepustowego. Padło więc pytanie drugie: jak filtrowanie sygnału PWM filtrem o częstotliwości odcięcia dużo wyższej od częstotliwości pierwszej harmonicznej tego sygnału miałoby doprowadzić do uzyskania sinusoidy? Uświadomiłem sobie wówczas, że podświadomie cały czas ów filtr traktowałem jako układ całkujący - w takim wypadku "matematyka by się zgadzała" (tak na moje niewprawne oko) i nawet jakiś fragment pamięci o całkowaniu PWM w falownikach zaczął świtać. Z tym, że prawdziwy kondensator przecież w końcu by się zaczął nasycać co tłumaczyłoby zaokrąglenie tylko po jednej stronie. I w sumie prawie bym na tym skończył gdybym nie przypomniał sobie, że układ całkujący można byłoby w prosty sposób zrealizować na wzmacniaczu operacyjnym. Pojawiło się więc trzecie pytanie: czy stosując takie rozwiązanie otrzymałbym sygnał bliższy oczekiwanemu? Oraz czwarte: czemu w ogóle miałby być inny skoro oba układy "całkują" i są oparte na kondensatorze (który tak czy inaczej pewnie się nasyci)? Zajrzałem do Wikipedii, a tam pod hasłem "Układ całkujący" znalazłem cytuję:
    "W żargonie elektronicznym układem całkującym bywa nazywany układ inercyjny pierwszego rzędu złożony z rezystora i kondensatora (...). Układ RC zachowuje się zatem jak układ całkujący jedynie w sytuacji gdy kondensator jest rozładowany"
    Biorąc pod uwagę powyższe, doszedłem do wniosku, że na potrzeby artykułu dokonał Pan skrótu myślowego (co przyznam pozwoliło mi się skupić na programowej implementacji przetwornika i nie rozpraszać się niepotrzebnie nad matematyką) i w rzeczywistości niesymetryczny kształt wynika z faktu zastosowania układu, który całkuje „w obie strony”. Dlatego chciałbym zapytać czy mój tok myślenia, który tak rozwlekle tu przytoczyłem jest właściwy?
    Pozdrawiam,
    Janusz

    ps.
    Tu gdzie jestem dostęp do internetu jest problematyczny więc przepraszam za bałagan (zdaje się, że za pierwszym razem wysłałem ten tekst nie tym formularzem co powinienem).

    OdpowiedzUsuń
    Odpowiedzi
    1. Pomyłka - miało być "niesymetryczny kształt wynika z faktu zastosowania układu, który całkuje tylko w jedną stronę".
      No i inna kwestia, że całkowanie stałej wartości napięcia da nam "przyrost liniowy podczas gdy szybkie opadanie (przy przejściu z jednego stanu w drugi) da nam hiperbolę i wówczas przebieg będzie wyglądał dokładnie jak na oscyloskopie ("rampa w górę" po czym hiperbola w dół i płynne przejście znów do rampy), a zatem mamy pełne całkowanie mimo zastosowania samego tylko kondensatora... za dużo tego na mój niematematyczny umysł - naprawdę będę wdzięczny za wyjaśnienie.

      Pozdrawiam,
      Janusz

      Usuń
    2. Chyba już pojąłem.

      Kondensator całkuje tylko gdy jest rozładowany więc działanie jest zbliżone do całkowania tylko dla napięć bliskich 0. Zgaduję, że nasze czasy ładowania i rozładowania są na tyle szybkie, że nie mam mowy o całkowitym naładowaniu więc możemy przyjąć, że następuje efekt całkowania. Gdybyśmy dysponowali oscyloskopem o wyższej rozdzielczości pewnie zobaczylibyśmy, że nasz sinus na wyjściu jest mocno "ząbkowany" (tak jak wyjście na niektórych falownikach - tych opartych na PWM właśnie – o których już wspominałem).
      Mój błąd polegał na tym, że zapomniałem iż nie całkujemy PWM żeby dostać inny przebieg o tej samej częstotliwości (tak jak sugeruje to artykuł). Stąd oczekiwałem innego zachowania się przebiegu na wyjściu.
      Wciąż nie jestem przekonany, iż filtr dolnoprzepustowy "zaokrągliłby" tylko jedną krawędź przebiegu. https://en.wikipedia.org/wiki/Sawtooth_wave - w tym artykule można zaobserwowcać jak tworzy się "piła" poprzez dodawanie kolejnych harmonicznych i taka asymetria nie pojawia się na żadnym etapie. Na moje oko to wynik zwykłego rozładowania kondensatora gdy współczynnik wypełnienia jest stosunkowo mały (PWM=0 przez więcej niż 95% cyklu), który jest doładowywany w sposób "liniowy" (liniowa zmiana współczynnika wypełnienia) za pomocą PWM właśnie. Czyli efekt filtracji (jak również całkowania) dużego skoku. Nasza „piła” jest niesymetryczna bo stanowi efekt działania układu opóźniającego jakim jest kondensator, a nie dlatego również została „odfiltrowana” (bo nie została nawet częściowo poddana filtracji).
      W ogóle cała część o „wygładzaniu” należałoby ją poprawić. Zwłaszcza, że stwierdza wprost iż „wygładzamy” PWM podczas gdy otrzymany sygnał ma częstotliwość znacznie niższą niż pierwsza harmoniczna tego PWM (np. możemy uzuskać sinusa o częstotliwości zarówno kilku jak i kilkuset tyśięcy hertzów bez zmiany częstotliwości PWM).

      Pozdrawiam
      Janusz

      Usuń
  31. Dlaczego nie mogę pobrać kodu programu ??

    OdpowiedzUsuń