Sketch optimieren (DE)

Hallo zusammen,

mögt Ihr mir bitte Tipps geben, wie ich meine Sketch optimieren/verkleinern kann?
Der Sketch soll lediglich einen Zähler von 0-99 darstellen und auf 2x 7Segment Anzeigen via BCD ausgeben. Vor beiden 7Seg-Anzeigen befinden sich Decoder-IC's von BCD auf a-g.

Demnächst versuche ich noch einen einstellbaren Timer ohne delay einzubauen (Eieruhr).

Besten Dank
Max

neue Version

//BCD_7SEG Zaehler v1.4             (Max B. 26.09.2017)
//Zaehler +/-/reset, Ausgabe auf BCD 7-Segment 2 Digits
//min.=0; max.=99
//wenn +/- >=1s = +/- 1Hz Takt

//(UnoArduSim) #include <pgmspace.h>

//Ein-/Ausgaenge (const uint8_t / byte)
const uint8_t S0_CU = 2; bool plus = 0;
const uint8_t S1_CD = 3; bool minus = 0;
const uint8_t S2_CR = 4; bool reset = 0;
//------------BCD_Einer------------------BCD_Zehner----
const uint8_t BCD_1_1 = 5; const uint8_t BCD_1_10 = 9;
const uint8_t BCD_2_1 = 6; const uint8_t BCD_2_10 = 10;
const uint8_t BCD_4_1 = 7; const uint8_t BCD_4_10 = 11;
const uint8_t BCD_8_1 = 8; const uint8_t BCD_8_10 = 12;
const uint8_t alarm = 13;
//Variablen
bool s_x = 0; //Merker fuer Eingabe
bool m_reset = 0; //Merker fuer Reset
byte zaehler_dec = 0; //DEC Zaehler 0-100
word zaehler_bcd = 0; //BCD Zaehler 0-160(byte)
const uint16_t M_1s = 1000; //1 Sekunde
unsigned long j_ms = 0; //Zeitpunkt "jetzt"
unsigned long l_ms = 0; //Zeitpunkt "zuletzt"
unsigned long alarm_ms = 0; //Zeitpunkt "alarm"


void setup() //(OB100)
{//Start setup
	Serial.begin(19200);
	pinMode(S0_CU, INPUT); pinMode(S1_CD, INPUT); pinMode(S2_CR, INPUT);
	pinMode(BCD_1_1, OUTPUT); pinMode(BCD_1_10, OUTPUT);
	pinMode(BCD_2_1, OUTPUT); pinMode(BCD_2_10, OUTPUT);
	pinMode(BCD_4_1, OUTPUT); pinMode(BCD_4_10, OUTPUT);
	pinMode(BCD_8_1, OUTPUT); pinMode(BCD_8_10, OUTPUT);
	pinMode(alarm, OUTPUT);
	
	while (!Serial) {/*NICHTS*/} //auf serielle Uebertragung warten
	Serial.println(F("\nEingabe Pin: 2=plus, 3=minus, 4=reset"));
	Serial.println(F("Ausgabe Pin: BCD_1=5-8, BCD_10=9-12"));
	Serial.println(F("\nBetriebsbereit..."));
	DEC_zu_BCD();
}//Ende setup


void loop() //(OB1)
{//Start loop
	//Eingaenge einlesen (erspart Platz)
	plus  = digitalRead(S0_CU);
	minus = digitalRead(S1_CD);
	reset = digitalRead(S2_CR);
	
	j_ms = millis();
	//Freigabe fuer weitere Eingabe, sobald keine Taste gedrueckt ist.
	if (plus == 0 && minus == 0 && reset == 0)
	{
		s_x = 0;
		l_ms = millis(); //1Hz zaehler blockieren
	}
	//Taster reset
	if (reset == 1 && s_x == 0 && m_reset == 0)
	{
		Serial.println(F("\nReset..."));
		zaehler_dec = 0;
		zaehler_bcd = 0;
		s_x = 1;
		m_reset = 1;
		Bit_lesen();
	}
	//Taster plus
	if (plus == 1 && minus == 0 && reset == 0 && s_x == 0 && zaehler_dec < 99)
	{
		zaehler_dec += 1;
		s_x = 1;
		m_reset = 0;
		DEC_zu_BCD();
		Bit_lesen();
	}
	//Taster minus
	if (minus == 1 && plus == 0 && reset == 0 && s_x == 0 && zaehler_dec > 0)
	{
		zaehler_dec -= 1;
		s_x = 1;
		m_reset = 0;
		DEC_zu_BCD();
		Bit_lesen();
	}
	//zaehlen mit 1Hz
	if ((j_ms - l_ms >= M_1s) && ((plus == 1 && zaehler_dec < 99) || (minus == 1 && zaehler_dec > 0)))
	{
		s_x = 0; //weiteres zaehlen freigeben
		l_ms = millis();
	}
	//Alarm ab 99 oder ab 0 wenn minus aktiv
	if ((zaehler_dec >= 99 || (zaehler_dec < 1 && minus == 1)) && (j_ms - alarm_ms >= M_1s))
	{
		digitalWrite(alarm, digitalRead(alarm) ^ 1);
		alarm_ms = millis();
	}
	else if ((zaehler_dec < 99 && minus == 0) || (zaehler_dec > 0 && minus == 1))
	{
		digitalWrite(alarm, LOW);
	}
	return;
}//Ende loop

void DEC_zu_BCD() //(FC1)
{//Start DEC_zu_BCD()
	//|-BCD-----=--------DEC-------HEX----+-------MOD(10)-|
	zaehler_bcd = (zaehler_dec / 10 * 16) + (zaehler_dec % 10);
	Serial.print(F("Zaehlerstand = "));
	Serial.print(zaehler_dec);
	Serial.print(F(" | BCD = "));
	Serial.println(zaehler_bcd, BIN);
}//Ende DEC_zu_BCD()

void Bit_lesen() //(FC2)
{//Start Bit_lesen()
	//erste 4Bit "Einer"
	digitalWrite(BCD_1_1, bitRead(zaehler_bcd, 0));
	digitalWrite(BCD_2_1, bitRead(zaehler_bcd, 1));
	digitalWrite(BCD_4_1, bitRead(zaehler_bcd, 2));
	digitalWrite(BCD_8_1, bitRead(zaehler_bcd, 3));
	//letzte 4Bit "Zehner"
	digitalWrite(BCD_1_10, bitRead(zaehler_bcd, 4));
	digitalWrite(BCD_2_10, bitRead(zaehler_bcd, 5));
	digitalWrite(BCD_4_10, bitRead(zaehler_bcd, 6));
	digitalWrite(BCD_8_10, bitRead(zaehler_bcd, 7));
}//Ende Bit_lesen()

https://forum.arduino.cc/index.php?board=31.0

Max_Beginner:
mögt Ihr mir bitte Tipps geben, wie ich meine Sketch optimieren/verkleinern kann?

Ich finde, dass Du die Klammern anders setzen solltest. Einheitlich entweder als

bla {
...}

oder

bla
{
...
}

Ich finde die zweite Variante lesbarer - Gewohnheit. Ansonsten halt das IMO Übliche: Gliedern (Leerzeilen), Zeilen nicht länger als 80 Zeichen, passend einrücken, Funktionen maximal so lang, dass man sie vollständig auf einen (lesbaren :slight_smile: Blick sehen kann. Für „Schönheit“ will ich ja schon seit langem ein Länzchen brechen. Anlass gibt es immer wieder.

Gruß

Gregor

gregorss:
Ich finde, dass Du die Klammern anders setzen solltest. Einheitlich entweder als

bla {

...}




oder



bla
{
...
}




Ich finde die zweite Variante lesbarer - Gewohnheit. Ansonsten halt das IMO Übliche: Gliedern (Leerzeilen), Zeilen nicht länger als 80 Zeichen, passend einrücken, Funktionen maximal so lang, dass man sie vollständig auf einen (lesbaren :-) Blick sehen kann. Für „Schönheit“ will ich ja schon seit langem ein Länzchen brechen. Anlass gibt es immer wieder.

Gruß

Gregor

Danke dir schonmal! :slight_smile:

Gibt es denn noch Tipps, welche die Funktion verkleinern könnten?

Besten Dank
Max

Ich finde die Funktionen kurz und übersichtlich genug. Ich bevorzuge es auch, so zu programmieren, dass es gut zu verstehen ist, wenn möglich sogar von einem Programmier-Unkundigen. Wenn eine Funktion dadurch ein paar Zeilen mehr hat, ist das okay.

Ich würde wahrscheinlich die Variablennamen noch kürzer machen. Z. B.

BlaBlubb1-3 // uh, shit, das geht wegen dem Minuszeichen nicht.

statt

Blablubb_1_3

Aber auch das ist Geschmackssache. Dann würde ich vor die if()s jeweils eine Leerzeile machen, damit das ein bisschen deutlicher gegliedert ist.

Als ich mal einen Freund, der beruflich programmiert, gefragt habe, was für Stilvorgaben er beachtet, sagte er, dass das zunächst davon abhängt, was der Auftraggeber vorgibt. Dann folgen gute Lesbarkeit und sinnvolle, nicht zu viele Kommentare. Schon dass Du nach solchen Sachen fragst, verschafft Dir 100 Punkte. Fast eine Waschmaschine :slight_smile:

Andererseits kann man's auch übertreiben. Ich finde, dass Lesbarkeit und Verständlichkeit an oberster Stelle stehen sollten. Das kann man durch viele Sachen erreichen. Merk' Dir einfach, was Du an fremdem Code gut oder schlecht findest. Dann machst Du's irgendwann automatisch so, dass es „schön“ ist.

Gruß

Gregor

:slight_smile:
Dann werde ich es erstmal dabei belassen und mich mal mit der "Eieruhr" befassen.

Mir kam noch in den Sinn, die eine Rechnung für DEC zu BCD, welche ja 2mal aufgerufen wird in eine definierte Funktion zu packen. Hast du da eine Idee wie das umsetz bar ist?

Hab da mal etwas mit void funktion...(int blub, int bla) {} gesehen. Da kann man dann mit return blub... die Werte an die Stelle in der man die Funktion aufruft übertragen?

Max_Beginner:
Mir kam noch in den Sinn, die eine Rechnung für DEC zu BCD, welche ja 2mal aufgerufen wird in eine definierte Funktion zu packen. Hast du da eine Idee wie das umsetz bar ist?

Ich habe jetzt nicht alles intensiv gelesen, weils zuerst ja nur um Schönheit ging (zumindest habe ich das so verstanden). Was derlei Optimierungen angeht, sind andere fitter als ich. Ich hoffe, so jemand liest gerade mit ...

Gruß

Gregor

gregorss:
Ich würde wahrscheinlich die Variablennamen noch kürzer machen. Z. B.

BlaBlubb1-3 // uh, shit, das geht wegen dem Minuszeichen nicht.

statt

Blablubb_1_3

Es gibt zwei Konventionen. CamelCase und Snake_case

Bei CamelCase fangen Funktionen mit Kleinbuchstaben an (Großbuchstaben am Anfang sind für Klassen) und jedes weitere Wort fängt mit einem Großbuchstaben an.

Bei Snake_case fängt jedes Wort mit einem Kleinbuchstaben an und die Wörter sind durch Unterstriche getrennt.

Im Arduino-Bereich ist zwar CamelCase üblich, aber Snake_case ist oft einfacher lesbar da die Wörter deutlicher getrennt sind.

Max_Beginner:
Hab da mal etwas mit void funktion...(int blub, int bla) {} gesehen. Da kann man dann mit return blub... die Werte an die Stelle in der man die Funktion aufruft übertragen?

Der erste Datentyp vorne ist der Rückgabewert. void bedeutet einfach "nichts". Wenn die Funktion etwas zurückgeben soll, dann musst du das bei der Signatur angeben

Serenifly:
Es gibt zwei Konventionen. CamelCase und Snake_case

Wer hat sich denn diese Namen ausgedacht? :slight_smile:

Danke Dir!
Dann kann ich mir morgen ein bischen Snake_case aneignen. Finde es bedeutend einfacher zu lesen und kenne diese Trennung (_) von den großen SPS`en.

Gibt es eine feste Regel, ab wann man einen Standart Befehl/Ablauf in eine eigene Klasse zusammen fügt?

Ich danke Euch!
Max

Max_Beginner:
Wer hat sich denn diese Namen ausgedacht? :slight_smile:

Gibt es eine feste Regel, ab wann man einen Standart Befehl/Ablauf in eine eigene Klasse zusammen fügt?

Nein. Das hängt vom Aufbau ab. Klassen bieten sich dann an wenn man eine Kombinationen aus Variablen und Funktionen hat die man mehrmals verwendet.

:confused:
Muss ich nach dem Aufruf einer Klasse mit goto/return.. wieder an die Stelle zurück springen oder
passiert das automatisch? Konnte es leider noch nicht testen.

Noch eine Frage: Wenn ich den Sketch in UnoArduSim laufenlassen, erhalte ich ab dem Zählerstand 80 eine 4Byte lange Zahl. Der Erhaltene Wert liegt aber bei max.160. Dort sollte ja ein Byte(0-255) ausreichen.. Liegt hier ein Fehler seitens dem Emulator vor?

Neuerungen: Umwandlung von Dezimal zu BCD in eigener Klasse(mit serieller Ausgabe)

if (digitalRead(s_plus) == HIGH && s_x == 0) //Taster plus
 {
   zaehler_dec++;
   s_x = 1;
   DEC_zu_BCD(); //++++NEU++++
   Bit_lesen();
 }
if (digitalRead(s_minus) == HIGH && s_x == 0 && zaehler_dec > 0) //Taster minus
 {
   zaehler_dec -= 1;
   s_x = 1;
   DEC_zu_BCD(); //++++NEU++++
   Bit_lesen();
 }
}//loop Ende

...

void DEC_zu_BCD() //(FC2)
{
   //|-BCD-----=--------DEC-------HEX----+-------MOD(10)-|
   zaehler_bcd = (zaehler_dec / 10 * 16) + (zaehler_dec % 10);
   Serial.print(F("Zaehlerstand = "));
   Serial.print(zaehler_dec);
   Serial.print(F(" | BCD = "));
   Serial.println(zaehler_bcd, BIN);
}

vollständiger Code:

//BCD_7SEG Zaehler v1.3             (Max B. 20.09.2017)
//Zaehler +/-/reset, Ausgabe auf BCD 7-Segment 2 Digits
//min.=0; max.=99
/*(serielle Ausgabe)
  Eingabe Pin: 2=plus, 3=minus, 4=reset
  Ausgabe Pin: BCD_1=5-8, BCD_10=9-12
  Betriebsbereit...
  ...
  Zaehlerstand = 11 | BCD = 10001
  ...
*/
//(UnoArduSim) #include <pgmspace.h>

//Ein-/Ausgaenge (const uint8_t / byte) >>>Datentypen sinnvoll?<<<
const uint8_t s_plus = 2;
const uint8_t s_minus = 3;
const uint8_t s_reset = 4;
/*------------BCD_Einer------------------BCD_Zehner-------*/
const uint8_t BCD_1_1 = 5; const uint8_t BCD_1_10 = 9;
const uint8_t BCD_2_1 = 6; const uint8_t BCD_2_10 = 10;
const uint8_t BCD_4_1 = 7; const uint8_t BCD_4_10 = 11;
const uint8_t BCD_8_1 = 8; const uint8_t BCD_8_10 = 12;
//const uint8_t FREI = 13;
//Variablen
bool s_x = 0; //Merker fuer Eingabe
byte zaehler_dec = 0; //DEC Zaehler 0-100
byte zaehler_bcd = 0; //BCD Zaehler 0-160


void setup() //(OB100)
{//Start setup
	Serial.begin(19200);
	pinMode(s_plus, INPUT); pinMode(s_minus, INPUT); pinMode(s_reset, INPUT);
	pinMode(BCD_1_1, OUTPUT); pinMode(BCD_1_10, OUTPUT);
	pinMode(BCD_2_1, OUTPUT); pinMode(BCD_2_10, OUTPUT);
	pinMode(BCD_4_1, OUTPUT); pinMode(BCD_4_10, OUTPUT);
	pinMode(BCD_8_1, OUTPUT); pinMode(BCD_8_10, OUTPUT);
	
	while (!Serial) {/*NICHTS*/} //auf serielle Uebertragung warten
	Serial.println(F("\nEingabe Pin: 2=plus, 3=minus, 4=reset"));
	Serial.println(F("Ausgabe Pin: BCD_1=5-8, BCD_10=9-12"));
	Serial.println(F("\nBetriebsbereit..."));
}//Ende setup


void loop() //(OB1)
{//Start loop
     //Freigabe fuer weitere Eingabe, sobald keine Taste gedrueckt ist.
 if (digitalRead(s_plus) == LOW && digitalRead(s_minus) == LOW && digitalRead(s_reset) == LOW) 
     //verkuerzen auf S_p..+S_m..+..==0?
   {
	s_x = 0;
	return;
   }
 if ((digitalRead(s_reset) == HIGH && s_x == 0) || zaehler_dec > 99) //Taster reset
   {
	Serial.println(F("\nReset..."));
	zaehler_dec = 0;
	zaehler_bcd = 0;
	s_x = 1;
	Bit_lesen();
   }
 if (digitalRead(s_plus) == HIGH && s_x == 0) //Taster plus
   {
	zaehler_dec++;
	s_x = 1;
	DEC_zu_BCD();
	Bit_lesen();
   }
 if (digitalRead(s_minus) == HIGH && s_x == 0 && zaehler_dec > 0) //Taster minus
   {
	zaehler_dec -= 1;
	s_x = 1;
	DEC_zu_BCD();
	Bit_lesen();
   }
}//Ende loop


void Bit_lesen() //(FC1)
{//Start Bit_lesen()
    //erste 4Bit "Einer"
	digitalWrite(BCD_1_1, bitRead(zaehler_bcd, 0));
	digitalWrite(BCD_2_1, bitRead(zaehler_bcd, 1));
	digitalWrite(BCD_4_1, bitRead(zaehler_bcd, 2));
	digitalWrite(BCD_8_1, bitRead(zaehler_bcd, 3));
   //letzte 4Bit "Zehner"
	digitalWrite(BCD_1_10, bitRead(zaehler_bcd, 4));
	digitalWrite(BCD_2_10, bitRead(zaehler_bcd, 5));
	digitalWrite(BCD_4_10, bitRead(zaehler_bcd, 6));
	digitalWrite(BCD_8_10, bitRead(zaehler_bcd, 7));
}//Ende Bit_lesen()


void DEC_zu_BCD() //(FC2)
{//Start DEC_zu_BCD()
	//|-BCD-----=--------DEC-------HEX----+-------MOD(10)-|
	zaehler_bcd = (zaehler_dec / 10 * 16) + (zaehler_dec % 10);
	Serial.print(F("Zaehlerstand = "));
	Serial.print(zaehler_dec);
	Serial.print(F(" | BCD = "));
	Serial.println(zaehler_bcd, BIN);
}//Ende DEC_zu_BCD()

Max_Beginner:
Muss ich nach dem Aufruf einer Klasse mit goto/return.. wieder an die Stelle zurück springen oder
passiert das automatisch?
...
Neuerungen: Umwandlung von Dezimal zu BCD in eigener Klasse(mit serieller Ausgabe)

Du musst zwischen Funktionen, Klassen und Methoden unterscheiden. Die haben schon alle ihre genaue Bedeutung, und wenn Du das hier durcheinanderwirfst, kommt man auch beim Lesen etwas durcheinander :wink:

Was Du in deinem Sketch bisher nutzt, sind ganz normale 'C' Funktionen. Also das, was man in anderen Programmiersprachen auch als 'Unterprogramme' bezeichnet. Wenn Du so eine Funktion aufrufst (z.B. in loop() ) so werden die Befehle darin abgearbeitet, und am Ende springt der Prozessor automatisch wieder zurück und macht beim Befehl hinter dem Aufruf weiter. Da braucht's kein goto.
Ein 'return' muss man einsetzen, wenn die Funktion auch einen Wert zurückgeben soll ( also nicht 'void' ist ). Man kann es einsetzen, wenn man mitten aus der Funktion wieder zurückspringen will und nicht erst am Ende ( Was man aus Übersichtlichkeitsgründen aber nur in Ausnahmefällen tun sollte ).

Ein Klasse ist - sehr vereinfacht ausgedrückt - ein logisch zusammengehöriger Block von Funktionen und Variablen. Die Funktionen, die zu einer Klasse gehören, werden dann 'Methoden' genannt. Die Variablen innerhalb der Klasse sind üblicherweise auch nur in den Methoden bekannt. Greifst Du in Funktionen, die nicht zu dieser Klasse gehören darauf zu, bekommst Du einen Fehler. Man kann diese Variablen zwar auch für andere Funktionen bekannt machen, das widerspricht aber eigentlich der Idee der klassenorientierten Programmierung ( nennt sich 'Datenkapselung' ). Das Thema ist durchaus komplex, und hier nur seeehr grob beschrieben. Klassen gehören zur Programmierung in C++.

Ich denke Du solltest dich erstmal mit den normalen Funktionen befassen, und da auch mit Parameterübergaben und lokalen Variablen. Das ist dann schon eine gewisse kleine 'Vorstufe' für Programmierung mit Klassen.

P.S. implizit verwendest Du schon Klassen in deinem Sketch: 'Serial' ist z.B. so ein Klassenobjekt, und was hinter dem '.' steht sind die Methodennamen, die Du aufrufst.

MicroBahner:
Du musst zwischen Funktionen, Klassen und Methoden unterscheiden. Die haben schon alle ihre genaue Bedeutung, und wenn Du das hier durcheinanderwirfst, kommt man auch beim Lesen etwas durcheinander :wink:

Danke Dir vielmals! :slight_smile:

Dem war ich mir tatsächlich noch nicht im klaren.
Mal schauen, ob ich zum Feierabend ein wenig Zeit finde und mich einlesen kann, bezüglich objektorientierter Programmiernung.

In C brauchst Du goto nie (es gibt einige Außnahmen wi Goto sinnvoll ist, aber bei so komplizierten Programmen die weder ich noch Du je schreiben werden).
Grüße Uwe