一.IIC传输数据的格式
1.写操作
2.读操作
3.IIC信号
二. IIC底层驱动
#define SCL_PIN GPIO_PIN_6
#define SDA_PIN GPIO_PIN_7
#define SCL_PORT GPIOB
#define SDA_PORT GPIOB
/*********************
* 函数宏定义
**********************/
#define SCL_H HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, 1)
#define SCL_L HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, 0)
#define SDA_H HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, 1)
#define SDA_L HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, 0)
#define SDA_IN HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN)
/*
* 函数名:I2C_GPIO_ReInit
* 功能描述:将模拟I2C的SCL和SDA引脚进行重新初始化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
extern void I2C_GPIO_ReInit(void);
/*
* 函数名:I2C_Start
* 功能描述:模拟发出I2C的开始信号-->在SCL的高电平下,SDA的电平由高到低变化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
extern void I2C_Start(void);
/*
* 函数名:I2C_Stop
* 功能描述:模拟发出I2C的停止信号-->在SCL的高电平下,SDA的电平由低到高变化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
extern void I2C_Stop(void);
/*
* 函数名:I2C_GetAck
* 功能描述:模拟I2C获取从设备的响应信号-->在SCL的高电平下,从设备将SDA拉低
* 输入参数:无
* 输出参数:无
* 返回值:成功得到响应返回0,否则返回-1
*/
extern int I2C_GetAck(void);
/*
* 函数名:I2C_ACK
* 功能描述:模拟I2C发出一个应答信号,在第九个时钟将SDA拉低
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
extern void I2C_ACK(void);
/*
* 函数名:I2C_NACK
* 功能描述:模拟I2C发出一个非应答信号,在第九个时钟将SDA拉高
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
extern void I2C_NACK(void);
/*
* 函数名:I2C_WriteByte
* 功能描述:模拟I2C发出一个字节的数据
* 输入参数:data-->要发送出去的数据,范围0~255
* 输出参数:无
* 返回值:无
*/
extern void I2C_WriteByte(uint8_t data);
/*
* 函数名:I2C_ReadByte
* 返回值:返回读取到的数据
* 输入参数:ack-->根据此参数判断在读到一个字节之后是否发出应答信号
* 输出参数:无
*/
extern uint8_t I2C_ReadByte(uint8_t ack);
#endif /* __DRIVER_I2C_H */
void I2C_Delay(uint32_t t)
{
volatile uint32_t tmp = t;
while(tmp--);
}
/*
* 函数名:I2C_GPIO_ReInit
* 功能描述:将模拟I2C的SCL和SDA引脚进行重新初始化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void I2C_GPIO_ReInit(void)
{
/* 1. 使用结构体定义硬件GPIO对象 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 2. 将SCL和SDA的GPIO寄存器的值恢复为复位上电值 */
HAL_GPIO_DeInit(SCL_PORT, SCL_PIN);
HAL_GPIO_DeInit(SDA_PORT, SDA_PIN);
/* 3. 使能SCL和SDA的GPIO的时钟,因为他们都是GPIOF组的,所以这里只使能GPIOF的时钟 */
__HAL_RCC_GPIOF_CLK_ENABLE();
/* 4. 设置GPIO的模式为开漏输出模式,响应速度设置为快速响应 */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
/* 5. 选择要设置的GPIO引脚 */
GPIO_InitStruct.Pin = SCL_PIN;
/* 6. 调用init函数初始化GPIO */
HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SDA_PIN;
HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);
}
/*
* 函数名:I2C_Start
* 功能描述:模拟发出I2C的开始信号-->在SCL的高电平下,SDA的电平由高到低变化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void I2C_Start(void)
{
SCL_H;
SDA_H;
I2C_Delay(100);
SDA_L;
I2C_Delay(100);
}
/*
* 函数名:I2C_Stop
* 功能描述:模拟发出I2C的停止信号-->在SCL的高电平下,SDA的电平由低到高变化
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void I2C_Stop(void)
{
SDA_L;
SCL_H;
I2C_Delay(100);
SDA_H;
I2C_Delay(100);
}
/*
* 函数名:I2C_GetAck
* 功能描述:模拟I2C获取从设备的响应信号-->在SCL的高电平下,从设备将SDA拉低
* 输入参数:无
* 输出参数:无
* 返回值:成功得到响应返回0,否则返回-1
*/
int I2C_GetAck(void)
{
int i = 0;
SCL_L;
SDA_H;
I2C_Delay(100);
SCL_H;
while(SDA_IN != 0)
{
i++;
if(i == 100)
{
SCL_L;
return -1;
}
I2C_Delay(10);
}
SCL_L;
return 0;
}
/*
* 函数名:I2C_ACK
* 功能描述:模拟I2C发出一个应答信号,在第九个时钟将SDA拉低
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void I2C_ACK(void)
{
SCL_L;
SDA_L;
I2C_Delay(100);
SCL_H;
I2C_Delay(100);
}
/*
* 函数名:I2C_NACK
* 功能描述:模拟I2C发出一个非应答信号,在第九个时钟将SDA拉高
* 输入参数:无
* 输出参数:无
* 返回值:无
*/
void I2C_NACK(void)
{
SCL_L;
SDA_H;
I2C_Delay(100);
SCL_H;
I2C_Delay(100);
}
/*
* 函数名:I2C_WriteByte
* 功能描述:模拟I2C发出一个字节的数据
* 输入参数:data-->要发送出去的数据,范围0~255
* 输出参数:无
* 返回值:无
*/
void I2C_WriteByte(uint8_t data)
{
uint8_t i = 0;
for(i=0; i<8; i++)
{
SCL_L;
I2C_Delay(100);
if(data & 0x80)
{
SDA_H;
}
else
{
SDA_L;
}
data <<= 1; // 发出1bit数据后,要更新数据,将data的次高位移位到最高位
SCL_H;
I2C_Delay(100);
}
I2C_GetAck();
}
/*
* 函数名:I2C_ReadByte
* 返回值:返回读取到的数据
* 输入参数:ack-->根据此参数判断在读到一个字节之后是否发出应答信号
* 输出参数:无
*/
uint8_t I2C_ReadByte(uint8_t ack)
{
uint8_t i = 0;
uint8_t data = 0;
SDA_H;
for(i=0; i<8; i++)
{
SCL_L;
I2C_Delay(100);
SCL_H;
I2C_Delay(100);
data <<= 1; // 更新数据前,要将上一次数据左移1位用来保存接下来的这一位数据
if(SDA_IN == 1)
{
data++;
}
else
{
data = data;
}
}
// 根据ack决定是否发出应答
if(ack == 0)
{
I2C_ACK();
}
else if(ack == 1)
{
I2C_NACK();
}
return data;
}