I am finally getting to the code. I will spread this over three posts. The first code will be the base module that will be imported into python programs for two other projects. The base module can, of course, be run by itself. If it is run by itself, the LCD will display what you see in the photograph in the header of this blog.
I did not originate the base module. I based it on the code found at Matt Hawkins blog (referenced previously). I added to it, made some modifications, and made it more suitable to be imported into other projects. Matt was not the originator of the code either. I believe the original code was modified from code for the Audunio and appears in Micky Skyler's Adafruit tutorial, "Drive a 16x2 LCD with Raspberry Pi" that is referenced in the previous post. That tutorial presents a python class for the display controller called Adadruit_CharLCD.py. This code is quite comprehensive and I believe others took what they needed from it for their code. This is my version of that code, my base module:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# For two line X 16 character display using Hitachi HD4478U Display Driver Chip | |
# Based on Matt Hawkins' code on his blog at www.raspberrypi-spy.co.uk | |
# Matt Hawkins modified the code by texy (From Raspberry Pi forum) | |
# Ultimate source is probably Mickey Sklar's python class in the file Adafruit_CharLCD.py | |
# The above file is at Mickey Sklar's Adafruit turorial, "Drive a 16x2 LCD with Raspberry Pi" | |
# MJL -- thepiandi.blogspot.com -- 03/23/2013 | |
import RPi.GPIO as GPIO | |
import time | |
# Define LCD signal mapping to Pi GPIO port | |
LCD_RS = 7 # Register Select to GPIO 7 | |
LCD_E = 8 # Data Enable to GPIO 8 | |
LCD_D4 = 25 # Display Data Bus D4 to GPIO 25 | |
LCD_D5 = 24 # Display Data Bus D5 to GPIO 24 | |
LCD_D6 = 23 # Display Data Bus D6 to GPIO 23 | |
LCD_D7 = 18 # Display Data Bus D7 to GPIO 18 | |
# Define switch mapping to GPIO PORT | |
SwitchInput = 10 # Switch input to GPIO 10 | |
# Define LCD Module Parameters | |
LCD_WIDTH = 16 # For 16 x 2 Display | |
LCD_CHR = True # For Character Data Register | |
LCD_CMD = False # For Command Data Register | |
# For 16 x 2 display | |
LCD_LINE_1 = 0x80 # LCD DDRAM Left Hand Address for First Line | |
LCD_LINE_2 = 0xC0 # LCD DDRAM Left Hand Address for Second Line | |
# Timing Constants | |
E_PULSE = 0.00005 | |
E_DELAY = 0.00005 | |
def lcd_init(): # Initialize display | |
lcd_byte(0x33, LCD_CMD) # To Understand this and the next command, read | |
lcd_byte(0x32, LCD_CMD) # page 46 of HD 44780U Document | |
lcd_byte(0x28, LCD_CMD) # Use four bits commands, 2 line display, 5x8 char format | |
lcd_byte(0x0C, LCD_CMD) # Turn display on with curser off | |
lcd_byte(0x06, LCD_CMD) # Increment Address, no shift | |
lcd_byte(0x01, LCD_CMD) # Clear Display | |
def lcd_string(message): # Prepare and Send One Line of Characters | |
message = message.ljust(LCD_WIDTH, " ") # Make string 16 characters long | |
for i in range(LCD_WIDTH): | |
lcd_byte(ord(message[i]), LCD_CHR) # Convert each character to ASCII and send to LCD | |
def toggle_enable(): # Toggle Enable pin to latch data in LCD | |
time.sleep(E_DELAY) | |
GPIO.output(LCD_E, True) | |
time.sleep(E_PULSE) | |
GPIO.output(LCD_E, False) | |
time.sleep(E_DELAY) | |
def lcd_byte(bits, mode): # Send the data to the LCD Module | |
# Send byte to data pins | |
# bits = data | |
# mode = True for character | |
# = False for command | |
GPIO.output(LCD_RS, mode) # Register Select (character or command) | |
# Send High Bits | |
GPIO.output(LCD_D4, bits&0x10) | |
GPIO.output(LCD_D5, bits&0x20) | |
GPIO.output(LCD_D6, bits&0x40) | |
GPIO.output(LCD_D7, bits&0x80) | |
toggle_enable() # latch the data | |
# Send low Bits | |
GPIO.output(LCD_D4, bits&0x01) | |
GPIO.output(LCD_D5, bits&0x02) | |
GPIO.output(LCD_D6, bits&0x04) | |
GPIO.output(LCD_D7, bits&0x08) | |
toggle_enable() # latch the data | |
# We start doing stuff here | |
GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers rather than PI pin numbers | |
GPIO.setwarnings(False) # Stop Annoying messages that "channel is already in use" | |
GPIO.setup(LCD_E, GPIO.OUT) # Enable Pin | |
GPIO.setup(LCD_RS, GPIO.OUT) # Register Select Pin | |
GPIO.setup(LCD_D4, GPIO.OUT) # LCD D4 Pin | |
GPIO.setup(LCD_D5, GPIO.OUT) # LCD D5 Pin | |
GPIO.setup(LCD_D6, GPIO.OUT) # LCD D6 Pin | |
GPIO.setup(LCD_D7, GPIO.OUT) # LCD D7 Pin | |
GPIO.setup(SwitchInput, GPIO.IN) # Switch input pin | |
lcd_init() # Initialize the Display | |
lcd_byte(0x01, LCD_CMD) # Clear Display | |
time.sleep(.25) | |
if __name__ == '__main__': | |
# test code | |
try: | |
lcd_byte(LCD_LINE_1, LCD_CMD) | |
lcd_string("Hi From My Pi") | |
lcd_byte(LCD_LINE_2, LCD_CMD) | |
lcd_string("") | |
time.sleep(30) | |
lcd_byte(0x01, LCD_CMD) # Clear Display | |
GPIO.cleanup() | |
except KeyboardInterrupt: | |
lcd_byte(0x01, LCD_CMD) # Clear Display | |
GPIO.cleanup() | |
You will need to have RPI.GPIO installed to control the GPIO pins on the PI. I believe some distributions already include it. Micky Skyler's Adafruit tutorial covers how to get it if you need it.
Lines 23 and 24 are probably for my situation only. More on this in my next post.
If you want to understand the code you have to read it along with the
Hitachi data sheet for the HD4478U. Pages 24 and 25 have the pertinent information.
Lines 40 through 42 of the code are particularly interesting (and I can't take any credit for this code). The HD44780 must be initialized before it can be used. See Figure 24 on page 46 for the sequence of events to initialize the chip. There are timing requirements between steps like "Wait for more than 4.1ms". The PI is slow enough that we don't have to worry about the timing.
The initialization starts (line 40 of the code) with the controller in 8 bit mode, not four bit mode. The first two commands have DB7 - DB4 (data bus pins) set to 0011 which corresponds to hex 3, so the first two hex 3's are sent, one hex 3 at a time in line 40. The next two commands are 0011 and 0010, corresponding to hex 3 and hex 2, which are sent to the controller by line 41. The next line, 0010 specifies 4 bit operation which is hex 2 (8 bit operation would be 0011), while the following line has N = 1 for a two line display and F = 0 for 5x8 dots. Therefore the forth command is 1000, or 8 hex. Code line 42 sends those two commands, hex 2 and hex 8, to the controller. I'll leave the rest of the initialization commands for you to figure out for yourself.
For those unfamiliar with hexadecimal numbers, I plan to devote a post to hex, binary and decimal numbers and bit manipulation.
Line 105,
if __name__ == '__main__'
, was something that I did not understand at first. I saw quite a few questions about it on forums, so I guess I was not the only person who wanted to know. Why do we need this line at all? I need it because I intend to import this base module into a couple of other programs for my next posts. This will save me from copying lines 12 to 102 into the new programs, and you will only see the new code giving a cleaner presentation. So how do you interpret line 105? You can consider
__name__
and __main__
to be python interpreter or internal variables. When you run my base module, LCD_2X16.py, the interpreter assigns '__main__'
to __name__
. Since line l05 evaluates to true, the code following that line will run, outputting the text to the display. However, I have imported LCD_2X16.py into other programs by writing from LCD_2X16 import *
in those programs. If I then run one of those programs, everything from LCD_2X16.py will be part of the calling program. However, when if __name__ == '__main__'
is reached it will compute to false because __name__
now equals 'LCD_2X16'
, not '__main__'
thus the test code will be ignored. Pretty neat! if __name__ == '__main__'
is only written in the base module, not my other programs.
Thanks for doing this, the commenting of this code alone is such a help for noobs like me to start getting around how to do this programming thing.
ReplyDeleteAre you able to advise how I can print to the lcd_string a 'moving' number (eg a clock or voltage / temp reading)as I'm still struggling a bit to learn python syntax, tools and terms.