www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Brauche logische Hilfe bei Problemansatz


Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Leute,

ich habe in meinem Programm eine Bitmaske definiert.
Bsp: 0010 1101

Jede Stelle steht dafür, ob ein gewisser Messwert gemessen werden soll 
oder nicht( z.b. 101 = Strom messen, Spannung nicht, Temeratur messen)

Jetzt habe ich mir eine Display Anzeige ausprogrammiert und bei dir 
möchte ich diese Werte auch anzeigen lassen können. Also man sagt man 
will den 3ten gemessen Wert haben, wie komme ich dann auf diese Stelle? 
Im oberen Beispiel wäre ja:
Bitmaske: 0010 1101
Daten gemessen: 4
Mögliche Auswahl: 0-3
=> Auswahl: 3

Dann müsste ich intern im µC wissen, dass es Stelle 5 in der Bitmaske 
ist. (Wenn man mit Stelle 0 zu zählen beginnt)

Mir würden zwar ein paar Lösungsansätze einfallen, allerdings nur welche 
mit sehr langem Code. Wenn jemand eine elegante Lösung dafür hätte, wäre 
ich wirklich sehr dankbar! Anscheinend stehe ich gerade auf der Leitung 
:/

Autor: Stefan_KM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Bitmaske: 0010 1101
> Daten gemessen: 4
> Mögliche Auswahl: 0-3
> => Auswahl: 3

Kannst du dasnochmal näher erläutern?

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also irgendwie versteh ich das nicht... die Bitmaske bestimmt doch nicht 
das was gemacht wird. Sondern entsprechend dem, was gemacht wird, wird 
die Bitmaske gesetzt :-)

Autor: cri (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal ein Ansatz:

char foo(char auswahl) {
 unsigned char i,t;
 for(i=0,t=1;t&&auswahl>=0;t<<=1,i++)
        if (bitmask&t&&!auswahl--) return i;
 return -1;
}

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also über den PC über USB kann ich die Bitmaske einstellen.

Je nach der Bitmaske messe ich dann die Werte und speichere sie in einem 
Array ab. (erster abgespeicherte Wert entspricht der ersten 1 in der 
Bitmaske).

Bitmaske: 0010 1101

Hier werden also 4 Werte gemessen. (weil 4 einser drinnen sind). Die 
Stelle gibt an, welche Werte gemessen werden. Z.b. Stelle Null bedeutet, 
dass Spannung gemessen wird. Stelle eins bedeutet, dass Strom gemessen 
wird usw.

Also werden hier 4 Werte gemessen, also kann man auf dem Display auch 4 
Werte anzeigen. Über einen Joystick kann man eine Variable 
inkrementieren bzw. dekrementieren im Bereich von 0-3 (Weil es ja 4 
Werte sind).
Wenn diese Variable jetzt 3 ist, dann soll der 4te gemessen Wert 
angezeigt werden. Bei der Bitmaske 00101101 wäre dass dann 00!1!0 1101 
(der einser zwischen den !) Also wäre dass die Stelle 5 in der Bitmaske.

Wie kann ich in dem programm diese 5 berechnen, indem ich die Bitmaske 
und die Auswahl als Eingabe habe?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich interpretiere mal frei

> Bitmaske: 0010 1101

Du hast 8 mögliche Messwerte.
Davon wurden 4 tatsächlich gemessen.
Nämlich diejenigen auf den Kanälen 0, 2, 3, 5

> Daten gemessen: 4

OK. Dies deshalb, weil 4 Kanäle freigeschaltet wurden

> Mögliche Auswahl: 0-3
> => Auswahl: 3

Finde ich keine gute Idee.
Je nachdem, wieviele Kanäle freigeschaltet sind, hat dann die
Temperatur einmal die Nummer 1, dann wieder die Nummer 2 usw.

Frag deinen Benutzer nach dem Kanal von dem er den Messwert haben 
möchte. Diese Nummer bleibt immer gleich und ändert sich nicht.

> Mir würden zwar ein paar Lösungsansätze einfallen, allerdings nur
> welche mit sehr langem Code.

Zeig doch mal was du ausbaldobert hast

> Wenn jemand eine elegante Lösung dafür hätte, wäre
> ich wirklich sehr dankbar!

Ich würd mir in einem Array die Bitmasken der freigeschalteten Kanäle 
bereit legen. Die Benutzereingabe ist dann Index in dieses Array, aus 
dem ich dann die Kanalnummer des abzufragenden Wertes bekomme
uint8_t Channels[8];
uint8_t NrChannels;

...

  NrChannels = 0;
  for( i = 0; i < 8; ++i ) {
    if( Bitmaske & ( 1 << i ) )
      Channels[NrChannels++] = i;
  }

  .... Benutzer nach einer Zahl zwischen 0 und NrChannels fragen
  ....

  Channel_to_Sample = Channels[ Auswahl ];


Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hol Dir zu Beginn eines Messzyklusses eine Kopie der Maske (um das 
Original nicht zu beschädigen), zähle die Messstellennummer (ist auch 
der Index auf das Array mit Ergebnissen) hoch und schiebe dabei die 
Kopie der Maske nach rechts, wodurch das LSB der Maske im Carry landet. 
Nun entscheidest Du anhand des Carrys, ob Du die Messung ausführst oder 
überspringst.

...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast wrote:

> Hier werden also 4 Werte gemessen. (weil 4 einser drinnen sind).

Ja, und diese 4 Werte legst du in einem Wertearray nacheinander ab. In 
der Reihenfolge in der sie gemessen wurden. Ohne Leerraum dazwischen.

> Wenn diese Variable jetzt 3 ist, dann soll der 4te gemessen Wert
> angezeigt werden. Bei der Bitmaske 00101101 wäre dass dann 00!1!0 1101
> (der einser zwischen den !) Also wäre dass die Stelle 5 in der Bitmaske.

Die brauchst du dann gar nicht mehr.
Wenn der Benutzer den 4.ten Wert sehen will (was auch immer der ist), 
dann zeigst du ihm auch den 4.ten Wert aus dem Wertearray

Autor: Stefan_KM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also mal überlegen.

Du könntest 8mal schieben, und je nachdem nach welchem Schieben eine 1 
herauskommt, weißt du welcher Wert gemessen wurde:

if (Wert & 0x01 == 1)dann wurde Messwert 1 gemessen
if (Wert >> 1 & 0x01 == 1) dann wurde Messwert 2 gemessen
if (Wert >> 2 & 0x01 == 1) dann wurde Messwert 3 gemessen
if (Wert >> 3 & 0x01 == 1) dann wurde Messwert 4 gemessen
.
.
.
if (Wert >> 7 & 0x01 == 1) dann wurde Messwert 8 gemessen

Was hälts du davon?

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja das stimmt schon Karl Heinz, allerdings zeige ich nicht nur den Wert 
an, sondern auch, was dieser Wert bedeutet.

Also auf dem Display auf der ersten Zeile den Namen, z.b. "Voltage:" und 
bei der 2ten Zeile dann den Wert.
So wie du es geschrieben hast, hatte ich auch vor den Wert auszugeben. 
Allerdings habe ich die Registername in einem Array auch abgespeichert 
und für diese benötige ich dann die Stelle in der Bitmaske (weil die dem 
Index des Arrays entsprechen)

Die anderen Antworten kann ich mir erst später durchlesen, da ich gerade 
unterricht habe. Danke schonmal

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan_KR, meine Bitmaske ist 64 Bit groß... So wie du es gelöst 
hättest, wäre auch mein Ansatz gewesen =) Es muss doch auch eine 
elegantere Lösung geben? (vielleicht passt eh schon eine hier gepostet 
lösung, ich schaue es mir dann in 1 Stunde an)

Autor: Tobias K (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schon mal was von Bitfeldern gehört.
also z.B.
typedef struct
{
   uint8 Bit0:1;
   uint8 Bit1:1;
   uint8 Bit2:1;
   uint8 Bit3:1;
   uint8 Bit4:1;
   uint8 Bit5:1;
   uint8 Bit0:1;
   uint8 Bit0:1;
}Bitfeld1;

kann ma auch ganz gut hier nachlesen
http://www.cpp-tutor.de/cpp/le08/le08_03.htm

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast wrote:
> Ja das stimmt schon Karl Heinz, allerdings zeige ich nicht nur den Wert
> an, sondern auch, was dieser Wert bedeutet.

Ja und?

Die Struktur ist schon erfunden
struct Result
{
  long Value;
  char Caption[20];
  char Unit[5];
};

> So wie du es geschrieben hast, hatte ich auch vor den Wert auszugeben.
> Allerdings habe ich die Registername in einem Array auch abgespeichert
> und für diese benötige ich dann die Stelle in der Bitmaske (weil die dem
> Index des Arrays entsprechen)

Nicht wenn du dir beim Messwert selber mithilfe der Struktur die 
relevanten Informationen dazuspeicherst. Anstalle eines simplen longs 
für den Messwert, ist das Ergebnis einer Messung ein Result-Objekt, 
welches Informationen darüber enthält, mit welcher Überschrift das 
Ergebnis anzuzeigen ist, welche Einheit es hat etc.

struct ChannelDescription
{
  char Caption[20];
  char Unit[5];
};

struct ChannelDescription Channels[] = 
{ { "Temperatur",  "°C" },
  { "Strom",       "A" },
  { "Volt",        "V" },
  { "Druck",       "hPa" },
  { "Luftfeuchte", "%" },
  { "",            "" },
  { "",            "" },
  { "",            "" },
  { "",            "" }
};

struct Result
{
  long Value;
  struct ChannelDescription* pChannel;
};

#define MAX_NR_RESULTS 8

struct Result Results[MAX_NR_RESULTS];
uint8_t NrResults;

...

  //
  // die freigegebenen Kanäle sampeln
  //
  NrResults = 0;
  for( i = 0; i < MAX_NR_RESULTS; ++i ) {
    if( BitMaske & ( 1 << i ) ) {
      Results[NrResults].Value = SampleAdc( i );
      Results[NrResults].pChannel = &Channels[i];
      NrResults++;
    }
  }

  // ... Benutzer nach einer Zahl zwischen 0 und NrResults fragen
  // -> Answer

  //
  // und die Daten für diesen Kanal ausgeben
  //
  printf( "%s: %d %s", Results[Answer].pChannel->Caption,
                       Results[Answer].Value,
                       Results[Answer].pChannel->Unit );

  ...

Anstelle von dem 1 << i möchtest du vielleicht noch eine (simple) 
Vereinfachung anbringen. 1 << i ist auf einem AVR eine teure Operation.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmmm jetzt habe ich selbst eine Lösung gecodet und jetzt kommst du mit 
dem daher :D Stimmt, dein Lösungsweg ist elegant, vielleicht schreibe 
ich es noch so um. (zumindest ist deiner viel besser zu lesen) Der 
Vollständigkeit halber schreibe ich mal meine Lösung her:
// Berechnung von benötigtem Registerstrings-Index
        for(int i = 0; i < 32; ++i)              // Durchlaufen aller Stellen
        {
          if(((measurement_mask_low >> i) && 0x01) == 1)  // Kontrolle ob an der Stelle 1 steht
          {  
            if(level_one != measured_counter)      // Wurde die Stelle noch nicht gefunden?
            {
              measured_counter++;            // Erhöhe, da eine weitere "1" gefunden wurde
            }
          }
        }
        for(int i = 0; i < 7; ++i)              // Durchlauf der verbleibenden Stellen
        {
          if(((measurement_mask_high >> i) && 0x01) == 1)  // Kontrolle ob an der Stelle 1 steht
          {  
            if(level_one != measured_counter)      // Wurde die Stelle noch nicht gefunden?
            {
              measured_counter++;            // Erhöhe, da eine weitere "1" gefunden wurde
            }
          }
        }

        // Ausgabe
        display_write_text(registernames[measured_counter], measured[level_one]);

Noch eine kurze Erklärung:
In level_one steht die Auswahl von dem User (ich weiß, schlechter 
Variablenname...)
In registernames sind die Indexe konstant durchnummeriert von 0-39
In measured sind die Indexe variable. Also [2] bedeutet einmal Spannung, 
ein anderes mal Strom.

Würdet ihr mir trotzdem raten, das Programm so wie Karl Heinz es gesagt 
hat abzuändern? Wie schaut es mit Speicherverbrauch aus? Wäre da ein 
großer Unterschied zwischen den beiden Varianten? (bzw. in der Laufzeit 
des Programmes?)

Danke schonmal

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast wrote:
> Hmmm jetzt habe ich selbst eine Lösung gecodet und jetzt kommst du mit
> dem daher :D

:-)

> Würdet ihr mir trotzdem raten, das Programm so wie Karl Heinz es gesagt
> hat abzuändern? Wie schaut es mit Speicherverbrauch aus?

Wieviele Kanäle hast du?
Genausoviele Pointer hab ich zusätzlich im Array stehen :-)
Das ist alles.
Aber dafür ist die Ausgabe eine simple Sache :-)
(Dir ist hoffentlich aufgefallen, dass mein Code auch das Abfragen der 
einzelnen ADC Kanäle umfasst. Die Auswertung anzeigen ist in einem 
einzigen printf mit ein paar Array-Indexoperationen zusammengefasst :-)

PS: Du kannst da in der struct ChannelDescription auch die notwendigen 
Umrechnungskonstanten für die einzelnen ADC Kanäle unterbringen. Nur mal 
so als Hinweis, wozu man die Struktur zu noch so allem ausnutzen kann um 
das Programm zu vereinheitlichen.
struct ChannelDescription
{
  char Caption[20];
  char Unit[5];
  double Faktor;    // anzuzeigender_Messwert = Faktor * ADC + Offset
  double Offset;
};

struct ChannelDescription Channels[] = 
{ { "Temperatur",  "°C",   102.0 / 1024, -32.0 },   // ADC: 0 -> -32°C, ADC: 1024 -> 70°C
  { "Strom",       "A",      1.0 / 1024,   0.0 },   // 0 bis 1 Ampere
  { "Volt",        "V",      5.0 / 1024,   0.0 },   // 0 bis 5 Volt
  { "Druck",       "hPa", 1000.0 / 1024,   0.0 },
  { "Luftfeuchte", "%",    100.0 / 1024,   0.0 },   // 0 bis 100%
  { "",            "",              0.0,   0.0 },
  { "",            "",              0.0,   0.0 },
  { "",            "",              0.0,   0.0 },
  { "",            "",              0.0,   0.0 },
};

struct Result
{
  double Value;
  struct ChannelDescription* pChannel;
};

.....

  //
  // die freigegebenen Kanäle sampeln
  //
  NrResults = 0;
  for( i = 0; i < MAX_NR_RESULTS; ++i ) {
    if( BitMaske & ( 1 << i ) ) {
      Results[NrResults].Value = SampleAdc( i ) * Channels[i].Faktor + Channels[i].Offset;
      Results[NrResults].pChannel = &Channels[i];
      NrResults++;
    }
  }

  ....

Wenn du in Zeitnot bist, oder dir Floating Point aus Speichergründen 
nicht leisten kannst oder willst, müsste man das auf Festkommaarithmetik 
umbauen. Aber das Prinzip bleibt das gleiche :-) Es gibt eine 
Beschreibung für jeden einzelnen ADC Kanal, die alles enthält, was für 
diesen Kanal interessant ist. Beim Messergebnis steht dabei aus welchem 
Kanal es gewonnen wurde (über den Pointer; könnte man auch als Index 
ausführen, wenn du dir den Speicher für n Pointer nicht leisten kannst 
oder willst) und damit steht dann auch bei jedem Messergebnis die 
komplette Palette an Informationen zur Verfügung die benötigt wird, um 
die ADC Ergebnisse zu manipulieren (inkl. Umrechnung des ADC Wertes in 
einen für den Leser vernünftigen Wert). Alle Sensoren werden gleich 
behandelt, es gibt keinen Unterschied zwischen den Sonsoren. Jeder 
Sensor kann für sich kalibriert werden (indem die Umrechnungskonstanten 
beim Kanal kalibriert werden), einen neuen Kanal einfügen ist trivial, 
einen Kanal herausnehmen ist trivial, feststellen welche ADC Kanäle 
überhaupt mit einem Sensor bestückt sind ist trivial möglich (zb. über 
die Umrechnungskonstanten), etc., etc.

Unterschätze nie die Möglichkeiten die sich eröffnen, wenn man Dinge die 
zusammengehören auch beisammen lässt (zb. in dem man sie in einer 
Struktur gruppiert)

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sehr schön danke, wieder was dazugelernt :D
Aber da ich meine Werte über einen Messchip bekomme, benötige ich den 
ADC nicht (zumindest nicht für diese Zwecke ;) )

Ich werde alles mal so lassen, weil mein atmega32 zu 95,4% voll ist und 
ich sowieso in extremer Zeitnot bin.

Somit bin ich mit meinem Projekt fertig :D Sobald ich alles nocheinmal 
kontrolliert habe und eine Dokumentation davon fertiggestellt habe, 
werde ich das Projekt hier raufladen. (ich freu mich schon auf 
Verbesserungsvorschläge dann eurerseits) =)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.