总线由 SDA和SCL 组成
1.数据线 SDA 数据线用来传输数据
2.时钟线 SCL 时钟线用来同步数据收发
引脚配置
SDA:
由于SDA 需要接受从机的ACK信号,因此将其配置为开漏输出
SCL
配置为推挽
起始信号
当SCL为高电平期间,SDA由高到低的跳变,
起始信号是一种电平跳变时序信号,而不是一个电平信号。
该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。
停止信号
当 SCL 为高电平期间, SDA 由低到高的跳变;
停止信号也是一种电平跳变时序信号,而不是一个电平信号。
该信号由主机发出,在停止信号发出后,总线就处于空闲状态
应答信号
发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
数据有效性
IIC 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,
1.只有在SCL高电平时,此时的SDA才是有效数据
2.只有在SCL信号为低电平期间,数据线上的高电平或低电平状态才允许变化
空闲状态
IIC 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。
1.主机发送起始信号
2.主机发送要读取的 I2C 从设备地址+1位的读写控制位(此处为写操作)
3.接受从机发送的 ACK 应答信号
4.主机发送要读取的寄存器地址
5.从机发送的 ACK应答信号
6.主机发送起始信号
7.主机发送要读取的 I2C 从设备地址+1位的读写控制位(此处为读操作)
8.从机发送的 ACK 应答信号
9.从I2C器件里面读取到的数据
10.主机发出 NO ACK 信号,表示读取完成,不需要从机再发送 ACK 信号了(假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,当主机发出的是非应答信号并以停止信号发出为结束,从机就会结束传输)
11.主机发出 STOP信号,停止 I2C 通信
1.开始信号
2.发送 I2C 设备地址+1位的读写控制位(为 0 表示写操作,为 1 表示读操作)
3.接受从机发送的 ACK 应答信号
4.发送要写写入数据的寄存器地址。
5.接受从机发送的 ACK 应答信号
6.发送要写入寄存器的数据
7.接受从机发送的 ACK 应答信号
8.停止信号
参考:
https://blog.csdn.net/qq_43858116/article/details/126031721
https://blog.csdn.net/as480133937/article/details/105259075
https://blog.csdn.net/ShenZhen_zixian/article/details/131395791
HAL库,硬件IIC
参考: https://blog.csdn.net/That_Assassin/article/details/135981146
#define DEV_ADDR (0xA0)
#define EEPROM_PAGESIZE 8
#define EEPROM_MAX_TRIALS 300
#define I2Cx_TIMEOUT_MAX 300
uint8_t I2C_CurrAddr=0;
uint8_t I2C_BaseAddr=0;
/**
* @brief 将缓冲区中的数据写到I2C EEPROM中
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @arg NumByteToWrite:写的字节数
* @retval 无
*/
void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint16_t NumByteToWrite)
{
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
Addr = WriteAddr % EEPROM_PAGESIZE;
count = EEPROM_PAGESIZE - Addr;
NumOfPage = NumByteToWrite / EEPROM_PAGESIZE;
NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;
/* If WriteAddr is I2C_PageSize aligned */
if(Addr == 0)
{
/* If NumByteToWrite < I2C_PageSize */
if(NumOfPage == 0)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
/* If NumByteToWrite > I2C_PageSize */
else
{
while(NumOfPage--)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, EEPROM_PAGESIZE);
WriteAddr += EEPROM_PAGESIZE;
pBuffer += EEPROM_PAGESIZE;
}
if(NumOfSingle!=0)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
}
/* If WriteAddr is not I2C_PageSize aligned */
else
{
/* If NumByteToWrite < I2C_PageSize */
if(NumOfPage== 0)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
/* If NumByteToWrite > I2C_PageSize */
else
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / EEPROM_PAGESIZE;
NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;
if(count != 0)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
}
while(NumOfPage--)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, EEPROM_PAGESIZE);
WriteAddr += EEPROM_PAGESIZE;
pBuffer += EEPROM_PAGESIZE;
}
if(NumOfSingle != 0)
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
}
}
/**
* @brief 写一个字节到I2C EEPROM中
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @retval 无
*/
uint32_t I2C_EE_ByteWrite(uint8_t* pBuffer, uint8_t WriteAddr)
{
HAL_StatusTypeDef status = HAL_OK;
status = HAL_I2C_Mem_Write(&hi2c1,DEV_ADDR, (uint16_t)WriteAddr, I2C_MEMADD_SIZE_8BIT, pBuffer, 1, 100);
/* Check the communication status */
if(status != HAL_OK)
{
/* Execute user timeout callback */
//I2Cx_Error(Addr);
}
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
/* Check if the EEPROM is ready for a new operation */
while (HAL_I2C_IsDeviceReady(&hi2c1,DEV_ADDR, EEPROM_MAX_TRIALS, I2Cx_TIMEOUT_MAX) == HAL_TIMEOUT);
/* Wait for the end of the transfer */
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
return status;
}
/**
* @brief 在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数
* 不能超过EEPROM页的大小,AT24C02每页有8个字节
* @param
* @arg pBuffer:缓冲区指针
* @arg WriteAddr:写地址
* @arg NumByteToWrite:写的字节数
* @retval 无
*/
uint32_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr, uint8_t NumByteToWrite)
{
HAL_StatusTypeDef status = HAL_OK;
/* Write EEPROM_PAGESIZE */
status=HAL_I2C_Mem_Write(&hi2c1,DEV_ADDR,WriteAddr, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(pBuffer),NumByteToWrite, 100);
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
/* Check if the EEPROM is ready for a new operation */
while (HAL_I2C_IsDeviceReady(&hi2c1,DEV_ADDR, EEPROM_MAX_TRIALS, I2Cx_TIMEOUT_MAX) == HAL_TIMEOUT);
/* Wait for the end of the transfer */
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
{
}
return status;
}
/**
* @brief 从EEPROM里面读取一块数据
* @param
* @arg pBuffer:存放从EEPROM读取的数据的缓冲区指针
* @arg WriteAddr:接收数据的EEPROM的地址
* @arg NumByteToWrite:要从EEPROM读取的字节数
* @retval 无
*/
uint32_t I2C_EE_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr, uint16_t NumByteToRead)
{
HAL_StatusTypeDef status = HAL_OK;
status=HAL_I2C_Mem_Read(&hi2c1,DEV_ADDR,ReadAddr, I2C_MEMADD_SIZE_8BIT, (uint8_t *)pBuffer, NumByteToRead,1000);
return status;
}