Help with Port Manipulation and Incrementing a Count Please!

Hello, friends,

I'm in an introductory microprocessors class, and I was wondering if someone here could help me understand an assignment.

The lab I'm doing requires me to create a numeric counter that shows up on a seven-segment display and increments at the push of a button. I did this for a previous lab using arrays and digitalWrite, but now I have to do the same thing using port manipulation. I used the same code to increment the counter as the lab that worked, but for some reason, I can't get it to work with the port manipulation. The counter illuminates a zero but not any other number.

Can you please take a look at what might be causing this?

/
 * Experiment 9: 7-Segment Counter Using Port Manipulation
 * References Used: http://www.electroschematics.com/9636/arduino-segment-display-counter/
 *                  https://www.arduino.cc/en/Reference/PortManipulation
 */

 // This program writes a cycle of zero through nine to a seven-segment display using port manipulation rather than an array

int input_button = 11;

bool button = 1;
bool prev_button = 1;
int count = 0;
int count_out = 0;
int numeral_out = 0;
int i = 0;
int var = 0;


void setup()

{
  Serial.begin ( 9600 );
  
  pinMode (11, INPUT_PULLUP);
  
  DDRD = B11111100;
  DDRB = B000001;  

}

int button_count()

{
  prev_button = button;                   //Moving the count along
  button = digitalRead (input_button);    //Reading the button from pin 11

  if (button == 0)
  {
    delay (100);                          //Allowing time before next cycle
    if (button == 0)
    {
      if (button != prev_button)          //Adding the stipulation that the button only reads as high upon initial press
      {
       count++;
      }
  if (count > 9)                          //Sending the back to zero at the end of the cycle so that it will repeat indefinitely
  {
    count = 0;
  }
    }

  return count;
  Serial.print (count);
  
}
}


void loop()

{

  if (count == 0)
      {
        PORTD = B11111100;
        PORTB = B000000;
      }
  else if (count == 1)
      {
        PORTD = B11111100;
        PORTB = B000001;
      }
  else if (count == 2)
      {
       PORTD = B01101100;
       PORTB = B000001;
      }
  else if (count == 3)
      {
       PORTD = B00111100;
       PORTB = B000001;  
      }
  else if (count == 4)
      {
       PORTD = B10011000;
       PORTB = B000001; 
      }
  else if (count == 5)
      {
       PORTD = B10011010;
       PORTB = B000001; 
      }
  else if (count == 6)
      {
       PORTD = B11110100;
       PORTB = B000001; 
      }
  else if (count == 7)
      {
       PORTD = B00011100;
       PORTB = B000000; 
      }
  else if (count == 8)
      {
       PORTD = B11111100;
       PORTB = B000001;
      }
  else if (count == 9)
      {
       PORTD = B11011100;
       PORTB = B000001;
      }
}

You don't have free access to all the pins in PortD or PortB on an Uno. Some of the pins have special uses and you should not be writing to them

To set some pins on a port use the style

PORTD |= 0b00110000; where the bits you want HIGH are set as 1 and the others won't change

To clear some pins use the style

PORTD &= 0b11001111; where the bits you want LOW are set as 0 and the others won't change

...R

Google “Arduino pinout” to find a chart that shows the port name and bit number for each of the Arduino pins. For example, this chart for the Arduino UNO:

http://www.pighixxx.com/test/wp-content/uploads/2017/05/uno.png

Each digital port has has a one-letter name and up to eight bits, labeled 0 (LSB) through 7 (MSB). Each bit can represent a digital I/O pin.

Each port has three registers (replace ‘x’ with the port name):
PORTx for OUTPUT
DDRx for Data Direction Register (set by pinMode())
PINx for INPUT (as a special feature, writing 1 to a bit in the PINx register will toggle the output)

Robin2: You don't have free access to all the pins in PortD or PortB on an Uno. Some of the pins have special uses and you should not be writing to them

To set some pins on a port use the style

PORTD |= 0b00110000; where the bits you want HIGH are set as 1 and the others won't change

To clear some pins use the style

PORTD &= 0b11001111; where the bits you want LOW are set as 0 and the others won't change

...R

Thanks for the info. I'll try that right away.

So, if the assignment says we must use ports to write to a 7-segment display, will I have to choose seven pins that work out of all of the ports collectively and use them together? I already am not using either pin 0 or pin 1 in Port D, but upon further research it also says not to use pins 6 or 7. How can I tell if there are any other pins I should not use for certain things?

Your code looks basically adequate, and is a fine example of the way (I think) we'd LIKE to see questions about class assignments - includes properly formatted code that is almost complete, and a clear description of the problem...

  pinMode (11, INPUT_PULLUP);
  
  DDRD = B11111100;
  DDRB = B000001;

I think that your problem is that pinMode() also manipulates the DDR bits (DDRB3 for pin 11), so setting the DDR PORT register AFTER you've done the pinMode() probably stops the button from working. (and/or writing all of PORTB undoes the INPUT_PULLUP, which is a bit more mysterious.) Using the "&=" and "|=" to set the port bits will fix this. I don't see any reason to avoid pins 6 and 7. Probably the intent was for you to use all of PORTD, but avoiding 0/1 lets you continue to to debugging via Serial.print() (hint!) If you know about arrays, you should probably use them for translating the number to the PORTx bit values, rather than the string of 'if' statements...

audiojulie: Thanks for the info. I'll try that right away.

So, if the assignment says we must use ports to write to a 7-segment display, will I have to choose seven pins that work out of all of the ports collectively and use them together? I already am not using either pin 0 or pin 1 in Port D, but upon further research it also says not to use pins 6 or 7. How can I tell if there are any other pins I should not use for certain things?

Where did it say not to use 6 or 7? That's a bit odd. I think you either misunderstood or you should avoid that source.

Delta_G:
Where did it say not to use 6 or 7? That’s a bit odd. I think you either misunderstood or you should avoid that source.

“The two high bits (6 & 7) map to the crystal pins and are not usable,” says the Port Manipulation page on this site, but I think I took it out of context.

I’ve tried really hard, but I’m still confused. I mapped out what I was going to do (see the image attached) and implemented it in the code, but I feel like I’m missing a key concept. Only segment E illuminates now, and nothing happens when the button is pressed. Maybe it’s a syntax error?

I would have liked to use arrays, but I’m still not confident that I know much about them, and this and another lab, as well as the final project for the class (which is based upon the two) are due tomorrow morning. My lecture instructor is an adjunct, who is brand new to teaching, so he doesn’t offer extra help. My lab instructor thought this code should have worked. None of my peers had gotten it working 100% correctly. I probably should have thought to come to the forum earlier, but as of right now, I have a long 11 hours ahead of me. Sorry to write a novel, but I feel obligated to explain why I are the dumb.

Thanks so much for the help!

 // This program writes a cycle of zero through nine to a seven-segment display using port manipulation rather than an array

int input_button = 11;

bool button = 1;
bool prev_button = 1;
int count = 0;
int count_out = 0;
int numeral_out = 0;
int i = 0;
int var = 0;


void setup()

{
  Serial.begin ( 9600 );
  
  DDRD = B11111100;
  DDRB = B000001;

}

int button_count()

{
  prev_button = button;                   //Moving the count along
  button = digitalRead (input_button);    //Reading the button from pin 11
 
  if (button == 0)
  {
    delay (100);                          //Allowing time before next cycle
    if (button == 0)
    {
      if (button != prev_button)          //Adding the stipulation that the button only reads as high upon initial press
      {
       count++;
      }
  if (count > 9)                          //Sending the count back to zero at the end of the cycle so that it will repeat indefinitely
  {
    count = 0;
  }
    }

  return count;
  Serial.print (count);
  
}
}


void loop()

{

  if (count == 0)
      {
        PORTD |= 0b01000000;
        PORTD &= 0b11111100;
        
        PORTB |= 0b000000;
        PORTB &= 0b001000;
      }
  else if (count == 1)
      {
        PORTD |= 0b00000000;
        PORTD &= 0b00011000;

        PORTB |= 0b000000;
        PORTB &= 0b001001;
      }
  else if (count == 2)
      {
       PORTD |= 0b01100000;
       PORTD &= 0b11101000;
       
       PORTB |= 0b000001;
       PORTB &= 0b001001;
      }
  else if (count == 3)
      {
       PORTD |= 0b00010000;
       PORTD &= 0b10111100;

       PORTB |= 0b000000;
       PORTB &= 0b001001;
      }
  else if (count == 4)
      {
       PORTD |= 0b10000000;
       PORTD &= 0b11011000;

       PORTB |= 0b000000;
       PORTB &= 0b001001;
      }
  else if (count == 5)
      {
       PORTD |= 0b00100100;
       PORTD &= 0b11110100;

       PORTB |= 0b000000;
       PORTB &= 0b001001;
      }
  else if (count == 6)
      {
       PORTD |= 0b01000000;
       PORTD &= 0b11111100;

       PORTB |= 0b000000;
       PORTB &= 0b001001;
      }
  else if (count == 7)
      {
       PORTD |= 0b00011100;
       PORTD &= 0b00011100;

       PORTB |= 0b000001;
       PORTB &= 0b001001;
      }
  else if (count == 8)
      {
       PORTD |= 0b11100000;
       PORTD &= 0b11111100;

       PORTB |= 0b000001;
       PORTB &= 0b001001;
      }
  else if (count == 9)
      {
       PORTD |= 0b00000000;
       PORTD &= 0b11110100;

       PORTB |= 0b000000;
       PORTB &= 0b001001;
      }
}

audiojulie: "The two high bits (6 & 7) map to the crystal pins and are not usable," says the Port Manipulation page on this site, but I think I took it out of context.

Yeah, it's talking about bits 6 and 7 on PORTB. But those aren't pins that are broken out on the board. PORTD is the only port that has 8 pins on it, but 0 and 1 are the serial pins so you have to think about those.

Arduino unfortunately has no single PORT that has more than 6 usable pins so you're stuck to using pins in at least two different ports. Do carefully check the pin mapping.

To get a feel for port manipulation, why don't you start by modifying blink to simply blink one (or all) of your display pins? That's a quick and easy playground for that purpose. Blinking a single pin also helps you confirm you have your mapping correct.

+1 on the |= and &= operators. You don't want to mess around with pins you're not using at that moment. Only manipulate the relevant pins, or you may mess up other things in the process. Makes for horrible debugging.

I found it a bit counter intuitive but a high bit makes an output HIGH, and makes in INPUT activate the internal pull-up. A low bit makes an output low, and an input deactivate the internal pull-up. pinMode should only be changing the DDR register of the relevant pin, but if you do a DDR write to the complete register and you write a 0 to that pin, it deactivates the pull-up, which is what your sketch seems to do.

Good luck with the assignment!

A few points:

At the end of button_count function:

return count;
  Serial.print (count);

Think about what the return statement does. That Serial.print there is pretty useless don't you think?

        PORTD |= 0b01000000;
        PORTD &= 0b11111100;

To turn a bit on you have the top one right. To turn a bit off you have to get the opposite bit. Bitwise NOT.

PORTD &= ~0b11111100

Although it isn't clear what you want to do with bit 6 there because it's being turned on in the top one and back off in the bottom.

Oh boy...

I changed the setup loop to read...

  DDRD = 0b11111100;
  DDRB = 0b010000;

  pinMode(11, INPUT_PULLUP);

I also added the ~ operator to each of the &= functions that I'd put in the wrong way, and then I switched all of the highs on pin 11 to lows, so that when they are inverted, it will stay an input pullup. I took out the useless Serial.print, too, but for some reason, after all of that, the same one segment lights up and doesn't even increment. I tried modifying Blink to use ports, by setting it up a similar way. I feel like there's something huge I'm missing, but I can't put my finger on it...

I read all about bit math, and I found a lot of interesting things that don't pertain to this project, but I also came across this...

    void setup()
    {
        // set pins 1 (serial transmit) and 2..7 as output,
        // but leave pin 0 (serial receive) as input
        // (otherwise serial port will stop working!) ...
        DDRD = B11111110;  // digital pins 7,6,5,4,3,2,1,0
        // set pins 8..13 as output...
        DDRB = B00111111;  // digital pins -,-,13,12,11,10,9,8
        // Turn off digital output pins 2..7 ...
        PORTD &= B00000011;   // turns off 2..7, but leaves pins 0 and 1 alone
        // Write simultaneously to pins 8..13...
        PORTB = B00111000;   // turns on 13,12,11; turns off 10,9,8
    }

I'm confused as to why PORTB has more bits than pins. Wouldn't the console show an error if I had the wrong number of bits?

I skimmed this, too, but didn't find anything about this type of application.

audiojulie: I'm confused as to why PORTB has more bits than pins. Wouldn't the console show an error if I had the wrong number of bits?

All bytes have 8 bits. Sometimes only some of them are important. But all bytes have 8 bits. The console isn't going to show you an error for a legal line of code. It gives errors for bad syntax or doing things that are against the compilers rules. But it is not there to protect you from doing something stupid.

You have to understand that each bit corresponds to one pin. Put the code aside for a minute and get out a sheet of paper. Figure out which pins need to be HIGH and LOW for a given digit to display. Then figure out which bits in your bytes correspond to those pins. What you are doing now just seems random.

Take this for instance.

PORTD |= 0b01000000;
        PORTD &= ~0b11111100;

So the first line turns pin 6 high but then the second line turns pins 2 through 7 all low (including 6). What was the point of writing it high if you are just going to write it low on the next line?

What you want to do isn't really that hard, and it doesn't require much bitmath really. You just need to get straight what your port needs to look like for each digit. If you'll sit down with a piece of paper and map the pins to bytes you should end up with two bytes per digit, one for the pins on PORTD and one for the pins on PORTB. Once you have that figured out on paper, writing the code is just a matter of putting the right two in each one of those else if statements.

Delta_G: The console isn't going to show you an error for a legal line of code. It gives errors for bad syntax or doing things that are against the compilers rules. But it is not there to protect you from doing something stupid.

One way to make the compiler a little more helpful is to go into Preferences and set the "Compiler warnings" to "All" instead of "Default". This will warn you when your sketch does something that the compiler thinks might be a mistake but isn't strictly illegal. You should strive to write code that doesn't trigger any warnings but don't obsess over it at the expense of getting the assignment done. :)

Can you please post your latest code.

In the code of post #1 and post #6 you never call the button_count function in loop.

Delta_G: Put the code aside for a minute and get out a sheet of paper. Figure out which pins need to be HIGH and LOW for a given digit to display. Then figure out which bits in your bytes correspond to those pins. What you are doing now just seems random.

I did what I think this means earlier; it's attached to post #6.

The numbers I wrote out were which bits need to go high from the last state and which need to go low, this includes changing from 9 to 0, which is probably not right.

For each case, the bits I needed to go high used the |=. For the ones I needed to go low, I used the &=.

Can you look at the image attached to post #6 and tell me if that's what you mean?

cattledog: Can you please post your latest code. In the code of post #1 and post #6 you never call the button_count function in loop.

I didn't think I needed to call it, because I was returning the count. I just tried adding a call to button_count(); to the beginning of the void loop, and it didn't do anything. My code is almost the same as it was before.

I'll help you out a little bit here. You're trying. That's worth something.

Say we have a byte pattern representing a digit in 7 segment and pins 8 and 9 on Arduino are connected to the segments represented by the middle two bits in the pattern.

So I want to take the middle 2 bits of one byte and put them in the bottom 2 bits of another byte whilst not changing any of the other 6 bits. So I'm applying a 2 bit pattern to an 8 bit port.

byte pattern = 0b11010110;

//  First mask off the 2 bits from the pattern that I want.

byte maskedPattern |= 0b00011000; 

// shift it down into postition to match where it goes in the port
maskedPattern >>= 3;

//  Then need a copy of the PORT

byte temp = PORTB;

// mask out the two pins that I want to write my pattern to

temp &= 0b11111100;

// or in my pattern to set the bits that need to be set

temp |= maskedPattern;

// write it back to the port

PORTB = temp;

Follow through there on a piece of paper and write out the bits and make sure you understand what's happening there. Work through it a few times on paper with different starting values for PORTB and/or pattern. Study that and understand it and think about setting up one pattern per digit and applying the bits of it you need to the ports your segments are hooked to. Think about how to arrange your segments on the pins to make this whole thing easiest and most efficient.

Following on from what @Delta_G has said it would probably be a good idea to have two byte variables in your program that hold the pattern of bits that you want to create - these bytes would be a proxy or image for the settings in the PORTs. You can over-write those bytes any way you like to get your required bit pattern. Then when you have the correct bit pattern in those bytes you can have a little routine to transfer that pattern to the I/O Ports using the techniques shown by @Delta_G and in Reply #1

This also has the advantage that you can print the values in the proxy bytes for debugging purposes.

...R