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
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
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
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.
Autor: Karl heinz Buchegger (kbuchegg) (Moderator) Datum: 16.08.2010 13:37 ^^ ^^ His Leetness der Moderator :D mfg mf
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.
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
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.
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
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.
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 :-)
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.