www.mikrocontroller.net

Forum: Compiler & IDEs Hilfe! Modellbauservo & Interrupts


Autor: Andreas W. (adamdreyer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen!
Ich habe ein Problem mit meinem AVR.
Ich würde gerne mit einem ATTiny85 ein Modellbauservo mit einem Sinus 
ansteuern. Der Sinus soll 2,5 Hz haben, damit kann ich dann ein von der 
Länge passendes Pendel zum schwingen bringen.

Modellbauservos benötigen alle 20ms einen Puls von 0,9 bis 2,1 ms Länge. 
Die Pulslänge bestimmt die Soll-Lage: 0,9 ist ganz links; 1,5 ist in der 
Mitte; 2,1 ist ganz rechts.

Den Modellbauservo-Beitrag im Forum habe ich mir schon durchgelesen, die 
Lösung dort kommt leider für einen Attiny85 nicht in Frage: Ich habe 
keinen 16 Bit Timer.

Mein Programm soll einen 20ms Interrupt haben, der das Signal und den 
anderen Timer-Interrupt aktiviert. (Timer 0, Zählmarke OCR0A)

Der andere Timer soll nach der passenden Zeit (zwischen 0,9 und 2,1ms) 
auch einen Interrupt haben. Nach diesem Interrupt wird der zweite Timer 
wieder deaktiviert und eine neue Signallänge berechnet. (Timer 1, 
Zählmarke OCR1B)

Kann mir einer von euch helfen? Ich bin schon zwei Tage dabei und finde 
den Fehler einfach nicht! Die einzelnen Timersignale kommen raus, die 
habe ich mit dem Oszi gemessen. Es scheint als werden die Interrupts gar 
nicht erst aktiviert (oder nicht auf die richtige Art und Weise).
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h> 

#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))

#define WARTEN  0
#define SIGNAL_STARTEN 2 //Servosignal starten
#define SIGNAL_STOPP 3


char cSREG;

uint32_t servoposition;

char communication;



ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
{
    cli();
  cSREG = SREG;
    // Interrupt Code /

  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
  SREG = cSREG;

  sei();
}

/* Timer/Counter0 Compare Match A */

ISR (TIM0_COMPA_vect)
{
    cli();
  cSREG = SREG;
    // Interrupt Code /
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
  SREG = cSREG;

  sei();
}

// Definiert einen Sinus von 2,5 Hz; mit 50Hz abgetastet.
const unsigned char positionen[9] = {56, 66, 93, 120, 131, 120, 93, 66};
#define MAXPOS 9

/*
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
zwischen 1,9 und 2,1 ms lang sein. Bei 2ms steht das Servo neutral; bei 1,9ms bewegt es sich
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
bewegt, um ein Pendel mit 2,5s Eigenfrequenz zum schwingen anzuregen.
*/


int main(void)
{
       // Initialisierungen: globale Variablen
  servoposition = 0;
  
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
      // daraus ergibt sich 1 MHz
      // Das finde ich gut, daher keine eigenen Einstellungen
      // Watchdog deaktivieren
  WDTCR = 0x00;
  GIMSK = 0x00; // Pin Change Interrupts von außen deaktivieren
  ADCSRA = 0x00; //ADC deaktivieren
  DIDR0 = 0x00; // Keine Analogen Eingänge
  
  // Portpin
  MCUCR = 0x40; // Pull Up Widerstände deaktivieren
  PORTB = 0x00; // PortB 4, also 0x10 wird als Signalgeber verwendet
  DDRB = 0x3F; // Data Direction Register, komplett als Ausgang

  //PINB  // Eingangsregister

/*********************************************************************************/
// Timer 1 einstellen
  TCCR1 = 0x05;   // Do not clear on compare match mit OCR1C; 
          // Sysclock/16 ist die Timer Clock: nach 56 Zählern sind 0,9ms vorbei.
  OCR1B = 56;   //  OCR1B 56 an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.

/*********************************************************************************/
// Timer 0 einstellen
  TCCR0A = 0x42;  // Toggle OCR0A on compare match
          // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
          // wird der Timer bei Compare Match mit OCR0A zurückgesetzt 
  TCCR0B = 0x04;  // Kein Output force
          // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
  OCR0A =  78;  // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
  
/*********************************************************************************/  
  TIMSK = 0x10;   // Timer 0 OCR0A Compare Match Interrupt enabled,
          // Timer 1  OCR1B Compare Match vorerst disabled
  GTCCR = 0x00;   // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
  SREG |= 0x80;   // Interrupts aktivieren
  sei();
  
/*********************************************************************************/   
   
   while(1) { 
     // Statemachine              
    switch (communication)
    {
/**************************************************/
    case WARTEN: { // Warte auf nächsten Interrupt compare

      break;
    }

/**************************************************/
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
// Signal soll gestartet werden.
    case SIGNAL_STARTEN:{ 

      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten

      TIMSK = 0x30; // Interrupts OCIE1A aktivieren
      communication = WARTEN;

      break;
    }


/**************************************************/
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
// Signal soll gestoppt werden.

    case SIGNAL_STOPP:{ 
      // Timer 1 stoppen
      cli();
      CLEAR_BIT(PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
      TIMSK = 0x10; // Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
      sei();      
      // Neues Signal berechnen
      // Das Signal wird in  OCR1B geschrieben. 56 ist Vollausschlag nach links,
      // 75+56 ist vollausschlag nach rechts,
      // 37 + 56 ist die Neutrallage.
      OCR1B = positionen[servoposition];
      servoposition++;
      if (servoposition > MAXPOS)
        servoposition = 0;
      communication = WARTEN;

      break;
    }
/**************************************************/
    default : {
      communication = WARTEN;
      break;
      }
    }
  }                        
 
 /***********************************************************************/
  
   return 0;    
}



Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Andreas,

in deinem Code fallen sofort folgende Dinge auf:

1. communication ist nicht volatile, sollte es aber sein. Der Modifier 
'volatile' besagt dem Compiler, dass keine Optimierungen beim Zugriff 
auf die Variable vorgenommen werden dürfen. Sonst kann es passieren, 
dass die Variable in einem Register gehalten wird und das Programm von 
einer Änderung in einer ISR nichts mitbekommt.

2. In ISRs sind Interrupts bereits beim Eintritt deaktiviert und werden 
beim Austritt automatisch wieder aktiviert. Ebenso wird SREG vom 
Compiler selbst gesichert. cli() und sei() usw. sind deshalb nicht 
nötig.

3. Es empfielt sich, bei Registerzuweisungen die gesetzten Bits einzeln 
anzugeben, also z.B. TIMSK = (1<<OCIE0A) anstatt TIMSK = 0x10, weil das 
man hier sofort die Bedeutung des Codes sehen kann ohne erstmal im 
Datenblatt nachschauen zu müssen.  Zudem können sich die Bitpositionen 
je nach AVR ändern, der Bitname bleibt aber meistens gleich, sodass das 
Programm einfacher auf andere AVR-Controller portiert werden kann.

MfG Mark

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die 20ms brauchst du nicht einhalten.
Du kannst nach 3ms oder sogar noch früher den nächsten Impuls senden.
Die 20ms sind historisch bedingt.
Ergo: Dein Programm (das ich mir nicht näher angesehen habe) ist zu 
kompliziert.
Bei dem Rest stimme ich Mork völlig zu.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das SREG im Interrupt zu sichern ist überhaupt nicht nötig unter C.

Autor: Andreas W. (adamdreyer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, das war alles schon mal sehr hilfreich.

Ich habe die Schreibweise auf die (für mich derzeit noch 
unübersichtlichere) "saubere" Schreibweise geändert. Wahrscheinlich 
gewöhne ich mich noch dran. Überzeugt hat mich das 
Kompatibilitäts-Argument.

Dann gings.

Keine Ahnung, welches Bit ich wo falsch gesetzt hatte....

Jettzt bleibt noch ein Problem:

Obwohl ich da meinen Vektor mit 9 Werten für den Sinus definert habe, 
springt das Signal von links auf rechts und wieder zurück, hält sich 
manchmal an die Kurve und manchmal nicht. hm.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h> 

#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))

#define WARTEN  0
#define SIGNAL_STARTEN 2 //Servosignal starten
#define SIGNAL_STOPP 3


char cSREG;

uint32_t servoposition;

volatile char communication;



ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
{
    // Interrupt Code /
  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
}

/* Timer/Counter0 Compare Match A */

ISR (TIM0_COMPA_vect)
{
    // Interrupt Code /
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
}

// Definiert einen Sinus von 2,5 Hz; mit 50Hz abgetastet.
const unsigned char positionen[9] = {56, 66, 93, 120, 131, 120, 93, 66};
#define MAXPOS 9

/*
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
zwischen 1,9 und 2,1 ms lang sein. Bei 2ms steht das Servo neutral; bei 0,9ms bewegt es sich
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
bewegt, um ein Pendel mit 2,5hz Eigenfrequenz zum schwingen anzuregen.
*/

int main(void)
{
       // Initialisierungen: globale Variablen
  servoposition = 0;
  
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
      // daraus ergibt sich 1 MHz
      // Das finde ich gut, daher keine eigenen Einstellungen

  WDTCR = 0;  // Watchdog deaktivieren
  GIMSK = 0;  // Pin Change Interrupts von außen deaktivieren
  ADCSRA = 0;  // ADC deaktivieren
  DIDR0 = 0;  // Keine Analogen Eingänge
  
  // Portpin
  MCUCR = 0 |(1<<PUD);// Pull Up Widerstände deaktivieren
  PORTB = 0;       // Port B komplett auf Null
  DDRB = 0x3F;     // Data Direction Register, komplett als Ausgang

/*********************************************************************************/
// Timer 1 einstellen

  OCR1B = 56;   // OCR1B 56: an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.

/*********************************************************************************/
// Timer 0 einstellen
  TCCR0A = 0|((1<<COM0A0)|(1<<WGM01));// Toggle OCR0A on compare match
          // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
          // wird der Timer bei Compare Match mit OCR0A zurückgesetzt
  TCCR0B = 0|(1<<CS02);  // Kein Output force
          // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
  OCR0A =  78;  // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
  
/*********************************************************************************/  
  TIMSK = 0|(1<<OCIE0A);   // Timer 0 OCR0A Compare Match Interrupt enabled,
          // Timer 1  OCR1B Compare Match vorerst disabled
  GTCCR = 0x00;   // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
  SREG  = 0 | (1 << 7);   // Interrupts aktivieren
  sei();
  
/*********************************************************************************/   
   
   while(1) { 
     // Statemachine              
    switch (communication)
    {
/**************************************************/
    case WARTEN: { // Warte auf nächsten Interrupt compare

      break;
    }

/**************************************************/
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
// Signal soll gestartet werden.
    case SIGNAL_STARTEN:{ 
      cli(); //Interrupts sperren
      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten
      TCCR1 = 0|((1<<CS12)); // Timer starten mit Sysclock/8
      SET_BIT(TIMSK, OCIE1B);// Interrupts OCIE1B aktivieren
      sei(); //Interrupts wieder freigeben
      communication = WARTEN;
      break;
    }


/**************************************************/
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
// Signal soll gestoppt werden.

    case SIGNAL_STOPP:{ 
      // Timer 1 stoppen
      cli(); //Interrupts sperren
      CLEAR_BIT(  PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
      TCCR1 = 0; //Timer stoppen
      CLEAR_BIT(  TIMSK, OCIE1B);// Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
      sei(); //Interrupts wieder freigeben  
      // Neues Signal berechnen
      // Das Signal wird in  OCR1B geschrieben. 56 ist Vollausschlag nach links,
      // 75+56 ist vollausschlag nach rechts,
      // 37 + 56 ist die Neutrallage.
      OCR1B = positionen[servoposition];
      servoposition++;
      if (servoposition > MAXPOS)
        servoposition = 0;
      communication = WARTEN;
      break;
    }
/**************************************************/
    default : {
      communication = WARTEN;
      break;
      }
    }
  }                        
 
 /***********************************************************************/
  
   return 0;    
}


Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Solche Fehler liegen meistens an so Sachen wie fehlende 
Mutexe/Semaphores. Das heißt, du hast eine Variable, die in einer ISR 
als auch im Hauptprogramm beschrieben werden aber dessen Schreibzugriff 
nicht synchronisiert wird.

Während die Variable im Hauptprogramm geschrieben wird, sollten die 
Interrupts deaktiviert sein.

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>if (servoposition > MAXPOS)

MAXPOS ist doch weiter oben auf 9 gesetzt worden.
Der Vergleich greift also bei ServoPosition = 10.
Dein Feld hat aber nur 8 Einträge...

Autor: Andreas W. (adamdreyer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo, das wars.
Jetzt gehts. Mit dem Signal steuere ich zwei Servos, und mit den zwei 
Servos steuere ich acht Spinnenbeine.
Sie kann jetzt strampeln wenn man sie an einem Faden unter die Decke 
hängt :-)

Nach einigen Sekunden setzt allerdings immer das Programm aus und die 
Beine bewegen sich nicht mehr.

Also noch mal ran. seufz

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du denn jetzt mal den Zugriff auf die Variable communication 
Interrupttauglich gemacht?

Autor: Andreas W. (adamdreyer)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ja, jetzt ja. Ich poste gleich noch mal den aktuellen Quelltext.

Momentan scheint es mir, als verplant er ab einer gewissen Zeitgrenze 
schlichtweg, die Signaldauer weiter anzupassen. Komisch ist, dass es 
erst nach 10 Sekunden, manchmal auch noch später passiert -> also wenn 
alle Schleifen und ISRs schon hunderte male aufgerufen worden sind.
Auf Lötfehler hab ichs auch schon untersucht - viel konnte man da nicht 
falsch machen.

Einen Watchdog mit sechzig Millisekunden habe ich mittlerweile eingebaut 
- der wird alle zwanzig Millisekunden zurückgesetzt. Hat auch nichts 
geändert, nach einigen Sekunden gehts nicht mehr.

Ein Foto vom (simplen) Aufbau habe ich angehängt.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h> 
#include <avr/wdt.h>

#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))

#define WARTEN  0
#define SIGNAL_STARTEN 2 //Servosignal starten
#define SIGNAL_STOPP 3

uint32_t servoposition;

volatile char communication;


ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
{
  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
}

/* Timer/Counter0 Compare Match A */
ISR (TIM0_COMPA_vect)
{
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
}

// Definiert einen Sinus von 1,2 Hz; mit 50Hz abgetastet.
const unsigned char positionen[25] = {105, 115, 123, 129, 131, 129, 123, 115, 105, 93, 81, 71, 63, 57, 56, 57, 63, 71, 81, 93};
#define MAXPOS 20

/*
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
zwischen 0,9 und 2,1 ms lang sein. Bei 1,5 ms steht das Servo neutral; bei 0,9ms bewegt es sich
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
bewegt, um Pendel mit ca 1Hz Eigenfrequenz zum schwingen anzuregen.
*/

int main(void)
{
       // Initialisierungen: globale Variablen
  servoposition = 0;
  
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
      // daraus ergibt sich 1 MHz
      // Das finde ich gut, daher keine eigenen Einstellungen

  wdt_disable(); // Watchdog deaktiveren falls er vom letzen neustart noch aktiv ist
  GIMSK = 0;  // Pin Change Interrupts von außen deaktivieren
  ADCSRA = 0;  // ADC deaktivieren
  DIDR0 = 0;  // Keine Analogen Eingänge
  
  // Portpin
  MCUCR = 0 |(1<<PUD);// Pull Up Widerstände deaktivieren
  PORTB = 0;       // Port B komplett auf Null
  DDRB = 0x3F;     // Data Direction Register, komplett als Ausgang

/*********************************************************************************/
// Timer 1 einstellen
  OCR1B = 56;   // OCR1B 56: an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.
  OCR1C = 56;   // OCR1C 56: an dieser Zählmarke wird außerdem der Timer zurückgesetzt

/*********************************************************************************/
// Timer 0 einstellen
  TCCR0A = 0|((1<<COM0A0)|(1<<WGM01)); // Toggle OCR0A on compare match
              // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
              // wird der Timer bei Compare Match mit OCR0A zurückgesetzt
  TCCR0B = 0|(1<<CS02);  // Kein Output force
              // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
  OCR0A =  78;      // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
  
/*********************************************************************************/  
  TIMSK = 0|(1<<OCIE0A);   // Timer 0 OCR0A Compare Match Interrupt enabled,
              // Timer 1  OCR1B Compare Match vorerst disabled
  GTCCR = 0x00;       // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
  SREG  = 0 | (1 << 7);   // Interrupts aktivieren
  sei();
  wdt_enable(WDTO_60MS);  // Watchdog nach 64ms aktivieren. 

 
/*********************************************************************************/   
   
   while(1) { 
     // Statemachine              
    switch (communication)
    {
/**************************************************/
    case WARTEN: { // Warte auf nächsten Interrupt compare

      break;
    }

/**************************************************/
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
// Signal soll gestartet werden.
    case SIGNAL_STARTEN:{ 
      cli(); //Interrupts sperren
      wdt_reset(); //Watchdog zurücksetzen
      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten
      TCCR1 = 0|((1<<CS12) |(1<<CS10)|(1<<COM1A0)); // Timer starten mit Sysclock/16
      SET_BIT(TIMSK, OCIE1B); // Interrupts OCIE1B aktivieren
      communication = WARTEN;
      sei(); //Interrupts wieder freigeben
      
      break;
    }


/**************************************************/
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
// Signal soll gestoppt werden.

    case SIGNAL_STOPP:{ 
      // Timer 1 stoppen
      cli(); //Interrupts sperren
      CLEAR_BIT(  PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
      TCCR1 = 0; //Timer stoppen
      CLEAR_BIT(  TIMSK, OCIE1B); // Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
      communication = WARTEN;      
      sei(); //Interrupts wieder freigeben  
      // Nächstes Signal berechnen
      OCR1B = positionen[servoposition];
      OCR1C = positionen[servoposition];
      servoposition++;
      if (servoposition >= MAXPOS)
        servoposition = 0;

      break;
    }
/**************************************************/
    default : {
      cli();
      communication = WARTEN;
      sei();
      break;
      }
    }
  }                        
 
 /***********************************************************************/
  
   return 0;    
}

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich vermute mal, dass die Versorgungsspannung vom Servomotor versaut 
wird. Der "Angstkondensator" sieht mir nach Tantal aus, da sollte auf 
jeden Fall noch was keramisches dabei.

Autor: OlliW (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Andreas,

ich habe deinen Code nicht ganz durchgesehen... weil er mir unnötig 
kompliztiert erscheint (oder ich habe etwas wesentliches übersehen). 
Einen Servopuls kannst du z.B. so machen (ist für einen ATmega8 mit 
16MHz, aber die Übersetzung in ATtiny85 ist trvial, nur Timer0 halt auch 
mit CTC statt OVF und Timer1 mit OCR1C, und passende Prescaler).

volatile unsigned char rc_dotask;
volatile unsigned char rc_ovf;

ISR( TIMER0_OVF_vect )  //alle 16ms bei 16Mhz ATmega und Prescaler 1024
{
  TCNT1= 0;
  rc_outPORT|= (1<<rc_outP);
  rc_ovf= 1;
}

ISR( TIMER1_COMPA_vect )    //ISR to generate the PPM packet
{
  rc_outPORT&=~ (1<<rc_outP);
  if( rc_ovf ){ rc_ovf= 0; rc_dotask= 1; }
}

im main dann alle Initialisierungen und so etwas

  rc_dotask= rc_ovf= 0;
  sei();
  while( 1 ){
    if( rc_dotask ){
      rc_dotask= 0;
      //hier hat man nun alle Zeit etwas zu machen,
      // z.B. den neuen Wert für die Servopulslänge einzustellen
      OCR1A= t-1;
    }
  }


Sieht in manchem ähnlich wie dein Code aus nur einfacher, kein ständges 
Interrupt ein und ausschalten und so, keine Mehrfachzustände, etc,...

Läuft bei mir als Servotester problemlos.

Olli

Autor: Michael 93 (michael93) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi!

Wenn du normale Servos hast, was ich annehme, solltest du die 20 ms 
pause nicht sehr weit unterbieten (am besten gar nicht). Sonst rauchen 
dir deine Servos sehr schnell ab. Wenn du Digital-Servos benutzt, dürfte 
das kein Problem sein.

Erklärung: Wird die pause kürzer gemacht, läuft der Motor öfter an, und 
das schlagartig in alle Richtungen. Das tut dem nicht sehr gut.

Viel Spass noch, Michael

Autor: Andreas W. (adamdreyer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke dafür!

Nachdem ich jetzt die Servo-Schwingung auf etwas mehr als 1Hz gestellt 
habe von vorher 4 Hz (durch einen Rechenfehler), kackt das Programm auch 
nicht mehr ab.

Also hat der µC wahrscheinlich wirklich zwischendurch keinen Strom 
gehabt.

Jetzt laufen die Servos langsamer, und es funktioniert alles. Danke an 
alle!

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.