C51 microcontroller and LCD1602

Table of Contents

1. Pin interface description diagram

2. Use of modules

1. First we initialize the module

Define busy detection function

Module initialization

2.Transmission of data

3. Determine the character display position

4. Integration of code

In this article we use LCD1602 to display a line of characters

1. Pin interface description diagram

No.
1
Feet
: VSS
for power ground
Connect to GND

No.
2
Feet
:VDD
catch
5V
positive power supply
Connect to 5V

No.
3
Feet
:VL
Contrast adjustment terminal for LCD monitor
,
The contrast is the weakest when connected to the positive power supply, and the contrast is highest when connected to the ground.

If it is too high, “ghosting” will occur.

, when using it, you can pass a
10K
The potentiometer adjusts the contrast.

No.
4
Feet
:
RS
For register selection, the data register is selected when the high level is high, and the instruction register is selected when the low level is low.

No.
5
Feet
:
R/W
It is a read and write signal line. When the level is high, the reading operation is performed, and when the level is low, the writing operation is performed.

When RS and
R/W
common to low level can
write command
or display address

When RS is low level R/W
can be high when
Read busy signal
,

When RS is high level
R/W
is low when the
data input
.

No.
6
Feet
:
E
terminal is the enabling terminal, when
E
When the terminal jumps from high level to low level, the liquid crystal module
Excuting an order
.

No.
7

14
Feet
:
D0
~
D7
for
8
bit bidirectional data lines.

No.
15
Pin: Backlight positive terminal.
Connect to 5V

No.
16
Pin: Negative pole of backlight.
Connect to GND

2. Use of modules

We want LCD1602 to display characters. In fact, we need to initialize LCD1602 first, and then tell it where to display characters (write address command) and what content to display (write data);

1. First we initialize the module

According to the module manual, we can understand the steps of module initialization, but we can see that 4. We are required to detect the busy signal every time we write instructions, read and write data operations.

So we need to detect the busy signal first, so how to detect the busy signal?

We can see that there is a sentence in the description of the pin interface above:
When RS is low level R/W
can be high when
Read busy signal

Then follow the LCD1602 module control instruction list

From instruction 9 we can see that we only need to detect the high and low bits of BF.

LCD1602 read timing diagram

Define busy detection function

We define a function that detects busy signals based on the read timing diagram.

void Detection_busy()//Busy detection
{ //Let the data be busy from the beginning
char count = 0x80;
date_byte = 0x80;
while(count & 0x80)
{
RS = 0;
RW = 1;
E = 0;
_nop_();//A delay of 1 microsecond in executing an empty function is actually a machine cycle of 1.085 microseconds
E = 1;
_nop_();
count = date_byte;//Determine whether there are characters being transmitted in the character channel
E = 0;
_nop_();
}

}
Module initialization

Here the busy detection function is defined. We start the initialization of the module and define an initialization function according to the module initialization steps in the manual.

void InitializeLcd1602()//lcd1602 initialization process
{
//(1) Delay 15ms
Delay15ms();
//(2) Write command 38H (does not detect busy signal)
WriteOrder(0x38);
//(3) Delay 5ms
Delay5ms();
//(4) In the future, every write instruction and read/write data operation need to detect the busy signal
//(5) Write command 38H: Display mode setting
Detection_busy();
WriteOrder(0x38);
//(6) Write command 08H: Display off
Detection_busy();
WriteOrder(0x08);
//(7) Write command 01H: Display clear screen
Detection_busy();
WriteOrder(0x01);
//(8) Write command 06H: Display cursor movement settings
Detection_busy();
WriteOrder(0x06);
//(9) Write command 0CH: display on and cursor setting
Detection_busy();
WriteOrder(0x0C);
}

2. Data transmission

After the module initialization is completed, we start transmitting data.

If we want the data to be displayed on the LCD display, we need to tell it where the characters are displayed and what characters are displayed.

Let’s first look at the write operation timing diagram and timing parameter diagram of LCD1602

Based on these two timing diagrams, we define the write instruction function and the write data function

void WriteDate(char date)//Write data
{
Detection_busy();
RS = 1;//Give es high level to select the data register
RW = 0;//Give rw low level for write operation
date_byte = date;//Transfer data
E = 0;//Give e a low level first
_nop_();//The rising edge time of e
E = 1;//Give e a high level

_nop_();//e’s high level time pulse width
E = 0; //Give e a low level
_nop_();
}

void WriteOrder(char order)//The writing instruction is the position of the input character
{
Detection_busy();
RS = 0;//Give es low level to select the instruction register
RW = 0;//Give rw low level for write operation
date_byte = order;//Transfer data
E = 0;//Give e a low level first
_nop_();//The rising edge time of e
E = 1;//Give e a high level

_nop_();//e’s high level time pulse width
E = 0; //Give e a low level
_nop_();

3. Determine the character display position

Make judgments based on the LCD internal display address

Define a function to encapsulate LCD reading and writing, because what we need to display is a string. If we only display a single character, we only need to call the reading and writing function.

void WriteString(int line,char location,char *s)
{
//line: number of lines, location: the position of the first character, s: the string to be displayed
switch(line)
{
case 1:
WriteOrder(location);
//To pass the position of data display, you only need to give the first address, because the cursor automatically moves to the right when the LCD module displays characters.
while(*s != '\0')
{
\t\t
WriteDate(*s);//Write data
//location + = 0x01;
s++;
}

break;
case 2:
WriteOrder(location);//Pass the location where the data is displayed
while(*s != '\0')
{
\t\t\t
WriteDate(*s);//Write data
//location + = 0x01;
s++;
}
break;
}

4. Integration of code

#include "reg52.h"
#include "intrins.h"

#define date_byte P0 //Define the group of lines P0-P7 as date_byte

sbit RS = P1^0;
sbit RW = P1^1;
sbit E = P1^4;

void Delay5ms() //@11.0592MHz
{
unsigned char i, j;

i = 9;
j = 244;
do
{
while (--j);
} while (--i);
}

void Delay15ms() //@11.0592MHz
{
unsigned char i, j;

i = 27;
j = 226;
do
{
while (--j);
} while (--i);
}

void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;

_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}

void Detection_busy()//Busy detection
{
char count = 0x80;
date_byte = 0x80;
while(count & 0x80)
{
RS = 0;
RW = 1;
E = 0;
_nop_();
E = 1;
_nop_();
count = date_byte;//Determine whether there are characters being transmitted in the character channel
E = 0;
_nop_();
}
}

void WriteDate(char date)//Write data
{
Detection_busy();
RS = 1;//Give es high level to select the data register
RW = 0;//Give rw low level for write operation
date_byte = date;//Transfer data
E = 0;//Give e a low level first
_nop_();//The rising edge time of e
E = 1;//Give e a high level

_nop_();//e’s high level time pulse width
E = 0; //Give e a low level
_nop_();
}

void WriteOrder(char order)//The writing instruction is the position of the input character
{
Detection_busy();
RS = 0;//Give es low level to select the instruction register
RW = 0;//Give rw low level for write operation
date_byte = order;//Transfer data
E = 0;//Give e a low level first
_nop_();//The rising edge time of e
E = 1;//Give e a high level

_nop_();//e’s high level time pulse width
E = 0; //Give e a low level
_nop_();
}

void InitializeLcd1602()//lcd1602 initialization process
{
//(1) Delay 15ms
Delay15ms();
//(2) Write command 38H (does not detect busy signal)
WriteOrder(0x38);
//(3) Delay 5ms
Delay5ms();
//(4) In the future, every write instruction and read/write data operation need to detect the busy signal
//(5) Write command 38H: Display mode setting
Detection_busy();
WriteOrder(0x38);
//(6) Write command 08H: Display off
Detection_busy();
WriteOrder(0x08);
//(7) Write command 01H: Display clear screen
Detection_busy();
WriteOrder(0x01);
//(8) Write command 06H: Display cursor movement settings
Detection_busy();
WriteOrder(0x06);
//(9) Write command 0CH: display on and cursor setting
Detection_busy();
WriteOrder(0x0C);
}

void WriteString(int line,char location,char *s)
{
switch(line)
{
case 1:
WriteOrder(location);//Pass the location where the data is displayed
while(*s != '\0')
{
\t\t
WriteDate(*s);//Write data
//location + = 0x01;
s++;
}
break;
case 2:
WriteOrder(location);//Pass the location where the data is displayed
while(*s != '\0')
{
WriteDate(*s);//Write data
//location + = 0x01;
s++;
}
break;
}
}

void main()
{
char date = 'M';
char order = 0x80;
Delay1000ms();
InitializeLcd1602();
Detection_busy();
WriteOrder(0x01);
WriteString(1,0x80 + 0x02,"HAPPY A DAY");
WriteString(2,0x80 + 0x48,"wjh.");
\t
}

Final display effect

Finally, because I don’t have a potentiometer, I connected VL to GND for the highest contrast, but if the contrast is too high, ghosting will occur.

This can be adjusted using a potentiometer.