写一页数据的时序图为:
读一个字节的时序图为:

将PB6和PB7和PC8配置为如下,PB6作为时钟线SCL,PB7作为数据线SDA,PC8为读使能



代码如下:
相关宏定义:
#define SCL_Pin GPIO_PIN_6
#define SCL_GPIO_Port GPIOB
#define SDA_Pin GPIO_PIN_7
#define SDA_GPIO_Port GPIOB
#define I2C_READ 0xA1
#define I2C_WRITE 0xA0
#define I2C_WRITE_ENABLE HAL_GPIO_WritePin(I2C1_WP_GPIO_Port, I2C1_WP_Pin, GPIO_PIN_RESET)
#define I2C_WRITE_DISABLE HAL_GPIO_WritePin(I2C1_WP_GPIO_Port, I2C1_WP_Pin, GPIO_PIN_SET)
#define SCL_OUT(X) if(X){HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_RESET);}
#define SDA_OUT(X) if(X){HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_RESET);}
#define SDA_IN HAL_GPIO_ReadPin(SDA_GPIO_Port,SDA_Pin) //推挽模式下只有配置输入模式才能读取电平状态(开漏模式下可以随便读)
起始信号:
void IIC_Start(void)
{
SDA_OUT(1);
SCL_OUT(1);
SDA_OUT(0);
SCL_OUT(0);
}
停止信号:
void IIC_Stop(void)
{
SDA_OUT(0);
SCL_OUT(1);
SDA_OUT(1);
}
等待应答:
uint8_t IIC_Wait_Ack(void)
{
SDA_OUT(1);
SCL_OUT(1);//SCL拉高以后,主机便可以去读取从机给的信号
if(SDA_IN)
{
printf("iic wait Ack fail
");
}
SCL_OUT(0);
return 0;
}
主机发送的应答信号ACK:低电平0表示应答,1表示非应答
void IIC_Ack(void) //主机发送应答信号Ack
{
SDA_OUT(0);
SCL_OUT(1);
SCL_OUT(0);
}
void IIC_NAck(void) //主机发送非应答信号NAck
{
SDA_OUT(1);
SCL_OUT(1);
SCL_OUT(0);
}
主机发送8位数据给从机MSB 高位先发:
void IIC_Send_Byte(uint8_t byte)
{
for(uint8_t i=0;i<8;i++)
{
SDA_OUT(byte & (0x80>>i));
SCL_OUT(1); //拉高时钟线,让从机读取数据
SCL_OUT(0); //拉低时钟线,告诉从机别读,主机准备发送下一位数据
}
}
从机发送8位数据给主机:
uint8_t IIC_Read_Byte(void)
{
uint8_t byte = 0;
//SDA配置为输入模式
//IIC_SDA_Mode_IN();
//SCL_OUT(0);//先拉低时钟线,等待从机发送数据
SDA_OUT(1); //主机需要释放SDA,这时从机会自己占据SDA线给主机发送数据
for(uint8_t i=0;i<8;i++)
{
SCL_OUT(1);//拉高时钟线,主机开始接收数据
if(SDA_IN == GPIO_PIN_SET)
{
byte |= (0x80>>i); //数据为 1
}
SCL_OUT (0);//告诉对方此时准备数据
}
return byte;
}
I2C写一个字节:
void IIC_Mem_Write_Byte(uint8_t dev_addr, uint16_t mem_addr,uint8_t pData)
{
IIC_Start();
IIC_Send_Byte(dev_addr);//写设备地址
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr>>8);//写内存地址高8位
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr&0xff);//写内存地址低8位
IIC_Wait_Ack();
IIC_Send_Byte(pData);
IIC_Wait_Ack();
IIC_Stop();
}
I2C读一个字节:
uint8_t IIC_Mem_Read_Byte(uint8_t dev_addr, uint16_t mem_addr)
{
uint8_t data;
IIC_Start();
IIC_Send_Byte(dev_addr);//写设备地址
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr>>8);//写内存地址高8位
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr&0xff);//写内存地址低8位
IIC_Wait_Ack();
IIC_Start(); //再次发送一次起始信号
IIC_Send_Byte(dev_addr | 0x01);//再发送一次写设备地址(最后一位置1,表示对从机进行数据读操作)
IIC_Wait_Ack();
data = IIC_Read_Byte();
IIC_NAck();
IIC_Stop();
return data;
}
I2C写一页数据:
void IIC_Mem_Write_Page_Data(uint8_t dev_addr, uint16_t mem_addr,uint8_t *pData,uint16_t size)
{
uint8_t i=0;
IIC_Start();
IIC_Send_Byte(I2C_WRITE);//设备地址
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr>>8);//内存地址高8位
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr&0xff);//内存地址低8位
IIC_Wait_Ack();
while(size>0)
{
IIC_Send_Byte(*pData++);
IIC_Wait_Ack();
size--;
i++;
if(i == 32) //写满一页就退出
{
break;
}
}
IIC_Stop();
}
I2C读一页数据:
void IIC_Mem_Read_Page_Data(uint8_t dev_addr, uint16_t mem_addr,uint8_t *pData,uint16_t size)
{
IIC_Start();
IIC_Send_Byte(I2C_WRITE);//设备地址
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr>>8);//内存地址高8位
IIC_Wait_Ack();
IIC_Send_Byte(mem_addr&0xff);//内存地址低8位
IIC_Wait_Ack();
IIC_Start(); //再次发送一次起始信号
IIC_Send_Byte(I2C_READ);//再发送一次设备地址(最后一位置1,表示对从机进行数据读操作)
IIC_Wait_Ack();
while(size>1)
{
*pData++ = IIC_Read_Byte();
IIC_Ack();
size--;
}
*pData++ = IIC_Read_Byte();
IIC_NAck();
IIC_Stop();
}
部分测试代码(一个字节):
uint8_t dev_Addr = 0xA0;//设备地址,不同设备可能不一样
uint16_t memAddr = 0x0000;
uint8_t sendData = 0x55;
uint8_t Data;
I2C_WRITE_ENABLE; //打开I2C写使能
IIC_Mem_Write_Byte(dev_Addr , memAddr, sendData);
Delay_ms(5); //这里一定得有延时,具体多少不清楚
I2C_WRITE_DISABLE;
Data= IIC_Mem_Read_Byte(dev_Addr , memAddr);
printf("%d
",Data);
结果:

部分测试代码(读取一页数据和写一页数据):
uint8_t Pri_read_from_eprom(void)
{
IIC_Mem_Read_Page_Data(I2C_WRITE,(4 <<5),(uint8_t *)&config_value, 32);
IIC_Mem_Read_Page_Data(I2C_WRITE,(5 <<5),(uint8_t *)&config_set, 16);
IIC_Mem_Read_Page_Data(I2C_WRITE,(6 <<5),(uint8_t *)&config2_value2, 32);
}
uint8_t Restor_All_Init_to_E2P(void)//备份所有数据到EEPROM
{
I2C_WRITE_ENABLE;
Delay_ms(10);
for(uint8_t j=0;j<8;j++)
{
IIC_Mem_Write_Page_Data(I2C_WRITE,((7 + (3*j))<<5),(uint8_t *)(&config_value_init[j]), 32);
Delay_ms(10);
IIC_Mem_Write_Page_Data(I2C_WRITE,((8 + (3*j))<<5),(uint8_t *)(&config_set_init[j]), 32);
Delay_ms(10);
IIC_Mem_Write_Page_Data(I2C_WRITE,((9 + (3*j))<<5),(uint8_t *)(&config2_value2_init[j]), 32);
Delay_ms(10);
}
I2C_WRITE_DISABLE;
}
