Forum: Mikrocontroller und Digitale Elektronik Servos "zittern"


von Robotico (Gast)


Lesenswert?

Hallo,

ich hab mich in den letzten Tagen mal in die Welt der C-Programmierung 
von AVRs eingearbeitet und bin zur Zeit dabei, mal ein Servo mit dem µC 
zu steuern.
Verbunden habe ich rot mit extra 5 Volt aus einem Netzteil, der 
Mikrocontroller ist über mein Testboard versorgt und zwar über die 
USB-Schnittstelle.
Verbunden habe ich Masse miteinander und die Signalleitung des Servos 
mit PB1 meines µC.

Ich habe mir auch ein kleines Programm geschrieben, mit dem ich den 
Servo zum Test über ein Poti steuern kann. Da das nicht funktioniert 
hat, habe ich einfach mal diesen Code hier genommen: 
http://www.mikrocontroller.net/articles/Modellbauservo_Ansteuerung#Signalerzeugung_f.C3.BCr_1_Servo_.28C.29

Der Servo hat sich dann kurz bewegt und hat dann aber immer leicht hin- 
und hergeruckelt. Da dachte ich, der Servo ist vielleicht kaputt und 
einen anderen versucht, bei dem das gleiche und bei einem dritten auch 
das gleiche, somit glaube ich nicht, dass alle 3 Servos kaputt sind.
Wisst ihr an was das liegen könnte? Könnte irgendwas vom Mikrocontroller 
das Signal stören? (Der Mikrocontroller macht sonst nichts anderes.) 
Oder liegt es vielleicht daran, dass ich den internen Takt des Atmega8 
verwendet habe?
Ich kanns mir einfach nicht erklären.

von Mr. K. (kaktus-)


Lesenswert?

Haste mal das PWM Signal angeschaut was die Servos bekommen? 
Möglicherweise ist es nicht stabil. Auf der verlinkten Seite sind viele 
Codes, welcher denn ?

: Bearbeitet durch User
von Robotico (Gast)


Lesenswert?

Hab doch im Link en Anker gesetzt, oder? Der erste gleich, der ganz 
simple, der auf Mittelstellung stellen soll.
Ne, habe aber auch kein Oszi mit dem ich das anschauen könnte.

von Andreas S. (igel1)


Lesenswert?

Läuft Dein MC auch wirklich bei 1MHz?
Sonst passen die Timings im Beispiel nicht mehr.

Viele Grüße
Igel1

von Robotico (Gast)


Lesenswert?

ja müsste er, der interne Takt des mega8 sind ja 1MHz

von Mike (Gast)


Lesenswert?

Robotico schrieb:
> ja müsste er, der interne Takt des mega8 sind ja 1MHz

Das kommt drauf an. Das Datenblatt sagt: "The calibrated internal RC 
Oscillator provides a fixed 1.0, 2.0, 4.0, or 8.0 MHz clock". Da ist 
also alles möglich, je nach dem, wie die Fuses gesetzt sind.

von µC-Bastler (Gast)


Lesenswert?

Robotico schrieb:
> ja müsste er, der interne Takt des mega8 sind ja 1MHz

Hast du das mal miteiner LED überprüft?

von Robotico (Gast)


Lesenswert?

So schnell sind meine Augen nicht, um 1 Mhz zu erkennen :D


Mike schrieb:
> Das kommt drauf an. Das Datenblatt sagt: "The calibrated internal RC
> Oscillator provides a fixed 1.0, 2.0, 4.0, or 8.0 MHz clock". Da ist
> also alles möglich, je nach dem, wie die Fuses gesetzt sind.

Ja stimmt, da hast du Recht, aber da ich da nichts umgestellt habe, 
dürften es noch standardmäßig 1Mhz sein


Habe jetzt ein Wackler bei dem Stecker entdeckt, der die Massen 
verbindet, das habe ich schnell gefixt, jetzt passiert leider gar nichts 
mehr, die Servos geben kein Mucks mehr von sich, bewegen sich aber auch 
nicht auf Ausgangstellung wenn ich sie davor von Hand verdrehe oder 
ähnliches.

von Bernhard M. (boregard)


Lesenswert?

Na ja, wenn der Takt nicht stimmen würde, dann müsste der Servo an den 
Anschlag gehen, oder bei leichter Abweichung nicht mittig stehen.
Mit Zittern hat das aber nichts zu tun.

von Jürgen S. (jurs)


Lesenswert?

Robotico schrieb:
> Ich kanns mir einfach nicht erklären.

Ändere mal das delay zwischen den Impulsen auf 19 Millisekunden:
1
_delay_ms( 19 );      // ist nicht kritisch

Ergibt sich ein Unterschied?

von Robotico (Gast)


Lesenswert?

Also jetzt funktioniert es soweit, dass sich das Servo auf eine 
bestimmte Position einstellt.
Jetzt habe ich den Code wieder soweit abgeändert, dass ich es über ein 
ADC mit einem Poti steuern kann.
1
#define F_CPU 1000000UL
2
3
#include <avr/io.h>
4
#include <stdint.h>
5
#include <util/delay.h>
6
7
8
void long_delay(uint16_t ms)
9
{
10
    for(; ms>0; ms--) _delay_us(1);
11
}
12
13
14
int main(void)
15
{
16
  uint16_t adc_wert;
17
  
18
  DDRB = 0xFF;
19
20
  ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
21
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);
22
  
23
    while(1)
24
    {
25
        adc_wert = ADCW;
26
    adc_wert += 1000;
27
    
28
    PORTB |= (1<<PB1);
29
    long_delay(adc_wert);    // in den 1500 steckt die Lageinformation
30
    PORTB &= ~(1<<PB1);
31
 
32
    _delay_ms(18);      // ist nicht kritisch
33
    }    
34
  
35
  return 0;
36
}

Leider lässt sich es nicht steuern, es bleibt immer auf derselben 
Stelle.
Habe es auch mit 19ms probiert, kein Unterschied.

Sieht jemand einen Fehler im Code?
Das ADC-Register ist auf jeden Fall richtig eingestellt, habe es schon 
mit einem Lauflicht getestet und den ADCW auch schon auf dem Display 
ausgegeben, müsste ein Wert zwischen 0 und 1023 geben, 10 Bit eben.

von Andreas S. (igel1)


Lesenswert?

Was für ein Testboard benutzt Du?

von µC-Bastler (Gast)


Lesenswert?

Robotico schrieb:
> So schnell sind meine Augen nicht, um 1 Mhz zu erkennen :D

Du LED soll auch nicht am Oszillator hängen.

Schreib ein kurzes Programm.

while (1)
{
  LED an
  delay (500ms)
  LED aus
  delay (500ms)
}

Und 1 Hz wirst du sicher erkennen und von 2, 4 oder 8 Hz sicher 
unterscheiden können ;-)

von Andreas S. (igel1)


Lesenswert?

Schau' Dir Deine long_delay routine einmal genau an ...

von Andreas S. (igel1)


Lesenswert?

... und mache uns bitte kein "u" für ein "m" vor ;-)

von Robotico (Gast)


Lesenswert?

Andreas S. schrieb:
> Was für ein Testboard benutzt Du?

Ein selbstgebautes, mit paar Potis, LEDs, Taster, Anschluss für LCD, 
usw.

µC-Bastler schrieb:
> Robotico schrieb:
>> So schnell sind meine Augen nicht, um 1 Mhz zu erkennen :D
>
> Du LED soll auch nicht am Oszillator hängen.
>
> Schreib ein kurzes Programm.
>
> while (1)
> {
>   LED an
>   delay (500ms)
>   LED aus
>   delay (500ms)
> }
>
> Und 1 Hz wirst du sicher erkennen und von 2, 4 oder 8 Hz sicher
> unterscheiden können ;-)

Achso meinst du das :D Ja habs mal getestet und die LED blinkt mit 
schönen 1 Hertz :)

von Andreas S. (igel1)


Lesenswert?

Upps - jetzt habe ich mich selbst reingelegt - der routinen-parameter 
"ms" hat mich aus der Bahn geworfen ...

Vergiß bitte die letzten 2 Beiträge von mir.

von Andreas S. (igel1)


Lesenswert?

Ah, halt doch: bitte streiche meinen letzten Beitrag und untersuche die 
Routine doch nochmals ...

von Andreas S. (igel1)


Lesenswert?

1us ist bei 1Mhz Takt eine Winzigkeit.

Wenn Du diesen Winzig-Delay in einer for-Schleife z.B. 1000x aufrufst, 
so dürften Dir die Instruktionen, die die for-Schleife ausmachen, 
vermutlich das Timing völlig vergurken.

Will sagen: Du kommt mit weit mehr als 1000us Verzögerung aus Deiner 
long_delay routine wieder raus.

Viele Grüße

Igel1

von Mike (Gast)


Lesenswert?

Andreas S. schrieb:
> Wenn Du diesen Winzig-Delay in einer for-Schleife z.B. 1000x aufrufst,
> so dürften Dir die Instruktionen, die die for-Schleife ausmachen,
> vermutlich das Timing völlig vergurken.

So ist das, wenn der Prozessor noch was anderes zu tun hat (Schleife 
abarbeiten) als delay_us und man sich scheut, solche zeitkritischen 
Sachen einem Timer zu überlassen, der die kritische Pulsdauer völlig 
selbständig erzeugen kann.

von Robotico (Gast)


Lesenswert?

Ich scheue mich nicht, das war doch nur zum Test.


Wusste ich nicht, dann werde ich es am Wochenende mal mit einem Timer 
aufbauen und testen, danke.

von Mike (Gast)


Lesenswert?

Robotico schrieb:
> Wusste ich nicht, dann werde ich es am Wochenende mal mit einem Timer
> aufbauen und testen, danke.

Um dein Software Pulsdauer zu retten, kannst du dir auch einfach mal im 
Simulator die tatsächliche Dauer der Verzögerungsschleife ansehen und 
korrigierend bei den Konstanten eingreifen ;-)

von Peter D. (peda)


Lesenswert?

Andreas S. schrieb:
> 1us ist bei 1Mhz Takt eine Winzigkeit.

Das ist genau ein NOP.
Mit der Schleife wird das wohl 5µs dauern.

von Amateur (Gast)


Lesenswert?

Mal zwei "elektrische" Fragen:

>Verbunden habe ich Masse miteinander und die Signalleitung des Servos
>mit PB1 meines µC.

Kommt denn der Servo mit der µP-Spannung aus?
Reicht die Ausgangsleistung des µP überhaupt zum Treiben des Servos aus?

von Andreas S. (igel1)


Lesenswert?

Du kannst das Problem ganz einfach umschiffen*:

Wenn Du den Aufruf "long_delay(adc_wert);" durch "_delay_us(adc_wert)" 
ersetzen könntest, wärst Du aus dem Schneider: kein 
loing_delay-Funktionsaufruf, keine Schleife => keine Probleme.

... kannst Du aber leider nicht :-)

Warum? Weil die Funktion _delay_us(...) aus der AVR Libc in Deinem Fall 
vermutlich nur bis zu einem adc_wert von 768 funktioniert.
Kannst Du lesen hier:
http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html#gab20bfffeacc678cb960944f5519c0c4f

Also paßt Du einfach auf, daß der Übergabewert diese 768 Schwelle nicht 
überschreitet.

Dazu teilst Du ganz schlicht den adc_wert durch 4 (oder schiebst 
adc_wert zwei Bits nach rechts) und rufst mit diesem geviertelten Wert 
den _delay_us(...) dann einfach viermal hintereinander auf.

Könnte so aussehen (Achtung: Code ist ungetestet):
1
   while(1)
2
    {
3
        adc_wert = ADCW;
4
        adc_wert += 1000;
5
        adc_wert /= 4;   // nur 2er Potenzen verwenden, sonst kann der
6
                         // Compiler nicht in shift-operationen optimieren
7
    
8
        PORTB |= (1<<PB1);
9
        _delay_us(adc_wert);
10
        _delay_us(adc_wert);
11
        _delay_us(adc_wert);
12
        _delay_us(adc_wert);
13
        PORTB &= ~(1<<PB1);
14
 
15
    _delay_ms(18);      // ist nicht kritisch
16
    }


Viele Grüße

Igel1


PS: * alles oben Geschriebene ist nur Theorie und nicht wirklich selbst 
ausprobiert. Hab' schon länger nicht mehr AVR programmiert - also bitte 
Tipps mit Vorsicht genießen ...

von Andreas S. (igel1)


Lesenswert?

> Kommt denn der Servo mit der µP-Spannung aus?
> Reicht die Ausgangsleistung des µP überhaupt zum Treiben des Servos aus?

Ja - zumindest, wenn man diesem Artikel glaubt (ich tu's) :
http://www.mikrocontroller.net/articles/Modellbauservo_Ansteuerung#Signalerzeugung_f.C3.BCr_1_Servo_.28C.29

von H.Joachim S. (crazyhorse)


Lesenswert?

Timer1 mit 1MHz rennen lassen,
CTC-Modus, Top on OCR1A=20000 (=20ms)
OCR1A-Int setzt deinen Pin, OCR1B-Int setzt ihn wieder zurück
Bei 1500 im OCR1B-Register bekommst du schöne 1,5ms Signale im 
20ms-Abstand, alles ist fein.
Andere Servolage: anderen Wert nach OCR1B schreiben (1000...2000)

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:

> Warum? Weil die Funktion _delay_us(...) aus der AVR Libc in Deinem Fall
> vermutlich nur bis zu einem adc_wert von 768 funktioniert.
> Kannst Du lesen hier:
> 
http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html#gab20bfffeacc678cb960944f5519c0c4f
>
> Also paßt Du einfach auf, daß der Übergabewert diese 768 Schwelle nicht
> überschreitet.


Darauf muss er nicht aufpassen.
Aber du hättest die Doku mal weiter studieren sollen.
Man darf die _delay_xx Funktionen nur mit Konstanten benutzen und nicht 
mit zur Laufzeit berechneten Ausdrücken. Sonst stimmt das Timing nicht, 
das darauf ausgelegt ist, dass der Compiler die komplette Berechnung der 
Anzahl der Schleifendurchläufe im Vorfeld berechnen kann. Mit Variablen 
geht das nicht und daher bleibt die Berechnung (die mit Floating Point 
gemacht wird) im Programm. Was dir das komplette Timing zerschiesst.


Servo Pulse mit _delay_xx zu erzeugen, ist einfach nur Unsinn. Das kann 
man für einen schnellen Test machen, für mehr ist das aber nicht 
geeignet. Die Aufgabe wird einem Timer übertragen und gut ists.

von Karl H. (kbuchegg)


Lesenswert?

> Reicht die Ausgangsleistung des µP überhaupt zum Treiben des Servos aus?

Die ist nicht das Problem.
Auf der Signalleitung zieht das Servo keinen Strom.
Die Frage ist aber: was stellt das Servo mit der Versorgungsspannung an?
Wenn Motoren anlaufen, dann ziehen sie Strom. Und den muss das Netzteil 
liefern können.

von Robotico (Gast)


Lesenswert?

Amateur schrieb:
> Mal zwei "elektrische" Fragen:
>
>>Verbunden habe ich Masse miteinander und die Signalleitung des Servos
>>mit PB1 meines µC.
>
> Kommt denn der Servo mit der µP-Spannung aus?
> Reicht die Ausgangsleistung des µP überhaupt zum Treiben des Servos aus?

Was ist eine µP-Spannung?
Außerdem betreibe ich den Servo extra über ein 5V-Netzteil, wie weiter 
oben beschrieben.

@Andreas S.: Ja gute Idee, aber das funktioniert nicht, da das Attribut 
nur eine Konstante sein darf wie Karl Heinz erläutert hat.
Ich werds gleich mit nem Timer machen, brauche ich sowieso für das 
Programm später, der µC soll sich später nicht ausruhen und nur nen 
Servo steuern ;)

Karl Heinz schrieb:
> Die ist nicht das Problem.
> Auf der Signalleitung zieht das Servo keinen Strom.
> Die Frage ist aber: was stellt das Servo mit der Versorgungsspannung an?
> Wenn Motoren anlaufen, dann ziehen sie Strom. Und den muss das Netzteil
> liefern können.

Den sollte das Netzteil können. Das kann laut Aufdruck 3A und der Servo 
ist so winzig, der ist aus nem kleinen Modellflugzeug, der sollte so gut 
wie nix ziehen. Das schafft selbst die USB-Schnittstelle.

von Bussard (Gast)


Lesenswert?

Zittern kommt meist von einem dieser beiden "Probleme":

- etliche Servos funktionieren erst sauber mit einer Impulsspannung
  von > 3.2  bis 3.5V, darunter reagieren sie auf eventuelle
  abgerundete  Impulsflanken (als Test - keine saubere Lösung - R
  von ca. 1k von Impuls zu +5V des AVRs)
- die Plusspannung des Servos ist trotz "3A-Netzteil" nicht sauber
  bei Motoranlauf -> mit einem kleinen Elko > 100µF testweise puffern

Gruß

von Robotico (Gast)


Lesenswert?

Das "Zitter"problem war schon gelöst. Das lag an dem Wackler des 
Steckers, der die Massen verbunden hat, der Servo hat keine Masse somit. 
Daher das "Zittern".

Ansonsten habe ich mein Programm jetzt mal mit nem Timer aufgebaut, 
funktioniert 1A. Danke an alle für die Hilfe!

Hätte ich aber auch selbst drauf kommen können, wenn ich mir überlegt 
hätte wie ein Assemblerprogramm aufgebaut wäre.
Lauter NOPs (1 Takt) und das Dekrementieren einer Variable (1 Takt) in 
einer BRNE Verzweigung (1/2 Takte).
Das wären ja schon 3-4 µs pro Schleifendurchlauf...

von Andreas S. (igel1)


Lesenswert?

> @Andreas S.: Ja gute Idee, aber das funktioniert nicht, da das Attribut
> nur eine Konstante sein darf wie Karl Heinz erläutert hat.

Okay, okay - war wohl nicht eine meiner Sternstunden gestern Abend.

Ich wollte Dir als Einsteiger das Interrupt-Programmieren ersparen,
aber der Schuß ging wohl nach hinten los - sorry.

Zum reinen Prüfen, ob's der Servo überhaupt tut, sollte das folgende 
genügen:

Kommentiere einfach in Deinem Originalprogramm 
(Beitrag "Re: Servos "zittern"") die folgende Zeile 
aus:
1
// adc_wert += 1000;

Dann drehe nochmals an Deinem Poti (=ADC-Eingangsspannung verändern). Da 
sollten dann hinten an PB1 Pulswerte zwischen fast 0 und vielleicht 6ms 
rumkommen. Bei Pulswerten zwischen 1-2ms sollte Dein Servo hübsch das 
Ärmchen drehen.

Viele Grüße

Igel1


PS:  ... ich sehe gerade: während ich meinen Beitrag verfaßt habe, hast 
Du es mit dem Timer geschafft - das ist natürlich viel perfekter - 
Glückwunsch!

: Bearbeitet durch User
von Mr. K. (kaktus-)


Lesenswert?

>Ansonsten habe ich mein Programm jetzt mal mit nem Timer aufgebaut,
>funktioniert 1A. Danke an alle für die Hilfe!

Was hällst du davon deinen Code hier anstandshalber zu veröffentlichen?

Ich brauche ihn nicht, evtl jemand anders?

von Robotico (Gast)


Lesenswert?

Mr. Kaktus schrieb:
>>Ansonsten habe ich mein Programm jetzt mal mit nem Timer
> aufgebaut,
>>funktioniert 1A. Danke an alle für die Hilfe!
>
> Was hällst du davon deinen Code hier anstandshalber zu veröffentlichen?
>
> Ich brauche ihn nicht, evtl jemand anders?

Tut mir Leid, klar kann ich gerne machen:

1
#define F_CPU 1000000UL
2
 
3
#include <avr/io.h>
4
#include "lcd-routines.h"
5
#include <stdint.h>
6
#include <stdlib.h>
7
#include <util/delay.h>
8
#include <avr/interrupt.h>
9
 
10
   uint8_t high;
11
 
12
ISR( TIMER1_COMPA_vect )                // Interruptbehandlungsroutine
13
{
14
  OCR1A = 20000-OCR1A;      // Das Servosignal wird aus der Differenz von
15
                                        // Periodenlänge (2500*0,008ms=20ms) und letztem
16
                                        // Vergleichswert (OCR1A) gebildet 
17
                    
18
  if (high==0)  
19
  {
20
    high = 1;
21
  }
22
  else
23
  {
24
    high = 0;
25
  }
26
}
27
28
29
int main (void)
30
{
31
  lcd_init();
32
  
33
  DDRB = 0xFF;
34
 
35
  TCCR1A = (1<<COM1A0);                 // Togglen bei Compare Match
36
  TCCR1B = (1<<WGM12) | (1<<CS10);      // CTC-Mode; Prescaler 8
37
  TIMSK  = (1<<OCIE1A);                 // Timer-Compare Interrupt an
38
 
39
  OCR1A = 18500;                         // Neutralposition ((2500-2312)*0.008ms)=1,5ms)
40
 
41
  sei();                                // Interrupts global an
42
43
  ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADFR) | (0<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
44
  ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);
45
  
46
  uint16_t adc_wert;
47
  char Buffer[20];
48
  
49
  high = 0;
50
  
51
  while(1)
52
    {
53
        adc_wert = ADCW;
54
    adc_wert = 1000 + (adc_wert);
55
    
56
    lcd_setcursor(0,1);
57
    lcd_string("Timer Wert:");
58
    itoa(adc_wert,Buffer,10);
59
    lcd_setcursor(0,2);
60
    lcd_string(Buffer);
61
    lcd_setcursor(5,2);
62
    lcd_string("ms");
63
    
64
    cli();
65
    if (high == 0)
66
    {
67
      OCR1A = 20000 - adc_wert;
68
    }
69
    else
70
    {
71
      OCR1A = adc_wert;
72
    }
73
    sei();
74
    _delay_ms(50);
75
    
76
  }
77
  
78
  return 0;    
79
}

Das Programm gibt gleich noch den den Wert von 1000 bis 2023 ms auf dem 
LCD aus, das entspricht dann ca. Links- und Rechtsanschlag.
Ist es etwas durcheinander programmiert und kann man sicherlich deutlich 
effektiver machen, aber wie gesagt gings mir hier nur darum, das mal zu 
testen.

Die Dateien lcd_routines.c und lcd_routines.h findet man hier: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung

Muss natürlich auf den µC, den Takt und das LCD angepasst werden.


Andreas S. schrieb:
> Ich wollte Dir als Einsteiger das Interrupt-Programmieren ersparen

Das ist nicht nötig. Ist doch eine gute Sache so ein Interrupt und 
schwierig ist es auch nicht. Habe schon etwas Erfahrung im AVR 
programmieren mit Assembler, ist zwar schon ein Jahr her, aber da 
erinnert man sich an einiges wieder. Und C konnte ich auch schon, von 
daher ist das für mich nicht all zu schwer, aber danke trotzdem!

von Robotico (Gast)


Lesenswert?

Also für die eigentliche Servo-Ansteuerung ohne LCD: alles wo davor 
"lcd" steht, raus damit.

von Karl H. (kbuchegg)


Lesenswert?

Robotico schrieb:


da ...
>
>    uint8_t high;

muss noch ein volatile hin
1
volatile uint8_t high;


Gut, das wird jetzt in deinem Fall sich nicht auswirken, weil der 
COmpiler mit den ganzen LCD Funktionsaufrufen sowieso keine Chance haben 
wird, den Wert in der Hauptschleife in einem Register vorzuhalten, aber 
wenn du das Programm dann mal in einer abgespeckten Version benutzt 
könnte es dazu kommen. Und dann funktioniert es aus unerklärlichen 
Gründen nicht mehr.

von Karl H. (kbuchegg)


Lesenswert?

KLeiner Trick

Das hier
1
  if (high==0)  
2
  {
3
    high = 1;
4
  }
5
  else
6
  {
7
    high = 0;
8
  }

kann man auch so schreiben
1
  high = 1 - high;

ist kürzer und solange high nur die Werte 1 und 0 haben kann, läuft es 
aufs gleiche raus. Genauso wie
1
   high ^= 0x01;

: Bearbeitet durch User
von Robotico (Gast)


Lesenswert?

Karl Heinz schrieb:
> Robotico schrieb:
>
> da ...
>>
>>    uint8_t high;
>
> muss noch ein volatile hin
> volatile uint8_t high;
>
> Gut, das wird jetzt in deinem Fall sich nicht auswirken, weil der
> COmpiler mit den ganzen LCD Funktionsaufrufen sowieso keine Chance haben
> wird, den Wert in der Hauptschleife in einem Register vorzuhalten, aber
> wenn du das Programm dann mal in einer abgespeckten Version benutzt
> könnte es dazu kommen. Und dann funktioniert es aus unerklärlichen
> Gründen nicht mehr.

Was macht denn das volatile? Das kenne ich noch nicht.



Karl Heinz schrieb:
> KLeiner Trick
>
> Das hier  if (high==0)
>   {
>     high = 1;
>   }
>   else
>   {
>     high = 0;
>   }
>
> kann man auch so schreiben  high = 1 - high;
>
> ist kürzer und solange high nur die Werte 1 und 0 haben kann, läuft es
> aufs gleiche raus. Genauso wie   high ^= 0x01;

Stimmt, danke für den Tipp.

von Karl H. (kbuchegg)


Lesenswert?

Robotico schrieb:
>
> Was macht denn das volatile? Das kenne ich noch nicht.

FAQ: Was hat es mit volatile auf sich

: Bearbeitet durch User
von Robotico (Gast)


Lesenswert?

Ah okay vielen Dank,

man lernt eben immer zu. Werde ich natürlich nächstes Mal beachten.
Ist das nur bei Interrupts so oder allgemein bei Variablen, die in 
mehreren Funktionen verwendet werden?

von Wolfgang (Gast)


Lesenswert?

Robotico schrieb:
> Ist das nur bei Interrupts so oder allgemein bei Variablen, die in
> mehreren Funktionen verwendet werden?

C kann von haus aus nicht mit Interrupts anfangen, d.h. der Compiler 
weiß nicht, dass es eine Beziehung bezüglich der Verwendung zwischen den 
Variablen gibt. Wenn die Optimierung eingeschaltet ist, baut sie 
unkontrolliert Mist, wenn der Compiler nicht mit "volatile" zu einer 
bestimmten Vorgehensweise gezwungen ist. IMHO dürfte das Problem also 
nur zusammen mit Variablenverwendung in Interrupts auftreten.

von Karl H. (kbuchegg)


Lesenswert?

Robotico schrieb:
> Ah okay vielen Dank,
>
> man lernt eben immer zu. Werde ich natürlich nächstes Mal beachten.
> Ist das nur bei Interrupts so oder allgemein bei Variablen, die in
> mehreren Funktionen verwendet werden?


Was im Link nicht steht:

Wenn in einem Code eine Funktion aufgerufen wird
1
int i;
2
3
int main()
4
{
5
  ...
6
7
  while( 1 )
8
  {
9
    if( i == 5 )
10
      irgendwas;
11
12
    foo();
13
  }
14
}

dann muss der Compiler sowieso davon ausgehen, dass sich alle globalen 
Variablen verändert haben. D.h. hier ist implizit sowieso enthalten, 
dass i sich zwischendurch durch den Funktionsaufruf verändert haben 
könnte.

Der 'Optimierungsfall' schlägt nur dann zu, wenn es keine für den 
COMpiler ersichtliche Möglichkeit gibt, wie i seinen Wert verändern 
könnte.

So wie hier
1
int i;
2
3
int main()
4
{
5
  int j;
6
  ...
7
8
  while( 1 )
9
  {
10
    if( i == 5 )
11
      j = 8;
12
  }
13
}

aufgrund des Programmverlaufs in der Schleife existiert keine 
theoretische Möglichkeit, wie i seinen Wert verändern könnte. Und dann 
darf der Compiler die Variablenzugriffe wegoptimieren.
Sobald es aber auch nur den Hauch einer Chance gibt, wie zb einen 
Funktionsaufruf, ist dem Compiler diese Möglichkeit sowieso bereits 
verbaut.

Interrupt Routinen sind eben insofern ein Sonderfall, als es sich um 
Funktionen handelt, die zu jedem Zeitpunkt erfolgen können, ohne dass 
davon im Source Code etwas zu erkennen wäre.

Ein Grenzfall sind Aliasing-Konstrukte, wie sich durch Pointer möglich 
sind. Das war lange Zeit eine Grauzone, bis das Konzept des 
'Strict-Aliasing' in die Sprache mit aufgenommen wurde.

: Bearbeitet durch User
von Robotico (Gast)


Lesenswert?

Ah okay ja logisch macht Sinn. Aber warum hat man das nicht beim 
Compilter so mit eingebaut, dass wenn der Interrupt schon in der 
gleichen C-Datei steht, dass der das dann auch rausfindet.
Wie im Beitrag steht, könnte sich der Interrupt ja auch in einer anderen 
Datei befinden, da kann ich das nachvollziehen.


Aber vielen Dank für die ausführlichen Erklärungen!

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.