Part 4: Omega2 - I2C Example

Using the I2C library to control 2x16 LCD Module, and other things

The Onion2 Maker Kit came with a backlit 2x16 LCD module with I2C interface.







The I2C LCD Example is completely documented here. I will simply summarize the steps required to get this up and running.

The LCD module requires four connections to the Expansion Dock. You will note that this reduces the number of GPIO pins as compared to the normal parallel interface to the Hitachi HD44780 LCD Controller.

GND
Vcc
SDA
SCL

Getting Started

From the Linux Terminal, enter:
opkg update
opkg install python-light pyOnionI2C


Note that you only have to do this once on your workstation PC.
Next, you will create a Python library file called lcdDriver.py with the following code:

Code:
# lcdDriver.py
from OmegaExpansion import onionI2C
import time

# sleep durations
writeSleep = 0.0001 # 100 us
initSleep = 0.2

## LCD Display commands
# 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

LCD_LINE1 = 0x80
LCD_LINE2 = 0xC0
LCD_LINE3 = 0x94
LCD_LINE4 = 0xD4

# 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,address, port=0):
    # i2c device parameters
    self.address = address
    self.i2c = onionI2C.OnionI2C(port)

    # lcd defaults
    self.lcdbacklight = LCD_BACKLIGHT #default status
    self.line1= "";
    self.line2= "";
    self.line3= "";
    self.line4= "";

    self.lcdWrite(0x03)
    self.lcdWrite(0x03)
    self.lcdWrite(0x03)
    self.lcdWrite(0x02)

    self.lcdWrite(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
    self.lcdWrite(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
    self.lcdWrite(LCD_CLEARDISPLAY)
    self.lcdWrite(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
    time.sleep(initSleep)

# function to write byte to the screen via I2C
def writeBytesToLcd(self, cmd):
  self.i2c.write(self.address, [cmd])
  time.sleep(writeSleep)

# creates an EN pulse (using I2C) to latch previously sent command
def lcdStrobe(self, data):
  self.writeBytesToLcd(data | En | self.lcdbacklight)
  time.sleep(writeSleep)
  self.writeBytesToLcd(((data & ~ En) | self.lcdbacklight))
  time.sleep(writeSleep)

def lcdWriteFourBits(self, data):
  # write four data bits along with backlight state to the screen
  self.writeBytesToLcd(data | self.lcdbacklight)
  # perform strobe to latch the data we just sent
  self.lcdStrobe(data)

# function to write an 8-bit command to lcd
def lcdWrite(self, cmd, mode=0):
  # due to how the I2C backpack expects data, we need to send the top four and bottom four bits of the command separately
  self.lcdWriteFourBits(mode | (cmd & 0xF0))
  self.lcdWriteFourBits(mode | ((cmd << 4) & 0xF0))

# function to display a string on the screen
def lcdDisplayString(self, string, line):
  if line == 1:
    self.line1 = string;
    self.lcdWrite(LCD_LINE1)
  if line == 2:
    self.line2 = string;
    self.lcdWrite(LCD_LINE2)
  if line == 3:
    self.line3 = string;
    self.lcdWrite(LCD_LINE3)
  if line == 4:
    self.line4 = string;
    self.lcdWrite(LCD_LINE4)

  for char in string:
    self.lcdWrite(ord(char), Rs)

# function to display multiple lines on the screen
def lcdDisplayStringList(self, strings):
  for x in range(0, min(len(strings), 4)):
    self.lcdDisplayString(strings[x], x+1)

# clear lcd and set to home
def lcdClear(self):
  self.lcdWrite(LCD_CLEARDISPLAY)
  self.lcdWrite(LCD_RETURNHOME)

# write the current lines to the screen
def refresh(self):
  self.lcdDisplayString(self.line1,1)
  self.lcdDisplayString(self.line2,2)
  self.lcdDisplayString(self.line3,3)
  self.lcdDisplayString(self.line4,4)

# turn on the backlight
def backlightOn(self):
  self.lcdbacklight = LCD_BACKLIGHT
  self.refresh()

# turn off the backlight
def backlightOff(self):
  self.lcdbacklight = LCD_NOBACKLIGHT
  self.refresh()

With this file saved in your /root directory, you are now ready to create your test program.

Code:
# I2C LCD test program

# software delay function
def delay(d):
  while d > 0:
    d -= 1

import onionGpio
import lcdDriver

# initialize LCD
lcdAddress = 0x3F
lcd = lcdDriver.Lcd(lcdAddress)
lcd.backlightOn()

gpio0 = onioGpio.OnionGpio(0)
gpio0.setOutputDirection(0)
value = 0
n = 0
lcd.lcdDisplayString("Hello",1)   # display on line 1
lcd.lcdDisplayString("World",2)   # display on line 2
delay(1000000)

while 1:
  gpio0.setValue(value)
  lcd.lcdDisplayStringList(["Hello AAC", "n = " + str(n)])
  value = 1 - value
  n += 1
  delay(200000)

# end of main loop


Time Functions

You may notice in the code above a software delay function was created. This can also be accomplished using the time module as shown in the modified code below:
Code:
# I2C LCD test program using time

import onionGpio
import lcdDriver
import time

# initialize LCD
lcdAddress = 0x3F
lcd = lcdDriver.Lcd(lcdAddress)
lcd.backlightOn()

gpio0 = onioGpio.OnionGpio(0)
gpio0.setOutputDirection(0)
value = 0
n = 0
lcd.lcdDisplayString("Hello",1)   # display on line 1
lcd.lcdDisplayString("World",2)   # display on line 2
time.sleep(5)   # wait for 5 seconds

while 1:
  gpio0.setValue(value)
  lcd.lcdDisplayStringList(["Hello AAC", "n = " + str(n)])
  value = 1 - value
  n += 1
  time.sleep(1)   # delay for 1 second

# end of main loop


GPIO

One of the first software tests on a new MCU is to measure how fast one can toggle a GPIO pin. For this, we can run this simple code:
Code:
# GPIO toggle test
import onionGpio

gpio0 = onioGpio.OnionGpio(0)
gpio0.setOutputDirection(0)

while 1:
  gpio0.setValue(1)
  gpio0.setValue(0)

# end of while loop
I measured this with an oscilloscope to be period of about 5ms, i.e. a repetition rate of 200Hz. This is surprisingly and disappointingly slow.
There are two reasons for this slow speed.
  1. Python is an interpreted language, i.e., program script is not compiled into machine code.
  2. The GPIO functions are implemented as file transfer functions.

GPIO Side-track using time module

Let us briefly revisit our basic GPIO program. Here we add the time module in order to implement delays within the code. This program has been modified to flash the LED on GPIO0 once every second.
Code:
# GPIO toggle test
import onionGpio
import time

DELAY_500ms = 0.5
gpio0 = onioGpio.OnionGpio(0)
gpio0.setOutputDirection(0)

while 1:
  gpio0.setValue(1)
  time.sleep(DELAY_500ms)
  gpio0.setValue(0)
  time.sleep(DELAY_500ms)
# end of while loop

FAST GPIO

A workable solution for fast GPIO is yet to come.
For the time being, you can execute the following commands from the Linux shell (command line):

fast-gpio set 0 1
fast-gpio set 0 0


... more to come, sometime later, maybe much later!!!
... if you have some ideas that you would like to see implemented, simply leave a comment below.

Blog entry information

Author
MrChips
Views
1,580
Last update

More entries in General

More entries from MrChips

Share this entry

Top