Forum: Compiler & IDEs neuen Datentyp (10-bit) zur Speicheroptimierung erstellen


von Tobias K. (tobias-k)


Lesenswert?

Hallo Community,

ich arbeite gerade an einem Prgramm mit dem ich Melodien über einen uC 
(ATmega8a)abspielen möchte. Das Programm funktioniert einwandfrei und 
die Melodien Werden in zwei 8-bit Array mit einmal dem Ton (Zahlenwerte 
0 - 38) und im anderen der Tondauer (Zahlenwerte 0 - 15).

Auf diese Art und weise verschwende ich jedoch 6 bit von insgesagt 16 
bit, da ich ja nur 6 + 4 bit brauche. Ich könnte also 37,5% Speicher 
einsparen und bei bisher 650 verwendeten Byte ist das nicht zu 
vernachlässigen.

Gibt es eine einfache oder gute Möglichkeit den kompletten Speicher 
auszunutzen?

von Rolf M. (rmagnus)


Lesenswert?

Tobias K. schrieb:
> Gibt es eine einfache oder gute Möglichkeit den kompletten Speicher
> auszunutzen?

Bei der Tondauer ist es ja einfach. Da du dafür genau 4 Bit brauchst, 
kannst du einfach zwei Stück in ein Byte schreiben, dann hast du schon 4 
von deinen 6 Bit gespart.
Ob es sich jetzt lohnt, die restlichen 2 Bit auch noch einzusparen, 
musst du dir überlegen. Vielleicht kannst du dir ja alternativ noch ein 
nettes Zusatzfeature einfallen lassen, für das du die Bits nutzen kannst 
(z.B. vibrato).

von Peter D. (peda)


Lesenswert?

Wenn der Speicher kanpp ist, nimm einfach den ATmega328.

von Tobias K. (tobias-k)


Lesenswert?

Rolf M. schrieb:
> Bei der Tondauer ist es ja einfach. Da du dafür genau 4 Bit brauchst,
> kannst du einfach zwei Stück in ein Byte schreiben, dann hast du schon 4
> von deinen 6 Bit gespart.

Danke für den Tipp, die Idee hatte ich auch schon und werde ich 
vermutlic hauch umsetzten. Die restlichen 2 Bit wären da nicht so 
einfach unter zu bringen. Gibt es C ne möglichkeit einen 6 bit Datentyp 
zu definieren, der mir einfach nacheinander en Speicher auffüllt, weil 
mit bitschieberein wird das nicht allzu toll

von Bauform B. (bauformb)


Lesenswert?

Wenn man Ton und Dauer zusammenfasst, kommt man auf 624 unterschiedliche 
Noten. Im wirklichen Leben verwendet eine Melodie aber nur einen kleinen 
Teil davon. Du könntest max. 255 unterschiedliche Noten erlauben und die 
Melodie in einem normalen Byte Array speichern.

Zum Abspielen gehst du einen Umweg über eine Lookup Table, die dir 
Tonhöhe und Tondauer für eine bestimmte Note liefert. Diese Tabelle muss 
nur so viele Einträge haben, wie die Melodie unterschiedliche Noten hat. 
"Alle meine Entchen" für Xylophon kommt mit weniger als 16 aus ;) Da 
könnte man auch noch 2 Noten pro Melodie-Byte speichern.

Der GCC kennt __attribute__((packed)) für ein typedef struct. Damit kann 
man im Prinzip beliebig kurze Datentypen erzeugen. Allerdings wird der 
Code für die Zugriffe auf gepackte Daten u.U. ziemlich groß und langsam.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Tobias K. schrieb:
> Gibt es C ne möglichkeit einen 6 bit Datentyp zu definieren, der mir
> einfach nacheinander en Speicher auffüllt, weil mit bitschieberein wird
> das nicht allzu toll

Nein, so etwas gibt es nicht, und um Bitschieberei kämst Du so oder so 
nicht herum. Der Compiler würde sie, wenn er Datentypen unterstützen 
würde, die kleiner sind als ein Byte, zwar vor Dir verbergen, aber 
geshiftet werden muss so oder so.

Auch Bitfelder helfen Dir nicht bei Deinem Problem.

Entweder Speicher sparen und wüste Shift-Orgien, oder Speicher 
"verschwenden" mit deutlich weniger Programmieraufwand.

Du könntest natürlich auch überlegen, ob Deine Datenstruktur so, wie sie 
ist, sinnvoll ist. Musst Du für jeden angeschlagenen Ton jedesmal 
einzeln dessen Lautstärke festlegen, oder würde es nicht vielmehr 
genügen, nur bei Lautstärkenänderungen die Lautstärke festzulegen?

Dann könntest Du mit einem Byte pro Operation - Ton anschlagen bzw. 
Lautstärke ändern - auskommen.

Was hier übrigens fehlt, ist die Zeitinformation; wie lange soll ein Ton 
erklingen? Auch das könntest Du in diesem Format unterbringen, wenn Du 
die oberen zwei Bits eines jeden Bytes für die Unterscheidung 
Ton/Lautstärke/Länge vorsiehst, hast Du jeweils sechs Bits (0..63) für 
den jeweiligen Wert zur Verfügung.

Und Du brauchst immer noch nur ein Byte pro Kommando.

Eine Melodie könnte dann so aussehen:

D4 L5 T1 T5 T7 L3 D2 T2 L5 D4 T3 T8 T16 L1 T1 L8 D1 T8 T9

(T für Ton, L für Lautstärke und D für Dauer)

von Amateur (Gast)


Lesenswert?

Es ist kein Problem die Daten zu "packen", aber...
Hast Du so viel Rechenleistung über?

Da fallen bei jedem Datum etliche Zwischenbefehle an.
1. Eigene Adressberechnungen.
2. Schiebebefehle.
3. Maskierungssachen.

Bei Tönen geht es oft relativ zügig zu.

von (prx) A. K. (prx)


Lesenswert?

Tobias K. schrieb:
> die Melodien Werden in zwei 8-bit Array mit einmal dem Ton (Zahlenwerte
> 0 - 38) und im anderen der Tondauer (Zahlenwerte 0 - 15).

39 x 39 x 39 = 59319. Du kannst also 3 Ton-Werte in 16 Bits packen. Ist 
halt etwas langsamer, da ranzukommen.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Auch Bitfelder helfen Dir nicht bei Deinem Problem.
Man kann Bitfelder benutzen, um das Problem zu lösen.

Ziel ist es, diesen "10 Bit Datentypen" in ein Array ohne padding zu 
speichern. Es gibt genau 4 Möglichkeiten, wie die 10 Bit in 2 
aufeinander folgenden Bytes angeordnet sein können. Es werden also 4 
Bitfelder benötigt.
1
struct tb1 {
2
  unsigned int fB : 4;
3
  unsigned int sB : 6;
4
  unsigned int f1 : 6;
5
};
6
struct tb2 {
7
  unsigned int f1 : 2;
8
  unsigned int fB : 4;
9
  unsigned int sB : 6;
10
  unsigned int f2 : 4;
11
};
12
struct tb3 {
13
  unsigned int f1 : 4;
14
  unsigned int fB : 4;
15
  unsigned int sB : 6;
16
  unsigned int f2 : 2;
17
};
18
struct tb4 {
19
  unsigned int f1 : 6;
20
  unsigned int fB : 4;
21
  unsigned int sB : 6;
22
};
23
void set(int index, uint8_t* data, int8_t fourBit, int8_t sixBit)
24
{ 
25
  int const offset = index * 10 / 8;
26
  switch(index % 4) {
27
    case 0: {
28
      struct tb1* p = (struct tb1*)&data[offset];
29
      p->fB = fourBit;
30
      p->sB = sixBit;
31
      break;
32
    }
33
    case 1: {
34
      struct tb2* p = (struct tb2*)&data[offset];
35
      p->fB = fourBit;
36
      p->sB = sixBit;
37
      break;
38
    }
39
    case 2: {
40
      struct tb3* p = (struct tb3*)&data[offset];
41
      p->fB = fourBit;
42
      p->sB = sixBit;
43
      break;
44
    }
45
    case 3: {
46
      struct tb4* p = (struct tb4*)&data[offset];
47
      p->fB = fourBit;
48
      p->sB = sixBit;
49
      break;
50
    }
51
  }
52
}
53
void get(int index, uint8_t const* data, int8_t* fourBit, int8_t* sixBit)
54
{
55
  int const offset = index * 10 / 8;
56
  switch(index % 4) {
57
    case 0: {
58
      struct tb1* p = (struct tb1*)&data[offset];
59
      *fourBit = p->fB;
60
      *sixBit = p->sB;
61
      break;
62
    }
63
    case 1: {
64
      struct tb2* p = (struct tb2*)&data[offset];
65
      *fourBit = p->fB;
66
      *sixBit =p->sB;
67
      break;
68
    }
69
    case 2: {
70
      struct tb3* p = (struct tb3*)&data[offset];
71
      *fourBit = p->fB;
72
      *sixBit =p->sB;
73
      break;
74
    }
75
    case 3: {
76
      struct tb4* p = (struct tb4*)&data[offset];
77
      *fourBit = p->fB;
78
      *sixBit =p->sB;
79
      break;
80
    }
81
  }
82
}

von Tobias K. (tobias-k)


Lesenswert?

Danke für die ganzen tollen Antworten. Werde jetzt vermutlich einfach 
die Dauern (4bit) immer zu zweit in ein Byte packen, da sich das nicht 
allzu kompliziert realisieren lässt.

von Rolf M. (rmagnus)


Lesenswert?

Rufus Τ. F. schrieb:
> Was hier übrigens fehlt, ist die Zeitinformation; wie lange soll ein Ton
> erklingen?

Ich hätte vermutet, dass der Paramter "Tondauer" das angibt. ;-)
Ich glaub, du hast da was verwechselt.

von W.S. (Gast)


Lesenswert?

Tobias K. schrieb:
> Auf diese Art und weise verschwende ich jedoch 6 bit von insgesagt 16
> bit

Dann speichere deine Melodie eben nicht als Array, sondern als 
Bitstream. Einfach nahtlos die jeweils 10 Bit aneinanderkleben und 
fertig. Das Auseinandernehmen geht genauso einfach - jedenfalls bei 
einem 32 Bitter. Kann dein µC wenigstens 16 Bit Operationen ohne 
Umstände?

Also etwa in dem Stil:
1
unsigned int Akku;
2
unsigned int tmp;
3
int bitcount;
4
unsigned int* P;
5
6
if (bitcount < 10)
7
{ tmp = *P++;
8
  Akku = Akku | (tmp<<bitcount);
9
  bitcount+= 10;
10
}
11
result = akku & 0x3FF;
12
bitcount -= 10;

W.S.

von Rolf M. (rmagnus)


Lesenswert?

W.S. schrieb:
> Kann dein µC wenigstens 16 Bit Operationen ohne Umstände?

Das wird der

Tobias K. schrieb:
> ATmega8a

eher nicht können. Der kann nicht mal 8 Bit um mehr als 1 Bit am Stück 
schieben. Dein Code wird darauf also besonders ineffizient sein. Spielt 
aber vielleicht an der Stelle gar keine so große Rolle.

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Der kann nicht mal 8 Bit um mehr als 1 Bit am Stück schieben.

Er kann auch 4 Bit und 8 Bit am Stück und GCC weiss das. ;-)

Solange die Shiftcount konstant ist, und der Datentyp nicht zu breit, 
gibt GCC sich respektable Mühe, effizienten Code auszuwerfen.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Solange die Shiftcount konstant ist, und der Datentyp nicht zu breit

.. was beides hier nicht zutrifft.

Fazit: Der Chip ist zu mickrig für diesen Einsatz.

W.S.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
>> Solange die Shiftcount konstant ist, und der Datentyp nicht zu breit
>
> .. was beides hier nicht zutrifft.
> Fazit: Der Chip ist zu mickrig für diesen Einsatz.

Es kommen nur 2 Feldlängen vor. Folglich kann man einen Bitstream 
implementieren, ohne dynamische Shifts verwenden zu müssen.

GCCs Optimierung der Shifts für AVR betrifft 16-Bit Daten. Das ist 
hierfür breit genug.

Ein besserer µC ist dafür freilich von Vorteil. Wenn der Dividieren 
kann, dann geht auch die erwähnte und sparsamste Radix-39 Codierung 
recht flott.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

W.S. schrieb:
> A. K. schrieb:
>> Solange die Shiftcount konstant ist, und der Datentyp nicht zu breit
>
> .. was beides hier nicht zutrifft.
>
> Fazit: Der Chip ist zu mickrig für diesen Einsatz.

Oder das Programm ist nicht ausreichend für den Chip optimiert. Ob man 
jetzt gleich auf eine komplett andere Architektur umsteigen muss, nur 
weil die Trivial-Implementation von ein paar Bit-Schieberein sonst nicht 
effizient wäre?

von Karl K. (karl2go)


Lesenswert?

W.S. schrieb:
> Fazit: Der Chip ist zu mickrig für diesen Einsatz.

Klarer Full für einen 10-Bitter. Der kann das.

von S. R. (svenska)


Lesenswert?

Tobias K. schrieb:
> Ich könnte also 37,5% Speicher einsparen und bei
> bisher 650 verwendeten Byte ist das nicht zu vernachlässigen.

Alternativ könntest du auch die Melodie ins Flash legen (mit __flash 
oder PROGMEM). Damit ist deine Melodie zwar nicht kleiner, aber du 
sparst RAM. Im Gegensatz zum Flash geizen die Chips mit RAM. :-)

von Sempfdazugeber (Gast)


Lesenswert?

Ich musste auch mal 4 x 10 bit AD-Werte etwas umpacken.
(In 5 Bytes a 8 Bit.)

Über C habe ich mir gar nicht erst den Kopf zerbrochen,
sondern gleich den Assembler angeworfen.

Herausgekommen ist eine lustige Schiebeorgie, die bemerkenswerterweise
beim Aufruf sowohl die Daten packen als auch entpacken kann.
Wohlgemerkt mit der selben Routine.
Wie nicht anders zu erwarten reichen genau 42 Zeilen Assembler dafür.


Das soll in C erstmal einer nachmachen...

von S. R. (svenska)


Lesenswert?

Sempfdazugeber schrieb:
> Wohlgemerkt mit der selben Routine.

Du hättest die Routine ja netterweise dazulegen können.
So bist du nur ein Schwafler. Tacuisses, philosophus mansisses.

von Sempfdazugeber (Gast)


Lesenswert?

> Du hättest die Routine ja netterweise dazulegen können.

Die war fuer einen Kleinen PIC und haette dem TO also so nuex genuetzt.

Der TO soll sich gefaelligst selber mit Assembler beschaeftigen.
Fuer Copy & Pasta spende ich hier nicht!

von S. R. (svenska)


Lesenswert?

>> Tacuisses, philosophus mansisses.
> Fuer Copy & Pasta spende ich hier nicht!

Danke für den Beweis. :-D

von Sempfdazugeber (Gast)


Lesenswert?

> Danke für den Beweis.

Nuex zu danken. :-=

von Sempfdazugeber (Gast)


Lesenswert?

P.S.:

> ein Schwafler

Schwafler sind die, die ihre Postings mit:

> Hallo Community,

beginnen und selber nuex zu 100 % zustande bringen.
Wenn nur C im Kopf ist, kann auch nur C rauskommen.

Eine Datenwortbreite von 10 bit legt auf einem 8 bit Controller
ohnehin eine schnelle Assemblerroutine nahe.

Und wenn da bei ihm da nuex ist, muss er es halt lernen.
Nur der harte Weg ist der richtige.


Lächeln und Winken!

von Da D. (dieter)


Lesenswert?

Moby, bist du es?

von Rolf M. (rmagnus)


Lesenswert?

Sempfdazugeber schrieb:
> Wie nicht anders zu erwarten reichen genau 42 Zeilen Assembler dafür.
>
> Das soll in C erstmal einer nachmachen...

Was? Dass man so viele Zeilen dafür braucht?

von 2⁵ (Gast)


Lesenswert?

Sempfdazugeber schrieb:
> Ich musste auch mal 4 x 10 bit AD-Werte etwas umpacken.
> (In 5 Bytes a 8 Bit.)

Wie waren denn Ein- und Ausgabendaten kodiert? Z.B. die 4x 10bit als 4x 
16 Bit Ints und die 5 Byte? 4x8 Bit und dann ein Byte mit den restlichen 
4x2 Bit? Oder als Bit-Stream?

> Herausgekommen ist eine lustige Schiebeorgie, die bemerkenswerterweise
> beim Aufruf sowohl die Daten packen als auch entpacken kann.
> Wohlgemerkt mit der selben Routine.

Ohne Fallunterscheidung? Dann können die Eingabedaten wohl nicht als 
4x16Bit kodiert gewesen sein...

> Wie nicht anders zu erwarten reichen genau 42 Zeilen Assembler dafür.

In C werden es wohl deutlich unter 42 Zeilen sein. Wäre jetzt nur 
interessant, wie viel Zeilen Assemblercode der Compiler daraus macht.

von Sempfdazugeber (Gast)


Lesenswert?

> Was? Dass man so viele Zeilen dafür braucht?

Ja, das Loop Unrolling ist schuld daran!
Aber, es soll ja schnell gehn.
Aber es sind gut investierte 42 Worte im Flash.

> Rolf M.

Bist du der von der beirischen Trinkerrunde?

von Sempfdazugeber (Gast)


Lesenswert?

> die 4x 10bit als 4x 16 Bit Ints
[x]

> und die 5 Byte? 4x8 Bit und dann ein Byte mit den restlichen
> 4x2 Bit? Oder als Bit-Stream?
Wie speichert man wohl 5 Byte...

(Tipp: hintereinander...)

Es ging bei mir auch um Wandlerdaten von einem AD.
Und kein schnoedes Tongepiepse.


> In C werden es wohl deutlich unter 42 Zeilen sein. Wäre jetzt nur
> interessant, wie viel Zeilen Assemblercode der Compiler daraus macht.

Nicht schwafeln sondern machen!

von Sempfdazugeber (Gast)


Lesenswert?

>> sowohl die Daten packen als auch entpacken kann.
>> Wohlgemerkt mit der selben Routine.

> Ohne Fallunterscheidung? Dann können die Eingabedaten wohl nicht als
> 4x16Bit kodiert gewesen sein...

Unsigned ints waren es.
Das Resultat waren 5 unsigned chars.
Beim Auspacken natuerlich andersrum.

Auf eine C-Routine die das ebenfalls ohne Fallunterscheidung
schafft, waere ich ja mal gespannt :-).

Und ja: Es gibt keine Fallunterscheidung zwischen den
Operationen Packen und Entpacken in der Assemblerroutine.

Was in den 4 uints steht, landet in den 5 uchars und vice versa.
In einem Arbeitsgang.


> Moby, bist du es?
[ ]

von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> GCCs Optimierung der Shifts für AVR betrifft 16-Bit Daten. Das ist
> hierfür breit genug.
>
> Ein besserer µC ist dafür freilich von Vorteil. Wenn der Dividieren
> kann, dann geht auch die erwähnte und sparsamste Radix-39 Codierung
> recht flott.

OK, dann geht die Schieberei ja doch ausreichend gut. Aber vom 
Dividieren würde ich doch eher absehen.

W.S.

von W.S. (Gast)


Lesenswert?

Sempfdazugeber schrieb:
> Ich musste auch mal 4 x 10 bit AD-Werte etwas umpacken.
> (In 5 Bytes a 8 Bit.)

jaja.

So, nachdem hier viel Senf geflossen ist, kommt zum Abschluß noch ne 
Portion Mayo:
1
; Protokoll 3: vierkanalig, 9 Bit
2
; Kanäle 0, 2, 3, 4, hier A,B,C,D genannt
3
; Byte-Block-Aufbau:
4
;    Seb_A    Seb_B    Seb_C    Seb_D    Seb_E    Seb_F
5
; 76543210 76543210 76543210 76543210 76543210 76543210   Bitnummern
6
; 0001000A 1AAAAAAA 1ABBBBBB 1BBBCCCC 1CCCCCDD 1DDDDDDD   Kanäle
7
8
prot_3:  MOVLW  10h
9
         MOVWF  Seb_A
10
         MOVF   Avalue0+1,W   ; A
11
         MOVWF  Seb_B
12
         BTFSC  Seb_B,7
13
         BSF    Seb_A,0
14
         BSF    Seb_B,7
15
         MOVF   Avalue2+1,W   ; B
16
         MOVWF  Seb_C
17
         MOVF   Avalue2,W
18
         MOVWF  Seb_D
19
         RLF    Avalue0,F     ; 9. Bit-->Carry
20
         RRF    Seb_C,F       ; als MSB in Seb_C
21
         RRF    Seb_D,F
22
         BSF    Carry
23
         RRF    Seb_C,F       ; Seb_C ist jetzt fertig
24
         RRF    Seb_D,F
25
         BSF    Carry
26
         RRF    Seb_D,F       ; B ist jetzt untergebracht
27
         MOVLW  B'11110000'
28
         ANDWF  Seb_D,F
29
         SWAPF  Avalue3+1,W
30
         ANDLW  B'00001111'
31
         IORWF  Seb_D,F       ; hi(C) ist jetzt untergebracht
32
33
         SWAPF  Avalue3+1,W
34
         ANDLW  B'11110000'
35
         MOVWF  Seb_E
36
         BTFSC  Avalue3,7
37
         BSF    Seb_E,3
38
         BSF    Carry
39
         RRF    Seb_E,F      ; C ist jetzt komplett untergebracht
40
41
         MOVF   Avalue4+1,W
42
         MOVWF  Seb_F
43
         RLF    Avalue4,W
44
         RLF    Seb_F,F
45
         SKIP   NC
46
         BSF    Seb_E,1
47
         BTFSC  Seb_F,7
48
         BSF    Seb_E,0
49
         BSF    Seb_F,7

Das war von mir, ist rund 10 Jahre alt und stammt aus meiner eigenen 
Firmware für den FA-NWT (Funkamateur-Wobbler). Und es sind dort 9 Bit 
pro Meßwert und 7 zu benutzende Bits in den zu sendenden Bytes.

Quasi Radio Eriwan.

Klar: In Hochsprache notiert sich sowas viel einfacher, aber was da als 
MC herauskommt, ist gerade in solchen Fällen gruseliger. So ist das 
eben, wenn man was auf nem 8 Bitter durchziehen muß.

W.S.

von S. R. (svenska)


Lesenswert?

Sempfdazugeber schrieb:
> Nicht schwafeln sondern machen!

Sagt der Schwafler.

von Rolf M. (rmagnus)


Lesenswert?

Sempfdazugeber schrieb:
>> Was? Dass man so viele Zeilen dafür braucht?
>
> Ja, das Loop Unrolling ist schuld daran!

Ja, das ist eben der Nachteil, wenn man das in Assembler macht: Da muss 
man sich um sowas von Hand kümmern.

>> Rolf M.
>
> Bist du der von der beirischen Trinkerrunde?

?

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

Warum ist Geschwindigkeit hier überhaupt ein Thema?

Selbst die kürzesten Töne werden mindestens 10 ms lang sein, sonst würde
man sie gar nicht mehr als solche wahrnehmen. Nachdem der ATmega die
Periodendauer eines Tons in ein entsprechendes Timer-Register
geschrieben hat, hat er somit alle Zeit der Welt, um die Daten für den
nächsten Ton vorzubereiten.

Sempfdazugeber schrieb:
> Ich musste auch mal 4 x 10 bit AD-Werte etwas umpacken.
> (In 5 Bytes a 8 Bit.)
>
> Über C habe ich mir gar nicht erst den Kopf zerbrochen,
> sondern gleich den Assembler angeworfen.
>
> Herausgekommen ist eine lustige Schiebeorgie, die bemerkenswerterweise
> beim Aufruf sowohl die Daten packen als auch entpacken kann.
> Wohlgemerkt mit der selben Routine.
> Wie nicht anders zu erwarten reichen genau 42 Zeilen Assembler dafür.

Hast du den Code mit NOPs aufgefüllt, um auf die 42 zu kommen? ;-)

Da der TE keine 10-Bit-Zahlen braucht, sondern jeweils eine 4Bit- und
eine 6-Bit-Zahl, ist noch jede Menge zusätzlicher Bit-Operationen nötig,
um am Ende jeden der insgesamt 8 Zahlenwerte in einem eigenen Register
stehen zu haben.

Der Assemblercode im Anhang nimmt 5 Bytes mit den gepackten Daten für 4
Töne entgegen und liefert in 20 Taktzyklen die 4 Notenwerte und die 4
Dauern in 8 Bytes zurück. Das dabei verwendete Packschema ist im
Kommentar ganz am Anfang beschrieben. Ich habe den Code nicht getestet,
hoffentlich enthält er trotzdem keine Fehler.

: Bearbeitet durch Moderator
von PittyJ (Gast)


Lesenswert?

Ich hatte gerade nach Flash-Bausteinen gesucht.
256 KBytes SPI Flash kosten 82 Cent. Für diesen Preis würde ich mir die 
Bitpackerei ersparen.

von Joerg W. (joergwolfram)


Lesenswert?

Ich denke, das geht auch problemlos mit 8 Bit je Note. Anstelle einer 
Tondauer würde ich den Notenwert nehmen:

1/1, 3/4, 1/2, 3/8, 1/4, 3/16, 1/8, 1/16

sind 8 Werte, für die man 3 Bits benötigt.

Es bleiben 5 Bits für die Note (Tonhöhe) übrig. Das ergibt 32 Tonhöhen, 
was gegenüber den 38 geforderten zuwenig ist. Die meisten Melodien 
spielen sich zwischen 1-2 Oktaven ab, also könnte man 30 Tonhöhen 
verwenden und die Vorletzte als Pause und die Letzte für 8 Steuercodes 
nutzen:

- +1 Oktave
- -1 Oktave
- Beginn Wiederholung 1-fach
- Beginn Wiederholung 2-fach
- Beginn Wiederholung 3-fach
- Beginn Wiederholung 4-fach
- Ende Wiederholung
- Ende vom Lied

Ebenso könnte man noch eine weitere Note "abzwacken" und damit 8 
Laustärkestufen realisieren. Der AVR kann die Steuercodes schnell genug 
auswerten, so dass die Verzögerung eher im einstelligen 
Mikrosekunden-Bereich liegt und damit unhörbar ist.

Jörg

von Peter D. (peda)


Lesenswert?

W.S. schrieb:
> Einfach nahtlos die jeweils 10 Bit aneinanderkleben und
> fertig. Das Auseinandernehmen geht genauso einfach

Man braucht ja nur 4 Fälle zu unterscheiden, dann ist man wieder an 
einer Bytegrenze.

von W.S. (Gast)


Lesenswert?

Peter D. schrieb:
> Man braucht ja nur 4 Fälle zu unterscheiden

Ich vermute mal, daß das Ausführen dieser Unterscheidungen genauso viel 
oder mehr an Rechenzeit und Code kostet wie die variable Verschiebung.

W.S.

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> Ich vermute mal, daß das Ausführen dieser Unterscheidungen genauso viel
> oder mehr an Rechenzeit und Code kostet wie die variable Verschiebung.

Bei AVRs kostet ein ausgeführter Sprung nur einen Takt mehr als ein 
nicht ausgeführter, dynamische 16-Bit Shifts hingegen benötigen 5 Takte 
pro Bit.

Bei 16/32-Bittern mit Barrelshifter ist eine Fallunterscheidung jedoch 
ungünstig und mit tiefer Pipeline und Sprungvorhersage wirds erst recht 
kompliziert.

: Bearbeitet durch User
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.