[STM32CubeMX learning] I2C read and write 24C02

Table of contents

1. I2C bus

2. I2C driver writing

3、24C02

4. EEPROM read and write function writing

5. Verification


1. I2C bus

The I2C bus has two lines: the clock line SCL and the data line SDA. When the bus is idle, both lines are at high level.

I2C data transmission specification:

①When SCL is high level, SDA must remain stable; only when SCL is low level, SDA can change.

② At the beginning and end of data transmission, you need to define the start signal and stop signal:

        Start signal: SDA high -> low when SCL is high.

        Stop signal: SDA low -> high while SCL is high.

④The I2C data is first transmitted to the high bit, and the number of bytes of data between the start signal and the end signal is not limited, but each byte must be 8 bits, and each byte is followed by a "response/non-response bit" (by the receiver) supply).

        In the ninth clock cycle, the receiver pulls the SDA line low to generate an "acknowledgement"

        In the ninth clock cycle, the receiver pulls the SDA line high to generate a "non-acknowledgement"

2. I2C driver writing

Configure I2C pins in STM32CubeMX:

 i2c.h:

#ifndef _I2C_H
#define _I2C_H

#include "main.h"

//IO 操作函数
#define IIC_SDA_GPIO_MODE_Pos    GPIO_CRL_MODE7_Pos                           

#define I2C_SDA_IN {IIC_SDA_GPIO_Port->CRL&=0X0FFFFFFF;GPIOB->CRL|=(uint32_t)8<<IIC_SDA_GPIO_MODE_Pos;}
#define I2C_SDA_OUT {IIC_SDA_GPIO_Port->CRL&=0X0FFFFFFF;GPIOB->CRL|=(uint32_t)3<<IIC_SDA_GPIO_MODE_Pos;}

#define I2C_SCL_LOW      HAL_GPIO_WritePin(IIC_SCL_GPIO_Port,IIC_SCL_Pin,GPIO_PIN_RESET)
#define I2C_SCL_HIGH     HAL_GPIO_WritePin(IIC_SCL_GPIO_Port,IIC_SCL_Pin,GPIO_PIN_SET)
#define I2C_SDA_LOW      HAL_GPIO_WritePin(IIC_SDA_GPIO_Port,IIC_SDA_Pin,GPIO_PIN_RESET)
#define I2C_SDA_HIGH     HAL_GPIO_WritePin(IIC_SDA_GPIO_Port,IIC_SDA_Pin,GPIO_PIN_SET)

#define I2C_SDA_READ     HAL_GPIO_ReadPin(IIC_SDA_GPIO_Port,IIC_SDA_Pin)

#define I2C_ACK          0//应答
#define I2C_NOACK        1//非应答

void I2C_Start(void);
void I2C_Stop(void);
void I2C_PutAck(uint8_t Ack);
uint8_t I2C_GetAck(void);

uint8_t I2C_WriteByte(uint8_t Data);
uint8_t I2C_ReadByte(uint8_t ack);

#endif 

i2c.c:

#include "i2c.h"

static void I2C_Delay(void)
{
   uint8_t i=10; 
   while(i)
   {
     i--;
   }
}

/*开始信号,SCL高时,SDA高->低*/
void I2C_Start(void)
{
  I2C_SCL_HIGH;//SCL高
  I2C_Delay();

  I2C_SDA_HIGH;//SDA高
  I2C_Delay();
  I2C_SDA_LOW;//SDA低  
  I2C_Delay();

  I2C_SCL_LOW;//SCL低
  I2C_Delay();
}

/*停止信号,SCL高时,SDA低->高*/
void I2C_Stop(void)
{
  I2C_SDA_LOW;//SDA低
  I2C_Delay();

  I2C_SCL_HIGH;//SCL高
  I2C_Delay();

  I2C_SDA_HIGH;//SDA高
  I2C_Delay();
}

/*第9个时钟周期时输出应答/非应答信号,SCL高时,接收方将SDA拉低/高*/
void I2C_PutAck(uint8_t Ack)
{
  I2C_SCL_LOW;//SCL低
  I2C_Delay();

  if(I2C_ACK == Ack)
    I2C_SDA_LOW;//应答
  else
    I2C_SDA_HIGH;//非应答
  I2C_Delay();

  I2C_SCL_HIGH;//SCL高,第9个时钟周期
  I2C_Delay();
  I2C_SCL_LOW;//SCL低
  I2C_Delay();
}

/*第9个时钟周期时获得应答/非应答信号*/
uint8_t I2C_GetAck(void)
{
  uint8_t ack;

  I2C_SCL_LOW;//SCL低
  I2C_Delay();

  I2C_SDA_IN;//SDA配置为输入模式

  I2C_SCL_HIGH;//SCL高,第9个时钟周期
  I2C_Delay();

  if(I2C_SDA_READ)
    ack = I2C_NOACK;//非应答
  else
    ack = I2C_ACK;//应答

  I2C_SCL_LOW;  //SCL低
  I2C_Delay();

  I2C_SDA_OUT;//SDA配置为输出模式

  return ack;//返回应答位
}

/*I2C写1字节,SCL低->SDA写1位->SCL高(循环8次),然后获得应答位*/
uint8_t I2C_WriteByte(uint8_t Data)
{
  uint8_t cnt;

  for(cnt=0; cnt<8; cnt++)
  {
    I2C_SCL_LOW;//SCL低
    I2C_Delay();

    if(Data & 0x80)
      I2C_SDA_HIGH;//SDA高
    else
      I2C_SDA_LOW;//SDA低
    Data <<= 1;
    I2C_Delay();

    I2C_SCL_HIGH;//SCL高
    I2C_Delay();
  }
  I2C_SCL_LOW;//SCL低
  I2C_Delay();

  return I2C_GetAck();//获得应答位
}

/*I2C读一字节,SCL高->SDA读1位->SCL低(循环8次),然后发送应答位*/
uint8_t I2C_ReadByte(uint8_t ack)
{
  uint8_t cnt;
  uint8_t data;

  I2C_SCL_LOW;//SCL低
  I2C_Delay();

  I2C_SDA_IN;//SDA配置为输入模式
  for(cnt=0; cnt<8; cnt++)
  {
    I2C_SCL_HIGH;//SCL高
    I2C_Delay();

    data <<= 1;
    if(I2C_SDA_READ)
      data |= 0x01;//SDA为高

    I2C_SCL_LOW;//SCL低
    I2C_Delay();
  }
  I2C_SDA_OUT;//SDA配置为输出模式

  I2C_PutAck(ack); //产生应答(或者非应答)位

  return data;  //返回数据
}

3、24C02

①容量:2K bits = 2048 bits = 256 bytes。

②8 bytes per page, 32 pages in total.

③The address length is 8 bits.

④The device address, after the start signal, is an 8-bit device address as follows:

⑤ Steps to write one byte to EEPROM:

 ⑥ Steps to read one byte of data from EEPROM:

 4. EEPROM read and write function writing

eeprom.h

#ifndef _EEPROM_H
#define _EEPROM_H

#include "main.h"

#define EEPROM_DEV_ADDR           0xA0//地址1010000

#define EEPROM_WR                 0x00//写(地址的最后1位)
#define EEPROM_RD                 0x01//读(地址的最后1位)

uint8_t EEPROM_WriteByte(uint8_t Addr, uint8_t Data);
uint8_t EEPROM_ReadByte(uint8_t Addr, uint8_t *Data);

#endif 

eeprom.c

#include "eeprom.h"
#include "i2c.h"

/*EEPROM写1字节*/
uint8_t EEPROM_WriteByte(uint8_t Addr, uint8_t Data)
{
  uint8_t  ack;

  /*开始*/
  I2C_Start();

  /*设备地址/写*/
  ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_WR);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*数据地址*/
  ack = I2C_WriteByte(Addr);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*写1字节数据*/
  ack = I2C_WriteByte(Data);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*停止*/
  I2C_Stop();

  return I2C_ACK;
}

/*EEPROM读1字节*/
uint8_t EEPROM_ReadByte(uint8_t Addr, uint8_t *Data)
{
  uint8_t  ack;

  /*开始*/
  I2C_Start();

  /*设备地址/写*/
  ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_WR);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*数据地址*/
  ack = I2C_WriteByte(Addr);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*重新开始*/
  I2C_Start();

  /*设备地址/读*/
  ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_RD);
  if(I2C_NOACK == ack)
  {
    I2C_Stop();
    return I2C_NOACK;
  }

  /*读一字节数据*/
  *Data = I2C_ReadByte(I2C_NOACK);   //读取1字节,产生非应答

  /*停止*/
  I2C_Stop();

  return I2C_ACK;
}

5. Verification

Add #include "eeprom.h" #include "i2c.h" to the main.c file

Add the following code to the main function:

  /* USER CODE BEGIN 2 */
  uint8_t  ACK;
  uint8_t  WRITE_DATA = 0;
  uint8_t  READ_DATA;

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    WRITE_DATA++;
    ACK = EEPROM_WriteByte(0, WRITE_DATA);//0地址写1字节数据
    if(I2C_ACK == ACK)
    {
      printf("Write Data=%d\r\n",WRITE_DATA);
    }
    else printf("Write Data Erro\r\n");
    Delay_Ms(100);

    ACK = EEPROM_ReadByte(0, &WRITE_DATA);//0地址读一字节数据
    if(I2C_ACK == ACK)
    {
      printf("Read Data=%d\r\n",WRITE_DATA);
    }
    else printf("Write Data Erro\r\n");

  }
  /* USER CODE END 3 */

The printed results are as follows:

 

Guess you like

Origin blog.csdn.net/weixin_46183891/article/details/123388488