Skocz do zawartości

[C] ATMEGA88 sterowanie 6 serwami


gohandi

Pomocna odpowiedź

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

Link do komentarza
Share on other sites

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

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

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.

Link do komentarza
Share on other sites

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

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

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

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.