Forum: Mikrocontroller und Digitale Elektronik MSP430 erstellen einer LUT


von Mathias U. (munter)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe (wieder Mal) ein kleines Problemchen mit dem MSP430F612, bzw. 
mit dessen Programmierung:

Software ist wie immer die IAR 3.21A
Sprache: C

Ich möchte eine LookUpTable erstellen, die meine Messwerte enthält.
Das Programm ist jetzt erstmal nur zum Testen.
Ich habe an zwei ADC12-Kanälen 2 verschiedene Spannungen dran.
Die Referenz ist 2,5V (interne Ref) und Vdd.
Jetzt möchte ich nur mal testweise eine LUT erstellen, die 4096 Werte
enthält, die die Millivolt darstellen sollen.
Ich möchte dann je nach Wandlerwert in die LUT reingehen, und die 
Spannung raussuchen lassen...

Wenn ich die LUT 2000 Elemente lang mache, dann geht es.
Wir die LUT allerdings länger als 2045, dann bleibt der Debugger an der 
Stelle hängen, die auf dem Bild zu sehen ist.
Die LUT wird dann NICHT gefüllt.
Das ist natürlich etwas ungünstig, weil ich ja 4096 Werte in der LUT 
haben muss.

Hier mal der Code:
1
#include <msp430x16x.h>
2
void fill_LUT(void);
3
WORD LUT[2055];
4
5
void main()
6
{
7
  WDTCTL = WDTPW+WDTHOLD;                           // Stop watchdog timer 
8
9
  fill_LUT();                                       // füllen der LookUpTable
10
11
  for(;;)
12
  {
13
   _NOP();                    
14
  }
15
}   // ende main
16
17
void fill_LUT(void)
18
{
19
  for (short j=0; j<2055; j++)
20
  {
21
    // WORD ist unsigned short, also 2 bytes
22
    LUT[j] = (WORD)(j*2500.0/4096.0);
23
  }  
24
}
Ich habe den Kram mit der Wandlung und so mal alles rausgenommen.

Das komische ist aber auch, dass, wenn ich mein "richtiges" Programm 
nehme,
und dann die LUT 4096 lang machen möchte, zeigt er mir folgenden Fehler:
1
Error[e16]: Segment DATA16_Z (size: 0x2026 align: 0x1) is too long for segment definition.
2
At least 0xc25 more bytes needed. The problem occurred while processing the segment placement command 
3
"-Z(DATA)DATA16_I,DATA16_Z,DATA16_N,HEAP+_HEAP_SIZE=1100-2500",
4
where at the moment of placement the available memory ranges were "CODE:1100-2500"
5
   Reserved ranges relevant to this placement:
6
   CODE:1100-2500       DATA16_I

Was ist das für ein Fehler? Was kann ich da machen?
Hat einer Rat?
Bzw. wie erstelle ich auf dem MSP430F1612 eine LUT mit 4096 Elementen?
Dankeschön!
mathias

von Dan (Gast)


Lesenswert?

Der MSP430F1612 hat nur 5k RAM!

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Ich möchte eine LookUpTable erstellen, die meine Messwerte enthält.

Das ist aber keine LUT, denn die ist eigentlich konstant. Du willst 
einen Puffer für deine Messwerte.

>Ich möchte dann je nach Wandlerwert in die LUT reingehen, und die
>Spannung raussuchen lassen...

Warum das? Musst du soooo schnell wandeln? Eine Umrechnung per Funktion 
ist WSENTLICH sparsamer im RAMverbrauch.

>Wenn ich die LUT 2000 Elemente lang mache, dann geht es.

Das sind bei 2 Byte/Element bereit 4000 Byte.

>Das komische ist aber auch, dass, wenn ich mein "richtiges" Programm
>nehme,und dann die LUT 4096 lang machen möchte, zeigt er mir folgenden Fehler:

Wer des Englischen halbwegs mächtig ist erkennt, dass da irgendwas zu 
gross ist. Dein Array?

MfG
Falk

von Mathias U. (munter)


Lesenswert?

Gut, dass ist mir zwar klar, bringt mich aber zu der Erkenntnis, dass 
ich die LUT sicher nicht im RAM, sondern doch eher im Flash abspeichern 
sollte... ;-)
Davon hat er jetzt 55kB.

Wie ich da rangehe, weiss ich zwar *noch nicht, aber evtl. hat ja einer 
noch solch einen genialen Einfall! (Das mit dem Ram war wirklich ein 
Wink mit dem Pfahl, der gewirkt hat!)

*edit @falk: arrg, falsch ausgedrückt! Die LUT soll nicht die Messwerte 
enthalten!
Es soll wirklich schon eine richtige LUT sein.
Also quasi: Ich bekomme einen Wandlerwert. Dieser Wandlerwert 
repräsentiert eine Spannung. Diese Spannung steht in der LUT. Mit dem 
Wandlerwert gehe ich in die LUT und hole mir die Spannung raus...

Die Spannungen in der LUT sind jetzt erstmal nur zum testen! In meiner 
richtigen LUT stehen dann dB-Werte drin...
Und ich denke, dann spare ich schon Rechenzeit, weil ich die ja dann 
nicht jedesmal neu berechnen muss

mathias

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Die Spannungen in der LUT sind jetzt erstmal nur zum testen! In meiner
>richtigen LUT stehen dann dB-Werte drin...

Das kann man auch berechnen.

>Und ich denke, dann spare ich schon Rechenzeit, weil ich die ja dann
>nicht jedesmal neu berechnen muss

Sicher, aber wieviele Wandlungen pro Sekunde willst du denn machen?

MFG
Falk

P.S. Wie man ein Array beim MSP430 in den Flash verlegt weiss ich jetzt 
auch nicht.

von Christian R. (supachris)


Lesenswert?

Alsooooo...mit
1
const WORD LUT[2055];

Legst du dir die LUT in den FLASH-Speicher.

Dann hast du bei deiner Füllerei allerdings ganz schönen Blödsinn 
drinne:
1
LUT[j] = (WORD)(j*2500.0/4096.0);

Was soll denn das bedeuten? Das sind ja dann irgendwie Float-Zahlen, die 
der Compiler erst ma umrechnet....
Da du aber die LUT im Flash haben willst, geht das so schon mal sowieso 
nicht.

du musst die LUT gleich bei der Deklaration füllen:
1
const WORD LUT[5] = {100, 256, 2035, 0, 4};

Mal als Beispiel für eine 5 Worte große LUT.


Bist du sicher, dass eine Berechnung viel länger dauern würde? Wie 
schnell tastest du denn ab? Mit einer 2 oder 3 stufigen Näherung kannst 
du ziemlich schnell und relativ genau auch solche Potenzen ausrechnen. 
Mathematik II: Reihenentwicklung.
Dann noch den Hardware-Multiplizierer benutzt, am besten in der MACS 
Betriebsart, und du kommst rasend schnell zu deinem Ergebnis ohne massig 
Speicherplatz-bedarf.
Allerdings ist da der Einsatz des Gehirnes mehr gefordert.....

von Mathias U. (munter)


Lesenswert?

Falk Brunner wrote:

> Das kann man auch berechnen.
> ...
> Sicher, aber wieviele Wandlungen pro Sekunde willst du denn machen?

Sicher kann man das auch berechnen, aber ich möchte jetzt mal eine LUT 
erstellen. (auch zum Lernen!)
Ob das an dieser Stelle nötig oder sinnvoll ist, sei mal dahigestellt.
Aber ich finde es schon sinnvoll, lieber ein Array EINMAL zu füllen, als 
bei jedem neuen Wandlerwert meine Werte zu berechnen ( log10, 
Potenzieren...).
Da glaube ich, ist es einfacher den MSP mal schnell in einer Tabelle 
nachschauen zu lassen...

Wandeln möchte ich dann alle 125ms also 8* pro sek..

Wie man ein solches Array jetzt in den Flash reinkriegt wäre die Lösung.
Aber ich weiss halt auch noch nicht genau wie...bin halt Anfänger.

Hat einer ne Idee?
danke

von Falk B. (falk)


Lesenswert?

@ Mathias U. (munter)

>Sicher kann man das auch berechnen, aber ich möchte jetzt mal eine LUT
>erstellen. (auch zum Lernen!)

OK.

>Wie man ein solches Array jetzt in den Flash reinkriegt wäre die Lösung.
>Aber ich weiss halt auch noch nicht genau wie...bin halt Anfänger.

Siehe das letzte Posting von Christian R.
Die Werte brechnet man sinnvollerweise mit Excel und lässt sich am 
besten per Makro den Quelltext erzeugen.

MFG
Falk

von Christian R. (supachris)


Lesenswert?

Lies doch mal mein Posting. Da steht doch alles drin.

Außerdem musst DU das Array füllen, wenn´s im Flash ist. Nicht der 
Compiler und nicht der MSP430. Die Werte müssen alle zur Kompilierzeit 
bekannt und konstant sein.

von Klugscheisser (Gast)


Lesenswert?

Warum soll ein Compiler nicht die Konstanten ausrechnen können?
Das kann ja schon jeder doofe^H^H^H^H^Hbessere Assembler.

timfrq  set  80000000.0/2.0/@cvf(timcnt)
Fs  set  1000.0*@cvf(smplrt)/@cvf(lfos)/@cvf(skipcnt)
  msg  'timfrq:',timfrq,'  Fs:',Fs
            ;Specify sampling frequency.
PI  set  2.0*@asn(1.0)      ;Compute PI as 2.0*arcsin(1.0)
factor  set  PI/180.0      ;Multiplier for degrees to radians

s_coef  macro  f

_freq  set 
@pow(lfomax/lfomin,@cvf((f/phases)*phases)/@cvf(lfos-1))*lfomin

von Falk B. (falk)


Lesenswert?

@ Klugscheisser (Gast)

>Warum soll ein Compiler nicht die Konstanten ausrechnen können?
>Das kann ja schon jeder doofe^H^H^H^H^Hbessere Assembler.

Na dann scheib mal ein paar Makros für den C-Compiler zu DIESEM Problem.

MfG
Falk

von Mathias U. (munter)


Lesenswert?

Danke Christian & Falk.
Hab da smit der LUT jetzt mal so gemacht.
Hab sie selber initialisiert...war etwas tricky, da ich mich mit Makros 
in Excel nicht auskenne, aber es geht jetzt!

Danke

von Dieter Stotz (Gast)


Lesenswert?

Hallo,

zunächst ist mal zu überlegen, was Dir eine LUT bringen soll. Wenn 
tatsächlich keine Berechnung (sondern nur noch ein Vergleichen) 
stattfinden soll, dann benötigst Du sehr viele Werte in der LUT. Wenn Du 
damit nur eine  Nichtlinearität ausgleichen willst, kommt man ggf. mit 
weniger Werten aus, das erfordert dann aber nach dem Auslesen der 
benachbarten LUT-Werte noch eine Interpolationsrechnung.

LUT hat gegenüber Berechnung schon Vorteile, vor allem, wenn man per 
Abgleichdaten Werte in der LUT beeinflussen möchte. Aber gerade wenn die 
o. g. Interpolation nötig ist, muss man sich überlegen, ob eine laufende 
Berechnung nicht genauso den Zweck erfüllt. Nur um einen Prop.-Faktor zu 
ersetzen, dafür setzt kein Mensch eine LUT ein.

Gruß

Dieter

von Tobias K. (kurzschluss81)


Lesenswert?

Also generel ist es möglich werte während des Betreibs in den Flash zu 
schreiben. es ist zwar etwas hakelig da dazu der Prozessor aus sein 
muss, du due richtige Schreibfrequenz brauchst, du aufpassenmaust das du 
nicht grad den bereich beschreibst wo dein Programm drin ist ... .
aber generell ist es möglich. Für welchen zwek auch immer

von Mathias U. (munter)


Lesenswert?

Hallo, ich muss nochmal was zur Effektivität von LUT's fragen.

Hier mal ein einfaches Beispiel. Ich möchte einfach nur einen Wert auf 
der als linear angenommenen Wandlerkennlinie des MSP ermitteln.
Das kann ich ja einmal über die lineare Formel machen, oder halt die 
Werte in einer LUT ablegen.
Ich denke, hier bringt die Anwendung einer LUT nicht so wahnsinnig viel, 
richtig?
Mal abgesehen davon, dass ich ja die ganzen Multiplikationen auf dem 
MSPF1612 hardware-mäßig machen könnte.

wert = lut_1[adcwert];
oder
wert = 100*(anstieg*adcwert + y_schnitt);

Aber wie sieht die Sache beim Potenzieren aus?
Die beiden Varianten machen unten das gleiche. Aber welche Version wird 
effektiver sein?
In der LUT stehen die Werte drin, die halt mit pow(...) berechnet 
werden.

summe_wert = summe_wert + lut_pow[(wert-6640)/10];
oder
summe_wert = summe_wert + pow(10, (0.001*wert));

Bringt an dieser Stelle eine LUT was, oder ist die Berechnung 
unwesentlich langsamer?

Also der Quellcode soll jetzt nur die grundsätzliche Funktion 
darstellen...

von szimmi (Gast)


Lesenswert?

Hiho,
also bei float-Operationen ohne HW-Multiplizierer bringen die LUT's 
schon etwas, denke ich. Kommt halt auf die Funktion an.
Miss doch einfach mal die Laufzeiten mit LUT und Bibliotheksfunktion und 
vergleiche. Bei pow wird sich da ein signifikanter Unterschied ergeben. 
Wenn ich mich dunkel entsinne, dauert eine float-Division über die 
Bibliothek bei einem MSP430F1232 (hat keine HW-Multiplizierer) mit 5MHz 
CPU-Clock (MCLK) einige 100µs.

von Uhu U. (uhu)


Lesenswert?

Für eine Gerade brauchst du keine LUT - das geht mit der 
Geradengleichung genausogut - die lineare Interpolation, die du zwischen 
den Punkten machen mußt, ist letztlich nichts anderes, als die 
Berechnung einer Geraden.

Wenn du allerdings potenzieren mußt, sieht es anders aus. Da lohnt sie 
sich mit Sicherheit. Du mußt nur den Abstand der Stützpunkte so wählen, 
daß der maximal tolerierbare Fehler nicht überschritten wird. Die 
Zwischenwerte zwischen den Stützpunkten berechnest du dann mit linearer 
Interpolation.

Auch solltest du dir überlegen, ob sich das Problem mit vertretbarem 
Aufwand mit skalierter Integerarithmetik lösen läßt.

Ein schönes Werkzeug, um solche Probleme zu untersuchen, ist Maxima.

von Mathias U. (munter)


Angehängte Dateien:

Lesenswert?

Hallo, ich habe jetzt mal die Laufzeiten gemessen, die die Funktionen 
und das "Nachschauen" in der Lut brauchen...
Es kommt mir etwas komisch vor, oder ich versteh die Angaben einfach nur 
nicht...

hier mal mein Programm:
1
#include "owndef.h"
2
#include "init_mcu.h"
3
#include "math.h"
4
#include "lut_ueff_02.h"
5
#include "lut_upeak_02.h"
6
#include "lut_pow_02.h"
7
// ************************************************************************************************
8
// funktionsprototypen
9
// ************************************************************************************************
10
//void wait(WORD i);
11
WORD linear_normal(WORD adcwert);
12
WORD linear_lut(WORD adcwert);
13
QWORD pow_normal(WORD Lequ);
14
QWORD pow_lut(WORD Lequ);
15
// ************************************************************************************************
16
WORD adcwert        = 0;                            // wandlerwert des 1. kanals (eff)
17
WORD Lequ           = 0;                            
18
WORD Lequ_1         = 0;                            
19
QWORD Summe_Lequ    = 0;
20
QWORD Summe_Lequ_1  = 0;
21
// ************************************************************************************************
22
// ************************************************************************************************
23
// HAUPTPROGRAMM
24
// ************************************************************************************************
25
void main()
26
{
27
  WDTCTL=WDTPW+WDTHOLD;                             // stoppt den WDT
28
  init_mcu();                                       // initialisierungsachen fuer den µcontroller
29
30
  for (;;)                                          // beginn endlosschleife for(;;)
31
  {
32
    //Lequ = LUT_UEFF_02[adcwert];                
33
    Lequ = linear_lut(adcwert);    
34
    //Lequ_1 = (WORD)100*(EFF_ANSTIEG*adcwert + EFF_Y_SCHNITT);
35
    Lequ_1 = linear_normal(adcwert);
36
    
37
    //Summe_Lequ   = (QWORD)LUT_POW_02[(Lequ-6680)/10];
38
    Summe_Lequ = pow_lut(Lequ);
39
    //Summe_Lequ_1 = (QWORD)pow(10, 0.001*Lequ);
40
    Summe_Lequ_1 = pow_normal(Lequ);
41
  
42
    _NOP(); 
43
  }   // ende endlosschleife for (;;)
44
}     // ende main
45
// ************************************************************************************************
46
WORD linear_lut(WORD adcwert)
47
{ Lequ = LUT_UEFF_02[adcwert];
48
  return Lequ;}
49
WORD linear_normal(WORD adcwert)
50
{ Lequ_1 = (WORD)100*(EFF_ANSTIEG*adcwert + EFF_Y_SCHNITT);
51
  return Lequ_1;}
52
QWORD pow_lut(WORD Lequ)
53
{ Summe_Lequ   = (QWORD)LUT_POW_02[(Lequ-6680)/10];
54
  return Summe_Lequ;}
55
QWORD pow_normal(WORD Lequ)
56
{ Summe_Lequ_1 = (QWORD)pow(10, 0.001*Lequ);
57
  return Summe_Lequ_1;}

Folgendes passiert:
Ich rufe die jeweiligen Funktionen auf, die entweder in der LUT 
nachsehen, oder den gewünschten Wert berechnen.
Bei den auskommentierten Sachen handelt es sich um die Realisierung OHNE 
explititen Aufruf einer Funktion.
Aber für das Profiling brauche Ich ja Funktionen.
Hier mal die Angaben, die mir das Profiling ausgeben, einiges wurde 
entfernt:
1
Profiling information
2
3
Flat  %      Acc.    %       Calls  Function
4
Time         Time  
5
6
0     0.00   0       0.00    0      Outside main
7
8
13    0.03   13      0.03    1      linear_lut
9
6     0.02   421     1.06    1      linear_normal
10
11
201   0.51   201     0.51    1      pow_lut
12
6     0.02   11823   29.71   1      pow_normal
13
14
0     0.00   0       0.00    0      wait

Was ist denn die "Flat-Time" genau?
Die "Accumulated-Time" stellt ja die Zeit dar, die die Funktion inkl. 
aller  evtl. Unterfunktionsaufrufe, benötigt, oder?

Das scheint mir aber etwas komisch zu sein, DENN bei der normalen 
Berechnung eines Punktes auf einer linearen Kennlinie (linear_lut und 
linear_normal) ist da schon ein Faktor von knapp 32 drinn (421/13).
Da hiesse, hier ist eine LUT 32* schneller. Das kann ich mir gaaanz 
schlecht vorstellen.

Bei der Berechnung von pow liegt der Faktor bei knapp 59 (11823/201).
Ich meine, dass hier ein Vorteil einer LUT ggü. der Berechnung vorliegen 
muss ist klar, aber das der wirklich sooo groß sein soll, kann ich mir 
auch nicht vorstellen...

Wo liegt mein Denkfehler?
danke

edit: Ich habe mal einen Teil aus dem Disassambly angehangen.
Dabei hab ich die Berechnungen nicht mit einzelnen Funktionen, sondern 
gleich direkt in der for(;;) gemacht. (Das sind die auskommentierten 
Sachen)
Man kann deutlich erkennen, das das "Raussuchen" aus einer LUT weiniger 
Schritte benötigt, als das Berechnen der Werte...

von szimmi (Gast)


Lesenswert?

Hiho,
kurze Zwischenfrage, sind EFF_ANSTIEG bzw. EFF_Y_SCHNITT 
Float-Konstanten?
Flat ist denke ich die Zeit ohne Unterfunktionen, Acc die Zeit mit 
Unterfunktionen.

von szimmi (Gast)


Lesenswert?

Ah, die Frage hat sich geklärt. Habe mal Dein Assembler-File ein 
bisschen bearbeitet:
// eff-pegel berechnen
aus kennlinie
0025A4 1C420411 mov.w &adcwert,R12
0025A8 0D43 clr.w R13
0025AA B0128A2C call #?UL_TO_FLT
0025AE 3E40508D mov.w #0x8D50,R14
0025B2 3F40973C mov.w #0x3C97,R15
0025B6 B012262F call #?FLT_MUL
0025BA 3E400080 mov.w #0x8000,R14
0025BE 3F408542 mov.w #0x4285,R15
0025C2 B0127231 call #?FLT_ADD
0025C6 0E43 clr.w R14
0025C8 3F40C842 mov.w #0x42C8,R15
0025CC B012262F call #?FLT_MUL
0025D0 B012082D call #?FLT_TO_UL
0025D4 824C0811 mov.w R12,&Lequ_1

Hier siehst Du was die Laufzeit für die linear_normal verursacht.
Er ruft folgende Subroutinen aus der Bibliothek auf:
1. Cast von Unsigned nach Float (adc--> float)
2. erste float-Multiplikation (adc * EFF_ANSTIEG)
3. float- addition (+ EFF_Y_SCHNITT)
4. zweite float-Multiplikation (*100)
5. Cast von float nach unsigned
So gesehen sind die 421 als acc. time noch "gnädig". Zeitkritische 
Sachen im float mit nem Prozessor ohne Gleitkommaeinheit waren schon 
immer spannend :-)

von Christian R. (supachris)


Lesenswert?

Miss doch die Zeiten mal direkt auf dem Chip. Oszi an ein Pin und dann 
los. Ich wette, es kommt in etwa das gleich Verhältnis raus.

Wie schon gesagt, dauern Float-Operationen ewig auf dem kleinen Teil. 
Ist ja klar, ohne FPU. Da ist die LUT deutlich im Vorteil.

von Mathias U. (munter)


Lesenswert?

Gut, ich danke Euch! Das mit der fehlenden FPU und der 
floating-point-rechnung leuchtet jetzt auch ein. Mir kamen die Werte nur 
etwas zu hoch vor, aber ich denke, es passt schon so.

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.