Hjälp med kod!

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Excl
Inlägg: 6
Blev medlem: 10 november 2012, 14:15:15

Hjälp med kod!

Inlägg av Excl »

Hej!
Jag har hållit på med ett litet projekt men har fastnat med koden, som inte verkar fungera! Projektet går ut på att m.h.a. en AVR Butterfly så ska två st. servon roteras fram och tillbaka (0-180 grader och tillbaka). Jag använder mig av AVR-studio (med evalueringskiten ATMega 169) för skrivandet av programmet och hittills ser programmet ut på följande sätt:

Kod: Markera allt

/****Servomotor***/


#include<avr/io.h>
#include<stdio.h>
#define F_CPU 1000000UL 
#include<util/delay.h>


void init_ADC(void)
		{
			ADMUX=(1<<REFS0);// referens VCC=5V 
			ADMUX|= ((1<<ADLAR) | (1<<MUX2));// vänstrjusterat
			ADCSRA=0x00;// Clear previous ADC result
			ADCSRA=(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0);//Enable ADC, prescaler 8
			
		}

void init_PWM(void)
		{
		PORTB=	0x00;
		DDRB|=	((1<<DDB5) | (1<<DDB6));// PB5/OC1A OCH PB6/OC1B bits of PORTB as output
		ICR1=	4999; //TOP
		TCCR1A|=((1<<COM1A1) | (1<<COM1B1) | (1<<WGM11));//
		TCCR1B|=((1<<WGM13) | (1<<WGM12) | (1<<CS11) | (1<<CS10));// fast PWM, prescale 64
			
		}

int read_convert(void)
	{
		while(!(ADCSRA &(1<< ADIF)));//Väntar tills konverteringen är klar
		ADCSRA|=(1<<ADIF);//clear ADIF
		
		double	number=ADCH;// hämtar ADCH?
		double 	procent;
		double	position;
		
		
		
		for(number=0; number!=255; number++)
			{
				procent=number/255;
				_delay_ms(10);
			}
			position = 1000+procent*1000;

		for(number=255; number!=0; number--)
			{
				procent=number/255;
				_delay_ms(10);
			}
			position = 1000+procent*1000;
		return position;

	}


int main(void)
	{

	
	init_ADC();
	init_PWM();


	
		while(1)
		{
		ADCSRA |=(1<<ADSC);//Starta conversion
		OCR1A=read_convert();//läs konvertering

		DDRF=0b00100000;//byter signal till ADC5(pinne 5 portF)

		ADCSRA |=(1<<ADSC);//Starta conversion
		OCR1B=read_convert();

		DDRF=0b00010000;//byter signal till ADC4(pinne 4 portF)

		
		}
	}
Problemet är att servomotorerna roterar först mot startpositionen (samtidigt) och sen roterar de båda 180 grader (först den ena, efter en liten delay den andra). Men istället för att fortsätta programmet så händer inget mer!

Här är en liten förklaring till hur jag tänkt med vissa delar som kan vara otydliga samt vad jag skulle kunna behöva ha hjälp med:

Motorerna är kopplade: till pinnar PB5(OC1A) och PB6(OC1B) på port B.
Potentiometrarna är kopplade till PF4(TCK/ADC4) och PF5(TMS/ADC5) på port F.

Med ADCH och konverteringen är jag helt förvirrad nu och jag trodde att för att hämta ADCH så ska man sätta ADLAR = 1.
Ska jag först vänta tills konvertering är klar med while(!(ADCSRA &(1<< ADIF)));//Väntar tills konverteringen är klar och därefter hämta ADCH?

Med loopen "for(number=0; number!=255; number++) " har jag tänkt att number ska löpa från 0,1,2...till 255 så att jag får procent=0.004, 0.008,...., 1. Det behövs till positionerna för servo, som ska ändras från 0 grader till 180; position = 1000 motsvarar 0 grader och position=2000 till 180 grader. Samma princip gäller för den andra loopen "for(number=255; number!=0; number--) " vilket medför att servorna roteras tillbaka till position=1000 (0 grader).
Nu ser jag att procent skulle vara av typen double (och inte int) för att ha värdena 0.004, 0.008, ...., 1, men även denna ändring löser inte det problem jag har med kretsen.

DDRF =0b00100000 är set bit 5 of port F for output, bit 5 på port F har namnet ADC5 (ADC i ATmega169 finns på port F).
DDRF =0b00010000 är set bit 4 of port F for output, bit 4 på port F har namnet ADC4.

“byter till ADC5” : det är tänkt att servo ska roteras efter varandra och inte samtidigt.

Är det något som vet vad problemet är? Vad är det som behöver att fixas?

//Excl
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: Hjälp med kod!

Inlägg av exile »

Du har tänkt fel om for-looparna, du kommer få samma värde varenda gång efter for looparna vilket gör att du returnerar samma värden hela tiden.
Sedan undvik att använda double, visst fungerar det men tar onödig med resurser vilket kan behövas längre fram i ditt projekt. (tar ca 20-200 gånger längre tid samt ca 5-100 mer utrymme)
Excl
Inlägg: 6
Blev medlem: 10 november 2012, 14:15:15

Re: Hjälp med kod!

Inlägg av Excl »

Har du nägot förslag på hur for-looparna kan skrivas? Jag har testat med olika metoder men får ändå samma svar (rätt ny med programmering inom AVR!).. :/

//Excl
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hjälp med kod!

Inlägg av sodjan »

Det har så vitt jag ser inte ett smack med AVR att göra.
Hur har du tänkt att for looparna ska fungera?

Både looparna kommer att köras helt innan return.

Första loopen kan du plocka bort helt, den saknar funktion
efter som den andra loopen i alla fall ändrar värdet.

Delayen inom looparna har ingen direkt funktion (mer än att få
looparna att gå långsammare, om det var det som var meningen).
Användarvisningsbild
MicaelKarlsson
Inlägg: 4669
Blev medlem: 18 juni 2004, 09:16:07
Ort: Aneby
Kontakt:

Re: Hjälp med kod!

Inlägg av MicaelKarlsson »

Kod: Markera allt

      for(number=0; number!=255; number++)
         {
            procent=number/255;
            OCR1B = 1000+procent*1000;
            _delay_ms(10);
         }         
Om du gör så här borde du kunna stega upp OCR1B till startvärde med 10 ms mellan varje nytt värde?
Excl
Inlägg: 6
Blev medlem: 10 november 2012, 14:15:15

Re: Hjälp med kod!

Inlägg av Excl »

sodjan:

Jag har tänkt att for-looparna ska rotera servona 180 grader och sen när de nått 180 grader så roteras de tillbaka. Jag får dock samma resultat om jag tar bort den ena for-loopen - det verkar vara något i den övriga koden som inte stämmer, d.v.s koden verkar köras en gång men sedan blir det stopp :S

MicaelKarlsson:

Jag har även testat det alternativet men då fungerar programmet inte helt; det ena servot rör sig en aning (ca 40 grader) men sedan är det dött :(

Från det jag förstått så är det något fel med while - funktionen, då den inte fungerar som den ska. Med en for-loop (enl. sodjan) så BORDE det fungera då programmet borde köra om och om igen men det gör det inte -> problem med whilefunktionen . Men jag hittar inte problemet; är det någon som ser vad som bör ändras?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hjälp med kod!

Inlägg av sodjan »

Nej, det sa jag inte. :-)
Jag sa ett den första för loopen inte har någon funktion.
Men det fungerar lika dåligt utan den... :-)

> double number=ADCH;// hämtar ADCH?

Och även om den göra det (?) så spelar det ju ingen roll eftersom
for looparnai alla fall sätter number till något annat. ADC värdet
används inte till någonting alls.

Foor loppen kommer att köras till max-värdet innen servot justeras alls.
Det är därför det går direkt till 180 grad läget. Du räknar ju bara upp
"procent" 255 gånger (med en kort fördröjing) innan du beräknar "position"
och returnerar värdet. Loopen är helt onödig och du kan lika gärna göra :
(utan fördröjningen i och för sig, den kan du lägga till om du vill).

Kod: Markera allt

int read_convert(void)
   {
      return 1000;
   }
Poängen är att read_convert alltid returnerar 1000.
Du kan också vänta på ADC'n om du vill, men det har ingen funktion.

Är det OCR1A/B som sätter duty/position på servot ?
I så fall behöver du sätta dessa *inne* i looparna, något i stil med:

Kod: Markera allt

int read_convert(void)
   {
      while(!(ADCSRA &(1<< ADIF)));//Väntar tills konverteringen är klar
      ADCSRA|=(1<<ADIF);//clear ADIF
      
      double   number=ADCH;// hämtar ADCH?
      double    procent;
      double   position;
      
      for(number=0; number!=255; number++)
         {
            _delay_ms(10);
            procent=number/255;
            position = 1000+procent*1000;
            OCR1A = position;
            OCR1B = position;
         }

      for(number=255; number!=0; number--)
         {
            _delay_ms(10);
            procent=number/255;
            position = 1000+procent*1000;
            OCR1A = position;
            OCR1B = position;
         }
      return;
   }
Otestat...
Användarvisningsbild
Walle
Moderator
Inlägg: 7701
Blev medlem: 14 december 2004, 10:32:18
Ort: Stockholm

Re: Hjälp med kod!

Inlägg av Walle »

Det du har missat är att programmet kör igenom HELA FOR-loopen (om du tog bort den ena) varje gång som read_convert anropas. Alltså kommer read_convert att returnera samma värde till main-loopen varenda gång den körs.

Du vill:
  1. Anropa read_convert()
  2. Öka värdet och returnera det
  3. Uppdatera servots position till det nya värdet
  4. gå till 1
Men vad du egentligen gör (om du bara har kvar första for-loopen) är:
  1. Anropar read_convert()
  2. Loopar igenom FOR-loopen tills den är klar
  3. Returnerar 1000 och sätter servot till den positionen.
  4. Uppdaterar servots position.
Excl
Inlägg: 6
Blev medlem: 10 november 2012, 14:15:15

Re: Hjälp med kod!

Inlägg av Excl »

sodjan:

Har nu testat ditt program men då går servona inte ens ett helt varv utan ca 15 grader fram och tillbaka (antar även att du menar return position; i slutet, då endast return; ger en varning). Så här ser koden ut då det problemet uppstod:

Kod: Markera allt

/****Servomotor***/


#include<avr/io.h>
#include<stdio.h>
#define F_CPU 1000000UL 
#include<util/delay.h>


void init_ADC(void)
		{
			ADMUX=(1<<REFS0);// referens VCC=5V 
			ADMUX|= ((1<<ADLAR) | (1<<MUX2));// vänstrjusterat
			ADCSRA=0x00;// Clear previous ADC result
			ADCSRA=(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0);//Enable ADC, prescaler 8
			
		}

void init_PWM(void)
		{
		PORTB=	0x00;
		DDRB|=	((1<<DDB5) | (1<<DDB6));// PB5/OC1A OCH PB6/OC1B bits of PORTB as output
		ICR1=	4999; //TOP
		TCCR1A|=((1<<COM1A1) | (1<<COM1B1) | (1<<WGM11));//
		TCCR1B|=((1<<WGM12) | (1<<WGM13) | (1<<CS11) | (1<<CS10));// fast PWM, prescale 64
			
		}

int read_convert(void)
	{
		
		while(!(ADCSRA &(1<< ADIF)));//Väntar tills konverteringen är klar
		ADCSRA|=(1<<ADIF);//clear ADIF
		
		double	number=ADCH;
		double	position;
		double 	procent;
		
		
	for(number=0; number!=255; number++)
         {
            _delay_ms(10);
            procent=number/255;
            position = 1000+procent*1000;
            OCR1A = position;
            OCR1B = position;
         }

      for(number=255; number!=0; number--)
         {
            _delay_ms(10);
            procent=number/255;
            position = 1000+procent*1000;
            OCR1A = position;
            OCR1B = position;
         }
      return position;

	}


int main(void)
	{

	
	init_ADC();
	init_PWM();
	


	
		while(1)
		{
		ADCSRA |=(1<<ADSC);//Starta conversion
		OCR1A=read_convert();//läs konvertering

		DDRB |=(1<<PB6);//byter till ADC4

		ADCSRA |=(1<<ADSC);//Starta conversion
		OCR1B=read_convert();

		DDRB|=(1<<PB5);//byter till ADC5
		
		}
	
	}
Jag noterade att jag nu har OCR1A/B = position; i FOR-loopen samt OCR1A/B=read_convert(); i while-loopen; dessa krockar väl då i koden?

Walle:
Ja det är precis det jag försöker få koden att göra, men hur jag än ändrar for-loopen så beter sig kretsen i princip samma sak, vilket får mig att tro att problemet inte ligger enbart i for-loopen utan även i while-loopen. :S
Användarvisningsbild
Icecap
Inlägg: 26645
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Hjälp med kod!

Inlägg av Icecap »

Bara en rent programmeringsteknisk sak: for(number=0; number!=255; number++) är inte ett bra sätt att göra detta på!
for(number = 0; number < 255; number++) är mycket bättre.

Varför då?
Jo, om number råkar pillas på av någon orsak (taskigt program) kan den hoppa över helt enkelt. Och i detta fall är det en annan fälla: Du använder dumt nog double! Varför? Nåväl, en double är flyttal och det finns en avrundning som gör att värdet kanske inte kan stega till exakt 255,00000. När du sedan jämför i programmet kan det vara att värdet först är 254,999999 - vilket INTE är lika med 255,000000, nästa steg kan det vara 255,99999 vilket inte heller är lika med 255,000000.

Om du istället hade deklarerat variablerna som:
uint_16 number;
uint_16 position;
unsigned long procent;
ville en del vara mindre besvärligt och ta kortare tid.

Dock blir din uträkning ganska besvärlig, procent=number/255; blir ju som mest 1 när number = 255, annars är den noll. Men det är för att du räknar fel till att börja med! Du kan ersätta
procent=number/255;
position = 1000+procent*1000;
med
procent=(number * 1000)/255;
position = 1000+procent;
Då får du exakt samma värden men utan double. Tyvärr blir 255*1000 = 255000 vilket en unsigned int inte klarar, alltså måste procent vara av typen unsigned long, då är saken biff
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: Hjälp med kod!

Inlägg av exile »

Har du tänk rätt angående pulstiden?

Vilken frekvens kör du på? 8Mhz?

Kan du verifiera tiderna är mellan 1ms och 2ms?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Hjälp med kod!

Inlägg av sodjan »

> Har nu testat ditt program men då går servona inte ens ett helt varv utan ca 15 grader fram och tillbaka...

Jaha, och ? :-)

Det handlade mest om att visa varför ditt program över huvudtaget inte
kunde fungera, ja sa aldrig att mitt program skulle fungera (korrekt)... :-)
dangraf
Inlägg: 530
Blev medlem: 9 juni 2003, 15:30:56
Ort: göteborg

Re: Hjälp med kod!

Inlägg av dangraf »

eftersom det finns en hel del fel i programmet blir jag inte riktigt klok på dels vad du vill göra och vad som fungerar/inte fungerar.

1. har du verifierat oscillatorn så att du verkligen vet att delayfunktionen väntar 10ms( och att du får rätt förhållanden på OCR registerna)?

2. Om du vill rotera servot mellan 0-180 grader, vad ska du då med ADC värdet till?vill du egentligen koppla en POT till ADCn och på så sätt styra antalet grader på servot? Ytterligare en tolkning är att du vill att servot skall svänga fram och tillabaka mellan t.ex 180 grader och det inställda gradtalet mha ADCn.
Just nu skriver du över ADC värdet med 0.

3. Har du kontrollerat utsignalerna till servona mha oscilloskop? Pulslängden skall vara mellan 0.5ms - 2ms med en periodtid på 20ms. Om du har fel periodtid kommer servot ställa sig i max/min läge direkt och därefter inte göra mycket mer. Du verkar ha en periodtid på 10ms och jag är osäker på pulslängden.

http://www.ermicro.com/blog/?p=771



ett annat upplägg på koden skulle kunna se ut ung såhär med reservation för kompileringsfel:

Kod: Markera allt

#define POS_MAX	2000		// max värde för OCR ger 2ms duty
#define POS_MIN	1000		// min värde för OCR ger 0.5ms duty
#define STEP	10		// steglängd
#define PERIOD_MS  20

typedef struct
{
	int position;
	int step;
	int *pOCR
	
}servo_t;



void setServo( servo_t *pServo )
{
	// stega upp värdet på servot
	pServo->position += pServo->step;

	// kontollera värdena på postitionen och justera steglängden.
	if( pServo->position >= POS_MAX )
	{
		pServo->position = POS_MAX;
		pServo->step = -STEP;
	}
	
	if( pServo->position <= POS_MIN )
	{
		pServo->position = POS_MIN;
		pServo->step =  STEP;
	}
	*(pServo->pOCR) = pServo->position;	// sätt värdet på registret
}

int main
{
 servo_t servoA = {0,STEP,&OCRR1A};
 servo_t servoB = {0,STEP,&OCRR1B};
 init_PWM();

while(1)

{
	setServo( &servoA );
	setServo( &servoB );
 	_delay_ms( PERIOD_MS  );
	
}
koden ovan är tänkt att strunta i ADC värdet och enbart verifiera output compare delen.

Anledningen till att jag lagt alla variabler i en struct är att man lättare kan uppdatera funktionaliteten i framtiden. t.ex att de vrider sig med varandra, mot varandra eller att man lägger in fler variabler i strukten beroende på vad man vill att koden ska göra.

lycka till
Excl
Inlägg: 6
Blev medlem: 10 november 2012, 14:15:15

Re: Hjälp med kod!

Inlägg av Excl »

Jag har insett att koden jag skrev hade bara problem och bestämde mig att skriva om koden på nytt:

Kod: Markera allt

/****Servomotor***/


#include<avr/io.h>
#include<stdio.h>
#define F_CPU 1000000UL
#include<util/delay.h>



void init_ADC(void)
		{
			ADMUX=(1<<REFS0);// referens VCC=5V 
			ADCSRA=(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0);//Enable ADC,fCPU/prescaler=1MHz/8=125kHz
			
		}

void init_PWM(void)
		{
		TCCR1A=0;//Stänger av PWM signaler 
		ICR1=1249;//TOP=(fCPU/(prescaler*PWM_sig))-1=(1000000/(8*100))-1=1249
		TCCR1A|=((1<<COM1A1) | (1<<COM1B1) | (1<<WGM11));//
		TCCR1B|=((1<<WGM13) | (1<<WGM12) | (1<<CS11));// fast PWM, prescale 8
			
		}

unsigned int read_convert(unsigned char ch)
	{
		 
		
		ADMUX = (ADMUX & ~15) | ch;//Väljer ADCx kanal
		ADCSRA = (1<<ADEN);//Enable ADC
		ADMUX | (1<<ADLAR);//Vänsterjustering av ADC resultet
		ADCSRA |=(1<<ADSC);// Startat ADC konvertering

		while(ADCSRA & (1<<ADSC));// loop som kör tills konverteringen är klar

		return (ADC);//returnerar resultatet för konverteringen


int main(void)
	{

	
	init_ADC();
	init_PWM();
	read_convert();
	
	
		while(1)
		{
			DDRB|=(1<<PB5);// Pin PB5(OCR1A) är output
			ch|=(1<<MUX2);//Läser värde på PF4
			OCR1A=ADCH;//converterad värde läses på pin PB5

			DDRB|=(1<<PB6);//Pin PB6(OCR1B) är output
			ch|=(1<<MUX0) | (1<<MUX2);//läser värde på PF5
			OCR1B=ADCH;//converterad värde läses på pinPB6				
		}
	}
Dock får jag fram ett felmeddelande som jag inte riktigt förstår:

../ProjektServo.c:47: error: too few arguments to function 'read_convert'

Vad menas med detta felmeddelande?

exile:

fCPU frekvensen är 1 MHz, tiderna är mellan 1-2 ms.
Användarvisningsbild
nodanolo
EF Sponsor
Inlägg: 90
Blev medlem: 25 mars 2009, 23:05:38
Ort: Järfälla

Re: Hjälp med kod!

Inlägg av nodanolo »

Din funktion 'read_convert(unsigned char ch)' förväntar sig att du skickar med en parameter av typen unsigned char.

Men på tredje raden i 'main()' så anropar du funktionen UTAN argument, därav error.
Skriv svar