Help find the reason for flicker in segment F

I wrote code for a two-digit display using multiplexing by segment. It runs fine, but I’m getting flicker on segment F, and only on segment F. If I refresh every milli, it goes away, but the other segments don’t flicker even refreshing every two or even three millis. I don’t know why it’s segment F. It’s not the first or last segment, and not different in any way from segment E.

I’ve tried switching things around on the Nano and on the display, and the flicker stays with whatever is defined as the segment F driver on the Nano, even if that’s a different pin. I can’t figure out what’s going on. Attached is my code, which is kinda long, but all the action is in the loop() at the bottom.

If anyone has an explanation or a suggestion for something to try, please post.

If it matters, I’m using 1K resistors on the common cathode lines.

const byte SEGMENTS  = 7;    //Number of segments. 8 if using decimal point
const byte DIGITS    = 2;    //Number of displays used - two in this example

   // Define the pins used for the common segments - need not be consecutive

const byte SEGApin = A5;
const byte SEGBpin = A4;
const byte SEGCpin = 2;
const byte SEGDpin = 4;
const byte SEGEpin = 5;
const byte SEGFpin = A3;
const byte SEGGpin = A2;

   // Array allows pins to be addressed in A-G sequence regardless of pin numbers

byte SEGARRAY[]  = {SEGApin, SEGBpin, SEGCpin, SEGDpin, SEGEpin, SEGFpin, SEGGpin};

   // Define pins used by common anodes or common cathodes - add others as needed

const byte CACC0pin  = 6;
const byte CACC1pin  = 3;

   // Array allows using any number of digits - add others as needed

byte  CACCpin[]   = {CACC0pin,CACC1pin};    //The digit's pin number
byte  DIGIT[DIGITS];                        //And its displayed character

   // Use these defs for common cathode displays

const byte SEGON     = HIGH;
const byte SEGOFF    = LOW;
const byte CACCON    = LOW;
const byte CACCOFF   = HIGH;

   // Use these defs for common anode displays

//const byte SEGON     = LOW;
//const byte SEGOFF    = HIGH;
//const byte CACCON    = HIGH;
//const byte CACCOFF   = LOW;

   // The bit value of each segment

const byte segA  = bit(0);
const byte segB  = bit(1);
const byte segC  = bit(2);
const byte segD  = bit(3);
const byte segE  = bit(4);
const byte segF  = bit(5);
const byte segG  = bit(6);
//const byte segH  = bit(7);          //the decimal point if used

   // Segment patterns of the characters "0" thru "9", plus a "10" character

const byte char0 = segA + segB + segC + segD + segE + segF;
const byte char1 = segB + segC;
const byte char2 = segA + segB + segD + segE + segG;
const byte char3 = segA + segB + segC + segD + segG;
const byte char4 = segB + segC + segF + segG;
const byte char5 = segA + segC + segD + segF + segG;
const byte char6 = segA + segC + segD + segE + segF + segG;
const byte char7 = segA + segB + segC;
const byte char8 = segA + segB + segC + segD + segE + segF + segG;
const byte char9 = segA + segB + segC + segD + segF + segG;
const byte char10  = segA + segD + segG;     //used to display "100"

   // Array links a value to its character

byte charArray[] = {char0, char1, char2, char3, char4, char5,
                    char6, char7, char8, char9, char10};

unsigned long PREVmillis;
unsigned long CURmillis;

byte SEGCOUNT;  //Segment counter - count up to SEGMENTS value
byte CURSEG;    //Current segment bit position

byte i;

void setup() {

  for(i = 0; i < SEGMENTS; ++i) {        //Initialize segment pins to OUTPUT, off
    pinMode(SEGARRAY[i], OUTPUT);
    digitalWrite(SEGARRAY[i], SEGOFF);
  }

  for(i = 0; i < DIGITS; ++i) {          //Same for CACC pins
    pinMode(CACCpin[i],OUTPUT);
    digitalWrite(CACCpin[i],CACCOFF);
  }

  for(i=0; i < DIGITS; ++i) {            //Set all displays to "0"
    DIGIT[i] = char0;
  }

     // Initialize so first refresh will re-init everything

  SEGCOUNT   = SEGMENTS - 1;             //Segments counter - set to end
  CURSEG     = bit(SEGMENTS -1);         //Bit position of last segment

  PREVmillis = millis();

     // Below for demo purposes - normally to display a VALUE 0-9, it would be
     //     DIGIT[i] = charArray[VALUE]

  DIGIT[0]    = char8;                     //Current segment pattern of Digit0 "8"
  DIGIT[1]    = char8;                     //Current segment pattern of Digit1 "8"

}


void loop() {

  CURmillis = millis();

  if ((CURmillis - PREVmillis) > 1) {    // 2ms refresh period = 71 Hz per segment

    PREVmillis = CURmillis;

       // Turn the current segment OFF while making changes - prevents ghosting

    digitalWrite(SEGARRAY[SEGCOUNT], SEGOFF);

       //This section selects the next segment

    CURSEG     = CURSEG << 1;            //shift left to next bit position
    SEGCOUNT++;                          //used as index into SEGARRAY
    if (SEGCOUNT == SEGMENTS) {          //if done with last segment, start over
      SEGCOUNT = 0;                      //re-initialize
      CURSEG   = 1;
    }

       //This section turns the CA or CC pins on/off per the patterns of the characters
       //If the CURSEG bit of the DIGIT[n] segment pattern is 1, turn on the CACCpin[n]

    for(i = 0; i < DIGITS; ++i) {
      if (DIGIT[i] & CURSEG) digitalWrite(CACCpin[i], CACCON);
      else digitalWrite(CACCpin[i], CACCOFF);
    }

       // Now turn the new segment driver ON

    digitalWrite(SEGARRAY[SEGCOUNT], SEGON);
  }
}

Nevermind. The problem was caused by the fact that the millis() value periodically increments by 2, which was producing a short ON time for segment F. I don’t know why just segment F. Probably some kind of strange beat between the 2X millis increment and the processing of segment F within the algorithm Anyway, I changed the beginning of the loop so it accumulates the number of individual changes in the millis value, by any amount. So now everything is stable at an interval of three millis changes between segment updates.

byte milliCount = 0;                     //Number of millis changes so far
const byte Refresh   = 3;             //Number of millis changes between segments 

void loop() {

  CURmillis = millis();

  if (CURmillis != PREVmillis) {
    milliCount++;
    PREVmillis = CURmillis;
  }

  if (milliCount == Refresh) {
    milliCount = 0;
    // etc.

For a multiplexed display, I would anyway consider driving it from a timer so it is insensitive to heavy calculations in the loop () or blocking activity such as waiting for a response from an NTP server etc. etc.

For optimum brightness, but needing more resistors, I'd probably also have multiplexed at the granularity of 1 digit and not 1 segment. The difference would be even more noticeable with just a single seven segment display, possibly by a factor of 7 or 8 depending on how you handle the decimal point.

millis() value periodically increments by 2… I changed the beginning of the loop so it accumulates the number of individual changes in the millis value

You could also switch to using micros().

For optimum brightness, but needing more resistors, I'd probably also have multiplexed at the granularity of 1 digit and not 1 segment.

Problem with that is you would need to increase the value of the series resistors, to avoid overloading the arduino pins connected to the digit commons. So each segment would be on for a higher % of the time, but the instantaneous current through each segment would be less, one change cancelling out the other. The limiting factor here is the current capacity of the arduino pins connected to the digit common pins. Of course, a couple of transistors would fix that, but so would a max7219!

Yes, I think brightness is the same at the same average current whether multiplexing by segment or by digit. But the refresh rate needs to be twice as fast (including the DP) by segment. The tradeoff is the lower number of parts required since only one resistor per digit is required. In this example I used 1K resistors, so the maximum current per segment line is about 13mA, which is not a problem for the GPIO pins. Multiplexing by digit, I think you would get the same brightness with 2K resistors and half the refresh frequency.