#!/usr/bin/perl # # lcd_i2c.pl # anita2R # Version 1.00, 24th May 2016 # # A script to display data on a SainSmart LCD character display # connected via the i2c bus # # The data to be displayed is passed to the routine as parameter -s # The line number is passed as parameter -l # # If -s is empty or null (""), the initialization code is run # If there is a text string and a line number the text is displayed # on that line and is truncated/padded to fit # If there is no line number, the text is displayed on line 1 and # overflowed onto line 2 - existing text on both lines is overwritten # Other line parameters: # -l 50 turns display off # -l 51 turns display on # use Getopt::Std; use HiPi::Utils; use HiPi::BCM2835::I2C qw( :all ); # use strict; # # get the parameters (-s, -l) getopt('sl:'); # pass parameters to meaningful variables our ( $opt_s, $opt_l ); my $lnParam = $opt_l; my $strParam = $opt_s; # # setup bus number, speed and i2c 'backpack' address my $i2cBus = BB_I2C_PERI_1; # bus #1 (vers.1 used bus #0) my $i2cSpeed = BB_I2C_CLOCK_100_KHZ; # said to be reliable on RPi my $i2cAddr = 0x3f; # SainSmart lcd backpack 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 $width = 16; # Maximum characters per line my $lines = 2; # LCD lines my $dataMode = 0x01; # Mode - send data my $cmdMode = 0x00; # Mode - send command my $line1 = 0x80; # Address for the 1st line my $line2 = 0xC0; # Address for the 2nd line my $blStatus = 0x08; # Backlight mask On=0x08 - Off=0x00 my $loMask = 0xF0; # Masks off low-order bits in byte my $sEN = 0x04; # 0000 0100 - mask to set enable bit my $cEN = 0x0B; # 0000 1011 - mask to 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) # # Test if initialization required # (no text in -s & line not 50 or 51 (display off / on) my $initFlag = 0; if (( $strParam eq "" && $lnParam != 50 && $lnParam != 51 )) { $initFlag = 1; } # # Change line number (if any) to line address / exit if not valid # line = 50 display off, line = 51 display on, 0 = no -l parameter if ( $lnParam != 0 ) { if ( $lnParam == 1 ) { $lnParam = $line1; } elsif ( $lnParam == 2 ) { $lnParam = $line2; } elsif (( $lnParam != 50 && $lnParam != 51 )) { # non valid -l value exit 1; } } # # 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 ); # # # ****************************************** # # ************** Main Program ************** # # if ( $initFlag ) { # init flag = 1, initialise display &init; } elsif ( $lnParam == 50 ) { # line = 50, turn display off &sendByte( 0x08, $cmdMode ); } elsif ( $lnParam == 51 ) { # line = 51, turn display on &sendByte( 0x0C, $cmdMode ); } else { # there is text - print it, depending on line number my ( $data1, $data2 ); if ( $lnParam == 0 ) { # no line specified - print on line 1 & overflow to line 2 if ( length($strParam) > $width ) { # overflow onto line 2 # get substring as long as display width $data1 = substr( $strParam, 0, $width ); # cut at last space so as not to split a word ($data1) = split /\s+(?=\S*+$)/, $data1; # remainder of string on second line $data2 = substr( $strParam, length($data1) + 1 ); } else { # text fits on line 1 $data1 = $strParam; $data2 = ""; } # pad / truncate both lines $data1 = &padTrStr( $data1 ); $data2 = &padTrStr( $data2 ); # print both lines &prntStr( $data1, $line1 ); &prntStr( $data2, $line2 ); } else { # pad / truncate then print on one (specified) line $data1 = &padTrStr( $strParam ); &prntStr( $data1, $lnParam ); } } # exit 0; # # # ****************************************** # # ************** Subroutines *************** # # # *************** Initialize *************** # sub init { # 8-bit write (no LCD data in lower nibble) &writeByte( 0x30, $cmdMode ); # 0011 xxxx Sets 8-bit mode &writeByte( 0x30, $cmdMode ); # repeat in case LCD in 4-bit # mode but out of sync &writeByte( 0x20, $cmdMode ); # 0010 xxxx Sets 4-bit mode # *** now in 4-bit mode *** # both nibbles of data sent to LCD using sendByte &sendByte( 0x28, $cmdMode ); # 0010 1000 2 lines & small chars. &sendByte( 0x0C, $cmdMode ); # 0000 1100 Display On, no cursor &sendByte( 0x01, $cmdMode ); # 0000 0001 Clear display } # # *************** Write Byte *************** # sub writeByte { my $byte = $_[0]; # # writes byte to i2c object (LCD) # Enable 'control port' toggled high-low # # write data with enable set $objI2c->bus_write( $byte | $sEN ); $objI2c->delayMicroseconds($enHold); # clear enable, clear data, keep backlight & mode $objI2c->bus_write( $byte | $cEN ); $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 & backlight status bits into low-order bits # # mask off 4 low-order bits & 'add' mode and backlight my $data_high = (( $data & $loMask ) | $mode | $blStatus ); # shift 4 low bits to high bits, mask-off low order bits # & 'add' mode and backlight my $data_low = ((( $data << 4 ) & $loMask ) | $mode | $blStatus ); # Send both nibbles of data to write routine &writeByte( $data_high ); &writeByte( $data_low ); } # # ************* Print a String ************* # sub prntStr { my $message = $_[0]; my $line = $_[1]; my $i; # # send address for required line to LCD &sendByte( $line, $cmdMode ); # iterate through message string for ( $i = 0 ; $i < $width ; $i++ ) { # send bytes to be displayed (use character values) &sendByte( ord( substr( $message, $i, 1 )), $dataMode ); } } # # ********* Pad/Truncate a String ********** # sub padTrStr { my $string = $_[0]; # truncate $string = substr( $string, 0, $width ); # pad message with spaces - so it fills the line $string = sprintf( "%-${width}s\n", $string ); return ($string); }