Forum: Mikrocontroller und Digitale Elektronik jedes dritte Bit in 54 Byte überprüfen; ATmega 644p


von Christoph (Gast)


Lesenswert?

Hallo!

 Ich habe ein Array von 54 Byte in meinem ATmega 644p, über SPI 
eingelesene Werte. Nun interessiert mich jedes dritte Bit, also aus dem 
ersten Byte  die Positionen 2 und 5, aus dem 2. Byte die 0, 3 und 6, 
dann aus dem dritten Byte 1,4 und 7, dann gehts wieder von vorne los.

Wie code ich sowas effizient? Der Rest des Programms ist C, inline asm 
würde aber zur Not auch gehen. Im Prinzip würde ich gern Bit [2] testen, 
drei mal rechtsschieben, Bit[2} testen, schieben, testen,... Aber ich 
kann ja schlecht die kompletten 54 Byte am Stück schieben. Müsste also 
im Prinzip 2Byte schieben, und wenn der Inhalt des oberen Byte komplett 
raus geschoben ist, das nächste Byte nachladen.

Hat jemand spontan eine bessere Idee? Fehlen noch wesentliche Infos, die 
ich bislang vergessen hab mitzuteilen?

Grüße,
 Christoph

von Sebastian (Gast)


Lesenswert?

Was willst du denn mit den Bits dann machen? Das wär noch interessant.

Im prinzip könntest du schon die 56 Byte durschschieben. In Assambler 
mit cpc, dauert halt etwas...

ich denke, ich würde mir eine Schleife bauen, die immer 3 Byte auf die 
von dir genannten Bits testet (explizit die drei bit, ohne schieben) und 
dann die nächsten 3 Byte durchmacht. Dürfte wohl am schnellsten gehn.
Aber genau sagen kann man das erst, wenn man weiß, was mit den Bits dann 
passieren soll.

Sebastian

von Karl H. (kbuchegg)


Lesenswert?

Christoph schrieb:

> würde aber zur Not auch gehen. Im Prinzip würde ich gern Bit [2] testen,
> drei mal rechtsschieben, Bit[2} testen, schieben, testen,...

Das klingt soch schon fast wie wenn es ein vernünftiges Konzept wäre.
Die Frage ist nur, was du unter 'testen' verstehst.

> Aber ich
> kann ja schlecht die kompletten 54 Byte am Stück schieben.

Ah, geh.
Deine Abfolge wiederholt sich doch alle 3 Bytes. Und zufällig ergeben 
die auch noch in Summe 8 Bits. Passt doch perfekt.

Die Zählung versteh ich nicht ganz:

> also aus dem
> ersten Byte  die Positionen 2 und 5, aus dem 2. Byte die
> 0, 3 und 6, dann aus dem dritten Byte 1,4 und 7,


    +     +         +     +     +          +     +     +
7 6 5 4 3 2 1 0     7 6 5 4 3 2 1 0      7 6 5 4 3 2 1 0
   Byte n              Byte n+1             Byte n+2

Jeweils die mit + markierten Bits möchtest du haben
1
   j = 0;
2
   for( i = 0; i < 54; i = i + 3 )
3
     Result[j++] = ( ( Data[i]   & 0x20 ) << 2 ) |     // Bit 7
4
                   ( ( Data[i]   & 0x04 ) << 4 ) |     //     6
5
                   ( ( Data[i+1] & 0x80 ) >> 2 ) |     //     5
6
                   ( ( Data[i+1] & 0x10 )      ) |     //     4
7
                   ( ( Data[i+1] & 0x02 ) << 2 ) |     //     3
8
                   ( ( Data[i+2] & 0x40 ) >> 4 ) |     //     2
9
                   ( ( Data[i+2] & 0x08 ) >> 2 ) |     //     1
10
                   ( ( Data[i+2] & 0x01 )              //     0
11
     ;

In Result hast du jetzt nur noch die dich interessierenden Bits in der 
richtigen Reihenfolge.

von Christoph (Gast)


Lesenswert?

In den 54 Byte stecken 144 Input Pins, die ich aus einer Boundary Scan 
Kette  eingelesen habe. Je Pin gibts drei Bit, Input, Output, Output 
Enable. Für den Status interessiert mich in diesem Fall nur der Input. 
Alle Inputs, die auf low-Pegel liegen, werden in einen Puffer 
eingetragen. Ich teste also "wenn am dritten Bit des i-ten Triple low 
Pegel ist, schreibe i in den Puffer".

Vermutlich ist Überprüfen von je drei Byte geschickter und schneller, 
als die ganze Manipulation des Arrays, und auch besser als das 
ausknobeln, an welcher Stelle ich nun prüfe.

Ich muss alle 144 Pins nacheinander durchscannen und Werte 
zurückschreiben, daher ist das ganze schon ein wenig zeitkritisch.

von Karl H. (kbuchegg)


Lesenswert?

Christoph schrieb:
> In den 54 Byte stecken 144 Input Pins, die ich aus einer Boundary Scan
> Kette  eingelesen habe. Je Pin gibts drei Bit, Input, Output, Output
> Enable. Für den Status interessiert mich in diesem Fall nur der Input.
> Alle Inputs, die auf low-Pegel liegen, werden in einen Puffer
> eingetragen. Ich teste also "wenn am dritten Bit des i-ten Triple low
> Pegel ist, schreibe i in den Puffer".

Ach sag das doch gleich.
Da muss man doch gar nicht die Bits extra extrahieren.
Du fasst dein ByteArray als fortlaufendes Bitarray auf.

Bit Nr x findet sich dann in Byte   x / 8
Und dort ist es das Bit an der Position 7 - ( x % 8 )

Um das dich interessierende Statusbits des Sensors i zu finden, suchst 
du also nach dem Bit Nr  3 * i + 2

Astelle des extensiven Schiebens bei der Ausmaskierung des Bits, würd 
ich eine Tabelle mit Masken nehmen.
1
uint8_t  bitMask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
2
3
uint8_t isSet( uint8_t sensorNr, uint8_t bitOffset )
4
{
5
  uint8_t bitNr = sensorNr * 3 + bitOffset;  // jeweils 3 Bits pro Sensor
6
  uint8_t byteNr = bitNr / 8;
7
  uint8_t maskNr = 7 - bitNr % 8;
8
9
  if( data[byteNr] & bitMask[maskNr] )
10
    return 1;
11
  return 0;
12
}
13
14
uint8_t isStatusSet( uint8_t sensorNr )
15
{
16
  return isSet( sensorNr, 2 );   // status ist das 3-te Bit des Triplets
17
}
18
19
uint8_t isInputSet( uint8_t sensorNr )
20
{
21
  return isSet( sensorNr, 1 );   // input ist das 2-te Bit des Triplets
22
}
23
24
uint8_t isOutputSet( uint8_t sensorNr )
25
{
26
  return isSet( sensorNr, 0 );   // output ist das 1-te Bit des Triplets
27
}

von Christoph (Gast)


Lesenswert?

> Ach sag das doch gleich.

Sorry. :-) Ich bin noch ziemlich unerfahren im Programmieren, manchmal 
fehlt mir noch der Blick für die wirklich relevanten Infos...

>     +     +         +     +     +          +     +     +
> 7 6 5 4 3 2 1 0     7 6 5 4 3 2 1 0      7 6 5 4 3 2 1 0
>    Byte n              Byte n+1             Byte n+2

fast. Die Bytes n+1 und n+2 müssen getauscht werden:

    +     +           +     +     +      +     +     +
7 6 5 4 3 2 1 0     7 6 5 4 3 2 1 0      7 6 5 4 3 2 1 0
   Byte n              Byte n+1             Byte n+2



Um das ganze nicht per Funktionsaufruf zu machen, wäre das ganze dann so 
zu lösen, oder (Maske umsortiert, spart wieder berechnungs-Aufwand):
1
uint8_t  bitMask[] = {0x04, 0x20, 0x01, 0x08, 0x40, 0x02, 0x10, 0x80};
2
for (i=0; i<54;i++){
3
  uint8_t byteNr = (i * 3) / 8;
4
  if (!(bsInBuffer[byteNr] & bitMask[i%8])) addTxBuffer(i);
5
  }
6
}

Vielen Dank schonmal für deine/eure Hilfe!

von Karl H. (kbuchegg)


Lesenswert?

Christoph schrieb:

>>     +     +         +     +     +          +     +     +
>> 7 6 5 4 3 2 1 0     7 6 5 4 3 2 1 0      7 6 5 4 3 2 1 0
>>    Byte n              Byte n+1             Byte n+2
>
> fast. Die Bytes n+1 und n+2 müssen getauscht werden:
>
>     +     +           +     +     +      +     +     +
> 7 6 5 4 3 2 1 0     7 6 5 4 3 2 1 0      7 6 5 4 3 2 1 0
>    Byte n              Byte n+1             Byte n+2

Dann stimmt an deiner Zählerei etwas nicht :-)

<Zitat>
Nun interessiert mich jedes dritte Bit
</Zitat>

Fang links zum zählen an und zähl jedes dritte Bit.
Ahhhh. Du fängst bei 0 an zu zählen. Ja geht auch.
Siehst du, das sind so die kleinen Informationen.
Ehe man sich ein Verfahren überlegt, muss man exakt wissen, was wo 
steht, in welcher Richtung gezählt wird, etc.

Drum ist es immer gut, sich ein Schaubild zu malen. Die sind nämlich 
meist ziemlich eindeutig, was man von textuellen Beschreibungen nicht 
sagen kann.

> Um das ganze nicht per Funktionsaufruf zu machen,

kannst du machen.
Aber wahrscheinlich wird der Compiler sowieso da eine inline Version 
daraus generieren :-):
Genauso, wie er lokale Variablen rausschmeissen wird.

> wäre das ganze dann so
> zu lösen, oder (Maske umsortiert, spart wieder berechnungs-Aufwand):

Da deine Zählweise anders rum ist, hebt sich das dann auf :-)
Also Maske so wie bei mir, aber Berechnung der MaskenNr als i % 8

> [c]
> uint8_t  bitMask[] = {0x04, 0x20, 0x01, 0x08, 0x40, 0x02, 0x10, 0x80};

Die ist dir wohl etwas durcheinandergekommen :-)

  uint8_t byteNr = (i * 3) / 8;
  if (!(bsInBuffer[byteNr] & bitMask[i%8]))

Stimmt auch nicht. i ist nicht die Bitnummer.
Lass doch die Hilfs-Variablen drinnen, wenn du ansonsten durcheinander 
kommst. Dein Compiler kann viel besser optimieren, als du denkst. Der 
schmeisst sie dann wieder raus bzw. hält das Zwischenergbenis in einem 
Register. Aber die Übersicht ist für dich als Programmierer mit den 
Hilfsvariablen viel besser! Solche Low-Level Optimierungen wie 
Funktionen inlinen oder Hilfsvariablen eliminieren oder konstante 
Ausdrücke zusammenfassen, überlasst man am Besten dem Compiler.

Probiers mit Papier und Bleistift durch. Oder mach erst mal eine Version 
am PC und lass dir die ByteNr und die Maske ausgeben. Dann siehst du 
sehr schnell, ob die Zugriffe dort sind, wo du sie haben möchtest.

von Karl H. (kbuchegg)


Lesenswert?

Aber eine Optimierung, für die ich meine Hand nicht ins Feuer legen 
würde, dass sie der Optimizer schafft, ist diese hier
1
  for (i = 0, bitNr = 0; i < 54; i++, bitNr += 3 )
2
  {
3
    if( !(bsInBuffer[bitNr/8] & bitMask[bitNr%8]) )
4
      addTxBuffer( i );
5
  }
6
}

Damit bist du die (kostspielige) Multiplikation mit 3 innerhalb der 
Schleife los. Manche Optimizer finden sowas raus, manche nicht.

von Ulrich P. (uprinz)


Lesenswert?

Nur so als Idee, wenn es auf zeit ankommt:

Der AVR kann in den unteren Registern auch Bit-Operationen direkt 
durchführen. D.h. er kann set, clr, tst... in einem Zyklus auf ein Bit 
eines der unteren Register anwenden. Das wird vermutlich einen längeren 
Assembler Code erfordern, aber an Geschwindigkeit kaum zu übertreffen 
sein.
ATMEL hat für solche Operationen bei den neueren CPUs extra ein 
'virtulles' GPIO Register dort unten abgelegt (GPIOR0). Es kann mit 
allen Bit-Befehlen angesteuert werden, die auch die I/O-Ports 
unterstützen, nur das an diesem Register eben keine realen I/O Pinne 
hängen.

Gruß, Ulrich

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.