mike_p
Posts: 30
Joined: Fri Aug 01, 2014 2:35 pm
Location: Surrey, UK

16x2 LCD - simple C code

Tue Dec 09, 2014 7:09 pm

Hi

I've recently bought a 16x2 LCD unit (they're stupidly cheap now!)

When starting to play with it I found various python scripts and a couple of C scripts, but felt they were either unwieldy or hid too much control from the programmer.

In particular I felt it was clumsy in that in all the code I've found, the routines set/unset the RS pin for every write operation.

So I've put together some simplified code (using wiringPi) which I hope will help someone else.

If I've made any silly errors, please feel free to advise me!

Code: Select all

/*
 * lcd.c:
 *	  Simple program to send a string to the LCD 
 */

#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>

#define LCD_E 23
#define LCD_RS 22
#define LCD_D4 24
#define LCD_D5 25
#define LCD_D6 8
#define LCD_D7 7

void pulseEnable ()
{
   digitalWrite (LCD_E, HIGH) ; 
   delay(0.5); //  1/2 microsecond pause - enable pulse must be > 450ns
   digitalWrite (LCD_E, LOW) ; 
}

/*
  send a byte to the lcd in two nibbles
  before calling use SetChrMode or SetCmdMode to determine whether to send character or command
*/
void lcd_byte(char bits)
{
  digitalWrite (LCD_D4,(bits & 0x10)) ;  
  digitalWrite (LCD_D5,(bits & 0x20)) ;  
  digitalWrite (LCD_D6,(bits & 0x40)) ;  
  digitalWrite (LCD_D7,(bits & 0x80)) ;  
  pulseEnable();

  digitalWrite (LCD_D4,(bits & 0x1)) ;  
  digitalWrite (LCD_D5,(bits & 0x2)) ;  
  digitalWrite (LCD_D6,(bits & 0x4)) ;  
  digitalWrite (LCD_D7,(bits & 0x8)) ;  
  pulseEnable();      	
}

void SetCmdMode() 
{
  digitalWrite (LCD_RS, 0); // set for commands
}

void SetChrMode() 
{
  digitalWrite (LCD_RS, 1); // set for characters
}

void lcd_text(char *s)
{
  while(*s) 
	lcd_byte(*s++);
 }

void lcd_init()
{
   wiringPiSetupGpio () ; // use BCIM numbering
   // set up pi pins for output
   pinMode (LCD_E,  OUTPUT);
   pinMode (LCD_RS, OUTPUT);
   pinMode (LCD_D4, OUTPUT);
   pinMode (LCD_D5, OUTPUT);
   pinMode (LCD_D6, OUTPUT);
   pinMode (LCD_D7, OUTPUT);
   
   // initialise LCD
   SetCmdMode(); // set for commands
   lcd_byte(0x33); // full init 
   lcd_byte(0x32); // 4 bit mode
   lcd_byte(0x28); // 2 line mode
   lcd_byte(0x0C); // display on, cursor off, blink off
   lcd_byte(0x01);  // clear screen
   delay(3);        // clear screen is slow!
}

int main (int argc, char *argv []) 
{
  lcd_init();
  
  SetChrMode(); 
  if (argc>1) 
     lcd_text(argv[1]);
  else 
     lcd_text("hello world!");
	 
  return 0 ;
}
I've reduced pauses/delays to the minimum so it runs fast.

to run (as root):

Code: Select all

./lcd 

will print hello world or you can send a string as a command line parameter

Code: Select all

./lcd  "my text" 

gkoper
Posts: 13
Joined: Sun Mar 10, 2013 2:53 pm

Re: 16x2 LCD - simple C code

Tue Jun 20, 2017 5:45 pm

I used this as a skeleton to quickly make mine, using hard-coded bit set/clr operations. Works wonderfully, after properly adding delays as suggested on page 17 of the manual GE-C1602B-TMI-JT/R. A minimum of 40 microseconds is needed for commands and 1.55 ms for a display clear.

User avatar
FTrevorGowen
Forum Moderator
Forum Moderator
Posts: 5048
Joined: Mon Mar 04, 2013 6:12 pm
Location: Bristol, U.K.
Contact: Website

Re: 16x2 LCD - simple C code

Tue Jun 20, 2017 7:56 pm

gkoper wrote:I used this as a skeleton to quickly make mine, using hard-coded bit set/clr operations. Works wonderfully, after properly adding delays as suggested on page 17 of the manual GE-C1602B-TMI-JT/R. A minimum of 40 microseconds is needed for commands and 1.55 ms for a display clear.
FWIW, you may find my "tweaked timings" for reliable working with different versions of 4-bit or 8-bit (physical) interfaces of interest**:
http://www.cpmspectrepi.uk/raspberry_pi ... gData.html
Trev.
** My 'C' code is deliberately "structured" to reflect/mimic the circuitry/i.c.'s etc. in use and, to some extent, the pin labling/terminology of the i.c.'s also.
Still running Raspbian Jessie on some older Pi's (an A, B1, B2, B+, P2B, 3xP0, P0W) but Stretch on my 2xP3A+, P3B+, P3B, B+, A+ and a B2. See: https://www.cpmspectrepi.uk/raspberry_pi/raspiidx.htm

User avatar
Gavinmc42
Posts: 3613
Joined: Wed Aug 28, 2013 3:31 am

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 1:56 am

Removing delays can stop it working.
LCD drivers are slow as they are designed not to use much power.

Just been doing some software for a DIY i2c LCD, spent hours trying to find what was wrong yesterday.
Added delay first thing this morning and now it works. :oops:

I also tie the R/W so the Pi only writes, reading 5V LCD driver bus back onto 3V3 GPIO is not a good idea.
This means you only need 6 GPIO pins on the Pi.
Using i2c LCD saves even more pins.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 6:29 am

The code is technically wrong but you will get away with it on WiringPi so long as it's GPIO is slow :-)

Someone referenced the GE-C1602B-TMI-JT/R so lets look at the datasheet
https://www.manualshelf.com/manual/glei ... ge-15.html

The thing I would like you to look at is a little thing called Data hold time (Th) and then look at this code

Code: Select all

void lcd_byte(char bits)
{
  digitalWrite (LCD_D4,(bits & 0x10)) ; 
  digitalWrite (LCD_D5,(bits & 0x20)) ; 
  digitalWrite (LCD_D6,(bits & 0x40)) ; 
  digitalWrite (LCD_D7,(bits & 0x80)) ; 
  pulseEnable();     /* E-CLK PULSED high then low */

  digitalWrite (LCD_D4,(bits & 0x1)) ;   /* REALLY you are going to change the data immediately what about data hold */
  digitalWrite (LCD_D5,(bits & 0x2)) ; 
  digitalWrite (LCD_D6,(bits & 0x4)) ; 
  digitalWrite (LCD_D7,(bits & 0x8)) ; 
  pulseEnable();         
}
That LCD is fast the data hold is only 10ns some are much much slower

This one is 40ns
http://www.dema.net/pdf/bolymin/[email protected]
This one is 80ns
https://www.sparkfun.com/datasheets/LCD ... 8STBWW.pdf

I know from experience some low power ones get out to over 250ns and commercially we only switch the data bus when we lift the E-clock line you leave the data out on the bus until then to meet the data hold requirements.

So the question to know if the code is safe is how fast can that digitalWrite occur after the pulseEnable() call???

User avatar
FTrevorGowen
Forum Moderator
Forum Moderator
Posts: 5048
Joined: Mon Mar 04, 2013 6:12 pm
Location: Bristol, U.K.
Contact: Website

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 9:46 am

LdB wrote: ...
I know from experience some low power ones get out to over 250ns and commercially we only switch the data bus when we lift the E-clock line you leave the data out on the bus until then to meet the data hold requirements.

So the question to know if the code is safe is how fast can that digitalWrite occur after the pulseEnable() call???
1) Presumably for microcontroller/microprocessor "direct drive". (The nearest equivalent for the Pi's GPIO's + wiringPi is 8-bit GPIO write for the data ie. ~1uS)
2) Everything has to "slow down" if an I2C device is used to provide all the control signals since the delay in writing the serial control and data bits to the I2C chips parallel port has to be allowed for. (and, maybe, also the possible "open-drain" style outputs settling. This is something I've not been able to investigate in detail, no longer having access to even a basic oscilloscope).
Trev.
Still running Raspbian Jessie on some older Pi's (an A, B1, B2, B+, P2B, 3xP0, P0W) but Stretch on my 2xP3A+, P3B+, P3B, B+, A+ and a B2. See: https://www.cpmspectrepi.uk/raspberry_pi/raspiidx.htm

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 9:49 am

mike_p wrote:When starting to play with it I found various python scripts and a couple of C scripts, but felt they were either unwieldy or hid too much control from the programmer.
Personally, I don't find this ...

Code: Select all

#include <wiringPi.h>
#include <lcd.h>
#define LCD_E 23
#define LCD_RS 22
#define LCD_D4 24
#define LCD_D5 25
#define LCD_D6 8
#define LCD_D7 7
#define LCD_ROWS 2
#define LCD_COLS 16
#define LCD_BITS 4
...
  wiringPiSetupGpio() ;
  int fd = lcdSetup (LCD_ROWS, LCD_COLS, LCD_BITS, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0,0,0,0) ;
  lcdPrintf (fd, "Hello, world") ;
...
( compile with -lwiringPi -lwiringPiDev )

at all unwieldy or clumsy ...

... and with the distinct advantage in that it will work with all (supported) GPIO expander chips like the mcp23008, '017, pcf8574, etc.

-Gordon
--
Gordons projects: https://projects.drogon.net/

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 10:11 am

On a Pi3 baremetal you can clock the GPIO signal at 68Mhz (21-28Mhz on a Pi1) ... you need to set high drive on the output and getting it off the board at that speed isn't fun because it's easy to latch up the IO. I put all my scope captures on the 30OMhz digital storage scope and code up over in the baremetal section.

I have a second source to verify my numbers if you doubt my work
https://github.com/hzeller/rpi-gpio-dma-demo

So no, linux O/S and the wiring pi are going to be the limits not the hardware.

User avatar
FTrevorGowen
Forum Moderator
Forum Moderator
Posts: 5048
Joined: Mon Mar 04, 2013 6:12 pm
Location: Bristol, U.K.
Contact: Website

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 10:35 am

LdB wrote:On a Pi3 baremetal you can clock the GPIO signal at 70Mhz (21-28Mhz on a Pi1) ... you need to set high drive on the output and getting it off the board at that speed isn't fun because it's easy to latch up the IO. All the scope captures on the 30OMhz digital storage scope and code is over in the baremetal section.
So no, linux O/S and the wiring pi are the only going to be the limits not the hardware.
Yes, but that's a "different kettle of fish" - the vast majority of Pi users will not be "bare metal guru's". And, of course, whether there's an overall O.S. or not, as you move from low-level "machine-code" to "higher-level" "languages", whether compiled or interpreted, "things slow down". In my days in R&D, I stopped building systems** around the time of the early micro-controllers (c.f. the post-Z80, Zilog devices with on-board, interpreted integer Basic, which I replaced with a faster, compiled Forth system) and, for me, the Pi allows me to resurrect some of those skills with a limited amount of equipment, as befits a "post-retirement hobby".
Trev.
** Moved on to high-level computer simulations of atmospheric and I.R. physics, being, primarily, a physicist.
Still running Raspbian Jessie on some older Pi's (an A, B1, B2, B+, P2B, 3xP0, P0W) but Stretch on my 2xP3A+, P3B+, P3B, B+, A+ and a B2. See: https://www.cpmspectrepi.uk/raspberry_pi/raspiidx.htm

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 10:37 am

Agreed but this gets back to the problem how fast will those two instructions be executed .. I have no idea it's totally reliant on software. On hardware they can be 14.2/2 = 7.1ns apart.

I am not saying it won't work just I had doubts based solely on the code as presented.

User avatar
FTrevorGowen
Forum Moderator
Forum Moderator
Posts: 5048
Joined: Mon Mar 04, 2013 6:12 pm
Location: Bristol, U.K.
Contact: Website

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 10:49 am

LdB wrote:Agreed but this gets back to the problem how fast will those two instructions be executed .. I have no idea it's totally reliant on software. On hardware they can be 14.2/2 = 7.1ns apart.
Which, especially as this is the 'C'/'C++' forum, comes down to what the respective compilers produce and how that is "interspersed" within other, "O.S" stuff, run on which processor etc. etc. and, TBH, probably somewhat O.T. from the o.p.
Trev.
Still running Raspbian Jessie on some older Pi's (an A, B1, B2, B+, P2B, 3xP0, P0W) but Stretch on my 2xP3A+, P3B+, P3B, B+, A+ and a B2. See: https://www.cpmspectrepi.uk/raspberry_pi/raspiidx.htm

User avatar
[email protected]
Posts: 2020
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
Contact: Website

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 10:56 am

FWIW: To drive these LCDs "properly", you need to use the R/W pin, and read back the device status rather than rely on software (or kernel) timing routines. However most of these devices are 5v which are fine to be driving from the Pi's 3.3v, as long as you tie the R/W line low, to force the device into permanent write mode. Even some of the gpio expander devices (e.g. the Adafruit ones) don't bother to connect up the R/W line - using the spare 7th bit for e.g. backlight control.

-Gordon
--
Gordons projects: https://projects.drogon.net/

User avatar
Gavinmc42
Posts: 3613
Joined: Wed Aug 28, 2013 3:31 am

Re: 16x2 LCD - simple C code

Wed Jun 21, 2017 11:21 am

44780 compatible chips are not all the same either.
I tie the R/W and don't bother to read back, just allow lots of time ie 10-100ms.
Used them on Raspbian/PiCore/Ultibo in 4bit data + 2 control and i2c via microcontrollers.

Even Arduino's and clone stuff like Pic32, Feathers etc.
Lots of libraries and nearly all work fine these days.
Unless you want to use it as console or fast updating, just writing to them works most of the time.
If you remember they are slow :oops:

If you do want to check by reading back you will need a voltage translator/buffer.
If you want to learn them, feel free to write your own routines, just read the data sheet for the driver you are using.
And remember they are not all the same timing wise/voltage I/O etc.
I'm dancing on Rainbows.
Raspberries are not Apples or Oranges

Return to “C/C++”