Trans-Lux display & Propeller
in Propeller 1
Hi All -
Back in 2008 I picked up some surplus Trans-Lux displays from a local electronics salvage store. I've driven one half of a display using an ATMega168, but that was really at the limit of what it could do
Some pictures here: https://goo.gl/photos/TfGjPaihxif2U6dF7
In 2009, I ordered a few Propeller Proto Boards (#32212) thinking that someday I would get around to working with these displays again. I'm thinking that day has arrived!
Driving these involves using clock and data lines to shift in 320 display bits, three more bits to select which row in the 5x7 characters light up, and then pulling an enable line low. This repeats for each of the 7 rows that make up the 5x7 characters. The input connector has two sets of clock/data/enable, one for the top two rows of characters and another for the bottom two rows. From the photos you can see I was using only one of these sets - only half of the display is lit.
I'm thinking that the Propeller is going to work for this and two cogs could drive both halves of the display. Each cog would just be a loop to read data from a shared address space and send it using clock/data/enable. Another cog could read the serial port to allow external input to manipulate the display data.
So, am I going down the right path here with my approach? If so, does anyone know offhand of any project doing something similar?
Thanks for your time,
Matt
Back in 2008 I picked up some surplus Trans-Lux displays from a local electronics salvage store. I've driven one half of a display using an ATMega168, but that was really at the limit of what it could do
Some pictures here: https://goo.gl/photos/TfGjPaihxif2U6dF7
In 2009, I ordered a few Propeller Proto Boards (#32212) thinking that someday I would get around to working with these displays again. I'm thinking that day has arrived!
Driving these involves using clock and data lines to shift in 320 display bits, three more bits to select which row in the 5x7 characters light up, and then pulling an enable line low. This repeats for each of the 7 rows that make up the 5x7 characters. The input connector has two sets of clock/data/enable, one for the top two rows of characters and another for the bottom two rows. From the photos you can see I was using only one of these sets - only half of the display is lit.
I'm thinking that the Propeller is going to work for this and two cogs could drive both halves of the display. Each cog would just be a loop to read data from a shared address space and send it using clock/data/enable. Another cog could read the serial port to allow external input to manipulate the display data.
So, am I going down the right path here with my approach? If so, does anyone know offhand of any project doing something similar?
Thanks for your time,
Matt

Comments
Welcome to the Prop forum
You will probably use 3 cogs for this.
Serial cog running the FullDuplexSerial object
Lux driver cog
Cog to handle the data transfer between the other 2 cogs
The simplest way will be having a hub buffer holding all display characters. Then the lux driver cog will just keep displaying this set of characters. Get this part working first. Then you can expand to have the serial update the buffer. Once working you can use a hub flag to indicate when updates are required to save power as I am guessing the lux panel is intelligent in that it has latches for each character that do not need updating unless the content changes.
Anyway, we are here to help. So just ask. Use the (code) and (/code) tags to post code. Note replace ( and ) with less-than and greater-than characters.
I started just with a main loop to prove out some basics. Here's my code:
/* TransLuxController.c */ #include "simpletools.h" int main() { high(2); // enable bottom (high = off) low(3); // data bottom low(4); // clock bottom high(5); // enable top (high = off) low(6); // data top low(7); // clock top while(1) { for (int j = 1; j < 8; j++) { for (int i = 0; i < 2; i++) { shift_out(3, 4, MSBFIRST, 8, 0b10011001); } shift_out(3, 4, MSBFIRST, 3, j); low(2); high(2); } } }What I'm finding so far is that the speed of the built in shift_out library code is not fast enough.
Is it possible to increase the rate data is shifted out? Or drop down to lower level code to achieve this?
Cluso99 -
Unfortunately, the board is not that smart. The shift registers are shared for each of the 7 rows in the characters, so keeping the board lit involves constantly sending data to it even if the data for the characters have not changed.
Thanks,
Matt
#include "simpletools.h" // v2 code int main() { // set P00 to P07 to output DIRA |= 0b11111111; // set P02 and P05 high, and everything else to low OUTA |= 0b00100100; OUTA &= 0b00100100; unsigned int CLKL = 1 << 4; unsigned int DATL = 1 << 3; unsigned int ENAL = 1 << 2; // pauses are micro seconds set_pause_dt(CLKFREQ/1000000); while(1) { for (int j = 1; j < 8; j++) { for (int i = 0; i < 160; i++) { // data high OUTA |= DATL; // clock on OUTA |= CLKL; // clock off OUTA &= ~CLKL; // data low OUTA &= ~DATL; // clock on OUTA |= CLKL; // clock off OUTA &= ~CLKL; } if (j & 0x04) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x02) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x01) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; // enable low OUTA &= ~ENAL; pause(10); // enable high OUTA |= ENAL; } } }I had to restart SimpleIDE to pickup the changes.
/* TransLuxController.c */ #include "simpletools.h" void topdisplay(); void bottomdisplay(); // v3 code int main() { // set P00 to P07 to output DIRA |= 0b11111111; // set P02 and P05 high, and everything else to low OUTA |= 0b00100100; OUTA &= 0b00100100; // pauses are micro seconds set_pause_dt(CLKFREQ/1000000); //cog_run(topdisplay, 128); bottomdisplay(); //int *cog = cog_run(bottomdisplay, 222); //pause(3000000); //cog_end(cog); } void topdisplay() { unsigned int CLKU = 1 << 7; unsigned int DATU = 1 << 6; unsigned int ENAU = 1 << 5; while(1) { for (int j = 1; j < 8; j++) { for (int i = 0; i < 160; i++) { // data high OUTA |= DATU; // clock on OUTA |= CLKU; // clock off OUTA &= ~CLKU; // data low OUTA &= ~DATU; // clock on OUTA |= CLKU; // clock off OUTA &= ~CLKU; } if (j & 0x04) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; if (j & 0x02) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; if (j & 0x01) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; // enable low OUTA &= ~ENAU; pause(20); // enable high OUTA |= ENAU; } } } void bottomdisplay() { unsigned int CLKL = 1 << 4; unsigned int DATL = 1 << 3; unsigned int ENAL = 1 << 2; while(1) { for (int j = 1; j < 8; j++) { for (int i = 0; i < 160; i++) { // data low OUTA &= ~DATL; // clock on OUTA |= CLKL; // clock off OUTA &= ~CLKL; // data high OUTA |= DATL; // clock on OUTA |= CLKL; // clock off OUTA &= ~CLKL; } if (j & 0x04) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x02) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x01) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; // enable low OUTA &= ~ENAL; pause(10); // enable high OUTA |= ENAL; } } }but changing toresults in no output on the display. I'm probably missing something else here.
Probably easiest to write a Simple PASM program that just updates the display from a buffer of characters in hub.
Not sure how C handles a PASM cog ???
If I remember correctly you can like LMM or CMM also create COG code.
But starting a cog requires that THIS startet cog sets the Pins it uses.
So on start of your void bottomdisplay() you are missing:
// set P00 to P07 to output
DIRA |= 0b11111111;
// set P02 and P05 high, and everything else to low
OUTA |= 0b00100100;
OUTA &= 0b00100100;
it has to be done in the same cog.
Enjoy,
Mike
Thank you both for more suggestions.
@Cluso99 - I'll see if the C approach works out and keep PASM in the back pocket. What I've found with the C code now is that I should be able to drive 4 displays wide with only a minor amount of flicker.
@msrobots - Thanks. That makes sense. I suspected there was something like that that I missed.
I've attached a photo showing the display with randomized bits shifted in.
This photo and a video have been added to the album linked in the first post of this discussion.
Now to figure out how to get something readable displayed.
I would be grateful for any comments regarding how I'm using shared memory, particularly so if I'm doing it wrong.
#include "simpletools.h" void top_bank(); void bot_bank(); void charAt(int x, int y, int idx, int color); void dotAt(int x, int y, int rg); static volatile unsigned int top_buff[70]; static volatile unsigned int bot_buff[70]; static volatile unsigned int brightness = 2; static int font[] = { 0x00, 0x00, 0x00, 0x00, 0x00,// (space) 0x00, 0x00, 0x5F, 0x00, 0x00,// ! 0x00, 0x07, 0x00, 0x07, 0x00,// " 0x14, 0x7F, 0x14, 0x7F, 0x14,// # 0x24, 0x2A, 0x7F, 0x2A, 0x12,// $ 0x23, 0x13, 0x08, 0x64, 0x62,// % 0x36, 0x49, 0x55, 0x22, 0x50,// & 0x00, 0x05, 0x03, 0x00, 0x00,// ' 0x00, 0x1C, 0x22, 0x41, 0x00,// ( 0x00, 0x41, 0x22, 0x1C, 0x00,// ) 0x08, 0x2A, 0x1C, 0x2A, 0x08,// * 0x08, 0x08, 0x3E, 0x08, 0x08,// + 0x00, 0x50, 0x30, 0x00, 0x00,// , 0x08, 0x08, 0x08, 0x08, 0x08,// - 0x00, 0x60, 0x60, 0x00, 0x00,// . 0x20, 0x10, 0x08, 0x04, 0x02,// / 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0 0x00, 0x42, 0x7F, 0x40, 0x00,// 1 0x42, 0x61, 0x51, 0x49, 0x46,// 2 0x21, 0x41, 0x45, 0x4B, 0x31,// 3 0x18, 0x14, 0x12, 0x7F, 0x10,// 4 0x27, 0x45, 0x45, 0x45, 0x39,// 5 0x3C, 0x4A, 0x49, 0x49, 0x30,// 6 0x01, 0x71, 0x09, 0x05, 0x03,// 7 0x36, 0x49, 0x49, 0x49, 0x36,// 8 0x06, 0x49, 0x49, 0x29, 0x1E,// 9 0x00, 0x36, 0x36, 0x00, 0x00,// : 0x00, 0x56, 0x36, 0x00, 0x00,// ; 0x00, 0x08, 0x14, 0x22, 0x41,// < 0x14, 0x14, 0x14, 0x14, 0x14,// = 0x41, 0x22, 0x14, 0x08, 0x00,// > 0x02, 0x01, 0x51, 0x09, 0x06,// ? 0x32, 0x49, 0x79, 0x41, 0x3E,// @ 0x7E, 0x11, 0x11, 0x11, 0x7E,// A 0x7F, 0x49, 0x49, 0x49, 0x36,// B 0x3E, 0x41, 0x41, 0x41, 0x22,// C 0x7F, 0x41, 0x41, 0x22, 0x1C,// D 0x7F, 0x49, 0x49, 0x49, 0x41,// E 0x7F, 0x09, 0x09, 0x01, 0x01,// F 0x3E, 0x41, 0x41, 0x51, 0x32,// G 0x7F, 0x08, 0x08, 0x08, 0x7F,// H 0x00, 0x41, 0x7F, 0x41, 0x00,// I 0x20, 0x40, 0x41, 0x3F, 0x01,// J 0x7F, 0x08, 0x14, 0x22, 0x41,// K 0x7F, 0x40, 0x40, 0x40, 0x40,// L 0x7F, 0x02, 0x04, 0x02, 0x7F,// M 0x7F, 0x04, 0x08, 0x10, 0x7F,// N 0x3E, 0x41, 0x41, 0x41, 0x3E,// O 0x7F, 0x09, 0x09, 0x09, 0x06,// P 0x3E, 0x41, 0x51, 0x21, 0x5E,// Q 0x7F, 0x09, 0x19, 0x29, 0x46,// R 0x46, 0x49, 0x49, 0x49, 0x31,// S 0x01, 0x01, 0x7F, 0x01, 0x01,// T 0x3F, 0x40, 0x40, 0x40, 0x3F,// U 0x1F, 0x20, 0x40, 0x20, 0x1F,// V 0x7F, 0x20, 0x18, 0x20, 0x7F,// W 0x63, 0x14, 0x08, 0x14, 0x63,// X 0x03, 0x04, 0x78, 0x04, 0x03,// Y 0x61, 0x51, 0x49, 0x45, 0x43,// Z 0x00, 0x00, 0x7F, 0x41, 0x41,// [ 0x02, 0x04, 0x08, 0x10, 0x20,// "\" 0x41, 0x41, 0x7F, 0x00, 0x00,// ] 0x04, 0x02, 0x01, 0x02, 0x04,// ^ 0x40, 0x40, 0x40, 0x40, 0x40,// _ 0x00, 0x01, 0x02, 0x04, 0x00,// ` 0x20, 0x54, 0x54, 0x54, 0x78,// a 0x7F, 0x48, 0x44, 0x44, 0x38,// b 0x38, 0x44, 0x44, 0x44, 0x20,// c 0x38, 0x44, 0x44, 0x48, 0x7F,// d 0x38, 0x54, 0x54, 0x54, 0x18,// e 0x08, 0x7E, 0x09, 0x01, 0x02,// f 0x08, 0x14, 0x54, 0x54, 0x3C,// g 0x7F, 0x08, 0x04, 0x04, 0x78,// h 0x00, 0x44, 0x7D, 0x40, 0x00,// i 0x20, 0x40, 0x44, 0x3D, 0x00,// j 0x00, 0x7F, 0x10, 0x28, 0x44,// k 0x00, 0x41, 0x7F, 0x40, 0x00,// l 0x7C, 0x04, 0x18, 0x04, 0x78,// m 0x7C, 0x08, 0x04, 0x04, 0x78,// n 0x38, 0x44, 0x44, 0x44, 0x38,// o 0x7C, 0x14, 0x14, 0x14, 0x08,// p 0x08, 0x14, 0x14, 0x18, 0x7C,// q 0x7C, 0x08, 0x04, 0x04, 0x08,// r 0x48, 0x54, 0x54, 0x54, 0x20,// s 0x04, 0x3F, 0x44, 0x40, 0x20,// t 0x3C, 0x40, 0x40, 0x20, 0x7C,// u 0x1C, 0x20, 0x40, 0x20, 0x1C,// v 0x3C, 0x40, 0x30, 0x40, 0x3C,// w 0x44, 0x28, 0x10, 0x28, 0x44,// x 0x0C, 0x50, 0x50, 0x50, 0x3C,// y 0x44, 0x64, 0x54, 0x4C, 0x44,// z 0x00, 0x08, 0x36, 0x41, 0x00,// { 0x00, 0x00, 0x7F, 0x00, 0x00,// | 0x00, 0x41, 0x36, 0x08, 0x00,// } 0x08, 0x08, 0x2A, 0x1C, 0x08,// -> 0x08, 0x1C, 0x2A, 0x08, 0x08 // <- }; // main cog code, responsible for starting the independent cogs that update each bank int main() { // pauses are micro seconds set_pause_dt(CLKFREQ/1000000); cog_run(top_bank, 128); cog_run(bot_bank, 128); // this produces a changing random pattern through all display dots //while (1) { // for (int j=0; j<10; j++) { // for (int i=0; i < 7; i++) { // top_buff[i*10+j] = (rand()<<30)|(rand()<<15)|rand(); // bot_buff[i*10+j] = (rand()<<30)|(rand()<<15)|rand(); // } // } // pause(100000); //} // scroll all of the characters in the font array and // alternate the color of each character int cf = 0; int array_len = sizeof(font)/sizeof(int); while (1) { int cfi = cf; for (int y = 0; y < 4; y++) { for (int x = 0; x < 16; x++) { int clr = cfi % 3 + 1; charAt(x, y, cfi, clr); cfi++; if (cfi >= (array_len / 5)) { cfi = 0; } } } pause(1000000); cf++; if (cf >= (array_len / 5)) { cf = 0; } } } void charAt(int x, int y, int idx, int color) { for (int col = 0; col < 5; col++) { int ver = font[idx * 5 + col]; for (int row = 0; row < 7; row++) { if ((ver & 0x01) > 0) { dotAt(x * 5 + col, y * 7 + row, color); } else { dotAt(x * 5 + col, y * 7 + row, 0b00); } ver = ver >> 1; } } } void dotAt(int x, int y, int rg) { int ay = (y > 13) ? y - 14 : y; int addToMod = ((ay > 6) ? (ay - 7) * 10 : 5 + (ay * 10)) + x / 16; int addrColShift = 30 - (x % 16 * 2); if (y > 13) { bot_buff[addToMod] &= ~(0b11 << addrColShift); if (rg > 0) { bot_buff[addToMod] |= (rg & 0b11) << addrColShift; } } else { top_buff[addToMod] &= ~(0b11 << addrColShift); if (rg > 0) { top_buff[addToMod] |= (rg & 0b11) << addrColShift; } } } void top_bank() { // set P05 to P07 to output DIRA |= 0b11100000; // set P05 high, and everything else to low OUTA |= 0b00100000; OUTA &= 0b00100000; unsigned int CLKU = 1 << 7; unsigned int DATU = 1 << 6; unsigned int ENAU = 1 << 5; unsigned int offset; unsigned int end; unsigned int datval; while(1) { for (int j = 1; j < 8; j++) { offset = (j-1)*10; end = offset + 10; for (int addr = offset; addr < end; addr ++) { datval = top_buff[addr]; for (int bit = 0; bit < 32; bit++) { if (datval & 0x80000000) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; datval = datval << 1; } } // shift out each of the row select bits if (j & 0x04) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; if (j & 0x02) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; if (j & 0x01) { OUTA |= DATU; } else { OUTA &= ~DATU; } OUTA |= CLKU; OUTA &= ~CLKU; // toggle enable OUTA &= ~ENAU; pause(brightness); OUTA |= ENAU; } } } void bot_bank() { // set P00 to P07 to output DIRA |= 0b00011111; // set P02 and P05 high, and everything else to low OUTA |= 0b00000100; OUTA &= 0b00000100; unsigned int CLKL = 1 << 4; unsigned int DATL = 1 << 3; unsigned int ENAL = 1 << 2; unsigned int offset; unsigned int end; unsigned int datval; while(1) { for (int j = 1; j < 8; j++) { offset = (j-1) * 10; end = offset + 10; for (int addr = offset; addr < end; addr ++) { datval = bot_buff[addr]; for (int bit = 0; bit < 32; bit++) { if (datval & 0x80000000) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; datval = datval << 1; } } // shift out each of the row select bits if (j & 0x04) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x02) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; if (j & 0x01) { OUTA |= DATL; } else { OUTA &= ~DATL; } OUTA |= CLKL; OUTA &= ~CLKL; // toggle enable OUTA &= ~ENAL; pause(brightness); OUTA |= ENAL; } } }