Forum: Mikrocontroller und Digitale Elektronik ATMega8 und Servo völlig merkwürdiges Verhalten


von Christian G. (christian1973)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

ich habe das am Ende des Posts angehängte Programm zur Ansteuerung eines 
Servos sowie 7 Leeds (die die Position des Servos anzeigen) durch einen 
ATMega8.
Zusätzlich soll dieses Programm auch noch ein weiteres LED (das so 
genannte Kabinenlicht) ansteuern, dass aber durchgehend brennt, während 
der Servo seine Bewegungsroutine abläuft.

Diese "Bewegungsroutine" wird durch einen Button gestartet und besteht 
aus einer Anzahl festgelegter Positionen und zugehöriger Waittimes - 
beides in je einem Array abgelegt und per For-Schleife mit Zähler 
durchlaufen.

Die 7 LEDs sind an PD0-6,
der Servo hängt an PB1,
die Kabinen-LED an PB5
und der Button an PC3

ATMega un Servo haben getrennte Stromversorguzngen und geteilte Masse 
und sowohl die VCC als auch AVCC sind mit einem 100nF Papa abgesichert. 
Die Mainline hat dazu noch einen eigenen Kapa. Auf dem Board ist 
ausserdem eine Kontrolleuchte, die einfach (mit Widerstand) zwischen 
Main und GND eingehängt ist, um anzuzeigen, dass das Board Saft hat.


Nun zu meinen 3 Problemen:

1. Nachdem der Servo die vorgegebene Bewegungsroutine durchgeführt hat, 
fängt er an scheinbar willkürliche (in Wahrheit aber immer die gleichen) 
zusätzlichen Bewegungen auszuführen. Diese gehen mit dem Anschalten von 
mehreren LEDs einher.

2. Ganz zum Schluss soll der Servo wieder in die Mitte fahren und die 
mittlere LED blinkt drei mal. Tatsächlich aber blinkt erst die mittlere 
LED 3 mal un dann fährt der Servo in Mitteposition - obwohl der Code das 
so gar nicht herzugeben scheint.

3. Während Servo und die 7 Positions-LEDs zumindest angehen, leuchtet 
das Kabinenlicht am PB5 nicht - ich hab hier auch schon PD7 oder PC1 
ausprobiert, ohne Erfolg. Manchmal gab es auch den völlig komischen 
Effekt dass, wenn der Servo über den maximalen Anstellpunkt hinausgehen 
wollte (was eigentlich auch schon nicht sein kann, da ich diese Position 
gar nicht in meinem Array habe) dann das Kabinenlicht für diesen 
Zeitraum anging.



Ich bin echt komplett ratlos. Kann da bitte einmal jemand mit mehr 
Ahnung als ich draufschauen und mir sagen, welchen Anfängerfehler ich 
nun wieder verbockt habe?


So hier noch der Code - Achtung nicht erschrecken ich arbeite mit 
Compiletime-Makros um bestimmte Funktionen ein und Auszuschalten, daher 
die ganzen #ifdef Statements:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define BUTTON DDC3       // if active PORT for the button to start the action
5
//#define COUNTER 3         // if active one press of the button initiates number of cycles
6
7
8
#define RUNLED 1          // if active, the 7 lights are used to indicate turret position
9
#define CABINLED DDB5     // if active, PORT for the cabinlight - it is turned on while turret moves
10
11
// positions of the 7 lights in the light pattern array
12
#define LIGHT_OFF             0         // all lights off
13
#define LIGHT_LEFT            1         // the left most led
14
#define LIGHT_CENTER          4         // the center led
15
#define LIGHT_RIGHT           7         // the right most led
16
17
18
// do we use the servo and do we have 2 servos?
19
#define SERVO 1
20
#define SERVO_UP DDB1
21
//#define SERVO_DOWN DDB2 
22
// Servo Frame-Time:
23
#define FRAME_TIME 800 // msec
24
25
// servo positions
26
#define ABS_CENTER            1500      // center or 0 position
27
#define SERVO_LEFT            750       // roughly -90° - was 675
28
#define SERVO_RIGHT           2250      // roughly +90°
29
#define SERVO_STEP            240       // 3 Schritte von Mitte zu Max Left und Max Right
30
31
32
// wait times in cycle
33
#define START_CYCLE_SLEEP     1000
34
#define AFTER_CYCLE_SLEEP     2000
35
36
#define RAPID_MOVE_SLEEP      350
37
#define FOLLOW_FIGHTER_SLEEP  500
38
#define QUICK_FIRE_SLEEP      750
39
#define FINAL_FIRE_SLEEP      1250
40
41
42
unsigned char ledPattern[]     = { 0b0000000,   // 0 - all off
43
                                   0b1000000,   // 1 - left most
44
                                   0b0100000,   // 2 - left middle
45
                                   0b0010000,   // 3 - left center
46
                                   0b0001000,   // 4 - center
47
                                   0b0000100,   // 5 - right center
48
                                   0b0000010,   // 6 - right middle
49
                                   0b0000001 }; // 7 - right most
50
                                   
51
52
uint16_t      servoPattern[]   = { 1570,
53
                                   1600,
54
                                   2045,
55
                                   1710,
56
                                   1850,
57
                                   1990,
58
                                   1500,
59
                                   750,
60
                                   850,
61
                                   1200,
62
                                   1350,
63
                                   1400,
64
                                   1500,
65
                                   1250,
66
                                   1000,
67
                                   750,
68
                                   1500
69
                                 };
70
71
uint16_t      inCyclePause[]   = { RAPID_MOVE_SLEEP,  
72
                                   FOLLOW_FIGHTER_SLEEP,
73
                                   QUICK_FIRE_SLEEP,
74
                                   FOLLOW_FIGHTER_SLEEP,
75
                                   FOLLOW_FIGHTER_SLEEP,
76
                                   FINAL_FIRE_SLEEP,
77
                                   FINAL_FIRE_SLEEP,
78
                                   RAPID_MOVE_SLEEP,
79
                                   RAPID_MOVE_SLEEP,
80
                                   FOLLOW_FIGHTER_SLEEP,
81
                                   FOLLOW_FIGHTER_SLEEP,
82
                                   QUICK_FIRE_SLEEP,
83
                                   QUICK_FIRE_SLEEP,
84
                                   FOLLOW_FIGHTER_SLEEP,
85
                                   RAPID_MOVE_SLEEP,
86
                                   FINAL_FIRE_SLEEP,
87
                                   RAPID_MOVE_SLEEP
88
                                 };
89
90
void sleep(int milliseconds) {
91
  while (milliseconds > 0) {
92
    _delay_ms(1);
93
    milliseconds--;
94
  }
95
}
96
97
98
char mapLedPosFromServoPos (uint16_t pos){
99
  return(map(pos, SERVO_LEFT, SERVO_RIGHT, LIGHT_LEFT, LIGHT_RIGHT));
100
}
101
102
// the 7 leds on the hull defining the position of the turret
103
void initLed(void) {
104
  #ifdef RUNLED
105
    DDRD  = 0b11111111;             // PORTD output for the RUNLED
106
    PORTD = ledPattern[LIGHT_OFF];  // turn all LEDs off
107
    
108
    sleep(1000);
109
    PORTD=ledPattern[LIGHT_CENTER];
110
    sleep(1000);
111
    PORTD=ledPattern[LIGHT_OFF];
112
    sleep(1000);
113
    PORTD=ledPattern[LIGHT_CENTER];
114
    sleep(1000);
115
    PORTD=ledPattern[LIGHT_OFF];
116
    sleep(1000);
117
    PORTD=ledPattern[LIGHT_CENTER];
118
    sleep(1000);
119
    PORTD=ledPattern[LIGHT_OFF];
120
  #endif
121
122
  // the cabin led 
123
  #ifdef CABINLED
124
    // nothing to do
125
  #endif
126
}
127
128
void initButton(void){
129
  // the button to control when movement starts
130
  #ifdef BUTTON
131
    DDRC  =   0b1110111;          // PC3 as input
132
    PORTC |=  0b0001000;          // enable pull ups for PC3
133
  #endif
134
}
135
136
void initServo(void) {
137
  #ifdef SERVO
138
    DDRB  =   0b11111111;
139
    
140
    // as per website:
141
    //    https://www.electronicwings.com/avr-atmega/servo-motor-interfacing-with-atmega16
142
    // FPWM = FOSC / ( N * ( 1 + TOP ) )
143
    // Where N is pre-scaler divider i.e. 1, 8, 64, 256, or 1024.
144
    // FPWM = 8000000 / ( 64 * ( 1 + 2499 ) )
145
    // ICR1=20000 defines 50Hz PWM
146
    ICR1    =   FRAME_TIME * 1000;
147
    
148
    
149
    // initialisiere die Timer für PWM auf PB1 und PB2
150
    #ifdef SERVO_UP
151
      TCCR1A  |=  (0<<COM1A0)|(1<<COM1A1)|(0<<COM1B0);
152
    #endif
153
    #ifdef SERVO_DOWN
154
      TCCR1A  |=  (1<<COM1B1);
155
    #endif
156
    #ifndef SERVO_DOWN
157
      TCCR1A  |=  (1<<COM1B0);
158
    #endif
159
    
160
    TCCR1A  |=  (0<<FOC1A) | (0<<FOC1B) | (1<<WGM11) | (0<<WGM10);
161
    TCCR1B  |=  (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
162
    
163
    /*
164
    TCCR1A  |=  (0<<COM1A0) | (1<<COM1A1) | (0<<COM1B0) | (1<<COM1B0) | (0<<FOC1A) | (0<<FOC1B) | (1<<WGM11) | (0<<WGM10);
165
    
166
    TCCR1B  |=  (0<<ICNC1) | (0<<ICES1) | (1<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
167
    */
168
    
169
    // reset timer
170
    TCNT1   =   0;
171
    
172
    // rotate to 0 degress (center)
173
    #ifdef SERVO_UP
174
      OCR1A =   ABS_CENTER;
175
    #endif
176
    #ifdef SERVO_DOWN
177
      OCR1B =   ABS_CENTER;
178
    #endif
179
  #endif
180
  
181
  //sei();
182
}
183
184
void initAVR(void){
185
  #ifdef RUNLED
186
    initLed();
187
  #endif
188
  #ifdef BUTTON
189
    initButton();
190
  #endif
191
  #ifdef SERVO
192
    initServo();
193
  #endif
194
}
195
196
197
int main() {
198
  #ifdef COUNTER
199
    unsigned int runCounter;    
200
  #endif
201
  
202
  // initialize all componets
203
  initAVR();
204
  
205
  // and go...
206
  while(1) {
207
    #ifdef BUTTON
208
      // PC3 is low?
209
      if((PINC & 0b0001000)==0) {
210
    #endif
211
      #ifdef CABINLED
212
        PORTC ^= (1<<CABINLED);
213
      #endif
214
      #ifdef COUNTER
215
        for (runCounter=0; runCounter<COUNTER; runCounter++){
216
      #endif
217
          // We start with the center position
218
          #ifdef RUNLED
219
            PORTD=ledPattern[LIGHT_CENTER];
220
          #endif
221
          #ifdef SERVO_UP
222
            OCR1A = ABS_CENTER;
223
          #endif
224
          #ifdef SERVO_DOWN
225
            OCR1B = ABS_CENTER;
226
          #endif
227
          sleep(START_CYCLE_SLEEP);
228
        
229
          // RUNLEDs and Servo movement
230
          for(char i=0;i<=sizeof(servoPattern)-1;i++) {
231
            #ifdef RUNLED
232
              PORTD=ledPattern[mapLedPosFromServoPos(servoPattern[i])];
233
            #endif
234
            #ifdef SERVO_UP
235
              OCR1A = servoPattern[i];
236
            #endif
237
            #ifdef SERVO_DOWN
238
              OCR1B = servoPattern[i];
239
            #endif
240
            
241
            sleep(inCyclePause[i]);
242
          }
243
          
244
          // end of cycle
245
          #ifdef RUNLED
246
            PORTD=ledPattern[LIGHT_CENTER];
247
          #endif
248
          #ifdef SERVO_UP
249
            OCR1A = ABS_CENTER;
250
          #endif
251
          #ifdef SERVO_DOWN
252
            OCR1B = ABS_CENTER;
253
          #endif
254
      #ifdef COUNTER
255
        }
256
      #endif
257
258
      #ifdef RUNLED
259
        sleep(AFTER_CYCLE_SLEEP*2);
260
        // turn off all RUNLEDs
261
        PORTD=ledPattern[LIGHT_OFF];
262
        sleep(1000);
263
        PORTD=ledPattern[LIGHT_CENTER];
264
        sleep(1000);
265
        PORTD=ledPattern[LIGHT_OFF];
266
        sleep(1000);
267
        PORTD=ledPattern[LIGHT_CENTER];
268
        sleep(1000);
269
        PORTD=ledPattern[LIGHT_OFF];
270
      #endif
271
    #ifdef BUTTON
272
      }
273
    #endif
274
    
275
    // finally make sure to turn off the cabin light
276
    #ifdef CABINLED
277
      sleep(AFTER_CYCLE_SLEEP*3);
278
      PORTC ^= (0<<CABINLED);
279
    #endif
280
  } // Main loop
281
}

: Bearbeitet durch User
von H. H. (Gast)


Lesenswert?

Christian G. schrieb:
> Die 7 LEDs sind an PD0-6,

Ohne Vorwiderstand?

von Stefan F. (Gast)


Lesenswert?

Ich habe beim Lesen des Quelltextes nach dem oberen viertel aufgehört, 
weil er voller irreführender Kommentare ist, die nicht mit dem C-Code 
überein stimmen. Korrigiere das zuerst.

Beispiel:
1
// configure pins PC3, PC4 and PC5 on Port C as input 
2
// and enable internal pull-up resistors
3
4
DDRC  =   0b1110111;          // PC3 as input

Was du da wirklich machen wolltest, kann ich nicht erkennen.

von Christian E. (cerker)


Lesenswert?

Was heißt "geteilte Masse" .. du hast die GND schon an einem Punkt 
verbunden und nicht nur die Signalleitung.

Und was sind Mainline, Papa, Kapa?

Und trotzdem gib dem Servo mal 1000-2200µF parallel. Die sind darauf 
ausgelegt, an einem Akku zu hängen der ev. generatorisch erzeugte 
Leistung aufnimmt. Außerdem sind Stromspitzen von einigen A durchaus 
drin.

Das ist im normalen RC-Betrieb schon häufig nötig, besonders wenn eine 
einfache Dioden-Akkuweiche verwendet wird.

Gruß,
Christian

von Stefan F. (Gast)


Lesenswert?

Was soll das werden?:
1
PORTC ^= (0<<CABINLED);

Der Ausdruck in den Klammern ergibt 0, also wird da gar kein Pin 
getoggelt.

von Christian G. (christian1973)


Lesenswert?

H. H. schrieb:
> Christian G. schrieb:
>> Die 7 LEDs sind an PD0-6,
>
> Ohne Vorwiderstand?

Ja stimmt. Das soll natürlich später nicht so sein :-)

von Christian G. (christian1973)


Lesenswert?

Stefan ⛄ F. schrieb:
> Was soll das werden?:
>
1
> PORTC ^= (0<<CABINLED);
2
>
>
> Der Ausdruck in den Klammern ergibt 0, also wird da gar kein Pin
> getoggelt.

Ich bin da einfach echt noch Noob - was exakt heisst denn dieses 
Statement genau? Switch den Port an CABINLED (also DDB5) hin und her?

Ich hab immer gedacht, dass die 0 for dem Port ein explizites 
ausschalten ist. Und das Statement
1
PORTC ^= (1<<CABINLED);
den Port anschaltet

von Christian G. (christian1973)


Angehängte Dateien:

Lesenswert?

Christian E. schrieb:
> Was heißt "geteilte Masse" .. du hast die GND schon an einem Punkt
> verbunden und nicht nur die Signalleitung.
>
> Und was sind Mainline, Papa, Kapa?
>
> Und trotzdem gib dem Servo mal 1000-2200µF parallel. Die sind darauf
> ausgelegt, an einem Akku zu hängen der ev. generatorisch erzeugte
> Leistung aufnimmt. Außerdem sind Stromspitzen von einigen A durchaus
> drin.
>
> Das ist im normalen RC-Betrieb schon häufig nötig, besonders wenn eine
> einfache Dioden-Akkuweiche verwendet wird.
>
> Gruß,
> Christian

Geteilte Masse heißt, dass der Servo (gespeist von einer 9v Block über 
den im Bild gezeigten Aufbau reduziert auf 4,5V) zusätzlich auf die 
Messeleitung des Boards mit dem Mikrocontroller gezogen ist.

Die Mainline sollte die VCC Leitung des Bords mit Mikrocontroller sein. 
und der Kapa ist ein 100nF - so ein 104er

von Stefan F. (Gast)


Lesenswert?

Christian G. schrieb:
> Das soll natürlich später nicht so sein

Das soll auch jetzt nicht so sein, denn damit überlastest du das IC oder 
dessen Stromversorgung. Dadurch wird die interne Spannungsversorgung 
instabil, was beliebige Fehlfunktionen auslösen kann.

von Christian G. (christian1973)


Lesenswert?

Stefan ⛄ F. schrieb:
> Ich habe beim Lesen des Quelltextes nach dem oberen viertel aufgehört,
> weil er voller irreführender Kommentare ist, die nicht mit dem C-Code
> überein stimmen. Korrigiere das zuerst.
>
> Beispiel:
>
1
> // configure pins PC3, PC4 and PC5 on Port C as input
2
> // and enable internal pull-up resistors
3
> 
4
> DDRC  =   0b1110111;          // PC3 as input
5
>
>
> Was du da wirklich machen wolltest, kann ich nicht erkennen.

Hab den Quelltext etwas aufgeräumt. Die irreführenden Kommentare waren 
nur Merkhilfen für mich, woher das Zeug kam und wie die dortige 
Erklärung war.
1
DDRC = 0b1110111;

soll den PC3 Port als Input Port für den Button nutzen. Wenn der auf GND 
zieht (internal pullups) startet der Zyklus - Funktioniert auch im 
Prinzip.

von Christian G. (christian1973)


Lesenswert?

Stefan ⛄ F. schrieb:
> Christian G. schrieb:
>> Das soll natürlich später nicht so sein
>
> Das soll auch jetzt nicht so sein, denn damit überlastest du das IC oder
> dessen Stromversorgung. Dadurch wird die interne Spannungsversorgung
> instabil, was beliebige Fehlfunktionen auslösen kann.

Ok. Dann muss ich das mal ändern. Kann das der denn auch der Grund für 
diese irrationalen, aber reproduzierbaren Servobewegungen sein. Ich 
meine, die sind zwar nicht mit dem Array der geplanten Bewegung 
übereinzubringen, aber auch nicht völlig willkürlich, sondern schon 
immer recht gleich.

von Stefan F. (Gast)


Lesenswert?

Christian G. schrieb:
> Ich bin da einfach echt noch Noob - was exakt heisst denn dieses
> Statement genau?

Ich empfehle dir, die Grundlagen der Programmiersprache zu lernen. Zieh 
dir das mal rein: http://stefanfrings.de/mikrocontroller_buch/index.html

In diesem Fall konkret Band1 Kapitel 4.2.20.4, 4.2.20.6 und 6.7.4

PORTC ^= irgendwas ist eine Kurzform von PORTC = PORTC ^ irgendwas.

CABINLED ist DDB5, was wiederum 5 ist.

(1<<5) ist eine 1 fünf mal nach links geschoben, also 32.

PORTC ^= 32 ändert den Wert von Bit 5 im Register PORTC, schaltet also 
dem Pin PC5 um.

Aber (0<<irgendwas) ergibt 0 und bewirkt daher nichts.

> Ich hab immer gedacht, dass die 0 for dem Port ein
> explizites ausschalten ist.

Siehe Kapitel 6.7

von H. H. (Gast)


Lesenswert?

Christian G. schrieb:
> H. H. schrieb:
>> Christian G. schrieb:
>>> Die 7 LEDs sind an PD0-6,
>>
>> Ohne Vorwiderstand?
>
> Ja stimmt. Das soll natürlich später nicht so sein :-)

Das sollte auch jetzt nicht so sein.

von Stefan F. (Gast)


Lesenswert?

Christian G. schrieb:
> Geteilte Masse heißt, dass der Servo (gespeist von einer 9v Block

Möööööööp!

9V Block Batterien sind nur für ganz geringe Stromstärken geeignet und 
außerdem die mit dem schlechtesten Preis-/Leistungs-Verhältnis.

Für Modellbau Servos empfehle ich dringend mindestens 4-5 Rundzellen in 
Größe AA.

von Stefan F. (Gast)


Lesenswert?

> DDRC = 0b1110111;
> soll den PC3 Port als Input Port

Estmal sind standardmäßig alle I/O Pins als Input konfiguriert. Was du 
hier in Wirklichkeit machst ist, alle Pins außer PC3 als Ausgang zu 
konfigurieren.

Wenn du nur dieses eine Bit auf LOW setzen willst, dann so:

DDRC &= ~(1<<PC3)

Der Vollständigkeit halber: Wenn du z.B. das Bit für PC2 auf HIGH setzen 
willst, dann so:

DDRC |= (1<<PC2)

Anstelle von PC3 und PC2 kannst du auch einfach 3 und 2 schreiben.

von Stefan F. (Gast)


Lesenswert?

Christian G. schrieb:
> Kann das der denn auch der Grund für
> diese irrationalen, aber reproduzierbaren Servobewegungen sein.

Wie ich bereits schrieb:

Stefan ⛄ F. schrieb:
> Dadurch wird die interne Spannungsversorgung
> instabil, was beliebige Fehlfunktionen auslösen kann.

von Stefan F. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Anstelle von PC3 und PC2 kannst du auch einfach 3 und 2 schreiben.

Oder auch DDB3 und DDB2. Spielt keine Rolle, denn diese Konstanten haben 
alle den Wert 3 bzw. 2.

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.