[erledigt] Gibt es einen Container für unterschiedlich breite Integer?

Hallo allerseits!

Mein Oszi für arme pausiert gerade, weil ich mit einer anderen Bastelei beschäftigt bin. Ich überlege aber schon einmal, wie ich einige Teile der Software umsetze.

Ein Teil ist der Speicher für aufgezeichnete Samples. Da der Speicher nicht allzu groß sein kann, soll er wenigstens gut organisiert sein. Ich dachte an einen Container für Samples mit unterschiedlicher Breite (Bits). Da das OLED-Display nur 64 Pixel hoch ist, würden 6 Bit als maximale Breite je Messwert genügen. Wenn der Speicher für Samples 128 Byte groß wäre, sollte man durch geschickte Bitschubsereien 160 Samples à 6 Bit unterbringen können.

Hat schon mal jemand einen solchen Container programmiert? Also einen, der in einer vorgegebenen Anzahl von Bytes unterschiedlich breite Samples unterbringt? Selbst so einen Container zu programmieren könnte interessant sein, aber interessante Details hat das Oszi-Projekt auch so zu bieten.

Gruß

Gregor

Hi Gregor, das Thema ist interessant. Ich habe zwar recht wenig Ahnung und Erfahrung diesbezüglich, aber mein Ansatz wäre mal:

Bei den ersten 128 Werten die sechs bit entsprechend maskiert reinschreiben, so dass bit 7 und 8 nicht verändert werden.
Ab Wert 129 dann eben die 6 bits auf 3 Speicherstellen verteilen und da jeweils in 7 und 8 bitwriten. Als Speicherstelle dann (Speicherstelle-128)*3 für die ersten beiden bits, etc.

Das reinschreiben sollten nicht mehr als 30 Zeilen Code geben, so schätzungsweise nach meiner nicht vorhandenen Erfahrung

Übrigens ein sehr aussagekräftiger Thread Titel.

ElEspanol:
Übrigens ein sehr aussagekräftiger Thread Titel.

Ja. Als ich überlegt habe, was ich da schreibe, war ich ein bisschen müde :slight_smile: Das begünstigt Wortfindungsstörungen.

Gruß

Gregor

Vielleicht gefällt dir ja das Ergebnis meiner kleinen Fingerübung.
Relativ flexibel (16 oder weniger Bits für die Werte),
die Fehlerbehandlung (Index/Wert) kannst du ja bei Bedarf selbst hinzufügen.

class SamBuff {
  public:
    SamBuff(uint16_t bySize, byte biSize) {
      mBuf = (byte*)malloc(byteSize);
      bitSize = biSize;
      if (mBuf != NULL) {
        byteSize = bySize;
        memset(mBuf, 0, byteSize);
        bMask = 0;
        for (byte i = 0; i < bitSize; i++) {
          bMask <<= 1;
          bMask |= 1;
        }
      } else {
        byteSize = 0;
      }
    }
    ~SamBuff() {
      free(mBuf);
    }
    uint16_t get(uint16_t index) {
      uint16_t bitAddress = index * 8;
      uint16_t byteAddress = bitAddress / bitSize;
      byte bitOffset = bitAddress % bitSize;
      uint16_t value = *((uint16_t*)(mBuf + byteAddress));
      value >>= bitOffset;
      return value & bMask;
    }
    void set(uint16_t index, uint16_t newVal) {
      uint16_t bitAddress = index * 8;
      uint16_t byteAddress = bitAddress / bitSize;
      byte bitOffset = bitAddress % bitSize;
      uint16_t value = *((uint16_t*)(mBuf + byteAddress));
      value &= ~(bMask << bitOffset);
      value |= (newVal << bitOffset);
      *((uint16_t*)(mBuf + byteAddress))  = value;
    }
    uint16_t maxVal() {
      return bMask;
    }
  private:
    byte* mBuf;
    uint16_t byteSize;
    byte bitSize;
    uint16_t bMask;
};

SamBuff test(128, 6);

void setup() {
  Serial.begin(250000);
  printSome(F("vorher "), 0, 16);
  uint16_t tVal = test.maxVal();
  for (byte i = 0; i < 16; i++) {
    test.set(i, tVal--);
  }
  printSome(F("filled "), 0, 16);
  test.set(4, test.maxVal());
  test.set(6, test.maxVal());
  printSome(F("set 4+6"), 0, 16);
}

void printSome(const __FlashStringHelper* mark, uint16_t from, uint16_t howMany) {
  Serial.print(mark);
  Serial.write(' ');
  for (byte i = from; i < howMany; i++) {
    Serial.print(test.get(i));
    if (i != howMany - 1) {
      Serial.print(F(", "));
    }
  }
  Serial.println();
}
void loop() {}
vorher  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
filled  63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48
set 4+6 63, 62, 61, 60, 63, 58, 63, 56, 55, 54, 53, 52, 51, 50, 49, 48

Nette "Fingerübung" :slight_smile:

Klitzekleine Anmerkung: Trotz uint16_t from gibt es bei byte i = from keine Warnung.

Das Drucken funktioniert so nur in den ersten 255 Bytes,
kannst ja aus dem i auch einen uint16_t machen.

:wink:

Whandall:
Vielleicht gefällt dir ja das Ergebnis meiner kleinen Fingerübung.
...

Danke! Das muss ich mir bei nächster Gelegenheit mal genauer ansehen. Da ist vieles drin, mit dem ich bislang nie zu tun hatte.

Gruß

Gregor

Da ist vieles drin, mit dem ich bislang nie zu tun hatte.

Als ich die Aufgabenstellung gelesen hatte, dachte ich mir:
Ist doch ganz einfach, den [] Operator überladen, und gut ist....

No!
So einfach nicht.

Aber, mir scheint, es ist möglich!
Über den Umweg eines Mimikry Stellvertreters.

Dann sind auch plötzlich solche Sachen möglich:
datenfeld[index]++;
datenfeld[index] += 2;

Einen herzlichen Dank, für die spannende Frage.
Denn das ist eine Gelegenheit für mich, was zu lernen.

Wenn ich zu brauchbaren Ergebnissen komme, dann stelle ich sie hier mal vor!

Auf jeden Fall werde ich es ausarbeiten, denn das Verfahren kann man noch öfter nutzen.

Hallo nochmal!

Was ich suche, gibt es wohl wirklich nicht schon fix und fertig. Und wenn ich nicht noch durch Whandalls durchsteige, muss ich mir wohl selber etwas ausdenken (was nichts macht, weil das Problem interessant ist).

Gruß

Gregor

Du wirst nichts Einfacheres finden, als eine Klasse, die die Byte Anzahl und die Bitbreite im Konstruktor erhält
und dir lesenden und schreibenden Zugriff auf einzelne Elemente erlaubt.
Die habe ich dir hingeschrieben (zur Lösung des Problems von 160 6-Bit Elementen in 128 Bytes)

Nimm die Klasse und benutze sie, verstehen musst du die 'Innereien' nicht wirklich.

Willst du bei Ethernet, WiFi, Bluetooth, etc. auch immer verstehen wie es implementiert wurde?

Whandall:
Nimm die Klasse und benutze sie, verstehen musst du die 'Innereien' nicht wirklich.
Willst du bei Ethernet, WiFi, Bluetooth, etc. auch immer verstehen wie es implementiert wurde?

Nein, weil derlei Dinge zu umfangreich sind. Was ich haben möchte, ist weit weniger komplex.

Gruß

Gregor

combie:
Über den Umweg eines Mimikry Stellvertreters.

Also, ich bin ja kein C++-Crack, aber entweder ich habe da fundamentalere Lücken als ich dachte, oder das ist nicht der beste Link für das Konzept.

Signifikant weniger komplex geht nicht.

Arbeite an deiner Erwartungshaltung.

Grundsätzlich finde ich die Einstellung etwas haben zu wollen (gefragt wurde nach einer Lösung)
und dann nachher die Antwort nicht benutzen zu wollen, weil man sie nicht haarklein erklärt bekommen hat
und die eigenen Fähigkeiten zu beschränkt sind, sie einfach zu lesen, sagen wir mal 'wenig motivierend'.

Schreib demnächst dazu, ob du auch eine Antwort haben willst, und wie die genau geartet sein soll, damit sie deinen Erwartungen entspricht.

ElCaron:
Also, ich bin ja kein C++-Crack, aber entweder ich habe da fundamentalere Lücken als ich dachte, oder das ist nicht der beste Link für das Konzept.

Ja, vielleicht ist es nicht ganz der richtige Begriff....
Schien mir allerdings angemessen.

Der gregorss hätte gerne ein riesiges Bitfeld.
In dem er einzelne 6 Bit Worte adressieren kann.

Der Zweck ist wohl Speicher sparen, Schluss mit der 2 Bit verplemperung

Das einfache [] Operator überladen bringt einen nicht weiter, weil die Referenzen, welche man da behandeln muss, immer auf Bytegrenzen liegen. Kein Wunder, denn das Byte ist die kleinste adressierbare Einheit.

Wenn man also ein SpezialArray bauen will, welches aus 100 Worten, jeweils 3 Bit, besteht, kann man schlecht array[4]=2; schreiben, um das Wort mit dem Index 4 zu adressieren.
Das Wort könnte ja sich über Bytegrenzen hinweg erstrecken, irgendwo in einem Byte beginnen.

Darum das Mimikry.
Ein Element (Stellvertreter) schaffen, welches sich wie ein Byte ansprechen lässt, sich wie ein Byte verhält, so aussieht, usw.
Es darf sich halt nur in der internen Breite, von einem Byte unterscheiden.
Dann gehen auch solche Ausdrücke: array[4]=2 oder array[4] <<=1

Überlebenssicherung, durch tarnen als Byte

Ich wünsche mir folgende Syntax:

// bitfeld global definieren, mit 100 Zellen, jede 3 Bit breit
BitFeld<100,3> feld;


// nutzung
void loop()
{
  feld[0] = 1; // eine Eins in die erste Zelle schreiben
  feld[4]++; // Wert in der Zelle mit dem Index 4 um einen erhöhen
 //usw
}

Wenn 8 Werte in 6 Byte abgelegt werden (oder 4 in 3 Byte), muss man, wenn gewünscht den [] Operator sowieso selber schreiben.
Dann kann man sich zum Daten laden und schreiben erstmal auf die erforderlichen Prozeduren beschränken.
Wenn man dann den Anwendungsfall berücksichtigt: Speicherplatz sparen beim sequentiellen Daten lesen und schreiben, kommt man sehr gut ohne einen [] Operator mit Random Access aus, und braucht nur einen speziellen 6bit-Ringpuffer mit einer read und einer write Methode.

Natürlich ist ein genereller (x Werte in y Byte)-Algorithmus bewundernswerter, aber vielleicht kann man erstmal ein konkretes Minimal-Beispiel bauen und dann daraus ein template, wenn man noch Lust und das Bedürfnis hat.
Nicht dass dieser Puffer zur Realisierung der Schönheit mehr RAM braucht als er einspart.

Dass ich selbst jetzt sofort sowas baue, habe ich übrigens nicht versprochen. So bin ich eben.

Wenn 8 Werte in 6 Byte abgelegt werden (oder 4 in 3 Byte), muss man, wenn gewünscht den [] Operator sowieso selber schreiben.

Das ist eben der Punkt, wo das Leben interessant wird!
Denn das triviale überschreiben des [] Operators trägt nicht weit genug.
Beim Lesen, mag das ja noch gehen, aber das Versagen beginnt beim schreiben.
feld[4]++;

Dass ich selbst jetzt sofort sowas baue, habe ich übrigens nicht versprochen. So bin ich eben.

Du könntest aber... mir mal zeigen, wie es einfach geht.... ohne Mimikry

Whandall:
Signifikant weniger komplex geht nicht.

Arbeite an deiner Erwartungshaltung.

Grundsätzlich finde ich die Einstellung etwas haben zu wollen (gefragt wurde nach einer Lösung)
und dann nachher die Antwort nicht benutzen zu wollen, weil man sie nicht haarklein erklärt bekommen hat
und die eigenen Fähigkeiten zu beschränkt sind, sie einfach zu lesen, sagen wir mal 'wenig motivierend'.

Schreib demnächst dazu, ob du auch eine Antwort haben willst, und wie die genau geartet sein soll, damit sie deinen Erwartungen entspricht.

Du verstehst wohl etwas falsch. Ich habe nicht nach einer Lösung gefragt. Meine Frage war, ob es einen solchen Container gibt. Ein Nein hätte genügt. Du scheinst jetzt aber ein Problem damit zu haben, dass ich die von Dir programmierte Klasse verstehen möchte.

Ich werde das mit dem Verstehen Deiner Klasse jedenfalls noch versuchen. Ob ich sie dann tatsächlich nutze steht auf einem anderen Blatt.

Gruß

Gregor

combie:
ohne Mimikry

Ist das jetzt ein Sprachkonstrukt, oder hast Du Dir das ausgedacht? Unter "Mimikry c++" finde ich nichts.

Das habe ich mir ausgedacht.
Wie gesagt, ein besserer ist mir nicht eingefallen.
Deswegen ja auch extra den Link dazu gesetzt, damit man erahnen kann, was ich damit meine