Skocz do zawartości

[C] Problemy z programem do Line Follower na AtMega8 i L293D


malum

Pomocna odpowiedź

Skleciłem dzisiaj już chyba ostateczną wersję płytki do Line Follower'a i zabrałem się za oprogramowanie.

Stan czujników pokazywany jest przez diody, a zrealizowałem to tak:


int main(void)
{
 /* Wszystkie linie portu D to wyjścia */
 DDRD  = 0x3f;

 /* Początek nieskończonej pętli */
 while(1)
 {
    // wykrywanie linii w czujniku 1
 if(!(PINC & 0x01)) PORTD = 0x01;
 else PORTD = 0x00;

 // wykrywanie linii w czujniku 5
 if(!(PINC & 0x10)) PORTD = 0x20;
 else PORTD = 0x00;

 // wykrywanie linii w czujniku 2
 if(!(PINC & 0x02)) PORTD = 0x02;
 else PORTD = 0x00;

 // wykrywanie linii w czujniku 4
 if(!(PINC & 0x08)) PORTD = 0x10;
 else PORTD = 0x00;

 // wykrywanie linii w czujniku 3
 if(!(PINC & 0x04)) PORTD = 0x04;
 else PORTD = 0x00;

 // wykrywanie linii w czujniku 6
 if(!(PINC & 0x20)) PORTD = 0x08;
 else PORTD = 0x00;

 }
}

Mam jednak problem z silnikami, a mówiąc konkretniej pojęcia nie mam jak nimi sterować. Założenie jest takie, że jeśli np. lewy czujnik wykryje linię

if(!(PINC & 0x01)) PORTD = 0x01;
 else PORTD = 0x00;

to silnik ma zacząć obracać się w tył, a jeśli nie widzi linii to ma się obracać w przód.

Problem polega tylko na tym, że nie wiem jak się za to zabrać.

Układ (w wielkim skrócie) mam zmontowany tak:


L293D     AtMega8
z  1      do 15        
z  2      do 14
z  7      do 19
z  9      do 16
z 10      do 18
z 15      do 17


czyli


 L293D        AtMega8
z 1-2EN       do PB1        
z   1A        do PB0
z   2A        do PB5
z  3-4EN      do PB2
z   3A        do PB4
z   4A        do PB3

Pominąłem oczywiście masy i zasilenia.

Mówiąc krótko, mam 6 pinów łączących oba scalaki, ale w jaki sposób za ich pomocą sterować silnikami, lub choćby w ogóle je uruchomić, z resztą powinienem sobie poradzić. Czytałem kurs języka C, ale o sterowaniu silnikami niczego tam nie ma, jest jedynie program koguta policyjnego, ale nie ma w nim ani schematu, ani dokładnego opisu, a na dodatek jeszcze procesor inny 🙁

Pomóżcie proszę bo utknąłem...

Link do komentarza
Share on other sites

Ja robie to zawsze w takie sposób że piszę sobie funkcje strowanie silnika. W taki oto sposób.

void front()
{
   PORTB &= ~_BV(4); 
   PORTB &= ~_BV(0);
   PORTB |= _BV(3);
   PORTB |= _BV(5);
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2); 
}

//lub

void turn_left_fast()
{
 PORTB &= ~_BV(3); 
   PORTB &= ~_BV(0);
   PORTB |= _BV(4);
   PORTB |= _BV(5);
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2); 
}

porty musisz sobie dobrać eksperymentalnie.

Zasada działania jest taka iż kanały pwm muszą być w stanie wysokim. Natomiast podając stan wysoki na jeden z portów sterowanie (1A 2A lub 3A 4A) sterujesz obrotami silnika do przodu lub do tyłu.

Link do komentarza
Share on other sites

Zasada działania jest taka iż kanały pwm muszą być w stanie wysokim. Natomiast podając stan wysoki na jeden z portów sterowanie (1A 2A lub 3A 4A) sterujesz obrotami silnika do przodu lub do tyłu.

Czyli jak rozumiem, gdy podaję stan wysoki na 1A a niski na 2A to silnik obraca się w jedną stronę, a gdy podaję stan niski na 1A i wysoki na 2A to silnik kręci w drugą stronę - zgadza się?

Tylko teraz jeszcze jedno pytanie 🙂 - jak rozróżnić który to !a, który 1A, 2A, itd.

Patrząc na przykład:

void turn_left_fast()
{
   PORTB &= ~_BV(3);
   PORTB &= ~_BV(0);
   PORTB |= _BV(4);
   PORTB |= _BV(5);
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2);
}

nie bardzo potrafię go rozczytać - mógłbyś wyjaśnić linijka po linijce co tu się dzieje i co masz na myśli pisząc "porty musisz sobie dobrać eksperymentalnie" - gdzie są w programie te porty i jak je dobierać, bo ja wszędzie widzę PORTB, ale nie bardzo rozumiem jak wskazać konkretny pin danego portu, jak to robiłem np. w części odpowiedzialnej za zapalanie diód.

Link do komentarza
Share on other sites

void turn_left_fast()
{
   PORTB &= ~_BV(3); //stan wysoki na PB3 (4A)
   PORTB &= ~_BV(0);//stan wysoki na PB0 (1A)
   PORTB |= _BV(4); //Stan niski na PB4 (3A)
   PORTB |= _BV(5); //stan niski na PB5 (2A)
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2);
} 

Dobrze rozumujesz. A oto twój wyjaśniony kod:D

[ Dodano: 13 Gru 09 03:14 ]

Zapomniałem dodać że, numery wyjść portów znajdują się w nawiasie. Po twoim wcześniejszym poście odczytałem ich połączenie z mostkiem H. Co do dobierania eksperymentalnego to chodzi o to iż nie wiem jak masz podłączone silniki. Może się tak zdażyć że robot pojedzie do tyłu lub będzie się kręcić. Wtedy musisz zmienić wyjścia.

_BV(1)  //znaczy dokładnie to samo co <<1
_BV(2)  //znaczy dokładnie to samo co <<2 itd

PORTB &= ~_BV(3); // ustawia stan wysoki na wyjściu 3 portu B
PORTB |= _BV(3); // ustawia stan niski na wyjściu 3 portu B 
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

W dalszym ciągu coś mi nie gra.

Napisałem taki program:


#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

void ledy_on()
{
 for(int i=0;i<=6;i++)
 {
    _delay_ms(1000);
    switch(i)
    {
      case 0: 
			  PORTD |= 0x01; 
			  break;
      case 1: 
	          PORTD |= 0x02; 
			  break;
      case 2: 
			  PORTD |= 0x04; 
			  break;
      case 3: 
			  PORTD |= 0x08; 
			  break;
      case 4: 
			  PORTD |= 0x10; 
			  break;
      case 5: 
			  PORTD |= 0x20; 
			  break;
         case 6: 
			  PORTD = 0x00;
			  break;
    }
 }
}

void przod() 
{
   PORTB &= ~_BV(3); //stan wysoki na PB3 (4A)
   PORTB &= ~_BV(0);//stan wysoki na PB0 (1A)
   PORTB |= _BV(4); //Stan niski na PB4 (3A)
   PORTB |= _BV(5); //stan niski na PB5 (2A)
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2);
}

void tyl() 
{
   PORTB &= ~_BV(4); //stan wysoki na PB3 (3A)
   PORTB &= ~_BV(5);//stan wysoki na PB0 (2A)
   PORTB |= _BV(3); //Stan niski na PB4 (4A)
   PORTB |= _BV(0); //stan niski na PB5 (1A)
   PORTB &= ~_BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB &= ~_BV(2);
}

int main(void)
{ 
 /* Linie portu D (0-6) będą wyjściami */
 DDRD  = 0x3f;

 while(1)
 {
przod();
_delay_ms(1000);
   tyl();
_delay_ms(1000);
   ledy_on(); 	 
 }
}

Moim zdaniem powinien:

1. uruchomić oba silniki na 1 sek obracając je "do przodu"

2. uruchomić oba silniki na 1 sek obracając je "do tyłu"

3. Zapalić kolejno 6 diod, po czym je wygasić

4. Wrócić do kroku 1

Tym czasem:

1. Jeden z silników włącza się na 1 sek obracając się "do przodu"

2. Zapalają się kolejno wszystkie diody, po czym wygasają.

3. Procedura powtarza się - powrót do kroku 1.

Wynika z tego, że ten silnik w ogóle nie realizuje części programu odpowiedzialnej za obroty "do tyłu", ale co ważniejsze, drugi silnik kręci się bez przerwy w jedną stronę,niezależnie od realizacji programu.

Gdzie popełniłem błąd?

Link do komentarza
Share on other sites

Przede wszystkim zapomniałeś zainicjować portu B jako wyjście a to musisz koniecznie zrobić i od tego powinieneś zacząć pisanie programu.

DDRB  = 0x1F; 

Teraz powinno być dobrze

Jeżeli nadal będą problemy spróbuj pozmieniać linie wyjścia, i zmież miernikiem czy mostek daje napięcie na wyjściu.

Link do komentarza
Share on other sites

Przede wszystkim zapomniałeś zainicjować portu B jako wyjście a to musisz koniecznie zrobić i od tego powinieneś zacząć pisanie programu.
DDRB  = 0x1F; 

Teraz powinno być dobrze

Jeżeli nadal będą problemy spróbuj pozmieniać linie wyjścia, i zmierz miernikiem czy mostek daje napięcie na wyjściu.

Gdy wrzucam ten kod silniki w ogóle nie startują, diody zapalają się jak poprzednio, ale oba silniki stoją. Próbowałem też wersję z 0x3f bo chyba tak powinno być.

Zasilenie silników jest OK, oba silniki pracują, ale nie tak jakbym chciał.

Link do komentarza
Share on other sites

   PORTB &= ~_BV(3); //stan wysoki na PB3 (4A) 

Komentarz nie zgadza się z kodem - program ustawia stan niski, nie wysoki.

Podobnie jest z kodem, który miał dawać stan niski - daje wysoki.

Może dlatego nie działa?

Link do komentarza
Share on other sites

   PORTB &= ~_BV(3); //stan wysoki na PB3 (4A) 

Komentarz nie zgadza się z kodem - program ustawia stan niski, nie wysoki.

Podobnie jest z kodem, który miał dawać stan niski - daje wysoki.

Może dlatego nie działa?

Nie bardzo rozumiem - możesz wprowadzić poprawki do powyższego opisu? Z tego co piszesz wynika, że praktycznie wszystkie linijki sterowania silnikami są złe

Link do komentarza
Share on other sites

powinno być:

PORTB |= _BV(3); //stan wysoki na PB3 (4A) 
...
PORTB &= ~_BV(3); //Stan niski na PB4 (4A) 

ogólnie |= daje stan wysoki, a &=~ niski.

[ Dodano: 13 Gru 09 10:58 ]

czyli:

void przod()
{
   PORTB |= _BV(3); //stan wysoki na PB3 (4A)
   PORTB |= _BV(0);//stan wysoki na PB0 (1A)
   PORTB &= ~_BV(4); //Stan niski na PB4 (3A)
   PORTB &= ~_BV(5); //stan niski na PB5 (2A)
   PORTB |= _BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB |= _BV(2);
}
  • Lubię! 1
Link do komentarza
Share on other sites

powinno być:
PORTB |= _BV(3); //stan wysoki na PB3 (4A) 
...
PORTB &= ~_BV(3); //Stan niski na PB4 (4A) 

ogólnie |= daje stan wysoki, a &=~ niski.

[ Dodano: 13 Gru 09 10:58 ]

czyli:

void przod()
{
   PORTB |= _BV(3); //stan wysoki na PB3 (4A)
   PORTB |= _BV(0);//stan wysoki na PB0 (1A)
   PORTB &= ~_BV(4); //Stan niski na PB4 (3A)
   PORTB &= ~_BV(5); //stan niski na PB5 (2A)
   PORTB |= _BV(1); //kanały pwm (maksymalne wypełnienie)
   PORTB |= _BV(2);
}

SUPER!! Dzięki za pomoc - masz u mnie piwko!

Problemem były odwrotne komentarze - nic dziwnego, że mi silniki szalały.

Dla potomnych 😃 mających ten sam problem zamieszczam kod:


#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

void ledy_on()
{
 for(int i=0;i<=6;i++)
 {
    _delay_ms(1000);
    switch(i)
    {
      case 0: 
			  PORTD |= 0x01; 
			  break;
      case 1: 
	          PORTD |= 0x02; 
			  break;
      case 2: 
			  PORTD |= 0x04; 
			  break;
      case 3: 
			  PORTD |= 0x08; 
			  break;
      case 4: 
			  PORTD |= 0x10; 
			  break;
      case 5: 
			  PORTD |= 0x20; 
			  break;
         case 6: 
			  PORTD = 0x00;
			  break;
    }
 }
}

void silniki() 
{
   PORTB |= _BV(1);
   PORTB |= _BV(2);


   PORTB |= _BV(0);   // lewy silnik w lewo ON
   _delay_ms(10000);
   PORTB &= ~_BV(0);  // lewy silnik w lewo OFF


PORTB |= _BV(5);   // lewy silnik w prawo ON
   _delay_ms(10000);
   PORTB &= ~_BV(5);  // lewy silnik w prawo OFF


   PORTB |= _BV(4);   // prawy silnik w lewo ON
   _delay_ms(10000);
   PORTB &= ~_BV(4);  // prawy silnik w lewo OFF


   PORTB |= _BV(3);   // prawy silnik w prawo ON
   _delay_ms(10000);
   PORTB &= ~_BV(3);  // prawy silnik w prawo OFF
}



int main(void)
{ 
 /* Linie portu D (0-6) będą wyjściami */
 DDRD  = 0x3f;
 DDRB  = 0x3f;

 while(1)
 {
silniki();
   ledy_on(); 	 
 }
}

Jeszcze raz dzięki wszystkim za pomoc!

[ Dodano: 13 Gru 09 11:39 ]

Przy okazji mam jeszcze jedno pytanie - oprogramowanie które piszę jest przeznaczone do robota klasy line follower. W jaki sposób najlepiej zrealizować skręt, bo są dwie wersje:

1. silnik prawy kręci do przodu, a lewy do tyłu

2. jeden z silników zatrzymuje się podczas gdy drugi w dalszym ciągu się obraca

Która wersja jest lepsza?

Link do komentarza
Share on other sites

Przy okazji mam jeszcze jedno pytanie - oprogramowanie które piszę jest przeznaczone do robota klasy line follower. W jaki sposób najlepiej zrealizować skręt, bo są dwie wersje:

1. silnik prawy kręci do przodu, a lewy do tyłu

2. jeden z silników zatrzymuje się podczas gdy drugi w dalszym ciągu się obraca

Która wersja jest lepsza?

Wszystko zależy od konstrukcji twojego lfr'a. Jeśli masz koła w osi obrotu robota to przy pierwszej opcji robot skręci w miejscu. Ma to niestety dużą wadę, musisz w bardzo krótkim czasie 2 razy zmienić silnikowi obroty z jazdy do przodu (powiedzmy obroty w prawo), na w lewo, później znów w prawo. Konsekwencja tego jest taka, że tracisz dużo czasu. Jeśli twój robot nie będzie bardzo szybki ten sposób skrętu jest wygodny. Druga opcja jest zdecydowanie szybsza, nie wymaga zmiany kierunku obrotów silnika, ale za to promień skrętu rośnie. Jest jeszcze jedna opcja, ale to już dla bardziej zaawansowanych 😉, gdy robot skręca np w prawo, prawy silnik powinien zmniejszyć obroty, lewy silnik zwiększyć. Dzięki temu robot szybciej pokona zakręt.

Link do komentarza
Share on other sites

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!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.