www.mikrocontroller.net

Forum: Compiler & IDEs Servo miitels Timer-Overflow


Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
mit folgendem Code versuche ich, einen Servo anzusteuern.
Leider bewegt sich der Servo nicht.
void servo(uint8_t p)
{
 uint8_t i,j;
 TCCR2 |= (1<<CS22) | (1<<CS20); //Prescaler 128
 TIMSK |= (1<<TOIE2);             //Interrupt bei Timer2 enablen
 TCNT2 = 255-(0.001+p*0.000025);  //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
 serv1 = 0;
 TCNT2 = 0;
 
 for(i=0;i==10;i++)       //diese Schliefe soll 20ms dauern
  {
   while(serv2 != 1)
   {}
   TCNT2 = 0;
   serv2 = 0;
  }
}
SIGNAL(SIG_OVERFLOW0) 
{ 
 count = 1; 
} 
 
SIGNAL(SIG_OVERFLOW2)
{
 serv1++;
 serv2++;
} 

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

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab noch was vergessen:
AVR: ATMega8
Clock: 16Mhz

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Martin.. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: karlheinz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);
}

Autor: karlheinz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);
  }
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.