Skocz do zawartości

[ATMEGA32] Sterowanie serwomechanizmem przy pomocy USART


Aronkiewny

Pomocna odpowiedź

Witam, to mój pierwszy temat. Na początku zaznaczam, że moja wiedza w temacie programowania mikrokontrolerów jest znikoma, lecz muszę wykonać pewien projekt, dlatego liczę na Waszą pomoc. Potrzebuję stworzyć układ do sterowania serwomechanizmem przy pomocy przycisków i komend z terminala. Póki co udało mi się stworzyć program, który steruje serwem przy pomocy przycisków i wysyła do terminala informację w którą stronę kręci się serwo (serwo praca ciągła) teraz potrzebuję dodać kod do obsługi poleceń z terminala. Chodzi o to, żeby np po wpisaniu w terminalu danego znaku bądź ciągu znaków serwo zaczęło się kręcić w określonym kierunku. Poniżej zamieszczam mój dotychczasowy kod :

#include <avr/io.h>
#include <inttypes.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "USART.h"

#define SERWO_PIN (1<<PD5)
#define key_right (1<<PD2)
#define key_right_down !(PIND & key_right)

#define key_left (1<<PD3)
#define key_left_down !(PIND & key_left)

char char_array[10]; //tablica dla funkcji USARTWriteString
volatile uint8_t licznik=0; //zmienna licznik

int main(void)
{
   //konfiguracja przyciskow
   DDRD&=~key_right | key_left;
   PORTD |= key_right | key_left;
   //konfiguracja PWM (TIMER1)
   DDRD |= SERWO_PIN;
   TCCR1A |= 1<<WGM11;
   TCCR1B |= 1<<WGM12 | 1<<WGM13;
   TCCR1A |= 1<<COM1A1 | 1<<COM1B0;
   TCCR1B |= 1<<CS11;
   ICR1 = 19999;
   OCR1A=1460;
//inicjalizacja USART Baud Rate = 9600
   USARTInit(51);
   //konfiguracja TIMER0
   TCCR0 |= (1<<CS02) | (1<<CS00); // źródłem CLK (8MHz), preskaler 1024
   TIMSK |= (1<<TOIE0);           //przerwanie po przepełnieniu (overflow)

   sei(); //globalne odblokowanie obsługi przerwań

  while(1)
  {
   if(key_right_down){
	   _delay_ms(100);
       OCR1A = 2200;;
       sprintf(char_array,"W lewo");
   }
   else if(key_left_down){
       _delay_ms(100);
       OCR1A = 800;
       sprintf(char_array,"W prawo");
   }
   else {
       _delay_ms(100);
       OCR1A = 1400;
       sprintf(char_array,"W miejscu");
   }

  }
}


ISR(TIMER0_OVF_vect) //funkcja obsługi przerwania TIMER0
{
 licznik++;     //zwiększa zmienną licznik
 if(licznik>30)  //jeśli 30 przerwan (czyli ok 1 s)
 {
USARTWriteChar('[');
   USARTWriteString(char_array);
   USARTWriteChar(']');
   licznik=0;     //zeruje zmienną licznik
 }
}

No i teraz pytanie w którym miejscu wstawić funkcję do odczytywania z terminala ?

Link do komentarza
Share on other sites

Witam wszystkich - to też mój pierwszy post.

Aronkiewny, powinieneś przemyśleć jeszcze ten program. Po pierwsze wyrzuć wszystkie delay i wysyłanie danych uartem z przerwania. Wysyłanie uartem czegoś w przerwaniu do odmierzania czasu to nie jest dobry pomysł, bo wysyłanie trwa długo.

Nie wiem jak działa biblioteka "USART.h" , ja korzystam z biblioteki peterfleury - ma swoją stronę, znajdziesz - i z funkcji opisanych tutaj do parsowania nadlatujących danych opisanych na adnbr.co.uk/articles/parsing-simple-usart-commands.

Tworząc program ustalasz wymaganą rozdzielczość czasową, najlepiej 1/5/10/50/100ms, coś co jest łatwo zwielokrotnić do 1s skoro chcesz w takim interwale wysyłać coś uartem. W przerwaniu można ewentualnie ustawiać sobie flagi pyknięcia 10ms, 100ms, 1s.

W funkcji głównej możesz ustalić sobie warunkami if - cos co wydarzy się gdy piknie 100ms - bo ustawiona jest flaga tego zdarzenia na 1, program wchodzi w takiego ifa i robi co tam chcesz(pierwsze to zeruje flagę tych 100ms).

Można też zrobić tak by robione było coś gdy flaga zostanie ustawiona po raz 1, 2 czy trzeci.

Przykładowo np. w ósmej sekundzie zmieniasz stan diody if(liczba_s%8==0) {coś co tu ma być wykonane}. Oczywiście całość takiego if z dzieleniem modulo (reszta z dzielenia 8/8=0 co 8s) musi być wewnątrz ifa pyknięcia sekundy by nie sprawdzać co 1us czy to ósma s.

Dzięki takiemu zaplanowaniu pętli while(1) nie będziesz miał "zamrożeń" programu.

Pętla główna w zależności od tego ile tu wrzucisz będzie obiegać przy kwarcu 8MHz mln razy na sekundę, nie przegapisz np. wciśnięcia klawisza czy nadlatujących danych z uarta.

Sama biblioteka Uart by Peter Fleury działa w przerwaniach i napełnia bufor znaków. Musisz mieć w swoim programie funkcje do obsługi tego bufora - pobierasz linie tekstu, obrabiasz i w zależności czy to jest jakaś komenda to wrzucasz dane do zmiennych w pętli głównej.

Oczywiście trzeba obrobić jakoś drgania styków klawisza, ale to też lepiej robić bez delay. Można to robić w obiegu pętli instrumentując zmienne dla różnych przycisków (np. K1, K2, K3) gdy klawisz wciśnięty i zerując gdy puszczony. Jak dana zmienna osiągnie jakąś określoną wartość to wykonujesz akcję dla wciśniętego klawisza, możesz tez przypisać więcej akcji do jednego przycisku w zależności od tego jak długo klawisz jest trzymany.

Potem można sobie porobić z takich bloków funkcje by połapać się o co tu w ogóle biega w pętli while.

Dzisiaj bawiłem się czymś takim na atmedze328P/18,4MHz/115200:

while (1) {
   process_uart(); //funkcja obsługująca  bufor RX i wywołująca funkcje parsujące dane
   if (interwal<=0) interwal=variable_A;
   if(ms5_flag)
   {ms5_flag=0;
   	if (ms5_flag%2==0) // cos co wydarzy się co drugie tykniecie timera
   	{interwal--;
   		if(interwal<=0) LED_TOG;
   	}
   }
   if(s1_flag) {	/* sprawdzamy flagę, rozdzielczość 1s */
   	s1_flag = 0;	/* zerujemy flagę */

   		//	if(sekundy)
   			uart_putint(sekundy,10); //wysyłamy liczbę sekund na uart
   			uart_puts("\r\n");
   		}
 }
}

ISR(TIMER0_COMPA_vect)
{
ms5_flag = 1;	/* ustawiamy flagę co 5ms */
ms5_cnt++;
if(ms5_cnt>=199) {	/* gdy licznik ms > 199 (minęła 1 sekunda) */
	s1_flag=1;	/* ustaw flagę tyknięcia sekundy */
	sekundy++;	/* zwiększ licznik sekund */
	if(sekundy>59) sekundy=0; /* jeśli ilość sekund > 59 - wyzeruj */
	ms5_cnt=0;	/* wyzeruj licznik setnych ms */
}
}

Wysyłam uartem A=50 i funkcja parsująca przypisuje zmiennej variable_A tę wartość, ja to sobie wrzucam do zmiennej interwal i potem ją zmniejszam co 10ms (co drugie pyknięcie 5ms z timera). Jak interwal dojdzie do 0 to zmieniam stan pinu określonego makrem LED_TOG i "napełniam" interwal nowa wartością z A. Komenda A jest wykorzystywana do określenia częstotliwości migania diody zdefiniowanej makrem LED_TOG. Co sekundę wysyłam też aktualna wartość zmiennej sekundy na uart.

Analogicznie można zrobić co tam się chce. No i zmienne/flagi modyfikowane również w przerwaniu powinny być globalne volatile.

Powodzenia

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.