gohandi Napisano Październik 7, 2010 Udostępnij Napisano Październik 7, 2010 Witam Chciałem stworzyc sterownik PWM dla 6 serw na podstawie atmegi88. Kożystając z sprzetowych generatorow 4 z 6 serw maja rozdzielczość 24 kroków na ok.190 stopni czyli jakies 8 stopni. Postanowilem napisac sterownik oparty na 16bitowym timerze1 przy częstotliwości 8Mhz udało mi sie uzyskac rozdzielczość 15250 kroków na 190 stopni ale napotkałem problem. Dla testów włączyłem również sobie przetwornik analogowo cyfrowy w ktorym na potencjometrze okreslam kat wychylenia. Wartosc z potencjometru pobrana jest tylko zapierwszym razem potem juz "nie odswieza" i wsumie nie wiem w ktora strone isc. docelowo ma byc zrealizowana jeszcze obsluga portu USART kod programu. #define F_CPU 8000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <avr/iom88.h> #define zero 546*8 //czas przy ktorym serwo ustawia sie na 0 stopni #define czasi 3000*8 //okres oblsugi jednego pinu (ns) //przedzial nastawy 0 do 15250 /* Steruje 6 serwami kazdym po kolei i przewiduje ze kazdym serwo bede obslugiwal przez 3ms daje mi to 6 * 3 = 18ms, co jest okresem dla PWM co jest zblizone do katalogowego 20ms */ void delay_us(int ttw) { int i; for(i = 0; i<ttw; i++) _delay_us(1); } void delay_ms( int ttw) { int i; for (i = 0 ; i<ttw ; i++) _delay_ms(1); }; int ID ; int tryb; int volatile nastawa[6];// = {0, 100, 7500, 10000, 12500, 15250}; /* Przyjelem ze uzyje do stowrzenia sygnalu PWM przerwania od porownania z wartoscia OCR1A Sygnał pwm podzieliłem na 3 fazy 1. odczekanie czasu do do poczatkowego polozenia serwa (tryb 3) 2. odczekanie czasu nastawy (tryb 0) 3. odczekanie do konca okresu obslugi jednego pinu. (tryb 1) inicjalizujac PWM funkcja startPWM konfiguruje timer1 wlaczam przerwania oraz ustawiam czas do odmierzenia dla zerowego polozenia funkcja stopPWM jedyne co wylacza to zezwolenie na globalne przerwania wartości nastaw przechowuje w tablicy nastawa ID jest to numer wysterowywanego serwa tryb okresla w ktorej faze generacji sygnalu pwm jestesmy */ ISR(TIMER1_COMPA_vect){ switch(tryb){ case 0: OCR1A =150+nastawa[ID]; tryb=1; break; case 1: PORTB&=~(1<<ID); OCR1A=czasi-150-zero-nastawa[ID]; tryb=2; break; case 2: ID++; if (ID==6) ID = 0; PORTB|=1<<ID; tryb = 0; OCR1A=zero; break; default: PORTB=0; break; } } int startPWM(void){ //włączam licznik TCCR1A=0b00000000; // CTC z ICR1A Piny rozlączone TCCR1B=0b00001001; //prescaler 1 PORTB|=1; tryb=0; ID=0; OCR1A=zero; sei(); TIMSK1=0b00000010; } int stopPWM(void){ PORTB=0; cli(); } void InitADC(void){ //Funkcja konfiugurujaca uC ADMUX=0b01100101; //Napiecie odniesienia na AVcc(01) przypisanie do lewej(1) zarezerwowany(0) Wybor ADC5(0101) ADCSRA= 0b10000011; //wlaczam ADC (1) niezaczynam konwersji(0) wylaczam Atrigergin(0) bitCzyskonczone(0) niewlaczam przerwan od skonczenia(0) ustawiam preskaler 8 (011) ADCSRB=0b0; DIDR0=0b0; } void StartADC(void){ //Funkcja zaczyna konwersje ADCSRA|=1<<ADSC; } void WaitADC(void){ //Funckja czeka na skonczenie konwersji while(ADCSRA&(1<<ADIF)); } int volatile ii; int main (void) { nastawa[0] = 150; nastawa[1] = 0; nastawa[2] = 0; nastawa[3] = 0; nastawa[4] = 0; nastawa[5] = 0; ii=0; DDRB=0b00111111; startPWM(); InitADC(); //nastawa[0]=15000; while(1) { StartADC(); WaitADC(); ii = ADCH; nastawa[0]=ii*60; } return 0; } Pozdrawiam Cytuj Link do komentarza Share on other sites More sharing options...
OldSkull Październik 7, 2010 Udostępnij Październik 7, 2010 W StartADC() nie zerujesz każdorazowo flagi przerwania ADIF. Cytuj Link do komentarza Share on other sites More sharing options...
gohandi Październik 7, 2010 Autor tematu Udostępnij Październik 7, 2010 W StartADC() nie zerujesz każdorazowo flagi przerwania ADIF. Nie udało mi się tego osiągnąć. Pomimo zmiany niestety nadal niedziałało. Jak tymczasowo zrezygnowałem z czekania na ukończenie konwersji zaskoczyło i działo. Ostatecznie wyrzuciłem przypisanie nastawy do przerwania od skonczenia kowersji i teraz działa. Mam takie zapytanie jeszcze czy podczas używania makra _delay_ms peryferia również wstrzymują pracę? Cytuj Link do komentarza Share on other sites More sharing options...
swierkdeck Październik 7, 2010 Udostępnij Październik 7, 2010 Nie - _delay_ms bazuje na _delay_cycles, a ta funkcja to po prostu powtarzanie instrukcji nop n-razy. Peryferia zawsze pracują niezależnie, chyba że uśpimy mikrokontroler (do pewnego stopnia) 1 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
OldSkull Październik 7, 2010 Udostępnij Październik 7, 2010 Odpalałeś ten kod w symulatorze? zapewne przerwania globalne nie są włączone - przed InitADC() daj sei();. Cytuj Link do komentarza Share on other sites More sharing options...
gohandi Październik 8, 2010 Autor tematu Udostępnij Październik 8, 2010 Nie - _delay_ms bazuje na _delay_cycles, a ta funkcja to po prostu powtarzanie instrukcji nop n-razy. Peryferia zawsze pracują niezależnie, chyba że uśpimy mikrokontroler (do pewnego stopnia) Chm... To ja czegos nie rozumiem. Na początku przy testowaniu tego programowego PWM chciałem zrobić zeby sie powoli sam obracał od 0 do 190 stopni własnie w oparciu o funkcje delay_ms() ktora powtarza _delay_ms() kod wyglądał mniejwiecej tak nastawa[0] = i*38; delay_ms(10); i++; if (i==400) i=0; i w sumie nic się nie działo, pierwsze o czym pomyslalem to wlasnie to ze licznik tez jest wstrzymywany wiec nie jest wstanie doliczyc do zadanej wartosci. Odpalałeś ten kod w symulatorze? zapewne przerwania globalne nie są włączone - przed InitADC() daj sei();. globalne zezwolenie udzielam w funkcji startPWM(). Symulatora niestety jeszcze nie nauczyłem się używać. Wszystko testuję na płytce rozwojowej. Ogólnie moge powiedzieć, że ten PWM mi działa tylko obawiam się że jak dołącze obsługę USART'u to zaczną się problemy. Jeśli bym mógł jeszcze prosić o opinie samego PWM, czy zostawic go w takiej formie czy podejsc do tego z innej strony. Pozdrawiam. Cytuj Link do komentarza Share on other sites More sharing options...
OldSkull Październik 8, 2010 Udostępnij Październik 8, 2010 PWM nie gryzie się z UARTem - w przeciwieństwie do 8051, w którym taktowaniem UARTa zajmował się jeden z Timerów, w AVRach ma on własny sygnał zegarowy. Ostatni powód jaki mi przychodzi do głowy, dlaczego nie działa ADC - optymalizacja. Niestety optymalizacja jest dobra tak długo jak długo nie korzysta się z przerwań, w takim wypadku potrafi wszystko popsuć. 1 Cytuj Link do komentarza Share on other sites More sharing options...
gohandi Październik 8, 2010 Autor tematu Udostępnij Październik 8, 2010 Z przetwornikiem ADC wydaje mi sie ze komenda WaitADC() mogła nie działać bo było własnie zezwolenie globalne na przerwania. Jak tylko sie udało ukonczyc konwersje zostało wykonane przerwanie i flaga ADIF została wyzerowana a nie miała o tym jak sie dowiedziec petla while() w WaitADC() ja to tak rozumiem, ale nie wiem ile w tym prawdy. aktualnie program wygląda następująco #define F_CPU 8000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <avr/iom88.h> #define zero 546*8 //czas przy ktorym serwo ustawia sie na 0 stopni #define czasi 3000*8 //okres oblsugi jednego pinu (ns) //przedzial nastawy 0 do 15250 /* Steruje 6 serwami kazdym po kolei i przewiduje ze kazdym serwo bede obslugiwal przez 3ms daje mi to 6 * 3 = 18ms, co jest okresem dla PWM co jest zblizone do katalogowego 20ms */ void delay_us(int ttw) { int i; for(i = 0; i<ttw; i++) _delay_us(1); } void delay_ms( int ttw) { int i; for (i = 0 ; i<ttw ; i++) _delay_ms(1); }; int ID ; int tryb; int volatile nastawa[6];// = {0, 100, 7500, 10000, 12500, 15250}; /* Przyjelem ze uzyje do stowrzenia sygnalu PWM przerwania od porownania z wartoscia OCR1A Sygnał pwm podzieliłem na 3 fazy 1. odczekanie czasu do do poczatkowego polozenia serwa (tryb 3) 2. odczekanie czasu nastawy (tryb 0) 3. odczekanie do konca okresu obslugi jednego pinu. (tryb 1) inicjalizujac PWM funkcja startPWM konfiguruje timer1 wlaczam przerwania oraz ustawiam czas do odmierzenia dla zerowego polozenia funkcja stopPWM jedyne co wylacza to zezwolenie na globalne przerwania wartości nastaw przechowuje w tablicy nastawa ID jest to numer wysterowywanego serwa tryb okresla w ktorej faze generacji sygnalu pwm jestesmy */ ISR(TIMER1_COMPA_vect){ switch(tryb){ case 0: OCR1A =150+nastawa[ID]; tryb=1; break; case 1: PORTB&=~(1<<ID); OCR1A=czasi-150-zero-nastawa[ID]; tryb=2; break; case 2: ID++; if (ID==6) ID = 0; PORTB|=1<<ID; tryb = 0; OCR1A=zero; break; default: PORTB=0; break; } } ISR(ADC_vect){ //przerwanie od zakonczenia Konwersjio ADC. int val; val = ADCH; nastawa[0]=val*60; } int startPWM(void){ //włączam licznik TCCR1A=0b00000000; // CTC z ICR1A Piny rozlączone TCCR1B=0b00001001; //prescaler 1 PORTB|=1; tryb=0; ID=0; OCR1A=zero; sei(); TIMSK1=0b00000010; } int stopPWM(void){ PORTB=0; cli(); } void InitADC(void){ //Funkcja konfiugurujaca uC ADMUX=0b01100101; //Napiecie odniesienia na AVcc(01) przypisanie do lewej(1) zarezerwowany(0) Wybor ADC5(0101) ADCSRA= 0b10001011; //wlaczam ADC (1) niezaczynam konwersji(0) wylaczam Atrigergin(0) bitCzyskonczone(0) niewlaczam przerwan od skonczenia(0) ustawiam preskaler 8 (011) ADCSRB=0b0; DIDR0=0b0; } void StartADC(void){ //Funkcja zaczyna konwersje ADCSRA|=1<<ADSC; } void WaitADC(void){ //Funckja czeka na skonczenie konwersji while(ADCSRA&(1<<ADIF)); } int volatile ii; int main (void) { nastawa[0] = 150; nastawa[1] = 0; nastawa[2] = 0; nastawa[3] = 0; nastawa[4] = 0; nastawa[5] = 0; ii=0; DDRB=0b00111111; startPWM(); InitADC(); while(1) { StartADC(); } return 0; } Jeśli ktoś widzi jakieś "głupoty" to prosił bym o zwrócenie uwagi. Pozdrawiam 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!