写一页数据的时序图为:
读一个字节的时序图为:
将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; }