Skocz do zawartości

Atmega8, ADC - obługa dwóch wejść


Mabog10

Pomocna odpowiedź

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

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

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

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

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

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

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

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

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

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

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

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.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

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

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ę »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.