greebqmaster Napisano Wrzesień 11, 2013 Udostępnij Napisano Wrzesień 11, 2013 Witam, Postanowiłem zrobić linefollowera wg przepisu z waszej strony: Linefollower - kurs Wszystko IDEALNIE TAK SAMO podłączyłem, wgrałem kod bascomowy i niestety nie robot nie banglał ;_; Zabrałem się zatem do napisania własnego kodu w C, ponieważ w tym lepiej mi idzie, w celu sprawdzenia czy w ogóle hardware działa. Posprawdzałem miernikiem czy procek, cny70 i l293 działają i wszytko było super. Procek przez l293 dawał napięcie i silniczki jeździły na sztywnym kodzie. Posprawdzałem napięcie na czujnikach i zmieniało się w zależności biały/czarny kolor, czy zakrycie/odkrycie czujnika. #include <avr/io.h> // zalaczenie biblioteki obslugi uC AVR #include <util/delay.h> // zalaczenie biblioteki opoznien #include "hd44780.h" // zalaczenie biblioteki obslugi ekranu LCD #define cz_L PC5 // czujniki #define cz_S PC4 #define cz_P PC3 int granica=300; volatile uint8_t czl;//zmienna do pomiaru ADC z czujnika lewego volatile uint8_t czs;//zmienna do pomiaru ADC z czujnika srodkowego volatile uint8_t czp;//zmienna do pomiaru ADC z czujnika prawego int main(void) { ADCSRA = (1<<ADEN) //włączenie ADC |(1<<ADFR) //włączenie trybu Free run |(1<<ADSC); //rozpoczęcie konwersji ADMUX = (1<<ADLAR) //Wyrównanie wyniku do lewej |(1<<REFS0) //VCC jako napięcie referencyjne |(0<<MUX0); //Wybór wejścia początkowego czyli PC0 DDRC=0x00; // ustawienie wszystkich bitów portu C na wejścia DDRB=0xFF; // ustawienie wszystkich bitow portu B na wyjscia DDRD=0xFF; // ustawienie wszystkich bitów portu D na wyjścia for(;;) // petla glowna { for(int i=0;i<3;i++) // petla zczytywania wyników { if(i==0) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX2) | (1<<MUX0); czl=ADCH; } if(i==1) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX2); czs=ADCH; } if(i==2) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX1) | (1<<MUX0); czp=ADCH; } } //ustawianie silników w zależności od wyników if(czs < granica) // srodek { PORTB=0x06; PORTD=0x02; } else if (czl < granica) // prawy { PORTB=0x06; PORTD=0x0A; } else if (czp < granica) // lewy { PORTB=0x06; PORTD=0x08; } } } Oczywiście nie działa, bo po co w takim razie zakładałbym ten temat.. Jakiej bym granicy nie ustawił, to działa mi tylko lewy silnik. Doszedłem do wniosku, że to pewnie dlatego, że zacina mi się wszystko na prawym czujniku i coś się nie zeruje, ale przecież MUX'y są zerowane za każdym razem więc ręce mi opadają.. Poszperałem w internetach i w ogóle tam gdzie się dało chcąc uzyskać wiedzę, która pomogłaby mi w problemie, niestety bez skutku... Czy ktoś mógłby mi pomóc w znalezieniu ewentualnego błędu? Dziękuję za wszelkie odpowiedzi i pozdrawiam. greebqmaster PS różnicą w schemacie może być pomieszane podłączenie czujników - lewy PC5, środek PC4, prawy PC3. Schemat: __________ Komentarz dodany przez: Treker Poprawiłem znaczniki i grafiki. Proszę zapoznać się z regulaminem punkt 2)i. Cytuj Link do komentarza Share on other sites More sharing options...
MirekCz Wrzesień 11, 2013 Udostępnij Wrzesień 11, 2013 Pominąłeś fakt, że konwersja ADC trwa ileś czasu. Stąd Twoje czytanie wyników z ADCH jest błędne, ponieważ w najlepszym wypadku odczytasz trzy razy tą samą wartość (albo raz prawidłową z ostatniego pomiaru, a dwa razy coś losowego - zależy jak interpretuje to mikrokontroler). Po włączeniu ADC a przed odczytaniem wyniku musisz poczekać na koniec konwersji. Można to robić timerami itd, albo po prostu czytać odpowiedni bit i czekać tak długo aż konwersja się nie skończy. Kod z wymienionego bloga: while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji PS. Reszty Twojego kodu nie przeglądałem..., ale gdyby coś nadal nie działało polecam jeszcze raz dokładnie przejrzeć wszystkie tutoriale o ADC dla AVR na wymienionej stronie... Cytuj Link do komentarza Share on other sites More sharing options...
marek1707 Wrzesień 11, 2013 Udostępnij Wrzesień 11, 2013 Przetwornik ADC nie jest prostym okienkiem, przez które w postaci liczby widzisz napięcie na wybranym wejściu. To znaczy trochę tak, ale nie do końca. Nie wnikałem w część kodu inicjującą pracę przetwornika - ta pewnie jest dobra jeśli w ogóle dostajesz jakieś wyniki. Moim zdaniem powinieneś zastanowić się chwilę jak taki ADC działa. Otóż po pierwsze jego wejście musi być podłączone do odpowiedniego pinu procesora - to jasne. Po drugie przetwornik musi sprawdzić i zapamiętać w specjalnym kondensatorze (mówimy "spróbkować") napięcie na tym wejściu, po trzecie przetworzyć to napięcie na postać cyfrową i wreszcie po czwarte wysłać wynik do rejestrów ADCH i ADCL. Czy dajesz mu taką sznasę i czy Twój program podąża za cyklem pracy przetwornika? Chyba nie. Wysyłasz jakiś numer wejścia do MUX a chwilę później odczytujesz coś z rejestru ADCH. Czy spodziewasz się, że jest tam coś sensownego? Owszem, tam będzie wynik którejś poprzedniej konwersji ale której? Przecież ADC w swoim rytmie (pracuje w trybie Free Run) będzie pobierał adresy z MUX, próbkował, przetwarzał i wysłał wyniki. Nie obchodzi go czy wynik odczytałeś a zawartość MUX jest ważna wyłącznie tuż przed startem przetwarzania, czyli w chwili próbkowania napięcia. Musisz się z tym zsynchronizować. Przetwornik ma taki specjalny bit, którym sygnalizuje zakończenie konwersji i wpisanie wyniku do rejestru wyjściowego. Jeśli poczekasz na jedynkę, odczytasz wynik i wpiszesz nowy adres to.. nie będzie tak prosto. ADC w trybie samobieżnym rozpocznie nową konwersję (z zastanymi ustawieniami MUX i REFS) jednocześnie z ustawieniem bitu gotowości wyniku. Zanim więc zdążysz wpisań nowy numer wejścia, przetwornik spróbkuje napięcie wg dotychczasowych danych z MUX. Prawidłowa praca w tym trybie jest oczywiście możliwa ale musisz to sobie rozpisać na kartce, bo wyniki będą opóźnione o dwa przestawienia MUXa. Ja często tak robię i to działa. Jeśli nie chcesz się tak motać, możesz pracować w trybie single conversion. To najprostszy rodzaj zabawy, w której to najpierw ustawiasz MUX, startujesz przetwornik na jedną konwersję, czekasz na gotowość ADC i odczytujesz wynik. Wybieraj. Moim zdaniem pełna szybkość przetwarzania nie jest Ci wcale potrzebna a tryb pojedynczy jest dużo prostszy do opanowania. EDIT: Mirek, byłeś szybszy 🙂 1 Cytuj Link do komentarza Share on other sites More sharing options...
greebqmaster Wrzesień 11, 2013 Autor tematu Udostępnij Wrzesień 11, 2013 5 min przed Twoim postem doszedłem do wniosku, że skorzystanie z pojedyńczych konwersji chyba będzie szybsze, do czego przekonał mnie post Mirka. Daję poniżej nowy kod. Po każdym ustawieniu MUX'a robię czytaj-czekaj z konwersją i dopiero potem przypisuję wynik do zmiennej. Niestety, dalej to samo - cały czas jest uruchomiony tylko lewy silnik 🙁 #include <avr/io.h> // zalaczenie biblioteki obslugi uC AVR #include <util/delay.h> // zalaczenie biblioteki opoznien int granica=300; volatile uint8_t czl;//zmienna do pomiaru ADC z czujnika lewego volatile uint8_t czs;//zmienna do pomiaru ADC z czujnika srodkowego volatile uint8_t czp;//zmienna do pomiaru ADC z czujnika prawego int main(void) { ADCSRA = (1<<ADEN) //włączenie ADC |(1<<ADPS2); //prescaler 16 ADMUX = (1<<ADLAR) //Wyrównanie wyniku do lewej |(1<<REFS0) //VCC jako napięcie referencyjne |(0<<MUX0); //Wybór wejścia początkowego czyli PC0 DDRC=0x00; // ustawienie wszystkich bitów portu C na wejścia DDRB=0xFF; // ustawienie wszystkich bitow portu B na wyjscia DDRD=0xFF; // ustawienie wszystkich bitów portu D na wyjścia for(;;) // petla glowna { for(int i=0;i<3;i++) // petla zczytywania wyników { if(i==0) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX2) | (1<<MUX0); ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czl=ADC; } if(i==1) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX2); ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czs=ADC; } if(i==2) { ADMUX = 0; ADMUX = (1<<ADLAR) |(1<<REFS0) |(1<<MUX1) | (1<<MUX0); ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czp=ADC; } } //ustawianie silników w zależności od wyników if(czs < granica) // srodek { PORTB=0x06; PORTD=0x02; } else if (czl < granica) // prawy { PORTB=0x06; PORTD=0x0A; } else if (czp < granica) // lewy { PORTB=0x06; PORTD=0x08; } } } Cytuj 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
marek1707 Wrzesień 11, 2013 Udostępnij Wrzesień 11, 2013 Może zamiast wysyłać nam co chwila nowy, na szybko zmieniony kod usiądź spokojnie i przemyśl sprawę, OK? Po pierwsze całe for i zagnieżdżone w nim if-y stały się niepotrzebne. Po drugie robisz dosuwanie wyniku do lewej (ADLAR) a czytasz całe 16 bitów więc wynik będzie raczej duży. Po co zerujesz MUX? Każdorazowa zamiana napięcia odniesienia wymaga odczekania pewnego czasu na stabilizację i -o ile pamiętam, jednej konwersji na pusto. Czy zrobiłeś test z protezą przetwornika tzn. czy ręczne wpisywanie spodziewanych wyników daje w tym samym kodzie prawidłowe reakcje silników? Czy możesz wypisać na czymś trzy wyniki z trzech wejść? Zanim wyślesz kod, sam zastanów się co w nim jest nie tak. Cytuj Link do komentarza Share on other sites More sharing options...
greebqmaster Listopad 30, 2013 Autor tematu Udostępnij Listopad 30, 2013 Witam po raz ostatni w tym temacie. Musiałem wtedy zostawić na dłuższą chwilę tego robota z powodu nawału innej pracy, dlatego brak odpowiedzi z mojej strony. Robot działa! Zastosowałem Wasze porady i udało mi się napisać poprawny kod. Dodatkowo posiedziałem ostatnio sporo nad tym linefollowerem i dowiedziałem się, że m.in. spaliłem jeden z czujników CNY70 + trochę uprościłem kod. Podaję go poniżej: #include <avr/io.h> // zalaczenie biblioteki obslugi uC AVR #include <util/delay.h> // zalaczenie biblioteki opoznien int granica=180; volatile uint8_t czl;//zmienna do pomiaru ADC z czujnika lewego volatile uint8_t czs;//zmienna do pomiaru ADC z czujnika srodkowego volatile uint8_t czp;//zmienna do pomiaru ADC z czujnika prawego int main(void) { ADCSRA = (1<<ADEN) //włączenie ADC |(1<<ADPS2); //prescaler 16 DDRC=0x00; // ustawienie wszystkich bitów portu C na wejścia DDRB=0xFF; // ustawienie wszystkich bitow portu B na wyjscia DDRD=0xFF; // ustawienie wszystkich bitów portu D na wyjścia PORTB=0x06; // ustawienie odpowiednich wejść portu B na "1" (porty EN w L293D) PORTD=0x00; // wyzerowanie portu D for(;;) // petla glowna { ADMUX = 0; ADMUX = (1<<REFS0) //VCC jako napięcie referencyjne |(1<<MUX2); //Wybór wejścia początkowego czyli PC0 ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czs=ADC; // Przypisanie wartosci z przetwornika do zmiennej czs ADMUX = 0; ADMUX = (1<<REFS0) //VCC jako napięcie referencyjne |(1<<MUX1) | (1<<MUX0); //Wybór wejścia początkowego czyli PC1 ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czp=ADC; // Przypisanie wartosci z przetwornika do zmiennej czp ADMUX = 0; ADMUX = (1<<REFS0) //VCC jako napięcie referencyjne |(1<<MUX2) | (1<<MUX0); //Wybór wejścia początkowego czyli PC2 ADCSRA |= (1<<ADSC); //ADSC: uruchomienie pojedynczej konwersji while(ADCSRA & (1<<ADSC)); //czeka na zakończenie konwersji czl=ADC; // Przypisanie wartosci z przetwornika do zmiennej czl if (czs>granica) { PORTD=(PORTD | 0x06); // Jezeli wartosc z ADC dla czujnika srodkowego przekroczy granice, dzialaja obydwa silniki } else { if(czl>granica) PORTD=(PORTD | 0x04); // Wlaczenie prawego silnika dla odczytu z lewego czujnika else PORTD=(PORTD & (~(0x04))); // Wylaczenie prawego silnika if(czp>granica) PORTD=(PORTD | 0x02); // Wlaczenie lewego silnika dla odczytu z prawego czujnika else PORTD=(PORTD & (~(0x02))); // Wylaczenie lewego silnika } } //for } //main Dla zainteresowanych - efekt mojej pracy: - baterie padły i musiałem ciągnąć z usb - na górnym fragmencie zamiennik dla kółka (bateria w spinaczu do prania 😃 ) klinował się między dwoma kartkami Dziękuję za udzieloną pomoc i pozdrawiam,greebqmaster Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!