Forum: Compiler & IDEs Hilfe! Modellbauservo & Interrupts


von Andreas W. (adamdreyer)


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).
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h> 
4
5
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
6
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
7
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))
8
9
#define WARTEN  0
10
#define SIGNAL_STARTEN 2 //Servosignal starten
11
#define SIGNAL_STOPP 3
12
13
14
char cSREG;
15
16
uint32_t servoposition;
17
18
char communication;
19
20
21
22
ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
23
{
24
    cli();
25
  cSREG = SREG;
26
    // Interrupt Code /
27
28
  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
29
  SREG = cSREG;
30
31
  sei();
32
}
33
34
/* Timer/Counter0 Compare Match A */
35
36
ISR (TIM0_COMPA_vect)
37
{
38
    cli();
39
  cSREG = SREG;
40
    // Interrupt Code /
41
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
42
  SREG = cSREG;
43
44
  sei();
45
}
46
47
// Definiert einen Sinus von 2,5 Hz; mit 50Hz abgetastet.
48
const unsigned char positionen[9] = {56, 66, 93, 120, 131, 120, 93, 66};
49
#define MAXPOS 9
50
51
/*
52
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
53
zwischen 1,9 und 2,1 ms lang sein. Bei 2ms steht das Servo neutral; bei 1,9ms bewegt es sich
54
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
55
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
56
bewegt, um ein Pendel mit 2,5s Eigenfrequenz zum schwingen anzuregen.
57
*/
58
59
60
int main(void)
61
{
62
       // Initialisierungen: globale Variablen
63
  servoposition = 0;
64
  
65
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
66
      // daraus ergibt sich 1 MHz
67
      // Das finde ich gut, daher keine eigenen Einstellungen
68
      // Watchdog deaktivieren
69
  WDTCR = 0x00;
70
  GIMSK = 0x00; // Pin Change Interrupts von außen deaktivieren
71
  ADCSRA = 0x00; //ADC deaktivieren
72
  DIDR0 = 0x00; // Keine Analogen Eingänge
73
  
74
  // Portpin
75
  MCUCR = 0x40; // Pull Up Widerstände deaktivieren
76
  PORTB = 0x00; // PortB 4, also 0x10 wird als Signalgeber verwendet
77
  DDRB = 0x3F; // Data Direction Register, komplett als Ausgang
78
79
  //PINB  // Eingangsregister
80
81
/*********************************************************************************/
82
// Timer 1 einstellen
83
  TCCR1 = 0x05;   // Do not clear on compare match mit OCR1C; 
84
          // Sysclock/16 ist die Timer Clock: nach 56 Zählern sind 0,9ms vorbei.
85
  OCR1B = 56;   //  OCR1B 56 an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.
86
87
/*********************************************************************************/
88
// Timer 0 einstellen
89
  TCCR0A = 0x42;  // Toggle OCR0A on compare match
90
          // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
91
          // wird der Timer bei Compare Match mit OCR0A zurückgesetzt 
92
  TCCR0B = 0x04;  // Kein Output force
93
          // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
94
  OCR0A =  78;  // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
95
  
96
/*********************************************************************************/  
97
  TIMSK = 0x10;   // Timer 0 OCR0A Compare Match Interrupt enabled,
98
          // Timer 1  OCR1B Compare Match vorerst disabled
99
  GTCCR = 0x00;   // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
100
  SREG |= 0x80;   // Interrupts aktivieren
101
  sei();
102
  
103
/*********************************************************************************/   
104
   
105
   while(1) { 
106
     // Statemachine              
107
    switch (communication)
108
    {
109
/**************************************************/
110
    case WARTEN: { // Warte auf nächsten Interrupt compare
111
112
      break;
113
    }
114
115
/**************************************************/
116
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
117
// Signal soll gestartet werden.
118
    case SIGNAL_STARTEN:{ 
119
120
      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
121
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten
122
123
      TIMSK = 0x30; // Interrupts OCIE1A aktivieren
124
      communication = WARTEN;
125
126
      break;
127
    }
128
129
130
/**************************************************/
131
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
132
// Signal soll gestoppt werden.
133
134
    case SIGNAL_STOPP:{ 
135
      // Timer 1 stoppen
136
      cli();
137
      CLEAR_BIT(PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
138
      TIMSK = 0x10; // Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
139
      sei();      
140
      // Neues Signal berechnen
141
      // Das Signal wird in  OCR1B geschrieben. 56 ist Vollausschlag nach links,
142
      // 75+56 ist vollausschlag nach rechts,
143
      // 37 + 56 ist die Neutrallage.
144
      OCR1B = positionen[servoposition];
145
      servoposition++;
146
      if (servoposition > MAXPOS)
147
        servoposition = 0;
148
      communication = WARTEN;
149
150
      break;
151
    }
152
/**************************************************/
153
    default : {
154
      communication = WARTEN;
155
      break;
156
      }
157
    }
158
  }                        
159
 
160
 /***********************************************************************/
161
  
162
   return 0;    
163
}

von Mark .. (mork)


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

von STK500-Besitzer (Gast)


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.

von Simon K. (simon) Benutzerseite


Lesenswert?

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

von Andreas W. (adamdreyer)


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.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h> 
4
5
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
6
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
7
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))
8
9
#define WARTEN  0
10
#define SIGNAL_STARTEN 2 //Servosignal starten
11
#define SIGNAL_STOPP 3
12
13
14
char cSREG;
15
16
uint32_t servoposition;
17
18
volatile char communication;
19
20
21
22
ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
23
{
24
    // Interrupt Code /
25
  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
26
}
27
28
/* Timer/Counter0 Compare Match A */
29
30
ISR (TIM0_COMPA_vect)
31
{
32
    // Interrupt Code /
33
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
34
}
35
36
// Definiert einen Sinus von 2,5 Hz; mit 50Hz abgetastet.
37
const unsigned char positionen[9] = {56, 66, 93, 120, 131, 120, 93, 66};
38
#define MAXPOS 9
39
40
/*
41
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
42
zwischen 1,9 und 2,1 ms lang sein. Bei 2ms steht das Servo neutral; bei 0,9ms bewegt es sich
43
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
44
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
45
bewegt, um ein Pendel mit 2,5hz Eigenfrequenz zum schwingen anzuregen.
46
*/
47
48
int main(void)
49
{
50
       // Initialisierungen: globale Variablen
51
  servoposition = 0;
52
  
53
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
54
      // daraus ergibt sich 1 MHz
55
      // Das finde ich gut, daher keine eigenen Einstellungen
56
57
  WDTCR = 0;  // Watchdog deaktivieren
58
  GIMSK = 0;  // Pin Change Interrupts von außen deaktivieren
59
  ADCSRA = 0;  // ADC deaktivieren
60
  DIDR0 = 0;  // Keine Analogen Eingänge
61
  
62
  // Portpin
63
  MCUCR = 0 |(1<<PUD);// Pull Up Widerstände deaktivieren
64
  PORTB = 0;       // Port B komplett auf Null
65
  DDRB = 0x3F;     // Data Direction Register, komplett als Ausgang
66
67
/*********************************************************************************/
68
// Timer 1 einstellen
69
70
  OCR1B = 56;   // OCR1B 56: an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.
71
72
/*********************************************************************************/
73
// Timer 0 einstellen
74
  TCCR0A = 0|((1<<COM0A0)|(1<<WGM01));// Toggle OCR0A on compare match
75
          // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
76
          // wird der Timer bei Compare Match mit OCR0A zurückgesetzt
77
  TCCR0B = 0|(1<<CS02);  // Kein Output force
78
          // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
79
  OCR0A =  78;  // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
80
  
81
/*********************************************************************************/  
82
  TIMSK = 0|(1<<OCIE0A);   // Timer 0 OCR0A Compare Match Interrupt enabled,
83
          // Timer 1  OCR1B Compare Match vorerst disabled
84
  GTCCR = 0x00;   // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
85
  SREG  = 0 | (1 << 7);   // Interrupts aktivieren
86
  sei();
87
  
88
/*********************************************************************************/   
89
   
90
   while(1) { 
91
     // Statemachine              
92
    switch (communication)
93
    {
94
/**************************************************/
95
    case WARTEN: { // Warte auf nächsten Interrupt compare
96
97
      break;
98
    }
99
100
/**************************************************/
101
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
102
// Signal soll gestartet werden.
103
    case SIGNAL_STARTEN:{ 
104
      cli(); //Interrupts sperren
105
      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
106
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten
107
      TCCR1 = 0|((1<<CS12)); // Timer starten mit Sysclock/8
108
      SET_BIT(TIMSK, OCIE1B);// Interrupts OCIE1B aktivieren
109
      sei(); //Interrupts wieder freigeben
110
      communication = WARTEN;
111
      break;
112
    }
113
114
115
/**************************************************/
116
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
117
// Signal soll gestoppt werden.
118
119
    case SIGNAL_STOPP:{ 
120
      // Timer 1 stoppen
121
      cli(); //Interrupts sperren
122
      CLEAR_BIT(  PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
123
      TCCR1 = 0; //Timer stoppen
124
      CLEAR_BIT(  TIMSK, OCIE1B);// Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
125
      sei(); //Interrupts wieder freigeben  
126
      // Neues Signal berechnen
127
      // Das Signal wird in  OCR1B geschrieben. 56 ist Vollausschlag nach links,
128
      // 75+56 ist vollausschlag nach rechts,
129
      // 37 + 56 ist die Neutrallage.
130
      OCR1B = positionen[servoposition];
131
      servoposition++;
132
      if (servoposition > MAXPOS)
133
        servoposition = 0;
134
      communication = WARTEN;
135
      break;
136
    }
137
/**************************************************/
138
    default : {
139
      communication = WARTEN;
140
      break;
141
      }
142
    }
143
  }                        
144
 
145
 /***********************************************************************/
146
  
147
   return 0;    
148
}

von Simon K. (simon) Benutzerseite


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.

von STK500-Besitzer (Gast)


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...

von Andreas W. (adamdreyer)


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

von Simon K. (simon) Benutzerseite


Lesenswert?

Hast du denn jetzt mal den Zugriff auf die Variable communication 
Interrupttauglich gemacht?

von Andreas W. (adamdreyer)


Angehängte Dateien:

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.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <inttypes.h> 
4
#include <avr/wdt.h>
5
6
#define SET_BIT(PORT, BITNUM)    ((PORT) |=  (1<<(BITNUM)))
7
#define CLEAR_BIT(PORT, BITNUM)  ((PORT) &= ~(1<<(BITNUM)))
8
#define TOGGLE_BIT(PORT, BITNUM) ((PORT) ^=  (1<<(BITNUM)))
9
10
#define WARTEN  0
11
#define SIGNAL_STARTEN 2 //Servosignal starten
12
#define SIGNAL_STOPP 3
13
14
uint32_t servoposition;
15
16
volatile char communication;
17
18
19
ISR(TIM1_COMPB_vect) // Timer 1 meldet einen  OCR1B Vergleich
20
{
21
  communication = SIGNAL_STOPP; //weitere (unkritische) Berechnungen
22
}
23
24
/* Timer/Counter0 Compare Match A */
25
ISR (TIM0_COMPA_vect)
26
{
27
  communication = SIGNAL_STARTEN; //weitere (unkritische) Berechnungen
28
}
29
30
// Definiert einen Sinus von 1,2 Hz; mit 50Hz abgetastet.
31
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};
32
#define MAXPOS 20
33
34
/*
35
Modellbauservos benötigen fünfzig mal pro Sekunde, alle 20 ms, einen 5V-Peak. Dieser Peak muss 
36
zwischen 0,9 und 2,1 ms lang sein. Bei 1,5 ms steht das Servo neutral; bei 0,9ms bewegt es sich
37
zum linken Vollausschlag; bei 2,1ms bewegt es sich zum rechten Vollausschlag.
38
Ich möchte einen Sinus defineren, so dass sich das Modellbauservo immer nach links und rechts
39
bewegt, um Pendel mit ca 1Hz Eigenfrequenz zum schwingen anzuregen.
40
*/
41
42
int main(void)
43
{
44
       // Initialisierungen: globale Variablen
45
  servoposition = 0;
46
  
47
      // Der interne Takt ist von Werk aus eingestellt auf 8 MHz mit prescaler von 8, 
48
      // daraus ergibt sich 1 MHz
49
      // Das finde ich gut, daher keine eigenen Einstellungen
50
51
  wdt_disable(); // Watchdog deaktiveren falls er vom letzen neustart noch aktiv ist
52
  GIMSK = 0;  // Pin Change Interrupts von außen deaktivieren
53
  ADCSRA = 0;  // ADC deaktivieren
54
  DIDR0 = 0;  // Keine Analogen Eingänge
55
  
56
  // Portpin
57
  MCUCR = 0 |(1<<PUD);// Pull Up Widerstände deaktivieren
58
  PORTB = 0;       // Port B komplett auf Null
59
  DDRB = 0x3F;     // Data Direction Register, komplett als Ausgang
60
61
/*********************************************************************************/
62
// Timer 1 einstellen
63
  OCR1B = 56;   // OCR1B 56: an dieser Zählmarke wird der  OCR1B-Interrupt aktiviert.
64
  OCR1C = 56;   // OCR1C 56: an dieser Zählmarke wird außerdem der Timer zurückgesetzt
65
66
/*********************************************************************************/
67
// Timer 0 einstellen
68
  TCCR0A = 0|((1<<COM0A0)|(1<<WGM01)); // Toggle OCR0A on compare match
69
              // die ersten zwei WGM Bits sind auf 10 gesetzt dadurch
70
              // wird der Timer bei Compare Match mit OCR0A zurückgesetzt
71
  TCCR0B = 0|(1<<CS02);  // Kein Output force
72
              // Prescaler ist auf 256 gesetzt: 20ms sind nach 78 Zählern vorbei.
73
  OCR0A =  78;      // OCR0A auf 78 gestellt: 20 ms können damit abgemessen werden
74
  
75
/*********************************************************************************/  
76
  TIMSK = 0|(1<<OCIE0A);   // Timer 0 OCR0A Compare Match Interrupt enabled,
77
              // Timer 1  OCR1B Compare Match vorerst disabled
78
  GTCCR = 0x00;       // kein PWM B, kein Output force, kein löschen von TCCR1, Timer starten  
79
  SREG  = 0 | (1 << 7);   // Interrupts aktivieren
80
  sei();
81
  wdt_enable(WDTO_60MS);  // Watchdog nach 64ms aktivieren. 
82
83
 
84
/*********************************************************************************/   
85
   
86
   while(1) { 
87
     // Statemachine              
88
    switch (communication)
89
    {
90
/**************************************************/
91
    case WARTEN: { // Warte auf nächsten Interrupt compare
92
93
      break;
94
    }
95
96
/**************************************************/
97
// Timer 0A Interrupt ist gekommen: 20ms sind vorbei. 
98
// Signal soll gestartet werden.
99
    case SIGNAL_STARTEN:{ 
100
      cli(); //Interrupts sperren
101
      wdt_reset(); //Watchdog zurücksetzen
102
      TCNT1 = 0; // Timer 1 auf null stellen: Signal wird von nun an gemessen
103
      SET_BIT(PORTB, PB4); //Port B 4 auf 1 stellen, um das Signal zu starten
104
      TCCR1 = 0|((1<<CS12) |(1<<CS10)|(1<<COM1A0)); // Timer starten mit Sysclock/16
105
      SET_BIT(TIMSK, OCIE1B); // Interrupts OCIE1B aktivieren
106
      communication = WARTEN;
107
      sei(); //Interrupts wieder freigeben
108
      
109
      break;
110
    }
111
112
113
/**************************************************/
114
// Timer 1B Interrupt ist gekommen: Servosignal ist vorbei. 
115
// Signal soll gestoppt werden.
116
117
    case SIGNAL_STOPP:{ 
118
      // Timer 1 stoppen
119
      cli(); //Interrupts sperren
120
      CLEAR_BIT(  PORTB, PB4); //Port B 4 auf 0 stellen um das Signal zu stoppen
121
      TCCR1 = 0; //Timer stoppen
122
      CLEAR_BIT(  TIMSK, OCIE1B); // Interrupts OCIE1B deaktivieren, nur OCIE0A bleibt an
123
      communication = WARTEN;      
124
      sei(); //Interrupts wieder freigeben  
125
      // Nächstes Signal berechnen
126
      OCR1B = positionen[servoposition];
127
      OCR1C = positionen[servoposition];
128
      servoposition++;
129
      if (servoposition >= MAXPOS)
130
        servoposition = 0;
131
132
      break;
133
    }
134
/**************************************************/
135
    default : {
136
      cli();
137
      communication = WARTEN;
138
      sei();
139
      break;
140
      }
141
    }
142
  }                        
143
 
144
 /***********************************************************************/
145
  
146
   return 0;    
147
}

von Simon K. (simon) Benutzerseite


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.

von OlliW (Gast)


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

von Michael 9. (michael93) Benutzerseite


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

von Andreas W. (adamdreyer)


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!

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.