Mabog10 Napisano Grudzień 20, 2011 Udostępnij Napisano Grudzień 20, 2011 Witam, piszę program wykorzystujący przetwornik ADC. Do pinów PC0 i PC1 mam podpięte fototranzystory. Program ma działać tak, ze po poświeceniu na lewy czujnik serwo obraca się w lewo, a na prawy w prawo. Jeśli wykorzystuję tylko jeden z kanałów, to wszystko ładnie działa, ale nie mam pomysłu co zrobić, żeby jakoś zsynchronizować dwa czujniki. Tak wygląda kod źródłowy programu na chwilę obecną. Kiedy go załączam i poświecę na któryś z czujników serwo zaczyna tylko drgać (nie wie w którą stronę się obrócić). Co mogę zrobić, żeby to w miarę płynnie działało? Próbowałem wprowadzić opóźnienia, ale to nic nie daje(albo zrobiłem to nieumiejętnie). Możliwe że trzeba coś pokombinować z obsługa przerwań, ale nie wiem za bardzo jak się za to zabrać. #define F_CPU 1000000L #include<avr/io.h> #include<util/delay.h> unsigned int pomiar; //zmienna do przechowywania wyniku pomiaru ADC (wartosc od 0-1023) int main(void) { DDRB=0x02; // , ustalenie pinu PB1 (wyjscie od serwa) DDRC=0x00; //ustalenie portu C jako wejscie TCCR1A=0x80; //konfiguracja licznika TCR1, wygenerowanie PWM dla serwa TCCR1B=0x11; ICR1=10000; //ustalenie sygnalu dla serwomechanizmu OCR1A=750; ADMUX=_BV(REFS0)|_BV(ADLAR); // wybor napiecia referencyjnego ADC i korygowania do lewej strony ADCSRA=_BV(ADEN)|_BV(ADFR)|_BV(ADPS1); // aktywowanie ADC, tryb free running, ustalenie preskalera na 4 for(;;) { ADMUX=0x60; //ustawienie czujnika pinu PC0 do pomiaru ADCSRA|=_BV(ADSC); // start konwersji ADC pomiar=ADCH; if (pomiar > 0) OCR1A=1100; //ruch serwa w prawo else OCR1A=750; // serwo na środku ADMUX=0x61; //ustawienie czujnika Pinu PC1 do pomiaru if (pomiar > 0) OCR1A=400; //ruch serwa w lewo else OCR1A=750; // serwo na środku } } Link do komentarza Share on other sites More sharing options...
kling Grudzień 20, 2011 Udostępnij Grudzień 20, 2011 ADMUX can be safely updated in the following ways: 1. When ADFR or ADEN is cleared. 2. During conversion, minimum one ADC clock cycle after the trigger event. 3. After a conversion, before the Interrupt Flag used as trigger source is cleared. When updating ADMUX in one of these conditions, the new settings will affect the next ADC conversion. Myślę, że to jest Twój głowny problem. Na początek polecam wyłączyć Freeruning mode i ręczenie wyzwalać pomiar, wtedy kiedy to jest potrzebne;) Dodatkowo warunek pomiar>0 nie jest szczęśliwym rozwiązaniem. Zawsze będą występowały jakies szumy i w 95% przypadków będzie on spełniony. Lepiej doświadczalnie sprawdzić jakie napięcie daje oświetlony element i ustawić adekwatny do tego próg. Link do komentarza Share on other sites More sharing options...
Barto Grudzień 20, 2011 Udostępnij Grudzień 20, 2011 Widzę że kolega mnie trochę ubiegł. Sprawdź najpierw jakie faktycznie dostajesz pomiary (wyświetl na ledach albo wyślij po RS) i daj na pewno większe wartości. Polecam też napisać sobie pożądaną prostą bibliotekę od pomiarów ADC. Takie ręczne sterowanie startem i zakończeniem konwersji (sprawdzaj programowo rejestr zakończenia) może się właśnie nieciekawie kończyć. Przy takim preskalerze pomiary na ADC masz o wiele szybsze (rzędy wielkości) od sygnałów sterujących serwem (Tp≈20ms) Link do komentarza Share on other sites More sharing options...
kling Grudzień 20, 2011 Udostępnij Grudzień 20, 2011 Nie wiem czy dobrze rozumiem Twoj post. Mnie chodziło o coś w tym stylu (kod na Mege32): ADMUX = (ADMUX & 0xE0) | CH; //wybor kanału ADCSRA|=(1<<ADSC); //start pomiaru while(!(ADCSRA & (1<<ADIF))); //oczekiwanie na koniec ADCSRA|=(1<<ADIF); // dla pewności:) pomiar = 1.27 * ADC;//oczyt wyniku Myślę ze to najlepszy pomysł dla początkującego.🙂 Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. 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
dondu Grudzień 20, 2011 Udostępnij Grudzień 20, 2011 Startujesz ADC, a już w nastepnej instrukcji odczytujesz pomiar. Problem polega na tym, że ADC jeszcze nie zdążył niczego zmierzyć 🙂 ADCSRA|=_BV(ADSC); // start konwersji ADC pomiar=ADCH; I teraz musisz najpierw zastanowić się, czy możesz czekać w pętli na zakończenie pomiaru, czy też jest dla Ciebie istotne, by mikrokontroler robił coś innego w tym czasie. Jeżeli to pierwsze to wystarczy że będziesz sprawdzał flagę ADSC. Jeżeli to drugie to musisz napisać program inaczej i/lub wykorzystać przerwania. Tutaj znajdziesz przykłady w 5 artykułach o ADC dla Atmega8 czyli Twojego mikrokontrolera: http://mikrokontrolery.blogspot.com/2011/03/drzaskowy-pamietnik-wstep.html Link do komentarza Share on other sites More sharing options...
Mabog10 Grudzień 20, 2011 Autor tematu Udostępnij Grudzień 20, 2011 Rzeczywiście napisanie biblioteki jeszcze przerasta moje umiejętności programowania, ale dzięki za wskazówkę. Rozwiązanie klinga postaram się wypróbować jutro, napiszę z jakim skutkiem. Co do pomiar>0 , to ustawiłem taką wartość tylko do testów, wyskaluję to później odpowiednio. W każdym razie, jak używałem jednego czujnika i na niego świeciłem, serwo skręcało, kiedy gasiłem latarkę zawsze wracało na pozycję neutralną, także myślę że akurat to nie jest największym problemem w tej chwili. @dondu - dzięki za link, poczytam sobie Link do komentarza Share on other sites More sharing options...
dondu Grudzień 21, 2011 Udostępnij Grudzień 21, 2011 Rozwiązanie kol. kling działać będzie prawidłowo, choć jest niepotrzebnie skomplikowane, ponieważ wykorzystując flagę ADIF, musi ją obowiązkowo kasować "ręcznie". ADIF is cleared by hardware when executing the corresponding interrupt Handling Vector. Alternatively, ADIF is cleared by writing a logical one to the flag. co kol. kling oczywiście robi. Zamiast tego wystarczy sprawdzać ustawioną wcześniej flagę ADSC: ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji pomiar=ADCH; Takie rozwiązanie jest opisane w pierwszym artykule z linku podanego wyżej. Można także nie czekać, tylko działać w pętli głównej, a za pomocą IF() oddzielić tę część, która ma się wykonać po zakończeniu pomiaru. Ale można też od razu zrobić porządnie, czyli wykorzystać przerwanie i zwolnić mikrokontroler do innych zadań, co i tak niechybnie będziesz musiał zrobić budując swojego robota - spróbuj bo warto - takie przykłady też tam znajdziesz. Link do komentarza Share on other sites More sharing options...
Mabog10 Grudzień 21, 2011 Autor tematu Udostępnij Grudzień 21, 2011 Na chwilę obecną zrobiłem takie coś(dzięki uwagom dondu): for(;;) { ADMUX=0x60; //ustawienie czujnika pinu PC0 do pomiaru ADCSRA|=_BV(ADSC); // start konwersji ADC while(ADCSRA & (1<<ADSC)); pomiar=ADCH; if (pomiar > 0) OCR1A=1100; //ruch serwa w prawo ADMUX=0x61; //ustawienie czujnika Pinu PC1 do pomiaru ADCSRA|=_BV(ADSC); // start konwersji ADC while(ADCSRA & (1<<ADSC)); pomiar=ADCH; if (pomiar > 0) OCR1A=400; //ruch serwa w lewo } W tym rozwiązaniu serwo obraca się w lewo lub w prawo w zależności na który czujnik poświecę, ale nie wraca do pozycji neutralnej i czasami zaczyna wariować. Poczytam o przerwaniach i spróbuję jeszcze zmodyfikować swój program. Link do komentarza Share on other sites More sharing options...
kling Grudzień 21, 2011 Udostępnij Grudzień 21, 2011 A która linijka w programie miałaby być odpowiedzialna za ustawienie serwa w pozycji neutralnej? Chyba, że ja już oślepłem zupełnie:) Link do komentarza Share on other sites More sharing options...
Mabog10 Grudzień 22, 2011 Autor tematu Udostępnij Grudzień 22, 2011 Nie oślepłeś, w tej wersji nie ma takiej linijki 😉 Nie mam pomysłu jak ją wstawić, żeby było dobrze, bo jeśli dam taką instrukcję np. przy pomiarze z PC0, to gdy świecę na czujnik PC0 jest ok(serwo idzie w lewo), ale gdy świecę na drugi czujnik(PC1), to jest informacja z tego czujnika, że serwo ma pójść w prawo, ale prawie jednocześnie jest informacja z PC0, ze tam nie ma sygnału i przez to serwo idzie na środek(oscyluje: prawo - środek). Link do komentarza Share on other sites More sharing options...
Bobby Grudzień 22, 2011 Udostępnij Grudzień 22, 2011 Mabog10, a może jeśli wartość na obu czujnikach będzie powyżej pewnego progu (i różnica między nimi będzie niewielka) to niech ustawia serwo w pozycji neutralnej. Link do komentarza Share on other sites More sharing options...
kling Grudzień 23, 2011 Udostępnij Grudzień 23, 2011 ale nie wraca do pozycji neutralnej Jeżeli ma wracać gdy nie świecisz na żadnej czujnik, za każdym ifem dodaj instrukcje 'else' a po niej instrukcję ustawiającą serwo w stan neutralny. To o czym pisze Bobby to właśnie wariowanie Twojego serwa. ono nie wie, co ma zrobić, gdy oba czujniki zbierają pomiary. Natomiast jeżeli ma ogarniać sygnał z obu czujników naraz to, myślę, że warto na początek zrobić to tak: 1. Zapisać pomiar z 1 czujnika do jednej zmiennej 2. Zapisać pomiar z 2 czujnika do drugiej zmiennej 3. W if'ach porównać te zmienne z uwzględniem pewnej histerezy i ustawić serwo 4. W elsie , jeżeli różnica między czujnikami będzie znajdowała sie z pasmie histerezy, ustawić serwo na stan neutralny. 1 Link do komentarza Share on other sites More sharing options...
Mabog10 Grudzień 23, 2011 Autor tematu Udostępnij Grudzień 23, 2011 Jeśli chodzi o to "else" po instrukcji, to oczywiście działa, już to testowałem, ale tylko dla pojedynczego czujnika. Dla dwóch czujników serwo wariuje, z przyczyn które podałem w poprzednim poście. Myślę, że zastosowanie dwóch zmiennych jest dobrym pomysłem, wypróbuję ten sposób(ale niestety dopiero po Nowym Roku, w tej chwili nie mam dostępu do swojego sprzętu). Jeśli nadal nie będzie dobrze, to zastosuję jeszcze trzeci czujnik, który zamontuję pomiędzy tymi dwoma które już są i wtedy on będzie odpowiadał za ustawienie serwa w pozycji neutralnej. Jak nie poświecę na żaden, to nie załączy się silnik napędzający robota i nie ma znaczenia w jakiej pozycji będzie znajdować się serwo. W każdym razie, jak przetestuje rozwiązanie kolegi klinga, to podzielę się swoimi spostrzeżeniami. Pomysł Bobby'ego też jest dobry - to czwarty punkt rozwiązania klinga W międzyczasie zamieszczam link do filmu obrazującego działanie pojedynczego czujnika: Ok, nareszcie znalazłem czas, aby przetestować rozwiązanie klinga. Wszytko ładnie działa, tak jak chciałem. Po modyfikacji kod wygląda następująco: for(;;) { ADMUX=0x60; //ustawienie czujnika pinu PC0 do pomiaru ADCSRA|=_BV(ADSC); // start konwersji ADC while(ADCSRA & (1<<ADSC)); pomiar1=ADCH; ADMUX=0x61; //ustawienie czujnika Pinu PC1 do pomiaru ADCSRA|=_BV(ADSC); // start konwersji ADC while(ADCSRA & (1<<ADSC)); pomiar2=ADCH; if (pomiar1>pomiar2) OCR1A=1100; // ruch w prawo else if (pomiar1<pomiar2) OCR1A=400; //ruch w lewo else OCR1A=750; // pozycja neutralna } Oczywiście program można jeszcze nieco ulepszyć, ale na chwilę obecną wynik mnie satysfakcjonuje. Link do filmu(z dwoma czujnikami): Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »