STM32 analog I2C timing read and write EEPROM Lite

Platform: STM32ZET6 (core board) + ST-LINK/V2 + SD card + USB serial cable + external EEPROM (no pull-up resistor required)

Project introduction: The main files are in the USER group, bsp_i2c_ee.c, bsp_i2c_ee.h, bsp_eeprom.c, bsp_eeprom.h and main.c, among which bsp_i2c_ee.c mainly uses the basic analog I2C timing, and bsp_eeprom.c mainly uses The basic operations defined in the previous file perform EEPROM read and write operations. Other protocols similar to I2C timing can add new content on the basis of retaining bsp_i2c_ee.c. Some of the content of this article draws on the summary of other netizens, and I would like to express my gratitude here.


1.Hardware part: The circuit connection is relatively simple. The 24C02N I bought on Taobao mainly has four wires, two power wires, one SCL and one SDA. Here we connect SCL and SDA to the 6 and 7 pins of the B port. As shown in the picture above, if you need to change the pin settings, you only need to change the macro.


 After the MCU and EEPROM are connected, as shown in the figure below, the MCU is used as the master, and the EEPROM is used as the slave. The slave address cannot be repeated. Since the STM32ZET6 (core board) provides the pull-up input function, we can easily connect the EEPROM The module's SCL and SDA are directly connected to PB6 and PB7.


2. Software part:

The key lies in the simulation of the I2C timing, the main simulation is the start signal, the end signal, the response signal, the non-response signal, wait for the response signal to be received, send a byte, and read a byte.

These jobs are produced by the following function simulations, respectively.

int I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack();
void I2C_NoAck();
uint8_t I2C_GetAck(void);
void I2C_SendByte(uint8_t Data);
uint8_t I2C_ReadByte(uint8_t ack);


2.1 Start signal and end signal


 As shown in the figure, when SCL (SCLK) is high, SDA (SDI) transitions from high to low as a start signal. It is reflected in the program as follows:

int I2C_Start(void)
{
	I2C_SDA_OUT(); //Configure SDA as push-pull output
	
	SDA_H;
	SCL_H;//Active high
	I2C_delay();//Delay
	//Check whether SDA is ready at this time (high level)
	if(!SDA_read)
	{
		printf("\r\nSDA line is low, bus is busy, exit\r\n");
		return DISABLE;//SDA bus is busy, exit
	}
	//Create a falling edge, the falling edge is the start sign
	SDA_L;
	I2C_delay();
	//Check that SDA has become low at this time
	if(SDA_read)
	{
		printf("\r\nSDA line is high, bus error, exit\r\n");
		return DISABLE;//SDA bus is busy, exit
	}
	SCL_L;
	return ENABLE;
}

 When SCL (SCLK) is high, SDA (SDI) transitions from low to high as a termination signal. It is reflected in the program as follows:

void I2C_Stop(void)
{
	I2C_SDA_OUT(); //Configure SDA as push-pull output
	
	SCL_L;
	//Create a rising edge, the rising edge is the end sign
	SDA_L;	
	SCL_H;//Active high
	I2C_delay();//Delay
	SDA_H;
	I2C_delay();
}

2.2 Acknowledgement and non-acknowledgement signals



//The response signal of the host, the host puts the ninth bit high, and the slave pulls it low to indicate a response
static void I2C_Ack()
{
	SCL_L;
	I2C_SDA_OUT(); //Configure SDA as push-pull output
	
	SDA_L;//Set low
	I2C_delay();    //Note that the delay time should be greater than 4 microseconds, and the same is true for other locations
	SCL_H;
	I2C_delay();
	SCL_L;
}

//The master's non-response signal, the slave sets the ninth bit high, and the host pulls it low to indicate non-response
static void I2C_NoAck()
{
	SCL_L;
	I2C_SDA_OUT(); //Configure SDA as push-pull output
	
	I2C_delay();
	SDA_H;//Set high
	I2C_delay();
	SCL_H;
	I2C_delay();
	SCL_L;
}

2.3 Reception of Acknowledge Bits

uint8_t I2C_GetAck(void)
{
  uint8_t time = 0;
	I2C_SDA_IN(); //Configure SDA as pull-up input
	
	SDA_H;
	I2C_delay();
	SCL_H;
	I2C_delay();
	while(SDA_read)//The slave does not respond, if it responds, it will pull down the ninth bit
	{
		time++;
		if(time > 250)
		{
			//The termination signal cannot be issued when not responding, otherwise, the second stage cannot be performed in the composite read-write mode
			//SCCB_Stop();
			
			SCL_L;
			return DISABLE;
		}
	}
	SCL_L;
	return ENABLE;
}

2.4 Reading a Byte and Writing a Byte

//I2C write a byte
void I2C_SendByte(uint8_t Data)
{
  uint8_t cnt;
  I2C_SDA_OUT(); //Configure SDA as push-pull output

  for(cnt=0; cnt<8; cnt++)
  {
    SCL_L; //SCL is low (when SCL is low, change SDA)
    I2C_delay();

    if(Data & 0x80)
    {
      SDA_H; //SDA is high, starting from the lowest bit
    }
    else
    {
      SDA_L; // SDA low
    }
    Data <<= 1;
    SCL_H; //SCL high (send data)
    I2C_delay();
  }
  SCL_L; //SCL low (waiting for response signal)
  I2C_delay();
}

//I2C reads a byte
uint8_t I2C_ReadByte(uint8_t ack)
{
  uint8_t cnt;
  uint8_t data;
  I2C_SDA_IN(); //Configure SDA as pull-up input
	
  for(cnt=0; cnt<8; cnt++)
  {
    SCL_L; //SCL low
    I2C_delay();
		
    SCL_H; //SCL high (read data)
    data <<= 1;
    if(SDA_read)
    {
      data |= 0x01; //SDA high (data valid)
    }
    I2C_delay();
  }
  //Send a response signal, low for response, high for non-response
  if(ack == 1)
  {
     I2C_NoAck ();
  }
  else
  {
     I2C_Ack ();
  }
  return data; //return data
}
2.5 The initialization of GPIO and the reconfiguration of the input and output mode of SDA. We noticed earlier that when the response bit is received and a byte of information is read,

SDA needs to be set to input mode to read the data sent from the slave (EEPROM).

//GPIO configuration function
void I2C_GPIO_Configuration(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

  GPIO_InitStructure.GPIO_Pin = PIN_I2C_SCL;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //Push-pull output
  GPIO_Init(PORT_I2C_SCL, &GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
  GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
}


//Reset SDA to pull-up input mode
void I2C_SDA_IN()
{
   GPIO_InitTypeDef  GPIO_InitStructure;
   GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //Pull-up input, so that no pull-up resistor is needed outside the board
   GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);	
}

//Reset SDA to push-pull output mode
void I2C_SDA_OUT()
{
   GPIO_InitTypeDef  GPIO_InitStructure;
   GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //Push-pull output
   GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz; //IO port speed is 50MHz
   GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);	
}
//I2C initialization
void I2C_Initializes(void)
{
  I2C_GPIO_Configuration();
  SCL_H; //Set state
  SDA_H;
}
2.6 EEPROM-related macro definitions
#define EEPROM_DEV_ADDR 0xA0 //Address (device address)
#define EEPROM_WR                 0x00                     //写
#define EEPROM_RD                 0x01                     //读
2.7 Call the previously defined I2C timing simulation function to complete the EEPROM read and write one byte operation. (to borrow from others)

//write a byte
int EEPROM_WriteByte(uint16_t Addr, uint8_t Data)
{
  /* 1. Start */
  I2C_Start();

  /* 2. Device address /write*/
  I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_WR);
	
  //read the acknowledge bit
  if(!I2C_GetAck())
  {
	printf("\r\nNo answer when sending device address!\r\n");
	I2C_Stop();
	return DISABLE;
  }
  /* 3. Data address */
  #if (8 == EEPROM_WORD_ADDR_SIZE)
  I2C_SendByte((uint8_t)(Addr&0x00FF)); //Data address (8 bits)
  #else
  I2C_SendByte((uint8_t)(Addr>>8)); //Data address (16 bits)
  I2C_SendByte((uint8_t)(Addr&0x00FF));
  #endif
  I2C_GetAck();//No need to judge the status of the acknowledge bit

  /* 4. Write one byte of data */
  I2C_SendByte(Data);

  /* 5. stop */
  I2C_Stop();
}

// read a byte
int EEPROM_ReadByte(uint16_t Addr, uint8_t *Data)
{
  /* 1. Start */
  I2C_Start();

 /* 2. Device address /write*/
  I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_WR);
  //read the acknowledge bit
  if(!I2C_GetAck())
  {
	printf("\r\nThe two-phase write phase of reading a string of data is non-response!\r\n");
	I2C_Stop();
	return DISABLE;
  }

 /* 3. Data address */
  #if (8 == EEPROM_WORD_ADDR_SIZE)
  I2C_SendByte((uint8_t)(Addr&0x00FF)); //Data address (8 bits)
  #else
  I2C_SendByte((uint8_t)(Addr>>8)); //Data address (16 bits)
  I2C_SendByte((uint8_t)(Addr&0x00FF));
  #endif

 /* 4. Start over */
  I2C_Start();

 /* 5. Device address /read */
  I2C_SendByte(EEPROM_DEV_ADDR | EEPROM_RD);
  //read the acknowledge bit
  if(!I2C_GetAck())
  {
	printf("\r\nThe two-phase read phase of reading a string of data is non-response!\r\n");
	I2C_Stop();
	return DISABLE;
  }

 /* 6. Read one byte of data */
  *Data = I2C_ReadByte(I2C_NOACK); //Only 1 byte is read (non-response is generated)

 /* 7. Stop */
  I2C_Stop();
}
2.8 Repeatedly call the function of reading and writing a byte to read and write multiple bytes of data at the same time.

// write multiple bytes
void EEPROM_WriteNByte(uint16_t Addr, uint8_t *pData, uint16_t Length)
{
	uint16_t i;

	//Every time a byte is written, EEPROM_WriteByte is called once
	for(i=0;i<Length;i++)
	{
		//data input
		EEPROM_WriteByte(Addr, *pData);
		Addr++;
		pData++;
		// delay
		Delay_us(10000);
	}
}

//read multiple bytes
void EEPROM_ReadNByte(uint16_t Addr, uint8_t *pData, uint16_t Length)
{
	uint16_t i;

	//Every time a byte is written, call EEPROM_ReadByte once
	for(i=0;i<Length;i++)
	{
		//data input
		EEPROM_ReadByte(Addr, pData);
		Addr++;
		pData++;
		// delay
		Delay_us(10000);
	}
}
2.9 Test read and write function
#define EEPROM_BUF_LEN 64 //Test BUF length
void System_Initializes(void)
{
  // timer configuration
  SysTick_Init();
	
  // serial port configuration
  USART_Config();

  //I2C configuration
  I2C_Initializes();
}
int main(void)
{
  uint8_t cnt;
  uint8_t  line = 0;
  uint8_t  w_buf[EEPROM_BUF_LEN];
  uint8_t r_buf [EEPROM_BUF_LEN];

  System_Initializes();
  /* fill buffer */
  for(cnt=0; cnt<EEPROM_BUF_LEN; cnt++)
  {
    w_buf[cnt] = cnt;
  }
  printf("************************I2C protocol read and write EEPROM experiment ***************** ****\r\n");
  //print the read content
  for(cnt=0; cnt<EEPROM_BUF_LEN; cnt++)
  {
    printf("w_buf[%d] = %d\t", cnt, w_buf[cnt]);
    line++;
    if(line >= 4)
    {
	printf("\r\n");
	line = 0;
    }
  }
  //0 address continuously writes EEPROM_BUF_LEN section data
  EEPROM_WriteNByte(0, w_buf, EEPROM_BUF_LEN);
  Delay_us(100000);

 //The 0 address continuously reads the EEPROM_BUF_LEN section data, and prints
  EEPROM_ReadNByte(0, r_buf, EEPROM_BUF_LEN);
  //print the read content
  for(cnt=0; cnt<EEPROM_BUF_LEN; cnt++)
  {
    printf("r_buf[%d] = %d\t", cnt, r_buf[cnt]);
    line++;
    if(line >= 4)
    {
	printf("\r\n");
	line = 0;
    }
  }
}




Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326768295&siteId=291194637