Many years ago, I got about a dozen 16×16 bi-color LED display panels that were a part of a broken segment on a marquis. Until now, they’ve been sitting in my spare parts cabinet just waiting for a project to come up. Nothing has and now space has become a premium, so it’s time to do something with them or get rid of them. Since I haven’t used them before and I don’t have a display driver, I don’t know how useful they will be or even if they will work.
When these panels came to me, they were mounted on a frame and had small FPC (flexible printed circuit) ribbon cables connecting each row in series. The backs are printed with two part numbers KLM-163-CAH and KLM-163-CAN which made tracking down a datasheet challenging. I found one on datasheetdirect.com. The panels are powered by 5VDC and take 5VDC TTL logic levels (making them 100% compatible with Arduino or any other 5V microcontroller).
In terms of basic operation, you have to select the line, write a serial string to indicate the LEDs to light, then flip an enable bit to lock in that sequence until you get done with the next line. The serial data is timed by an external clock so timing with the serial sequence isn’t important. In fact, I bit-bashed my way through the example code and it worked just fine. The display only shows one line at a time, so you have to be able to scan through the sequence pretty fast. The data sheet has a pretty clear timeline of the sequence of operations, so figuring out the timing shouldn’t hold you up. The only thing that I got hung up on (and still haven’t figured out) is an offset on line addressing, but I’ve worked around it with a 15 line offset.
I also put a bit in the code that allows the Arduino to accept a serial sequence (through the USB port) and it will display that sequence. I tested it using HyperTerminal and it showed the bit-wise ASCII codes for the keys I pressed (so that’s success, right?)
I plan on writing at least one more post about these displays, so look for that in the future.
Here is the Arduino code I used to drive the display:
#define SBaud 9600
#define OE 2 //Out Enable pin
#define Latch 3 //Latch pin
#define CLK 4 //Clock pin
#define GData 5 //Green data pin
#define RData 6 //Red data pin
#define Ad0 8 //Address bit 0 pin
#define Ad1 9 //Address bit 1 pin
#define Ad2 10 //Address bit 2 pin
#define Ad3 11 //Address bit 3 pin
#define Timer 0 //Delay Timer
#define RowMin 0 // Decimal min in row
#define RowMax 15 // Decimal max in row
#define ColMin 0 // Decimal min in col
#define ColMax 15 // Decimal max in col
#define SMax 32 // number of bytes to read from serial
word Display[] = {0x0000, 0x03C0, 0x0FF0, 0x1FB8, 0x3F1C, 0x3F8C, 0x7EC6, 0x7C46, 0x760E, 0x631E, 0x303C, 0x387C, 0x1CF8, 0x0FF0, 0x03C0, 0x0000};
int Di = 0;
int Row = 0;
int Col = 0;
void setup() {
pinMode(OE, OUTPUT);
pinMode(Latch, OUTPUT);
pinMode(CLK, OUTPUT);
pinMode(GData, OUTPUT);
pinMode(RData, OUTPUT);
pinMode(Ad0, OUTPUT);
pinMode(Ad1, OUTPUT);
pinMode(Ad2, OUTPUT);
pinMode(Ad3, OUTPUT);
//Start with all bits low
digitalWrite(OE, LOW);
digitalWrite(Latch, LOW);
digitalWrite(CLK, LOW);
digitalWrite(GData, LOW);
digitalWrite(RData, LOW);
digitalWrite(Ad0, LOW);
digitalWrite(Ad1, LOW);
digitalWrite(Ad2, LOW);
digitalWrite(Ad3, LOW);
//Start the serial monitor
Serial.begin(SBaud, SERIAL_8N1);
Serial.setTimeout(1); //stop checking the port if no data is available after 1 ms
}
void loop() {
//check the serial buffer for data, if there is some, use it
//otherwise continue putting data on the screen
if (Serial.available() > SMax) {
//when enough bytes are available to fill the screen, load them into the image buffer
for (Di = 0; Di <= SMax ; Di++){
if (Di%2 == 1) {
Display[Di/2] = word(highByte(Display[Di/2]) , Serial.read());
}
else {
Display[Di/2] = word(Serial.read() , lowByte(Display[Di/2]));
}
}
for (Di=0;Di<SMax;Di++){
if (Di%2==1){
Serial.print(lowByte(Display[Di/2]));
}
else{
Serial.print(highByte(Display[Di/2]));
}
}
Serial.println(“”);
}
// initialize enable to be high (disable)
// Step 1. Bring both Latch and Enable High to prepare for new data
digitalWrite(OE, HIGH);
digitalWrite(Latch, HIGH);
for (Row = RowMin; Row <= RowMax; Row++){
// Step 2. With Latch and Enable High, set the row address
writeAddress(Row+15);
// Step 3. Enable goes low to activate the display
digitalWrite(OE, LOW);
delay(Timer);
// Step 4. The bitwise data is written to red and green
for (Col = ColMin; Col <= ColMax; Col++){
writeBit(bitRead(Display[Row],Col), 0);
}
// Step 5. After the row is done, bring Enable High and
//Latch Low to set the data for that row
digitalWrite(CLK, LOW);
digitalWrite(OE, HIGH);
digitalWrite(Latch, LOW);
digitalWrite(Latch, HIGH);
}
}
void writeBit(boolean RVal, boolean GVal) {
digitalWrite(CLK, LOW);
if (RVal) {digitalWrite(RData, LOW);}
else {digitalWrite(RData, HIGH);}
if (GVal){digitalWrite(GData, LOW);}
else {digitalWrite(GData, HIGH);}
digitalWrite(CLK, HIGH);
}
void writeAddress(int RowNum) {
if (bitRead(RowNum,0)==1){ digitalWrite(Ad0, HIGH);}
else {digitalWrite(Ad0, LOW);}
if (bitRead(RowNum,1)==1){ digitalWrite(Ad1, HIGH);}
else {digitalWrite(Ad1, LOW);}
if (bitRead(RowNum,2)==1){ digitalWrite(Ad2, HIGH);}
else {digitalWrite(Ad2, LOW);}
if (bitRead(RowNum,3)==1){ digitalWrite(Ad3, HIGH);}
else {digitalWrite(Ad3, LOW);}
}
That was my project day!
If you liked this project, check out some of my others:
Instant Parade!
The ThrAxis – Our Scratch-Built CNC Mill
Give Aging Technology a Chance
Did you like It’s Project Day? You can subscribe to email notifications by clicking ‘Follow’ in the side bar on the right, or leave a comment below.