RaspberryPi/LCD Display: Difference between revisions
From charlesreid1
| Line 151: | Line 151: | ||
===The Code=== | ===The Code=== | ||
====I2C Library==== | |||
First, an I2C library: | |||
<pre> | <pre> | ||
| Line 187: | Line 191: | ||
def read_block_data(self, cmd): | def read_block_data(self, cmd): | ||
return self.bus.read_block_data(self.addr, cmd) | return self.bus.read_block_data(self.addr, cmd) | ||
</pre> | |||
====Driver==== | |||
Now, the driver. Make sure you set the LCD address correctly. | |||
<pre> | |||
import i2c_lib | |||
from time import * | |||
# LCD Address | |||
ADDRESS = 0x27 / 0x3F | |||
# commands | |||
LCD_CLEARDISPLAY = 0x01 | |||
LCD_RETURNHOME = 0x02 | |||
LCD_ENTRYMODESET = 0x04 | |||
LCD_DISPLAYCONTROL = 0x08 | |||
LCD_CURSORSHIFT = 0x10 | |||
LCD_FUNCTIONSET = 0x20 | |||
LCD_SETCGRAMADDR = 0x40 | |||
LCD_SETDDRAMADDR = 0x80 | |||
# flags for display entry mode | |||
LCD_ENTRYRIGHT = 0x00 | |||
LCD_ENTRYLEFT = 0x02 | |||
LCD_ENTRYSHIFTINCREMENT = 0x01 | |||
LCD_ENTRYSHIFTDECREMENT = 0x00 | |||
# flags for display on/off control | |||
LCD_DISPLAYON = 0x04 | |||
LCD_DISPLAYOFF = 0x00 | |||
LCD_CURSORON = 0x02 | |||
LCD_CURSOROFF = 0x00 | |||
LCD_BLINKON = 0x01 | |||
LCD_BLINKOFF = 0x00 | |||
# flags for display/cursor shift | |||
LCD_DISPLAYMOVE = 0x08 | |||
LCD_CURSORMOVE = 0x00 | |||
LCD_MOVERIGHT = 0x04 | |||
LCD_MOVELEFT = 0x00 | |||
# flags for function set | |||
LCD_8BITMODE = 0x10 | |||
LCD_4BITMODE = 0x00 | |||
LCD_2LINE = 0x08 | |||
LCD_1LINE = 0x00 | |||
LCD_5x10DOTS = 0x04 | |||
LCD_5x8DOTS = 0x00 | |||
# flags for backlight control | |||
LCD_BACKLIGHT = 0x08 | |||
LCD_NOBACKLIGHT = 0x00 | |||
En = 0b00000100 # Enable bit | |||
Rw = 0b00000010 # Read/Write bit | |||
Rs = 0b00000001 # Register select bit | |||
class lcd: | |||
#initializes objects and lcd | |||
def __init__(self): | |||
self.lcd_device = i2c_lib.i2c_device(ADDRESS) | |||
self.lcd_write(0x03) | |||
self.lcd_write(0x03) | |||
self.lcd_write(0x03) | |||
self.lcd_write(0x02) | |||
self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE) | |||
self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON) | |||
self.lcd_write(LCD_CLEARDISPLAY) | |||
self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT) | |||
sleep(0.2) | |||
# clocks EN to latch command | |||
def lcd_strobe(self, data): | |||
self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT) | |||
sleep(.0005) | |||
self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT)) | |||
sleep(.0001) | |||
def lcd_write_four_bits(self, data): | |||
self.lcd_device.write_cmd(data | LCD_BACKLIGHT) | |||
self.lcd_strobe(data) | |||
# write a command to lcd | |||
def lcd_write(self, cmd, mode=0): | |||
self.lcd_write_four_bits(mode | (cmd & 0xF0)) | |||
self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0)) | |||
# put string function | |||
def lcd_display_string(self, string, line): | |||
if line == 1: | |||
self.lcd_write(0x80) | |||
if line == 2: | |||
self.lcd_write(0xC0) | |||
if line == 3: | |||
self.lcd_write(0x94) | |||
if line == 4: | |||
self.lcd_write(0xD4) | |||
for char in string: | |||
self.lcd_write(ord(char), Rs) | |||
# clear lcd and set to home | |||
def lcd_clear(self): | |||
self.lcd_write(LCD_CLEARDISPLAY) | |||
self.lcd_write(LCD_RETURNHOME) | |||
</pre> | </pre> | ||
Revision as of 02:29, 15 June 2016
Also see Arduino LCD Display, which was the original.
Overview
See Arduino LCD Display#Overview for an overview of how the LCD display works. It works by communicating with the microcontroller using I2C pins, which provide a way for integrated circuits to communicate with each other.
The Circuit
Breadboard Diagram
Need to make a firtzing diagram for this.
Breadboard Photo
Here's a photo of the breadboard:
File:RaspberryPi LCD Circuit.jpg
Python Sketch
More information about the Raspberry Pi GPIO pinouts and how to control 'em with Python is over on the RaspberryPi/Blink page.
Specifically, here is some Python code written to control GPIO pins on the Raspberry Pi: RaspberryPi/Blink#The_Python_Code
The Code
This section covers the software needed to drive the LCD display via the GPIO pins on the Raspberry Pi.
Adafruit provides an entire package for interfacing with character LCD displays [1]. Unfortunately, however, these are for LCDs that do not have an integrated circuit onboard, and so have a much greater number of pins. This is an excellent illustration of why we would want to install an integrated circuit to control the LCD, rather than controlling the LCD directly.
Without the IC onboard the LCD, we might need to connect up to 16 pins:
But with the Pi, we have only the two power pins (which can/should be connected to something other than the Pi) and the SDA/SCL pins.
Preparing the Pi
Basic Connection to Pi
Make sure you can log in to the Pi. This consists of the following steps:
- Mount the SD card and edit cmdline.txt, hard-code an IP address in cmdline.txt
- Unmount the SD card and insert it back into the Pi and power it up
- Give the Pi a minute to finish booting up, then connect it using an Ethernet cable to your desktop/computer
- Remotely log in to the Pi via SSH
Installing Pi GPIO Library
Adafruit code for controlling LCDs from the Raspberry Pi: https://github.com/adafruit/Adafruit_Python_CharLCD
Make sure the RPi-GPIO package is installed:
Download the package on your laptop and copy it over to the Pi:
[laptop] $ wget https://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.5.11.tar.gz [laptop] $ scp RPi.*.tar.gz pi@169.254.113.200:~/.
Then you install it manually on the Pi:
[pi] $ cd ~ [pi] $ tar xzf RPi*.tar.gz [pi] $ cd RPi* [pi] $ sudo python setup.py install
This has other prerequisites, all available from Adafruit on GitHub (need to add links):
Adafruit_Python_PureIO Adafruit_Python_GPIO py-spidev
These will need to be installed in a similar way (downloading on an internet-connected computer, and copied over to the py so you can run python setup.py install). Alternatively, you can hook up the Pi to an internet-connected router, which makes it easier to both remotely connect to it and to be able to use tools like pip and apt-get.
Enable SPI
In order to use the GPIO, you need the GPIO library installed above (which provides bindings between Python and the Raspberry Pi kernel, which contains code to control the pins on-board the Pi).
However, in order to use I2C, which is the protocol used to communicate from one integrated circuit to another, you need to enable SPI on the Raspberry Pi.
This page [2] states that SPI is not enabled by default on Raspbian. However, I had Kali for ARM installed, and SPI was enabled by default. If you run the lsmod command, you can check whether the SPI kernel module is enabled. (This stuff is mostly over my head anyway, but you don't have to understand all the details of what's going on to make it work...)
$ lsmod Module Size Used by cfg80211 498967 0 rfkill 22468 1 cfg80211 snd_soc_wm8804 8209 0 snd_soc_pcm512x_i2c 2562 0 regmap_spi 2307 1 snd_soc_wm8804 snd_soc_tas5713 5858 0 snd_soc_pcm512x 16523 1 snd_soc_pcm512x_i2c regmap_i2c 3338 3 snd_soc_wm8804,snd_soc_pcm512x_i2c,snd_soc_tas5713 snd_soc_bcm2708_i2s 7595 0 regmap_mmio 3548 1 snd_soc_bcm2708_i2s snd_soc_core 167442 4 snd_soc_pcm512x,snd_soc_wm8804,snd_soc_tas5713,snd_soc_bcm2708_i2s snd_compress 8840 1 snd_soc_core snd_pcm_dmaengine 5770 1 snd_soc_core snd_pcm 92149 4 snd_soc_pcm512x,snd_soc_wm8804,snd_soc_core,snd_pcm_dmaengine snd_timer 23475 1 snd_pcm spi_bcm2708 6006 0 i2c_bcm2708 6244 0 snd 67406 4 snd_soc_core,snd_timer,snd_pcm,snd_compress fuse 92370 1 ipv6 353829 0
The line we're looking for here relates to SPI:
spi_bcm2708 6006 0
This means we have the SPI kernel module enabled. In case you don't have it enabled, you can find instructions here [3]. Basically, enable the kernel module using modprobe:
$ sudo modprobe spi_bcm2708 $ sudo chown `id -u`.`id -g` /dev/spidev0.*
Hello World
See [4] for Raspberry Pi pinout diagram.
To run a simple hello world with the Sainsmart LCD hooked up to the Pi, it is necessary to send IC2 signals out over pins 3 and 5, SDA/SCL.
http://hardware-libre.fr/2014/03/en-raspberry-pi-using-a-4x20-characters-display/
SMBus
Let's introduce yet another acronym-infused techno-jargony term: SMBus.
To communicate over the I2C ports, you can use the I2C standard protocol, but you can also use SMBus, which is a subset of the I2C protocol. SMBus is common in devices, I suppose, so you want to use it when you can - that way, it'll work if I2C is used, and it'll work if SMBus is used.
Unfortunately, however, before we can use this code, we need to address the fact that the Pi uses 3.3 V logic, but the LCD display uses 5.5 V logic. So we need a logic converter, 3.3V to 5V.
If only we had aptitude
sudo apt-get install python-smbus i2c-tools. Enter "sudo reboot" to restart the pi and now the I2C pins will be available to use.
The Code
I2C Library
First, an I2C library:
import smbus
from time import *
class i2c_device:
def __init__(self, addr, port=1):
self.addr = addr
self.bus = smbus.SMBus(port)
# Write a single command
def write_cmd(self, cmd):
self.bus.write_byte(self.addr, cmd)
sleep(0.0001)
# Write a command and argument
def write_cmd_arg(self, cmd, data):
self.bus.write_byte_data(self.addr, cmd, data)
sleep(0.0001)
# Write a block of data
def write_block_data(self, cmd, data):
self.bus.write_block_data(self.addr, cmd, data)
sleep(0.0001)
# Read a single byte
def read(self):
return self.bus.read_byte(self.addr)
# Read
def read_data(self, cmd):
return self.bus.read_byte_data(self.addr, cmd)
# Read a block of data
def read_block_data(self, cmd):
return self.bus.read_block_data(self.addr, cmd)
Driver
Now, the driver. Make sure you set the LCD address correctly.
import i2c_lib
from time import *
# LCD Address
ADDRESS = 0x27 / 0x3F
# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# flags for function set
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00
# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00
En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit
class lcd:
#initializes objects and lcd
def __init__(self):
self.lcd_device = i2c_lib.i2c_device(ADDRESS)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x03)
self.lcd_write(0x02)
self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
sleep(0.2)
# clocks EN to latch command
def lcd_strobe(self, data):
self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
sleep(.0005)
self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
sleep(.0001)
def lcd_write_four_bits(self, data):
self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
self.lcd_strobe(data)
# write a command to lcd
def lcd_write(self, cmd, mode=0):
self.lcd_write_four_bits(mode | (cmd & 0xF0))
self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))
# put string function
def lcd_display_string(self, string, line):
if line == 1:
self.lcd_write(0x80)
if line == 2:
self.lcd_write(0xC0)
if line == 3:
self.lcd_write(0x94)
if line == 4:
self.lcd_write(0xD4)
for char in string:
self.lcd_write(ord(char), Rs)
# clear lcd and set to home
def lcd_clear(self):
self.lcd_write(LCD_CLEARDISPLAY)
self.lcd_write(LCD_RETURNHOME)
The Circuit
The Result
Flags