Hallo.
Folgendes geht mir nicht aus dem Kopf.
Ich bin versucht so modular wie möglich zu Programmieren.
Folgender Fall lässt sich sicher besser lösen, aber ich hab im Moment
keine Ahnung wie..
Ich habe einen Interleavten ADC-Buffer, also immer abwechselnd den einen
Wert und dann den anderen Wert.
Sagen wir mal Spannung und Strom.
Umschreiben ist zunächst mal nicht möglich da der Speicher des MCUs das
nicht zulässt.
Also habe ich zwei simple Funktionen:
1
int16_tlese_spannung(uint32_t*Buffer,intindex)
2
{
3
return(Buffer[index]>>16)&0x0000FFFF;
4
}
5
6
int16_tlese_strom(uint32_t*Buffer,intindex)
7
{
8
return(Buffer[index]&0x0000FFFF);
9
}
jetzt muss ich aber jede weitere Funktion zweimal definieren, daher hab
ich
zb
DFT_strom
DFT_spannung
RMS_strom
RMS_spannung
...
irgendwie nicht optimal.
Besser wärs ich übergeb einer Funktion einfach immer nur einen Buffer.
Ich müsste also mein 32bit array so übergeben dass die Funktion es als
16bit array ansieht, jedoch mit 32bit Schrittweite...
Und wenn es sich um eine allgemeine Funktion handeln soll (zb DFT) muss
natürlich ein 16bit array als Übergabe verlangt werden.
Ist das möglich? Kann ich ein Array irgendwie so virtualisieren dass
fürs auslesen eine Funktion zuständig ist?
Defakto soll also bei:
stromwert = array_strom[i]
eine Funktion ausgeführt werden.
Grüsse.
M.
Die Frage ist:
warum hast du in erster Linie schon mal einen uint32_t Buffer, wenn du
doch eigentlich 2 Stück 16 Bit Buffer benötigen würdest?
Aha!
Denn dann vereinfachen sich deine ganze Zugriffsfunktionen darauf, dass
da nicht Spannung und/oder Strom gelesen wird, sondern der i-te Index
aus einem der beiden Buffer.
D.h. die Information, ob das jetzt eine Spannung oder der Strom ist,
steckt nicht in den Funktionen, sondern darin, welchen Buffer ich in
weiterer Folge zur Rechnerei benutze.
dotm schrieb:> jetzt muss ich aber jede weitere Funktion zweimal definieren, daher hab> ich> zb> DFT_strom> DFT_spannung> RMS_strom> RMS_spannung> ...
Warum? Greift doch nur auf einen jeweils anderen Buffer zu, oder? Also
gleiche Funktion, anderer Übergabeparameter...
Und wie wäre es, die Funktion allgemeiner zu halten?
Code erhebt nicht den Anspruch, zu funktionieren, sollte aber
verdeutlichen was ich meine...
z.B. so
Leseroutine arbeitet mit 16 Bit-Feld: (uint16_t* Buffer,int index)
dann musst du beim Aufruf casten: xy= lese_spannung((uint16_t*)Buffer32,
idx, STROM)
Für die Unterscheidung Strom/Spannung könntest du weiteren
(boolean-)Parameter in Lesefunktion verwenden (s.o.).
Zugriff:
Spannung: Buffer[2*index+1]
Strom: Buffer[2*index]
Karl Heinz schrieb:> Die Frage ist:> warum hast du in erster Linie schon mal einen uint32_t Buffer, wenn du> doch eigentlich 2 Stück 16 Bit Buffer benötigen würdest?
ganz einfach:
Der Mikrocontroller liest seine Werte per DMA in den Speicher.
Bei n synchronen (x-bit) Kanälen ist die Ausgabe nun mal interleaved und
zwar in n*x bit grossen Datenblöcken.
Daran ist leider nichts zu ändern.
Michael schrieb:> Code erhebt nicht den Anspruch, zu funktionieren, sollte aber> verdeutlichen was ich meine...
Ja. so geht das natürlich.
Das ansprechen des Arrays geschieht bei dir natürlich immer noch mit
lese() anstelle array[].
Etwas besser ist es schon.
Random ... schrieb:> Wenn dein Datenbuffer immer gleich ist, könntest du den umcasten:
Ok, also sagen wir mal ich verwende eine einheitliche Bibliothek für
z.B. DFT oder ähnliches.
Diese erwartet ein 16bit array als eingabe
1
doubledft(uint16_t*array,intbin)
drinnen geschieht wahrscheinlich noch eine Grössenüberprüfung
dotm schrieb:> geht das dann?
Nein.
In einem Array müssen alle Array-Elemente hintereinander im Speicher
liegen.
Das tun sie bei dir aber nicht.
Dir bleibt in so einem Fall nichts anderes übrig, als das Interleaving
aufzulösen und diesen Zustand herzustellen.
dotm schrieb:> dotm schrieb:>> drinnen geschieht wahrscheinlich noch eine Grössenüberprüfung>> length = sizeof(Buffer)>> sizeof(array) mein ich natürlich
Ist egal.
Denn die Funktion kann die Größe sowieso nicht aus eigener Kraft
feststellen, sondern ist darauf angewiesen, dass du korrekte Werte
übergibst.
Karl Heinz schrieb:> dotm schrieb:>>> geht das dann?> Nein.>> In einem Array müssen alle Array-Elemente hintereinander im Speicher> liegen.> Das tun sie bei dir aber nicht.> Dir bleibt in so einem Fall nichts anderes übrig, als das Interleaving> aufzulösen und diesen Zustand herzustellen.
d.h. nicht ganz.
Es gibt noch die Möglichkeit, der Funktion die zusätzliche Information
mitzugeben.
zb könntest du ihr einen Startindex in ein uint16_t Array mitgeben und
das Inkrement, welches den Abstand zwischen den relevanten Werten
beschreibt.
und innerhalb werden die Zugriffs-Indizes entsprechend für den Zugriff
auf die Daten umgerechnet.
1
for(i=0;i<bini++)
2
wert=array[i*IndexInkrement+startIndex];
3
....
aber abgesehen davon, sehe ich da nicht viele weitere Möglichkeiten.
Eine Möglichkeit dreht sich zb darum, die Funktion als Makro auszuführen
und dann den Präprozessor dazu zu benutzen, um die beiden Versionen der
Funktion zu erstellen. Bei kurzen Funktionen kann man das machen, wenns
aber komplizierter wird, dann ist das ein pain in the ass.
Karl Heinz schrieb:> Dir bleibt in so einem Fall nichts anderes übrig, als das Interleaving> aufzulösen und diesen Zustand herzustellen.
geht das prinzipiell ohne weiteren speicheraufwand? was ich da
rausgelesen hab:
http://stackoverflow.com/questions/7780279/de-interleave-an-array-in-place
scheint das ohne zwischenspeicher nicht zu funktionieren.
Schwierig scheint es mir auch abzuschätzen ob der Rechenaufwand für das
umschreiben des Arrays sich dann durch das schnellere Ansprechen in der
Funktion amortisiert...
Karl Heinz schrieb:> zb könntest du ihr einen Startindex in ein uint16_t Array mitgeben und> das Inkrement, welches den Abstand zwischen den relevanten Werten> beschreibt.
Das scheint mir zumindest eine recht schnelle Lösung zu sein, da der
Index ja sowieso erhöht werden muss.
Es fällt somit die Schiebeoperation und die Maske vollständig weg.
Ich würde es so lösen:
int16_t lese_spannung(uint32_t* Buffer,int index,int typ)
{
return ((int16_t*)Buffer)[index<<1|!!typ];
}
Wenn man sicherstellen kann, daß typ nur 0 oder 1 ist, kann
man auch das "!!" weglassen.
dotm schrieb:> Karl Heinz schrieb:>> Dir bleibt in so einem Fall nichts anderes übrig, als das Interleaving>> aufzulösen und diesen Zustand herzustellen.>> geht das prinzipiell ohne weiteren speicheraufwand?
nein.
Nochmal.
Wenn du Funktion vorgegeben ist und ein Array will, dann will sie ein
Array. Und Array bedeutet, dass die relevanten Informationen
hintereinander im Speicher liegen. Dicht an dicht.
Entweder du stellst diesen Zustand her oder du pimpst die Funktion so,
dass diese Vorbedingung nicht mehr erwartet wird.
> Schwierig scheint es mir auch abzuschätzen ob der Rechenaufwand für das> umschreiben des Arrays sich dann durch das schnellere Ansprechen in der> Funktion amortisiert...
Das wirst du raustimen müssen.
Das hängt von einigen Faktoren ab, die sich aus der konkreten
Applikation ergeben.
dotm schrieb:> Karl Heinz schrieb:>> zb könntest du ihr einen Startindex in ein uint16_t Array mitgeben und>> das Inkrement, welches den Abstand zwischen den relevanten Werten>> beschreibt.>> Das scheint mir zumindest eine recht schnelle Lösung zu sein, da der> Index ja sowieso erhöht werden muss.> Es fällt somit die Schiebeoperation und die Maske vollständig weg.
Dann lässt sich das hier
1
for(i=0;i<bini++)
2
wert=array[i*IndexInkrement+startIndex];
3
....
natürlich so auf C-Ebene vereinfachen
1
endBin=startIndex+bin*IndexInkrement;
2
3
for(i=startIndex;i<endBin;i+=IndexInkrement)
4
wert=array[i];
5
....
der ursprüngliche Codevorschlag hat sich weniger auf for-Schleifen,
sondern mehr auf den Zugriff konzentriert.
Karl Heinz schrieb:> natürlich so auf C-Ebene vereinfachen
Genau. Habe mir die Funktionen angesehen, alle beinhalten in etwa diese
Schleifen.
Somit ist zwar für interleavte Arrays jede signalverarbeitende Funktion
umzuschreiben, jedoch ändert sich die Geschwindigkeit nicht allzusehr.
Wenn die Architektur einen Zugriff auf Wordsize mit Int Pointern erlaubt
(z.B. Arm, PC, ...) dann geht folgendes auch:
uint32_t * Spannung = buffer_adc;
uint32_t * Strom = (unsigned int)Spannung+2;
int16_t _inline_ lese_buffer(uint32_t* Buffer,int index)
{
return (int16_t)Buffer[index];
}
und dann lese_buffer(Spannung,idx) sowie lese_buffer(Strom,idx) bzw
du könntest auch Spannung sowie Strom so definieren, und ersparst dir
die Funktion, aber Inline wird es eh so optimieren.
#define Spannung (int16_t)BufferAdc
#define Strom (int16_t)((uint32_t*)((intptr_t)BufferAdc) + 2))
Dann kannst du Spannung[n] sowie Strom[n] verwenden, als wäre es ein
Array von int16_t ; Ob jetzt Spannung oder Strom zuerst kommt, bzw
wie das aligmnment genau ist habe ich nicht genau gechekct, ev. muss man
dies umändern.
Chris schrieb:> du könntest auch Spannung sowie Strom so definieren, und ersparst dir> die Funktion, aber Inline wird es eh so optimieren.
gute Idee.
Ich glaube fürs erste werde ich das so machen, um CMSIS verwenden zu
können.
Der richtige Ansatz wäre dennoch die Arrays im Speicher getrennt zu
haben.
Eventuell pfeif ich auf DMA...