Forum: Mikrocontroller und Digitale Elektronik [8-bit AVR] [C] Logik Alarmsystem bzw. "Majority Logic"


von Jonas S. (microwave)


Lesenswert?

Hi µC.net-Community!

Ich möchte auf einem ATmega16 eine Alarmsystem-Logik in C 
implementieren.
Die Anforderung ist trivial: Wenn zwei von drei Eingängen high sind, ist 
ein Alarmbit zu setzen.
Dazu ist die Verknüpfung D = A ^ B v B ^ C v A ^ C in C zu realisieren.
Die Eingänge müssen per Maskierung aus dem "active low"-Eingang "PIND" 
extrahiert werden.

Untenstehend sind "zwei" meiner Lösungsvorschläge präsentiert. Die 
allgemeine Frage ist nun, ob das irgendwie kürzer geht? Und spezifisch 
möchte ich wissen, ob "((I & B1) && (I & B2)) || ((I & B2) && (I & B3)) 
|| ((I & B1) && (I & B3))" vereinfacht werden kann. Die Knacknuss für 
mich ist die Kombination von "bitwise"- und "boolean"-Verknüpfungen.
Ich habe auch versucht, mittels Schieben die Eingangsbits auf die 
Position des Alarmbits zu bringen, womit die booleschen Operatoren 
wegfallen:
1
tmp = (((I & (1 << 3)) >> 3) & ((I & (1 << 4)) >> 4))
2
| (((I & (1 << 4)) >> 4) & ((I & (1 << 5)) >> 5))
3
| (((I & (1 << 3)) >> 3) & ((I & (1 << 5)) >> 5));
4
    
5
if(tmp){
6
  PORTD |= ALARMRANGE;
7
}else{
8
  PORTD &= ~ALARMRANGE;
9
}
Da die Shifts äquivalent zu * bzw. / 2^n sind, habe ich jetzt 
arithmetische und bitweise Operatoren in dem Ausdruck. Wieder weiss ich 
nicht, wie ich das allgemein vereinfachen soll.
Falls es nicht kürzer geht - welche Variante würdet ihr dann bevorzugen, 
und warum?


So, hier der C-Quellcode:
Die verwendeten Ports sind als initialisiert zu betrachten.
Als kürzeste Entsprechung in C habe ich Folgendes gefunden:
1
#include <avr/io.h>
2
3
#define B1 (1 << 3)
4
#define B2 (1 << 4)
5
#define B3 (1 << 5)
6
#define ALARMRANGE (1 << 6)
7
8
int main(void){
9
  unsigned char I = 0;
10
11
  for(;;){
12
    I = ~PIND;
13
14
    if (((I & B1) && (I & B2)) || ((I & B2) && (I & B3)) || ((I & B1) && (I & B3))){
15
      PORTD |= ALARMRANGE;
16
    }else{
17
      PORTD &= ~ALARMRANGE;
18
    }
19
  }
20
21
  return 0;
22
}
Das Maskieren kann separat erfolgen:
1
for(;;){
2
  I1 = ~PIND & B1;
3
  I2 = ~PIND & B2;
4
  I3 = ~PIND & B3;
5
6
  if ((I1 && I2) || (I2 && I3) || (I1 && I3)){
7
    PORTD |= ALARMRANGE;
8
  }else{
9
    PORTD &= ~ALARMRANGE;
10
  }
11
}

Vielen Dank im Voraus!

Grüsse - Microwave89

von chris (Gast)


Lesenswert?

1
for(;;){
2
  I1 = ~PIND & B1;
3
  I2 = ~PIND & B2;
4
  I3 = ~PIND & B3;
5
6
  uint8_t tmp=0;
7
  if (I1)
8
    tmp++;
9
  if (I2)
10
    tmp++;
11
  if (I3)
12
    tmp++;
13
  
14
  if (tmp >= 2)
15
  {
16
    PORTD |= ALARMRANGE;
17
  }
18
  else
19
  {
20
    PORTD &= ~ALARMRANGE;
21
  }
22
}

von Jonas S. (microwave)


Lesenswert?

Hi chris

Vielen Dank für deinen Vorschlag! Das gefällt mir viel besser als meine 
Holzhammer-Lösungen.
Dein Ansatz ist leserlicher und einfach erweiterbar. Indem ich die 
Einzelvariablen mit einem booleschen Array[n] und die if-Abfragen mit 
einer for-Schleife ersetze, könnte ich auf m Eingänge aus n an 
allgemeinen Positionen reagieren. :)

Für den Spezialfall nebeneinander liegender Eingänge fällt das boolesche 
Array weg:
1
#define n 3  //Total number of inputs
2
#define m 2  //Number of inputs for output activation
3
#define BITOFFSET 0x3  //Offset from LSB
4
... //int main() and so on...
5
  for(;;){
6
    if((m > n) || (8 < n+BITOFFSET)){
7
      LEDPORT |= (1 << 7);
8
      break;
9
    }
10
11
    buttons_input = ~BUTTONS;
12
    tmp = 1;
13
    for (int i = BITOFFSET; i < n+BITOFFSET; i++){
14
      if ((1 << i) & buttons_input)
15
        tmp++;
16
    }
17
18
    if (m < tmp){
19
      LEDPORT |= (1 << 6);
20
    }else{
21
      LEDPORT &= ~(1 << 6);
22
    }
23
  }

Grüsse - Microwave

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.