Forum: Mikrocontroller und Digitale Elektronik Servo Motor Steuerung über Potentiometer


von servomoto (Gast)


Lesenswert?

Hallo!

Für ein kleines Projekt würde ich gerne einen Servomotor über ein 
Potentiometer steuern. Der Servomotor läuft bereits, und lässt sich über 
das Poti steuern. Jedoch verliert der Servo anscheinend immer wieder das 
Signal. Dreh ich das Poti langsam von 0 auf ca. 1/2 Anschlag folgt mir 
der Servo ca. 1:1, dreh ich dann etwas weiter verliert der Motor auf 
einmal die Verbindung (Windows Plugin Sound kommt) und marschiert nur 
noch in größeren einzelnen Schritten weiter. Bleib ich dann wieder auf 
dem Wert, und drehe wieder langsam in die andere Richtung so fängt er 
sich wieder und marschiert 1:1 weiter bis er das nächste mal die 
Verbindung verliert.

Habt ihr eventuell eine Ahnung was das sein könnte? Vielen Dank schonmal 
für eure Hilfe & Ideen.

LG

Kurze Daten:
Servo VILROS MicroServe SG90 (Pulsbreite von 500us-2500us)
Arduino UNO Board - ATmega328p
(Den Code sende ich sobald sich einer damit auseinander setzen möchte)

von P.Rog (Gast)


Lesenswert?

servomoto schrieb:
> (Den Code sende ich sobald sich einer damit auseinander setzen möchte)

Das könnte ein Henne-Ei-Problem sein ...

von servomoto (Gast)


Lesenswert?

1
#define F_CPU 16000000
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
uint16_t ReadChannel(uint8_t mux_adc_channel);         //ADC-auslese Funktion deklarieren
7
uint16_t adc_value;                      // Rückgabewert der Auslese-Funktion
8
uint16_t valuetosend; 
9
uint16_t move = 0;
10
uint16_t adc_enabled = 1;
11
uint16_t grad;
12
13
void adc_init (){
14
   TCCR1A =  (1<<WGM11)|(1<<COM1A1) ;    // 9-Bit PWM Modus Auflösung 1024, NICHT invertiert
15
   TCCR1B = (1<<WGM13) | (1<<WGM12)|(1<<CS12)|(1<<CS10);    // Prescale 1024, Mode 14 (CTC)
16
   DDRB |= (1<< PB1);                  // PB1(OC1A) als Ausgang definieren
17
   ICR1 = 312;
18
}
19
20
ISR(USART_RX_vect)                                  // definiert Interrupt Service Routine für USART receive complete
21
{   
22
  cli();                                          // clear interrupt enable
23
  grad = UDR0;
24
  char sendString [3];
25
26
  if(grad >= 0 && grad <= 180)                // wenn Eingabe zwischen 0-180, dann accepted
27
  {  
28
    adc_enabled = 0;
29
    uart_sendstring(" Adc Disabled ");
30
31
    int_to_string(grad, sendString);
32
    uart_sendstring(sendString);
33
    move = grad * 5.688;
34
    OCR1A = (move / 35 ) + 8;
35
  }
36
  else                                            // wenn nicht, nochmalige Eingabe
37
  {  
38
    adc_enabled = 1;
39
    uart_sendstring("adc enabled");
40
  }
41
  
42
  sei();                                          // enable global Interrupt
43
}
44
45
// MAIN
46
47
int main(void)
48
{
49
   uart_init(115200);
50
   adc_init();
51
   sei();
52
53
   while(1)
54
     {  if(adc_enabled == 1)
55
     {
56
        adc_value = ReadChannel(0);             // Channel 0 auslesen (Wert 0...1023)
57
          OCR1A = (adc_value / 35 ) + 8;       // PWM-Vergleichswert setzen, werte müssen zwischen 8 und 32 liegen
58
     }
59
   }
60
   return 0;
61
 } 
62
63
// Auslesen des ADC
64
65
uint16_t ReadChannel(uint8_t mux_adc_channel)
66
{  
67
   ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1)| (1<<ADPS0);   // Frequenzvorteiler auf 32 setzen und ADC aktivieren
68
   ADMUX = mux_adc_channel;                           // übergebenen Kanal waehlen
69
   ADMUX |= (0<<REFS1) | (1<<REFS0);                   // interne Referenzspannung nutzen
70
   
71
//dummy readout
72
   ADCSRA |=(1<<ADSC);          // start ADC Conversio
73
   while( ADCSRA & (1<<ADSC) ) {};      // auf abschluss von Wandlung warten
74
   ADCSRA &= ~(1<<ADEN);                // ADC deaktivieren ("Enable-Bit" auf LOW setzen)
75
 
76
  return ADCW;
77
}

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

Vergiss deinen Plan den Servo über den Arduino besaften zu wollen und 
gönne dem Servo seine eigene Stromversorgung mit ausreichend Strom!

von servomoto (Gast)


Lesenswert?

Gut, danke. Werde es demnächst ausprobieren, hab nun aber noch 2 weitere 
Probleme :D

Und zwar: Soll über UART die aktuelle Grad Position gesendet werden, so 
dass der PC User immer weiß welche Stellung der Servo gerade hat. Dies 
funktioniert ebenfalls bereits, jedoch unterbricht die Datenübertragung 
immer wieder nach unterschiedlicher Zeit. Mittels _delay_ms(1000) sollte 
mir die Schnittstelle am PC jede Sekunde die aktuelle Position senden.

Hier der UART-Code und die Umrechnung auf Grad in dem ich die Werte vom 
Poti (0-1023) auf 0-180° umrechne (/5,68).
1
//UART
2
void uart_init (uint32_t baudrate)
3
{
4
  uint16_t  ubrr = (F_CPU / 8 / baudrate) - 1;     
5
                            
6
  UBRR0H = (uint8_t) (ubrr >> 8);      
7
  UBRR0L = (uint8_t) (ubrr & 0xff);    
8
9
    UCSR0A |= (1<<U2X0);  
10
                    
11
    UCSR0B |= ( 1<<TXEN0);   
12
  UCSR0B |= ( 1<<RXEN0);   
13
}
14
15
void uart_transmit (uint8_t data)
16
{
17
  while ( (UCSR0A & (1 << UDRE0)) == 0)  ;    
18
  UDR0 = data;              
19
}
20
21
uint8_t uart_receive ()
22
{
23
  while ( (UCSR0A & (1 << RXC0)) == 0)  ;   
24
  return(UDR0);                
25
}
26
27
void int_to_string ( uint16_t number, char *targetString )  
28
{
29
  int8_t digit;
30
31
  for (digit = 3; digit >= 0; digit--)
32
  {
33
    targetString[digit]= number%10 + '0';  
34
    number = number/10;
35
  }
36
  targetString[4] = 0;
37
}
38
39
void uart_sendstring ( char * str )
40
{
41
  while ( * str != 0 )
42
  {
43
    uart_transmit ( * str);
44
    str++;
45
  }
46
}
47
48
// MAIN
49
int main(void)
50
{
51
   char sendString [3];
52
   uart_init(115200);
53
54
   TCCR1A =  (1<<WGM11)|(1<<COM1A1) ;                  // 9-Bit PWM Modus Auflösung 1024, NICHT invertiert
55
   TCCR1B = (1<<WGM13) | (1<<WGM12)|(1<<CS12)|(1<<CS10);    // Prescale 1024, Mode 14 (CTC)
56
   DDRB |= (1<< PB1);                                // PB1(OC1A) als Ausgang definieren
57
   
58
   ICR1 = 312;
59
   
60
  while(1)
61
     {  
62
        adc_value = ReadChannel(0);                    // Channel 0 auslesen (Wert 0...1023)
63
        OCR1A = (adc_value / 35 ) + 8;                 // PWM-Vergleichswert setzen, werte müssen zwischen 8 und 32 liegen
64
65
      ADCSRA |= (1<<ADSC);
66
67
    while (ADCSRA & (1<<ADSC));
68
69
    uint16_t result = ADCL;
70
    result = result + (ADCH<<8);
71
72
    valuetosend = result / 5.68;
73
74
    int_to_string (valuetosend, sendString);
75
    uart_sendstring (sendString);
76
    uart_sendstring ("Grad ");
77
78
    PORTB ^= (1<<5);
79
80
    _delay_ms(1000);
81
   }
82
 return 0;
83
 }

von holger (Gast)


Lesenswert?

>hab nun aber noch 2 weitere Probleme :D

Löse erst mal dein erstes Problem. Weitere Probleme
sind solange das nicht geschehen ist nicht wichtig.

von servomoto (Gast)


Lesenswert?

Hab jetzt aber leider keine 5V Versorgung bei der Hand und würde gerne 
die anderen Probleme noch beseitigen. Ich bin leider alles schon zu oft 
durchgegangen und anscheinend verlese ich mich immer wieder an einer 
Stelle, da beides ja schon ziemlich gut funktioniert würde ich sagen, 
kann es nur an einer Kleinigkeit liegen. Kennt sich von euch zufällig 
jemand gut mit UART aus und verwendet ev. auch HTerm als 
Schnittstellensoftware?

von holger (Gast)


Lesenswert?

>kann es nur an einer Kleinigkeit liegen.

Nein, nur an deiner Unfähigkeit.

>   char sendString [3];
>    int_to_string (valuetosend, sendString);
1
void int_to_string ( uint16_t number, char *targetString )  
2
{
3
  int8_t digit;
4
5
  for (digit = 3; digit >= 0; digit--) // digit wird hier negativ, sendstring hat nur drei Byte, der erste Zugriff ist auf Byte vier
6
  {
7
    targetString[digit]= number%10 + '0';  
8
    number = number/10;
9
  }
10
  targetString[4] = 0; // sendstring hat nur drei Byte. Hier wird auf das fünfte zugegriffen.
11
}

von servomoto (Gast)


Lesenswert?

Danke für den Hinweis. Hab leider schon unzählige verschiedene 
Code-Versionen an denen ich rumbastle. Bin jetzt der Meinung dass die 
Versorung einfach zu wenig ist, somit die Verbindung verloren geht und 
daher dann auch das Schnittstellenprogramm nicht mehr arbeitet.

Das einzige was noch fehlt:

Der Servo soll durch Eingabe in HTerm (Dez.) (0-180°) an die gewünschte 
Position fahren. Bekommt der uC nun also eine Eingabe, so soll er den 
ADC deaktivieren (sodass Änderung mittels Poti kurzzeitig nicht mehr 
möglich ist), und an die gewünschte Position fahren. Ist dies 
abgearbeitet, so soll er den ADC wieder aktivieren.

Ich habe oben schon ein Interrupt geschrieben. Teste ich das ganze mit 
einer Hilfsvariable, so bekomm ich immer einen Wert gesendet, obwohl der 
ADC deaktiviert ist, und erst ein Wert kommen dürfte wenn ich eine 
Eingabe getätigt habe. Hat hierzu jemand vielleicht einen 
Lösungsvorschlag, oder gar einen Weg?

LG

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.