Forum: Compiler & IDEs Servo miitels Timer-Overflow


von Martin.. (Gast)


Lesenswert?

Hallo,
mit folgendem Code versuche ich, einen Servo anzusteuern.
Leider bewegt sich der Servo nicht.
1
void servo(uint8_t p)
2
{
3
 uint8_t i,j;
4
 TCCR2 |= (1<<CS22) | (1<<CS20); //Prescaler 128
5
 TIMSK |= (1<<TOIE2);             //Interrupt bei Timer2 enablen
6
 TCNT2 = 255-(0.001+p*0.000025);  //Timer vorladen
7
 DDRB = 0xFF;                     
8
 PORTB |= (1<<PB0);               //PB0 = 1
9
 while(serv1 != 1)                //dann solange warten bis der
10
Interrupt ausgelöst wird
11
  {}                               
12
 PORTB |= (0<<PB0);         //danach PB0 wieder low und damit das
13
20ms-low Signal eingeleitet
14
 serv1 = 0;
15
 TCNT2 = 0;
16
 
17
 for(i=0;i==10;i++)       //diese Schliefe soll 20ms dauern
18
  {
19
   while(serv2 != 1)
20
   {}
21
   TCNT2 = 0;
22
   serv2 = 0;
23
  }
24
}
25
SIGNAL(SIG_OVERFLOW0) 
26
{ 
27
 count = 1; 
28
} 
29
 
30
SIGNAL(SIG_OVERFLOW2)
31
{
32
 serv1++;
33
 serv2++;
34
}

Erkennt ihr da einen (Denk-)Fehler?
Gruß
Martin

von Martin.. (Gast)


Lesenswert?

Hab noch was vergessen:
AVR: ATMega8
Clock: 16Mhz

von Rolf Magnus (Gast)


Lesenswert?

Wie sind serv1 und serv2 definiert? Sie müssen volatile sein.

> TCNT2 = 255-(0.001+p*0.000025)

Wie kommst du auf diese Rechnung? Du weißt schon, daß TCNT2 nur
Ganzzahlen kennt?
Beispiel für p=100:

255-(0.001 + 0.0025) = 255 - 0.0035 = 254.9965
Davon wird der Nachkomma-Teil abgeschnitten und TCNT2 wird auf 254
gestellt. Dieser Wert kommt übrigens für jeden beliebigen Wert von p
raus.

> for(i=0;i==10;i++)       //diese Schliefe soll 20ms dauern

Die Schleife funktioniert so nicht. Du setzt erst i auf 0, dann läuft
die Schleife solange, wie i == 10 ist. Anders ausgedrückt: Die Schleife
läuft gar nicht.

von Martin.. (Gast)


Lesenswert?

Hallo,
serv1+2 sind volatile und haben am Anfang den Wert 0.
Für P werden vorraussichtlich Werte im Bereich von 0 - 40 angegeben.
Schleife habe ich jetzt so geändert:

for(i=0;i<10;i++)       //diese Schliefe soll 20ms dauern
  {
   while(serv2 != 1)
   {}
   TCNT2 = 0;
   serv2 = 0;
  }

Geht aber leider immer noch nicht.

von Martin.. (Gast)


Lesenswert?

Wie gehen Code-Tags??

Ich habe jetzt nochmal ein paar Sachen verändert, hier der gesamte
Code, der die Servos leider immer nochnicht bewegt:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

volatile uint8_t count = 0;
volatile uint8_t serv1 = 0;
volatile uint8_t serv2 = 0;
volatile uint8_t pos = 0;

void init_usart(void)
{
 UBRRL |= 0b01100111;
 UCSRB = (1<<TXEN) | (1<<RXEN);
 UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
 while (!(UCSRA & (1<<UDRE)));
 UDR = s;
}

void send_string(char *s)
{
 while(*s != '\0')
  {
   send_char(*s);
   s++;
  }
}

void waitms(uint16_t s)
{
 uint16_t i;
 uint8_t j;
 TCCR0 |= (1<<CS01) | (1<<CS00);  //Vorteiler 64
 TIMSK |= (1<<TOIE0);
 for(i=0;i<s;i++)
  {
   count = 0;
   TCNT0 = 6;                 // Timer auf 6 vorladen, da
(16000000/64)^-1 = 0.000004s
   while(count != 1)
    {}
}                           // -> 1ms = 0.000004s*250

 TIMSK |= (0<<TOIE0);
}

void servo(uint8_t p)
{
 uint8_t i,j;
 serv1 = 0;
 serv2 = 0;
 TCCR2 |= (1<<CS22) | (1<<CS20); //Prescaler 128
 TIMSK |= (1<<TOIE2);             //Interrupt bei Timer2 enablen
 TCNT2 = 255-(0.001+p*0.000025)/0.000008 ;  //Timer vorladen
 DDRB = 0xFF;
 PORTB |= (1<<PB0);               //PB0 = 1


  while(serv1 != 1)                //dann solange warten bis der
Interrupt ausgelöst wird
   {}
  PORTB |= (0<<PB0);         //danach PB0 wieder low und damit das
20ms-low Signal eingeleitet
  serv2 = 0;
  TCNT2 = 0;
  pos = 1;

  for(i=0;i<10;i++)       //diese Schliefe soll 20ms dauern
   {                       //Counter zählt von 0 bis 255, löst
Interrupt aus und TCNT2 wird zurückgesetzt
    while(serv2 != 1)     //Ein Durchlauf dauert 0.000008*256 = 0.002s,
also 10 Durchläufe
    {}
    TCNT2 = 0;
    serv2 = 0;
 }
}
SIGNAL(SIG_OVERFLOW0)
{
 count = 1;
}

SIGNAL(SIG_OVERFLOW2)
{
 serv1 = 1;
 serv2 = 1;
}
int main(void)
{
 sei();
 unsigned int i;
 char a = "112:444";
 i = 0;
 init_usart();
 do
  {
   send_string(a);
   servo(30);
   waitms(1000);
  }
  while (i == 0);
}


Gruß
Martin

von Karl H. (kbuchegg)


Lesenswert?

Ohne jetzt das komplette Timing analysiert
zu haben

main()
....

 do
  {
   send_string(a);
   servo(30);
   waitms(1000);
  }

Du gibst dem Servo nur alle heilige Zeiten mal einen
Puls. Das Servo moechte aber staendig einen Puls
haben. Je nach Servoelektronik kann es sein, dass
Dein Servo diesen Puls ganz einfach als Fehlpuls
auffasst und ignoriert.

Servos wollen alle 20 ms einen Puls der Laenge 1 bis
2 ms. Staendig!

Das ist mir jetzt nur so auf die Schnelle aufgefallen.
Hast Du schon mal am PortPin nachgesehen, ob da auch
Pulse rauskommen?

von Martin.. (Gast)


Lesenswert?

Hallo,
also geht es nicht, dem Servo nur Impulse zu schicken, wenn man grad
Lust hat. Dann muss ich das natürlich überarbeiten.
Würde es z.B. gehen, den Timer anzustossen und der läuft einfach vor
sich hin, produziert die Impulse und wenn ich ne andere Position will,
Impulsbreite ändern?
Wird durch die Interrupts, in denen die Impulse entstehen, nicht der
ganze Programmablauf behindert?
Gruß
Martin

von Martin.. (Gast)


Lesenswert?

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

volatile uint8_t count = 0;
volatile uint8_t serv1 = 0;
volatile uint8_t serv2 = 0;
volatile uint8_t pos = 0;

void init_usart(void)
{
 UBRRL |= 0b01100111;
 UCSRB = (1<<TXEN) | (1<<RXEN);
 UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
 while (!(UCSRA & (1<<UDRE)));
 UDR = s;
}

void send_string(char *s)
{
 while(*s != '\0')
  {
   send_char(*s);
   s++;
  }
}

void waitms(uint16_t s)
{
 uint16_t i;
 uint8_t j;
 TCCR0 |= (1<<CS01) | (1<<CS00);  //Vorteiler 64
 TIMSK |= (1<<TOIE0);
 for(i=0;i<s;i++)
  {
   count = 0;
   TCNT0 = 6;                 // Timer auf 6 vorladen, da
(16000000/64)^-1 = 0.000004s
   while(count != 1)
    {}
}                           // -> 1ms = 0.000004s*250

 TIMSK |= (0<<TOIE0);
}

void servo(uint8_t p)
{
 uint8_t i,j;
 TCCR2 |= (1<<CS22) | (1<<CS20); //Prescaler 128
 TIMSK |= (1<<TOIE2);             //Interrupt bei Timer2 enablen
 pos = 255-(0.001+p*0.000025)/0.000008;  //Timer vorladen
 DDRB = 0xFF;
}
SIGNAL(SIG_OVERFLOW0)
{
 count = 1;
}

SIGNAL(SIG_OVERFLOW2)
{

 if(serv1 == 0)
  {
   TCNT2 = pos;
   serv1 = 1;
   PORTB = (1<<PB0);
  }

 if(serv1 == 1)
  {
   PORTB = (0<<PB0);
   TCNT2 = 0;
   if(serv2 == 10)
    {
   serv1 = 0;
   serv2 = 0;
  }
   else
    {
   serv2++;
   TCNT2 = 0;
  }
  }
}
int main(void)
{
 sei();
 unsigned int i,j;
 char a = "112:444";
 i = 0;
 init_usart();
 DDRB = 0xFF;
 j = 0;
 do
  {
   send_string(a);
   servo(j++);
   PORTB ^= (1<<PB1);
   waitms(500);
  }
  while (i == 0);
}

Jetzt bewegt sich der Servo zumindest voll an den einen Ausschlag, was
zwar auch nicht gewollt ist, aber immerhin tut sich was. Kann hier
jemand erkennen, warum sich der Servo der Erhöhung von "j" nicht von
einer Seite zur anderen bewegt?

von Martin.. (Gast)


Lesenswert?

Und so funktionierts jetzt endlich:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

volatile uint8_t count = 0;
volatile uint8_t serv1 = 0;
volatile uint8_t serv2 = 0;
volatile uint8_t pos = 0;

void init_usart(void)
{
 UBRRL |= 0b01100111;
 UCSRB = (1<<TXEN) | (1<<RXEN);
 UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
 while (!(UCSRA & (1<<UDRE)));
 UDR = s;
}

void send_string(char *s)
{
 while(*s != '\0')
  {
   send_char(*s);
   s++;
  }
}

void waitms(uint16_t s)
{
 uint16_t i;
 uint8_t j;
 TCCR0 |= (1<<CS01) | (1<<CS00);  //Vorteiler 64
 TIMSK |= (1<<TOIE0);
 for(i=0;i<s;i++)
  {
   count = 0;
   TCNT0 = 6;                 // Timer auf 6 vorladen, da
(16000000/64)^-1 = 0.000004s
   while(count != 1)
    {}
}                           // -> 1ms = 0.000004s*250

 TIMSK |= (0<<TOIE0);
}

void init_servo(void)
{
 serv1 = 0;
 serv2 = 0;
}

void servo(uint8_t p)
{
 uint8_t i,j;
 TCCR2 |= (1<<CS22) | (1<<CS20); //Prescaler 128
 TIMSK |= (1<<TOIE2);             //Interrupt bei Timer2 enablen
 pos = 255-(0.001+p*0.000025)/0.000008;  //Timer vorladen
 DDRB = 0xFF;
}

SIGNAL(SIG_OVERFLOW0)
{
 count = 1;
}

SIGNAL(SIG_OVERFLOW2)
{

 if(serv1 == 0)
  {
   serv1 = 1;
   PORTB = (1<<PB0);
  }

 if(serv1 == 1)
  {
   PORTB = (0<<PB0);
   TCNT2 = 0;
   if(serv2 == 10)
    {
   serv1 = 0;
   serv2 = 0;
   TCNT2 = pos;
   PORTB = (1<<PB0);
  }
   else
    {
   serv2++;
   TCNT2 = 0;
  }
  }
}
int main(void)
{
 sei();
 unsigned int i,j;
 char a = "112:444";
 i = 0;
 init_usart();
 init_servo();
 DDRB = 0xFF;
 j = 0;
 do
  {
   send_string(a);
   servo(j++);
   PORTB ^= (1<<PB1);
   waitms(500);
  }
  while (i == 0);
}


Wäre trotzdem nett, wenn mir jmd. sagt, wie ich hier Code-Tags
verwenden kann.
Gruß
Martin

von karlheinz (Gast)


Lesenswert?

mal einen ganz einfachen servocode.
kanal ist der portpin wo er angeschlossen ist.
0-180 grad ist ist der wert 0-1850 bei meinen servo von conrad für 5
euro. muss man ermitteln, kann immer ein bisschen schwanken.
930~90grad, 470~45grad. kommt sehr gut hin.
delay-routine habe ich mal gefunden, supergenau.
servo läuft mit avr16-8mhz.


#include <inttypes.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define delay_us_(us)  _delayFourCycles_( ( ( 1*(F_CPU/4000) )*us)/2005
)

void _delayFourCycles_(uint16_t z)
{
  uint16_t i;

    for (i=0; i<z; i++)
    asm volatile("nop");
}

void delay_ms_(uint16_t z)
{
  uint16_t i;

  for (i=0; i<z; i++)
    delay_us_(999);
}

void servo_srf04(uint8_t kanal, uint16_t servo_vorgabe)
{
    uint16_t servo_wert, servo_wert1, servo_wert2;

  for (servo_wert=0; servo_wert<servo_vorgabe; servo_wert+=10)
  {
    servo_wert1=servo_wert+350;
    servo_wert2=20000-servo_wert1;
    PORTB|=(1<<kanal);
    delay_us_(servo_wert1);
    PORTB&=~(1<<kanal);
    delay_us_(servo_wert2);
  }
}

int main(void)
{
        uint8_t vorn=6;
  uint8_t hinten=7;

  DDRB|=(1<<PB6);
  PORTB&=~(1<<PB6);
  DDRB|=(1<<PB7);
  PORTB&=~(1<<PB7);

  servo_srf04(vorn,930);
  delay_ms_(1000);

  servo_srf04(vorn,470);
  delay_ms_(1000);

  servo_srf04(vorn,1850);
  delay_ms_(1000);

  servo_srf04(vorn,1400);
  delay_ms_(1000);

  servo_srf04(hinten,930);
  delay_ms_(1000);

  servo_srf04(hinten,470);
  delay_ms_(1000);

  servo_srf04(hinten,1850);
  delay_ms_(1000);

  servo_srf04(hinten,1420);
  delay_ms_(1000);
}

von karlheinz (Gast)


Lesenswert?

wenn ich den raum scanne, nehme ich den 0-180grad schwenk :

void servo_srf04(uint8_t kanal)
{
    uint16_t servo_wert, servo_wert1, servo_wert2;

  for (servo_wert=0; servo_wert<1850; servo_wert+=10)
  {
    servo_wert1=servo_wert+350;
    servo_wert2=20000-servo_wert1;
    PORTB|=(1<<kanal);
    delay_us_(servo_wert1);
    PORTB&=~(1<<kanal);
    delay_us_(servo_wert2);
    delay_ms_(50);
  }
  for (servo_wert=1850; servo_wert>10; servo_wert-=10)
  {
    servo_wert1=servo_wert+350;
    servo_wert2=20000-servo_wert1;
    PORTB|=(1<<kanal);
    delay_us_(servo_wert1);
    PORTB&=~(1<<kanal);
    delay_us_(servo_wert2);
    delay_ms_(50);
  }
}

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.