【STM32CubeMX学习】I2C读写24C02

目录

1、I2C总线

2、I2C驱动编写

3、24C02

4、EEPROM读写函数编写

5、验证


1、I2C总线

I2C总线有两根线:时钟线SCL、数据线SDA,当总线空闲时,两根线都处于高电平。

I2C的数据传输规范:

①当SCL为高电平的时候,SDA必须保持稳定;只有SCL为低电平时SDA才可以发生变化。

②在数据传输的开始和结束需要定义开始信号和停止信号:

        开始信号:SCL为高时,SDA高->低。

        停止信号:SCL为高时,SDA低->高。

④I2C数据先传高位,在开始信号和结束信号之间有多少字节数据不限定,但每字节必须是8位,每一字节后面都有一个“应答/非应答位”(由接受方提供)。

        在第9个时钟周期,接收方将SDA线拉低,产生“应答”

        在第9个时钟周期,接收方将SDA线拉高,产生“非应答”

2、I2C驱动编写

在STM32CubeMX配置I2C引脚:

 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,共32页。

③地址长度为8位。

④设备地址,开始信号之后,是8位的设备地址如下:

⑤向EEPROM写一字节步骤:

 ⑥从EEPROM读取一字节数据步骤:

 4、EEPROM读写函数编写

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、验证

在main.c文件添加 #include "eeprom.h" #include "i2c.h"

在main函数里加入下面代码:

  /* 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 */

打印结果如下:

猜你喜欢

转载自blog.csdn.net/weixin_46183891/article/details/123388488