Styrning av servo

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Adddeeee
Inlägg: 125
Blev medlem: 19 juni 2010, 18:18:54

Styrning av servo

Inlägg av Adddeeee »

Tjabba!

Har en liten fråga angående styrning av servo. Det är så att jag använder mig utav en PIC16F872 för att styra ett servo. Allt fungerar jättebra, men har stött på ett litet underligt problem. Följande kod används när allt fungerar som det ska:

Kod: Markera allt

#include <pic16f872.h>

#define TRUE 1
#define FALSE 0

void initPorts();
void initPWM();
void initISR();
void initAD();
int getAD();

// values
int pulse_width = 15;
int ADvalue = 514;

//counters
int period_counter = 0;
int pulse_high_counter = 0;
int point_one_ms_counter = 0;

//flags
char ten_ms_flag = FALSE;

void main()
{
	initPorts();
	initAD();
	initISR();
	initPWM();
	
	while(TRUE)
	{
		if(ten_ms_flag)
		{
			ten_ms_flag = FALSE;
			ADvalue = getAD();

			if(ADvalue <= 10)
				pulse_width = 6;
			else if(ADvalue <= 70)
				pulse_width = 7;
			else if(ADvalue <= 140)
				pulse_width = 8;
			else if(ADvalue <= 210)
				pulse_width = 9;
			else if(ADvalue <= 280)
				pulse_width = 10;
			else if(ADvalue <= 350)
				pulse_width = 11;
			else if(ADvalue <= 420)
				pulse_width = 12;
			else if(ADvalue <= 490)
				pulse_width = 13;
			else if(ADvalue <= 560)
				pulse_width = 14;
			else if(ADvalue <= 630)
				pulse_width = 15;
			else if(ADvalue <= 700)
				pulse_width = 16;
			else if(ADvalue <= 770)
				pulse_width = 17;
			else if(ADvalue <= 840)
				pulse_width = 18;
			else if(ADvalue <= 910)
				pulse_width = 19;
			else if(ADvalue <= 980)
				pulse_width = 20; 
		}
	
	}
}

void initPorts()
{
	TRISB = 0x00;
	TRISC = 0x00;
	TRISA = 0xff;
	TRISBbits.TRISB4 = 1;
	PORTBbits.RB1 = 1;
	PORTBbits.RB2 = 0;
	PORTCbits.RC2 = 0;
}	

void initISR()
{
	INTCON = 0xE0;
	OPTION_REG = 0b00000000;
	TMR0 = 206;
	T0IF = 0;
}
	
void initPWM()
{
	CCP1CON = 0x0F;
	T2CON   = 0x04;
	PR2     = 0xC7;
	CCPR1L 	= 0xC7;
}

void initAD()
{
	ADCON0 = 0b00000001;
	ADCON1 = 0b10001110;
}

int getAD()
{
	int value = 0;

	ADCON0bits.GO_DONE = 1;
	while(ADCON0bits.GO_DONE == 1);
	
	value = ADRESH;
	value = (value << 8) + ADRESL;
	
	return value;
}

static void interrupt isr(void)				//Avbrott varje 0.1 ms
{
	if(T0IF)
    {
		T0IF = 0;
		TMR0 = 206;				
		
		point_one_ms_counter = point_one_ms_counter + 1;
		period_counter = period_counter + 1;
		pulse_high_counter = pulse_high_counter + 1;

		if(pulse_high_counter <= pulse_width)
			PORTBbits.RB2 = 1;
		else
			PORTBbits.RB2 = 0;

		if(period_counter >= 200)
		{
			period_counter = 0;
			pulse_high_counter = 0;
		}
		
		if(point_one_ms_counter >= 100)		
		{
			ten_ms_flag = TRUE;
			point_one_ms_counter = 0;
		}
    }
}

Problemet uppstår dock när jag försöker ändra

Kod: Markera allt

int period_counter = 0;
int pulse_high_counter = 0;
int point_one_ms_counter = 0;
till

Kod: Markera allt

float period_counter = 0;
float pulse_high_counter = 0;
float point_one_ms_counter = 0;
Tycker inte att datatypsändringen bör ha någon större inverkan på själva styrningen.

Anledningen till att jag vill ändra dem till float är för att jag vill kunna utföra avbrott tätare än med 0.1 ms.
Användarvisningsbild
Icecap
Inlägg: 26623
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Styrning av servo

Inlägg av Icecap »

Jag antar att du menar RC-servo...

På min hemsida, under "Freebies" finns det ett projekt som styr just RC-servo och där timern används på ett helt annat sätt. Källkod (C) finns med, kolla lite på detta som inspiration kanske.

Att använda float i mikroprocessorer är ett tydligt tecken på att man inte har helt koll på vad man håller på med och detta är ett tydligt exempel!

Ska du ha 0,1ms upplösning använder du heltal med en upplösning på just 0,1ms. Med 0,1ms i upplösning får du bara 10 positioner på servosträckan (även om man kan tänja gränsen lite), med mitt projekt får du 1000 steg mellan ytterpunkterna...
Adddeeee
Inlägg: 125
Blev medlem: 19 juni 2010, 18:18:54

Re: Styrning av servo

Inlägg av Adddeeee »

Vad är det för fel med att använda float?
Användarvisningsbild
Icecap
Inlägg: 26623
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Styrning av servo

Inlägg av Icecap »

Just det...

float är flyttal och i µC utan FPU betyder det att en massa uträkningar ska utföras. Flyttal är helt enkelt något man inte använder i µC om man inte är piskat till det! De många uträkningar kräver en massa program, de tar tid osv.

Det är ganska osannolikt att de if(x>y) du gör i interrupten hinner bli klar helt enkelt! Om vi antar att µC'n kör på 8MHz oscillatorn blir det 2MHz i exekveringshastighet. Med 0,1ms mellan varje interrupt blir det 200 instruktioner TILL ALLT per interrupt, med alla de uträkningar du har som nu plötsligt ska vara float kommer varje exekvering av ISR'n ta längre tid än tiden mellan varje ISR, alltså kommer skiten att låsa sig.

Hela konstruktionen av programmet är alltså fel, för mycket jobb på fel ställe.
Adddeeee
Inlägg: 125
Blev medlem: 19 juni 2010, 18:18:54

Re: Styrning av servo

Inlägg av Adddeeee »

Ah, det var det jag tänkte, men har inte riktigt koll på hur lång tid det tar :) Nej jag vet, det är bara tester jag utför. Kommer endast signalera en flag som sedan behandlas utanför avbrottet för att minimera risken att mitt interrupt inte hinner köra klart :)

Självklart kommer jag inte använda float heller, men kunde inte bara släppa det när jag upptäckte att det inte fungerade. Men då vet jag!

Tack för hjälpen!
sodjan
EF Sponsor
Inlägg: 43243
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Styrning av servo

Inlägg av sodjan »

De ända beräkningar du gör med de tre variablerna är väl att sätta
de till "noll", öka med 1 samt lite jämförelser. Hur fick du ideen att
för in float det hela alls ?

Att göra "+1" på en int kanske tar 3-4 maskincykler, att göra det
på en float kan ta från 10 till kanske 100 *gånger* längre tid, det
beror lite på vilken typ av float den aktuella miljön använder.
Adddeeee
Inlägg: 125
Blev medlem: 19 juni 2010, 18:18:54

Re: Styrning av servo

Inlägg av Adddeeee »

Aa, jag vet. Det var inte så genomtänkt!

Anledningen var för att jag tänkte höja frekvensen på avbrottet. Istället för avbrott var 0.1 ms tänkte jag sätta var 0.05 ms och istället öka på variablerna med 0.5 istället för 1. På så sätt skulle jag få dubbelt så måpnga steg mellan ytterlägena. Dock var det endast i testsyfte. Kommer använda mig av integer och högre värden istället :)
Användarvisningsbild
Icecap
Inlägg: 26623
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Styrning av servo

Inlägg av Icecap »

Kolla på den jag har lagt upp (länk ovan), där finns det 1000 positioner mellan ytterpunkterna och det går åt 2 interrupt per puls. Du kan lätt plocka bort 11 puls-utgångar och bara använda en och att porta mjukvaran till ditt projekt lär knappast vara ett problem.

Att ha många interrupt är inte speciellt effektivt, med en timer-period på 0,05ms får du ÄNDÅ bara 20 positioner, mellan ändlägen, på det sätt jag kör det hela får du 1000 (eller annat valfritt värde) med bara 2 interrupts.

Som jag skrev ovan: hela strukturen i programmet är fel från början, iaf. i mitt tycke.
sodjan
EF Sponsor
Inlägg: 43243
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Styrning av servo

Inlägg av sodjan »

> Istället för avbrott var 0.1 ms tänkte jag sätta var 0.05 ms
> och istället öka på variablerna med 0.5 istället för 1.

Ja, precis. Det är just det som är så feltänkt. Du tänker
i "real-life" enheter ("10-dels millisekunder"). Och så vill
du alltså alltså öka dina variabel med hälften av det och
därmed med "0.5".

Tänkt istället att koden har sin minsta enhet som just "20-dels
millisekunder). Då blir "+1" detsamma som att öka med just en
20-dels millisekund ! Du använder alltså fortfarande heltals
variabler men varje steg betyder nu något annat.

Det är oftast helt fel att försöka representera verkligheten utanför
processorn med tal som måste ha rätt värde även internt i koden.
Den översättningen till verkligheten kan vänta till det t.ex ska
presenteras på en LCD eller skickas till annat system eller liknande.

Internt är det nästan alltid mycket enklare att räkna med en enhet
som gör koden enklare att skriva och ofta även snabbare och "renare".

I just detta fall kanske det inte alls är en "jämn" 10-del eller liknande
som är optimalt, det kanske ska vara ett steg som naturligare "mappar"
mot de värden som du får från ADC eller något annat. Eller som är enklare
att generera från timern. Det spelar ju igentligen igen som helst roll om
du har (t.ex) 23 servo-positioner istället för 20 (eller något annat).
Skriv svar