rocketnumbernine

Andrew's Project Blog: Hardware, Software, Stuff I find Interesting

Looking for something interesting to reach the minimum value on a component order, I picked up a couple of Maxim ICM7128 LED drivers, actually I got some Intersil ICM7228 which is interchangeable. The following shows some experiments to explore its capabilities - an Arduino is used but the C code extracts should be usable on any platform.

ICM7228 LED Driver Test setup

Overview

The two main issues with driving LEDs from a microcontroller is the number of IO pins required often exceeds that available, and the current drawn by the LEDs can exceed the maximum allowed from the device. Although Charlieplexing and driver circuitry can be used to get around these, an off the shelf chip that takes the burden can simplify a project.

The Maxim ICM7128 can drive up to 8 7-segment displays or an 8 by 8 matrix of individual LEDs with only 2 extra components (2 bypass capacitors). The 7218 comes in a number of varieties, for example, 7218A and 7218B variants provide support for common anode and common cathode circuits respectively.

The example circuit below is from Intersil ICM 7228 datasheet (exact pins vary between variants but functionality is same), the pins connected to switches are instead wired to a microcontroller.

ICM7228A pinout

Data can be output to the device in raw/undecoded mode which allows each 7-segment segment and the decimal point to be individually controlled (which would be used if an LED matrix were being used), or in Hex (0123456789AbCdEF) or Code-B (0123456789-EHLP ) format which eliminates the need for the microcontroller program to store the segments that need to be lit for each number.

Once the desired state of the LEDs is sent to the 7218, it takes care of multiplexing the LED matrix, each segment is lit every 1/250 of a second - which should ensure no flicker is visible.

The Maxim 7218A can provide a minimum of 30mA per LED. However, the common cathode version (7218B/7228B) only provides a quater of that.

Experiments

The 7228 is fairly simple to use, the 8 data bus pins: ID0,..,ID7 and MODE pin are set to desired values and the WRITE is pulsed low for atleast 250ns to indicate the device should read. Each interaction with the device follows the same pattern: a control byte followed by 0 or more data bytes containing data for display:

  1. MODE is set to 1 and a byte of control data is written to ID0,..,ID7 and WRITE pulsed low.
  2. MODE is set to 0 and 0 or more data bytes are written to ID0,...,ID7 each followed by a low pulse of WRITE.

The following shows some Arduino code to test the various features of the 7228, the full Arduino source can be found here.

First the 8 data pins and 2 control pins are connected to the arduino, we'll define some constants to simplify accessing them from code:

1
2
3
4
#define MODE_PIN 10
#define WRITE_PIN 11
#define ID0_PIN 2
#define ID_PIN(i) ID0_PIN+i

So the Mode and Write pins are on Arduino Digital Pins 10 and 11, to simplify the code we assume that pins ID0 to ID7 are connected to consecutive Arduino pins starting at pin 2, so we can define a function ID_PIN() to give us a particular pins number based on an offset, so to set ID4 high we can write:

digitalWrite(ID_PIN(4), HIGH);

To pulse the write pin low we write a 0 and then 1 (a digitalWrite call takes approximately 5µs on a 16MHz Arduino, which is longer than the 250ns required minimum low WRITE pin pulse width - see the write cycle timing in Figure 3 on page 7 of the datasheet).

1
2
3
4
5
void writePulse() {
  digitalWrite(WRITE_PIN, LOW);
  // 250ns delay not explicitly required
  digitalWrite(WRITE_PIN, HIGH);
}

In the following examples, we'll store the values to be sent to the ID0,..ID7 pins in the 8 bits of an unsigned char (with the value of ID0 in bit 0, ID1 in bit 1 etc). The following helper function takes the desired value of the MODE pin and an array of data, each bit is written to its corresponding IO pin and therefore the the 7218 input data line and then a writePulse sent to force the device to read the input.

1
2
3
4
5
6
7
8
9
10
11
12
13
void sendBytes(unsigned char data[], int length,
               boolean mode)
{
  // set the desired mode
  digitalWrite(MODE_PIN, mode);

  for (int i=0; i<length; i++) {
    for (int c=0; c<8; c++) { // for each bit
      digitalWrite(ID_PIN(c), data[i] & 1<<c);
    }
    writePulse(); // tell the 7218 to read the data
  }
}

Because a command to the 7218 device consists of a byte of control data sent with MODE pin high and then 0 or more data bytes sent with MODE pin low, we can abstract into a function, calling the sendBytes twice, first for the control byte and then any data:

1
2
3
4
5
6
void sendCommand(unsigned char control,
                 unsigned char digit[], int length)
{
  sendBytes(&control, 1, 1);
  sendBytes(digit, length, 0);
}

So for example, to send a control byte with ID4, ID6, and ID7 set High (and all others low), and then send 8 display data bytes held in array 'digits', we could use the following:

sendCommand(1<<4 | 1<<6 | 1<<7, digits, 8);
1<<4 sets the 4th bit (00010000 in binary) 1<<6 the 6th, and 1<<7 the 7th and we OR them together to get 11010000.

The table below summarises the meaning of the ICM7218 data pins in control mode (when MODE is High), each of the features is described below

PinNameif Highif Low
ID7Sequential Data Update SelectData ComingNo Data Coming
ID6Decoding Scheme Selection (if ID5 Low)HexCode B
ID5Decode/No Decode SelectionNo DecodeDecode
ID4Lower Power Mode SelectNormal OperationDisplay disabled
ID3RAM Bank SelectBank ABank B
ID2Single Digit Address Mode Select (if ID7 Low)Digit Address Bit 2
ID1Single Digit Address Mode Select (if ID7 Low)Digit Address Bit 1
ID0Single Digit Address Mode Select (if ID7 Low)Digit Address Bit 0
ICM7228 Control Pin Descriptions

Lower Power Mode Select - ID4

Sending a control byte with ID4 = 0, puts the ICM7128 into low power 'shutdown' mode: the digits are blanked and the multiplexing oscillator is stopped. the datasheet claims the devices will use 10µA when shutdown but about 3µA was observed.

sendCommand(0x00, 0, 0);
If the device remains on but the the segments are turned off the device uses about 200µA.

Decoding - ID5 & ID6

ID5 and ID6 control how the ICM7218 will treat the display data that follows, which can be in one of three modes:

  1. No Decode (ID5 High) - each segment of the digits can be controlled individually by a bit of ID0-ID7. This mode would be used when a matrix of LEDs are being controlled rather than 7-Segment displays.
  2. Hex Mode (ID5 Low, ID6 High) - the device will treat the input on pins ID0-ID4 as a number and display the according Hexadecimal character on the digit: "0123456789AbCdEF".
  3. Code-B Mode (ID5 Low, ID6 Low) - the device will treat the input on pins ID0-ID4 as a number and display the according Code B character on the digit: "0123456789-EHLP " (the last digit is a space and can be used to blank a digit)

Note, that in all modes the decimal point is controlled by ID7 - a 0 value will turn it on and 1 value turn it off - the other way around would have seemed more natural. When Hex and Code-B mode is being used only 4 bits of data (excluding the decimal point) need to be stored - the 7228 makes use of this by providing ability to store two values for each digit, selected by ID3 - RAM Bank Select - the decimal point is set for both banks however. I can't see of many practical uses for this.

Sequential Data Update Select - ID7

After the control byte, display data is sent to the device, this can be a sequence of bytes for each digit (from digit 0, to digit 7) or a data for a specific digit:

  1. ID7 High - Data Coming, the device will expect a succession of display data on each following pulse of the WRITE pin.
    1
    2
    unsigned char digit[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87 };
    sendCommand(1<<4 | 1<<6 | 1<<7, digit, 8);
  2. ID7 Low - Data not coming, bits ID0, ID1, and ID2 form the 3 digit address of the single digit data that follows, so we can repeat the behaviour of the example above by looping over each digit:
    1
    2
    3
    4
    for (unsigned char i=0; i<8; i++) {
      // i will fill the 3 low bits of the command byte(ID0, ID1, and ID2)
      sendCommand(i | 1<<4 | 1<<6, &i, 1);
    }

An example of using no decode mode is shown below, it loops around lighting each LED segment (stored in the 'segment' array) on each digit in.

1
2
3
4
5
6
7
8
9
10
unsigned char segment[] = { 1<<6 | 0x80, 1<<5 | 0x80, 1<<2 | 0x80,
       1<<3 | 0x80, 1<<0 | 0x80, 1<<4 | 0x80, 1<<2 | 0x80, 1<<1 | 0x80};
  for (int i=0; i<3; i++) {
    for (unsigned char digit=0; digit<8; digit++) {
      for (int j=0; j<8; j++) { 
        sendCommand(digit | 1<<4 | 1<<5, &segment[j], 1);
        delay(50);
      }
    }
  }

Although the examples above assume a 7 segment display, the ICM7218 could be used to control a 8x8 matrix of LEDs, here the advantage of offloading multiplexing to an external device is evident - if the desired on/off values are held in an 8 byte array, then the microcontroller can send the data to the device and continue processing:

1
2
unsigned char data[8] = ... 
  sendCommand(1<<4 | 1<<5 | 1<<7, data, 8);

Finally, an example that counts up from zero. Each time the count is incremented the 8 digits are filled with the appropriate decimal digit value and the entire set of digits rewritten:

1
2
3
4
5
6
7
8
unsigned char digit[8];
 for (int count=0; count<999999; count++) {
   for (int power=1, i=0; i<8; i++, power*=10) {
      digit[i] = (count/power % 10) | 0x80;
    }
    sendCommand(1<<4 | 1<<6 | 1<<7, digit, 8);
    delay(50);
  }

Conclusions

The ICM7218 is a good all-rounder, in summary:

  1. Simplifies control of upto 8 7-segment displays or a 8x8 grid of LEDs by reducing the components required and simplifying microcontroller code.
  2. Fairly widely available in small quantities, although not cheap - the price varies widely between suppliers so its worth shopping around.
  3. Available in through hole and SMD versions.
  4. Incredibly simple to use API.
  5. Common Cathode versions (B & D) have limited current output.
  6. Not possible to adjust the power sent to the LEDs (either digitally or through a single trim resistor).
  7. Uses 10 controller IO pins - although if using Hex or Code-B without decimal point only need to connect 6 pins (ID0-ID3, MODE, and WRITE), the others could be hardwired. See here for an example of fronting the ICM7218 with an I2C interface chip to reduce the number of pins to 2.

Other LED driver chips are available (for example see Maxim's range - with better LED intensitiy, or SPI/I2C control to reduce the number of IO pins required, but the 7218 has its uses.

References/Bibliography

  • Example Arduino Sketch
  • Maxim ICM7218/7228 Datasheet
  • Intersil ICM7228 datasheet
  • Use of ICM7218 with an I2C chip and only 2 IO pins

  • It looks like your browser doesn't support javascript so comments won't work

    Tags/Categories: arduino, howto, LED