Single-chip microcomputer - IIC protocol and 24C02

1. Basic knowledge
1.1. The composition and working principle of the IIC serial bus
The I2C bus has only two bidirectional signal lines. One is the data line SDA, and the other is the clock line SCL.
insert image description here
1.2. Data transmission on the I2C bus
When the I2C bus transmits data, the data on the data line must remain stable when the clock signal is at a high level. Only when the signal on the clock line is at a low level, the high level on the data line Level or low state is allowed to change. When the SCL line is high level, the change of the SDA line from high level to low level indicates the start signal; when the SCL line is high level, the change of the SDA line from low level to high level indicates the termination signal.
1.3. Simulation subroutine
1.3.1. The start signal
is actually written strictly according to the timing
insert image description here

Void I2CStart(void)
{
    
           SDA = 1;//数据线为高电平
	SomeNop(  );//保持一段时间
	SCL = 1;//时钟线为高电平
	SomeNop(  );//保持一段时间
	SDA = 0;//数据线为低电平
	SomeNop(  );//保持一段时间
}

1.3.2. Termination signal
insert image description here
Same principle as above

void I2cStop(void)
{
    
    
	SDA = 0;
	SomeNop(  );
	SCL = 1;
	SomeNop(  );
	SDA = 1;
	SomeNop(  );
}

1.3.3 Answer I2C bus
insert image description here

void I2cACK(void)
{
    
    
	SDA = 0;
	SomeNop(  );
	SCL = 1;
	SomeNop(  );
	SCL = 0;
	SomeNop(  );
}

1.3.4 Non-acknowledging I2C bus
insert image description here

void I2cNOACK(void)
{
    
    
	SDA = 1;
	SomeNop(  );
	SCL = 1;
	SomeNop(  );
	SCL = 0;
	SomeNop(  );
}

2. Program
2.0, macro definition, header file

#include<reg52.h>
#include<intrins.h>	  //_nop_
#define uchar unsigned char
#define uint unsigned int

sbit SCL=P2^1;
sbit SDA=P2^0;
sbit RS=P1^0;
sbit RW=P1^1;
sbit E=P2^5;
sbit duan=P2^6;
sbit wei =P2^7;
sbit RST=P2^4;

#define RS_data RS=1
#define RS_command RS=0
#define RW_read   RW=1
#define RW_write  RW=0
#define	 E_close  E=0
#define	 E_open  E=1
#define	  Data   P0

char   string[]={
    
    "wuliwuli:"} ;
char   string1[]={
    
    "clemmence:"};
uchar arr[8];
uchar arr1[8];
uchar dat[]={
    
    12,13,14,15};

2.1. Delay part

void delay(uint k)
{
    
    
 uint i,j;
for(i=0;i<k;i++)
  {
    
    
	for(j=0;j<113;j++)
  {
    
    
   ;
  }
  }
}

For the detailed program explanation of the delay part, please refer to
the link: Single-chip microcomputer controls the on and off program explanation of a light

2.2. The start and end of the IIC protocol, the response and non-response of 24c02
For the detailed explanation of this part, see the first section of this article. Mostly written in terms of time slots.

//基本模块设置
//开始状态
void  start()
{
    
    
SDA=1;
_nop_();
SCL=1;
_nop_();
SDA=0;
_nop_();
SCL=0;
_nop_();
}

//结束状态
void  end()
{
    
    
SDA=0;
_nop_();
SCL=1;
_nop_();
SDA=1;
_nop_();
}

//应答
void  ack()
{
    
    
SDA=0;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}


//非应答
void  noack()
{
    
    
SDA=1;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}

2.3. Sending bytes to 2402
is difficult to understand, so let’s explain it in detail below.
Look at the program line by line

void sendbyte2402 (uchar dat )

Define a function with a return value, and the data to be sent, why should there be a return value. This is because errors may occur in this program, such as being unable to communicate with the device or writing data errors. By setting the return value, the caller can know the result of this program execution, so as to take corresponding measures or correct errors.

Specifically, in this program, when writing or reading data, you need to send some commands to the device, and check whether the device responds to the correct response signal. If the device does not respond or the response signal is wrong, the program will go wrong. If the return value is not set, the caller cannot know whether the program is successfully executed, nor can it perform subsequent processing according to the execution result. Therefore, setting the return value can improve the robustness (robustness) and reliability of the program.

uchar i;
 for(i=0;i<8;i++)	  //一字节等于8为二进制
 {
    
    

 }

Perform cyclic transmission of bytes, one byte is 8 for binary

  SCL=0;
   _nop_();

As mentioned in the previous basic knowledge, when the clock line is 1, it is held and the state cannot be changed, so in order to be able to write data, the clock is set to 0 here. The purpose of the delay is to determine that the clock line has been pulled high and the data can be written correctly.

   SDA=dat>>7;
   _nop_();

The 8-bit binary in dat is moved to the right by 7 bits and assigned to SDA, then the high bit is stored in SDA.
The purpose of the delay is to ensure that the data has been transmitted stably.

 SCL=1;

Pull the clock line high to ensure that the state does not change and the data is stored.

 dat=dat<<1;

dat is shifted to the left, the second highest bit becomes the highest bit, and the transmission continues.

 SCL=0;

About why adding scl=0;: In this code, after sending one bit of data each time, the clock line scl needs to be set to 0, so that the data can be sent next time. Since scl needs to be used later, it needs to be set to 0 before each use of scl to avoid the interference of the last clock signal. After the loop is over, scl needs to be set to 0 again to avoid unexpected clock signals in subsequent operations.

The complete program to send bytes to 24C02 is

void sendbyte2402 (uchar dat )
{
    
    
 uchar i;
 for(i=0;i<8;i++) //一字节等于8为二进制
 {
    
    
   SCL=0;
   _nop_();
   SDA=dat>>7;
   _nop_();
   SCL=1;
   dat=dat<<1;
 }
   SCL=0;//时钟重置 
}

2.4. Read bytes from 24C02
With the knowledge of sending data accumulated, now let’s read bytes from 2402

uchar readbyte2402() //主体一个字节
{
    
    
 uchar i,dat=0;
 SDA=1;
 for (i=0;i<8;i++)
 {
    
    
   SCL=1;	  // 数据稳定,方便读取
   _nop_();	   
	dat<<=1;	//	左移移位并赋值给dat	
	_nop_();	  		
	dat=dat|SDA;
	 _nop_();	  
	 SCL=0;
	 _nop_();	   
 }
 return bat;
}

Explain uchar readbyte2402() in detail
: A function called readbyte2402 is defined, it has no input parameters, and the return type is uchar, which is an unsigned integer of one byte.
uchar i,dat=0;: Two variables i and dat are defined, i is used for counting, dat is used to store the read data, and the initial value is 0.
SDA=1;: Set the SDA pin to high level, that is, input mode, ready to read data.
if(i=0;i<8;i++): Loop 8 times, read 8 bits sequentially.
SCL=1;: Set the SCL pin to high level, and pull the clock line high before reading data to ensure that the data is stable.
nop ();: No instruction, delay for a period of time.
dat<<=1;: Left shift, shift the read data to the left by one bit to make room for the next data bit.
nop ();: No instruction, delay for a period of time.
dat=dat|SDA;: OR the currently read SDA data bit with the dat variable, and store the result in dat.
nop ();: No instruction, delay for a period of time.
SCL=0;: Pull the SCL pin low, the data bit on the data line has been read, and the next data bit is ready to be read.
nop ();: No instruction, delay for a period of time.
return dat;: Return the 8-bit data that has been read, that is, one byte of data.

2.5. Write data to 24C02
parameter explanation

uchar *ptr,uchar addr,uchar n
uchar *ptr:指向一个uchar类型的指针,该指针指向一块内存区域,用于存储要写入24C02的数据。
uchar addr:表示24C02内存的地址,指示从哪个地址开始写入数据。
uchar n:表示要写入的数据长度,即写入多少个字节的数据。
sendbyte2402(0xa0);

This line of code is to send a write command to 24C02, and specify the device address as 0xa0. In the I2C protocol, the highest bit of the device address is fixed at 0, and the remaining seven bits are set by hardware wiring or software. In this code, the device address is 0xa0, which is 1010000 in binary, where the highest bit is 0. This address is defined by the manufacturer to identify the 24C02 memory chip. After sending this command, the next operation is to write data to 24C02.
Then it loops according to the data length, sends data to the address and pointer, and generates a response. After each cycle, the address and pointer are incremented by 1.
The complete code to write data to 24C02 is

//将数据写入到24C02
void write2402(uchar *ptr,uchar add, uchar n) //指针,地址,写入长度
{
    
    
uchar i;
start();
_nop_();
sendbyte2402(0xa0);
_nop_();
ack();
_nop_();	 //给2402发送写的命令 ,并应答

for(i=0;i<n;i++)
{
    
    
 _nop_();
sendbyte2402(add);
 _nop_();
 ack();
 _nop_();
sendbyte2402( *ptr);
_nop_();
 ack();
 _nop_();
 add++;
 ptr++;
}
end();
delay(10);
}

There are many empty bytes called for delay. Then the difference between the self-defined subroutine delay and nop is:
the delay instruction is used for delay, it can make the CPU suspend execution for a period of time in the program, and then continue to execute subsequent instructions after waiting for a period of time. The length of the delay can be specified by passing parameters to the delay command, usually in milliseconds or microseconds. The delay instruction consumes CPU resources, so it needs to be used with caution to avoid slowing down or freezing the program due to excessive delay.
The nop instruction is a no-op instruction. Its function is to let the CPU execute an empty instruction without any operation, just wait for one clock cycle and continue to execute the next instruction. The nop instruction is usually used to occupy or idle, so that the execution time of the program meets specific requirements. Compared with the delay instruction, the execution speed of the nop instruction is faster and does not consume too much CPU resources, but the length of time cannot be precisely controlled.
2.6. Use the I2C protocol to read n bytes of data from the 24C02 chip

sendbyte2402(add);	 //告诉地址
noack();   //不应答就开始读数据

It means to start reading data without answering. The response indicates that 24C02 is receiving data, if the receiver returns a non-response signal, it means that the receiver is not ready to receive data and can start to read data.

sendbyte2402(0xa1);	

Tell the chip to read the command

SCL=0;

In this code, the clock is set to 0 to prevent the chip from continuing to send some unnecessary I2C commands after reading the data. At this time, the clock line is pulled down, and the chip can no longer send commands, thus ensuring the security of transmission.

The clock line will fluctuate and change during the normal I2C communication process, but after the data reading is completed, it is necessary to ensure that the clock line is in an idle state (that is, there is no fluctuation and change) to avoid accidental generation of I2C commands. Pulling the clock line low is to keep the clock line low all the time to ensure an idle state.

The complete code for I2C to read data from 24C02 is

 //使用I2C协议从24C02芯片中读取n个字节的数据
 void read2402(uchar *ptr,uchar add, uchar n)
 {
    
    
start();
_nop_();
sendbyte2402(0xa0);
_nop_();
ack();
_nop_();


while(n) //循环n次,每次读取一个字节
{
    
    
sendbyte2402(add);	 //告诉地址
noack();   //不应答就开始读数据
_nop_();

start();
sendbyte2402(0xa1);	//告诉24C02,读命令	 告诉芯片要读取数据
ack();

*ptr=readbyte2402();
ack();
ptr++;
add++;
n--;//循环计数器 
}
SCL=0; 
stop();
 }

The details above have been mentioned in detail in this section, and the overall logic of the code is introduced below. The function implemented by this code is to read data.
Before reading data, you need to send a write command to 24C02 to tell it which address to read data from. Therefore, in the place of the read command, the write command needs to be executed first, and then the read command is sent, so that the 24C02 can read the data correctly and send it to the main control chip.

Next, enter the loop and read data.
First, tell the address. If there is no response, it means that there will be no data written, so the data can be read. Tell 24C02 to read data and generate a response, which means it can read data. The pointer and address are incremented by 1, the number of cycles is decremented by 1, and a read is completed.

2.7, Nixie tube latch

void cmg()
{
    
    
duan=1;
P0=0x00;
duan=0;

wei=1;
P0=0x00;
wei=0;

RST=0;	//关时钟DS1302
}

See the blog post for an explanation of this
link: Single-chip microcomputer - digital tube dynamic display

2.8, LCD1602 write command, read command operation and set working mode module (initialization)

//LCD1602写命令
 void writecom(uchar command)
 {
    
    
 delay(10);
 RS_command;
 RW_write;
 E_open;
 Data=command;
 _nop_();
 E_close;
 }
 
 //LCD1602写数据
 void writedata(uchar da)
 {
    
    
 delay(10);
 RS_data;
 RW_write;
 E_open;
 Data=da;
 _nop_();
 E_close;
 }
 

 //LCD初始化
 void Init()
 {
    
    
 cmg();
 delay(15);
 writecom(0x38);//数据总线8位,显示两行。5×7
 writecom(0x38);
 writecom(0x38);

 writecom(0x0e);  //显示功能开,有光标,光标闪烁
 writecom(0x06); 		// 光标右移,显示屏不移动
 writecom(0x01); 	   //清屏
 }
 }


For a detailed explanation, see the blog
link: MCU - LCD1602

2.9. Processing data
At the very beginning of the program, we define the characters to be displayed and the data to be stored.

char   string[]={
    
    "wuliwuli:"} ;
char   string1[]={
    
    "clemmence:"};
uchar arr[8];
uchar arr1[8];
uchar dat[]={
    
    12,13,14,15};

Process the four elements in the bat array and place them in arr

 void culi()
 {
    
    
 arr[0]='0'+dat[0]/10;
 arr[1]='0'+dat[0]%10;
 arr[2]='0'+dat[1]/10;
 arr[3]='0'+dat[1]%10;
 arr[4]='0'+dat[2]/10;
 arr[5]='0'+dat[2]%10;
 arr[6]='0'+dat[3]/10;
 arr[7]='0'+dat[3]%10;
 }
 }

The function culi() converts the four elements in the bat array (namely bat[0], bat[1], bat[2] and bat[3]) into corresponding two-digit numbers and saves them in the arr array. Divide each element by 10 to get the tens digit, and then take the remainder of 10 to get the ones digit. In this way, the four numerical values ​​are processed into two digits respectively, which is convenient for displaying on the 1602 LCD screen.
arr[0]='0'+bat[0]/10;
In this code, '0' is an ASCII code value representing the character "0". '0' + arr1[0]/10 Divides the value of arr1[0] by 10 to get the tens digit, which is then converted to one of the characters "0" through "9". Since characters are expressed in the form of ASCII code in the computer, the ASCII code value of "0" is 48, so you can use '0' plus any number to get the corresponding ASCII code character.


 void culi1()
 {
    
    
 arr[0]='0'+arr1[0]/10;
 arr[1]='0'+arr1[0]%10;
 arr[2]='0'+arr1[1]/10;
 arr[3]='0'+arr1[1]%10;
 arr[4]='0'+arr1[2]/10;
 arr[5]='0'+arr1[2]%10;
 arr[6]='0'+arr1[3]/10;
 arr[7]='0'+arr1[3]%10;
 }

arr1 is currently an empty array, and data will be read and stored from 24c02 later, so arr1 must be processed and placed in the arr array at the same time.
2.10. Specify the position of data display. Display content
2.10.1. Specify the position of display

 lcdpos(uchar line ,p)
{
    
    
uchar pos;
if(line==0)	 
pos=0x80;//写在第一行
if(line==1)	 
pos=0xc0;//写在第二行
pos=p+pos;
writecom(pos);
}

This code is for displaying data on a 16x2 character LCD. First, define an array dat with a length of 4 to store the data to be displayed. Next are two functions, the lcd_pos function is used to set the display position, where the parameter line represents the number of rows, and p represents the number of columns. Because the 1602 display has two lines, and each line can display 16 characters, the address of the first line starts from 0x80, the address of the second line starts from 0xc0, and the number of columns is based on this plus an offset. For example, when line=0 and p=5, the value of pos is 0x85, indicating the sixth character position of the first line.
2.10.2. Display data

lcddat(uchar n,uchar *ptr)
{
    
    
uchar i;
for(i=0;i<n;i++)
{
    
    
writedata (*(ptr+i));
}
}

n is the data length.
*(ptr+i) is a pointer operation, which means to take out the value in the memory pointed to by the pointer ptr, plus the offset i. The offset i here is actually a loop variable, which is used to control the data position of the traversal.
In the function lcddat, what ptr points to is an array, and the value of each element is written into the 1602 display by traversing the elements of the array. The advantage of using pointers is that it can avoid copying of arrays when passing function parameters and save memory space. At the same time, by passing the pointer, the data passed in by the caller can be modified inside the function.

2.11. Main function

void main()
{
    
    
uchar i;
Init();		//初始化,准备进行显示
delay(100);

i=0;
lcdpos(0,0)	;//0行0列开始
lcddat(9,string+i);		//wuliwuli:,是九个字符,显示dis里面的值

i=0;
lcdpos(1,0)	;//1行0列开始
lcddat(9,string1+i);	

//写数据到24C02,给数据让1602显示,从2402里面读数据,再给1602显示
 while(1)
 {
    
    
   i=0;
   write2402(dat+i,0,4);

   culi();
   i=0;
   lcdpos(0,10);
   lcddat(8,arr+i);

   i=0;
   read2402(arr1+i,0,4);

   i=0;
   culi1();
   lcdpos(1,10);
   lcddat(8,arr+i);
 }
}

The main function of the main function is that the main function of this code is to write data into the 24C02 memory, read the data in the memory and display it on the LCD1602 liquid crystal screen. Mainly the call to the previous function. General steps:
Initialize and prepare for display. A variable i of uchar type is defined here, and then the initialization function Init() and the delay function delay() are called to prepare for display.
Display character strings on LCD1602. The initial position of the cursor is set here, and then two character strings are displayed on the LCD1602. The first character string is taken from the variable dis, and starts to be displayed at line 0, column 0, and the display length is 9 characters. The second character string is taken from the variable dis1 and displayed starting from row 1, column 0, with a display length of 9 characters.
Write data into 24C02 memory. Here an infinite loop is used to continuously write data to the 24C02 memory. Use the function write2402() to write the data in the variable dat to address 0 of the memory, and write 4 bytes.
Read the data in the 24C02 memory and display it on the LCD1602. The culi() and culi1() functions are called here to control the delay time. Then use the function read2402() to read data from the 24C02 memory, store it in the variable arr1, and read 4 bytes starting from address 0. Finally, the 8 characters in the variable arr are displayed on the LCD1602.
3. Complete code

#include<reg52.h>
#include<intrins.h>	  //_nop_
#define uchar unsigned char
#define uint unsigned int

sbit SCL=P2^1;
sbit SDA=P2^0;
sbit RS=P1^0;
sbit RW=P1^1;
sbit E=P2^5;
sbit duan=P2^6;
sbit wei =P2^7;
sbit RST=P2^4;

#define RS_data RS=1
#define RS_command RS=0
#define RW_read   RW=1
#define RW_write  RW=0
#define	 E_close  E=0
#define	 E_open  E=1
#define	  Data   P0

char   string[]={
    
    "wuliwuli:"} ;
char   string1[]={
    
    "clemence:"};
uchar arr[8];
uchar arr1[8];
uchar dat[]={
    
    12,13,14,15};

// 延时部分
void delay(uint k)
{
    
    
 uint i,j;
for(i=0;i<k;i++)
  {
    
    
	for(j=0;j<113;j++)
  {
    
    
   ;
  }
  }
}

//基本模块设置
//开始状态
void  start()
{
    
    
SDA=1;
_nop_();
SCL=1;
_nop_();
SDA=0;
_nop_();
SCL=0;
_nop_();
}

//结束状态
void  end()
{
    
    
SDA=0;
_nop_();
SCL=1;
_nop_();
SDA=1;
_nop_();
}

//应答
void  ack()
{
    
    
SDA=0;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}


//非应答
void  noack()
{
    
    
SDA=1;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}

//发送字节给2402
void sendbyte2402 (uchar dat )
{
    
    
 uchar i;
 for(i=0;i<8;i++) //一字节等于8为二进制
 {
    
    
   SCL=0;
   _nop_();
   SDA=dat>>7;
   _nop_();
   SCL=1;
   dat=dat<<1;
 }
   SCL=0;//时钟重置 
}

//从2402里面读取字节
uchar readbyte2402() //主体一个字节
{
    
    
 uchar i;
 uchar dat=0;
 SDA=1;
 for (i=0;i<8;i++)
 {
    
    
   SCL=1;	  // 数据稳定,方便读取
   _nop_();	   
	dat<<=1;	//	左移移位并赋值给dat	
	_nop_();	  		
	dat=dat|SDA;
	 _nop_();	  
	 SCL=0;
	 _nop_();	   
 }
 return dat;
}

//将数据写入到24C02
void write2402(uchar *ptr,uchar add, uchar n) //指针,地址,写入长度
{
    
    
uchar i;
start();
_nop_();
sendbyte2402(0xa0);
_nop_();
ack();
_nop_();	 //给2402发送写的命令 ,并应答

for(i=0;i<n;i++)
{
    
    
 _nop_();
sendbyte2402(add);
 _nop_();
 ack();
 _nop_();
sendbyte2402( *ptr);
_nop_();
 ack();
 _nop_();
 add++;
 ptr++;
}
end();
delay(10);
}

 //使用I2C协议从24C02芯片中读取n个字节的数据
 void read2402(uchar *ptr,uchar add, uchar n)
 {
    
    
start();
_nop_();
sendbyte2402(0xa0);
_nop_();
ack();
_nop_();


while(n) //循环n次,每次读取一个字节
{
    
    
sendbyte2402(add);	 //告诉地址
noack();   //不应答就开始读数据
_nop_();

start();
sendbyte2402(0xa1);	//告诉24C02,读命令	 告诉芯片要读取数据
ack();

*ptr=readbyte2402();
ack();
ptr++;
add++;
n--;//循环计数器 
}
SCL=0; 
end();
 }


//数码管锁存
void cmg()
{
    
    
duan=1;
P0=0x00;
duan=0;

wei=1;
P0=0x00;
wei=0;

RST=0;	//关时钟DS1302
}


//LCD1602写命令
 void writecom(uchar command)
 {
    
    
 delay(10);
 RS_command;
 RW_write;
 E_open;
 Data=command;
 _nop_();
 E_close;
 }
 
 //LCD1602写数据
 void writedata(uchar da)
 {
    
    
 delay(10);
 RS_data;
 RW_write;
 E_open;
 Data=da;
 _nop_();
 E_close;
 }

 //LCD初始化
 void Init()
 {
    
    
 cmg();
 delay(15);
 writecom(0x38);//数据总线8位,显示两行。5×7
 writecom(0x38);
 writecom(0x38);

 writecom(0x0e);  //显示功能开,有光标,光标闪烁
 writecom(0x06); 		// 光标右移,显示屏不移动
 writecom(0x01); 	   //清屏
 }

 //对dat和arr1里面的数据进行处理
 void culi()
 {
    
    
 arr[0]='0'+dat[0]/10;
 arr[1]='0'+dat[0]%10;
 arr[2]='0'+dat[1]/10;
 arr[3]='0'+dat[1]%10;
 arr[4]='0'+dat[2]/10;
 arr[5]='0'+dat[2]%10;
 arr[6]='0'+dat[3]/10;
 arr[7]='0'+dat[3]%10;
 }

 void culi1()
 {
    
    
 arr[0]='0'+arr1[0]/10;
 arr[1]='0'+arr1[0]%10;
 arr[2]='0'+arr1[1]/10;
 arr[3]='0'+arr1[1]%10;
 arr[4]='0'+arr1[2]/10;
 arr[5]='0'+arr1[2]%10;
 arr[6]='0'+arr1[3]/10;
 arr[7]='0'+arr1[3]%10;
 }

//显示位置
 lcdpos(uchar line ,p)
{
    
    
uchar pos;
if(line==0)	 
pos=0x80;//写在第一行
if(line==1)	 
pos=0xc0;//写在第二行
pos=p+pos;
writecom(pos);
}

//显示数据
lcddat(uchar n,uchar *ptr)
{
    
    
uchar i;
for(i=0;i<n;i++)
{
    
    
writedata (*(ptr+i));
}
}

void main()
{
    
    
uchar i;
Init();		//初始化,准备进行显示
delay(100);

i=0;
lcdpos(0,0)	;//0行0列开始
lcddat(9,string+i);		//wuliwuli:,是九个字符,显示dis里面的值

i=0;
lcdpos(1,0)	;//1行0列开始
lcddat(10,string1+i);	

//写数据到24C02,给数据让1602显示,从2402里面读数据,再给1602显示
 while(1)
 {
    
    
   i=0;
   write2402(dat+i,0,4);

   culi();
   i=0;
   lcdpos(0,10);
   lcddat(8,arr+i);

   i=0;
   read2402(arr1+i,0,4);

   i=0;
   culi1();
   lcdpos(1,10);
   lcddat(8,arr+i);
 }
}

4. Running result
insert image description here
ps: explain here, forget that it is 1602, 15 has no place to display. Since programs and articles are written as they are done, the above subroutines will also be modified if they are modified, so I don’t want to modify them. Then the subroutine, that is, the content of the second section is written while doing, so the subroutine is written without running, there may be problems in some places, and the subroutine has been modified after running, but it is inevitable There are still some bugs. But the complete program, that is, the third part must be correct, and it must be possible to make it under the condition that the connection is the same as my port definition. (If I can't do it myself, I will put it in the draft box, and I will continue to read it another day.)
The 24C02 part this time is a bit difficult. It is a synthesis of 1602, IIC protocol and 24C02. Compared with the previous part, the newly added part is the use of IIC protocol and sending data to 24C02, writing data to 24C02, reading data in 24C02, etc.
Well, the part of 24C02 is here. If you have any questions, please leave a message in the comment area. If it can help you, please pay attention.

Guess you like

Origin blog.csdn.net/m0_46155417/article/details/129484935