PORT Manipulation of 7 segments and common anode pins

This code is working but I want to know how will I change the pin assignments of the segment pins and common anode pins in PORTB and PORTD of the the Arduino UNO. Originally, my segment pins assignments starts from pin 0 to pin 6 and common anode pins are from 8, 9 10. I tried changing the pin assignments by manipulating this part of the code:

for(int i=2; i>=0; i--){
    PORTB =  8 + segDigit[i]; //switching between digits to turn on
    PORTD = nums[dig[i]];
    delay(1);
    PORTD = 0b1111111;    
   }

I changed the '8' several pin numbers and adjusted the pin connections but all I get is a distorted display of the 7 segments. How is that? How do i shift the pin assignments?

Here is the complete working code: (segment pins: a-0, b-1, c-2, d-3, e-4, f-5, g-6; common anode pins: 10 (one's digit), 9 (tenth's digit) , 8 (hundredth's digit)

int num=0;
int dig[3];
const int segs[8] = {0,1,2,3,4,5,6,7} ;   // set the pins of the 7segment displays
const byte nums[10] = { 0b1000000,0b1111001,0b0100100,0b0110000,0b0011001,0b0010010,0b0000010,0b1111000,0b0000000,0b0010000};
const byte segDigit[3] = { B00000011, B00000101, B00000110};

void setup() {
  DDRB = 0xFF;   
  PORTB = 0x00;  // initializing port B as output and makes the port drive the 8 segments in the LED

  DDRD = 0xFF;
  PORTD = 0x00; // initializing port D as output and makes the port drive the transistors to select the digit to be displayed
 
   // initialize Timer1
   cli();          // disable global interrupts
   TCCR1A = 0;     // set entire TCCR1A register to 0
   TCCR1B = 0;     // set entire TCCR1B register to 0 
     
   // set compare match register to desired timer count:
   OCR1A = 24999; // (0.1/ (1/(16 MHz/1024)))-1= 24999
   // turning on CTC :
   TCCR1B |= (1 << WGM12);
   // Set CS10 and CS11 bits for 64 prescaler:
   TCCR1B |= (1 << CS10);
   TCCR1B |= (1 << CS11);
   // enable timer compare interrupt:
   TIMSK1 |= (1 << OCIE1A);
   sei();          // enable global interrupts
 }


void loop(){

    dig[2] = num/100;                       //Hundredths digit Common Anode Pin 8
    dig[1] = num/10 - dig[2]*10;            //Tenths digit Common Anode Pin 9
    dig[0] = num%10;                        
    
   for(int i=2; i>=0; i--){
    PORTB =  8 + segDigit[i]; //switching between digits to turn on
    PORTD = nums[dig[i]];
    delay(1);
    PORTD = 0b1111111;    
   }
   //delay(1000);

    
    if(num == 1000)                       //checks the num value and sets it to zero if equal to 1000
    num = 0;
}

ISR(TIMER1_COMPA_vect)                    //Interrupt Service Routine
{
  num++; //increment value by 1 
}

This is the kind of situation that digitalWrite() was invented for. Your segs[] array goes unused because the pin assigments are hard coded in the order that is dictated by the processor architecture.

Anything that you do beyond this, will be re-inventing digitalWrite(). Of course it can be done, as a learning exercise. At a minimum, you should learn to use symbolic names for the segments and other important elements of the display before you have a big mess of code to deal with.

aarg:
This is the kind of situation that digitalWrite() was invented for. Your segs[] array goes unused because the pin assigments are hard coded in the order that is dictated by the processor architecture.

Anything that you do beyond this, will be re-inventing digitalWrite(). Of course it can be done, as a learning exercise. At a minimum, you should learn to use symbolic names for the segments and other important elements of the display before you have a big mess of code to deal with.

Yes, I know that there is an easier way of doing it using digitalWrite() function and I have been using it for quite a time now but this is what my professor us making us do for a laboratory exercise. My professor requires us to use that code because it doesn't take up much process/time (i dont know the term exactly) because it lights the segments in one command line unlike using digitalWrite() wherein I have to make a function to light the segments and use bitRead() and call it once again in void loop() that it sometimes produces shadows in the segments if the code for turning off the segments is not placed in the right line of command of the whole code.

joshuaevangelista:
Yes, I know that there is an easier way of doing it using digitalWrite() function and I have been using it for quite a time now but this is what my professor us making us do for a laboratory exercise. My professor requires us to use that code because it doesn't take up much process/time (i dont know the term exactly) because it lights the segments in one command line unlike using digitalWrite() wherein I have to make a function to light the segments and use bitRead() and call it once again in void loop() that it sometimes produces shadows in the segments if the code for turning off the segments is not placed in the right line of command of the whole code.

Well, it goes without saying that lines have to go where they belong. :slight_smile: Why that would force you to use one particular approach over another is beyond me. All I can say is that there is sometimes more than one solution to a problem. A problem that you create can be solved by eliminating it.

Generally, you do not have to use bitRead with digitalWrite. I don't understand why you think that.

I've written many different versions of 7 segment scans. None of them used (or needed) port manipulation. The processor overhead for digitalWrite is very small. I have run my routines at scan rates up to 500 Hz and never had any shadows ("ghosting").

If you have to use port manipulation as an academic exercise (which you have already done!), but you need to swap pins, you will want to use symbolic names for the segments, to associate them with hardware pins. I mentioned that already. In my opinion, that is also a good answer to your original question.

For example, if you know that segment A is in bit 3 of Port A, you can define:

const byte segmentA = _BV(3);

or the more "old school" way:

#define segmentA = 0x08

Good luck!

Thank you with that! That pretty much answers my original question but I have another working code (given to me by classmate but did not explain) that has a different approach on selecting the pin assignments. It's not totally different from mine, the only difference is that he did not put the code of lighting the segments in a for loop but instead repeated the same code 3 times which is also pretty much in the same manner as mine. And as for his pin assignments (which is the main purpose of this reply) is that he used shifting of bits. His pin assigments are: segment pins: a-D9, b-D8, c-D7, d-D6, e-D5, f-D4, g-D3; common anode pins: D0,D1,D2. I want to know if you are familiar of how this works because I think this also is a possible and easier solution than defining variables. It wasn't explained to me clearly and I cannot contact my classmate.

Here is the code

byte nums[10] = {0x81, 0xCF, 0x92, 0x86, 0xCC, 0xA4, 0xA0, 0x8F, 0x80, 0x8C}; 
//Representation of 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 using common anode and PNP Transistor
// Pin assignment a-D9, b-D8, c-D7, d-D6, e-D5, f-D4, g-D3

int buffer[3]; //initializing the array
// 3 buffers assigned for the hundreds, tens, and ones

int num = 0;
// Start of number


void setup() 
// Pin setup, where the output pins are assigned 

{
  DDRB = 0xFF;   
  PORTB = 0x00;  // initializing port B as output and makes the port drive the 8 segments in the LED

  DDRD = 0xFF;
  PORTD = 0x00; // initializing port D as output and makes the port drive the transistors to select the digit to be displayed
    
   // initialize Timer1
   cli();          // disable global interrupts
   TCCR1A = 0;     // set entire TCCR1A register to 0
   TCCR1B = 0;     // set entire TCCR1B register to 0 
     
   // set compare match register to desired timer count:
   OCR1A = 24999; // (0.1/ (1/(16 MHz/64)))-1= 1562.5
   // turning on CTC :
   TCCR1B |= (1 << WGM12);
   // Set CS10 and CS11 bits for 64 prescaler:
   TCCR1B |= (1 << CS10);
   TCCR1B |= (1 << CS11);
   // enable timer compare interrupt:
   TIMSK1 |= (1 << OCIE1A);
   sei();          // enable global interrupts
}
void loop()

{
 
buffer[2] = ((num/100)%10); // For the 'hundredth' collumn 
buffer[1] = ((num/10)%10); //For the 'tenth' collumn 
buffer[0] = num%10; //For the 'ones' collumn 
  
   PORTD = (nums[buffer[2]] << 3) + B00000011;//1st digit (hundreds) //1st transistor is turned on
   PORTB = nums[buffer[2]] >> 5; 
    delay(1);
   PORTD = (nums[buffer[1]] << 3) + B00000101;  //2nd digit (tens) //2nd transistor is turned on
   PORTB = nums[buffer[1]] >> 5; 
    delay(1);
   PORTD = (nums[buffer[0]] << 3) + B00000110;  //3rd digit (ones)//3rd transistor is turned on
   PORTB = nums[buffer[0]] >> 5; 
    delay(1);    

}

ISR(TIMER1_COMPA_vect)
{
  num++; //increment value by 1 
}

aarg:
Well, it goes without saying that lines have to go where they belong. :slight_smile: Why that would force you to use one particular approach over another is beyond me. All I can say is that there is sometimes more than one solution to a problem. A problem that you create can be solved by eliminating it.

Generally, you do not have to use bitRead with digitalWrite. I don't understand why you think that.

I've written many different versions of 7 segment scans. None of them used (or needed) port manipulation. The processor overhead for digitalWrite is very small. I have run my routines at scan rates up to 500 Hz and never had any shadows ("ghosting").

If you have to use port manipulation as an academic exercise (which you have already done!), but you need to swap pins, you will want to use symbolic names for the segments, to associate them with hardware pins. I mentioned that already. In my opinion, that is also a good answer to your original question.

For example, if you know that segment A is in bit 3 of Port A, you can define:

const byte segmentA = _BV(3);

or the more "old school" way:

#define segmentA = 0x08

Good luck!

I have posted a separate reply regarding your answer because I cannot include codes in a quick reply. Hope you were able to read it.

Well, your classmate's code has no symbolic names at all. The segments are only referred to in a comment line, and the pins are hard coded. So that person will have the same difficulties in reassigning pins as you do, if that is part of the assignment.

It's not about the method that you use to write the pins, it's about how you define the data that you send to them. You see, actually, reassigning a pin to a different segment is as easy as redefining your numeric font (strong hint).

But it sounds like both of you will get at least a B grade. I don't want to hand you the A. I've given you two strong hints to help you to earn it.

I don't agree that your classmates solution achieves any gain by avoiding variables. If I were a professor, I would subtract marks for that sort of thing. Maybe not in this case, because it is well documented.