Forum: Mikrocontroller und Digitale Elektronik Bitweisen vertikalen Counter auswerten


von Christoph B. (nuke)


Lesenswert?

Hallo allerseits,

da mein Gehirn schon einen Knoten hat, hoffe ich, dass einer von euch 
eine gute Idee hat zu folgender Knobelaufgabe:

Ich habe mir für meinen Algorithmus (an der Stelle unwichtig) einen 
"vertikalen" Counter gebaut, bestehend aus 5 32Bit-Variablen, die 
jeweils für eine Bitposition stehen. Hierdurch entsteht ein separater 
Counter für jede Bitposition. Das ganze ist vorstellbar als 32 Werte mit 
jeweils 5 Bit, nur "transponiert" (so ähnlich wie in der 
Taster-Entprellroutine von Peter Dannegger: Entprellung).

Angenommen ich habe also für jede Bitposition einen anderen Counterwert, 
wie kann ich parallel für alle Bitpositionen die Counterwerte auf einen 
Bestimmten Bereich prüfen? Also x < Counter < y ist okay, deshalb Flag 
setzen in einer anderen Variable, ansonsten das Flag löschen. Nur eben 
parallel für alle Counter/Bitpositionen.

Ich habe es zuerst mit Boolescher Algebra versucht, die alle Bits auf 
geeignete Weise verUNDet und verODERt, das hat auch gut funktioniert, 
nur leider ist diese Variante sehr statisch hinsichtlich des Bereichs. 
Diesen würde ich gern über Defines festlegen, sodass er variabel bleibt.



Ich hoffe, das war jetzt nicht zu kompliziert, vielen Dank im Voraus!


Viele Grüße,
Christoph

von Steffen R. (steffen_rose)


Lesenswert?

Sind es jetzt 5 counter zu je 32Bit oder 32 Counter zu je 5Bit?
Wenn letzteres, warum nutzt du dafür nicht auch 32 Variable?

Dein funktionierender, aber unflexibler Algorithmus würde sicher für das 
Verständnis sinnvoll sein. Auch das Ziel, welches Du erreichen willst, 
wäre nicht schlecht.

Ansonsten:
Betrachte jedes Bit für sich und baue Deine Makros zum Ändern und 
vergleichen getrennt für jedes Bit.

von Stefan W. (dl6dx)


Lesenswert?

Christoph B. schrieb:
> Angenommen ich habe also für jede Bitposition einen anderen Counterwert,
> wie kann ich parallel für alle Bitpositionen die Counterwerte auf einen
> Bestimmten Bereich prüfen? Also x < Counter < y

> Ich habe es zuerst mit Boolescher Algebra versucht, die alle Bits auf
> geeignete Weise verUNDet und verODERt, das hat auch gut funktioniert,
> nur leider ist diese Variante sehr statisch hinsichtlich des Bereichs.

Ungeprüfte Idee für einen Ansatz:
Vergleiche deine Zähler (ich nehme an, sie sind als Array uint32_t[5] 
organisiert) mit einem zweiten Array gleicher Struktur, das jeweils den 
Vergleichswert enthält. Für den Vergleich müsstest du einen "vertikalen 
Addierer" bauen, der für das Ergebnis einen Satz Flags (Zero, Negativ, 
etc) bereitstellt. Den Vergleichswert erzeugst du jeweils über eine 
geeignete Funktion. Das könnte dann so aussehen:
1
typedef uint32_t[5] carr_data_t;
2
typedef uint32_t[2] carr_flags_t;
3
4
carr_data_t counter;
5
carr_data_t compare;
6
carr_flags_t flags;
7
8
void carr_set(carr_data_t carr, uint8_t wert);
9
10
void carr_cmp(carr_data_t carra, carrb);
11
12
void main(void)
13
{
14
carr_set(counter, 5);
15
carr_set(compare, 7);
16
17
carr_cmp(counter, compare, flags);
18
19
/* Flags enthält jetzt das Ergebnis für jeden Zähler */

Die Alternative, die Matrix erst zu transponieren und dann 32 Vergleiche 
zu machen, dürfte wesentlich aufwendiger sein.

Grüße

Stefan

von Yalu X. (yalu) (Moderator)


Lesenswert?

Stefan Wagner schrieb:
> Vergleiche deine Zähler (ich nehme an, sie sind als Array uint32_t[5]
> organisiert) mit einem zweiten Array gleicher Struktur, das jeweils den
> Vergleichswert enthält.

Ja, genau so.

Ich habe das hier mal als C-Programm umgesetzt. Die Funktion
lessorequal macht den eignetlichen Vergleich:

1
#include <stdio.h>
2
#include <string.h>
3
#include <stdint.h>
4
#include <inttypes.h>
5
6
void transpose(uint32_t in[], int nin, uint32_t out[], int nout) {
7
  uint32_t elemin, mask;
8
  int i, j;
9
10
  memset(out, 0, nout * sizeof (uint32_t));
11
  mask = 1;
12
  for(i=0; i<nin; i++) {
13
    elemin = in[i];
14
    for(j=0; j<nout; j++) {
15
      if(elemin & 1)
16
        out[j] |= mask;
17
      elemin >>= 1;
18
    }
19
    mask <<= 1;
20
  }
21
}
22
23
uint32_t lessorequal(uint32_t a[], uint32_t b[], int n) {
24
  uint32_t y;
25
  int i;
26
27
  y = ~0;
28
  for(i=0; i<n; i++)
29
    y = ~a[i] & (b[i] | y) | b[i] & y;
30
  return y;
31
}
32
33
void show(uint32_t arr[], int n) {
34
  int i;
35
36
  for(i=0; i<n; i++)
37
    printf("%2d: %10"PRIu32"\n", i, arr[i]);
38
}
39
40
uint32_t inrange(uint32_t tvalue[], uint32_t tlower[], uint32_t tupper[], int nbits) {
41
  return lessorequal(tlower, tvalue, nbits) & lessorequal(tvalue, tupper, nbits);
42
}
43
44
#define NVALUES 32
45
#define NBITS  5
46
47
// 32 Zählerwerte à 5 Bit
48
uint32_t value[NVALUES] = {
49
   0,  1,  2,  3,  4,  5,  6,  7,
50
   8,  9, 10, 11, 12, 13, 14, 15,
51
  16, 17, 18, 19, 20, 21, 22, 23,
52
  24, 25, 26, 27, 28, 29, 30, 31
53
};
54
55
// 32 Untere Grenzen
56
uint32_t lower[NVALUES] = {
57
   8,  8,  8,  8,  9,  9,  9,  9,
58
  10, 10, 10, 10, 11, 11, 11, 11,
59
  12, 12, 12, 12, 13, 13, 13, 13,
60
  14, 14, 14, 14, 15, 15, 15, 15
61
};
62
63
// 32 Obere Grenzen
64
uint32_t upper[NVALUES] = {
65
  16, 16, 16, 16, 17, 17, 17, 17,
66
  18, 18, 18, 18, 19, 19, 19, 19,
67
  20, 20, 20, 20, 21, 21, 21, 21,
68
  22, 22, 22, 22, 23, 23, 23, 23
69
};
70
71
uint32_t tvalue[NBITS];
72
uint32_t tlower[NBITS];
73
uint32_t tupper[NBITS];
74
75
int main(void) {
76
  uint32_t y;
77
78
  // Zählerwerte und Grenzen in vertikale Darstellung transponieren
79
  transpose(value, NVALUES, tvalue, NBITS);
80
  transpose(lower, NVALUES, tlower, NBITS);
81
  transpose(upper, NVALUES, tupper, NBITS);
82
83
  // Prüfen, welche der 32 Zählerwerte im erlaubten Bereich liegen
84
  y = inrange(tvalue, tlower, tupper, NBITS);
85
86
  // Bit i von y ist 1 gdw lower[i] <= value[i] <= upper[i]
87
  printf("%08"PRIx32"\n", y);
88
89
  return 0;
90
}

von Noch einer (Gast)


Lesenswert?

Oder einfach so eine Art binäre Suche?
1
5. Bit gesetzt ?
2
Ja
3
  Vergleichswert > 16 ?
4
    Nein -> fertig, true
5
    Ja
6
      4. Bit gesetzt?
7
      Ja
8
        Vergleichswert > 20 ?
9
        Nein -> fertig, true
10
        ...
11
Nein
12
  Vergleichswert >= 16 ?
13
    Ja -> fertig, false
14
    ...

Sind ja nur 32 Varianten

von Steffen R. (steffen_rose)


Lesenswert?

Stefan Wagner schrieb:
> Vergleiche deine Zähler (ich nehme an, sie sind als Array uint32_t[5]
> organisiert) mit einem zweiten Array gleicher Struktur, das jeweils den
> Vergleichswert enthält.

Die eigentliche Frage war doch nach einer flexiblen Lösung, die er per 
Makro variieren kann. Die gezeigten Lösungen sind effektiv. Aber aus 
meiner Sicht wurde nicht gezeigt, wie man daraus flexible Makros macht.

Ich habe die Flexibilität so verstanden, dass er außer den Zählerwerten, 
welche zu einem Bitwechsel führen, auch die Anzahl der Counterbits je 
bit und global auch die Anzahl der Counter variieren will.

Christoph B. schrieb:
> Ich habe es zuerst mit Boolescher Algebra versucht, die alle Bits auf
> geeignete Weise verUNDet und verODERt, das hat auch gut funktioniert,

@Stefan
Ich gehe mal davon aus, dass er mit seiner Aussage eine Lösung hat, die 
deinem Vorschlag nahe kommt.

Oder habe ich ihn falsch verstanden?

Edit:

Yalu X. schrieb:
> Ich habe das hier mal als C-Programm umgesetzt. Die Funktion
> lessorequal macht den eignetlichen Vergleich:

Die Vergleichs-Tabellen anfangs zu berechnen hatte ich bei dem Vorschlag 
von Stefan übersehen.

von Christoph B. (nuke)


Lesenswert?

Yalu X. schrieb:
1
uint32_t lessorequal(uint32_t a[], uint32_t b[], int n) {
2
  uint32_t y;
3
  int i;
4
5
  y = ~0;
6
  for(i=0; i<n; i++)
7
    y = ~a[i] & (b[i] | y) | b[i] & y;
8
  return y;
9
}

Das klingt interessant, an so eine Art und Weise hatte ich gedacht. 
Hättest du auch noch eine kleine Erklärung zu den Bitoperationen, bzw. 
eine Quelle? Konnte leider im Netz auf Anhieb nix finden...

von Possetitjel (Gast)


Lesenswert?

Christoph B. schrieb:

> Angenommen ich habe also für jede Bitposition einen
> anderen Counterwert, wie kann ich parallel für alle
> Bitpositionen die Counterwerte auf einen Bestimmten
> Bereich prüfen?

Rückfrage: Der Bereich soll für alle Counter identisch
sein?

Nach meinem laienhaften Verständnis hat Yalu den allgemeinen
Fall mit unterschiedlichen Intervallen je Counter bearbeitet.
Das könnte man evtl. noch vereinfachen.

von Possetitjel (Gast)


Lesenswert?

Stefan Wagner schrieb:

> Vergleiche deine Zähler [...] mit einem zweiten Array
> gleicher Struktur, das jeweils den Vergleichswert enthält.

Cool.

Ich hätte gedacht "Das geht nicht!", aber es geht offenbar
doch. Respekt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Es ist a<b genau dann, wenn bei der binären Subtraktion a-b ein Übertrag
(Borgen) entsteht (auf diese Weise funktioniert auch der CMP-Befehl der
meisten Prozessoren).

Die Wahrheitstabelle für den Übertrag c_i+1 bei der Subtraktion der
i-ten Binärstelle in Abhängigkeit von den Binärziffern a_i und b_i und
dem alten Übertrag c_i lautet:

1
a_i  b_i  c_i | c_i+1    (c_i+1 = 1  <=>  a_i - b_i - c_i < 0)
2
—————————————————————
3
 0    0    0  |   0
4
 0    0    1  |   1
5
 0    1    0  |   1
6
 0    1    1  |   1
7
 1    0    0  |   0
8
 1    0    1  |   0
9
 1    1    0  |   0
10
 1    1    1  |   1
11
—————————————————————

Dieser logische Zusammenhang wird – beginnend mit der niederwertigsten
Stelle – nacheinander auf die 5 Ziffern der Zahlen angewandt (i=0..4).
Der initiale Übertrag c_0 wird dabei auf 0 gesetzt. Der als letztes
berechnete Übertrag c_5 ist das gesuchte Ergebnis.

Man könnte damit die Bereichsprüfung als Ungleichung u<x<o realisieren.
Will man aber für x den kompletten Wertebereich von 0 bis 31 zulassen,
müsste die untere Grenze u=-1 und die obere o=32 sein. Beide Werte sind
aber mit 5 Bits nicht darstellbar.

Dieses Problem umgeht man, indem man stattdessen die Ungleichung u≤x≤o
verwendet. Dazu braucht man die Relation a≤b. Diese erhält man dadurch,
dass man in a<b die Operanden vertauscht (b<a bzw. a>b) und das Ergebnis
negiert (a≯b bzw. a≤b). Die negierten Überträge seien y genannt (y=¬c).
Die entsprechende Wahrheitstabelle lässt sich damit auf einfache Weise
aus der obigen für a<b ableiten:

1
b_i  a_i  y_i | y_i+1
2
—————————————————————
3
 0    0    1  |   1
4
 0    0    0  |   0
5
 0    1    1  |   0
6
 0    1    0  |   0
7
 1    0    1  |   1
8
 1    0    0  |   1
9
 1    1    1  |   1
10
 1    1    0  |   0
11
—————————————————————

Durch scharfes Hinsehen oder Verwendung eines KV-Diagramms ergibt sich
daraus der logische Term

  yi'  =  ¬ai ∧ bi  ∨  ¬ai ∧ yi  ∨  bi ∧ yi
       =  ¬ai ∧ (bi ∨ yi)  ∨  bi ∧ yi

Das ist genau der Term, welcher in der Funktion lessorequal verwendet
wird. Da c den Initialwert 0 hat, muss y wegen y=¬c entsprechend mit ¬0
starten.

von Christoph B. (nuke)


Lesenswert?

Sehr schöne Erklärung, vielen Dank! Da wäre ich ohne Weiteres nicht 
drauf gekommen ;-)

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.