Introduction to IIC communication protocol and EEPROM

Introduction to IIC

​IIC bus (Inter-Integrated Circuit) is an integrated circuit bus, which is a simple, two-way, two-wire, synchronous serial bus designed by Philips. The I2C serial bus generally has two signal lines, one is the bidirectional data line SDA, and the other is the clock line SCL. All the serial data SDA connected to the I2C bus device are connected to the SDA of the bus, and the clock line SCL of each device is connected to the SCL of the bus. The IIC bus is a multi-directional control bus, and multiple devices (slaves) can be mounted on a bus controlled by a host at the same time. Each device connected to the bus communicates with other devices through a unique address, and the roles of master and slave are interchangeable.

​The concept of master and slave. The master is responsible for the task coordination and distribution of the entire system. The slave usually completes certain tasks by receiving instructions from the master. The master and the slave are connected through the bus for data communication. The personal computer we usually use is this concept.
insert image description here

IIC characteristics

Both SDA and SCL are bidirectional lines, both connected to a positive supply voltage through a current source or pull-up resistor. When the bus is idle, both lines are high. The output stages of devices connected to the bus must be open-drain or open-collector to perform the wired-AND function. The data transmission rate on the I²C bus can reach 100kbit/s in standard mode, 400kbit/s in fast mode, and 3.4Mbit/s in high-speed mode. Not all microcontrollers support high-speed mode. The number of interfaces connected to the bus is only limited by the bus capacitance which is 400pF.

data validity

During the high period of the clock, the data on the SDA line must remain stable, and the data line can only be changed when the clock SCL is low.
insert image description here

start signal and stop signal

​Start condition: when SCL is high level, the transition from high to low on the SDA line is defined as the start condition, end condition: when SCL is high level, the transition from low to high on the SDA line The jump is defined as a stop condition. It should be noted that both the start and stop signals are sent by the host. If the device connected to the I2C bus has a hardware interface of the I2C bus, it is easy to detect the start and stop signals. The bus is considered busy after a start condition and free after a stop condition.insert image description here

answer signal

​ Whenever the master sends a byte of data to the slave, the master always needs to wait for a response signal from the slave to confirm whether the slave has successfully received the data. The clock required for the slave to respond to the master is still provided by the master Yes, the response occurs in the clock cycle immediately after the host completes the transmission of 8 data bits, low level 0 means response, 1 means non-response.
insert image description here

A complete communication process of IIC

① Both SDA and SCL are high at the beginning, and then the host pulls SDA low to indicate the start signal;

② In the next 8 time periods, the master controls the high and low of SDA and sends the slave address. If the 8th bit is 0, it means that the next is a write operation, that is, the host transmits data to the slave; if it is 1, it means that the next is a read operation, that is, the slave transmits data to the host; in addition, the data transmission is from the highest bit to the lowest bit, so the transmission method is MSB (Most Significant Bit).

③ The device corresponding to the slave address in the bus sends a response signal;

④ In the next 8 time periods, if it is a write operation, the host controls the level of SDA; if it is a read operation, the slave controls the level of SDA;

⑤ Each time the transmission is completed, the device receiving the data sends out a response signal;

⑥ Finally, when SCL is high, the host pulls SDA high from low to indicate a stop signal, and the entire transmission ends;

Introduction to EEPROM

​The full name of EEPROM is "Electrically Erasable Programmable Read-Only Memory", that is, Electrically Erasable Programmable Read-Only Memory. Usually used to store user configuration information data. EEPROM and Flash are essentially the same. Structurally, Flash operates by sectors, and EEPROM usually operates by bytes.

​BL24C02F is the same as AT24C02, 2 Kbits (256bytes), each page size is 16 Byte

​The EEPROM (BL24C02F) device needs an 8-bit device address word to enable the chip to perform read or write operations. As shown in the figure below, A0, A1, and A2 are determined by the designer. The developer can determine the value according to the schematic diagram. R/W is the read and write bit, 1 means read, 0 means write.
insert image description here

write protection

​BL24C02F has a Write Protect (WP) pin that provides hardware data protection. When connected to ground (GND), the Write Protect (WP) pin allows normal read and write operations. When the write protection pin is connected to Vcc, the write protection function is enabled, the specific operation is shown in Table 2
insert image description here

Write BL24C02F

BL24C02F supports byte write mode and page write mode. The byte write mode is to write one address and one data; the page write mode is to write data continuously, one address to write multiple data, but the page write mode cannot automatically span pages. If the length of one page is exceeded, the excess data will overwrite the original write input data. After each write, and before the next write, there needs to be a certain interval of time, usually 5ms (see the chip manual for details)

byte write mode
insert image description here

page write mode

insert image description here

Read BL24C02F

​ BL24C02F supports current address read mode, random address read mode and sequential read mode. The current address read mode is to continue to read data at the last position after the last read/write operation. For example, the last read/write was at address n, and then the data can be read directly from n+1; the random address read mode is Specify the data address, and then read the data; the sequential read mode is to read multiple data continuously.

​ In the current address read mode, there is no need to send the data address. The data address is the position after the last read/write operation. The timing is shown in Figure 9. Note that at the end, after the host receives the data, there is no need to generate a response signal.
insert image description here

​ In the random address read mode, it is necessary to send the device address and the data address to be read first, and then re-send the start signal, device address, and read data timing as shown in Figure 10.
insert image description here

​ In the sequential read mode, you need to start from the current address read mode or random address read mode first, and then you can read multiple data continuously. The timing is shown in Figure 11. EEPROM single-byte write data logic analyzer waveform analysis is shown in the figure
insert image description here
below The figure below shows the whole process of writing data 0x40 to the EEPROM address 0x00. The waveform of the
insert image description here
EEPROM read data logic analyzer is shown in the figure below. process waveform
insert image description here

IIC software driver

The program written below is a bit watery and not yet perfect. There is a problem with the package and will be updated continuously. It is no problem to drive devices using the IIC protocol normally, and you can take it away if you need it.
xx_iic.h

#ifndef __XX_IIC_H
#define __XX_IIC_H

/*发送速率*/
#define IIC_SPEED 	3	

#define ERROR_IIC_NO_ACK	0
#define IIC_ACK_SUCCESS		1 
#define IIC_SUCCESS 	1
#define IIC_FAIL		0

typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;

typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;

typedef struct 
{
    
    
	void (*iic_init)(void);						
	void (*iic_sda_output)(void);		
	void (*iic_sda_input)(void);
	void (*iic_scl_high)(void);
	void (*iic_scl_low)(void);
	void (*iic_sda_high)(void);
	void (*iic_sda_low)(void);
	void (*iic_delay_us)(uint32_t mus);
	uint8_t (*read_sda)(void);
	uint8_t *rxd;
	uint8_t *txd;
}IIC_TypeDef;


extern int8_t iic_send_data(IIC_TypeDef *xIIC,uint8_t SlaveAddr,uint8_t addr,uint8_t data[],uint8_t datalen);
extern int8_t iic_read_data(IIC_TypeDef *xIIC,uint8_t SlaveAddr,uint8_t addr,uint8_t data[],uint8_t datalen);
#endif

#include "xx_iic.h"

/**
 * @brief iic起始信号
 */
static void iic_start_signal(IIC_TypeDef *xIIC)
{
    
    
	xIIC->iic_sda_output();
	
	xIIC->iic_sda_high();
	xIIC->iic_delay_us(IIC_SPEED);
	xIIC->iic_scl_high();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_sda_low();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_scl_low();
	return;
}

/**
 * @brief iic停止信号
 */
static void iic_stop_signal(IIC_TypeDef *xIIC)
{
    
    
	xIIC->iic_sda_output();
	
	xIIC->iic_scl_low();
	xIIC->iic_delay_us(IIC_SPEED);
	xIIC->iic_sda_low();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_scl_high();
	xIIC->iic_delay_us(IIC_SPEED);

	xIIC->iic_sda_high();
	xIIC->iic_delay_us(IIC_SPEED);

	return;
}

/**
 * @brief iic等待从机应答
 * @return ERROR_IIC_NO_ACK 无应答
 * @return IIC_ACK_SUCCESS 从机应答成功
 */
static int8_t iic_wait_Ack(IIC_TypeDef *xIIC)
{
    
    
	uint8_t Errtime = 0;
	
	xIIC->iic_sda_input();
	
	xIIC->iic_sda_high();
	xIIC->iic_delay_us(IIC_SPEED);
	xIIC->iic_scl_high();
	xIIC->iic_delay_us(IIC_SPEED);
	
	while(xIIC->read_sda())
	{
    
    
		Errtime++;
		if(Errtime>250)
		{
    
    
			iic_stop_signal(xIIC);
			return ERROR_IIC_NO_ACK;
		}
	}
	xIIC->iic_scl_low();
	return IIC_ACK_SUCCESS;
}

/**
 * @brief iic应答从机
 */
static void iic_Ack(IIC_TypeDef *xIIC)
{
    
    
	xIIC->iic_sda_output();
	
	xIIC->iic_scl_low();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_sda_low();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_scl_high();
	xIIC->iic_delay_us(IIC_SPEED);
	xIIC->iic_scl_low();
	return;
}

/**
 * @brief iic不应答从机
 */
static void iic_nAck(IIC_TypeDef *xIIC)
{
    
    
	xIIC->iic_sda_output();
	
	xIIC->iic_scl_low();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_sda_high();
	xIIC->iic_delay_us(IIC_SPEED);
	
	xIIC->iic_scl_high();
	xIIC->iic_delay_us(IIC_SPEED);
	xIIC->iic_scl_low();	
	return;
}


/**
 * @brief iic主机发送一字节数据
 */
void iic_send_byte(IIC_TypeDef *xIIC,uint8_t txd)
{
    
    
	uint8_t i = 0;
	xIIC->iic_sda_output();
	
	xIIC->iic_scl_low();	
	for(i = 0;i<8;i++)
	{
    
    
		if((txd&0x80)>>7)
		{
    
    
			xIIC->iic_sda_high();
		}
		else
		{
    
    
			xIIC->iic_sda_low();
		}
		txd<<=1; 
		xIIC->iic_delay_us(IIC_SPEED);
		
		xIIC->iic_scl_high();
		xIIC->iic_delay_us(IIC_SPEED);
		xIIC->iic_scl_low();
		xIIC->iic_delay_us(IIC_SPEED);	
	}
}

/**
 * @brief iic主机
 * @param Ack: 1:应答 0:非应答
 */
uint8_t iic_read_byte(IIC_TypeDef *xIIC,uint8_t Ack)
{
    
    
	uint8_t i = 0,receive = 0;
	xIIC->iic_sda_input();

	for(i = 0;i<8;i++)
	{
    
    	
		xIIC->iic_scl_low();
		xIIC->iic_delay_us(IIC_SPEED);
		xIIC->iic_scl_high();
		receive<<=1;
		if(xIIC->read_sda())
				receive++;         
		xIIC->iic_delay_us(IIC_SPEED);	
		xIIC->iic_scl_low();
	}
	
	if (!Ack)
	{
    
    
			iic_nAck(xIIC);
	}
	else
	{
    
    
			iic_Ack(xIIC);
	}   
    return receive;
}

/**
 * @brief iic主机发送数据至从机
 * @param SlaveAddr: 从机地址(不带读写位的地址)
* @param data:发送数据缓冲区
 * @param datalen: 数据长度
 */
int8_t iic_send_data(IIC_TypeDef *xIIC,uint8_t SlaveAddr,uint8_t addr,uint8_t data[],uint8_t datalen)
{
    
    
	uint8_t i = 0;
	int8_t error = 0;
	
	iic_start_signal(xIIC);
	
	iic_send_byte(xIIC,SlaveAddr << 1);
	error = iic_wait_Ack(xIIC);
	if(error == ERROR_IIC_NO_ACK)
		return ERROR_IIC_NO_ACK;
	
	iic_send_byte(xIIC,addr);
	error = iic_wait_Ack(xIIC);
	if(error == ERROR_IIC_NO_ACK)
		return ERROR_IIC_NO_ACK;
	
	for(i = 0;i<datalen;i++)
	{
    
    
		iic_send_byte(xIIC,data[i]);
		error = iic_wait_Ack(xIIC);
		if(error == ERROR_IIC_NO_ACK)
			return ERROR_IIC_NO_ACK;
	}
	iic_stop_signal(xIIC);
	return IIC_SUCCESS;
}

/**
 * @brief iic主机接收数据从从机
 * @param SlaveAddr: 从机地址(不带读写位的地址)
 * @param data:接收数据缓冲区
 * @param datalen: 数据长度
 */
int8_t iic_read_data(IIC_TypeDef *xIIC,uint8_t SlaveAddr,uint8_t addr,uint8_t data[],uint8_t datalen)
{
    
    
	uint8_t i = 0;
	int8_t error = 0;

	iic_start_signal(xIIC);
	iic_send_byte(xIIC,(SlaveAddr << 1));
	error = iic_wait_Ack(xIIC);
	if(error == ERROR_IIC_NO_ACK)
		return ERROR_IIC_NO_ACK;
	
	iic_send_byte(xIIC,addr);
	error = iic_wait_Ack(xIIC);
	if(error == ERROR_IIC_NO_ACK)
		return ERROR_IIC_NO_ACK;
	
	iic_start_signal(xIIC);
	iic_send_byte(xIIC,(SlaveAddr << 1) | 1);
	error = iic_wait_Ack(xIIC);
	if(error == ERROR_IIC_NO_ACK)
		return ERROR_IIC_NO_ACK;
	for(i = 0;i<datalen - 1;i++)
	{
    
    
		data[i] = iic_read_byte(xIIC,1);
	}
	data[i] = iic_read_byte(xIIC,0);
	iic_stop_signal(xIIC);
	return IIC_SUCCESS;	
}

If the above code is used specifically, some functions shown in the figure below need to be provided, including initialization function (IO initialization), SDA pull high pull low read, SCL pull high pull low, SDA input and output, delay function
insert image description here

IIC_TypeDef IIC1 = 
{
    
    
	IIC_init,
	SDA_OUT,
	SDA_IN,
	SCL_H,
	SCL_L,
	SDA_H,
	SDA_L,
	Delay_us,
	SDA_READ,
	iic_rxd,
	iic_send,
};
IIC1.iic_init();
iic_send_data(IIC1,0x50,0x00,data,1);

Guess you like

Origin blog.csdn.net/qq_41290252/article/details/120053636