Skocz do zawartości

[C] ATtiny 13, serwa i przyciski


piotrek038

Pomocna odpowiedź

Witam,

Napisałem fragment kodu który miałby za zadanie obsługę serw... w sumie to jedynie poustawiałem bity... O ile w np. ATmedze8 potrafię obsłużyć serwo, tak w ATtiny mi nie idzie.



# include <avr/io.h>
# include <util/delay.h>

#define SERWO1 PB0
#define SERWO2 PB1

int main()
{
DDRB |= (1<<SERWO1) | (1<<SERWO2);
TCCR0A = (1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (0<<FOC0A) | (0<<FOC0B) | (1<<WGM02) | (0<<CS02) | (1<<CS01) | (0<CS00);
while(1)
{
	OCR0A = 1500;

}
}


nie wiem teraz jak zmusić uC do stworzenia ładnego PWMu ze zmiennym wypełnieniem i częstotliwością około tych 50...

W ATmedze występuje rejestr ICR który pozwala na ustawienie częstotliwości i OCR do zmiany wypełnienia. W ATtiny nie mamy rejestru ICR wiec jestem w kropce 😋

Czy ktoś mógłby wskazać mi gdzie leży błąd?

Jak sprawdzałem na oscyloskopie przebieg to wyglądało to bardziej jak EEG osoby nadpobudliwej niż PWM 😋

Byłbym bardzo wdzięczny za pomoc

Link do komentarza
Share on other sites

Na ATTiny13 nie uzyskasz 50Hz PWMa za chiny ludowe, bez użycia zewnętrznego zegara taktującego procesor. Jedyne rozwiązanie to programowe generowanie czasu impulsu w przerwaniach Timera, czyli defakto programowy PWM.

Link do komentarza
Share on other sites

no właśnie tego się obawiałem...

no to będę musiał się pomęczyć z programowym ;/ a to zupełnie mi nie wychodzi...

Czy na forum jest gdzieś opis programowego PWMu ale w C? szukałem, znalazłem w bascomie... a jego nie znam ani o drobinę...

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

Da się bez problemu, nawet teoretycznie 10 (20ms / 2ms max czasu impulsu dla serwa), istotne jest to żeby się mieścić w 20ms, ramce w której trzeba impulsy powtarzać. Ale na Tiny13 chyba i tak nie przekroczysz magicznej liczby 3, bo ci braknie zwyczajnie portów.

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

#include<avr/io.h>
#include<avr/interrupt.h>
#include<UART.c>

//------------ F_CPU 8MHz -----------//

#define LICZBA_SERW		8
#define SERWPORT 		PORTB
#define SERWDIR 		DDRB
#define SERW_PWMMIN		600
#define SERW_PWMMAX		2400
#define SERW_PWMSRODEK	((SERW_PWMMAX - SERW_PWMMIN)/2)
#define SERW_KATMIN		0
#define SERW_KATMAX		180
#define SERW_KATSRODEK	((SERW_KATMAX - SERW_KATMIN)/2)

int SERW_TablicaPwm[LICZBA_SERW];
int SERW_TablicaKatow[LICZBA_SERW];
int SERW_Numer=0;
int SERW_NumerOdbior=0;

void SERW_TablicaPwmInit()										//Inicjalizacja tabeli przechowującej pozycje serw
{
for(int i=0;i<LICZBA_SERW;i++)
	SERW_TablicaPwm[i]=SERW_PWMSRODEK;
}

void SERW_TablicaKatowInit()									//Inicjalizacja tabeli przechowującej dane z USART
{
for(int i=0;i<LICZBA_SERW;i++)
	SERW_TablicaKatow[i]=SERW_KATSRODEK;
}

void SERW_PortInit()											//Inicjalizacja portu do którego podłączono serwa
{
SERWDIR = 0xFF;
SERWPORT = 0x00;
}

void TIMER1_Init()
{
TCCR1B |= _BV(WGM12);										//Tryb CTC	
OCR1A = SERW_PWMSRODEK;										//Ustawienie wstępne serw w pozycji środkowej
}

void TIMER2_Init()
{
TCCR2 |= _BV(WGM21) | _BV(CS21) | _BV(CS22); 				//Tryb CTC, preskaler przez 256
OCR2 = 78; 													//Odmierzanie 2,5 ms
TIMSK |= _BV(OCIE2);										//Zezwolenie na przerwanie CTC
}

void SERW_ZmienPolozenie()										//Aktualizacja położenia serwa do pozycji odczytanej z USART
{																//z uwzględnieniem warunków położenia minimalnego i maksymalnego
for(int i=0;i<LICZBA_SERW;i++)
	if(SERW_TablicaKatow[i] > SERW_KATMAX)
		SERW_TablicaPwm[i]=SERW_PWMMAX;

	else if(SERW_TablicaKatow[i] < SERW_KATMIN)
		SERW_TablicaPwm[i]=SERW_PWMMIN;

	else 
		SERW_TablicaPwm[i]=SERW_TablicaKatow[i]*10+SERW_PWMMIN;
}

ISR(TIMER1_COMPA_vect)
{
TCCR1B &= ~_BV(CS11);										//Włączenie TIMER1
SERWPORT = 0x00;											//Wyzerowanie portu serw
TIMSK &= ~_BV(OCIE1A);										//Wyłączenie zezwolenia na przerwanie COMP od TIMER1
if(++SERW_Numer >= LICZBA_SERW)
	SERW_Numer=0;
}

ISR(TIMER2_COMP_vect)
{
TCNT1=0;													//Zerowanie TIMER1
OCR1A = SERW_TablicaPwm[SERW_Numer];						//Podanie wypełnienia dla danego serwa
TCCR1B |= _BV(CS11);										//Uruchomienie TIMER1 z preskalerem 8
SERWPORT |= _BV(SERW_Numer);								//Ustawienie odpowiedniego pinu
TIMSK |= _BV(OCIE1A);										//Zezwolenie na przerwanie COMP od TIMER1
}

ISR(USART_RXC_vect)
{
SERW_TablicaKatow[SERW_NumerOdbior]=UART_Odbierz();			//Odczyt danej z USART i zapisanie do tablicy
if(++SERW_NumerOdbior >= LICZBA_SERW)						//Inkrementacja zmiennej licznikowej przechowującej
	SERW_NumerOdbior=0;										//numer serwa do którego zapisano daną z USART

}

int main()
{
SERW_PortInit();
SERW_TablicaPwmInit();
SERW_TablicaKatowInit();
TIMER1_Init();
TIMER2_Init();
UART_OdbiornikInit();
sei();
while(1)
{
	SERW_ZmienPolozenie();										//Aktualizacja tablicy z pozycjami serw
}
return 0;
}

Masz tutaj kod do sterownika 8 serw na atmega 8. Taktowanie 8MHz.

Działa to w taki sposób, że z zewnątrz za pomocą uart przesyłane jest 8 liczb. Każda z niech z przedziału od 0 do 180. Są to kąty na jakie ma się ustawić dane serwo. Do transmisji są wykorzystane funkcje, które znajdują się w osobnym pliku uart.c, ale nie mogę go teraz znaleźć więc będziesz musiał je jakoś sobie zrobić. Lub po prostu wywal sobie całą transmisję i steruj przyciskami. Powinno działać choć dawno nie testowałem. 🤣

  • Lubię! 1
Link do komentarza
Share on other sites

Dzięki wielkie 😉

Edit.

Tak bawię się z tym kodem i mam pytanie... Teoretycznie, nawet jeśli nie wprowadziłbym żadnego sygnału z zewnątrz ustalającego kąty, wszystkie serwa powinny powędrować do neutrum? Tak?

  OCR1A = SERW_PWMSRODEK;                                        //Ustawienie wstępne serw w pozycji środkowej 

Bo przerwanie od TIMERA wystąpi...

Link do komentarza
Share on other sites

Tak jest. Po załączeniu zasilania wszystkie powinny się ustawić w pozycji środkowej. Oczywiście możesz zmienić trochę zakresy pracy serw bo ja je dobrałem dla moich TowerPro MG995

Link do komentarza
Share on other sites

No waśnie, a u mnie ani drgnie. W sumie dziwne, skoro program działa.

A jeśli chciałbym sterować przyciskami. Zamiast polecenia przerwania USART wrzuciłbym coś takiego:

void przycisk()													//Test przycisku
{
   if(!(PINC & 0x01))
   {
   	SERW_TablicaKatow[0] = 150;
   	_delay_ms(80);
   	while(!(PINC & 0x01)) {}
   	_delay_ms(80);
   }
}

i w głównej pętli programu dorzucił tą funkcję...

Próbowałem ale średnio działa... to znaczy w ogóle nie działa...

W obu przypadkach oscyloskop pokazuje 12ms co okolo 160...

Link do komentarza
Share on other sites

Możesz wywalić wszystko co związane z USARTem, i w miejsce tego wstawić funkcję obsługi przycisków. Dwa przyciski. Jednym zmieniasz w prawo drugim w lewo.

Funkcja mniej więcej takiej postaci (może być kilka błędów bo piszę ale nie testuje 😃 )

void Przycisk()
{
if(bit_is_clear(PINC,PC0))
{
SERW_TablicaKatow[0]++;
_delay_ms(20);
}
if(bit_is_clear(PINC,PC1))
{
SERW_TablicaKatow[0]--;
_delay_ms(20);
}
}

I musisz ją cały czas wywoływać w main. Jak naciśniesz jeden przycisk to serwo w prawo, a jak drugi to w lewo.

Link do komentarza
Share on other sites

Kurde... bo tak próbuję to napisać od samego rana, jakoś poskładać moją wiedzę ale nie mogę. Cały kod rozumiem, wiem co dlaczego i tak dalej ale nie wychodzi.

Chcę aby po wciśnięciu przycisku 1 serwa (wszystkie, ale każde w innej) ustawiły się w pozycji 1 po wciśnięciu przycisku 2 w pozycji 2 itd.

Czyli musiałbym zaimplementować fragment który zmieniałby zawartość tablicy SERW_TablicaKatow po wciśnięciu przycisku. Czyli w pętli głównej, pod wywołaniem funkcji SERW_ZmienPolozenie() powinna wystąpić następna funkcja która zmieniałaby zawartość tablicy.

Dobrze rozumiem?

Czyli jeśli zrezygnowałbym z tablicy SERW_TablicaKatow i wprowadzał dane bezpośrednio do drugiej tablicy mógłbym zrezygnować z funkcji SERW_ZmienPolozenie() tak?

Czyli upraszczając to w ten sposób wystarczyłby że wywołałbym w pętli głównej funkcję obsługi przycisków...

A jak do tej pory serwa nawet nie chcą się ustawić w neutrum... ;/ hmmm...

Czy może w moim rozumowaniu jest błąd?

Przepraszam że tak nadużywam waszej pomocy ale na prawdę zależy mi na tym żeby to zrozumieć...

Link do komentarza
Share on other sites

Tak. Jeżeli będziesz wprowadzał pozycje od razu do SERW_TablicaPwm, to możesz zrezygnować z funkcji Zmień położenie. Tylko musisz pamiętać aby nie przekroczyć zakresów pracy serw, bo tamta funkcja wcześniej tego pilnowała.

Napisz sobie obsługę przycisków taką jaka Ci potrzebna. W tej obsłudze wprowadzaj do tablicy SERW_TablicaPwm odpowiednie wartości, a serwa będą się tam przemieszczać.

Ale najpierw musisz uruchomić ten program bez obsługi przycisków, żebyś wiedział, że w ogóle Ci to działa. to znaczy serwa muszą się ustawić w pozycji środkowej. A dopiero później dodaj obsługę przycisków. Może wklej kod tego co już masz to zobaczymy dlaczego na razie nie działa. Jaką masz częstotliwość taktowania??

Link do komentarza
Share on other sites

Usunąłem wszystko co związane z UARTem oraz tablicę kątów.

//#define F_CPU 8000000UL   									//Rowniez ustawione w parametrach projektu
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//------------ F_CPU 8MHz -----------//

#define LICZBA_SERW        8
#define SERWPORT         PORTB
#define SERWDIR         DDRB
#define SERW_PWMMIN        600
#define SERW_PWMMAX        2400
#define SERW_PWMSRODEK    ((SERW_PWMMAX - SERW_PWMMIN)/2)

int SERW_TablicaPwm[LICZBA_SERW];
int SERW_Numer=0;
int SERW_NumerOdbior=0;

void SERW_TablicaPwmInit()                                        //Inicjalizacja tabeli przechowującej pozycje serw
{
   for(int i=0;i<LICZBA_SERW;i++)
       SERW_TablicaPwm[i]=SERW_PWMSRODEK;
}


void SERW_PortInit()                                            //Inicjalizacja portu do którego podłączono serwa
{
   SERWDIR = 0xFF;
   SERWPORT = 0x00;
}

void TIMER1_Init()
{
   TCCR1B |= _BV(WGM12);                                        //Tryb CTC
   OCR1A = SERW_PWMSRODEK;                                        //Ustawienie wstępne serw w pozycji środkowej
}

void TIMER2_Init()
{
   TCCR2 |= _BV(WGM21) | _BV(CS21) | _BV(CS22);                 //Tryb CTC, preskaler przez 256
   OCR2 = 78;                                                     //Odmierzanie 2,5 ms
   TIMSK |= _BV(OCIE2);                                        //Zezwolenie na przerwanie CTC
}

ISR(TIMER1_COMPA_vect)
{
   TCCR1B &= ~_BV(CS11);                                        //Włączenie TIMER1
   SERWPORT = 0x00;                                            //Wyzerowanie portu serw
   TIMSK &= ~_BV(OCIE1A);                                        //Wyłączenie zezwolenia na przerwanie COMP od TIMER1
   if(++SERW_Numer >= LICZBA_SERW)
       SERW_Numer=0;
}

ISR(TIMER2_COMP_vect)
{
   TCNT1=0;                                                    //Zerowanie TIMER1
   OCR1A = SERW_TablicaPwm[SERW_Numer];                        //Podanie wypełnienia dla danego serwa
   TCCR1B |= _BV(CS11);                                        //Uruchomienie TIMER1 z preskalerem 8
   SERWPORT |= _BV(SERW_Numer);                                //Ustawienie odpowiedniego pinu
   TIMSK |= _BV(OCIE1A);                                        //Zezwolenie na przerwanie COMP od TIMER1
}



int main()
{
   SERW_PortInit();
   SERW_TablicaPwmInit();
   TIMER1_Init();
   TIMER2_Init();
   sei();
   while(1)
   {
   	//funkcja zmiany wartoci w tablicy,
   }
   return 0;
}

Częstotliwość mam ustawioną na 8MHz, robię to za pomocą Eclipsa w ustawieniach projektu.

I ten kod nie działa. Również w ogóle nie zmieniony program nie chce działać. I dziwi mnie to bardzo. Serwo sobie "tyka".

Jeśli chodzi o podłączenie zasilania do uC to wszystko powinno być ok bo od samego początku przygody kieruję sie wskazówkami z KURSU C DLA AVR z tego portalu, są tam bardzo schematy poprawnego podłączenia zasilania.

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.