目录
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 */
打印结果如下:
?
|