Connection diagram for two i2c modules to arduino. Connecting LCM1602 LCD screen with I2C to Arduino. Create your own symbols

In this article I will tell you how to use the I2C interface module to control an LCD display (2x16 / 20x4) with using Arduino. This module allows you to reduce the number of controller pins used; instead of an 8 or 4-bit connection, only 2 pins are required (SDA and SCL).

Technical specifications

Display support: LCD 16×02 / 20×04
Optional: contrast adjustment
Supply voltage. 5V
Interface: I2C
Dimensions: 54mm x 19mm x 15mm

General information about the I2C interface module

Since the number of pins on Arduino controllers is limited and often when used various sensors and they run out of modules, there is a need to save them, for these cases this module was developed, with its help you can implement transmission over two contacts (SDA and SCL).

Now a little about the module itself; it is built on the PCF8574T chip. Resistors R8 (4.7 kOhm) and R9 (4.7 kOhm) are necessary for pulling up the SDA and SCL lines. Ideally, when connecting two or more devices via the I2C bus, you need to use a pull-up on only one device, I’ll write why later. There are three jumpers on the board (the diagram shows that lines A0, A1, A2 are connected to the power supply through resistors R4, R5, R6), they are needed to change the addressing of the device, there are 8 options in total. Changing the addressing gives us the ability to connect up to eight devices via the IC2 bus with the PCF8574T chip; the address options are shown in the figure (the default device address is 0x27). The module is also equipped with potentiometer R11, with its help you can change the contrast of the LCD display.

There are three groups of contacts on the module for connection:

First group:
SCL: Serial CLock
SDA: data line (Serial Dфta)
VCC: "+" power
GND: "-" power supply

Second group:
VSS: "-" power
VDD: "+" power supply
VO: Contrast control pin
RS: Register Select
RW: Read/Write (Write mode when connected to ground)
E: Enable (fall strobe)
DB0-DB3: Interface low bits
DB4-DB7: Interface high bits
A: "+" backlight power supply
K: "-" backlight power

Third group: (jumper set by default)
VCC:
A from LCD:

Connecting to Arduino

Required parts:
Arduino UNO R3 x 1 pc.
LCD display 1602A (2×16, 5V, Blue) x 1 pc.
Interface module I2C, IIC, TWI for LCD x 1 pc.
DuPont wire, 2.54 mm, 20 cm, F-M (Female - Male) x 1 pc.
USB cable 2.0 A-B x 1 pcs.

Connection:
First of all, solder the I2C module to LCD display, then you need to connect the display to the Arduino UNO. To do this, we will use DuPont wiring; we connect according to the table below.

For clarity, I will give another diagram.

For this experiment, you need to download and install the “LiquidCrystal_I2C” library. Then copy and paste this example code into the Arduino IDE program window and load it into the controller.

/* Tested on Arduino IDE 1.6.11 Test date 09/15/2016 */ #include #include LiquidCrystal_I2C lcd(0x27,16,2); // Set the address and size of the display void setup() ( lcd.init(); // Initialize lcd lcd.backlight(); // Turn on the backlight lcd.setCursor(0,0); // Set the cursor to the beginning of 1 line lcd .print("Hello, world"); // Print the text lcd.setCursor(0,1); // Set the cursor to the beginning of line 2 lcd.print("www.robotchip.ru"); // Print the text ) void loop() ( )

Download program

If you did everything correctly, but there are no symbols on the display, try increasing the contrast with the potentiometer.


Links
Download the LiquidCrystal_I2C library
Documentation for the PCF8574T chip
Documentation for LCD1602A

Buy on Aliexpress

I got it here from good store Chip Resistor is another device for study and use in useful devices. This device turned out to be designed to control an LCD display controlled by an HD44780 controller, in 4-bit mode. For this purpose, a microcircuit is installed on the board, which is a converter of the I2C bus into a parallel 8-bit port.

The board is routed in such a way that it can be immediately connected to the LCD display. The input supplies power and I2C lines. The board is immediately equipped with pull-up resistors on the SCL and SDA lines, a potentiometer for adjusting the contrast, and power supply for the display itself.

The jumper on the right turns the backlight on/off. Then, armed with a tester, the following table was compiled. After studying the module, it was revealed that P3 controls the backlight. If the jumper is installed, then 1 turns on the backlight, and 0 turns it off. When the jumper is removed, the backlight is always off. Next, it was decided to supplement the axlib library with functions for working with the I2C bus (software implementation) and functions for controlling the PCF8574 chip. In a nutshell, how the module works. In order to output a byte in parallel, you need to send the address of the microcircuit to the I2C bus (by default it is 0x4E. You can also change the address by soldering jumpers on the board and changing the value of the three least significant bits of the address), then after receiving the ACK, a data byte is sent. After the chip responds with an ACK, the byte appears on the parallel port of the chip. To control the LCD display, I took functions from the axlib library and slightly modified them to work with the I2C bus. #include #include #include #include #define ADD 0x4E // Chip address /* LCD Chip RS P0 RW P1 EN P2 D4 P4 D5 P5 D6 P6 D7 P7 There is a backlight on the P3 connection leg. 1 on, 0 off */ // Output data com |= 0x04; // E per unit pcf8574_byte_out(com, ADD); // Output data com &= 0xFB; // E to zero pcf8574_byte_out(com, ADD); // Output data) void init(void) ( _delay_ms(30); com(0x30); _delay_us(40); com(0x30); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x30); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x20); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x20); // Setting parameters com(0x80); // Setting parameters com(0x00); // Turn off the display com(0x80); // Turn off the display com(0x00); // Clear the display com(0x10); // Clear the display com(0x00); com(0x60); // Set the data input mode com(0x00); com(0xC0); // Turn on the display with the selected cursor) void char_out(BYTE data) ( BYTE data_h = ((data & 0xF0) + 0x09); BYTE data_l = ((data // Transfer the most significant 4 bits of data_h |= 0x04; pcf8574_byte_out(data_h, ADD); // Transfer the most significant 4 bits // Transfer the most significant 4 bits // Transfer low 4 bits // Transfer low 4 bits // Transfer low 4 bits) void str_out(BYTE *str) ( while((*str) != "\0") ( char_out(*str); str++; ) ) int main(void) ( init(); str_out("ЁPҐBET MҐP!" ); while(1) ( ) ) Exactly what's going on here. First we connect the libraries for I2C and for PCF8574. I’ve already written about I2C, so I’ll go on and on about it again, but I’ll tell you what’s in PCF8574.h. The library includes only three functions.
BYTE pcf8574_test(BYTE add) ( BYTE ask = ACK; add &= 0xFE; i2c_start(); ask = i2c_send_byte(add); i2c_stop(); return ask; ) The first function was written to check if a device is on the bus. In principle, it can be used to search for any device located on the bus. The function takes the address of the desired device and if it responds, it returns zero. If a device with such an address is not on the bus, it will return one.
BYTE pcf8574_byte_out(BYTE data, BYTE add) ( BYTE ask = ACK; add &= 0xFE; i2c_start(); ask = i2c_send_byte(add); if(!ask) ask = i2c_send_byte(data); i2c_stop(); return ask; ) This function is already tailored purely for this chip. As arguments, it is given a byte to be transferred to the bus and the address of the chip. The function will first query the chip by address and then send the byte. If the chip received a byte and responded with ACK, then the function will finish working with the chip and return zero as a successful send of the byte. And at this time the microcircuit will output this byte to its parallel port. Otherwise, we will receive NACK and return one, the transmission failed.
BYTE pcf8574_str_out(BYTE *data, BYTE col, BYTE add) ( BYTE ask = ACK; add &= 0xFE; i2c_start(); ask = i2c_send_byte(add); for(BYTE i=0; i This function is created for experimentation. Accepts a pointer to an array of one-byte data, the number of these bytes and the address of the chip. Actually, an attempt to transfer all data in one session, and not in one byte per session. The function works, but it is not suitable for the LCD display. Now let's get back to the main program. After connecting the libraries, we write the address of the chip. Next, we create three functions similar to lcd.h. The only difference is in the principle of data transfer.
void com(BYTE com) ( com |= 0x08; // P3 to one so that the backlight is on pcf8574_byte_out(com, ADD); // Output data com |= 0x04; // E per unit pcf8574_byte_out(com, ADD); // Output data com &= 0xFB; // E to zero pcf8574_byte_out(com, ADD); // Output data } This function only sends commands to the display. This is where the first line appeared with the logical addition of the command from 0x08. This bug is needed due to the fact that we transmit the byte not directly to the port of the LCD display, but through our repeater. That is, if we have supplied a byte, and then we need to output only one bit, then please assign the required bit to the previous byte and send it again to the port. This is such a hassle. Addition with 0x08 is necessary to constantly keep one in the third digit. Remember the backlight? It is this addition that turns on the backlight. Then we call the function of transferring a byte to the bus. It is written about above. Then we transfer the byte via the bus to the chip. Next, you should set E to one, which is what the logical addition of the byte with 0x04 actually does. After resetting E. Thus, you can send any command to the display only by passing the command itself as an argument. void init(void) ( _delay_ms(30); // Pause after power on com(0x30); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x30); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x30); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x20); // Switch to 4-bit mode _delay_us(40); // Delay for command execution com(0x20); // Setting parameters com(0x80); // Setting parameters com(0x00); // Turn off the display com(0x80); // Turn off the display com(0x00); // Clear the display com(0x10); // Clear the display com(0x00); // Set the data input mode com(0x60); // Set the data input mode com(0x00); // Turn on the display with the selected cursor com(0xC0); // Turn on the display with the selected cursor } This function only initializes the display. The sequence of commands is taken from the datasheet on the LCD display. void char_out(BYTE data) ( BYTE data_h = ((data & 0xF0) + 0x09); BYTE data_l = ((data // Transfer the most significant 4 bits of data_h |= 0x04; pcf8574_byte_out(data_h, ADD); // Transfer the most significant 4 bits data_h &= 0xF9; // Transfer the most significant 4 bits pcf8574_byte_out(data_h, ADD); // Transfer low 4 bits pcf8574_byte_out(data_l, ADD); // Transfer low 4 bits data_l |= 0x04; // Transfer low 4 bits } pcf8574_byte_out(data_l, ADD); data_l &= 0xF9; pcf8574_byte_out(data_l, ADD);

This function transmits data to the LCD display. It is executed in the same way as commands, except that the byte is transmitted first with the most significant nibble, and then with the low byte. And the rest is the same.

void str_out(BYTE *str) ( while((*str) != "\0") ( char_out(*str); str++; ) )

Well, this function is purely for passing a string to the display. Actually, it has nothing to do with our topic.

Project for AtmelStudio 6.2 

Competent 01.08.15 17:11

The comma is missing. That's right: "HELLO WORLD!" And this device is designed not only for the HD44780. Pull-up resistors are installed on the master side. According to the specification, data is written to the LCD controller in the falling direction E. Hence the first function is simplified: void com(BYTE com) ( com |= 0x08; // backlight pcf8574_byte_out(com | 0x04, ADD); // Data output pcf8574_byte_out(com , ADD); // E to zero) And the rest can also be significantly less. For example, void char_out(BYTE data) will take only two calls, and even more so without additional variables. LCD initialization was performed in violation of timing specifications.

Alexey 02.08.15 19:11

Due to the missing comma, the display will not be affected. This device is specifically designed for displays with this or a similar controller. But this microcircuit is a really simple port expander. I agree about E. Additional variables are needed. If you pass an argument to a function and perform certain actions with logic, glitches may occur. I've already encountered this. Initialization is performed without violation of timings. The documentation says that there is a 40 µs pause between commands. Due to the fact that the transfer takes place over the i2c bus, which in turn is software-based and slow, the periods are completed with interest. If you are still not too lazy, then write your version and send it to me. I'll publish it. In the end, this site is intended for an amateur audience and anyone who wants can express their opinion and vision on the life of MK.

Alexey 06.08.15 09:14

Added timings when initializing the display as noted by the respected “Literate”

Dmitry 06/14/16 21:57

ruslan 21.12.16 19:54
Alexey 12/21/16 21:53

Oh yeah. Especially the code in ASMA. Arduino experts will appreciate it to the fullest)))

Py.sy.
Even if you don’t look at ASM, the program there is written for a PIC controller. Is this “very” useful information for AVR specialists? especially for beginners))) I have nothing against PIC, but even the ASM of PIC and AVR is different. As for the details of the operation of the LCD display, you can take a look))) True, I also wrote it under CVAVR, but all the commands are disassembled and sorted into shelves. But in any case, decide for yourself where it is written more clearly))) The author writes, the reader chooses.

GeK 01/04/17 12:52

"I2C address of the chip (by default it is 0x4E"

The most significant 4 bits of the address are fixed,
The prefix for PCF8574 is 0100, and for PCF8574A it is 0111
The lower 3 bits depend on the state of the chip's inputs A2-A0. By default, all 3 jumpers are open, respectively, the chip address takes the value 0111111.
// A2 A1 A0 PCF8574 PCF8574A
// 1 1 1 0x20 0x38
// 1 1 0 0x21 0x39
// 1 0 1 0x22 0x3A
// 1 0 0 0x23 0x3B
// 0 1 1 0x24 0x3C
// 0 1 0 0x25 0x3D
// 0 0 1 0x26 0x3E
// 0 0 0 0x27 0x3F

Alexey 01/04/17 14:27

You got something mixed up.
Extract from the documentation for the microcircuit

0b01001110 is 0x4E
So everything is correct here. And if you need to change the address, then you just need to change it in Define.

Yuri 12/14/17 21:26

Good day! And you can also use the lcdgotoxy and lcdclear function code to work with the adapter for PCF8574.

Alexander 05.20.18 18:14

Good day! How do you output Russian text?

Alexey 05.20.18 23:04

This is a domestic display from MELT. The Cyrillic alphabet is hardwired into his memory.

Alexander 05/21/18 04:55

Good day! I write as you do in the Project for AtmelStudio 6.2 "ЁPҐBET MҐP!" it outputs normally
and if you write "HELLO WORLD!" brings out all sorts of nonsense. I have two
One of the display options has Cyrillic alphabet. the second is Chinese.

Alexey 05/21/18 09:22

I would first write a test program. Iterates through the entire memory and displays symbols and their addresses. And then figure out what the problem is. Most likely the character table does not match the ascii table.

Andrey 09/03/18 08:32

Good afternoon!

Can you provide a circuit diagram for Proteus?

Andrey 09/03/18 10:22

Or has no one checked at Proteuse?

Andrey 09/03/18 10:56

Figured out main_init

Pavel 05/30/19 23:35

A curious thing, the display address is 0x4E, and if the same display is connected to the Arduino, then the address is 0x27

Pavel 05/31/19 11:04

Thank you very much for your work! I scoured the entire Internet, and none of the examples given except yours worked. The only thing is that in the project archive the _delay_ delays are not specified in the display initialization function, and accordingly it does not work

Alexey 06/01/19 09:52

Well, this is more of a demonstration project. For good reason, the axlib library needs to be rewritten, but given the fact that STM32 and STM8 are moving by leaps and bounds, there is no point in AVR at all.

Pavel 06/05/19 12:57

STM does not have DIP packages; it is more difficult to make printed circuit boards. For my projects, the AVR’s capabilities are plenty, you can fit a lot on one Atmega 8

Alexey 06/05/19 15:20

Yes, but how much does Atmega8 and stm8s003 cost)))

Dmitry 06/07/19 00:41

Hello, Alexey.
Please tell me how to read the port status from pcf8574?
I want to make an external unit, 8 GPIO on the i2c bus - that’s it.

Dmitry 06/07/19 17:56

I'll answer myself
The function returns a byte - the state of the microcircuit ports
uint8_t pcf8574_byte_rcv(uint8_t addr)
{
uint8_t ask = ACK;
addr |= 0b01; //READ
uint8_t data=0;
i2c_start();
ask = i2c_send_byte(addr);
if(!ask) data = i2c_read_byte(NACK);
i2c_stop();

Return data;
}

Pavel 06/07/19 20:37

How much does it cost, 150 rubles, for the price of a relay in general), and how do you wire boards for STM? LUT is unreliable, the CNC router is not sure what it will take (haven’t tried it)

Perhaps one of the most popular screens on the market. Built on the popular HD44780U controller. From the name of the model it follows that the screen consists of two lines of 16 characters. There is no support for the Russian language in this particular model.

The sh2s data bus allows you to connect up to 127 devices via two wires, simultaneously. This I2C is implemented on the PCF8574T chip.

Connection diagram:

The blue thing is a variable resistance that allows you to adjust the screen contrast.

The jumper on the left is responsible for the screen backlight.

A block of 4 contacts is connected to arduino like this:

GND - GND
VCC - 5V
SDA - A4 (if Arduino MEGA, then to D20)
SCL - A5 (if Arduino MEGA, then to D21)

Library

Sketch

The display may have a different IIC address, instead of 0x27 it may be 0x3F. To accurately determine the address, you can use an i2c device scanner.

#include #include //set the address of the LCD screen 0x27, 16 characters, 2 lines LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() ( lcd.init(); // Initialize the screen //turn on the backlight lcd.backlight(); //Set the position starting from which the text is displayed. lcd.setCursor(2, 0); //output line 1 lcd.print("Hello, World!"); //print the second line in the same way lcd.setCursor(1, 1);

Perhaps one of the most popular screens on the market. Built on the popular HD44780U controller. From the name of the model it follows that the screen consists of two lines of 16 characters. There is no support for the Russian language in this particular model.

The sh2s data bus allows you to connect up to 127 devices via two wires, simultaneously. This I2C is implemented on the PCF8574T chip.

Connection diagram:

The blue thing is a variable resistance that allows you to adjust the screen contrast.

The jumper on the left is responsible for the screen backlight.

A block of 4 contacts is connected to arduino like this:

GND - GND
VCC - 5V
SDA - A4 (if Arduino MEGA, then to D20)
SCL - A5 (if Arduino MEGA, then to D21)

Library

Sketch

The display may have a different IIC address, instead of 0x27 it may be 0x3F. To accurately determine the address, you can use an i2c device scanner.

#include #include //set the address of the LCD screen 0x27, 16 characters, 2 lines LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() ( lcd.init(); // Initialize the screen //turn on the backlight lcd.backlight(); //Set the position starting from which the text is displayed. lcd.setCursor(2, 0); //output line 1 lcd.print("Hello, World!"); //print the second line in the same way lcd.setCursor(1, 1);

  • lcd.print("www.site"); ) void loop () ( )
  • The FC-113 module is based on the PCF8574T chip, which is an 8-bit shift register - an input-output “expander” for the I2C serial bus. In the figure, the microcircuit is designated DD1.
  • R1 is a trim resistor for adjusting the contrast of the LCD display.
  • Jumper J1 is used to turn on the display backlight.
  • Pins 1…16 are used to connect the module to the LCD display pins. Contact pads A1...A3 are needed to change the I2C address of the device. By soldering the appropriate jumpers, you can change the device address. The table shows the correspondence of addresses and jumpers: “0” corresponds to an open circuit, “1” to an installed jumper. By default, all 3 jumpers are open and the device address.

2 0x27 Connection diagram for LCD display to Arduino

via I2C protocol


3 The module is connected to Arduino in a standard way for the I2C bus: the SDA pin of the module is connected to analog port A4, the SCL pin is connected to analog port A5 of Arduino. The module is powered by +5 V from Arduino. The module itself is connected by pins 1…16 to the corresponding pins 1…16 on the LCD display. Connection diagram for LCD display to Arduino

Library for work

Now we need a library to work with LCD via the I2C interface. You can use, for example, this one (link in the line "Download Sample code and library"). Downloaded archive LiquidCrystal_I2Cv1-1.rar unzip to a folder\libraries\

, which is located in the Arduino IDE directory.

The library supports a set of standard functions for LCD screens:Function
Purpose LiquidCrystal()
creates a LiquidCrystal type variable and accepts display connection parameters (pin numbers); begin()
initializing the LCD display, setting parameters (number of lines and characters); clear()
clearing the screen and returning the cursor to the starting position; home()
return the cursor to the starting position; setCursor()
setting the cursor to a given position; write()
displays the symbol on the LCD screen; print()
displays text on the LCD screen; cursor()
shows the cursor, i.e. underlining under the place of the next character; noCursor()
hides the cursor; blink()
cursor blinking; noBlink()
Cancel flashing; noDisplay()
turning off the display while saving all displayed information; display()
turning on the display while saving all displayed information; scrollDisplayLeft()
scroll the display contents 1 position to the left; scrollDisplayRight()
scroll the display contents 1 position to the right; autoscroll()
enable autoscroll; noAutoscroll()
disable autoscroll; sets the text direction from left to right;
rightToLeft() text direction from right to left;
createChar() creates a custom character for the LCD screen.

4 Sketch for text output to LCD screen via I2C bus

Let's open the sample: File Samples LiquidCrystal_I2C CustomChars and we'll rework it a little. We will display a message at the end of which there will be a blinking symbol. The comments to the code comment on all the nuances of the sketch.

#include // include the Wire library #include // connect the LCD library #define printByte(args) write(args); // uint8_t heart = (0x0,0xa,0x1f,0x1f,0xe,0x4,0x0); // bit mask of the “heart” symbol LiquidCrystal_I2C lcd(0x27, 16, 2); // Set address 0x27 for 16x2 LCD display void setup() ( lcd.init(); // initializing the LCD display lcd.backlight(); // turn on the display backlight lcd.createChar(3, heart); // create a “heart” symbol in memory cell 3 lcd.home(); // place the cursor in the upper left corner, at position (0,0) lcd.!"); // print a line of text lcd.setCursor(0, 1); // move the cursor to line 2, character 1 lcd.print( " i "); // print the message on line 2 lcd.printByte(3); // print the “heart” symbol located in the 3rd cell lcd.print(" Arduino "); } void loop() (// flashing the last character lcd.setCursor(13, 1); // move the cursor to line 2, character 1 lcd.print("\t"); }

delay(500); lcd.setCursor(13, 1); // move the cursor to line 2, character 1 lcd.print(" "); delay(500);

5 By the way, the characters written by the command lcd.createChar();

, remain in the display memory even after turning off the power, because written to display ROM 1602. Create your own symbols for LCD display {00000, 01010, 11111, 11111, 01110, 00100, 00000} Let's take a closer look at the issue of creating your own symbols for LCD screens. Each character on the screen consists of 35 dots: 5 wide and 7 high (+1 reserve line for underlining). In line 6 of the above sketch we define an array of 7 numbers:

6 (0x0, 0xa, 0x1f, 0x1f, 0xe, 0x4, 0x0). Let's convert hexadecimal numbers to binary:

. These numbers are nothing more than bit masks for each of the 7 lines of the symbol, where "0" denotes a light point and "1" a dark point. For example, a heart symbol specified as a bitmask will appear on the screen as shown in the figure.


7 LCD screen control via I2C bus

As a bonus, let's look at the timing diagram for displaying the Latin characters "A", "B" and "C" on the LCD display. These characters are stored in the display ROM and are displayed on the screen simply by transmitting their addresses to the display. The diagram is taken from the RS, RW, E, D4, D5, D6 and D7 pins of the display, i.e. already after the FC-113 “I2C parallel bus” converter. We can say that we are diving a little deeper into the hardware.


Timing diagram of the output of Latin characters “A”, “B” and “C” on the LCD display 1602

The diagram shows that the characters that are in the display ROM (see p. 11 of the datasheet, link below) are transmitted in two nibbles, the first of which determines the table column number, and the second - the row number. In this case, the data is “latched” at the edge of the signal on the line E(Enable), and the line R.S.(Register select) is in a logical one state, which means data is being transferred. A low state on the RS line means instructions are being sent, which is what we see before each character is transmitted. In this case, the instruction code for carriage return to position (0, 0) of the LCD display is transmitted, which can also be found out by studying the technical description of the display.

And one more example. This timing diagram shows the output of the Heart symbol on the LCD display.


Again, the first two impulses Enable comply with instructions Home()(0000 0010 2) - return the carriage to position (0; 0), and the second two - output to the LCD display stored in memory cell 3 10 (0000 0011 2) the “Heart” symbol (instruction lcd.createChar(3, heart); sketch).