|
[Atmega8] Problem ze zmiennym okresem dzialania pętli |
| Autor |
Wiadomość |
Serwik


Dołączył: 10 Mar 2010 Posty: 4 Skąd: Warszawa
|
Wysłany: 10-03-2010, 23:08 [Atmega8] Problem ze zmiennym okresem dzialania pętli
|
|
|
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: | #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


Dołączył: 10 Mar 2010 Posty: 4 Skąd: Warszawa
|
Wysłany: 11-03-2010, 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 |
|
|
|
 |
Tomasz Gumny

Dołączył: 04 Lut 2004 Posty: 141 Skąd: Trzcianka/Poznań
|
Wysłany: 11-03-2010, 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. |
_________________ TomG |
|
|
|
 |
pajaczek
Moderator Ulubieniec Lloth



Dołączył: 24 Sty 2005 Posty: 1973 Skąd: Winny grod
|
Wysłany: 11-03-2010, 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). |
_________________ NASA wydala miliony $ na zaprojektowanie dlugopisu, ktory mozna by uzywac w przestrzeni kosmicznej...
... Rosjanie uzywaja olowkow. |
|
|
|
 |
Serwik


Dołączył: 10 Mar 2010 Posty: 4 Skąd: Warszawa
|
Wysłany: 11-03-2010, 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
| Kod: | #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);
}
} |
|
|
|
|
 |
pajaczek
Moderator Ulubieniec Lloth



Dołączył: 24 Sty 2005 Posty: 1973 Skąd: Winny grod
|
Wysłany: 11-03-2010, 23:00
|
|
|
| Serwik napisał/a: | | 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. |
_________________ NASA wydala miliony $ na zaprojektowanie dlugopisu, ktory mozna by uzywac w przestrzeni kosmicznej...
... Rosjanie uzywaja olowkow. |
|
|
|
 |
j_bravo
J_Bravo



Wiek: 32 Dołączył: 14 Wrz 2005 Posty: 164 Skąd: Lublin
|
|
|
|
 |
Serwik


Dołączył: 10 Mar 2010 Posty: 4 Skąd: Warszawa
|
Wysłany: 14-04-2010, 21:32 PWM na atmega162
|
|
|
Troszkę się męczę z uruchomieniem PWM
| Kod: | #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? |
|
|
|
 |
Tomasz Gumny

Dołączył: 04 Lut 2004 Posty: 141 Skąd: Trzcianka/Poznań
|
Wysłany: 15-04-2010, 11:13 Re: PWM na atmega162
|
|
|
| Załączenie trybu PWM chyba "nadpisuje" standardowe rejestry portu, ale na wszelki wypadek ustaw PD4 jako wyjście: DDRD=0x10; |
_________________ TomG |
|
|
|
 |
|
|