Thursday, June 11, 2015

Prototype 14-segment-display shield

Update Apr 14, 2019: The kit for this shield can be ordered here.

There are many ways (*) to drive the 6-digit 14-segment common cathode display from Seeed Studio.
This time I chose to multiplex two MAX7221, a method described here (but used for driving a bi-color 8x8 LED matrix).


The code is based on LedControl library, which I extended to cover the definition and display of 14-segment characters (digits, upper case letters, and a few specials). Below is a relevant fragment of the code I added:

/*
* Segment names in the 14-segment (plus DP) display:
*
*     -     A
*   |\|/|   F,I,J,K,B
*    - -    G,H
*   |/|\|   E,N,M,L,C
*     -  .  D,P
*/
// my wiring:
//            GFEDCBAx
// 1st byte: B11111111
//
//            NHJIKMLP
// 2nd byte: B11111111

const static byte charTable14Seg[43][2] = {
    {B01111110,B10001000},  // 0
    {B00001100,B00001000},  // 1
    {B10110110,B01000000},  // 2
    {B00011110,B01000000},  // 3
    {B11001100,B01000000},  // 4
    {B11010010,B00000010},  // 5
    {B11111010,B01000000},  // 6
    {B00000010,B00001100},  // 7
    {B11111110,B01000000},  // 8
    {B11011110,B01000000},  // 9
    {B00000000,B01000000},  // :
    {B00000000,B01000000},  // ;
    {B00000000,B01000000},  // <
    {B00000000,B01000000},  // =
    {B00000000,B01000000},  // >
    {B00000000,B01000000},  // ?
    {B00000000,B01000000},  // @
    {B11101110,B01000000},  // A
    {B00011110,B01100100},  // B
    {B01110010,B00000000},  // C
    {B00011110,B00100100},  // D
    {B11110010,B01000000},  // E
    {B11100010,B01000000},  // F
    {B01111010,B01000000},  // G
    {B11101100,B01000000},  // H
    {B00000000,B00100100},  // I
    {B00111100,B00000000},  // J
    {B11100000,B00001010},  // K
    {B01110000,B00000000},  // L
    {B01101100,B00011000},  // M
    {B01101100,B000100L0},  // N
    {B01111110,B00000000},  // 0
    {B11100110,B01000000},  // P
    {B01111110,B00000010},  // Q
    {B11100110,B01000010},  // R
    {B11011010,B01000000},  // S
    {B00000010,B00100100},  // T
    {B01111100,B00000000},  // U
    {B01100000,B10001000},  // V
    {B01101100,B10000010},  // W
    {B00000000,B10011010},  // X
    {B00000000,B00011100},  // Y
    {B00010010,B10001000},  // Z
};
...
void setChar14Seg(byte pos, byte ascii)
{
  if (pos>7)
    return;

  if (ascii>90 || ascii<48)
    return;

  byte index = ascii - 48;
  for(byte seg=0; seg < 8; seg++)
  {
    SetLed(SEG_AG, pos, seg, charTable14Seg[index][0] & 1 << seg);
    SetLed(SEG_GN, pos, seg, charTable14Seg[index][1] & 1 << seg);
  }
}

This method (hardware and software) can be used for up to 8 14/16-segment displays.


(*) Should be the topic of a future post.


20 comments:

  1. Can you post the code for arduino to use this library to write characters on that display?

    ReplyDelete
    Replies
    1. You can find the sketch here:
      https://drive.google.com/file/d/0B01cjIbSk11NaENFU01taHNrZ0U/view?usp=sharing

      The wiring I used to connect the display is documented in the sketch.

      Delete
  2. I am trying to use 2 max 7219s to control 4 double 14-segment displays. I am using digit 0 of 1st max to control outer ring of digit 0, and digit 0 of 2nd max to control inner segments, digit 1 to control outer digit 1, etc etc. The problem is, I have ghosting on every digit. If segment "A" of digit 0 is lit, the segment "A" of every digit is lit. Please help me!!!

    ReplyDelete
  3. Can you tell me, if i want to show some word on it like "THANKS"
    What should i do?

    ReplyDelete
    Replies
    1. As far as I remember, the LedControlLibrary has functions writeChar() and writeString(). If you call writeString("THANKS"), you should see it on the screen. It is really straightforward.

      Delete
  4. Hello

    I try to control a single 16segment display, can i do it also whis this sketch.

    ReplyDelete
    Replies
    1. It depends on your schematic (what LED driver you use, wiring of the display segments to the driver).
      But yes, you can adapt this sketch to your single 16-segment display, especially the character-set definition.
      If you need more help, please provide a schematic or other details.

      Delete
    2. This comment has been removed by the author.

      Delete
  5. I try to control a single 16 segment display, can that also be done whis this sketch?

    ReplyDelete
  6. Is an interrupt needed? Display seems dimmer than when using 7 segment only display. Any way to remedy this?

    ReplyDelete
    Replies
    1. Yes, the code uses interrupt.

      The ISR funtion(s) are:

      /////////////////////////////ISR Timer Functions ///////////////////////////
      ISR(TIMER2_COMPA_vect)
      { //This ISR toggles shutdown between the 2MAX7221's
      // Serial.println("in ISR");

      if(maxInShutdown==SEG_HN)
      {
      lc.shutdown(SEG_AG,true); // The order here is critical - Shutdown first!
      lc.shutdown(SEG_HN,false); // . . . Then restart the other.
      maxInShutdown=SEG_AG;
      }
      else
      {
      lc.shutdown(SEG_HN,true);
      lc.shutdown(SEG_AG,false);
      maxInShutdown=SEG_HN;
      }
      }

      void setISRtimer()
      { // setup ISR timer controling toggleing
      TCCR2A = 0x02; // WGM22=0 + WGM21=1 + WGM20=0 = Mode2 (CTC)
      TCCR2B = 0x05; // CS22=1 + CS21=0 + CS20=1 = /128 prescaler (125kHz)
      TCNT2 = 0; // clear counter
      OCR2A = ISR_FREQ; // set TOP (divisor) - see #define
      }

      void startISR()
      { // Starts the ISR

      TCNT2 = 0; // clear counter (needed here also)
      TIMSK2|=(1<<OCIE2A); // set interrupts=enabled (calls ISR(TIMER2_COMPA_vect)
      }

      void stopISR()
      { // Stops the ISR
      TIMSK2&=~(1<<OCIE2A); // disable interrupts
      }

      Delete
    2. And the definition:

      #define ISR_FREQ 190 //190=650Hz // Sets the speed of the ISR - LOWER IS FASTER
      // prescaler is /128 - 125,000/ISR_FREQ+1 (i.e 249=500Hz, 190=650Hz)
      // adjust depending on the sketch;

      Delete
    3. This is the setup function:

      void setup()
      {
      Serial.begin(9600);
      Serial.println("setup");

      lc.setIntensity(SEG_AG,15);
      lc.setIntensity(SEG_HN,15);

      setISRtimer(); // setup the timer
      startISR(); // start the timer to toggle shutdown
      ClearMatrix();

      getTimeFromRTC();
      }

      Delete
    4. The write function:

      ///////// Wrappers for LedControl functions . . . //////////
      void SetLed(byte Color, byte Row,byte Col, byte State)
      {
      stopISR(); // disable interrupts - stop toggling shutdown when updating
      lc.setLed(Color,Row,Col,State);
      startISR(); // enable interrupts again
      }

      void ClearMatrix()
      {
      stopISR(); // disable interrupts - stop toggling shutdown when updating
      lc.clearDisplay(SEG_AG);
      lc.clearDisplay(SEG_HN);
      startISR(); // enable interrupts again
      }

      void setChar14Seg(int pos, int ascii)
      {
      if (pos > 7)
      return;

      if (ascii <32 || ascii>90)
      return;

      byte index = ascii - 32;

      for(int seg=0;seg<8;seg++)
      {
      SetLed(SEG_AG, pos, seg, charTable14Seg[index][0] & 1< 5)
      return;

      if (ascii <32 || ascii>90)
      return;

      byte index = ascii - 32;

      for(int seg=0;seg<8;seg++)
      {
      SetLed(SEG_AG, pos, seg, charTable14Seg[index][0] & 1<<seg);
      SetLed(SEG_HN, pos, seg, (charTable14Seg[index][1] + (dotLit?1:0)) & 1<<seg);
      }
      }


      void writeTime(int hours, int minutes, int seconds)
      {
      char h1 = (hours/10)? '0' + hours/10 : ' ';
      writeChar(0, h1, false);
      char h2 = '0' + (hours%10);
      writeChar(1, h2, true);
      char m1 = '0' + (minutes/10);
      writeChar(2, m1, false);
      char m2 = '0' + (minutes%10);
      writeChar(3, m2, true);
      char s1 = '0' + (seconds/10);
      writeChar(4, s1, false);
      char s2 = '0' + (seconds%10);
      writeChar(5, s2, false);
      }

      void writeString(char wordIn[])
      {
      for (byte i=0; i<5; i++)
      {
      setChar14Seg(i, wordIn[i]);
      }
      }


      Delete
  7. Thank you Florin C!
    I could download the sketch also for arduino.
    I understand now why we need the interrupt, in our case because of the common cathode displays and each MAX7219 sharing the digits. When not using shutdown functions, then a ghosting will occur. Only downside as said, is that the display is dimmer, even when I use other resistor on ISET. Do you think we can somehow prevent the ghosting with additional circuitry to make a code without interrupt? Thank you!

    ReplyDelete
  8. Funnily, the 7219 chip brought me to my first PCB project with eagle, so therefore my perseverance with its features. Maybe you might want to look into the file, just to say whether it would work the way the components are wired? Link: https://drive.google.com/drive/folders/0B3r3oSiC8UONZE16VGJ4ZFdiWGc?usp=sharing

    ReplyDelete
  9. Well I could finish my proto clock, accepting that in the end it will be dimm for technical reasons, but still a great idea when there's supply left!

    https://drive.google.com/file/d/0B3r3oSiC8UONaEhJU3c2bFlSYU0/view?usp=sharing

    ReplyDelete
    Replies
    1. Do you have a blog/web site?
      If not, do you want to publish your interesting project here (under your name)?

      Delete