I am having a problem trying to do a XOR with 3 variables

185 views Asked by At

Well I am programming a micro (ATmega328p) using C for a school project. The thing is I am trying to represent this truth table (A,B and C are just inputs, buttons to be more specific) and XOR is the output on a LED, there are other 3 LEDs but they are doing a different gate and right now they are working OK.

Truth table

I am having problem with this specific part because the XOR is not working, it is supposed to be off when 2 buttons are pressed but is not. It just is only Off when all the buttons are not pressed:

        if ( ((BTS & _BV(BT1)) ^ (BTS & _BV(BT2))) || ((BTS & _BV(BT2)) ^ (BTS & _BV(BT3)))) //EXOR
        {
            LEDS |= _BV(LED2); //Set 1 on LED2
        }
        else
        {
            LEDS &= ~_BV(LED2); //Set 0 on LED2
        }

I'll put the full code here just to reference.

#include <avr/io.h>
#define F_CPU 16000000UL 
#include <util/delay.h>

#define delay 500

//--Tags
//-Inputs
#define BTS PINB
#define BT1 PINB0
#define BT2 PINB1
#define BT3 PINB2

//-Outputs
#define LEDS PORTD
#define LED0 PORTD2
#define LED1 PORTD3
#define LED2 PORTD4
#define LED3 PORTD5

void init_ports(void);

int main(void)
{
    init_ports();
    
    while (1) 
    {
        //Output 1
        if ((BTS & _BV(BT1)) && (BTS & _BV(BT2)) && (BTS & _BV(BT3))) // AND 
        {
            LEDS |= _BV(LED0); //Set 1 on LED0
        }
        else
        {
            LEDS &= ~ _BV(LED0); //Set 0 on LED0
        }
        
        //Output 2
        if ((BTS & _BV(BT1)) || (BTS & _BV(BT2)) || (BTS & _BV(BT3))) // OR 
        {
            LEDS |= _BV(LED1); //Set 1 on LED1
        }
        else
        {
            LEDS &= ~ _BV(LED1); //Set 0 on LED1
        }
        
        //Output 3
        
        if ( ((BTS & _BV(BT1)) ^ (BTS & _BV(BT2))) || ((BTS & _BV(BT2)) ^ (BTS & _BV(BT3)))) //EXOR
        {
            LEDS |= _BV(LED2); //Set 1 on LED2
        }
        else
        {
            LEDS &= ~_BV(LED2); //Set 0 on LED2
        }
        
        //Output 4
        
        if (!((BTS & _BV(BT1)) && (BTS & _BV(BT2)) && (BTS & _BV(BT3)))) // NOR 
        {
            LEDS |= _BV(LED3); //Set 1 on LED3
        }
        else
        {
            LEDS &= ~_BV(LED3); //Set 0 on LED3
        }
    }
}

void init_ports (void)
{
    //--Inputs
    DDRB  &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3));
    //-PULL-UP 
    PORTB &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3));
    //--Outputs
    DDRD  |=  (_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3));
    //-Off
    PORTD &= ~(_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3));
}

Any ideas of what could be wrong? TY!! :D

I tried using this boolean expression y = A'B'C + A'BC' + AB'C' + ABC

5

There are 5 answers

1
B. Pantalone On

Write some code to construct a truth table using your boolean expression and compare it to the table given in the assignment. Do they match? If not, think carefully about the truth table you were given and construct a boolean expression that will match. You can also search Google for something called a Karnaugh map, which can help you construct an expression from a truth table.

#include <stdio.h>

int main()
{
   printf(" A   B   C   EXOR\n");
   printf("------------------\n");

   for (int a = 0; a <=1; a++) {
      for (int b = 0; b <=1; b++) {
         for (int c = 0; c <=1; c++) {
            int exor = (a ^ b) || (b ^ c);
            printf(" %i   %i   %i   %i\n", a, b, c, exor);
         }
      }
   }
}
4
Fe2O3 On

Usually I ask beginners to use fewer variables in their code. In this case, using a trio of intermediate values compacts the code so that the reader can see what is going on.

That's an odd truth table for a 3-way XOR. The following might help you:

    while (1)
    {
        bool btn1 = BTS & _BV(BT1);
        bool btn2 = BTS & _BV(BT2);
        bool btn3 = BTS & _BV(BT3);

// AND
        if ( btn1 && btn2 && btn3 )
            LEDS |= _BV(LED0); //Set 1 on LED0
        else
            LEDS &= ~_BV(LED0); //Set 0 on LED0
// OR
        if( btn1 || btn2 || btn3 ) 
            LEDS |= _BV(LED1); //Set 1 on LED1
        else
            LEDS &= ~_BV(LED1); //Set 0 on LED1
//EXOR
//      if( btn1 ^ btn2 || btn2 ^ btn3 ) /* wrong! */
        if( btn1 || btn2 ^ btn3 )
            LEDS |= _BV(LED2); //Set 1 on LED2
        else
            LEDS &= ~_BV(LED2); //Set 0 on LED2
// NOR
        if( !( btn1 && btn2 && btn3 ) ) 
            LEDS |= _BV(LED3); //Set 1 on LED3
        else
            LEDS &= ~_BV(LED3); //Set 0 on LED3
    }

It's a bad idea to use leading underscores even for macro identifiers. Use something different.

EDIT:
As mentioned above, that's an odd truth table you've presented in this question.

Below goes one step further compacting the code to accentuate the logical operations you've used in your question. A couple of #define macros instead of hieroglyphic code:

    while (1)
    {
        bool btn1 = BTS & _BV(BT1);
        bool btn2 = BTS & _BV(BT2);
        bool btn3 = BTS & _BV(BT3);

#       define  ON(led) ( (LEDS) |= (_BV(led)) )
#       define OFF(led) ( (LEDS) &= ~(_BV(led)) )

// AND
        if ( btn1 && btn2 && btn3 )
            ON(LED0);
        else
            OFF(LED0);

// OR
        if( btn1 || btn2 || btn3 ) 
            ON(LED1);
        else
            OFF(LED1);

//EXOR (As given by the OP)
//      if( btn1 ^ btn2 || btn2 ^ btn3 ) /* wrong! */
        if( btn1 || btn2 ^ btn3 )
            ON(LED2);
        else
            OFF(LED2);

//XOR (Added to appease others)
        if( btn1 ^ btn2 ^ btn3 )
            ON(LED2);
        else
            OFF(LED2);

// NOR
        if( !( btn1 && btn2 && btn3 ) ) 
            ON(LED3);
        else
            OFF(LED3);
    }

EDIT 2:
In the acrimony raised by this question, the mislabelling of NAND as NOR has, until now, gone unremarked. Such is the distraction of too much code.

The following reduces the code such that readers can quickly satisfy themselves that all is in order. The issue of the odd truth table remains unresolved.

// Two functions to set/reset a particular LED
void  on( unsigned char led ) { LEDS |=  _BV(led); }
void off( unsigned char led ) { LEDS &= ~_BV(led); }

    // array of 2 where [0] invokes off() and [1] invokes on()
    void (*set)[](unsigned char) = { off, on };

    while(1) {
        // ensure unknown bitmask results in either 0 or 1
        bool btn1 = !!(BTS & _BV(BT1));
        bool btn2 = !!(BTS & _BV(BT2));
        bool btn3 = !!(BTS & _BV(BT3));

        set[ btn1 & btn2 & btn3 ](LED0); // AND
        set[ btn1 | btn2 | btn3 ](LED1); // OR

#ifdef ODD_EXOR_TRUTH_TABLE
        set[ btn1 | btn2 ^ btn3 ](LED2); //EXOR (from OP)
#else
        set[ btn1 ^ btn2 ^ btn3 ](LED2); //XOR (conventional)
#endif

        set[ !( btn1 & btn2 & btn3 ) ](LED3); // NAND (not NOR)
        set[ !( btn1 | btn2 | btn3 ) ](LED4); // NOR (LED #4 !!)
    }
0
Support Ukraine On

Your title talks about XOR of three variables which would just be

EXOR = A ^ B ^ C;

However, your truth table:

enter image description here

is not a three variable XOR. For a three variable XOR the result in line 6 and 7 should be zero.

The truth table instead represents "B exclusive-or C or A", like:

EXOR = B ^ C | A;
0
AterLux On

Note

((BTS & _BV(BT1)) ^ (BTS & _BV(BT2))

Here XOR operation is a bitwise (not logical).

Since there is bitwise AND in the both operands, each of them has all the bits reset, except for bit #BT1 at the left and #BT2 at the right.

Bits #BT2 at the left and #BT1 at the right are zeros. That means, ^ here works exactly as bitwise OR, producing the result, where both bits are set, if they are set in both operands.

Probably you want to convert operands to some predefined values.

E.g. you can use the ternary operator

((((BTS & _BV(BT1)) ? 1 : 0) ^ (BTS & _BV(BT2)) ? 1 : 0))

Or you can achieve the same by simple comparison:

((((BTS & _BV(BT1)) != 0) ^ ((BTS & _BV(BT2)) != 0))

0
ReAl On

As Support Ukraine wrote, the truth table is for

EXOR = (B ^ C) | A;

Anyway, let A, B, and C are bits 2, 1 and 0 of input value (0000_0ABC). Use 8-entry 1-bit output lookup table packed in one byte:

out = (0xF6 >> (input & 0x03)) & 0x01;

Where 0xF6 is the bitmask of your EXOR column (the top line is bit 0 and the bottom one is bit 7).