I2C communication protocol (transfer)

Source - Author @WeiKo90 &: https://blog.csdn.net/WeiKo90/article/details/77512135

Readers are advised to read the original text to ensure complete information

1. What is I2C

I2C bus ( Inter-Integrated Circuit ) is a simple, bidirectional two-wire synchronous serial bus developed by Philips . Information can be transmitted between devices connected to the bus by the SCL and SDA lines.

SCL is the clock line, and SDA is the data line. Under the clock signal controlled by the clock line SCL, SDA transmits data.

Each byte transmitted on SDA must be 8 bits, and each byte is followed by a response bit. The transmission of data is carried out bit by bit, and the most significant bit (MSB) is transmitted first.

2. I2C timing diagram and program

①Start signal and end signal


1) Start signal: When SCL is high, the SDA line changes from high level to low level to indicate the start signal, and the bus is occupied.

2) Termination signal: When SCL is high, the change of SDA line from low level to high level indicates the termination signal, and the bus is in the control line.

The specific reference procedure is as follows:

/*******************************************************************************  
* Function name: I2C_Start()  
* Function: Start signal: I2C_SDA signal generates a falling edge during the high period of I2C_SCL clock signal  
* input: none  
* output: none  
* Note: After the start, both I2C_SDA and I2C_SCL are 0. Only when SCL=0, SDA is allowed to change high and low levels.  
*******************************************************************************/  
void I2C_Start()  
{  
    I2C_SDA = 1;  
    I2C_SCL = 1;  
    I2C_Delay_us(5);//The setup time is I2C_SDA hold time>4.7us  
    I2C_SDA = 0;  
    I2C_Delay_us(4);//The hold time is >4us  
    I2C_SCL = 0;                  
}  
  
/*******************************************************************************  
* Function name: I2C_Stop()  
* Function: Termination signal: the I2C_SDA signal generates a rising edge during the high level of the I2C_SCL clock signal  
* input: none  
* output: none  
* Remark: Keep I2C_SDA and I2C_SCL both 1 after the end; it means the bus is idle  
*******************************************************************************/  
void I2C_Stop()  
{  
    I2C_SDA = 0;  
    I2C_Delay_us(4);  
    I2C_SCL = 1;  
    I2C_Delay_us(4);
    I2C_SDA = 1;          
}  

I2C send bytes and read bytes

1) When SCL is low, SDA is in the state of data conversion, and the data state is unstable and uncertain at this time.

2) When SCL is high, the data transmitted on SDA is stable, but the transmitted data may be high or low.

3) When sending a byte, pull down SCL first, and convert it to the SDA line according to the high and low level of a bit currently being processed in the byte to be sent. After the conversion is completed, pull up SCL for data transmission.

4) When reading a byte, first pull up SCL, determine whether the transmission data on SDA is high or low, and assign it to the corresponding variable, and then pull down SCL.

The specific reference procedure is as follows:

/*******************************************************************************  
* Box name: I2C_SendByte (unsigned char byt)  
* Function: send a byte  
* input: byt: bytes sent  
* output: none  
* Remarks: None  
*******************************************************************************/  
void I2C_SendByte(unsigned char byt)  
{  
    unsigned char i;  
    I2C_SCL = 0;  
    for(i = 0;i < 8;i++)  
    {          
        if(byt&0x80) //Start from the highest bit to judge whether the sent byte byt is 0 or 1 to judge whether I2C_SDA is 0 or 1  
        {  
            I2C_SDA = 1;  
        }  
        else  
        {  
            I2C_SDA = 0;  
        }             
        byt <<= 1; //After judging one bit, then shift one bit to the left and let the next bit continue to judge, one byte is 8 bits, so a total of 8 judgments are required  
        delay_us(2);  
        I2C_SCL = 1;          
        delay_us(2);  
        I2C_SCL = 0;  
    }  
    I2C_SCL = 0;  
}  
  
/*******************************************************************************  
* 函 数 名           : unsigned char IIC_Read_Byte(unsigned char ack)  
* Function: read a byte  
* Input: ack (0 or 1)  
* output: one byte of dat read  
* Remarks: None  
*******************************************************************************/  
unsigned char IIC_Read_Byte(unsigned char ack)  
{  
    unsigned char i,dat = 0;  
    I2C_SCL = 0;  
    for(i = 0;i < 8;i++ )  
    {  
        IIC_SCL = 1;  
        that <<= 1;  
        if(I2C_SDA) //According to the level of I2C_SDA, assign it to dat one by one  
        {   
            dat |= 0x01;   
        }             
        delay_us(1);   
        I2C_SCL = 0;  
    }                      
    IIC_Ack(ack); //If only 1 byte of data is received, send a non-acknowledgement signal 1,  
                      //Then a stop signal is generated to tell the slave microcontroller to stop receiving data, that is, there is no need to send it again  
                      //If you want to receive multiple bytes of data, you need to send a response signal 0 after receiving one byte of data.  
                      //Tell the slave to continue sending to the microcontroller  
    return dat;  
}  

The I2C host waits for a response and generates a response


Waiting for a response and generating a response is similar to reading a byte and writing a byte, the difference is that the read and write byte is a byte with 8 bits, so 8 cycles are performed inside the function, while waiting for a response and generating a response only need to process one bit data, so there is no need to loop, and its essence is similar.

/*******************************************************************************  
* Function name: I2C_Wait_Ack()  
* Function: Waiting for response: waiting for the slave device to pull I2C_SDA low  
* input: none  
* output: 0 or 1  
* Remarks: 0 indicates that the reply failed to be sent or no reply, 1 indicates that the reply was received  
*******************************************************************************/  
unsigned char I2C_Wait_Ack(void)  
{  
    unsigned char acktime;  
    I2C_SCL = 1;  
    delay_us(1);  
    while(I2C_SDA)                 //等待应答,即等待从设备把I2C_SDA拉低  
    {  
        acktime++;  
        if(acktime>200)            //如果超过200us没有应答,则发送失败,或者为非应答,表示接受结束  
        {  
            I2C_SCL = 0;  
            delay_us(1);  
            return 0;  
        }         
    }  
    delay_us(1);  
    I2C_SCL = 0;  
    return 1;  
}  
  
/*******************************************************************************  
* 函 数 名           : IIC_Ack(unsigned char ackbit)  
* 函数功能           : 产生应答  
* 输    入           : 0或1  
* 输    出           : 无  
* 备    注           : 0表示产生应答,1表示不产生应答  
*******************************************************************************/  
void IIC_Ack(unsigned char ackbit)  
{  
    I2C_SCL = 0;  
    I2C_SDA = ackbit;  
    delay_us(2);  
    I2C_SCL = 1;  
}  

3.主从机I2C通信过程


主机发送过程

1)主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始。
2)主机接着发送一个命令字节。该字节由 7 位的外围器件地址和 1 位读写控制位 R/W组成。(此时 R/W=0)
3)相对应的从机收到命令字节后向主机回馈应答信号 ACK。(ACK=0)
4)主机收到从机的应答信号后开始发送第一个字节的数据。
5)从机收到数据后返回一个应答信号 ACK。
6)主机收到应答信号后再发送下一个数据字节。

7)当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信。


②主机接收过程
1)主机发送启动信号后,接着发送命令字节。(其中 R/W=1)
2)对应的从机收到地址字节后,返回一个应答信号并向主机发送数据。
3)主机收到数据后向从机反馈一个应答信号。
4)从机收到应答信号后再向主机发送下一个数据 。
5)当主机完成接收数据后,向从机发送一个“非应答信号(ACK=1)”,从机收到ASK=1 的非应答信号后便停止发送。

6)主机发送非应答信号后,再发送一个停止信号,释放总线结束通信。


/*******************************************************************************
* 函 数 名           : write_eeprom(unsigned char add,unsigned char val)
* 函数功能	     : 向eeprom写一个字节
* 输    入           : eeprom中某个地址和要写入的数据字节
* 输    出           : 无
* 备    注           : 无
*******************************************************************************/
void write_eeprom(unsigned char add,unsigned char val)
{
    I2C_Start();
    I2C_SendByte(0xa0);
    I2C_Wait_Ack();
    I2C_SendByte(add);
    I2C_Wait_Ack();
    I2C_SendByte(val);
    I2C_Wait_Ack();
    I2C_Stop();
}

/*******************************************************************************
* 函 数 名           : unsigned char read_eeprom(unsigned char add)
* 函数功能	     : 从eeprom读一个字节
* 输    入           : eeprom中的某个地址
* 输    出           : 选中地址里面存储的数据
* 备    注           : 无
*******************************************************************************/
unsigned char read_eeprom(unsigned char add)
{
	unsigned char da;
  
	I2C_Start();
	I2C_SendByte(0xa0);
	I2C_Wait_Ack();
	I2C_SendByte(add);
	I2C_Wait_Ack();

	I2C_Start();
	I2C_SendByte(0xa1);
	I2C_Wait_Ack();
	da = IIC_Read_Byte(); 
	I2C_Stop();
	
	return da;
}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325384999&siteId=291194637