mir lässt das noch keine Ruhe.
(ich weiß gar nicht ob meine Hilfe hier erwünscht ist)
Und ich kann nicht testen, da ich einen solchen Baustein nicht habe.
Deswegen müssen meine Vorschläge recht abstrakt bleiben!
Eins deiner Probleme scheint mir zu sein, dass du mehrere Schritte auf einmal gehen möchtest.
Genannt sei:
Der Unterschied zwischen (Daten)Modell und (Daten)Struktur.
Du möchtest, bzw. stolperst, über die Struktur.
Dabei habe ich noch nicht mal verstanden, was du modellieren möchtest.
Vergleichbares Beispiel:
Ich bemerke, dass ich in einem Programm eine Liste brauche.
Die Liste ist ein abstraktes Datenmodell.
Um mit Listen im Programm umgehen zu können, muss ich das in Strukturen gießen.
z.B. in die Datenstruktur: Die Verkettete Liste.
Das ist der Weg, von Modell zur Struktur.
Ich wäre dir also sehr dankbar, wenn du erst das Modell beschreiben würdest, bevor wir im Detail auf die konkreten Strukturen eingehen.
Was willst du wirklich erreichen?
z.B. Punktmatrix oder 7 Segment Display.
Die Modelle unterscheiden sich grundlegend.
Natürlich kann man beides auch getrennt von einander abhandeln, und das dann hinter einer gemeinsamen Fassade verbergen.
Wie bei einem Ärztehaus, wo sich Zahnarzt, Orthopäde und Augenarzt ein Treppenhaus teilen. (und im Untergeschoss ist noch eine Apotheke)
In Sachen SPI...
Du nutzt SoftSPI mit einer selbstgebauten shift_out() (ca50kHz), obwohl die meisten AVR Hardware SPI haben. und damit auch an die möglichen 10MHz ran kommen. Auch gibt es ja auch noch auf allen(?) Arduinos shiftOut().
Hier könnte eine "Adapter" Schnittstelle, zu den verschiedenen Treibern, Sinn machen.
Ins besondere auch unter dem Blickwinkel, dass mehrere MAX7221 verkettet vorliegen können.
struct t_setup
{
const byte CLK = 24;
const byte CS = 25;
const byte MOSI = 26;
const byte SEGDP = 128; // Bit.7 - Datenblatt Tabelle 6
const byte SEGA = 64;
const byte SEGB = 32;
const byte SEGC = 16;
const byte SEGD = 8;
const byte SEGE = 4;
const byte SEGF = 2;
const byte SEGG = 1; // Bit.0
const byte DIGIT0 = 0x01; // Datenblatt kompatible Schreibweise
const byte DIGIT1 = 0x02;
const byte DIGIT2 = 0x03;
const byte DIGIT3 = 0x04;
....
Hier scheinen mir Dinge zusammengefasst zu sein, welche nicht zusammen gehören.
Erinnert mich an den Begriff "Gottklasse" oder "Big hairy object".
(ein beliebter OOP beginner Fehler)
Oft ist an solchen Punkten "sinnvoll aufteilen und delegieren" der bessere Weg.
// Datenblatt kompatible Schreibweise
Das könnte der falsche Ansatz sein.
Im Datenblatt ok, aber hier könnte es den Blick auf die wesentlichen/notwendigen Abstraktionen versperren.
const t_led LED[] = {
{MATRIX.DIGIT0, MATRIX.SEGA}, // LED 1
{MATRIX.DIGIT1, MATRIX.SEGA},
{MATRIX.DIGIT2, MATRIX.SEGA},
{MATRIX.DIGIT3, MATRIX.SEGA},
{MATRIX.DIGIT0, MATRIX.SEGB}, // LED 5
{MATRIX.DIGIT1, MATRIX.SEGB},
{MATRIX.DIGIT2, MATRIX.SEGB},
{MATRIX.DIGIT3, MATRIX.SEGB},
{MATRIX.DIGIT0, MATRIX.SEGC},
{MATRIX.DIGIT1, MATRIX.SEGC}, // LED 10
{MATRIX.DIGIT2, MATRIX.SEGC},
{MATRIX.DIGIT3, MATRIX.SEGC},
{MATRIX.DIGIT0, MATRIX.SEGD},
{MATRIX.DIGIT1, MATRIX.SEGD},
{MATRIX.DIGIT2, MATRIX.SEGD}, // LED 15
{MATRIX.DIGIT3, MATRIX.SEGD},
{MATRIX.DIGIT0, MATRIX.SEGE},
{MATRIX.DIGIT1, MATRIX.SEGE},
{MATRIX.DIGIT2, MATRIX.SEGE},
{MATRIX.DIGIT3, MATRIX.SEGE}, // LED 20
{MATRIX.DIGIT0, MATRIX.SEGF},
{MATRIX.DIGIT1, MATRIX.SEGF},
{MATRIX.DIGIT2, MATRIX.SEGF},
{MATRIX.DIGIT3, MATRIX.SEGF},
{MATRIX.DIGIT0, MATRIX.SEGG}, // LED 25
{MATRIX.DIGIT1, MATRIX.SEGG},
{MATRIX.DIGIT2, MATRIX.SEGG},
{MATRIX.DIGIT3, MATRIX.SEGG},
{MATRIX.DIGIT0, MATRIX.SEGDP},
{MATRIX.DIGIT1, MATRIX.SEGDP}, // LED 30
{MATRIX.DIGIT2, MATRIX.SEGDP} // LED 31
};
Ein Datenfeld, welches sich eigentlich auch über Zugriffsmethoden eliminieren lassen würde, denke ich mal. Der Verzicht auf die Datenblattkonformen Bezeichner würde das ermöglichen.
switch (LED[index].digit) {
case MATRIX.DIGIT0: // wenn bestimmtes Digit aktiv, dann
digit0_seg = digit0_seg | LED[index].segment; // Segmentwert vom Digit aufaddieren
MAX7221.shift_out(LED[index].digit, digit0_seg); // und ausgeben
break;
case MATRIX.DIGIT1:
digit1_seg = digit1_seg | LED[index].segment;
MAX7221.shift_out(LED[index].digit, digit1_seg);
break;
case MATRIX.DIGIT2:
digit2_seg = digit2_seg | LED[index].segment;
MAX7221.shift_out(LED[index].digit, digit2_seg);
break;
case MATRIX.DIGIT3:
digit3_seg = digit3_seg | LED[index].segment;
MAX7221.shift_out(LED[index].digit, digit3_seg);
break;
default:
Serial.print(F("case error ")); Serial.print('\t');
Serial.println(LED[index].digit);
break;
}
Auch hier scheint mir die Ähnlichkeiten der Fälle (Codeduplikate) so groß, dass eine Reduzierung (einheitliche Behandlung) möglich ist. Natürlich nur unter Verzicht der durchnummerierten Variablen/Konstanten
Da ich um das aufsplitten, was mir nachwievor nicht gefällt, wohl nicht drum herum komme,
Gerade solche Probleme lassen sich (vermutlich) leichter beheben, wenn man eine Schicht tiefer anfängt, die Dinge zu modellieren.
Andererseits ist aufsplitten nur dann böse, wenn es mit mit dem erzeugen von Duplikaten einhergeht.