Forum: Mikrocontroller und Digitale Elektronik bitweise Daten auslesen und verarbeiten


von Steffen R. (Gast)


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:
1
for (i = 7; i >= 0; i--)                           
2
{
3
    datatest= RXData[0];
4
    if (datatest & 0x80 >> i)
5
        data1[i] = '1';
6
     else
7
        data1[i] = '0';   
8
}

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

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

von Martin (Gast)


Lesenswert?

Das siehn nach einer CAN-ID aus.

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

von Klugscheisser (Gast)


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.

von P. S. (Gast)


Lesenswert?

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

von Klugscheisser (Gast)


Lesenswert?

Na, ich mache auch mal einen Codevorschlag:

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

von Klugscheisser (Gast)


Lesenswert?

Zu langsam... :-}

von Johannes M. (johnny-m)


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.

von Steffen R. (Gast)


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

von Steffen R. (Gast)


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.

von Johannes M. (johnny-m)


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

von Steffen R. (Gast)


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:
1
void LCDOutc(unsigned char str)
2
{
3
  switch (str)                   Display
4
  {                             
5
    case 'ä':
6
      str = 0xE1;
7
      break;
8
    case 'ö':
9
      str = 0xEF;
10
      break;
11
    case 'ü':
12
      str = 0xF5;
13
      break;
14
    case 'ß':
15
      str = 0xE2;
16
      break;
17
    case '°':
18
      str = 0xDF;
19
      break;
20
    case 'µ':
21
      str = 0xE4;
22
      break;
23
  }  
24
  PORTA=str;                       // str einfach an den Port schicken
25
  PORTK|=0x05;                     // RS und E high
26
  PORTK&=~0x02;                    // R/W low
27
  LCDClock();                      // Befehl ausführen
28
  chars++;                              
29
  if(chars==20)
30
  {
31
    if(row==1)
32
    {
33
      SecondLine();             // zweite Zeile 
34
    }
35
    if(row==2)
36
    {
37
      ThirdLine();              // dritte Zeile 
38
    }
39
    if(row==3)
40
    {
41
      FourthLine();             // vierte Zeile 
42
    }
43
    if(row==4)
44
    {
45
      LCDHome();                 // erste Zeile 
46
    }                    
47
  }/* if(chars==20) */   
48
}
49
50
void LCDOuts(unsigned char* str)
51
{
52
  while (*str != 0) {LCDOutc(*str++);}
53
}/* 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

von Johannes M. (johnny-m)


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:
1
char* itoa (int __val,
2
      char* __s,
3
      int __radix   
4
      )
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
1
char string1[17];
2
//....
3
itoa(data1, string1, 2);
aufgerufen werden, und den Rest macht die Funktion für Dich.

von Steffen R. (Gast)


Lesenswert?

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

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:
1
data11= ( RXData[0] << 8) | (RXData[1] & 0xE0) | ((RXData[1] & 0x07) << 2);
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?

von Steffen R. (Gast)


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

von Johannes M. (johnny-m)


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?

von Steffen R. (Gast)


Lesenswert?

*Ditsch

Mal schnell was geändert
1
unsigned int data11,data22;
2
3
for(ii = 0; ii <= 3; ii++)
4
    RXData[ii] = CAN0RXFG[ii];             
5
                                                   
6
data11 = ( (unsigned int)RXData[0] << 8) | (RXData[1] & 0xE0) | ((RXData[1] & 0x07) << 2);
7
data22 = ( (unsigned int)RXData[2] << 8) | (RXData[3] & 0xFE) >> 1;
8
9
data1[14]='\0';                        // String Terminator
10
for(i=13; i>=0; i--) {
11
    data1[i]=(data11 % 2) + '0';         
12
    data11 /= 2;
13
}
14
data2[15]='\0';                        // String Terminator
15
for(i=14; i>=0; i--) {
16
  data2[i]=(data22 % 2) + '0';          
17
  data22 /= 2;
18
}

Sieht schon besser aus, aber irgendwas stimmt noch nicht.

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

Irgendwo hängts :(

von Steffen R. (Gast)


Lesenswert?

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

itoa Nutzung umgehen. Sollte doch das gleiche sein!

von Johannes M. (johnny-m)


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;
                                                                 ^^^^
                                                                 ||||

von Johannes M. (johnny-m)


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!

von Steffen R. (Gast)


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.

von Johannes M. (johnny-m)


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'.

von Steffen R. (Gast)


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

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.