Forum: Mikrocontroller und Digitale Elektronik Zuordnungstabelle


von Julia (Gast)


Lesenswert?

Hallo,

ich stehe vor folgendem Problem.
Ich messe ADC Werte (10-bit) im mV Bereicht.
Nun möchte ich, je nach ADC Wert Werte aus einer Tabelle lesen, z.B.
Von...bis
0...330mV Wert 5 lesen
331mV-500mV Wert 10 lesen
501mV-700mV Wert 4 lesen
usw.

Denkbar ist so eine Tabelle mit ca. 100-200 Werten.
Mit diesem Wert wird dann einem Timer Wert aufaddiert, sodass je nach 
Einstellung etwas "geregelt" früher oder später erfolgen kann.
Nun ist meine Frage, wie adressiere ich so eine Tabelle am besten bzw. 
wie gestaltet man da einen Zugriff darauf in C? Ich habe daran gedacht, 
einen Array zu erstellen, mit je 1000mV eine Zeile und darunter dann 
einzelne Spalten je nach untergliederung.
Aber der Zugriff ist für mich ein Rätsel, klar man könnte mit unzähligen 
"If-Abfragen" schauen, wo man gerade steht, aber das wäre zeitlich nicht 
gerade vorteilhaft. Daher bin ich irgendwie auf der Suche nach einer 
eleganteren Lösung.

Danke.

VG
Julia

von Thomas (Gast)


Lesenswert?

Man könnte die Werte durch 100 teilen, dann ergäbe das
0-99  0
100-199 1
200-299 2
300-399 3

Dann könnte man ein Array definieren:

MyArray[100] = {5,4,10,...};
Das ganze kann man auch verfeinern wenn man durch weniger als 100 teilt.
Die Werte werden dann so aufgerufen:

unsigned char Ergebnis;
int Wert;  //das sind die Ursprungswerte

Ergebnis = MyArray[Wert/100];

Ist nur beispielhaft, sollte aber vom Prinzip her funktionieren.

Gruß

Thomas

von Achim M. (minifloat)


Lesenswert?

Du stehst vor zwei Problemen:
1. Deine Abstufungen in der Spannung sind willkürlich, also nicht 
gleichmäßig. Das macht die Arithmetische Erfassung schwierig
2. Deine Abstufungen des Wertes, der Ausgegeben werden soll, sind auch 
nicht gleichmäßig.
=>zwei Arrays, wenn es Platzsparend sein soll.

meine Idee hierzu:
1
#define ANZAHL_ABSTUFUNGEN 3
2
3
//0...330mV Wert 5 lesen
4
//331mV-500mV Wert 10 lesen
5
//501mV-700mV Wert 4 lesen
6
//über 700mv wird jetzt auch 4 ausgegeben
7
8
9
uint16_t eingang_mv[ANZAHL_ABSTUFUNGEN] = {330, 500, 700};
10
uint8_t ausgang[ANZAHL_ABSTUFUNGEN] = {5, 10, 4};
11
12
//umwandlungsfunktion
13
uint8_t umwandeln(uint16_t adc_mv)
14
{
15
   uint8_t array_count = 0;
16
17
   while( (adc_mv - eingang_mv[array_count]) > 0 ) 
18
   {
19
      array_count++;
20
21
      //Sicherung gegen zu große ADC-Werte, Abbruch mit return
22
      if (array_count > (ANZAHL_ABSTUFUNGEN - 1))
23
      {
24
         return(ausgang[ANZAHL_ABSTUFUNGEN - 1]);
25
      }
26
   }
27
  
28
   return(ausgang[array_count]);   
29
}

mfg mf

von Karl H. (kbuchegg)


Lesenswert?

Julia schrieb:
> Hallo,
>
> ich stehe vor folgendem Problem.
> Ich messe ADC Werte (10-bit) im mV Bereicht.
> Nun möchte ich, je nach ADC Wert Werte aus einer Tabelle lesen, z.B.
> Von...bis
> 0...330mV Wert 5 lesen
> 331mV-500mV Wert 10 lesen
> 501mV-700mV Wert 4 lesen
> usw.

Dein Haupthindernisgrund sind hier die ungleich großen Intervalle. Wenn 
möglich solltest du diese vermeiden bzw. einen größten gemeinsamen 
Teiler finden, so dass du aus deiner ungleichen Intervallgröße wieder 
eine gleich große in allen Intervallen bekommst, selbst wenn das 
bedeutet, dass du Ausgangswerte duplizieren musst.

Falls das partout nicht geht, dann bleibt dir nichts anderes übrig als 
jeweils ein Tuple zu erzeugen: Startwert des Intervalls und zugehöriger 
Ausgangswert. (Eventuell, wenn Lücken existieren können, müsste man 
Startwert + Endwert + Ausgangswert ein einem Triplet speichern)

Der Ausgangswert wird dann bestimmt, indem das Array durchlaufen wird 
und der zugehörige Ausgangswert aktiv gesucht wird. Das kann in deiner 
Applikation sogar schneller sein, als die direkte Methode, bei der 
gerechnet werden muss (*). Du hast den Vorteil, dass dein Eingangswert 
anscheinend von einem Benutzer kommt und sich damit nicht besonders 
schnell ändern wird. D.h. die Chancen stehen gut, dass der nächste 
Eingangswert mit demselben Tupel/Triplet in seinen Ausgangswert 
'übersetzt' werden kann, wie der vorhergehende bzw. das maximal das 
nachfolgende bzw. vorhergehende Dupel/Triplet zuständig ist und sich die 
Sucherei so in Grenzen hält.

(*) Auch wenn man dazu sagen muss, dass es bei einer Eingabe kaum eine 
Rolle spielt, wenn ein bischen gerechnet werden muss. Das geht 
normalerweise schnell genug.

von Achim M. (minifloat)


Lesenswert?

Autor: Karl heinz Buchegger (kbuchegg) (Moderator)
Datum: 16.08.2010 13:37
                  ^^ ^^
His Leetness der Moderator :D
mfg mf

von Karl H. (kbuchegg)


Lesenswert?

Joachim K. schrieb:

> =>zwei Arrays, wenn es Platzsparend sein soll.

Du musst jetzt den nächsten Schritt gehen und erkennen, dass die beiden 
Arrays nicht voneinander unabhängig sind sondern eine logische 
Verknüpfung zwischen den beiden existiert. So wie Vorname und 
Familienname zusammen einen Namen ergeben, so ergeben hier dein 
eingang_mv und das jeweils korrespondierende ausgang Element einen 
Eintrag in einer Übersetzungstabelle. Die beiden Werte gehören zusammen.

In C drückt man das so aus, dass man sich eine Struktur dafür baut.
1
struct TableEntry
2
{
3
  uint16_t  eingang_mv;
4
  uint8_t   ausgang;
5
};

und dann ein Array aus derartigen Strukturelementen erzeugt
1
#define ANZAHL_ABSTUFUNGEN 3
2
struct TableEntry table[ ANZAHL_ABSTUFUNGEN ] =
3
{
4
  { 330,  5 },
5
  { 500, 10 },
6
  { 700,  4 },
7
};

Jetzt ist beisammen, was zusammen gehört.
1
//umwandlungsfunktion
2
uint8_t umwandeln(uint16_t adc_mv)
3
{
4
  uint8_t array_count = 0;
5
6
  while( (adc_mv - table[array_count].eingang_mv) > 0 )
7
  {
8
    array_count++;
9
10
    //Sicherung gegen zu große ADC-Werte, Abbruch mit return
11
    if (array_count > (ANZAHL_ABSTUFUNGEN - 1))
12
    {
13
      return table[ANZAHL_ABSTUFUNGEN - 1].ausgang);
14
    }
15
  }
16
 
17
  return table[array_count].ausgang;
18
}

PS:
Warum hast du eigentlich keine for-Schleife genommen? Um Arrays 
abzuarbeiten, ist das eigentlich der einfachste und naheliegenste Weg.

Das #define für die Array-Größe würde man gerne wegbringen. Stattdessen 
sollte der Compiler für sich die Anzahl der Einträge abzählen.
Und jetzt siehst du zb auch den Vorteil einer Struktur: Mit der Struktur 
ist es unmöglich, dass du mehr eingang_mv Einträge als ausgang Einträge 
hast. Bei deiner Lösung mit 2 Arrays hast du eine Nebenbedingung mehr, 
auf die der Programmierer aufpassen muss.

von Achim M. (minifloat)


Lesenswert?

Karl heinz Buchegger schrieb:
> Warum hast du eigentlich keine for-Schleife genommen? Um Arrays
> abzuarbeiten, ist das eigentlich der einfachste und naheliegenste Weg.

Keine for-Schleife deswegen, weil man einer zweiten Abbruchbedingung 
hantieren muss und das ganze etwas unübersichtlich wird.
War auch nur eine Skizze eines Lösungsweges.
Wenn man jetzt die Struktur noch PROGMEM-t wird es richtig 
professionell.

Mal sehen was Julia dazu sagt.

mfg mf

von Karl H. (kbuchegg)


Lesenswert?

Joachim K. schrieb:
> Karl heinz Buchegger schrieb:
>> Warum hast du eigentlich keine for-Schleife genommen? Um Arrays
>> abzuarbeiten, ist das eigentlich der einfachste und naheliegenste Weg.
>
> Keine for-Schleife deswegen, weil man einer zweiten Abbruchbedingung
> hantieren muss und das ganze etwas unübersichtlich wird.

Ist nicht so schlimm.
Mit dem vorzeitigen return kann man das ganz gut in den Griff kriegen.

> War auch nur eine Skizze eines Lösungsweges.

Schon klar.
Ich bin auch nur darauf aufgesprungen, weil solche Mehr-Array Lösungen 
zwar verlockend einfach aussehen, aber der Teufel liegt im Detail. 
Strukturen haben noch mehr Vorteile (man denke zb an die Übergabe eines 
Strukturelements an eine Funktion, bei der ganz automatisch 
zusammengehörende Dinge beisammen bleiben, bzw. eine Struktur ganz 
einfach um ein neues Member erweitert werden kann, welches man dann in 
den Funktionen selbstverstädnlich zur Verfügung hat etc.)

> Mal sehen was Julia dazu sagt.

Jep.

Während ich meinen ersten Beitrag geschrieben habe ist mir nämlich der 
Gedanke gekommen, dass man den Index des zuletzt gefundenen 
Tabelleneintrags cachen könnte. Bei der nächsten Abfrage sieht man dann 
dort als erstes nach und die Chancen werden dann gar nicht schlecht 
stehen, dass man einen Treffer landet.
Und wenn nicht, geht man Tabelleneinträge nach vorne/hinten, je nach 
Eingangswert, bis man wieder einen Treffer hat.
Ist gar nicht so viel Aufwand, solange die Tabelleneinträge nur 
aufsteigend nach Eingangswert sortiert sind.

von Julia (Gast)


Lesenswert?

Hallo,

vielen Dank, das sind schonmal sehr gute Ideen, die mich weiterbringen.
Von ein Verbund (struct) habe ich schon gehört bzw. mit gearbeitet, aber 
das war damals auf C++ am PC und nicht für den µC. Sollte ja aber sich 
nicht wirklich unterscheiden.
Das struct werde ich auf jedenfall anwenden. Mir persönlich wäre 
allerdings eine direkte Zuweisung an den jeweiligen Index durch eine 
kleine einfache Rechnung lieber, falls doch mal etwas zeitkritisches 
erfolgen muss, könnte die Suche länger dauern.
Aber trotzdem schon einmal vielen Dank.

Wäre es eine große Erleichterung, wenn ich die Eingangswerte gleichmäßig 
Abstufe:
100mV
200mV
300mV
...

VG
Julia

von Karl H. (kbuchegg)


Lesenswert?

Julia schrieb:

> Wäre es eine große Erleichterung, wenn ich die Eingangswerte gleichmäßig
> Abstufe:
> 100mV
> 200mV
> 300mV
> ...

Natürlich.
Weil dir dann ja Spannung/100 direkt den Arrayindex gibt unter dem du 
den Ausgangswert findest. Das ist ja gerade der Trick bei Arrays, dass 
man den Index auch berechnen kann. Sonst könnte man ja gleich 200 
unterschiedliche Variablen mit eindeutigen Namen benutzen :-)

Wenn du nett zu deinem µC sein willst, dann machst du nicht Vielfache 
von 100 sondern vielfache von zb 128 (2-er Potenz). Denn das dividiert 
sich auch auf einem µC sehr leicht. Und wenn es für deinen Benutzer egal 
ist, dann sparst du deinem µC damit ein wenig Arbeit.

Du solltest im übrigen aber in die umgekehrte Richtung umrechnen. Also 
nicht deinen ADC Wert in Millivolt umrechnen, sondern die Millivolt bzw 
deine Abstufung in ADC Einheiten umrechnen und dann direkt mit dem Wert 
aus dem ADC, geeignet skaliert, in die Tabelle gehen um deinen 
Timer-Addierwert zu erhalten.

von Julia (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wenn du nett zu deinem µC sein willst, dann machst du nicht Vielfache
> von 100 sondern vielfache von zb 128 (2-er Potenz). Denn das dividiert
> sich auch auf einem µC sehr leicht. Und wenn es für deinen Benutzer egal
> ist, dann sparst du deinem µC damit ein wenig Arbeit.

Als vielfaches von 2 ist eher ungeeignet, da sich das später sehr 
schnell und stark potenziert (ab 2^10= 1024, der nächste Wert dann erst 
wieder bei 2^11=2048, usw.), das wäre zu grob.
Aber ich werde mal versuchen, das in 100er Schritte zu packen.

Dankeschön :-)

von Karl H. (kbuchegg)


Lesenswert?

Julia schrieb:
> Karl heinz Buchegger schrieb:
>> Wenn du nett zu deinem µC sein willst, dann machst du nicht Vielfache
>> von 100 sondern vielfache von zb 128 (2-er Potenz). Denn das dividiert
>> sich auch auf einem µC sehr leicht. Und wenn es für deinen Benutzer egal
>> ist, dann sparst du deinem µC damit ein wenig Arbeit.
>
> Als vielfaches von 2 ist eher ungeeignet, da sich das später sehr
> schnell und stark potenziert (ab 2^10= 1024, der nächste Wert dann erst
> wieder bei 2^11=2048, usw.), das wäre zu grob.

Ähm.
Ich meinte die Differenz von einer Stufe zur nächsten

> Aber ich werde mal versuchen, das in 100er Schritte zu packen.

Also

  0            ( = 0 * 128 )
  128          ( = 1 * 128 )
  256          ( = 2 * 128 )
  384          ( = 3 * 128 )
  512          ( = 4 * 128 )
  640          ( = 5 * 128 )
  .....



Hast du eine Zahl, dann dividierst du einfach durch 128 und erhältst den 
Index im Array, an dem du nachsehen musst.

Wenn du deinen ADC Bereich von 0 bis 1023 in zb 128 Schritte 
unterteilst, dann ist jeder Schritt 8 Einheiten breit. Deine Tabelle ist 
dann

   8      0
  16      5
  24      9
  32     ....

Den ADC Wert nimmst du her, dividierst durch 8 (geht schnell da 2-er 
Potenz) und benutzt das als Index in deine Tabelle um die 0, 5, 9, ... 
daraus zu erhalten, je nach ADC Wert.

von Julia (Gast)


Lesenswert?

So, ich bräuchte nur nochmal kurz eine Bestätigung meiner Gedanken =)
Ich habe mich nun dazu entschlossen, die Ergebnisse in 100mV Schritten 
anzugeben, bzw. hier noch einen Teilungsfaktor 100 einzuführen, d.h.
0mv=    0
100mV = 1
200mV = 2
300mV = 3

Durch den Teilungsfaktor erhalte ich dann meine Stelle in einem 
eindimesionalen array, der so aussehen könnte.

#define max_anzahl=10
uint8_t array [max_anzahl] = {5,10,4,5,6,10,5,6,7,8};

So ordne ich dann jedem 100er mV Wert direkt an seiner Stelle den 
passenden Wert zu.

Ist diese Lösung elegant?

VG
Julia

von Karl H. (kbuchegg)


Lesenswert?

Julia schrieb:

> #define max_anzahl=10
> uint8_t array [max_anzahl] = {5,10,4,5,6,10,5,6,7,8};

Bei einem maximalen ADC Wert von 1024 brauchst du 11 Werte.

> Ist diese Lösung elegant?

Ist ok.

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.