OK, I couldn’t help myself… running two more ’595 shift registers in parallel with the ones for the LED bargraph provides enough bits for a DL1414 four character ASCII display:
The serial data input (SER) and serial clock (SCK) lines drive both shift register strings with the same values at the same time. The two strings have different parallel register clocks (RCK), so you twiddle only the one you want; the other string gets the same data, but it never reaches those parallel outputs.
This being a breadboard lashup, there’s no red filter to boost the contrast; :
A red filter would really help, as shown on my old Tek ROM readout board.
The DL1414 datasheet seems very hard to find; that’s a clean version I fetched from datasheetarchive.com.
They’ve also become insanely expensive these days and you don’t want to use them for a new design, but if you happen to have a NOS tube lying around and hadn’t thought of monetizing your assets, well…
To simplify debugging, the test code blips both RCK lines after shifting the data out, which means the LED bargraph shows the actual bits driving the DL1414. Slow down the update rate, un-comment the delay(1000) statements, and you can watch the DL1414 pins in “real time”. Cheaper than a logic analyzer…
The test routine displays HELO, then shows the low word of the running millis() value in hex every 100 ms, for lack of anything smarter.
You could use a single shift register string with two ’595 chips driving both the LED bargraph and the DL1414; the bars would flicker briefly when updating the DL1414, but that might not be even slightly objectionable for some geek-chic projects.
You could put the two shift registers in series, which means you must shift twice as many bits to update the DL1414 display; you’d need only one RCK line for both sets if you shifted the unchanging bits every time. That might not be such a big deal, even though writing each character requires three complete shift sequences.
I used a union to overlay a word variable with a bitfield structure:
union DL1414_ {
word ShiftWord; // word overlay
struct { // bitfield sent to the display
unsigned int Addr:2;
unsigned int NotWrite:1;
unsigned int Ctl3_7:5; // unused bits
unsigned int Data:7;
unsigned int Data7:1; // unused bit
} ShiftBits;
};
Bit-twiddling operations then work on the bitfield elements and shifting uses byte-wide chunks:
union DL1414_ DL1414; DL1414.ShiftBits.Data = Char & 0x7F; DL1414.ShiftBits.Addr = ~CharID & 0x03; // reverse order of chars DL1414.ShiftBits.NotWrite = 1; // set up data and address shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8); shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
I briefly thought about overlaying two bytes instead of a word, but came to my senses. I assume the compiler can optimize those byte-sized shifts out of existence, but it doesn’t really matter.
The DL1414 address layout puts character 0 on the right, but we want it on the left to match the string subscript; just invert the address bits and move on.
The complete Arduino source code:
// LED Character Display
// Ed Nisley - KE4ANU - November 2012
// Uses obsolete DL1414 LED displays
// Display pinout:
// http://softsolder.com/2009/08/05/arduino-using-ancient-litronix-dl-1414-led-displays/
// http://softsolder.com/2012/12/07/arduino-snippets-dl1414-led-character-display/
//----------
// Pin assignments
// These are *software* pins for shiftOut(), not the *hardware* SPI functions
const byte PIN_MOSI = 8; // data to shift reg
const byte PIN_SCK = 6; // shift clock to shift reg
const byte PIN_RCKC = 12; // latch clock for LED character shift reg
const byte PIN_RCKB = 7; // latch clock for LED bar bits shift reg
const byte PIN_SYNC = 13; // scope sync
//----------
// Constants
const int UPDATEMS = 100; // update LEDs only this many ms apart
#define TCCRxB 0x02 // Timer prescaler
#define LED_SIZE 4 // chars per LED
#define LED_DISPLAYS 1 // number of displays
#define LED_CHARS (LED_DISPLAYS * LED_SIZE)
//----------
// Globals
char LEDCharBuffer[LED_CHARS + 1] = "HELO"; // raw char buffer, can be used as a string
union DL1414_ {
word ShiftWord; // word overlay
struct { // bitfield sent to the display
unsigned int Addr:2;
unsigned int NotWrite:1;
unsigned int Ctl3_7:5; // unused bits
unsigned int Data:7;
unsigned int Data7:1; // unused bit
} ShiftBits;
};
unsigned long MillisNow;
unsigned long MillisThen;
//-- Helper routine for printf()
int s_putc(char c, FILE *t) {
Serial.write(c);
}
//-- Write single char to DL1414
void WriteLEDChar(char Char,char CharID) {
union DL1414_ DL1414;
DL1414.ShiftBits.Data = Char & 0x7F;
DL1414.ShiftBits.Addr = ~CharID & 0x03; // reverse order of chars
DL1414.ShiftBits.NotWrite = 1; // set up data and address
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
digitalWrite(PIN_RCKC,HIGH);
digitalWrite(PIN_RCKC,LOW);
digitalWrite(PIN_RCKB,HIGH);
digitalWrite(PIN_RCKB,LOW);
// delay(1000);
DL1414.ShiftBits.NotWrite = 0; // write the character
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
digitalWrite(PIN_RCKC,HIGH);
digitalWrite(PIN_RCKC,LOW);
digitalWrite(PIN_RCKB,HIGH);
digitalWrite(PIN_RCKB,LOW);
// delay(1000);
DL1414.ShiftBits.NotWrite = 1; // disable write
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord >> 8);
shiftOut(PIN_MOSI,PIN_SCK,MSBFIRST,DL1414.ShiftWord & 0x00ff);
digitalWrite(PIN_RCKC,HIGH);
digitalWrite(PIN_RCKC,LOW);
digitalWrite(PIN_RCKB,HIGH);
digitalWrite(PIN_RCKB,LOW);
// delay(1000);
}
//------------------
// Set things up
void setup() {
pinMode(PIN_SYNC,OUTPUT);
digitalWrite(PIN_SYNC,LOW); // show we arrived
// TCCR1B = TCCRxB; // set frequency for PWM 9 & 10
// TCCR2B = TCCRxB; // set frequency for PWM 3 & 11
pinMode(PIN_MOSI,OUTPUT);
digitalWrite(PIN_MOSI,LOW);
pinMode(PIN_SCK,OUTPUT);
digitalWrite(PIN_SCK,LOW);
pinMode(PIN_RCKC,OUTPUT);
digitalWrite(PIN_RCKC,LOW);
pinMode(PIN_RCKB,OUTPUT);
digitalWrite(PIN_RCKB,LOW);
Serial.begin(9600);
fdevopen(&s_putc,0); // set up serial output for printf()
printf("LED Character Display - DL1414\r\nEd Nisley - KE4ZNU - November 2012\r\n");
for (byte i=0; i < LED_CHARS; ++i)
WriteLEDChar(LEDCharBuffer[i],i);
delay(1000);
MillisThen = millis();
}
//------------------
// Run the test loop
void loop() {
MillisNow = millis();
if ((MillisNow - MillisThen) > UPDATEMS) {
digitalWrite(PIN_SYNC,HIGH);
sprintf(LEDCharBuffer,"%04X",(word)MillisNow);
for (byte i=0; i < LED_CHARS; ++i)
WriteLEDChar(LEDCharBuffer[i],i);
digitalWrite(PIN_SYNC,LOW);
MillisThen = MillisNow;
}
}



#1 by hexley ball on 7-December-2012 - 11:13
“The serial data input (SER) and serial clock (SCK) lines drive both shift register strings with the same values at the same time. The two strings have different parallel register clocks (RCK)…”
Might want to double check that schematic, Chief :-)
#2 by Ed on 7-December-2012 - 13:01
The other shift register string appeared yesterday; it’s the one driving the bargraph!
[Edit: The top pair of '595 chips in the photo.]
#3 by hexley ball on 7-December-2012 - 13:42
I see — in fact it is just as you said in the very first sentence of the post. Oops.
#4 by Ed on 7-December-2012 - 15:19
Well, it’s not as if I’ve never missed anything like that… [wince]