www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik bitweise Daten auslesen und verarbeiten


Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich habe folgendes Problemchen.

Ich habe vier Byte Daten:

       Bit7   6    5    4    3    2    1  Bit0
       ---------------------------------------
Byte1  ID28 ID27 ID26 ID25 ID24 ID23 ID22 ID21
Byte2  ID20 ID19 ID18 XXXX XXXX ID17 ID16 ID15
Byte3  ID14 ID13 ID12 ID11 ID10 ID09 ID08 ID07
Byte4  ID06 ID05 ID04 ID03 ID02 ID01 ID00 XXXX

Ich möchte diese Daten nun auslesen und speichern, aber dabei die XXXX 
Bits auslassen. Die sollen dann später auf einem Display ausgegeben 
werden.

Ich will Byte 1 + 2 und Byte 3 + 4 zusammen speichern: data1[15], 
data2[16]
Das ganze soll dann wie folgt aussehen:

data1[0]..............data1[13]
  ID28..................ID15

und

data2[0]..............data2[14]
  ID14..................ID0

Das erste Byte verarbeite ich folgendermaßen:
for (i = 7; i >= 0; i--)                           
{
    datatest= RXData[0];
    if (datatest & 0x80 >> i)
        data1[i] = '1';
     else
        data1[i] = '0';   
}

Das zweite Byte verarbeite ich folgendermaßen
for (i = 7; i >= 0; i--)                   
{
    datatest= RXData[1];
    if (datatest & 0x80 >> i)
        data1[i+8] = '1';
    else
        data1[i+8] = '0';
}

Das scheint auch zu funktionieren, aber leider nehme ich da ja die 
beiden XXXX Bits mit, das soll ja nicht sein.

Die beiden anderen Byte verarbeite ich in gleicher Weise.

Kann mir vielleicht jemand helfen, das ganze eleganter zu lösen? Ich 
denke mit den 4 for Schleifen ist das ganz schön umständlich, vielleicht 
geht es ja auch noch kürzer und dabei dann Natürlich die 
gekennzeichneten Bits auslassen.

Wäre klasse, wenn mir dabei jemand auf die Sprünge helfen könnte.

Grüße
Steffen

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das siehn nach einer CAN-ID aus.

Schau dir mal die Bitschibeoperationen an >> und << das im zusammenhang 
mit & (and) und | (or).

Autor: Klugscheisser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Steffen R.

Es wäre vielleicht hilfreich, wenn Du erklären könntest was genau Du Dir 
gedacht hast, als Du das Programm genau so geschrieben hast.

Vor allem scheint mir, das es eigentlich nicht so funktionieren dürfte 
wie Du es beschreibst.

In beiden Routinen geht der Index (i ist sicherlich signed) von 7 bis 0 
geht. D.h. die Bits werden einzeln in datax gespeichert. Wozu soll das 
gut sein? Du hast doch in RXData schon das komplette Byte.

Im übrigen: Wozu wird datatest= RXData[0]; bei jedem 
Schleifendurchlauf ausgeführt. Das Ergebnis ist ja immer das selbe.

Mit dem "ignorieren" ist das so eine Sache. Je nachdem was Du mit den 
Daten nachher anfangen willst, gibt es sicherlich wesentlich einfachere 
Möglichkeiten.

Aber der Knackpunkt ist die Frage, warum und wozu Du die Bits einzeln 
brauchst.

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, 8 Zeilen for-Schleife um je 6-8 Bit zu kopieren? Manchmal ist es 
eleganter, man schreibt was einfach untereinander weg...

Autor: Klugscheisser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Na, ich mache auch mal einen Codevorschlag:

Allerdings werden hier die Bits nicht vereinzelt. (Wozu auch?)
Byte1 = RXData[0];
Byte2 = ((RXData[1] & 0xE0) >> 2) | (RXData[1] & 0x07); // ID20 ID19 ID18 XXXX XXXX ID17 ID16 ID15
Byte3 = RXData[2];
Byte4 = RXData[3] >> 1;  // ID06 ID05 ID04 ID03 ID02 ID01 ID00 XXXX

Autor: Klugscheisser (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu langsam... :-}

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klugscheisser wrote:
> Zu langsam... :-}
Oder zu schnell. Der OP will das Ganze offensichtlich als Array 
speichern (jedes Bit einzeln, was in meinen Augen Speicherverschwendung 
ist). Aber dann geht es so nicht. Deshalb habe ich meinen Beitrag auch 
zurückgezogen.

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

geht um CAN-ID!

Also eventuell muss auch nicht unbedingt jedes bit einzeln gespeichert 
werden.

Bin für jeden Vorschlag offen!

Grüße

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht noch ein Nachtrag. ich speichere die Bitweise, um sie binär 
auf einem LCD auszugeben. Wenn ich sie zusammen speicher wird nicht 
001101001 ausgegeben, sondern ein entsprechender Wert, wenn ich nicht 
irre.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> ich speichere die Bitweise, um sie binär
> auf einem LCD auszugeben. Wenn ich sie zusammen speicher wird nicht
> 001101001 ausgegeben, sondern ein entsprechender Wert, wenn ich nicht
> irre.
Du irrst, denn man kann bei der Umwandlung in (darstellbare) 
ASCII-Zeichen auch eine Basis angeben, und wenn Du als Basis 2 angibst, 
dann sollte auch eine Binärdarstellung herauskommen.

Ansonsten nochmal mein (anscheinend etwas voreilig gelöschter) Vorschlag 
von vorhin:
uint16_t data1, data2;

data1 = ((uint16_t) Byte1 << 8) | (Byte2 & 0xE0) | ((Byte2 & 0x07) << 2);
data2 = ((uint16_t) Byte3 << 8) | (Byte4 & 0xFE);
data1:
ID28 ID27 ID26 ID25 ID24 ID23 ID22 ID21 ID20 ID19 ID18 ID17 ID16 ID15 0 0
data2:
ID14 ID13 ID12 ID11 ID10 ID09 ID08 ID07 ID06 ID05 ID04 ID03 ID02 ID01 ID00 0

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Du irrst, denn man kann bei der Umwandlung in (darstellbare)
>ASCII-Zeichen auch eine Basis angeben, und wenn Du als Basis 2 angibst,
>dann sollte auch eine Binärdarstellung herauskommen.

Das verstehe ich noch nicht so ganz. Ich habe folgend mal meinen Code, 
mit dem ich die daten auf einem LCD ausgeben möchte:
void LCDOutc(unsigned char str)
{
  switch (str)                   Display
  {                             
    case 'ä':
      str = 0xE1;
      break;
    case 'ö':
      str = 0xEF;
      break;
    case 'ü':
      str = 0xF5;
      break;
    case 'ß':
      str = 0xE2;
      break;
    case '°':
      str = 0xDF;
      break;
    case 'µ':
      str = 0xE4;
      break;
  }  
  PORTA=str;                       // str einfach an den Port schicken
  PORTK|=0x05;                     // RS und E high
  PORTK&=~0x02;                    // R/W low
  LCDClock();                      // Befehl ausführen
  chars++;                              
  if(chars==20)
  {
    if(row==1)
    {
      SecondLine();             // zweite Zeile 
    }
    if(row==2)
    {
      ThirdLine();              // dritte Zeile 
    }
    if(row==3)
    {
      FourthLine();             // vierte Zeile 
    }
    if(row==4)
    {
      LCDHome();                 // erste Zeile 
    }                    
  }/* if(chars==20) */   
}

void LCDOuts(unsigned char* str)
{
  while (*str != 0) {LCDOutc(*str++);}
}/* void LCDOuts(char* str) */

Aber wie soll ich dann die Umwandlung anstellen, dass die Daten eben 
schön in einsen und nullen ausgegeben werden?

Grüße
Steffen

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es gibt fertige Bibliotheksfunktionen zur Umwandlung von Zahlen in 
ASCII-Strings. Wenn Du den AVR-GCC mit der AVR-libc benutzt, heißt die 
gesuchte Funktion itoa, steht in der stdlib, und wie die funktioniert 
(also was für Parameter sie erwartet), steht in der 
Bibliotheksdokumentation:
char* itoa (int __val,
      char* __s,
      int __radix   
      )
Die Funktion erwartet drei Parameter, von denen der erste die Variable 
ist, die in einen String konvertiert werden soll (ein unsigned int 
bzw. ein uint16_t ist hier angebracht, wie z.B. oben in meinem 
Beispiel). Der zweite (__s) ist ein Zeiger auf den Anfang des char 
-Arrays (also hier die Adresse des ersten Elements des Strings), in dem 
der String abgelegt werden soll, der dritte (__radix) ist die besagte 
Basis.

Für die Ausgabe brauchst Du zwei char -Strings, und zwar mit jeweils 
mindestens 17 Elementen, damit die 16 Bits plus Nullterminator 
hieneinpassen. Jetzt musst Du nur noch die Funktion z.B. mit
char string1[17];
//....
itoa(data1, string1, 2);
aufgerufen werden, und den Rest macht die Funktion für Dich.

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich habe es jetzt einmal folgendermaßen probiert:
data11= ( RXData[0] << 8) | (RXData[1] & 0xE0) | ((RXData[1] & 0x07) << 2);
data22= ( RXData[2] << 8) | (RXData[3] & 0xFE) >> 1;
 
  data1[14]='\0';                  // String Terminator
  for(i=13; i>=0; i--) {
    data1[i]=(data11 % 2) + '0';   // Modulo rechnen, dann den ASCII-Code 
                                   // von '0' addieren
    data11 /= 2;
  }
  data2[15]='\0';                  // String Terminator
  for(i=14; i>=0; i--) {
    data2[i]=(data22 % 2) + '0';   // Modulo rechnen, dann den ASCII-Code 
                                   // von '0' addieren
    data22 /= 2;
  }       

Das scheint aber irgendwie noch nicht so ganz zu funktionieren. Bei 
folgenden Werten:

RXData[0]=15
RXData[1]=255
RXData[2]=255
RXData[3]=254

sollten die 29 Bit alle eins sein, aber die Ausgabe auf dem Display 
zeigt etwas anderes.

Nach:
data11= ( RXData[0] << 8) | (RXData[1] & 0xE0) | ((RXData[1] & 0x07) << 2);
data22= ( RXData[2] << 8) | (RXData[3] & 0xFE) >> 1;

betragen die Werte:
data11=252
data22=127

Das passt auch genau zu der Displayausgabe:
00000011111100
000000001111111

Ich finde den Fehler einfach nicht. Die Umwandlung itoa scheint richtig 
zu sein, da die 252 ja zu den 11111100 passt und die 127 zu der 1111111.

Da scheint doch noch was bei data110 (RXData[0]...usw. nicht ganz zu 
stimmen....

Jemand eine Idee, woran es liegen könnte?

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

niemand eine Idee? Ich komme da einfach nicht weiter.

Es scheint so, als wenn das ( RXData[0] << 8) und ( RXData[1] << 8) 
nicht
gefressen wird.

Grüße

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen R. wrote:
> Es scheint so, als wenn das ( RXData[0] << 8) und ( RXData[1] << 8)
> nicht
> gefressen wird.
Was glaubst Du wohl, weshalb ich die Ausgangsvariablen in meinem 
Beispiel erst nach unsigned int konvertiert habe? Wenn die Variablen 
nur 8 Bit haben, kannst Du doch wohl durch ein wenig Überlegen drauf 
kommen, was passiert, wenn man die um 8 Stellen nach links schiebt, oder 
nicht? Allerdings fehlt die Definition der Ausgangsvariablen in Deinem 
obigen Post, weshalb man nicht mit Gewissheit sagen kann, ob es daran 
liegt.

Abgesehen davon habe ich keine Ahnung, was Du da mit den Array-Elementen 
rechnen willst. Was soll der Krempel?

Du hast nach der Umwandlung mit der Schieberei zwei 16-Bit-Werte, in 
denen schön sortiert die interessanten Bits stehen. Damit kann man auch 
rechnen. Wenn der ganze Rotz dann ausgegeben werden soll, werden eben 
diese beiden 16-Bit-Werte mit itoa in jeweils einen ASCII-String 
umgewandelt, den auch das Display oder ein Terminalprogramm versteht. 
Mit dem String selber wird nix gerechnet, kein Modulo oder sonst 
irgendwelche Sachen.

Oder soll das da oben eine selbst geschriebene 
Binär-ASCII-Konvertierungsfunktion werden?

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
*Ditsch

Mal schnell was geändert
unsigned int data11,data22;

for(ii = 0; ii <= 3; ii++)
    RXData[ii] = CAN0RXFG[ii];             
                                                   
data11 = ( (unsigned int)RXData[0] << 8) | (RXData[1] & 0xE0) | ((RXData[1] & 0x07) << 2);
data22 = ( (unsigned int)RXData[2] << 8) | (RXData[3] & 0xFE) >> 1;

data1[14]='\0';                        // String Terminator
for(i=13; i>=0; i--) {
    data1[i]=(data11 % 2) + '0';         
    data11 /= 2;
}
data2[15]='\0';                        // String Terminator
for(i=14; i>=0; i--) {
  data2[i]=(data22 % 2) + '0';          
  data22 /= 2;
}   

Sieht schon besser aus, aber irgendwas stimmt noch nicht.

Als Ergebnis habe ich nun 11111111111100(data1) und 
111111101111111(data2)!?

Irgendwo hängts :(

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Oder soll das da oben eine selbst geschriebene
>Binär-ASCII-Konvertierungsfunktion werden?

itoa Nutzung umgehen. Sollte doch das gleiche sein!

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen R. wrote:
> Sieht schon besser aus, aber irgendwas stimmt noch nicht.
>
> Als Ergebnis habe ich nun 11111111111100(data1) und
> 111111101111111(data2)!?
Klar, was soll denn auch das da:
> > data22 = ( (unsigned int)RXData[2] << 8) | (RXData[3] & 0xFE) >> 1;
                                                                 ^^^^
                                                                 ||||

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen R. wrote:
>>Oder soll das da oben eine selbst geschriebene
>>Binär-ASCII-Konvertierungsfunktion werden?
>
> itoa Nutzung umgehen. Sollte doch das gleiche sein!
Du schriebst aber in Deinem Post weiter unten noch was von itoa, weshalb 
ich davon ausging, dass da zusätzlich noch irgendwas mit itoa gemacht 
werden sollte!

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So,

das >> 1 war mal um das letzte Bit rauszuschieben.....

Jetzt funktioniert es, vielen Dank, war ne schwere Geburt. Hab bei den 
Bitschieberein auch was verwechselt.

Gibt es noch eine Möglichkeit bei data1 die beiden Nullen am Ende und 
bei data1 die Null am ende zu entfernen. Die entstehen ja durch die 
Umrechnerrei zu binär.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Steffen R. wrote:
> Gibt es noch eine Möglichkeit bei data1 die beiden Nullen am Ende und
> bei data1 die Null am ende zu entfernen. Die entstehen ja durch die
> Umrechnerrei zu binär.
Was heißt "entfernen"? Wenn Du weißt, dass in den beiden Stellen nichts 
Verwertbares steht, brauchst Du sie ja nicht auszugeben. Es steht Dir 
auch frei, die beiden Ausgangswerte vor der Konvertierung nach ASCII 
"rechtsbündig" auszurichten. Entweder direkt beim Einlesen (also bei dem 
Zweizeiler mit den Schiebeoperationen) oder direkt vor der Wandlung. 
Wenn Du alle 16 Bits gewandelt hast, kannst Du auch einfach den 
Nullterminator an die entsprechende Stelle setzen. Die meisten 
Standard-Ausgabefunktionen nehmen den schließlich als 
String-Ende-Markierung. Wenn Du also Dein Array mit 17 Elementen hast 
(16 plus '\0'), Du aber nur die ersten 14 Stellen ausgeben willst, dann 
setze einfach das 15. Element des Arrays zu '\0'.

Autor: Steffen R. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

vielen Dnak, ich habe jetzt erst einmal den Nullterminator an die 
entsprechende Stelle gesetzt. Das funktioniert erst einmal. Die anderen 
Varianten werde ich nach und nach mal versuchen umzusetzen.

Beste Grüße
Steffen

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.