Bitwise operations, again...

This is embaressing. I try to chop a digit into BCD format (AA, AB, AC, and AD) in order to send it to a 7448 BCD to 7 segments display. It works with this terrible code, but I can't figure out how to code to use the bitwise operand '&'.

    if(digit==1 || digit==3 || digit==5 || digit==7 || digit==9) digitalWrite(AA,HIGH); else digitalWrite(AA,LOW);
    if(digit==2 || digit==3 || digit==6 || digit==7) digitalWrite(AB,HIGH); else digitalWrite(AB,LOW);
    if(digit>3 && digit<8) digitalWrite(AC,HIGH); else digitalWrite(AC,LOW);
    if(digit>7) digitalWrite(AD,HIGH); else digitalWrite(AD,LOW);

How would you have phrased it?

TIA,
SimLego

You could try:

digitalWrite(AD, digit & 8);
digitalWrite(AC, digit & 4);
digitalWrite(AB, digit & 2);
digitalWrite(AA, digit & 1);

Assuming that AD is MSB and AA is LSB in the BCD number.
Cheers!

kowalski:
You could try:

digitalWrite(AD, digit & 8);

digitalWrite(AC, digit & 4);
digitalWrite(AB, digit & 2);
digitalWrite(AA, digit & 1);



Assuming that AD is MSB and AA is LSB in the BCD number. 
Cheers!

What he said. :wink:
To make the code more readable, I'd either use binary notation or make a comment for each line giving the binary. For example:

digitalWrite(AC, digit & B0100);

or

digitalWrite(AC, digit & 4); // bitmask against binary 0100

Note, that while this code will pick up zero (which your code doesn't), it will also try to put out a parallel nibble for hex values A-F. Either deal with it, or check that digit <= 9 before processing. (I forget what 7448's do if you feed them a nibble outside the scope of BCD...)

Thanks!

this code will pick up zero (which your code doesn't)

Yes it does.

else digitalWrite(AA,LOW);
else digitalWrite(AB,LOW);
else digitalWrite(AC,LOW);
else digitalWrite(AD,LOW);

it will also try to put out a parallel nibble for hex values A-F. Either deal with it, or check that digit <= 9 before processing.

A digit > 9 should really be impossible in this specific context.

SimLego:
Thanks!

this code will pick up zero (which your code doesn't)

Yes it does.

else digitalWrite(AA,LOW);

else digitalWrite(AB,LOW);
else digitalWrite(AC,LOW);
else digitalWrite(AD,LOW);

So it does. I was just focusing on the primary conditional. So you code outputs a zero on both digit == 0 and digit > 9. 8)

it will also try to put out a parallel nibble for hex values A-F. Either deal with it, or check that digit <= 9 before processing.

A digit > 9 should really be impossible in this specific context.

Without knowing the context we didn't know this, now did we? (Great... now I'm starting to sound like PaulS. :stuck_out_tongue: Too bad I don't have his forum-cred yet.) It is good that you seemed to consider it though.

const uint8_t   pins[]  = { AD, AC, AB, AA };
uint8_t         digit   = /* 0 - 255 */;

for ( int mask = 0b00001000, i = 0; mask; mask >>= 1, i++ )
{
    digitalWrite(pins[i], digit & mask ? HIGH : LOW);
}
const uint8_t   pins[]  = { AD, AC, AB, AA };

uint8_t         digit   = /* 0 - 255 */;

for ( int mask = 0b00001000, i = 0; mask; mask >>= 1, i++ )
{
   digitalWrite(pins[--i], digit & mask ? HIGH : LOW);
}

Is 'i' not set right? it starts at zero, is decremented to -1 before use, then reset to zero, and repeated.

Without knowing the context we didn't know this, now did we?

Ok, here's the context. Pardon my very rude C language.

// Crazy Race Pinball Display Test ver 0.06
// BallInPlay in progress

// STROBE LINES
const int X[8] = { 25, 27, 24, 51, 23, 29, 22, 49};

// BIT LINES
const int AA = 45;  //  
const int AB = 35;  //  
const int AC = 33;  //  
const int AD = 41;  //  
const int BA = 37;  //  
const int BB = 47;  //  
const int BC = 43;  //  
const int BD = 31;  //  

// THE REST
const int DBS = 53;  //  DISPLAY SELECT
const int ENA = 39;  //  "ENABLE" (enable what? blanking out leading zeros?)
int match_number = 0;
int BallInPlay=5; // 0-4 = Ball 1-5, 5=Game Over
int Tilt=0;
long int p[4]={0, 22222, 33333, 44444};
long int pp0,pp1;
int digit;
long int lastmilli=0;


void setup()
{
  for(int i=0; i<8; i++) pinMode(X[i], OUTPUT);
  pinMode(AA, OUTPUT);
  pinMode(AB, OUTPUT);
  pinMode(AC, OUTPUT);
  pinMode(AD, OUTPUT);
  pinMode(BA, OUTPUT);
  pinMode(BB, OUTPUT);
  pinMode(BC, OUTPUT);
  pinMode(BD, OUTPUT);
  pinMode(DBS, OUTPUT);
  pinMode(ENA, OUTPUT);
}


void loop()
{
  int i_strobe;
  digitalWrite(AA,LOW);
  digitalWrite(AB,HIGH);
  digitalWrite(AC,LOW);
  digitalWrite(AD,LOW);
  digitalWrite(BA,LOW);
  digitalWrite(BB,LOW);
  digitalWrite(BC,LOW);
  digitalWrite(BD,LOW);
  
  digitalWrite(ENA,LOW);
  if (millis() > lastmilli + 800)
  {
    p[0]+=3; 
    p[1]=p[0]+3; 
    p[2]=p[1]*10+10000; 
    p[3]=p[0]*2; 
    lastmilli=millis();
    match_number++; if(match_number >9) match_number = 0;
    BallInPlay++;
    if(BallInPlay > 5)
    {
      BallInPlay = 0;
      Tilt--; Tilt *= -1; // Toggle Tilt/Not Tilt
    }
  }
  pp0=p[0];
  pp1=p[3];
  digitalWrite(DBS,LOW);
  for (i_strobe=0; i_strobe<8; i_strobe++)
  {
    if(i_strobe==0) digit = 0; // Free Plays
    if(i_strobe==1) digit = 1; // Extra Ball
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp0%10); pp0/=10; }
    if(digit==1 || digit==3 || digit==5 || digit==7 || digit==9) digitalWrite(AA,HIGH); else digitalWrite(AA,LOW);
    if(digit==2 || digit==3 || digit==6 || digit==7) digitalWrite(AB,HIGH); else digitalWrite(AB,LOW);
    if(digit>3 && digit<8) digitalWrite(AC,HIGH); else digitalWrite(AC,LOW);
    if(digit>7) digitalWrite(AD,HIGH); else digitalWrite(AD,LOW);
    if(i_strobe==0) digit = 2; // Credits*10
    if(i_strobe==1) digit = 1; // Credits*1
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp1%10); pp1/=10; }
    if(digit==1 || digit==3 || digit==5 || digit==7 || digit==9) digitalWrite(BA,HIGH); else digitalWrite(BA,LOW);
    if(digit==2 || digit==3 || digit==6 || digit==7) digitalWrite(BB,HIGH); else digitalWrite(BB,LOW);
    if(digit>3 && digit<8) digitalWrite(BC,HIGH); else digitalWrite(BC,LOW);
    if(digit>7) digitalWrite(BD,HIGH); else digitalWrite(BD,LOW);

    //delay(1);
    delayMicroseconds(150);
    digitalWrite(X[i_strobe], HIGH);
    delayMicroseconds(700);
    digitalWrite(X[i_strobe], LOW);
  }
  pp0=p[1];
  pp1=p[2];
  digitalWrite(DBS,HIGH);
  for (i_strobe=0; i_strobe<8; i_strobe++)
  {    
    if(i_strobe==0) digit = BallInPlay + Tilt*8; // Ball in play/Game Over/Tilt
    if(i_strobe==1) digit = match_number;
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp0%10); pp0/=10; }
    if(digit==1 || digit==3 || digit==5 || digit==7 || digit==9) digitalWrite(AA,HIGH); else digitalWrite(AA,LOW);
    if(digit==2 || digit==3 || digit==6 || digit==7) digitalWrite(AB,HIGH); else digitalWrite(AB,LOW);
    if(digit>3 && digit<8) digitalWrite(AC,HIGH); else digitalWrite(AC,LOW);
    if(digit>7) digitalWrite(AD,HIGH); else digitalWrite(AD,LOW);
    if(i_strobe==0) digit = 0; // N/C
    if(i_strobe==1) digit = 0; // N/C
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp1%10); pp1/=10; }
    if(digit==1 || digit==3 || digit==5 || digit==7 || digit==9) digitalWrite(BA,HIGH); else digitalWrite(BA,LOW);
    if(digit==2 || digit==3 || digit==6 || digit==7) digitalWrite(BB,HIGH); else digitalWrite(BB,LOW);
    if(digit>3 && digit<8) digitalWrite(BC,HIGH); else digitalWrite(BC,LOW);
    if(digit>7) digitalWrite(BD,HIGH); else digitalWrite(BD,LOW);

    delayMicroseconds(150);
    digitalWrite(X[i_strobe], HIGH);
    delayMicroseconds(700);
    digitalWrite(X[i_strobe], LOW);
  }
}

pYro_65:

const uint8_t   pins[]  = { AD, AC, AB, AA };

uint8_t         digit   = /* 0 - 255 */;

for ( int mask = 0b00001000, i = 0; mask; mask >>= 1, i++ )
{
   digitalWrite(pins[--i], digit & mask ? HIGH : LOW);
}

Is 'i' not set right? it starts at zero, is decremented to -1 before use, then reset to zero, and repeated.

Good catch! Earlier version went from top of array and I failed to catch that.

Fixed above.

Thanks for all suggestions! I think I'll go for this alternative:

digitalWrite(AD, digit & 8); // bitmask against binary 1000
digitalWrite(AC, digit & 4); // bitmask against binary 0100
digitalWrite(AB, digit & 2); // bitmask against binary 0010
digitalWrite(AA, digit & 1); // bitmask against binary 0001

I see you calling basically the same thing 4 times in your code, so you might elect to make it a function. Something like:

void digitDisplay(byte digitOut, byte BCD_A, byte BCD_B, byte BCD_C, byte BCD_D) {
  digitalWrite(BCD_D, digitOut & 8); // bitmask against binary 1000
  digitalWrite(BCD_C, digitOut & 4); // bitmask against binary 0100
  digitalWrite(BCD_B, digitOut & 2); // bitmask against binary 0010
  digitalWrite(BCD_A, digitOut & 1); // bitmask against binary 0001
}

And then call it with digitDisplay(digit, AA, AB, AC, AD); and digitDisplay(digit, BA, BB, BC, BD);

BTW, for pin numbers (or really any integer you know will never get larger than 255) you can initialize the variables/constants as "byte" instead of "int" and save one byte of SRAM per variable/constant. Not that important here, but it might be a good habit to get into as your sketches get larger and larger.

You may even be able to get clever and instead of passing 4 numbers for the pins, do sort of like you did for the strobe and have a 4 element array of pin numbers (A[] and B[]) that you pass to the function. (And you can also set the pinmodes with for-loops like you did for the strobe.)

Just throwing some ideas out there.

What would be the fastest way to write a BCD number to a seven segment display? Could it be done in a single line of code?

PORTB = (PORTB & 0xf0) | (digit & 0x0f);

For this to work the seven segment display needs to be connected to Arduino pin 8..11 (PB0..PB3, LSB..MSB). This is about 50X faster than calling the Arduino digitalWrite() but hardwires the display to a very specific port address and wiring to allow update of all bits at once.

Cheers!

This is about 50X faster than calling the Arduino digitalWrite() but hardwires the display to a very specific port address and wiring to allow update of all bits at once.

On the other hand, 8 calls to manipulate one pin at a time will still be 6 times as fast as one call to digitalWrite().

On the other hand, 8 calls to manipulate one pin at a time will still be 6 times as fast as one call to digitalWrite().

On the third hand, even with my quite inefficient amateur code, the Arduino is already way too fast for the surrounding vintage hardware. I have to force Arduino to slow down using delay commands , or it runs way ahead of those late 70's 74xx circuits... :slight_smile:

kowalski:
What would be the fastest way to write a BCD number to a seven segment display? Could it be done in a single line of code?

PORTB = (PORTB & 0xf0) | (digit & 0x0f);

For this to work the seven segment display needs to be connected to Arduino pin 8..11 (PB0..PB3, LSB..MSB). This is about 50X faster than calling the Arduino digitalWrite() but hardwires the display to a very specific port address and wiring to allow update of all bits at once.

Cheers!

Note, if you ever plan to move this project to a different board than an AVR, such as an Arduino Due or Teensy 3.0, using direct port manipulation commands will likely need to be revisited.

// Crazy Race Display Test ver 0.08
// 0.08: REMmed out some not necessary(?) lines
// 0.08: Moved digitalWrite(ENA,LOW); to setup()
// BallInPlay still in progress

// 0.07: Better bitmasking, creds to kowalski and Sembazuru

// STROBE LINES
const int X[8] = { 25, 27, 24, 51, 23, 29, 22, 49};

// BIT LINES
const int AA = 45;  //  
const int AB = 35;  //  
const int AC = 33;  //  
const int AD = 41;  //  
const int BA = 37;  //  
const int BB = 47;  //  
const int BC = 43;  //  
const int BD = 31;  //  

// THE REST
const int DBS = 53;  //  DISPLAY SELECT
const int ENA = 39;  //  "ENABLE" (enable what? blanking out leading zeros?)
int match_number = 0;
int BallInPlay = 5; // 0-4 = Ball 1-5, 5=Game Over
int nFreePlays = 0;
int nExtraBalls = 1;
int Tilt=0;
long int p[4]={0, 22222, 33333, 44444};
long int pp0,pp1;
int digit;
long int lastmilli=0;


void setup()
{
  for(int i=0; i<8; i++) pinMode(X[i], OUTPUT);
  pinMode(AA, OUTPUT);
  pinMode(AB, OUTPUT);
  pinMode(AC, OUTPUT);
  pinMode(AD, OUTPUT);
  pinMode(BA, OUTPUT);
  pinMode(BB, OUTPUT);
  pinMode(BC, OUTPUT);
  pinMode(BD, OUTPUT);
  pinMode(DBS, OUTPUT);
  pinMode(ENA, OUTPUT);
  digitalWrite(ENA,LOW); // v0.08 moved to setup(), still works ?
}


void loop()
{
  int i_strobe;

    /* v0.08: PROBABLY NOT NECESSARY???
    digitalWrite(AA,LOW);
    digitalWrite(AB,HIGH);
    digitalWrite(AC,LOW);
    digitalWrite(AD,LOW);
    digitalWrite(BA,LOW);
    digitalWrite(BB,LOW);
    digitalWrite(BC,LOW);
    digitalWrite(BD,LOW);
    / / END of v0.08: PROBABLY NOT NECESSARY??? */
  
  // digitalWrite(ENA,LOW); // move to setup() ?
  
  if (millis() > lastmilli + 800)
  {
    p[0]+=3; 
    p[1]=p[0]+3; 
    p[2]=p[1]*10+10000; 
    p[3]=p[0]*2; 
    lastmilli=millis();
    match_number++; if(match_number >9) match_number = 0;
    BallInPlay++;
    if(BallInPlay > 5)
    {
      BallInPlay = 0;
      Tilt--; Tilt *= -1; // Toggle Tilt/Not Tilt
    }
  }
  pp0=p[0]; // PLAYER ONE
  pp1=p[3]; // PLAYER FOUR
  digitalWrite(DBS,LOW);
  for (i_strobe=0; i_strobe<8; i_strobe++)
  {
    if(i_strobe==0) digit = nFreePlays;
    if(i_strobe==1) digit = nExtraBalls;
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp0%10); pp0/=10; }
    digitalWrite(AD, digit & 8); // bitmask against bin 1000
    digitalWrite(AC, digit & 4); // bitmask against bin 0100
    digitalWrite(AB, digit & 2); // bitmask against bin 0010
    digitalWrite(AA, digit & 1); // bitmask against bin 0001
    if(i_strobe==0) digit = 2; // Credits*10
    if(i_strobe==1) digit = 1; // Credits*1
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp1%10); pp1/=10; }
    digitalWrite(BD, digit & 8); // bitmask against bin 1000
    digitalWrite(BC, digit & 4); // bitmask against bin 0100
    digitalWrite(BB, digit & 2); // bitmask against bin 0010
    digitalWrite(BA, digit & 1); // bitmask against bin 0001

    delayMicroseconds(150);
    digitalWrite(X[i_strobe], HIGH);
    delayMicroseconds(700);
    digitalWrite(X[i_strobe], LOW);
  }
  pp0=p[1]; // PLAYER TWO
  pp1=p[2]; // PLAYER THREE
  digitalWrite(DBS,HIGH);
  for (i_strobe=0; i_strobe<8; i_strobe++)
  {    
    if(i_strobe==0) digit = BallInPlay + Tilt*8; // Ball in play/Game Over/Tilt
    if(i_strobe==1) digit = match_number;
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp0%10); pp0/=10; }
    digitalWrite(AD, digit & 8); // bitmask against bin 1000
    digitalWrite(AC, digit & 4); // bitmask against bin 0100
    digitalWrite(AB, digit & 2); // bitmask against bin 0010
    digitalWrite(AA, digit & 1); // bitmask against bin 0001
    if(i_strobe==0) digit = 0; // N/C
    if(i_strobe==1) digit = 0; // N/C
    if(i_strobe==2) digit = 7; // 1=upper_right 2=lower_right 4=1M 8=dec_pts
    if(i_strobe>2) { digit=(int)(pp1%10); pp1/=10; }
    digitalWrite(BD, digit & 8); // bitmask against bin 1000
    digitalWrite(BC, digit & 4); // bitmask against bin 0100
    digitalWrite(BB, digit & 2); // bitmask against bin 0010
    digitalWrite(BA, digit & 1); // bitmask against bin 0001

    delayMicroseconds(150);
    digitalWrite(X[i_strobe], HIGH);
    delayMicroseconds(700);
    digitalWrite(X[i_strobe], LOW);
  }
}

Ok, so now that I'm finished with an IMO decent sketch - I will consider implementing your other suggestions later on - next step is to let the Playfield Arduino send updates on scores, credits, Ball In Play, Tilt and so on to the Display Arduino mentioned in this thread using Serial Communication. Should I start a new thread on that subject or am I allowed just to edit the Subject field on a reply here?

Thanks!
/SimLego

Should I start a new thread on that subject or am I allowed just to edit the Subject field on a reply here?

Yes, and yes, but... Changing the thread topic isn't a good idea, unless it was wrong to start with or you want to add SOLVED to it.

Changing the thread topic isn't a good idea, unless it was wrong to start with or you want to add SOLVED to it.

Ok, I'll start a new thread then.