PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Uno R3 mit 128x64 Pixel Display und Dreh-Encoder grafische Anzeigeprobleme



Wolle62
01.01.2019, 18:41
Werte Community,
ich bin immer noch autodidaktisch mit Übungen am Uno beschäftigt.
Nachdem ich nun das 128x64 Pixel Grafikdisplay und den Dreh-Encoder einzeln ansatzweise ergründet habe, sollte die beiden nun zusammen arbeiten.

Dazu habe ich mir 3 Zeilen mit je dem Text "Wert 1, Wert 2, Wert 3" erstellt, an deren Ende der eingelesene Wert des Dreh-Encoders ausgegeben wird.
Mit dem Button des Encoders kann ich die Zeilen "im Kreis herum" anspringen und die 3 Werte jeweils durch drehen ändern.
Das klappt prima.

Nun dachte ich mir: "Du hast hier ein Grafikdisplay!, Da geht ein bisschen Spielerei!"
Dazu habe ich um die Zeilen ein Rechteck zeichnen lassen, welches dann, je nach diesem vorhandenen Encoder-Wert (der rechts in jeder Zeile zusätzlich dezimal ausgegeben wird) von links schwarz gefüllt wird. Wie ein Bargraph eben so ist...
Die Schrift invertiert dabei allein. Das klappt in jeder Zeile allein auch.

Aber das Problem ist, dass zwar die Zahl rechts immer richtig ist, der Wert der Variablen stimmt also, aber der "Bargraph" mit der selben Variablen nicht richtig läuft.
Der höchste Wert, der in einer der 3 Zeilen "eingedreht" wird überlagert beim Zeilenwechsel den nächsten Graphen. Auch die Schrifttransparenz ist davon betroffen. Erst über dem "Überlagerungswert" ist sie dann wieder zu sehen.

Lacht ruhig über meine primitiven Versuche! Der Code ist bestimmt irgendwo zu kompliziert gedacht und umgesetzt.
Für Verbesserungsvorschläge und Hinweise bin ich echt dankbar.

Hier der Code:


#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif


U8G2_ST7565_64128N_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

int volatile Encoderwert = 1; // Wert vom Drehencoder
int Bar1=1; // Variable von Bargraph 1 und Zahl
int Bar2=1; // 2
int Bar3=1; // 3

int taster=4; // Alias für Pin4
int tasterstatus=0; // Tasterstatusvariable
int Cursor=8; // Variable für Y Achse des zu steuernden Bargraphs und der Zahl rechts
// 8 = Zeile 1, 28 = Zeile 2, 48 = Zeile 3

void setup(void) {
u8g2.begin(); //Displayeinstellungen
u8g2.setContrast(128);
u8g2.setFlipMode(0);
u8g2.setFontMode(1); // activate transparent font mode

pinMode(3, INPUT); //Pin 2 und 3 für Drehencoder
pinMode(2, INPUT);
pinMode(taster, INPUT); // Pin 4 für Taster


attachInterrupt(digitalPinToInterrupt(2),Encoderre ad,CHANGE);


}

void loop(void) {

tasterstatus=digitalRead(taster); //Tasterabfrage
if ((tasterstatus == HIGH)&&(Cursor==8)) //Wenn gedrückt und Cursor steht auf Y Position 8
{
Cursor=Cursor+20; //Schalte den Cursor weiter auf Y Pos. 28 (Zeile 2)
Encoderwert=Bar2; //Der Encoderwert wird durch den Wert von Bar2 ersetzt
} // damit die nicht vom Bar1 Wert überschrieben wird
else if ((tasterstatus == HIGH)&&(Cursor==28)) //Wenn Taste gedrückt und Y Position steht auf 28
{
Cursor=Cursor+20; //Dann Cursor auf Y Position 48 weiterschalten (Zeile3)
Encoderwert=Bar3; //Wie oben

else if ((tasterstatus == HIGH)&&(Cursor==48)) //Taste gedrückt und Y Pos. ist auf 48
{
Cursor=8; // wieder zurück auf Y Pos 8 (Zeile1)
Encoderwert=Bar1; // wie oben
}
else if (Cursor==8) // Ist der Taster nicht gedrückt und Y Pos ist 8
{
Bar1=Encoderwert; // geht der Wert aus "Encoderwert" auf die Bargraph 1 Variable "Bar1"
}
else if (Cursor==28)
{
Bar2=Encoderwert; // Wie bei Bargraph 1
}
else if (Cursor==48)
{
Bar3=Encoderwert; // Wie bei Bargraph 1
}

u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar1,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory
u8g2.setCursor(95, 20);
u8g2.print(Bar1); // write something to the internal memory

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar2,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,40,"Wert 2 = "); // write something to the internal memory
u8g2.setCursor(95, 40);
u8g2.print(Bar2); // write something to the internal memory

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar3,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,60,"Wert 3 = "); // write something to the internal memory
u8g2.setCursor(95, 60);
u8g2.print(Bar3); // write something to the internal memory


u8g2.sendBuffer(); // transfer internal memory to the display

delay(250);


}

void Encoderread(){

if(digitalRead(2)!= digitalRead(3))
Encoderwert--;
else Encoderwert++;

}

Irgendwie waren alle Umlaute nach der Vorschaufunktion gestört?? Bin mit Windows 7 unterwegs?? Kann oder muss ich hier was einstellen?
Habe es erstmal korrigiert
Danke!

Moppi
01.01.2019, 23:06
Hallo Wolle,

ändere mal bitte "int volatile Encoderwert = 1;" in "volatile int Encoderwert = 1;" !
Hier Referenz dazu: https://www.arduino.cc/reference/en/language/variables/variable-scope--qualifiers/volatile/

Übersetzung:

volatile ist ein Schlüsselwort, das als Variablenqualifikationsmerkmal bezeichnet wird. Es wird normalerweise vor dem Datentyp einer Variablen verwendet, um die Art und Weise zu ändern, auf die der Compiler und das nachfolgende Programm die Variable behandeln. Das Deklarieren einer variablen Variable ist eine Anweisung an den Compiler. Der Compiler ist eine Software, die Ihren C / C ++ - Code in den Maschinencode übersetzt. Dies sind die echten Anweisungen für den Atmega-Chip im Arduino.

Ich vermute, Du musst den Encoderwert auf die Breite der Box skalieren. Ich kann nicht sehen, wie breit die Box max. sein darf und wie hoch der Encoderwert maximal ist. Daher liegt die Vermutung nahe, dass der Encoderwert größer wird, als die Max-Boxbreite + dem Offset von 10. Wenn Max-Boxbreite = Max-Encoderwert sein sollte, dann beginnst Du an X-Position 10 und hast dadurch einen Überlauf.

Versuchsweise könnte man das mal so machen:



int Prozent=50;

int Bar1n=Bar1/100*Prozent;
int Bar2n=Bar2/100*Prozent;
int Bar3n=Bar3/100*Prozent;
u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar1n,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory
u8g2.setCursor(95, 20);
u8g2.print(Bar1); // write something to the internal memory

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar2n,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,40,"Wert 2 = "); // write something to the internal memory
u8g2.setCursor(95, 40);
u8g2.print(Bar2); // write something to the internal memory

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,Cursor,Bar3n,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,60,"Wert 3 = "); // write something to the internal memory
u8g2.setCursor(95, 60);
u8g2.print(Bar3); // write something to the internal memory

Damit wird jeder Bar nur 50% breit. Wenn dann alles klappt, kann man die Prozente erhöhen, bis es dann nicht mehr funktioniert und schauen, wo die Grenze ist. Könnte man dann für die Ausgabe im Display immer auf diesen Wert skalieren und fertig.

MfG

Wolle62
02.01.2019, 11:17
Hallo Moppi,
Danke für die schnelle Hilfe.
Zum Verständnis der Darstellung mal ein Bild im Anhang. Der Bargraph geht jeweils über die Schrift. Min und Max habe ich noch nicht festgelegt. Beim Start ist ja "1" definiert...

33902

Man sieht oben, dass der Bargraph die Schrift überdeckt. Die wäre normaler Weise jetzt auf die Länge invertiert zu sehen.
Der Balken hat jetzt die Länge des Wertes 43 aus Zeile 2! Das ist auch das Problem..

Also:
Der in irgendeiner Zeile maximale Wert von allen drei Zeilen wird beim durchschalten durch die Zeilen mit dem Button leider immer auf die anderen Zeilen verschleppt. Hinten, wo die gleiche Variable als Zahl angezeigt wird, stimmt die immer.
Drehe ich nun weiter, wird der Rest der Schrift ab dem Wert (Länge) 43 wieder invertiert sichtbar.
Ich hatte erwartet, dass der Bargraph beim Durchschalten automatisch immer die Länge der Zahl hinten bekommt, da es ja die selbe Variable ist??

Mit Deiner Änderung passiert nun folgendes:
Ich muss immer mit dem Encoder über den Wert 100 kommen, dann erscheint der Bargraph mit der Länge 50 auf einen Schlag und geht dann normal weiter jeden 2. Step.
Vor 100 ist keiner zu sehen.

Ich habe testweise mal alle Bargraphs im Sketch "parallel geschaltet" um ein Ansteuerproblem beim Display auszuschliessen. Dann laufen die sauber alle 3 gleichzeitig mit dem Encoder mit. Und die Schrift bleibt auch lesbar. Das Umschalten macht das Problem.

Gibt es bei der U8G2 Lib einen Unterschied zwischen "DrawBox" und "Print(Text)"?

Moppi
02.01.2019, 11:29
Der in irgendeiner Zeile maximale Wert von allen drei Zeilen wird beim durchschalten durch die Zeilen mit dem Button leider immer auf die anderen Zeilen verschleppt.

Das hatte ich heute Nacht noch nicht so gesehen, als ich mir den Code ansah. Wenn die Zahl stimmt, muss auch der Balken stimmen, denn Du führst die Zahl ja nicht extra in einer Variablen.

Also mal zum Vertändnis:

1. Du drehst am Rad ;)

2. Du veränderst damit einen Wert

3. den Wert zeigst Du in einer Zeile am Ende an

4. dieser Wert ist richtig und nicht falsch

5. Du verwendest denselben Wert zur Darstellung des Balkens

6. der Balken wird falsch dargestellt


Vielleicht liegt es an: u8g2.drawBox() :confused:
Eventuell brauchst Du für jede Zeile ein extra Objekt oder was in der Richtung.

1) Wenn Du am Rad drehst und weiter und weiter und immer weiter in dieselbe Richtung, dass der Wert immer größer wird, gibt es dann mal irgendwo einen Endwert? Oder wird der Wert immer größer und größer?
2) Was ist der maximale Wert für die Breite des Balkens in drawBox() für eine Zeile?

Ich habe noch mal geschaut, wenn ich das richtig sehe, dann verwendet U8G2 einen Pixelpuffer in welchem man sich "bewegen" kann, indem man eine Draw-Methode aufruft und mit X- und Y-Position in Pixel die Startposition oben Links angibt. Ist das korrekt?

Ich habe noch was gefunden in:


u8g2.drawBox(10,Cursor,Bar1,15); u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory

Du verwendest einmal absolute Angabe und einmal aus Variable Cursor.

ändere das entweder in:

u8g2.drawBox(10,Cursor,Bar1,15); u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,Cursor,"Wert 1 = "); // write something to the internal memory


oder in absolute Angabe, wie hier als Beispiel:


u8g2.drawBox(10,20,Bar1,15); u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory



Das ist glaub ich das Problem, dass Du in der ersten Zeile den Balken mit der Länge vom Wert aus der zweiten Zeile erzeugst und so ist das dann mit den anderen auch.

So müsste es besser sein:


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,20,Bar1,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory
u8g2.setCursor(95, 20);
u8g2.print(Bar1); // write something to the internal memory


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,40,Bar2,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,40,"Wert 2 = "); // write something to the internal memory
u8g2.setCursor(95, 40);
u8g2.print(Bar2); // write something to the internal memory


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,60,Bar3,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,60,"Wert 3 = "); // write something to the internal memory
u8g2.setCursor(95, 60);
u8g2.print(Bar3); // write something to the internal memory



MfG

Wolle62
02.01.2019, 12:01
Ein JA auf alle 6 Fragen.

Darstellungsende und maximale Balkenbreite bei dem Display ist logischer Weise 128. Da bleibt der stehen
bis 256 512 ... ,
dann fängt der Bargraph von vorn neu an.
Das Gleiche gilt linksrum für negative Werte. Das grenze ich später dann ein, auf Werte, die ich benutzen kann.


EDIT:
Hat sich überschnitten, ich setze Deinen Ansatz mit den Werten mal in die Tat um...

EDIT2:
Du hast Recht!! Es läuft. Ich habe nun absolute Angeben genommen.
Nun sind alle 3 Bargraphen immer gleichzeitig sichtbar und alle haben die richtige Länge
und behalten die auch! Schrift ist immer zu sehen! DANKEEE !!!!!
Der Tastendruck wechselt nun nur noch die Zuordnung des Encoders zur Zeile.
Sieht sogar noch besser aus als vorher. Allerdings fehlt nun ein Symbol oder Cursor vorn,
damit ich weiss in welcher Zeile ich bin.

Hast Du da einen Ansatz auf der Hand? Ohne das ich das Problem erneut einbaue?
Achso, wenn ich diese eingestellten Werte permanent behalten will, dann muss ich mich nun mit EEPROMs beschäftigen ?

Moppi
02.01.2019, 12:19
Ich habe oben Sachen nachgetragen.

In Deinem Ursprungs-Code hast Du immer die Variable Cursor zur Ausgabe der Box verwendet, aber Du hast Cursor nie geändert, um die die Box für die nächste Zeile zu zeichnen.

MfG

Wolle62
02.01.2019, 12:37
...aber Du hast Cursor nie geändert, um die die Box für die nächste Zeile zu zeichnen

MMM, in der Loop habe ich dafür ein IF ELSE Konstrukt, wo der Wert für "Cursor" je nach IST-Position und Tastendruck wechselt.

Die Koordinate der Box für Zeile 1 ist 8, da die oben links angegeben werden muss.
Die Schrift hat aber in der gleichen Zeile bei dem Font die 20, damit die mittig drin steckt.

Da müsste ich, sollte ich Variablen verwenden wollen, aus einer, eine zweite umrechnen lassen. Werde ich auch testen.

Moppi
02.01.2019, 12:49
Achso entschuldige, nicht aufgepasst!

Also 20-8 = 12.


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,20-12,Bar1,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory
u8g2.setCursor(95, 20);
u8g2.print(Bar1); // write something to the internal memory


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,40-12,Bar2,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,40,"Wert 2 = "); // write something to the internal memory
u8g2.setCursor(95, 40);
u8g2.print(Bar2); // write something to the internal memory


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,60-12,Bar3,15);
u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,60,"Wert 3 = "); // write something to the internal memory
u8g2.setCursor(95, 60);
u8g2.print(Bar3); // write something to the internal memory






MMM, in der Loop habe ich dafür ein IF ELSE Konstrukt, wo der Wert für "Cursor" je nach IST-Position und Tastendruck wechselt.

Das ja, das stimmt. Aber für die Ausgabe nutzt das so nichts. Weil Du beim Schreiben in den Pixelpuffer immer alles neu schreibst, nachdem der Puffer gelöscht wurde.


MfG

Wolle62
02.01.2019, 13:42
Vielen Dank!

Ich habs nun verstanden. Ohne die Hilfe wäre das nicht geworden.

So:

u8g2.clearBuffer(); // clear the internal memory


u8g2.setFont(u8g2_font_unifont_t_symbols);
u8g2.drawGlyph(3,Cursor+12,0x2192); //Hier ist der Pfeil

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,8,Bar1,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,20,"Wert 1 = "); // write something to the internal memory
u8g2.setCursor(95, 20);
u8g2.print(Bar1); // write something to the internal memory


u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,28,Bar2,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,40,"Wert 2 = "); // write something to the internal memory
u8g2.setCursor(95, 40);
u8g2.print(Bar2); // write something to the internal memory

u8g2.setDrawColor(1); /* color 1 for the box */
u8g2.drawBox(10,48,Bar3,15);

u8g2.setDrawColor(2); /* color 1 for the box */
u8g2.drawStr(25,60,"Wert 3 = "); // write something to the internal memory
u8g2.setCursor(95, 60);
u8g2.print(Bar3); // write something to the internal memory


u8g2.sendBuffer(); // transfer internal memory to the display

delay(250);

geht es nun. Ich habe einen Pfeil vor dem aktiven Bargraph und alles läuft wie es soll.
Den Rest konnte ich so lassen.
Den Font und das Min Max des Wertes passe ich dann erstmal an...

Danke!

Moppi
02.01.2019, 14:55
Prima, geschafft! :)

MfG

Wolle62
06.01.2019, 12:37
Super, nun habe ich noch einen EEPROM 24C16P in der Bastelkiste gefunden,
der jetzt die Werte des Drehencoders beim Drücken der Taste übernimmt.
Beim Einschalten werden die dann wieder ausgelesen und in die Variablen gesteckt.

So macht das Spass!

Nun die Frage dazu:
Die Speicherzellen des EEPROMs sollte man ja wohl "Im Kreis" nutzen, damit der länger hält.
Ist ein Siemens:
– Endurance 10"hoch"6 cycles1)
– Data retention 40 years1)
– ESD protection 4000 V on all pins

Ich werde den nicht ruinieren können. Ich weiß, eine SD-Card mit integriertem Controller nehmen...
Aber es geht hier ja ums lernen!

Deshalb trotzdem an die Profis, wie mache ich das mit der Speicherzellenausnutzung?

-Die Variable für die Adresse hochzählen?
-Aber wie lese ich dann am Start die Daten ab der "richtigen" (zuletzt gewählten) Zelle aus??

Da bin ich mit meinem Wissen am Ende.

Searcher
06.01.2019, 14:21
Deshalb trotzdem an die Profis, wie mache ich das mit der Speicherzellenausnutzung?

Hallo Wolle62,
ich hatte die gleiche Frage und gute Antworten darauf bekommen.
https://www.roboternetz.de/community/threads/69946-Schreibalgorithmus-f%C3%BCr-Flashspeicher

Für mich hat ein ganz einfacher Algorithmus gereicht. Nach Schreiben eine page die folgende komplette page mit einem Muster (hex ff durch Löschen) belegt. Und dann vor dem nächten Schreiben diese von Adresse Null aus gesucht, mit meinen Daten überschrieben und die nächste gelöscht. Also reihum alle Seiten nacheinander beschrieben.

Gruß
Searcher

Wolle62
06.01.2019, 15:45
Ja klar, man schreibt eine "Kennung" in die EEPROM Zelle vor den Nutz-Werten und findet die beim Start. Sollte auch gehen oder?
Das mit dem Uhrenmodul ist auch nicht schlecht.

Mein Uno sagt mir jetzt schon, dass der RAM knapp ist, da wird die Suchroutine hoffentlich noch mit laufen.
Auf die Dauer muss da deshalb noch ein Mega her.
Ich habe auch gerade ein 480x272 TFT in meiner Bastelkiste gefunden. Das geht mit dem Uno auf keinen Fall mehr.

Wolle62
21.01.2019, 19:00
Ich brauche Hilfe beim Zählen! Ehrlich jetzt!!
Ich erhalte meine Zahlenwerte ja aus der ISR mit dem Drehencoder erzeugt.

Variable:
volatile int Encoderwert;

setup:
attachInterrupt(digitalPinToInterrupt(2),Encoderre ad,CHANGE);

ISR:

void Encoderread(){

if(digitalRead(2)!= digitalRead(3))
Encoderwert++;



else Encoderwert--;

Was da rauskommt ist nun DEZIMAL.
Ich möchte aber eine Zeit im 10 Minuten Takt hoch und runter zählen lassen.
Statt 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, usw.
Also 10, 20, 30, 40, 50, 1:00, 1:10, 1:20, usw bis 23:50 vor- und rückwärts.
(Den Doppelpunkt und die letzte Null kann ich vor der Ausgabe im Display hinzufügen. Also 110, 120, 130, geht)

Kann ich das in der ISR direkt umrechnen lassen?
So etwas wie "if (Encoderwert==6) Encoderwert=10" geht nicht...

Im Netz gibt es haufenweise Online Rechner "dezimal<>senär" , aber es gibt immer nur die schriftliche Umrechnung in der
Schulversion davon. Wie macht man das beim Arduino am besten?
Geht das direkt in der ISR überhaupt?
Muss die Variable dann "nur "int" sein und nicht noch "volatile"?

Ich habe echt das Brett vorm Kopf. Ich denke auch (laut Mister Spok) nur zweidimensional, habe ich so den Verdacht.
Meine Sketche leben immer nur von "wenn, "dann". Auch wenn alles bis auf das zählen perfekt funktioniert.

Da fehlt mir die Grundausbildung auf dem Gebiet.

Searcher
22.01.2019, 12:00
Hallo Wolle62,


Was da rauskommt ist nun DEZIMAL.
Ich möchte aber eine Zeit im 10 Minuten Takt hoch und runter zählen lassen.
Statt 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, usw.
Also 10, 20, 30, 40, 50, 1:00, 1:10, 1:20, usw bis 23:50 vor- und rückwärts.
(Den Doppelpunkt und die letzte Null kann ich vor der Ausgabe im Display hinzufügen. Also 110, 120, 130, geht)

bin nicht sicher, ob ich Dich richtig verstehe. Aber angenommen, die Dezimalzahlen 50, 60, 70 ... werden als Minuten angenommen, die in eine Stunden- und Minutenangabe umgerechnet werden sollen, würde ich es zunächst mit Division und der Modulo Funktion versuchen:

Stunden = Minuten / 60 (da eine Stunde 60 Minuten hat, zeigt der ganzzahlige Anteil vom Ergebnis die Stunden)
Restminuten = Minuten % 60 (In Restminuten landet durch "modulo" der ganzahlige Rest der Division von Minuten/60)

Das Ergebnis in Stunden kann Nachkommastellen enthalten. Manche Programmiersprachen lassen da eine Ganzzahl zu, bei der automatisch die Nachkommastellen der Division abgeschnitten werden. Also gleich die ganzzahlige Stundenzeit enthält.

Dann Ausgabe mit "Stunden : Restminuten"



Kann ich das in der ISR direkt umrechnen lassen?
So etwas wie "if (Encoderwert==6) Encoderwert=10" geht nicht...

Die Rechnnung oben läßt sich in der ISR machen. Duch die Divisionen aber rechenintensiv und könnte zu zeitaufwendig sein. Müßtest Du ausprobieren. Ich bin weder C noch Arduino sprachig aber Variable, die in einer ISR benutzt werden, sollten wohl mit volatile deklariert werden? Schaden tut es nicht.

Gruß
Searcher

Wolle62
22.01.2019, 20:50
Ich will mir eine Aquarium-Beleuchtungssteuerung mit Wochentimer bauen.
Nun habe ich mit dem Sketch dafür angefangen. Bei der Konzeption habe ich überlegt, wie ich die Timerzeiten
eingeben will. Dafür fand ich den Drehencoder sinnvoll. Nun war die Frage, wie genau muss das überhaupt sein?
(Man will ja nicht am nächsten Tag Muskelkater im Finger haben, weil man den Encoder 1000mal drehen muss.)

Also entschied ich mich für den 10 Minutentakt. Da dann alle Zeiten hinten eine Null haben, habe ich mit nur 3 Stellen im Sketch gearbeitet.
Also für 19:10 > 191. für 20:30 > 203 und 08:10 > 81 usw.. Für die Anzeige auf dem Display habe ich die Einerstelle abgezwickt und mit 10 multipliziert.
Das Display zeigt dann diese Werte als Zeit an.

Im Eingabemenü der Timerwerte muss ich nur aufpassen, dass ich nicht 19:70. (197) eindrehe, da es dafür ja keinen Vergleichswert gibt, der vom RTC Modul kommt.
Das gibt ja nur normale Zeitwerte aus und keine Industriezeit, logisch...

In meiner Unkenntnis dachte ich mir: So eine Kleinigkeit wie das senäre Zählen kannst Du ja zum Schluss korrigieren.
Ein Schönheitsfehler nur, denn sonst läuft alles genau wie es soll.

Ich will also nicht die Werte in Zeit umrechnen, sondern die Zählweise des Drehencoders so beeinflussen, dass er senär hoch und runter zählt.

Searcher
23.01.2019, 05:22
Im senären Zahlensystem kommen Ziffern größer 5 nicht vor. Also 19:10 bzw 191 ist wegen der 9 nicht möglich.

Um nur gültige Uhrzeiten mit den 3-stelligen Dezimalzahlen darzustellen, könnte man nach Encoderwert++ die letzte Ziffer in Encoderwert mit der modulo Funktion abfragen und falls sie größer 5 ist, zur 3-stelligen Zahl nochmals 4 addieren. Bzw nach Encoderwert-- eine 4 subtrahieren.

Pseudocode falls die dreistellige Zahl in Encoderwert steht:


void Encoderread(){

if(digitalRead(2)!= digitalRead(3))
Encoderwert++;
Rest = Encoderwert % 10
if Rest > 5 then Encoderwert = Encoderwert + 4


else Encoderwert--;
Rest = Encoderwert % 10
if Rest > 5 then Encoderwert = Encoderwert - 4

Wolle62
23.01.2019, 12:55
Vielen Dank!!! Der Code macht das, was ich mir vorstelle !!! :D
Ich habe die Modulo Operation falsch interpretiert. Ich muss den Rest vergleichen lassen und nicht den Wert !!! Ich Dummbatz...

Damit habe ich schon experimentiert, aber es lief immer nur von Null bis 5 und dann wieder zurück auf Null bis 5.
Die nächste Zehner-Stelle mit der Eins kam nicht, weil ich den "Encoderwert" verglichen habe.

Dank Deiner Hilfe habe ich den Modulo halbwegs verstanden, aber noch nicht ganz.

Warum "Encoderwert%10" ? Weil das Zählen doch im Dezimal System bleibt, weil ich ja Zahlen über 5 bis 9 verarbeite?
"Rest>5" bedeutet also nach der 5. Operation?
Weshalb wird beim runterzählen auch auf "Rest>5" geprüft?


void Encoderread(){

if(digitalRead(2)!= digitalRead(3)){
Encoderwert=Encoderwert+1;

Rest = Encoderwert % 10;
if (Rest > 5) Encoderwert = Encoderwert + 4;

}

else Encoderwert=Encoderwert-1;

Rest = Encoderwert % 10;
if (Rest > 5) Encoderwert = Encoderwert - 4;

Encoderwert=constrain (Encoderwert,0,240); //Wert des Encoders begrenzen auf 240
}


So und mit "volatile int Rest;" als Variable geht es nun perfekt.
Wieder was gelernt!!
Ich bin froh, das es doch so geht, wie ich mir das vorgestellt habe. Schwein gehabt.

Searcher
23.01.2019, 17:02
Astrein :Strahl


Warum "Encoderwert%10" ? Weil das Zählen doch im Dezimal System bleibt, weil ich ja Zahlen über 5 bis 9 verarbeite?
"Rest>5" bedeutet also nach der 5. Operation?
Weshalb wird beim runterzählen auch auf "Rest>5" geprüft?

Liegt an Deiner Vorarbeit. Du hattest für die Uhrzeit die (maximal) 3-stellige Zahl vorgegeben bei denen die letzte Stelle das 10-Minuten Intervall angibt, das nur von 0 bis 5 gehen soll/darf. Wird nach oben gezählt, muß nach der 5 wieder die 0 kommen. Beim Nachuntenzählen muß nach der 0 die 5 folgen.

Die ersten beiden Stellen der 3-stelligen Zahl zählen aber "normal" im Dezimal System nach oben bzw unten.

Man muß also für Deine gewünschte Zählweise nur die letzte Ziffer für das 10-Minuten Intervall im Encoderwert überprüfen und gegebenenfalls korrigieren.

Die Modulo Funktion ist eine Division, bei der die Nachkommastellen nicht ausgerechnet werden, sondern als Ergebnis sozusagen nur die Nachkommastellen mit einer ganzen Zahl, eben dem Rest liefert. (Eigener Definitionsversuch - andere, sicher bessere sind im Inet zu findnen)

zB 195 % 10 = 5
195 geteilt durch 10 ist gleich 19 Rest 5
(10 * 19 = 190, 195 - 190 = 5)
Die 5 ist das Ergebnis der Modulo Funktion

oder zB 223 % 7 = 6
223 geteilt durch 7 ist gleich 31 Rest 6
(7 * 31 = 217, 223 - 217 = 6)
Die 6 ist das Egebnis der Modulo Funktion

Wenn man nun bei einer Dezimalzahl die letzte Ziffer "isolieren" möchte, bietet sich eben das Modulo mit 10 an, da ja bei einer Division durch 10 als Rest immer die Einerstelle bleibt. (Möchte man zB die letzten beiden Ziffern isolieren, nimmt man Modulo 100).

Jetzt hat man also die Einerstelle, die sich einfach auf den erlaubten Wertebereich überprüfen läßt.

Hat der Encoder nach oben gezählt und der Rest größer (Kurzzeichen >) 5 ist, wird 4 zum Encoderwert addiert. Aus der 6, 7, 8, 9 wird eine 0, 1, 2, 3 und es wird auch die nächste Stelle im Encoderwert durch den Überlauf (6 + 4 ist 10) hochgezählt. Möglicherweise reicht der Vergleich auf =6, da ja nach der Addition von 4 die 7, 8, 9 beim nächsten Encoderpuls nicht mehr in Frage kommen. So ist es jedenfalls sicher falls durch zB Kontaktprellen vielleicht doch irgendwie eine andere "verbotene" Zahl auftaucht.

Hat der Encoder nach unten gezählt, müssen die gleichen Ziffern (6, 7, 8, 9) abgefangen werden. Deshalb auch wieder der Vergleich auf größer 5, aber nun muß entsprechend der Encoderdrehrichtung die 4 abgezogen werden. Hier tritt auch wieder bei der 0 minus 4 ein Unterlauf aus, der die nächst höhere Stelle in der 3-stelligen Zahl decrementiert.

Bestimmt gibt es auch andere Möglichkeiten die Zählung zu implementieren; diese fiel mir halt als erste ein und hoffe Dich jetzt nicht zu viel zugeschwallt zu haben und hoffentlich keinen Unsinn erzählt zu haben.


Schwein gehabt.

:):D:lol:
Gruß
Searcher

Wolle62
23.01.2019, 18:17
Wenn man nun bei einer Dezimalzahl die letzte Ziffer "isolieren" möchte, bietet sich eben das Modulo mit 10 an, da ja bei einer Division durch 10 als Rest immer die Einerstelle bleibt. (Möchte man zB die letzten beiden Ziffern isolieren, nimmt man Modulo 100).

Danke, genau so habe ich das nun auch gemacht, um die 3-stellige Zahl in 2-stellig / 1-stellig zu transformieren, die dann mit Doppelpunkt zwischen angezeigt werden können, so das es nach Uhrzeit aussieht.

Der Uno ist nun voll (100% Programmspeicher, ich bin schockiert), ich habe daraufhin alle "int" auf "byte" gesetzt,da ich keine Werte über 255 habe.
Sonst hätten mir nur ein paar Bytes zur Fertigstellung gefehlt. (nun 98%)
Ich habe mir heute erstmal noch einen Mega R3 bestellt. Das nächste Projekt ist ja ein Farb-LCD 480x272 mit Touchscreen...
Alle Teile übrigens, außer dem Uno und dem Breadboard, sind vom Schrott. Ich bin so der Resteverwerter beim Basteln.

Ich wollte eigentlich noch die Displaybeleuchtungssteuerung ins Menü bekommen, aber durch meine unzureichende Kenntnisse ist der Code bestimmt
sehr ineffizient und dadurch so groß geworden. Ist ja auch mein allererstes Projekt auf dem Arduino... Ein Wunder dass das überhaupt läuft.
Die digitalen und PWM Ports sind auch alle belegt, deshalb der Mega.

Ich stelle den fertigen Sketch in Kürze hier ins Forum bei Projekte ein.Wer dann ganz viel Langeweile hat, kann sich den ja ansehen.
Den meisten Platz belegt die U8G2 Lib. Da sind es schon 76% Auslastung. Ich habe bislang keine anfängerfreundliche Alternative dafür gefunden,
die sehr viel ressourcenschonender ist.


Vielen Dank an Euch nochmals, ich kümmere mich jetzt um den LED Treiber mit PWM Eingang...

Wolle62
14.02.2019, 19:45
Ich will nicht einen neuen Thread aufmachen, da die Frage jetzt nichts mehr mit dem Titel gemein hat.
Also, ich habe eine "Funktion" in meinem Sketch, die, wenn aufgerufen solo läuft. as klappt auch alles.
Dazu habe ich in der Main loop die Abfrage nach dem Taster, der das Umschalten bewirkt und eine Variable, die den Zustand anzeigt.

Sinngemäß so:
Wenn ((Taster 1 == HIGH)&&( X==0)) dann X=1;
while X==1 Funktion();

In der Funktion frage ich den Taster dann wieder ab, um wieder zurück zur Main loop zu kommen:

Wenn ((Taster 1 ==HIGH)&&(X==1) dann X=0;

Was mich stört ist, dass wenn man den Taster festhält, es immer rein und raus und rein und raus geht. Man darf also immer nur kurz drücken. Klar, so steht es ja auch im Sketch...
Ich grübele, wie ich das verbessern kann, dass der Zustand für jedes Umschalten nur einmal läuft.
Mir fehlt da wieder eine Gehirnwindung. Helft mir mal auf die Sprünge. Wie macht man das richtig?

HaWe
14.02.2019, 20:19
du könntest meine Button Class verwenden, die abfragt, ob ein Button KURZ gedrückt wurde
(Rückgabewert: short press=1):

https://github.com/dsyleixa/Arduino/tree/master/ButtonClass

Alternative Rückgabewerte:
2 (double click)
3 (long press)
0 (no press)

____
( PS: ich schließe mich Searcher an, dass bei neuen, anderen Problemen auch ein neues Topic aufgemacht werden sollte)

Searcher
14.02.2019, 20:21
Ich will nicht einen neuen Thread aufmachen, da die Frage jetzt nichts mehr mit dem Titel gemein hat.
Neuer thread wär kein Problem und fänd ich auch besser.



Sinngemäß so:
Wenn ((Taster 1 == HIGH)&&( X==0)) dann X=1;
while X==1 Funktion();

In der Funktion frage ich den Taster dann wieder ab, um wieder zurück zur Main loop zu kommen:

Wenn ((Taster 1 ==HIGH)&&(X==1) dann X=0;

Was mich stört ist, dass wenn man den Taster festhält, es immer rein und raus und rein und raus geht. Man darf also immer nur kurz drücken.

So ganz klar ist mir der gewollte Ablauf nicht. Wenn Du in der Funktion nicht auf Taster HIGH abfragst sondern auf Taster==LOW, dann muß der Taster erst losgelassen werden damit X zu 0 wird. In der Folge wird in der main das X nicht mehr zu 1, es sei denn Taster wird erneut zu HIGH.

Also in der Funktion "Wenn ((Taster 1 ==LOW)&&(X==1) dann X=0;"

Gruß
Searcher

021aet04
14.02.2019, 22:32
Das geht relativ einfach. Wird in der SPS-Programmierung häufig verwendet. Man findet einiges wenn man nach Flankenauswertung sucht.
Aber prinzipiell geht es so:
Man benötigt eine Hilfsvariable, um den vorhergehende Zustand zu kennen und je nach Zustand muss man reagieren.
Die Hilfsvariable nenne ich "taste_alt", die Taste selbst nenne ich "taste". Das Programm reagiert auf pos. Flanke.

wenn ((taste == 1) && (taste_alt == 0))
{
x ^= x
}

taste_alt = taste


MfG Hannes

Wolle62
15.02.2019, 12:49
x ^= x das verstehe ich nicht. Was für ein Operator ist das "^="?
Ist das die Auswertung auf eine ansteigende Flanke?
So etwas hatte ich gesucht, aber nicht gefunden.

Ich habe es nun so gelöst:


void loop(){

tasterstatus3=digitalRead(taster3); //Der Taster fürs Einstellmenü
if ((tasterstatus3==LOW)&&(Einstellmenu==2))Einstellmenu=0;
if ((tasterstatus3==HIGH)&&(Einstellmenu==0))Einstellmenu=3;


while ((Einstellmenu==3)||(Einstellmenu==1))einstell();

}

void einstell(){ //Das Menü zu Einstellen des Timer

tasterstatus3=digitalRead(taster3); //damit man wieder rauskommt
if ((tasterstatus3==LOW)&&(Einstellmenu==3))Einstellmenu=1;
if ((tasterstatus3==HIGH)&&(Einstellmenu==1))Einstellmenu=2;

}



Wie gesagt, die Funktion läuft solo, wenn gesetzt(while..). Daher muss der Ausstieg in der Funktion selbst erfolgen.
Nun ist sicher, dass der Taster erst wieder losgelassen werden muss, bevor man mit dem nächsten Druck aus der Funktion zurück in die Loop kommt.
Umständlich, aber geht.
Der Tipp mit einer zusätzlichen Variablen war genau richtig! Danke!!!

Das nächste Problem dann in einem neuen Thread, verstanden!

021aet04
16.02.2019, 10:30
Die Zeile "x ^= x" ist falsch, da x immer 0 bleiben würde. Das sollte eigentlich "x ^= 0xff;" heißen. Diese Zeile ist gleichbedeutend mit "x = x ^ 0xff;", das Zeichen "^" bedeutet in der Sprache C "bitweises XOR". Die Wahrheitstabelle eines XOR ist (A & B sind Eingänge, Q ist der Ausgang):
A B Q
0 0 0
0 1 1
1 0 1
1 1 0

Sprich wenn A und B unterschiedlich sind, ist der Ausgang 1, sonst 0.

Das kann man z.B. zum Invertieren einer Led nutzen. Z.B. "PORTB ^= (1<<PB0);" ändert jedes mal den Ausgang PB0.

Das mit dem XOR habe ich geschrieben, weil du oben geschrieben hast ".....(x == Low) dann x = high" und dann "...(x == high) dann x = low", das wäre eine einfache Invertierung.

MfG Hannes

HaWe
16.02.2019, 10:33
verständlicher wäre
x= !x;
wenn man wechselweise von 1 auf 0 switchen will und wieder zurück

Wolle62
16.02.2019, 12:52
Oha, da habt ihr Opa aber eine Denksportaufgabe gegeben. Beschäftige ich mich gern mit.
Ich will ja auch lernen, meinen Code besser zu gestalten, und nicht , wie zur Zeit noch, alles mit
Wenn, dann zu verarbeiten.
Aber klar ist schon, dass es hier nicht um das schalten von 1 auf 0 und zurück allein geht.
Es ging um die Tatsache, dass die Bedingung Taster ==HIGH und der Zustand der Variable
beim hin- und herschalten immer gültig sind!

Also
1. "Taster==HIGH und Zustand==0" > Setze Zustand auf "1". Ist gültig und ich fliege aus der Loop in die Funktion
2. In der Funktion dann " Taster==HIGH und Zustand==1" > Setze Zustand wieder auf "0" ist auch gültig.

Halte ich den Taster also dauerhaft fest, geht es immer hin und her. Das loslassen des Tasters vor dem Wechsel
des Zustandes ist gefragt. Wobei "Taster==LOW" nicht geht, da das im Normalzustand ja immer zutrifft.
Ich versuche Eure Vorschläge mal zu verstehen und in einem Test umzusetzen.

HaWe
16.02.2019, 14:15
Oha, da habt ihr Opa aber eine Denksportaufgabe gegeben. Beschäftige ich mich gern mit.
Ich will ja auch lernen, meinen Code besser zu gestalten, und nicht , wie zur Zeit noch, alles mit
Wenn, dann zu verarbeiten.
Aber klar ist schon, dass es hier nicht um das schalten von 1 auf 0 und zurück allein geht.
Es ging um die Tatsache, dass die Bedingung Taster ==HIGH und der Zustand der Variable
beim hin- und herschalten immer gültig sind!

Also
1. "Taster==HIGH und Zustand==0" > Setze Zustand auf "1". Ist gültig und ich fliege aus der Loop in die Funktion
2. In der Funktion dann " Taster==HIGH und Zustand==1" > Setze Zustand wieder auf "0" ist auch gültig.

Halte ich den Taster also dauerhaft fest, geht es immer hin und her. Das loslassen des Tasters vor dem Wechsel
des Zustandes ist gefragt. Wobei "Taster==LOW" nicht geht, da das im Normalzustand ja immer zutrifft.
Ich versuche Eure Vorschläge mal zu verstehen und in einem Test umzusetzen.

daher empfahl ich oben meine ButtonClass.

021aet04
16.02.2019, 22:09
Hast du den Pseudocode aus meinem Post (#24) verstanden und weißt wie er funktioniert?

Du benötigst 2 Flankenauswertungen, eine zum Einsteigen und eine zum Verlassen der Funktion:


wenn ((Taste == 1) && (Taste_alt == 0)) x=1;
Taste_alt = Taste;

while (x == 1)
{
wenn ((Taste == 1) && (Taste_alt == 0)) x=0;
Taste_alt = Taste;
...
...
...
}


Wenn die Funktion so ist, invertierst du ja "x" einfach, so wie ich es oben geschrieben habe.

MfG Hannes

Wolle62
18.02.2019, 13:49
Hast du den Pseudocode aus meinem Post (#24) verstanden und weißt wie er funktioniert?

Der Code funktioniert auch genau wie gewünscht! Danke für die Hilfe.

So ganz genau verstehe ich es noch nicht.

wenn ((Taste == 1) && (Taste_alt == 0)) x=1; das ist noch klar:
Wenn "Taste" gedrückt und "Taste_alt" Variable gleich 0, dann setze "x" auf 1.



Taste_alt = Taste;
Diese Zuweisung ist dann aber nicht von der "if" Bedingung abhängig?
Somit kommt man nach dem ersten Click mit "taste_alt==1" in der "while" an.
Da die "if" hier nun nicht gültig ist, da "Taste_alt" ja nun "1" ist,bleibt man in der "while" und "Taste_alt" wird erst beim loslassen wieder auf "0" gesetzt?
Nach dem drücken in der "while" wird "Taste_alt!" wieder "1". Dadurch ist dann die "if" in der "loop" ungültig.
Auch hier wird "Taste_alt" erst nach dem loslassen des Tasters wieder auf "0" gesetzt??

Habe ich das so richtig verstanden? Der Code, wie im Post 24 läuft nur wenn man nicht in einer "while" feststeckt, oder?
Dann wird "x" bei jedem erneuten Tastendruck invertiert?

Ich denke noch nicht wie mein Arduino es kann. Sehr nett, dass man hier Hilfe von Profis hat.
So lernt man auch etwas richtig.
Danke!!

021aet04
19.02.2019, 16:18
Die Zeile "Taste_alt = Taste" muss immer sein, weil du so immer den Zustand vom letzten Zyklus hast.

Die "IF"-Anweisung ist nur war (und wird deswegen ausgeführt) wenn die Taste im letzten Zyklus nicht gesetzt war und du jetzt im aktuellen Zyklus die Taste drückst.
Der Ablauf:
- Taste nicht gedrückt und im letzten Durchlauf war sie auch nicht gedrückt
If Abfrage wird nicht ausgeführt, weil die Taste = 0
Diesen Zustand musst du zwischenspeichern (Taste_alt=Taste)

- Jetzt drückst du die Taste (Taste = 1) und der letzte Zyklus war 0
If Abfrage wird ausgeführt
Diesen Zustand musst du wieder zwischenspeichern. Weil die Taste jetzt 1 ist, wird auch Taste_alt 1

- Wenn du auf der Taste bleibst ist der Zustand 1, der vorhergehende Zustand war aber auch 1
If Abfrage wird nicht ausgeführt, weil Taste_alt = 1
Diesen Zustand musst du wieder zwischenspeichern. Weil die Taste noch immer 1 ist, bleibt auch Taste_alt 1

- Wenn du von der Taste heruntergehst wird Taste = 0
If Abfrage wird nicht ausgeführt, weil Taste 0 ist und Taste_alt 1
Diesen Zustand musst du wieder zwischenspeichern. Weil die Taste jetzt 0 ist, wird auch Taste_alt 0 (jetzt ist der gleiche Zustand wie oben => Taste = Taste_alt = 0)


Die Zuweisung "Taste_alt = Taste" musst du in jedem Zyklus machen, unabhängig ob die If Schleife ausgeführt wird oder nicht.


Die Zuweisung "Taste_alt = Taste" zwischen der Flankenerkennung und dem while muss ebenfalls dort sein, da es sonst nicht funktionieren würde. Wenn du die Zeile entfernen würdest (bzw nach der While-Schleife), bliebe Taste_alt = 0 und würde die While Schleife sofort wieder beenden (weil die Bedingung erfüllt ist).

MfG Hannes

Wolle62
19.02.2019, 17:24
Ja , verstanden!
Ist, wenn mans weiß, ganz einfach und logisch. Genau über solche Dinge bin ich froh.
Während meiner Übungen habe ich soviel dazu gelernt, dass ich immer geneigt bin
nochmal ganz von vorn anzufangen, da ich inzwischen viele Probleme ganz anders lösen würde.

Trotz allem bin ich bei Sketchen von Profis nur teilweise in der Lage alles richtig zu verstehen.
Das braucht noch Zeit.
Vielen Dank für Eure Bemühungen.

021aet04
19.02.2019, 20:23
Ist, wenn mans weiß, ganz einfach und logisch. Genau über solche Dinge bin ich froh.
Während meiner Übungen habe ich soviel dazu gelernt, dass ich immer geneigt bin
nochmal ganz von vorn anzufangen, da ich inzwischen viele Probleme ganz anders lösen würde.
Das ist normal. Wenn man nach einer Zeit sich seine Programme anschaut, würde man das Programm immer etwas anders machen. Ich mache aktuell einen Kurs zur S7-Programmierung (SPS-Steuerung von Siemens) und der Ausbildner hat genau das gleiche gesagt.


Trotz allem bin ich bei Sketchen von Profis nur teilweise in der Lage alles richtig zu verstehen.
Das braucht noch Zeit.
Fremden Code zu verstehen ist immer schwer, weil jeder seinen eigenen Stil hat und jeder etwas anders seine Programme schreibt.


Vielen Dank für Eure Bemühungen.
Gerne, dafür ist das Forum da.

MfG Hannes

HaWe
19.02.2019, 20:50
Trotz allem bin ich bei Sketchen von Profis nur teilweise in der Lage alles richtig zu verstehen.
Das braucht noch Zeit.

gerade C ist dafür bekannt, dass es dazu verleitet, völlig unverständlichen Code zu schreiben. Manche machen komplette Wettbewerbe daraus.
https://en.wikipedia.org/wiki/International_Obfuscated_C_Code_Contest
was für den nächsten Valentins Tag:


#include<stdio.h>
main()
{
int i,l,u;

char *ilu="STILL THINKING HOW I DID IT?\
!r/g.g#i.g/g!j/g.g#i.g/g+g!q0g!mag/h(g!m]g+g!p<g!h`i!l`g!k`g!h\
<g+g!p}g!i}g!i}g!h0g!g]g}g!g}h`g!h}g+g!p<g!h`g}g`g!h}g`h]g`g0g}\
g0g!g}g`g!h<g+g!q]g!n`g!o0g+g!r]g`h!g]g`g0h!g]g}g!g}g!j0g+g!n`g!\
i0g!h}g!g}g!g]g`g0g]g`g0g!h0g(g+g!m}g!g]g!h]g0g`g0g]g!n0g(g+g!m]g\
`g]g}g!g0g!g`h!gag]g!j0g(g+g!p]g0g`g0g`h]g!hag]g0g(g!k/g.h>g(g0g\
g]g+g!i`j-g`h0g`h-g`k-g`l*g0g!i0g|gg~i+g!i.g-g.k-g.h]g.h-g.k-\
g.i-g]g(g.g(g!g|hg~h+g!q`h0g]g`g!r(g.h>g/g]g~g0g+g!p0g`g0g!g\
}g]h+g!u]g0g";

for(i=28;l=ilu[i++];)
for(u=(*(ilu+++i)-((1<<6)+(1<<5)+(1<<2)+(1<<1)));u--;)
putchar(!((l-11)^(1<<5))?l-1-(1<<5):l-1);

}



https://ideone.com/I0HTec

Wolle62
21.02.2019, 17:51
Nur so aus Neugier, ist so ein Code nun effizienter als ein langer verschachtelter "wenn>dann" Code?
Was macht der Compiler damit? (Nur ganz grob, Details verstehe ich sowieso noch nicht..)

HaWe
21.02.2019, 19:52
Nur so aus Neugier, ist so ein Code nun effizienter als ein langer verschachtelter "wenn>dann" Code?
Was macht der Compiler damit? (Nur ganz grob, Details verstehe ich sowieso noch nicht..)

der obfuscated code oben ist sicher nicht "effektiver".
Es gibt bei C aber ein paar Tricks, den Code tatsächlich schneller zu machen:

statt x=x+1
x++

statt x=x+5
x+=5


statt
{
x=2*y;
y++;
}
x=2*y++;

Die Unterschiede sind aber minimal, das meiste macht der C Compiler selbstständig über seine eingebaute Code-Optimierung. Damit man hinterher seinen Code noch selber versteht und Fehler vermeidet, macht es mehr Sinn, ihn intuitiv verständlich zu schreiben.

Sisor
24.02.2019, 17:32
Es gibt bei C aber ein paar Tricks, den Code tatsächlich schneller zu machen:

statt x=x+1
x++

statt x=x+5
x+=5


statt
{
x=2*y;
y++;
}
x=2*y++;

Quelle? Belege?

Hier der Grund meiner Skepsis deine Behauptung betreffend (https://gcc.godbolt.org/z/0u6M7t).

HaWe
24.02.2019, 17:59
Quelle? Belege?

das war zugegebenermaßen aus der Erinnerung aus einem Skript für einen C-Programmierkurs zitiert und bezog sich auf Compiler ohne Optimierung.

IIRC, lautete die Erklärung:

x = x + 1;
würde 3 Instruktionen erfordern, um x zur Addierunit zu schieben, 1 zu addieren und dann wieder x zurückzuschreiben, wähend

x++
direkt im Register, in dem es steht, 1 inkrementiert.

(restliche Beispiele analog)

Falls diese Aussage nicht (mehr) stimmt, lasse ich mich ntl gern korrigieren.

Sisor
24.02.2019, 18:15
Falls diese Aussage nicht (mehr) stimmt, lasse ich mich ntl gern korrigieren.
Ok. Die Arduino-Sketche werden mit dem gcc-Flag -Os für optimize size also Programmgrößenoptimierung kompiliert.
Ohne Optimierung hast du zwar Recht, das deine Beispiele unterschiedlichen Code generieren können.

Dies gilt aber ausdrücklich nicht für Arduino.

HaWe
24.02.2019, 19:32
danke für die Klarstellung!

Sisor
24.02.2019, 20:09
Nur so aus Neugier, ist so ein Code nun effizienter als ein langer verschachtelter "wenn>dann" Code?
Was macht der Compiler damit? (Nur ganz grob, Details verstehe ich sowieso noch nicht..)

Grob gesagt geschehen drei Schritte im Compiler:
1. Frontend, Sprachspezifisch (bei Arduino C++): Code wird gelesen (geparst) und zu einem Syntaxbaum (A-S-T: abstact syntax tree) verarbeitet.
2. Middleend: Sprachunabhängig: A-S-T wird analysiert und optimiert.
3. Backend: Ausführbarer Code (z.B. universeller Bytecode oder CPU-Spezifischer Maschinencode) wird erstellt.

Wolle62
01.03.2019, 17:52
Wow, Danke für die Erleuchtung!
Diese Website "Compiler Explorer" ist mir völlig neu.
Da könnte ich meinen "Umstandscode" also mal reinhauen und schauen, was bei "komprimierte Funktion" steht.
Aber letztendlich kommt der gleiche Maschinencode bei raus? Gut zu wissen.
Ich dachte, mein Anfängercode ist langsamer und größer als einer vom Profi.
Das muss also nicht immer der Fall sein?

HaWe
01.03.2019, 18:05
Aber letztendlich kommt der gleiche Maschinencode bei raus? Gut zu wissen.
Ich dachte, mein Anfängercode ist langsamer und größer als einer vom Profi.
Das muss also nicht immer der Fall sein?
auszuschließen ist nichts, mögich ist alles, wenn auch nicht immer wahrscheinlich ;)