Forum: Compiler & IDEs 2 ADC und 2 Servos


von Herbert H. (klerijan)


Lesenswert?

Hi

Ich hab wieder mal ein Problem...

Ich möchte 2 Servos ansteuern und dazu 2 Potis verwenden, die Position 
des Servos soll von den Potis mittels ADC Wandlung kommen.

Mir ist (hoffentlich) so halbwegs klar was ich machen muss, aber 
irgendwie funktioniert das ganze nicht, ich schätz mal der Code passt 
nicht.

Mein Plan war folgender:
Ich führe eine ADC Wandlung durch,
sende den Wert an die PWM1
wechsle das Bit im ADMUX Register,
führe ADC Wandlung durch,
sende an PWM 2 usw

Wenn ich das ganze mit einem ADC und einem Servo mache funktioniert es, 
bei zwei leider nicht, da wackeln die Servos von eine Endstellung in die 
andere und fahren wieder zurück...

Ich hoffe ihr könnt mir bitte helfen, oder mir nen Tipp geben

Danke
lg

Hier der Code
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#define ADATE 5                     // Definition is missing in                                 
4
void pwm_init()                // some Versions of AVR Studio
5
{
6
    TCCR1A = _BV(COM1A1)           // _BV Bit Values      
7
                | _BV(COM1B1)      // Inverted Mode   
8
                | _BV(WGM11) ;     
9
    TCCR1B = _BV(WGM13) 
10
                | _BV(WGM12)    // Fast PWM
11
                | _BV(CS00);    // Prescaler
12
    ICR1 = 20000;              
13
    OCR1A = 1500;
14
  OCR1B = 1500;        
15
    DDRD = _BV(PD4)| _BV(PD5);     
16
}                                  
17
int main(void)
18
{
19
  DDRB |= (1 << 0); // Set LED as output
20
  
21
    ADMUX |= (1<<REFS0)|(1<<MUX0);           // Set reference to AVcc
22
    ADCSRA |= (1<<ADEN)|(1<<ADATE)|          // Enable ADC, Enable auto Triggering
23
            (1<<ADPS1)|(1<<ADPS0)|(1<<ADSC); // Prescaler = 8 --> DIGITAL
24
    pwm_init();      // initialize timer in PWM mode
25
    while(1)      // run forever
26
    {
27
      if((ADMUX&(1<<MUX0))){        // Check if MUX0=1
28
        while(!(ADCSRA&(1<<ADSC)));    // Wait until a conversion is finished
29
        OCR1A =ADC*2+1000;        // Calculate value for Servo
30
        ADMUX &= ~(1<<MUX0);      // Delete MUX0        
31
        }  
32
                
33
      if((ADMUX&(1<<MUX0))==0){  
34
        while(!(ADCSRA&(1<<ADSC)));    // Wait until conversion is finished  
35
        OCR1B =ADC*0.975+1000;       // Calculate value for Servo   
36
        ADMUX |= (1<<MUX0);         // Set MUX0 to 1  
37
        }  
38
   } 
39
}

von Karl H. (kbuchegg)


Lesenswert?

Herbert Huber schrieb:

>     ADCSRA |= (1<<ADEN)|(1<<ADATE)|          // Enable ADC, Enable auto
> Triggering

vergiss den Autotrigger.
Du weißt sonst nie, ob dein Umsetzen des MUX-Registers bei nächsten 
Wandlungsergebnis schon erfolgt ist oder nicht. Denn der Zeitpunkt, ab 
dem das Wandlungsergebnis korrekt dem ADC-Kanal zugeordnet werden kann, 
hängt davon ab, ob zum Zeitpunkt der Bit-Setzens bereits eine Wandlung 
im Gange ist oder nicht. Das kannst du aber nicht brauchen.

Lass den Autotrigger weg und starte jede Messung einzeln nachdem du die 
MUX Bits korrekt gesetzt hast.
1
int main()
2
{
3
  pwm_init();
4
5
  ADMUX |= (1<<REFS0);
6
  ADCSRA |= (1<<ADEN)|
7
            (1<<ADPS1)|(1<<ADPS0)|(1<<ADSC);
8
9
  while( 1 )
10
  {
11
    // Servo 1
12
    ADMUX = ADMUX & ~0x03 | 0;    // Kanal 0 aktivieren
13
    ADCSRA |= (1<<ADSC);          // Wandlung starten
14
    while((ADCSRA&(1<<ADSC)))    // aufs Ergebnis warten
15
      ;
16
    OCR1A = ADC*2 + 1000;         // und setzen
17
18
    // Servo 2
19
    ADMUX = ADMUX & ~0x03 | 1;    // Kanal 1 aktivieren
20
    ADCSRA |= (1<<ADSC);          // Wandlung starten
21
    while((ADCSRA&(1<<ADSC)))    // aufs Ergebnis warten
22
      ;
23
    OCR1B = ADC*2 + 1000;         // und setzen
24
  }
25
}

Die ganze ADC Abfragerei könntest du sinnvollerweise noch in eine 
Funktion auslagern, so das deine Hauptschleife abmagert zu
1
  while( 1 )
2
  {
3
    // Servo 1
4
    OCR1A = ADC_Read( 0 ) * 2 + 1000;
5
6
    // Servo 2
7
    OCR1B = ADC_Read( 1 ) * 2 + 1000;
8
  }
9
}

Und beachte: Die ADC Wandlung ist im Gange, SOLANGE das ADSC Bit gesetzt 
ist! Die Logik ist ganz einfach: Du setzt das Bit um die Wandlung zu 
starten und der ADC löscht es wieder wenn er fertig ist. Solange es also 
auf 1 ist, ist das Ergebnis noch NICHT fertig und du musst warten.

von Stefan E. (sternst)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Lass den Autotrigger weg und starte jede Messung einzeln

Du hast allerdings aus seinem Code einen entscheidenden Fehler in deinen 
übernommen. Schau dir nochmal das ADSC-Polling an. ;-)

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst schrieb:
> Karl Heinz Buchegger schrieb:
>> Lass den Autotrigger weg und starte jede Messung einzeln
>
> Du hast allerdings aus seinem Code einen entscheidenden Fehler in deinen
> übernommen. Schau dir nochmal das ADSC-Polling an. ;-)

Ja, habs gerade gemerkt und nachgetragen.

Nichts desto trotz: mit mehreren Kanälen kann man den Autotrigger nicht 
sinnvoll in dieser Form benutzen.

Ich werde sowieso nie verstehen, warum jeder immer wieder das Rad neu 
erfinden muss, wenn es im AVR-GCC-Tutorial fix&fertige Funktionen 
dafür gibt.

von Herbert H. (klerijan)


Lesenswert?

Vielen Dank jetzt funktioniert es :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.