MCP23017 + 4 digit LED display FLICKER problem

Hi guys :slight_smile:

Having a problem with display flickering using this code:

#include <Wire.h>
#include "Adafruit_MCP23017.h"

// Basic pin reading and pullup test for the MCP23017 I/O expander
// public domain!

// Connect pin #12 of the expander to Analog 5 (i2c clock)
// Connect pin #13 of the expander to Analog 4 (i2c data)
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
// Connect pin #9 of the expander to 5V (power)
// Connect pin #10 of the expander to ground (common ground)
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

// Define MCP23017 pins connected to the LED display
// Digits
byte digits[4] = {8,9,10,11};
// Segments A-G and DP.
// DP (pin 2) will always be lit on the 2nd digit.
byte segments[8] = {7,5,3,1,0,6,4,2};

// define numbers, from SEGMENT A-G
byte numbers[10][7] = {
  {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,LOW},  // zero
  {LOW,HIGH,HIGH,LOW,LOW,LOW,LOW},      // one
  {HIGH,HIGH,LOW,HIGH,HIGH,LOW,HIGH},   // two
  {HIGH,HIGH,HIGH,HIGH,LOW,LOW,HIGH},   // three
  {LOW,HIGH,HIGH,LOW,LOW,HIGH,HIGH},    // four
  {HIGH,LOW,HIGH,HIGH,LOW,HIGH,HIGH},   // five
  {HIGH,LOW,HIGH,HIGH,HIGH,HIGH,HIGH},  // six
  {HIGH,HIGH,HIGH,LOW,LOW,LOW,LOW},     // seven
  {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}, // eight
  {HIGH,HIGH,HIGH,HIGH,LOW,HIGH,HIGH}   // nine
};

// holds the value of the current measured voltage
float voltage = 10.98;

Adafruit_MCP23017 mcp;
  
void setup() {
  mcp.begin();      // use default address 0x20
  // Set all required MCP23017 pins to output
  // Digits
  for (byte digit = 0; digit < 4; digit++) {
    mcp.pinMode(digits[digit], OUTPUT);
      mcp.digitalWrite(digits[digit], LOW);
  }
  // Segments
  for (byte segment = 0; segment < 8; segment++) {
    mcp.pinMode(segments[segment], OUTPUT);
      mcp.digitalWrite(segments[segment], LOW);
  }
}


void loop() {
  displayNumber(voltage);

}

/*
 * Light a number in the display
 * @params: float number
*/
void displayNumber(float number) {
  char outstr[5];
  int pos = 0;
  int c, n, nbr;
  
  dtostrf(number,5, 2, outstr);

    // iterate through all chars in the array
    for (c = 0; c < 5; c++) {
      if (isDigit(outstr[c])) {
        nbr = outstr[c]-'0';
        // light the segments that form the number
        for (n = 0; n < 7; n++) {
          mcp.digitalWrite(segments[n], numbers[nbr][n]);
        }
        if (pos==1) {
          mcp.digitalWrite(segments[7], HIGH);
        }
        else {
          mcp.digitalWrite(segments[7], LOW);
        }
  
        // turn on the corresponding digit
        mcp.digitalWrite(digits[pos], HIGH);
        delay(5);
        mcp.digitalWrite(digits[pos], LOW);
        pos++;
      }
    }
}

/*
 * Turn off all segments in the display
*/
void turnAllOff() {
  for (int n=0; n<7; n++) {
    mcp.digitalWrite(segments[n], LOW);
  }
}

Basically it sets the necessary segments ON and lights the digit, waits for 5ms, turns off the digit and moves on to the next.
Although the logic looks sound to me :grin: I'm experiencing heavy flickering in the display.
Little help on how to overcome this would surely be appreciated :wink:

Thanks in advance,
Pedro

Reforged the code so I could do the exact sequence required:

1 - Turn off all segments
2 - Turn off all other digits
3 - Turn on current digit
4 - Turn on required segments
5 - let it show for 2ms (delay)
6 - repeat 1-5 for the next digit

/*
 * Light a number in the display
 * @params: float number
*/
void displayNumber(float number) {
  char outstr[5];
  int pos = 0;
  int c, n, nbr;
  
  dtostrf(number,5, 2, outstr);
  // iterate through all chars in the array
  for (c = 0; c < 5; c++) {
    if (isDigit(outstr[c])) {
      nbr = outstr[c]-'0';
      showDigit(pos,nbr);
      delay(2);
    }
    if (c!=2) {
      pos++;
    }
  }
}

void showDigit(byte pos, byte value) {
  int n;
  turnAllOff();
  switch (pos) {
    case 0:
      mcp.digitalWrite(digits[1], LOW);
      mcp.digitalWrite(digits[2], LOW);
      mcp.digitalWrite(digits[3], LOW);
      mcp.digitalWrite(digits[0], HIGH);
      for (n = 0; n < 7; n++) {
        mcp.digitalWrite(segments[n], numbers[value][n]);
      }
      mcp.digitalWrite(segments[7], LOW);
      break;
    case 1:
      mcp.digitalWrite(digits[0], LOW);
      mcp.digitalWrite(digits[2], LOW);
      mcp.digitalWrite(digits[3], LOW);
      mcp.digitalWrite(digits[1], HIGH);
      for (n = 0; n < 7; n++) {
        mcp.digitalWrite(segments[n], numbers[value][n]);
      }
      mcp.digitalWrite(segments[7], HIGH);
      break;
    case 2:
      mcp.digitalWrite(digits[0], LOW);
      mcp.digitalWrite(digits[1], LOW);
      mcp.digitalWrite(digits[3], LOW);
      mcp.digitalWrite(digits[2], HIGH);
      for (n = 0; n < 7; n++) {
        mcp.digitalWrite(segments[n], numbers[value][n]);
      }
      mcp.digitalWrite(segments[7], LOW);
      break;
    case 3:
      mcp.digitalWrite(digits[0], LOW);
      mcp.digitalWrite(digits[1], LOW);
      mcp.digitalWrite(digits[2], LOW);
      mcp.digitalWrite(digits[3], HIGH);
      for (n = 0; n < 7; n++) {
        mcp.digitalWrite(segments[n], numbers[value][n]);
      }
      mcp.digitalWrite(segments[7], LOW);
      break;
  }
}

/*
 * Turn off all segments in the display
*/
void turnAllOff() {
  for (int n=0; n<8; n++) {
    mcp.digitalWrite(segments[n], LOW);
  }
}

Same flickering ... :disappointed_relieved:

Hi Pedro. Post the WHOLE sketch please. It could be that what's causing the flickering is not in the parts you posted.

Paul

EDIT: Sorry, I see you have just replaced that those functions in your second post, and the rest of the sketch is unchanged?

If so, I think it might just be that the sketch is a little inefficient, performing some uneccessary and potentially quite slow operations when they could be avoided. Such as the dtostrf() fucnction and the splitting of the number into 4 digits. Those only need to be performed when the voltage changes. If you make some extra variables to hold the previous voltage (float), and an array holding the 4 digit values, that might help.

Also, that Adafruit library has a writeGPIOAB() function which allows you to write to all 16 outputs as a single operation, which should be much more efficient. You can make a uint16_t variable and write all the HIGH (=1) and LOW (=0) values to that using bitWrite() and then send them all to the chip with writeGPIOAB().

Paul

Thank you Paul for taking the time to look into this :slight_smile:
Your suggestions are much appreciated and make perfect sense :slight_smile: I'll surely try them out and report back with code mods :wink:

Hi Paul,

As promised :wink:

Also included is the technique I used for reading the voltage of a battery (in this case a simple test with a 9V battery)
and, of course, display it using a decent :grin: refresh rate.
So... no more ghosting on that display :sunglasses:

Some of the code was shamelessly copied from retrofly's fine work :wink:

Feeling satisfied with the results as I'm getting and error of ~1% on the readings, much probably due to the
resistor's tolerance used in the divider.

Next step for me would be changing all this (LOL) for fitting in a ATTiny85 :stuck_out_tongue:

Again thank you very much for pointing me in the right direction (namely writing all the registers at once) :wink:

#include <TimerOne.h>

#include <Wire.h>
#include "Adafruit_MCP23017.h"

// Basic pin reading and pullup test for the MCP23017 I/O expander
// public domain!

// Connect pin #12 of the expander to Analog 5 (i2c clock)
// Connect pin #13 of the expander to Analog 4 (i2c data)
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
// Connect pin #9 of the expander to 5V (power)
// Connect pin #10 of the expander to ground (common ground)
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

// Define MCP23017 pins connected to the LED display
// Digits
byte digits[4] = {8,9,10,11};
// Segments A-G and DP.
// DP (pin 7) will always be lit on the 2nd digit.
byte segments[8] = {0,1,2,3,4,5,6,7};

// define numbers, from SEGMENT A-G
byte numbers[10][7] = {
  {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,LOW},  // zero
  {LOW,HIGH,HIGH,LOW,LOW,LOW,LOW},      // one
  {HIGH,HIGH,LOW,HIGH,HIGH,LOW,HIGH},   // two
  {HIGH,HIGH,HIGH,HIGH,LOW,LOW,HIGH},   // three
  {LOW,HIGH,HIGH,LOW,LOW,HIGH,HIGH},    // four
  {HIGH,LOW,HIGH,HIGH,LOW,HIGH,HIGH},   // five
  {HIGH,LOW,HIGH,HIGH,HIGH,HIGH,HIGH},  // six
  {HIGH,HIGH,HIGH,LOW,LOW,LOW,LOW},     // seven
  {HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH}, // eight
  {HIGH,HIGH,HIGH,HIGH,LOW,HIGH,HIGH}   // nine
};

// variable to write all outputs at once
uint16_t MCP23017_outputs;

int batMonPin = 0;    // input pin for the divider
const float battMaxVoltage = 10.68;  // Change this for the maximum battery charge voltage
// holds the value of the current measured battery voltage
float battVolts;

Adafruit_MCP23017 mcp;
  
void setup() {
  Serial.begin(9600);
  mcp.begin();      // use default address 0x20
  // Set all required MCP23017 pins to output
  // Digits
  for (byte digit = 0; digit < 4; digit++) {
    mcp.pinMode(digits[digit], OUTPUT);
      mcp.digitalWrite(digits[digit], LOW);
  }
  // Segments
  for (byte segment = 0; segment < 8; segment++) {
    mcp.pinMode(segments[segment], OUTPUT);
      mcp.digitalWrite(segments[segment], LOW);
  }
  
  Timer1.initialize(1000000);         // initialize timer1, and set a 1 second period
  Timer1.attachInterrupt(readVoltage);  // attaches callback() as a timer overflow interrupt
}

void loop() {
  displayNumber(battVolts);
}

void readVoltage() {
  int bandGap, A0mV;
  for (int i=0; i <= 3; i++) bandGap=getBandgap();  //4 readings required for best stable value?
  A0mV = map(analogRead(batMonPin), 0, 1023, 0, bandGap); // read the voltage on the divider

  battVolts=(mapf(A0mV, 0, bandGap, 0, battMaxVoltage));
  /*
  Serial.print("Mapped Voltage = ");
  Serial.println(bandGap, DEC);
  Serial.println();
  */
}


int getBandgap(void) {
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     const long InternalReferenceVoltage = 1115L;  // Adust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref.
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0); 
#else
     // For 168/328 boards
     const long InternalReferenceVoltage = 1050L;  // Adust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref.
        // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);      
#endif
        // Start a conversion  
     ADCSRA |= _BV( ADSC );
        // Wait for it to complete
     while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L;
     return results;
}

/*
 * Shameless hack of the 'map' function :=0)
*/
float mapf(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

/*
 * Light a number in the display
 * @params: float number
*/
void displayNumber(float number) {
  char outstr[5];
  byte pos = 0;
  byte c, nbr;
  
  dtostrf(number,5, 2, outstr);
  // iterate through all chars in the array
  for (c = 0; c < 5; c++) {
    if (isDigit(outstr[c])) {
      nbr = outstr[c]-'0';
      showDigit(pos,nbr);
      delay(4);
    }
    if (c!=2) {
      pos++;
    }
  }
}

void showDigit(byte pos, byte value) {
  byte n;
  switch (pos) {
    case 0:
      // binary 0000000100000000
      MCP23017_outputs = 0x100;
      // order is LSB
      for (n = 0; n < 7; n++) {
        bitWrite(MCP23017_outputs, segments[n], numbers[value][n]);
      }
      bitWrite(MCP23017_outputs, 7, 0);
      mcp.writeGPIOAB(MCP23017_outputs);
      break;
    case 1:
      // binary 0000001000000000
      MCP23017_outputs = 0x200;
      // order is LSB
      for (n = 0; n < 7; n++) {
        bitWrite(MCP23017_outputs, segments[n], numbers[value][n]);
      }
      bitWrite(MCP23017_outputs, 7, 1);
      mcp.writeGPIOAB(MCP23017_outputs);
      break;
    case 2:
      // binary 0000010000000000
      MCP23017_outputs = 0x400;
      // order is LSB
      for (n = 0; n < 7; n++) {
        bitWrite(MCP23017_outputs, segments[n], numbers[value][n]);
      }
      bitWrite(MCP23017_outputs, 7, 0);
      mcp.writeGPIOAB(MCP23017_outputs);
      break;
    case 3:
      // binary 0000100000000000
      MCP23017_outputs = 0x800;
      // order is LSB
      for (n = 0; n < 7; n++) {
        bitWrite(MCP23017_outputs, segments[n], numbers[value][n]);
      }
      bitWrite(MCP23017_outputs, 7, 0);
      mcp.writeGPIOAB(MCP23017_outputs);
      break;
  }
}

hi guys,

Here I used your code for my project. changed my controller as esp8266 and control four 1.8 inches 4 digit 7 segments LED displays. I used I2C bus to communicate mcp23017 through esp8266. But I have faced a brightness problem and flickering problem. Guide me to solve it

Gunz:
Guide me to solve it

Sure, read this.