Aktyw Forum

Zarejestruj się na forum.ep.com.pl i zgłoś swój akces do Aktywu Forum. Jeśli jesteś już zarejestrowany wystarczy, że się zalogujesz.

Sprawdź punkty Zarejestruj się

[Atmega8] Problem ze zmiennym okresem dzialania pętli

Serwik
-
-
Posty: 4
Rejestracja: 10 mar 2010, o 22:32
Lokalizacja: Warszawa

[Atmega8] Problem ze zmiennym okresem dzialania pętli

Postautor: Serwik » 10 mar 2010, o 23:08

Próbowałem zrobić prosty program do sterowania serwem (Hitec HS311). Kod miał działać tak aby generować na wyjściu D procesora impuls o określonej szerokości a po nim 0 tak aby okres (impuls i zero) był stały (period=20ms)
Impuls jest sterowany przez zmienną t, która waha się w przedziale od 0.6 do 2.4ms co jest zgodne ze specyfikacja serwa i powinno powodować jego wychylenie od -90 do 90 stopni. t jest zwiększane i zmniejszane o stałą offset (tu: 0.05)
Problem polega na tym ze sygnal wyjsciowy na D jest inny niz zaprogramowany.

Kod: Zaznacz cały

#define F_CPU 4000000L #include <avr/io.h> #include <util/delay.h> int main(void) { double t = 1.5; double offset = 0.05; double period = 20.0; /* Wszystkie linie portu D będą wyjściami */ DDRD = 0xFF; (1) //t = t + offset; <--- INSTRUKCJA DODAWANIA W TYM MIEJSCU DZIALA POPRAWNIE /* Początek nieskończonej pętli */ while(1) { if ((t<=0.6)||(t>=2.4)) offset = -offset; PORTD = 0xAA; //1010 1010 _delay_ms(t); PORTD = 0x55; //0101 0101 _delay_ms(period-t); (2) t = t + offset; <--- TA INSTRUKCJA ZMIENIA OKRES PETLI } }
Wg powyższego programu okres sygnału powinien trwać równo 20ms. Niestety trwa on dłużej a zmienna t (impuls na wyjściu) nie przyjmuje wartości z przedziału 0.6-2.4 tylko ok 1.5-3.3. Problem znika gdy w pętli zakomentuje się instrukcje (2). t jest wówczas niezmienne i wynosi 1.5 co idealnie widać na oscyloskopie a serwo ustawia sie w pozycji 0. Zmiana wartości t na 0.6 skutkuje przechyleniem serwa o -90stopni a 2.4 o +90 (i to jest OK)
Program prawidłowo dodaje offset w miejscu (1). Po odkomentowaniu instrukcji (1) t zostaje zwiększone prawidłowo.
Problem pojawia się natomiast gdy t jest zmieniane w pętli np. w miejscu (2). Wówczas występuje to "przesunięcie" i rozciągniecie okresów.
Procesor ustawiony jest na zewnętrzny kwarc o częstotliwości 4MHz.
Stawiam pierwsze kroki w programowaniu AVR i będę wdzięczny za wyjaśnienie tej anomalii :)

Serwik
-
-
Posty: 4
Rejestracja: 10 mar 2010, o 22:32
Lokalizacja: Warszawa

Postautor: Serwik » 11 mar 2010, o 19:16

No prosze znalazlem odpowiedz w delay.h
\note In order for these functions to work as intended, compiler
optimizations <em>must</em> be enabled, and the delay time
<em>must</em> be an expression that is a known constant at
compile-time. If these requirements are not met, the resulting
delay will be much longer (and basically unpredictable), and
applications that otherwise do not use floating-point calculations
will experience severe code bloat by the floating-point library
routines linked into the application.

Okazuje sie ze _delay dziala poprawnie tylko gdy jako parametr poda sie wartość stałą. W innym przypadku funkcja dziala w sposob nieprzewidywalny :)

W zwiazku z tym pytanie:
Jak inaczej moge osiagnac efekt ktory zaplanowalem? Prosze o nakierowanie na jakies rozwiazania

Awatar użytkownika
Tomasz Gumny
-
-
Posty: 685
Rejestracja: 4 lut 2004, o 23:31
Lokalizacja: Trzcianka/Poznań

Postautor: Tomasz Gumny » 11 mar 2010, o 19:48

Napisz własną funkcję delay() - na przerwaniach lub po prostu w pętli. No i wyrzuć tą całą matematykę zmiennoprzecinkową - wystarczy wszystkie wartości przemnożyć przez 10.

Awatar użytkownika
pajaczek
Moderator
Moderator
Posty: 2650
Rejestracja: 24 sty 2005, o 00:39
Lokalizacja: Winny gród

Postautor: pajaczek » 11 mar 2010, o 19:52

A to ciekawe z dealy'em...

ale do rzeczy. A może by tak zastosować zwykły timer w trybie PWM ;) (zmieniając wypełnienie, osiągniesz właśnie swój efekt).

Serwik
-
-
Posty: 4
Rejestracja: 10 mar 2010, o 22:32
Lokalizacja: Warszawa

Postautor: Serwik » 11 mar 2010, o 21:46

Pożądany efekt uzyskałem w poniższy sposób. Niemniej delay'e są na tyle uciążliwe ze trzeba będzie sprawdzić co to jest to PWM :597:

Kod: Zaznacz cały

#define F_CPU 4000000L #include <avr/io.h> #include <util/delay.h> void my_delay_us(int us) { while(us>0) { us-=100; _delay_us(100.0); } } int main(void) { int t = 1500; int offset = 50; int period = 20000; /* Wszystkie linie portu D będą wyjściami */ DDRD = 0xFF; /* Początek nieskończonej pętli */ while(1) { if ((t<=600)||(t>=2400)) offset = -offset; t = t + offset; PORTD = 0xAA; my_delay_us(t); PORTD = 0x55; my_delay_us(period-t); } }

Awatar użytkownika
pajaczek
Moderator
Moderator
Posty: 2650
Rejestracja: 24 sty 2005, o 00:39
Lokalizacja: Winny gród

Postautor: pajaczek » 11 mar 2010, o 23:00

Niemniej delay'e są na tyle uciążliwe ze trzeba będzie sprawdzić co to jest to PWM
To trochę skrót myślowy, bo w klasycznym PWM (pulse width modulation) regulujesz jedynie szerokością (a dokładniej wypełnieniem) sygnału, zwykle puls zaczyna się w "zerze" okresu, lub wokół połowy okresu. Jednak w niektórych trybach jest możliwość ustalenie również momentu startu i końca "1", przy stałej szerokości okresu. W ATMega o ile pamiętam dostępne jest to w timerach 16 bitowych, wygląda to m/w tak, że licznik liczy od 0 do ustalonej wartości po osiągnięciu tej wartości się automatycznie zeruje. Są jeszcze 2 wartości, startu i stopu "1", pierwsza oczywiście niższa od drugiej. Doliczenie licznika do pierwszej ustawia powiązane wyjście, doliczenie do drugiej zeruje. Dzięki czemu możesz wysterować nie tylko szerokość impulsu, ale również moment startu i stopu. W niektórych rozwiązaniach możesz również odwrócić "0" z "1".
Mam nadzieję, że jasno opisałem, poszukaj w opisach timera. Jak nie znajdziesz postaram się odświeżyć nieco, bo dawno nie używałem tego trybu.

Aaa... będziesz miał jeszcze tą przewagę, że wszystko łatwo wykonasz w przerwaniach, a więc wykonywać się będzie niejako samo, bez uzależnienia od aktualnie wykonywanego fragmentu głównego programu.

Awatar użytkownika
j_bravo
-
-
Posty: 189
Rejestracja: 14 wrz 2005, o 16:30
Lokalizacja: Lublin
Kontaktowanie:

Postautor: j_bravo » 13 mar 2010, o 07:30

Fakt. Troszkę szkoda całego czasu procesora na generowanie impulsów. Może nie łatwiej, ale lepiej było by to zrobić na przerwaniu. Będzie czas na inne zadania np. miganie diodką ;P

Serwik
-
-
Posty: 4
Rejestracja: 10 mar 2010, o 22:32
Lokalizacja: Warszawa

PWM na atmega162

Postautor: Serwik » 14 kwie 2010, o 20:32

Troszkę się męczę z uruchomieniem PWM

Kod: Zaznacz cały

#include <avr/interrupt.h> #include <avr/io.h> int main(void) { PORTD=0x00; DDRD=0x00; //PWM, phase correct, 8-bit | Prescaler /8 TCCR3B = _BV(WGM30) | _BV(CS31); TCNT3 = 0x00; ICR3 = 20000; OCR3A = 1000; //sei(); while (1) { }; }
Ten prosty kod powinien (?) uruchomic timer 3 z sygnalem pwm na wyjsciu OC3A (Port PD4 na atmega162), jednak na wyjciu jest tylko 0. Czego tu brakuje?

Awatar użytkownika
Tomasz Gumny
-
-
Posty: 685
Rejestracja: 4 lut 2004, o 23:31
Lokalizacja: Trzcianka/Poznań

Re: PWM na atmega162

Postautor: Tomasz Gumny » 15 kwie 2010, o 10:13

Załączenie trybu PWM chyba "nadpisuje" standardowe rejestry portu, ale na wszelki wypadek ustaw PD4 jako wyjście: DDRD=0x10;

Wróć do „AVR/AVR32”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 28 gości