Skocz do zawartości

[C] ATtiny 13, serwa i przyciski


Pomocna odpowiedź

Napisano

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

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.

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ę...

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
#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

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

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

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

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.

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ć...

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??

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.

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