I2C Display
1602 LCD I2C
I have ordered a 1602 LCD display and I2C <-> 8bit parallel adapter for it. After connecting SCL and SDA lines to my CH341a USB/I2C adapter, I've tried to access from Linux through i2c-dev.
A simple example code writing "TIS" and moving "HELLO!"
//
// Test program for accessing 1602 LCD through i2c-dev + i2c-ch341a-usb in C++
//
// Copyright (c)2020 Tóthpál István, istvan@tothpal.eu
//
// =========================================================================================================
// 1602 LCD 4bit interface PCF8574 adapter
//
// SCL --
// G + G + G SDA --
// N 5 N P P P P 5 N VCC -- P P P P
// D V D 0 1 2 4--7 V D GND -- 0 1 2 4-7
// | | | | | | |||| | | | | | | | | |||| | |
// VSS VDD VO RS RW E D0-D7 A K vss vdd v0 rs rw e D0-D7 A K
// =========================================================================================================
#include <linux/i2c-dev.h>
#include <unistd.h>
// -------------------------------- My classes -----------------------------
#include "include/i2c/pcf8574.h"
#include "include/lcd/lcd1602.h"
int main (void) {
int line2 = 0x40;
volatile int pos = 0;
pcf8574 *wire = new pcf8574( 4, PCF8574_BASE_ADDRESS );
lcd1602 *lcd = new lcd1602( wire );
lcd->init();
lcd->displaycontrol( OPT_DISPLAYON, OPT_CURSORON, OPT_BLINKCURSOR );
lcd->ddramaddress(6);
lcd->data4('T');
lcd->data4('I');
lcd->data4('S');
while (1) {
lcd->ddramaddress(line2+pos-6);
pos++;
if (pos>6) {
lcd->data4(' ');
lcd->ddramaddress(line2+pos-6);
}
else {
lcd->ddramaddress(line2);
}
if (21 < pos) { pos=0; }
if (pos>5) { lcd->data4('H'); }
if (pos>4) { lcd->data4('E'); }
if (pos>3) { lcd->data4('L'); }
if (pos>2) { lcd->data4('L'); }
if (pos>1) { lcd->data4('O'); }
lcd->data4('!');
usleep(100000);
}
delete lcd;
delete wire;
return 0;
}
pcf8574.c
//
// Class file for PCF8574 IC through i2c-dev + i2c-ch341a-usb in C++
//
// Copyright (c)2020 Tóthpál István, istvan@tothpal.eu
//
#include <i2c/smbus.h>
#include <linux/i2c-dev.h>
#include "../include/i2c/pcf8574.h"
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
/*
*********************************************
***********************
* PCF8574 CLASS *
********************************************************************
*/
// --------- Constructor ---------
pcf8574::pcf8574( int line, int address ) {
__s32 opResult = 0; // for error checking of operations
char line_str[20];
i2c_line = line;
i2c_address = address;
sprintf(line_str, "/dev/i2c-%d", line);
myfile = open( line_str, O_RDWR );
opResult = ioctl( myfile, I2C_SLAVE_FORCE, address );
if (opResult < 0) {
printf("%mn", opResult );
exit(EXIT_FAILURE);
}
}
// ---------- Destructor ------------
pcf8574::~pcf8574() {
close( myfile );
}
int pcf8574::get_i2c_line() {
return i2c_line;
}
int pcf8574::get_i2c_address() {
return i2c_address;
}
// send address; get byte (no command/register)
__u8 pcf8574::read() {
return i2c_smbus_read_byte( myfile );
}
// send address; write byte (no command/register)
// p7...p0 => byte
int pcf8574::write( __u8 byte ) {
i2c_smbus_write_byte( myfile, byte );
return 0;
}
lcd1602.c
//
// Class file lcd1602 in C++
//
// Copyright (c)2020 Tóthpál István, istvan@tothpal.eu
//
#include "../include/i2c/pcf8574.h"
#include "../include/lcd/lcd1602.h"
#include <unistd.h>
#define wait usleep(50) // ~40us
/*
********************************************************************
* LCD1602 CLASS *
********************************************************************
*/
// --------- Constructor ---------
lcd1602::lcd1602( pcf8574 *w ) {
wire = w;
}
// ---------- Destructor ------------
lcd1602::~lcd1602() {
}
void lcd1602::send( int rs, __u8 data ) {
if (rs) {
dataout |= RS_PIN;
}
else {
dataout &= ~RS_PIN;
}
wire->write( dataout );
dataout = ( dataout & 0x0f ) | (( data & 0x0f ) << 4);
wire->write( dataout );
//pulse EN
dataout &= ~EN_PIN;
wire->write( dataout );
usleep(3);
dataout |= EN_PIN;
wire->write( dataout );
usleep(5);
dataout &= ~EN_PIN;
wire->write( dataout );
}
// send a 8bit command as 2 4bit data (4bit communication)
void lcd1602::command4( __u8 command ) {
dataout &= ~RS_PIN;
wire->write( dataout );
dataout &= ~EN_PIN;
wire->write( dataout );
__u8 h = command & 0xf0;
__u8 l = command & 0x0f;
send( 0, h>>4 );
send( 0, l );
}
// send 8data as2 4bit
void lcd1602::data4( __u8 data ) {
dataout |= RS_PIN;
wire->write( dataout );
dataout &= ~EN_PIN;
wire->write( dataout );
__u8 h = data & 0xf0;
__u8 l = data & 0x0f;
send( 1, h>>4 );
send( 1, l );
wait;
}
// clear display
void lcd1602::clear( void ) {
command4( CMD_CLEAR );
usleep(5000); // ~1.6ms
}
// function set - 8bit/4bit DATA, number of rows, font style (for 5x11 have to set 1 row)
void lcd1602::functionset( int datasize, int rows, int fontstyle ) {
command4( CMD_FUNCTIONSET | datasize | rows | fontstyle );
wait;
}
// display control - DSP on/off, Cursor on/off, blink cursor
void lcd1602::displaycontrol( int dspon, int cron, int blinkon ) {
command4( CMD_DISPLAYCONTROL | dspon | cron | blinkon );
wait;
}
r
// input set - moving direction, shift
void lcd1602::inputset( int direction, int shift ) {
command4( CMD_INPUTSET | direction | shift );
wait;
}
// set DDRAM address
void lcd1602::ddramaddress( int address ) {
command4( CMD_SETDDRAMADDRESS | (address & 0x7F) );
wait;
}
int lcd1602::init() {
dataout = 0x00 | PW_PIN;
wire->write( dataout ); // RS=0; EN=0; RW=0; D4-7=0
send( 0, 0b0011 ); // 8bit
usleep(5000);
send( 0, 0b0011 ); // 8bit
usleep(150);
send( 0, 0b0011 ); // 8bit
wait;
send( 0, 0b0010 ); // set 4bit
wait;
functionset( OPT_4BITDATALINE, OPT_2ROWS, OPT_5x8FONT ); // set 4bit, 2row, 5x8 font
displaycontrol( OPT_DISPLAYOFF, OPT_CURSOROFF, OPT_NOBLINKCURSOR ); // set display off / cr off / crbl off
clear();
inputset( OPT_INPUTINC, OPT_INPUTNOSHIFT ); // increment - no shift
return 0;
}
Using these codes here is my example application to type on LCD:
Download linux 64bit executable file: 1602LCD_x64.tar.gz