LCD-7S04 Serial 7 Segment LCD Control

Hello all,

This is my first posting, I hope I'm doing this right!

I recently bought this 7 Segment 4 digit LCD display (Embedded Adventures - Displays - 4 digit 7 segment LCD display), and am having a lot of trouble understanding how to get it to display what I want. I have programmed a countdown timer and would like to display the time, but I am unsure how to get the time (unsigned long, converted to ints) to display on the LCD. The site has a little tutorial in the datasheet, I hope one of you guys understand more than I do!

Thanks a ton,
Nate

I would recommend you use Arduino's SPI pins to connect the display. You'll need MOSI (pin11, data) and SCK (pin13, clock).
The datasheet mentions that all you have to do is shift in 5 bytes to update the display, and as you said they give a simple example. The display sections are active low, meaning that the bits you want to be lit should be 0s and the ones you need off should be 1s.
Please post what you are having a problem with exactly and we'll try to focus our help in that area.

Hey, thanks for the quick reply. I don't actually understand the example, so it hasn't done me any good. When you say shift in bits, what specifically does that look like in code? I haven't used the spi pins before... for example, if I want to display a 3 in the first digit, I need to light up sections 6 5 4 3 and 2 (I think). What should I send to the MOSI pin?

You use SPI. There's an SPI library.
Basically you call SPI.begin() in the setup method and then SPI.transfer('your data byte') 5 times for each LCD section, where the first byte is the column.

By shifting-out what they mean is transfer the data. This LCD uses a serial connection, so each bit of a byte has to be "clocked in". Basically the data line is held at '1' or '0' and when the clock signal goes low (the datasheet says it reacts to clock-fall) the state of the data line registers as an input bit.
You probably just need to read a little bit on serial communication in general if you want to understand it better.

Here's a sample program I quickly came up with. I haven't tested it, didn't even compile it, but it should get you started.

#include <SPI.h>

                      //   0     1     2     3     4     5     6     7     8     9
const uint8_t nums[] = { 0xEE, 0x22, 0x7C, 0x76, 0xB2, 0xD6, 0xDE, 0x62, 0xFE, 0xF6 };
const uint8_t dot = 0x01;
bool column = true;
uint8_t display[5];

void setup()
{
	SPI.begin();	
	SPI.setBitOrder(MSBFIRST);
	SPI.setDataMode(SPI_MODE2); 
	/*
		At CPOL=1 the base value of the clock is one (inversion of CPOL=0)
		For CPHA=0, data is captured on clock's falling edge and data is propagated on a rising edge.
		Mode 	CPOL 	CPHA
		0 		0 		0
		1 		0 		1
		2 		1 		0
		3 		1 		1
	*/
}

void loop()
{
	// Display system uptime
	display[0] = column ? 0xFF : 0x00; // if column = true then display[0] = 0xFF else display[0] = 0x00

	uint16_t secs = millis() / 1000;
	uint16_t mins = secs / 60;
	secs %= 60;
	display[1] = nums[(mins / 10) % 10]; // Tens of minutes
	display[2] = nums[mins % 10];
	display[3] = nums[(secs / 10) % 10]; // Tens of seconds
	display[4] = nums[secs % 10];

	for (uint8_t i = 0; i < 5; i++)
		SPI.transfer(~display[i]);

	// if you want to turn the dot on, you should use the following:
	// SPI.transfer(~(display[i] | dot));
	// ~ is a binary inverse. If your binary value is 00011010 its inverse will be 11100101, just as the datasheet requires.

	column = !column;
	delay(500);
}

First of all, holy crap thanks a ton! Just having some code with comments in front of me illuminated so much that many tutorials has not done, awesome.

So I tried your code (it compiled, first try) and the display is just all on. So I edited it as much as I felt comfortable, which was as follows

#include <SPI.h>

// 0 1 2 3 4 5 6 7 8 9
const uint8_t nums[] = { 0xEE, 0x22, 0x7C, 0x76, 0xB2, 0xD6, 0xDE, 0x62, 0xFE, 0xF6 };
const uint8_t dot = 0x01;
bool column = true;
uint8_t disp[5];

void setup()
{
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE2);
pinMode(13, OUTPUT);
/*
At CPOL=1 the base value of the clock is one (inversion of CPOL=0)
For CPHA=0, data is captured on clock's falling edge and data is propagated on a rising edge.
Mode CPOL CPHA
0 0 0
1 0 1
2 1 0
3 1 1
*/
}

void loop()
{
// Display system uptime
disp[0] = column ? 0xFF : 0x00; // if column = true then display[0] = 0xFF else display[0] = 0x00

uint16_t secs = millis() / 1000;
uint16_t mins = secs / 60;
secs %= 60;
disp[1] = nums[(mins / 10) % 10]; // Tens of minutes
disp[2] = nums[mins % 10];
disp[3] = nums[(secs / 10) % 10]; // Tens of seconds
disp[4] = nums[secs % 10];

for (uint8_t i = 0; i < 5; i++){
digitalWrite(13, HIGH);
SPI.transfer(~disp*);*

  • digitalWrite(13, LOW);*
  • }*
  • // if you want to turn the dot on, you should use the following:*
    _ // SPI.transfer(~(display | dot));_
    * // ~ is a binary inverse. If your binary value is 00011010 its inverse will be 11100101, just as the datasheet requires.*
    * column = !column;*
    * delay(500);*

}
So I just added code to turn my clock line on/off to send a byte. However, the display is still fully on. I have tried the prgm using my "data" connected to pin 12, although I also tried 11 and 10 (I want MOSI, pin 11, right? Or SPI pin 10?). When connected to pin 10 the display turns fully off.
I really appreciate your help and tolerance of my coding ignorance, I am stoked to be learning something new though!

You're welcome :slight_smile:

You don't have to change anything in the code, SPI is a hardware interface, you "tell" it to transmit a byte and it handles the data and clock lines on its own. Pin11 is the MOSI (Master Out Slave In) line, basically the data-out (out of the Arduino) line. Pin13 is the clock line, it toggles automatically as I already mentioned, so you don't have to manipulate any of the SPI pins yourself. Pin12 is the MISO (Master In Slave Out) pin, basically this is the data-in line, you only use it in case the devices you're "talking to" can transmit data back to the Arduino, this is not something your LCD can do, so you just leave it unused.
So the things you've added are not needed. One possible problem with the code is that the SPI settings transmits data at a rate of 4MHz by default. This might be too fast for the display.

  • I would suggest trying to set SPI to the lowest speed possible (just to see whether it works) or not. Add this line after SPI.begin: SPI.setClockDivider(SPI_CLOCK_DIV128);
  • If that doesn't help try to change SPI_MODE from 2 to the other options 0, 1, 3.
  • In the case that fails, SPI might still be too fast for this particular display. In such a case you will probably have to implement your own function to "clock-in" the data yourself.
    I think that the demo code in the datasheet is written in PIC specific C, but it shouldn't be very hard to adapt it to compile for the Arduino.

I tried all your suggestions, no success.

Should I be using the shiftOut() command if I can't get the SPI library to work? I still have yet to get a single comprehensible digit to show up on the display... Are the bytes you put in the nums[] array what I should be using as data?

Also I've never worked with PIC, so I'm pretty confused by what they're doing in the example. However if I can just get the damn thing to print even one digit that I want I think I'll be much better off in the direction of figuring this out!

You can try shiftout, in essence it simulates SPI in software, it might work.
If not I really can't tell you why it doesn't work for you. Do you get anything on the display? Do you see different shapes, changing every second? Does the colon blink?
I never worked with such a display before, so I can't really help you from past experience. I'm pretty sure that the values in nums should correspond with the values for each digit, you can confirm it yourself.
Also use Serial to print out the values to make sure I haven't made any mistakes in the code.

The display doesn't really do anything at all. I can occasionally get it to move some stuff, but with no real consistency. I have contacted Embedded Electronics, and they said they'd write a tutorial. No guarantee on when that will be made though...

I'll keep plugging away, and if I sot anything out I'll let you know. Thanks again!

Can you post the diagram and a picture of how you connected the LCD to the Arduino?

Just in case I translated the 'shiftout' function included in the datasheet into Arduino C:

const uint8_t dataPin = 11;
const uint8_t clkPin = 13;

void ea_lcd7s04_write_data_byte(uint8_t data)
{  
	for( uint8_t count = 0 ; count < 8 ; count++ )
	{ 
		digitalWrite(dataPin, (data & 0x80));
		data = data << 1; 
		digitalWrite(clkPin, LOW);
		delayMicros(10);
		digitalWrite(clkPin, HIGH);
	} 
}

I have spent a few minutes trying to figure out how to insert an image... no luck. I haven't felt like such a noob in a good long while.

However, let me describe it to you in vivid detail.
The VDD is connected to 5V
The DI is connected to pin 11
The VSS is connected to GND
And the CLK is connected to 13

So in your ported code, would "data" be one array value in "nums" (e.g. ea_lcd7s04_write_data_byte(~nums[0]) ) ? What does the & 0x80 do? Your code using a digitalWrite instead of a shiftOut, is that preferable?

Well the connectivity seems to be correct.
To attach an image, simply click on "Additional Options" below the text box when you click on Reply.

This function is basically what shiftOut does. You can play with the delayMicros value, maybe increasing/decreasing it will have some effect.
And yes, you call it as just like you quoted.
I believe that SPI and shiftOut should both work, but when all fails you need to see if it's a problem with the code or maybe the display or the connections to the display. So I recommend giving this function a go to see what happens.
& is a logic AND, basically data & 0x80 will either return 0x80 or 0. Anything that isn't 0 is considered a HIGH, 0 is LOW, therefore when the most significant bit in data is '1' dataPin will be HIGH and when it's 0 dataPin will be LOW.
data = data << 1; shifts the bits in the variable left by one place, meaning that with every pass of the loop you're going through all the bits in 'data'.

Okay.

After playing around with this a bit, I have gotten this code. All this does however, is make the screen do a disco dance party, turning segments on and off at what seems like random. I must say this is better than before, but not by much. Also, in the example code there is a bit of syntax that says data.7, what the .7 for? Is it something important?

void setup()
{
                       //   0     1     2     3     4     5     6     7     8     9
//const uint8_t nums[] = { 0xEE, 0x22, 0x7C, 0x76, 0xB2, 0xD6, 0xDE, 0x62, 0xFE, 0xF6 };

pinMode(10, OUTPUT);
pinMode(12, OUTPUT);	
digitalWrite(12,LOW);
}


void loop() {
byte nums[] = { 0xEE, 0x22, 0x7C, 0x76, 0xB2, 0xD6, 0xDE, 0x62, 0xFE, 0xF6 };

  ea_lcd7s04_write_data_byte(~(nums[0]));
  ea_lcd7s04_write_data_byte(~(nums[1]));
  ea_lcd7s04_write_data_byte(~(nums[2]));
  ea_lcd7s04_write_data_byte(~(nums[3]));
  delay(5000);
  
  }




void ea_lcd7s04_write_data_byte(byte data){
  
	for( int count = 0 ; count < 8 ; count++ )
	{ 
                digitalWrite(12, HIGH);
                digitalWrite(10, data & 0x80);
		data = data << 1; 
		digitalWrite(12, LOW);
		delay(200);
		
	} 
}

So this is basically your code, with the second half of the example code added. I used 1234 as a target, seemed do able... Anyway, let me know if you have any ideas why this might be acting so strangely.

You forgot that you need to write 5 bytes to the LCD each time, the first being 0xFF or 0x00 for the colon section, you're currently writing only 4.

Progress!

I have gotten the display to display numbers, and have it mostly working. Here's my code:

              //  0             1          2           3           4           5            6           7           8          9
byte nums[] = {0b11011101, 0b01000100, 0b11111000, 0b11101100, 0b01100101, 0b10101101, 0b10111101, 0b11000100, 0b11111101, 0b11100101 };
 
void setup()
{
// Set data as pin 10
pinMode(10, OUTPUT);

// Set CLK as pin 12
pinMode(12, OUTPUT);	
digitalWrite(12,LOW);
}


void loop() {
    // turn on colon 
  ea_lcd7s04_write_data_byte(0x00);
  
  // try to display 0 1 2 3
  ea_lcd7s04_write_data_byte(~(nums[0]));
  ea_lcd7s04_write_data_byte(~(nums[1]));
  ea_lcd7s04_write_data_byte(~(nums[2]));
  ea_lcd7s04_write_data_byte(~(nums[3]));
  delay(100);
  
  }




void ea_lcd7s04_write_data_byte(byte data){
  //data = byte(data);
	for( int count = 0 ; count < 8 ; count++ )
	{ 
                digitalWrite(12, HIGH);
                digitalWrite(10, data & 0x80);
		data = data << 1; 
                delayMicroseconds(100);
		digitalWrite(12, LOW);
		
		
	} 
}

With this code I can display numbers. However, it seems like there is a bit getting bumped around. So, if I display a zero (0b11011101) in my first digit, the last bit seems to get bumped into my 1 in the 2nd digits place. See pic below.

I have to think this is some strangeness in my for() loop, but I can't seem to get an iteration that works.

I would like to take this time to once again thank you for sticking with me, I hope you are getting some gratification out of this. I'm learning a ton!

I'm glad you got it working, looks good. I think the function you wrote is a little bit off.

This is a direct translation of the function from the datasheet:

void ea_lcd7s04_write_data_byte(byte data)
{
	for( int count = 0 ; count < 8 ; count++ )
	{ 
                digitalWrite(10, data & 0x80);
		data = data << 1; 
		digitalWrite(12, LOW);	
                delayMicroseconds(100);	
                digitalWrite(12, HIGH);
	} 
}

I think it should fix that odd bit problem.

Success! I had to modify my code, but it now makes a lot more sense as the bit correlate with the data sheet. Question though: how does the first bit get sent if the line is LOW going into the first iteration of the for loop? Here's my working code, hope someone else can enjoy it!

              //  0             1          2           3           4           5            6           7           8          9           null
byte nums[] = {0b11101110, 0b00100010, 0b01111100, 0b01110110, 0b10110010, 0b11010110, 0b11011110, 0b01100010, 0b11111110, 0b11110010, 0b00000000};
byte disp[] = {0b00000000, 0b00000000, 0b00000000, 0b00000000};
void setup()
{
int clkPin = 12;
int dataPin = 10;
  

pinMode(dataPin, OUTPUT);
pinMode(clkPin, OUTPUT);	
digitalWrite(dataPin, LOW);
digitalWrite(clkPin, LOW);
}


void loop() {
    // turn on colon 
    
    unsigned long secs = millis() / 1000;
	unsigned long mins = secs / 60;
	secs %= 60;
	disp[0] = nums[(mins / 10) % 10]; // Tens of minutes
	disp[1] = nums[mins % 10];
	disp[2] = nums[(secs / 10) % 10]; // Tens of seconds
	disp[3] = nums[secs % 10];

  ea_lcd7s04_write_data_byte(0x00);
  
  // try to display 0 1 2 3
  ea_lcd7s04_write_data_byte(~(disp[0]));
  ea_lcd7s04_write_data_byte(~(disp[1]));
  ea_lcd7s04_write_data_byte(~(disp[2]));
  ea_lcd7s04_write_data_byte(~(disp[3]));

  delay(100);
  
  
  

  
  }




void ea_lcd7s04_write_data_byte(byte data)
{
	for( int count = 0 ; count < 8 ; count++ )
	{ 
                digitalWrite(dataPin, data & 0x80);
		data = data << 1; 
		digitalWrite(clkPin, LOW);	
                delayMicroseconds(100);	
                digitalWrite(clkPin, HIGH);
	} 
}

The bit is 'read' by the LCD when the pin is toggled low, but it also has to come back high again to actually "clock" in.
When you first set the pin high, you would always fail to clock in the last bit of the last byte you were trying to write to the lcd.

I got this LCD too i've used the code that you posted, i tweaked it a little bit and i got into a very strange issue:

If i send to the display 910.8 it does display 910.7, if i send 10.8 it displays correctly 10.8 it's pretty weird, 0.8 shows correctly, 1.8 shows 1.7, souds like a rounding problem but i can't sort it out at all.
This display is pretty cheap i really hope that some of the gurus will write a library for this, i've implemented 2 functions, one
clears the lcd the seconds displays a positive floating point form 0 to 999.9.
The LCD is hooked to pin 8 and pin 12.

It's my forth or fith Arduino code so plase forgive me is is badly coded and uncommented.

void lcd_7s04_positive_disp(float number=0)

{
byte nums[] = {0b11101110, 0b00100010, 0b01111100, 0b01110110, 0b10110010, 0b11010110, 0b11011110, 0b01100010, 0b11111110, 0b11110010, 0b00000000};
byte dec[]  = {0b11101111, 0b00100011, 0b01111101, 0b01110111, 0b10110011, 0b11010111, 0b11011111, 0b01100011, 0b11111111, 0b11110011, 0b00000000};
byte decimals,ones,tens,hundreds,thousands=0;

   if ((number > 999.9) || (number < 0))
   // if number is over 999.9 display shows Err.
    {
      ea_lcd7s04_write_data_byte(~(0b11011100));
      ea_lcd7s04_write_data_byte(~(0b00011000));
      ea_lcd7s04_write_data_byte(~(0b00011001));
      ea_lcd7s04_write_data_byte(~(0b00000000));
   }

  if ((number <= 999.9) && (number > 99.9))
    { 
     hundreds = number/100;
     number = number-hundreds*100;
     tens = number/10;
     number = number-tens*10;
     ones = number/1;
     number = number-ones*1;
     decimals = number*10;
     ea_lcd7s04_write_data_byte(~(nums[hundreds]));
     ea_lcd7s04_write_data_byte(~(nums[tens]));
     ea_lcd7s04_write_data_byte(~(dec[ones]));
     ea_lcd7s04_write_data_byte(~(nums[decimals]));
}

  if ((number <= 99.9) && (number > 9.9))
    { 
     tens = number/10;
     number = number-tens*10;
     ones = number/1;
     number = number-ones*1;
     // decimals = number*10-ones*10; 
     decimals = number*10;
     ea_lcd7s04_write_data_byte(~(0b00000000));
     ea_lcd7s04_write_data_byte(~(nums[tens]));
     ea_lcd7s04_write_data_byte(~(dec[ones]));
     ea_lcd7s04_write_data_byte(~(nums[decimals]));
}

  if ((number <= 9.9) && (number > 0))
    { 
     ones = number/1;
     number = number-ones*1;
     decimals = number*10;
     ea_lcd7s04_write_data_byte(~(0b00000000));
     ea_lcd7s04_write_data_byte(~(0b00000000));
     ea_lcd7s04_write_data_byte(~(dec[ones]));
     ea_lcd7s04_write_data_byte(~(nums[decimals]));
}

}
void setup()
{
pinMode(8, OUTPUT); //data
pinMode(12, OUTPUT); // clk
digitalWrite(8, LOW);
digitalWrite(12, LOW);
Serial.begin(9600);
}

void loop() 
 {
 
  lcd_7s04_clr();
  lcd_7s04_positive_disp(1.8);
  delay(500);
}

void lcd_7s04_clr()
{
  ea_lcd7s04_write_data_byte(~(0b00000000));
  ea_lcd7s04_write_data_byte(~(0b00000000));
  ea_lcd7s04_write_data_byte(~(0b00000000));
  ea_lcd7s04_write_data_byte(~(0b00000000));
} 

void ea_lcd7s04_write_data_byte(byte data)
{
	for( int count = 0 ; count < 8 ; count++ )
	{ 
                digitalWrite(8, data & 0x80);
		data = data << 1; 
		digitalWrite(12, LOW);	// clk
                delayMicroseconds(100);	
                digitalWrite(12, HIGH);
	} 
}