Driving 7-segment Displays With a MM5480 and Arduino

For the last year, up until a couple of months ago, my heater was pretty unreliable, probably because of a flaky relay in its control unit, or maybe some bad capacitors, something that nowadays seems to kill almost all electronics prematurely.

Well getting bored of hitting the poor machine until it started working again, about every day, I finally replaced its Econgas control unit and, of course, I tore the old unit apart. It’s a nice piece of kit, It seems to be made by a Dutch firm called Pijnenburg, a name that I only associated with a particular brand of Dutch cakes before. ;-) Maybe more later about the internals of that unit, or maybe

never, it depends on whether I find any other interesting stuff inside. But wat got me started was its nice control panel, containing three red 7-segment LED displays, three buttons and a Micrel MM5480BN shift-register based LED driver. Something that just screamed to be hooked up to my trusty Arduinos!

Here is the result already:

But how I ended up with this result might be interesting for fellow tinkerers, so I’ll summarize my steps below.

Micrel MM5480 is actually quite a nice part. After a start bit, that is always high, you clock in another 35 bits of data, with wich you define the state of your LED’s. Once your 36th clock pulse goes high, the data that you’ve clock into the shift-register is latched to the LED drivers, eliminating the need for a seperate pin to do so.

Well…that was the nice bit. Now I only needed to find out how the display segments were connected to the MM5480, so which databit maps to which LED segment. To do so, I created a small program that shifted a 1 through all 35 databits, printed the current databit that was 1 to the Arduino IDE’s console and I noted the lit LED segment on a piece of paper. There are a few oddities in the segment mapping, those are only related to this particular board.

Next, I wrote a small demonstration program, that contains two tables. One to map segments to MM5480 databits and a sort or character ROM with hexadecimal numbers and some other stuff that is displayable on a 7-segment display. The sample program that I’ve listed below does nothing more than counting from 0 to 999 over and over again, but it’s a good start for some more useful programs.

(mm5480.pde) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Drive Micrel MM5480BN based 3x7 segment LED display
 * module from an Econgas control unit
 *
 * Hessel Schut, hessel@isquared.nl, 2009-10-17
 *
 */


// MM5480 shift register connections
#define PIN_SERDAT 2
#define PIN_SERCLK 3

 /* Segment numbering:
  *
  *   3333
  *  2    4
  *  2    4
  *   6666
  *  1    5
  *  1    5
  *   0000   77
  *
  * Digits are numbered from left to right, should the 
  * other way around, of course, silly me...           */


/* reg is a buffer containing the bits to be clocked to
 * the MM5480, reg[0], the first bit sent is always high,
 * indicating a start of frame, on the rising edge of 
 * SERCLK period 36, bits 1..36 will be latched to the display. */
boolean reg[36];

// (digit, segment) to bit mapping
static const int segment[][3] =
{
  {25, 13,  2},
  {27, 14,  3},
  {30, 15,  4},
  {31, 20,  5},
  {32, 21,  6},
  {33, 22,  7},
  {23, 24, 12}, // bleh?!
  {34, 26,  8}  // 26 is nc
};

// char to 7-segment mapping
static const byte digit[] =
{
  /*
  _76543210 */
  B00111111,  //  0  -- integers
  B00110000,  //  1
  B01011011,  //  2
  B01111001,  //  3
  B01110100,  //  4
  B01101101,  //  5
  B01101111,  //  6
  B00111000,  //  7
  B01111111,  //  8
  B01111101,  //  9  
  B01111110,  //  A  -- hex digits
  B01100111,  //  b
  B01000011,  //  c
  B01110011,  //  d
  B01001111,  //  E
  B01001110,  //  F  
  B00101111,  //  G  -- some alpha and the rest
  B01110110,  //  H
  B00110011,  //  J
  B00000111,  //  L
  B01100010,  //  n
  B01100011,  //  o
  B01011110,  //  P
  B01111100,  //  q
  B01000010,  //  r
  B01000111,  //  t
  B00100011,  //  u
  B01110101,  //  y
  B00110101,  //  ij
  B01001001,  //  x (dash dash dash)
  B01111011,  //  a   
  B00001111,  //  C
  B01011111,  //  e
  B01100110,  //  h
  B01011100,  //  degree
  B01000001,  //  =
  B00000100,  //  '
  B00010100,  //  "
  B00000000   //  space
};

// clear reg[] buffer
void eraseBuffer() {
  for (int i = 1; i <= 36; i++) {
    reg[i] = 0;
  };
  reg[0] = 1;
}

// write buffer to the display
void writeBuffer() {
  for(int databit = 0; databit <= 35; databit++) {
    // bit 0 is always high 
    if ((databit == 0) || (reg[databit] == 1)) {
      digitalWrite(PIN_SERDAT, HIGH);
    } else {
      digitalWrite(PIN_SERDAT, LOW);
    };
    // strobe clock
    digitalWrite(PIN_SERCLK, HIGH);
    digitalWrite(PIN_SERCLK, LOW);
  };
}

// set individual digit
void setLEDDigit(int LEDdigit, int value) {
  for(int d = 0; d <= 7; d++) {
    if ((digit[value] >> d) & B00000001) {
      reg[segment[d][LEDdigit]] = 1;
    };
  };
}

// write a value (<=999) to the display  
void writeValue(int val) {
  int h, t, u;
  if (val > 999) val = 999;

  h = val / 100;      // hunderds
  t = val % 100 / 10; // tens
  u = val %  10;      // units

  // write value without leading zeroes
  if (h > 0) setLEDDigit(2, h);
  if (h > 0 || t > 0) setLEDDigit(1, t);
  setLEDDigit(0, u);

  writeBuffer();
};

// write a value (<=99) to the display  
void writeValueRightMost(int val) {
  int t, u;
  if (val > 99) val = 99;

  t = val / 10; // tens
  u = val % 10; // units

  // write value without leading zeroes
  if (t > 0) setLEDDigit(1, t);
  setLEDDigit(0, u);

  writeBuffer();
};

void setup() {

  pinMode(PIN_SERDAT, OUTPUT);
  pinMode(PIN_SERCLK, OUTPUT);


  digitalWrite(PIN_SERDAT, LOW);
  for(int databit = 0; databit <= 35; databit++) {
    digitalWrite(PIN_SERCLK, HIGH);
    digitalWrite(PIN_SERCLK, LOW);
  };
}


void loop() {

  eraseBuffer();

  for(int i = 0; i <= 999; i++) {

   eraseBuffer();

   if (i & 1) reg[34] = 1; // blink dp

   writeValue(i);

   delay(100);

  };

}

Now for some more interesting programs, I have no idea yet.

I might think of something later, but for now it was a nice time to spent my saturday evening… ;)

Comments