www.leniwiec.org

W drugiej części tego minicyklu chciałbym Wam przedstawić różne metody wyprowadzania danych cyfrowych z mikrokontrolera w postaci napięcia proporcjonalnego do zdigitalizowanej wartości liczbowej.
Mamy tutaj kilka możliwych rozwiązań realizacji naszego DAC (Digital Analog Converter – Konwerter Cyfrowo Analogowy). Weźmy zatem na pierwszy rzut modulację PWM.

PWM (Puls Width Modulation)

PWM (Puls Withd Modulation), czyli modulacja szerokości impulsu, to nic innego jak stały przebieg prostokątny o zmiennym wypełnieniu od 0% do 100%. Jeśli ktoś nie wie jak wygląda taki przebieg, to poniżej może zobaczyć kilka oscylografów (jeśli tak można nazwać te kiepskie fotki), które powinny rozwiać wątpliwości. Aby jednak wykorzystać taki przebieg w naszej aplikacji, musimy go najpierw scałkować… Bez obawy! Nie będziemy tutaj przeprowadzać karkołomnych obliczeń matematycznych, my naszą całkę zrobimy przy pomocy filtru RC.

Oscylografy przebiegów PWM

Oscylografy przebiegów PWM

Zacznijmy od wygenerowania takiego przebiegu przy pomocy mikrokontrolera. Ja użyję tutaj atmega8, jednak nie ma to większego znaczenia (może to być z powodzeniem inna atmega).

Atmega8 jest wyposażona w 3 kanały pwm (2 podpięte pod 16bitowy Timer1 oraz 1 na 8bitowym Timer2). Nas interesują te dwa kanały na 16 bitowym liczniku / tajmerze, ponieważ tylko one pozwalają (w przypadku atmegi8) na generowanie sygnału o rozdzielczości 10bitów.

Dostęp do timera 1 zapewnia nam kilka rejestrów, do których mamy dostęp, nas jednak interesować będą tylko: TCCR1A , TCCR1B oraz OCR1A/B. Rejestry TCCR1A/B są to rejestry kontrolne, w których ustawiamy parametry timera, które nas interesują, natomiast OCR1A/B jest rejestrem porównawczym, który jest ciągle porównywany (w każdym cyklu timera) z aktualną wartością timera. Mówiąc prostymi słowy, jeśli nasz licznik naliczy tyle, ile mamy zapisane w OCR1A/B, wówczas __coś__ się stanie… w naszym przypadku, wartość ta będzie ustalała wartość wypełnienia przebiegu wyjściowego.

TCCR1A

Przyjrzyjmy się teraz bliżej rejestrom kontrolnym. Zgodnie z dokumentacją jest on zbudowany jak na poniższym rysunku:

Bity COM1A1, COM1A0, COM1B1, COM1B0 służą do ustawiania trybu porównywania wyjścia licznika, a ich dokładny opis można znaleźć w tabelce poniżej:

FOC1A/B są wykorzystywane tylko w trybach niezwiązanych z PWM tak że pomińmy je sobie teraz.
Dalej mamy WGM11 oraz WGM10 (w starszych układach było to odpowiednio PWM11 oraz PWM10), a także WGM12 i WGM13, które znajdują się już w rejestrze TCCR1B, służą one do ustawiania trybu pracy wyjścia. Poniższa tabelka przedstawia dostępne opcje.

Nas będzie interesował tryb numer 3, czyli PWM, Phase Correct, 10-bit (ustawione bity WGM10 i WGM11). W tym trybie będziemy mieli do dyspozycji 10 bitowy PWM z korekcją fazy. Co to oznacza? Oznacza to tyle, że nasz przebieg będzie mógł mieć 1024 (10bit) różne wartości wypełnienia oraz będzie pracował w trybie korekcji fazy, który polega na tym, że timer zlicza od zera do góry, a następnie z powrotem z góry do zera, przy czym zeruje się, gdy jego wartość jest równa wartości zapisanej w rejestrze OCR1A/B. Dzieje się tak w obydwu kierunkach – podczas zliczania w górę, jak i w dół. W rezultacie na wyjściu otrzymujemy bardziej symetryczny przebieg. Częstotliwość tak generowanego sygnału jest niestety około dwukrotnie mniejsza niż w przypadku trybu fast-pwm, co wynika z konieczności zliczania w obu kierunkach.

TCCR1B

Rejestr TCCR1B jest zbudowany tak, jak pokazuje poniższy rysunek.

Bity ICNC1 oraz ICES1 służą odpowiednio do włączania redukcji szumów wejściowych na nóżce oznaczonej jako ICP1 oraz do określenia zbocza, które aktywuje ICP1. Nie będą one jednak przez nas wykorzystywane.

Bit 5 nie służy do niczego ;-) Jest on po prostu zarezerwowany dla przyszłych ficzersów.

Bity WGM12 oraz WGM13 służą do ustalania trybu pracy timera i są opisane w powyższej tabelce.

Bity CS10, CS11 oraz CS12 służą do ustawiania preskalera. Czyli dzięki nim jesteśmy w stanie ustawić częstotliwość, z jaką będzie pędził nasz generator PWM! Na poniższym rysunku przedstawiony jest sposób ich ustawienia oraz odpowiadające im wartości podziału częstotliwości zegara (clk i/o).

I to by było tyle, jeśli chodzi o te dwa rejestry kontrolne. W praktyce wszystko sprowadza się do wybrania trybu pracy (bity WGM*), trybu porównywania (bity COM*) oraz ustawienia preskalera (bity CS*).

Program testowy

Wyposażeni już w wiedzę na temat rejestrów odpowiedzialnych za kontrolowanie pracy timera zbudujmy sobie prosty układ testowy i napiszmy program testowy…

Poniżej schemat układu testowego (jest to właściwie ten sam układ co w części 1 artykułu)

Oraz nasz program testowy:


int PWMval = 0;

void init_dac()
{
	OCR1A = 0;
	TCCR1A = _BV(COM1A1)|_BV(COM1A0)|_BV(WGM10)|_BV(WGM11);
	TCCR1B = 1<<CS10;
}

int main()
{
 char buffer[7];

 init_encoder();
 init_lcd();
 init_dac();

  while(1)
  {
    read_encoder();
    if(tackt != NEUTRAL)
    {
      
		if(tackt == LEFT && PWMval > 0)
		{
			PWMval--;
		} 
		else if(tackt == RIGHT && PWMval < 1023)
		{
			PWMval++;
		}
        
		lcd_clrscr();
		itoa(PWMval, buffer, 10);
		lcd_puts(buffer);

		OCR1A = PWMval;

		tackt = NEUTRAL;
	} 
  }
}

Jak widać na powyższym kodzie, mamy procedurkę init_dac()I, która ustawia timer 1 w trybie 10bitowego PWM z korekcją fazy, dalej ustawiamy bity COM1A0 oraz COM1A1 tak, aby rejestr OC1A był ustawiany, kiedy wartość licznika, podczas zliczania w górę, osiągnie wartość zadaną z rejestru OCR1A, oraz kasowany, kiedy wartości te będą takie same podczas zliczania w dół.

Dalej ustawiamy bit CS10 w rejestrze kontrolnym TCCR1B, co odpowiada ustawieniu preskalera na 1 oraz rozpoczęcie pracy generatora.

W głównej pętli programu, podczas manipulowania enkoderem w lewo i prawo, wartość zmiennej PWMval jest odpowiednio inkrementowana oraz dekrementowana, a następnie przypisywana do rejestru OCR1A i tym samym ustawiana jest nowa wartość wypełnienia przebiegu wyjściowego.

I to tak naprawdę wszystko. Prawda, że proste? Raptem tylko 3 linijki dzielą nas od działającego 10bitowego PWM’a ;-)

Co dalej z prostokątem ?

Ok, potrafimy już wygenerować przebieg prostokątny o wypełnieniu od 0% do 100%, teraz potrzebujemy, aby ten sygnał zmienił się w napięcie proporcjonalne do jego wypełnienia. Aby tego dokonać, należy sygnał ów uśrednić. Do tego celu zastosujemy odpowiedni filtr dolnoprzepustowy RC.

Problem z tego typu filtrami polega na tym, że stosując zbyt małą pojemność (wysoka częstotliwość odcięcia), przebieg na wyjściu nie nadaje się jako źródło napięcia odniesienia czy też napięcia sterującego cokolwiek, a z drugiej strony, zwiększając wartość C (niska częstotliwość odcięcia), zwiększa się czas propagacji, czyli czas, który upłynie pomiędzy przekręceniem gałką a pojawieniem się żądanego napięcia na wyjściu. Aby zniwelować w pewnym stopniu tę niedogodność, możemy zastosować filtry aktywne. Filtr taki jest zbudowany na wzmacniaczu operacyjnym, który ma bardzo dużą rezystancję wejściową, co dodatkowo jest nam na rękę, ponieważ podłączając wyjście naszego filtra RC (pasywnego) do odbiornika, którego rezystancja wejściowa jest niewielka, odbiornik ten rozładowuje w pewnym stopniu kondensator C, co negatywnie wpływa na stabilność całego budowanego przetwornika DAC.

Częstotliwość odcięcia dla filtru pokazanego na powyższym rysunku jest określana następującym wzorem:

Łącząc takie filtry w szereg, zwiększamy nachylenie filtru, tworząc filtr n-rzędu, jednak wówczas sygnał jest odpowiednio tłumiony (np. dla filtru 2go rzędu sygnał jest tłumiony dwa razy o 6db).
Nie chciałbym tutaj się rozwodzić na temat filtrów, ponieważ jest to temat bardzo obszerny i ten artykuł mógłby urosnąć do gigantycznych rozmiarów… Niemniej jednak, aby nie pozostawiać Wam niedosytu w tym temacie, polecam pobrać darmowy program Filter Pro wydany przez texas instruments i poeksperymentować w nim z różnego rodzaju filtrami.
Program ten jest dostępny pod tym linkiem: http://www.ti.com/tool/filterpro?DCMP=hpa_amp_general&HQS=NotApplicable%252bOT%252bfilterpro

Parametry

Wiemy już, jak zrealizować prosty przetwornik cyfrowo-analogowy przy wykorzystaniu pwm. Spróbujmy sobie teraz przeanalizować jego parametry, możliwości ich poprawy oraz przydatność tego rozwiązania w naszej aplikacji.

Wbrew pozorom parametry tak wykonanego DACa mogą być całkiem przyzwoite, a wyjścia portów I/O, zresztą tak jak cały kontroler, są wykonane w technologi CMOS, dzięki czemu w stanie wysokim na wyjściu pojawia się praktycznie napięcie zasilania, natomiast w stanie niskim zero.
Wykorzystując tę właściwość można zastosować popularną technikę zwiększającą precyzję tak generowanego sygnału, która polega na zasilaniu całego mikrokontrolera ze źródła napięcia odniesienia. Dzięki czemu otrzymywany na wyjściu sygnał jest całkiem niezłej jakości.
Nie chcę tutaj jednak wchodzić w szczegóły i rzucać konkretnymi danymi liczbowymi, ponieważ tak zbudowany przetwornik ma jedną zasadniczą wadę, która dyskryminuje go całkowicie w naszym zastosowaniu. Jest nią nieakceptowalnie długi czas ustalania średniego napięcia na wyjściu filtra dolnoprzepustowego podłączonego do wyjścia generatora PWM.
W przypadku gdyby nasz zasilacz korzystał z histerezy wykonanej przy pomocy wzmacniaczy operacyjnych, a kontroler służyłby tylko do zadawania odpowiednich napięć referencyjnych dla ustalenia napięcia i prądu wyjściowego, takie rozwiązanie byłoby akceptowalne. Niestety czas ustalania, nawet wykorzystując aktywne filtry dolnoprzepustowe, jest znacznie za długi i w przypadku zadziałania ogranicznika prądu lub przy skokach napięcia, zanim zdążyłoby się ustabilizować napięcie wyjściowe, podłączony do naszego zasilacza układ mógłby się upiec.

Podsumowanie

Niemniej jednak, polecam wszystkim eksperymenty z PWM, gdyż jest to technika bardzo szeroko wykorzystywana w różnych aplikacjach i na pewno przyda się Wam jeszcze nieraz!
W następnym odcinku postaram się zaprezentować rozwiązanie DAC’a oparte o drabinkę rezystorową R2R, pokażę Wam, jak zrealizować 16bitowy przetwornik oparty na r2r, wykorzystując tylko 3 piny procesora, a do tego nie będzie to kosztowało więcej niż 2 zł :-)

Jako że zbliża się Nowy Rok, a niestety przed Świętami czasu na pisanie nie było, życzę Wam wszystkim (tak, tak, Wam, spam-boty nękające mnie codziennie – też!!), wszystkiego najlepszego w nadchodzącym nowym roku numer 0x7DC! (miejmy nadzieję, że nie będzie on ostatnim..)

Stay tuned!

2 KOMENTARZY
Darek
17 lipiec 2014
ad

Jestem zielony w tych sprawach ale chciał bym jakoś wykonać taki układ jak PT8211S od podstaw,to znaczy opornik po oporniku,tranzystor po tranzystorze,posiada 8nóżek chciał bym to zrobić na płytce o rozmiarach 15cm na 15 jeśli to możliwe to prosił bym o pomoc,dziękuje

Darek
17 lipiec 2014
ad

Jestem zielony w tych sprawach ale chciał bym jakoś wykonać taki układ jak PT8211S od podstaw,to znaczy opornik po oporniku,tranzystor po tranzystorze,posiada 8nóżek chciał bym to zrobić na płytce o rozmiarach 15cm na 15 jeśli to możliwe to prosił bym o pomoc,dziękuje .

Wyślij