#!/usr/bin/perl # # lcd_i2c_cg.pl # Version 1.00 # June 2016 # anita2R # # A script to create custom characters for # an lcd character display # Eight 5x8 pixel characters are possible # # Call this script with two parameters: # --charAddr # --charStr # The address is a single value in the range 0 to 7 # The character string is 8 hex bytes (comma delimited) # Example # for a lower case 'g' that descends into the cursor row: # sudo ./lcd_i2c_cg.pl --charAddr 0 --charStr 00,00,0E,11,11,0F,01,0E # use HiPi::BCM2835::I2C qw( :all ); use HiPi::Utils; use Getopt::Long; # use strict; # # get the command line parameters my ($cstrParam, $caddrParam); GetOptions( 'charStr=s' => \$cstrParam, 'charAddr=i' => \$caddrParam ); # #setup bus number, speed and i2c 'backpack' address. my $i2cBus = BB_I2C_PERI_1; # bus #1 on vers 2. my $i2cSpeed = BB_I2C_CLOCK_100_KHZ; # fastest reliable on RPi my $i2cAddr = 0x3f; # SainSmart i2c lcd at 0x3f # setup regular user & group id's - for permission drop-back my $user = ''; my $group = ''; # setup values specific to the 2 row 16 character SainSmart i2c LCD my $dWidth = 16; # Displayed characters per line my $mWidth = 40; # Memory size per line my $dataMode = 0x09; # Mode - send data with backlight on my $cmdbMode = 0x08; # Mode - send command with backlight on my $line1 = 0x80; # Address command for the 1st line my $line2 = 0xC0; # Address command for the 2nd line my $loMask = 0xF0; # Masks off low-order bits in byte my $setEn = 0x04; # 0000 0100 mask: set enable bit my $clrEn = 0x0B; # 0000 1011 mask: clear enable bit/data # hold/wait times are in the code but do not seem to be required # perhaps the delay inherent in 12c serial # to parallel conversion is sufficient my $enHold = 0; # hold enable high (microseconds) my $wait = 0; # wait before next write (microseconds) # ****************************************** # # ***************** Setup ****************** # # # create an i2c device object my $objI2c = HiPi::BCM2835::I2C->new( peripheral => $i2cBus, address => $i2cAddr ); HiPi::Utils::drop_permissions_name( $user, $group ); $objI2c->set_baudrate( $i2cSpeed ); # # Initialize &init; # # ****************************************** # # ************** Main Program ************** # # # If a character byte string is present create the character # else just display the custom characters if( $cstrParam ) { # Set character generator RAM address to the # start of required custom character (0x00 to 0x07) &sendByte( &cgConv( $caddrParam ), $cmdbMode ); # Read 8 bytes from character string parameter # and write to CGRAM my @newChar = split( /,/, $cstrParam ); foreach( @newChar ) { &sendByte( hex $_, $dataMode ); } } # On line 1, display the 8 possible custom characters &sendByte( $line1, $cmdbMode ); for( my $n = 0; $n < 8; $n++ ) { &sendByte( $n, $dataMode ); &sendByte( 0x20, $dataMode ); } exit 0; # # # ****************************************** # # ************** Subroutines *************** # # # *************** Initialize *************** # sub init { # 8-bit write (Control bits not LCD data in lower nibble) &writeByte ( 0x38 ); # 0011 xxxx Sets 8-bit mode &writeByte ( 0x38 ); # repeat in case LCD was in # 4-bit mode/out of sync &writeByte ( 0x38 ); # & 4-bit mode needs # next byte to action cmd. &writeByte ( 0x28 ); # 0010 xxxx Sets 4-bit mode # now in 4-bit mode - both nibbles of data sent to LCD &sendByte ( 0x28, $cmdbMode ); # 0010 1000 - 2 lines, small chars &sendByte ( 0x0C, $cmdbMode ); # 0000 1100 Display On, no cursor &sendByte ( 0x01, $cmdbMode ); # 0000 0001 Clear display } # # *************** Write Byte *************** # sub writeByte { my $byte = $_[0]; # # writes byte to i2c object (LCD) # 'Enable' toggled high-low to latch byte # # write data with enable high $objI2c->bus_write( $byte | $setEn ); $objI2c->delayMicroseconds( $enHold ); # clear enable - keep backlight and mode bits $objI2c->bus_write( $byte & $clrEn ); $objI2c->delayMicroseconds( $wait ); } # # **************** Send byte *************** # sub sendByte { my $data = $_[0]; my $mode = $_[1]; # # splits data into high & low-order # puts each nibble into high-order bits # then adds mode bits into low-order bits # mode can be with or without backlight (cmdbMode/cmdxMode) # or dataMode (always with backlight) # # mask off 4 low-order bits & 'add' mode my $data_high = (( $data & $loMask ) | $mode ); # shift 4 low bits to high bits, # mask-off low order bits & 'add' mode my $data_low = ((( $data << 4 ) & $loMask ) | $mode ); # Send both nibbles of data to write routine &writeByte( $data_high ); &writeByte( $data_low ); } # # ***** CG Character Number to Command ***** # sub cgConv { my $cgAdd = $_[0]; # convert character number to address command value # bit 6 high for CG RAM addresses # Character data starts every 8 bytes $cgAdd = ( ($cgAdd *8) | 0x40); #0x40 = 0100 0000 return( $cgAdd ); } # ****************************************** #