一、基础信息
1.1、简介
基于STM32单片机HAL库,用GPIO模拟I2C主机时序,可作为程序库使用。
1.2、说明
1)根据硬件连接的I2C从机设备的地址字节数,对应定义DataAddressSizeof的值,仅支持1个字节和2个字节。
2)调用I2C_Typedef,定义I2C设备结构体;调用I2CInit,通过结构体传参的方式,设置I2C设备结构体的参数。
3)I2C设备结构体的ICTE为时序时间参数,单位为NOP,需基于单片机主频和I2C从机设备时序要求进行设置。
4)I2C设备结构体的ICGO为GPIO引脚端口号和引脚号参数。
5)调用函数I2CWriteData,往I2C设备写入数据;调用函数I2CReadData,从I2C设备读取数据。
二、头文件定义
2.1、读写地址的字节数
定义I2C从机读写地址的字节数。定义为1,表示地址为1个字节,取值范围为0~255;定义为2,表示地址为2个字节,取值范围0~65535。若定义为其他值,则编译程序会报错。
/*
* DataAddressSizeof:读写地址的字节数
* 取值:1和2
*/
#define DataAddressSizeof 2
2.2、函数执行结果
未具体说明的函数,参照此处定义;若函数注释有具体说明,则以注释内容为准。
typedef enum
{
I2CResult_NULL, //未执行
I2CResult_OK, //执行成功
I2CResult_ERROR, //执行出错
}
I2CResult_Typedef; //函数执行结果
2.3、I2C结构体
使用时,只调用I2C_Typedef定义I2C结构体。
typedef struct
{
uint8_t read; //读取
uint8_t write; //写入
}
I2CControlByte_Typedef; //控制字节
typedef struct
{
uint16_t scl_high_hold; //SCL高电平保持时间,单位:nop
uint16_t scl_low_hold; //SCL低电平保持时间,单位:nop
uint16_t sda_high_hold; //SDA高电平保持时间,单位:nop
uint16_t sda_low_hold; //SDA低电平保持时间,单位:nop
uint16_t wait_ack; //等待ACK,单位:nop。到达等待时间,仍未接收到ACK,则判定为Not ACK。
}
I2CTime_Typedef; //I2C时间设置
typedef struct
{
GPIO_TypeDef* scl_port; //SCL端口
GPIO_TypeDef* sda_port; //SDA端口
uint16_t scl_pin; //SCL引脚
uint16_t sda_pin; //SDA引脚
}
I2C_GPIO_Typedef; //I2C的GPIO
typedef struct
{
I2CControlByte_Typedef ICCB;//ControlByte
I2CTime_Typedef ICTE; //TIME
I2C_GPIO_Typedef ICGO; //GPIO
}
I2C_Typedef;
2.4、接口函数声明
供其他工程文件调用的函数。包含I2CInit、I2CWriteData、I2CReadData。
/*
* 功能:初始化I2C
* 输入:@p_I2C:I2C结构体指针
* @SetI2C:设置参数结构体
* 输出:无
* 备注:
*/
void I2CInit(I2C_Typedef* p_I2C,I2C_Typedef SetI2C);
#if DataAddressSizeof == 1
/*
* 功能:I2C写数据
* 输入:@p_I2C:I2C结构体指针
* @address:写入地址
* @p_data:写入数据缓存
* @size:写入数据大小
* 输出:I2CResult_OK:写入成功
* I2CResult_ERROR:写入失败
* 备注:
*/
I2CResult_Typedef I2CWriteData(I2C_Typedef* p_I2C,uint8_t address,uint8_t* p_data,uint8_t size);
/*
* 功能:I2C读数据
* 输入:@p_I2C:I2C结构体指针
* @address:读取地址
* @p_data:读取数据缓存
* @size:读取数据大小
* 输出:I2CResult_OK:读取成功
* I2CResult_ERROR:读取失败
* 备注:
*/
I2CResult_Typedef I2CReadData(I2C_Typedef* p_I2C,uint8_t address,uint8_t* p_data,uint8_t size);
#elif DataAddressSizeof == 2
/*
* 功能:I2C写数据
* 输入:@p_I2C:I2C结构体指针
* @address:写入地址
* @p_data:写入数据缓存
* @size:写入数据大小
* 输出:I2CResult_OK:写入成功
* I2CResult_ERROR:写入失败
* 备注:
*/
I2CResult_Typedef I2CWriteData(I2C_Typedef* p_I2C,uint16_t address,uint8_t* p_data,uint16_t size);
/*
* 功能:I2C读数据
* 输入:@p_I2C:I2C结构体指针
* @address:读取地址
* @p_data:读取数据缓存
* @size:读取数据大小
* 输出:I2CResult_OK:读取成功
* I2CResult_ERROR:读取失败
* 备注:
*/
I2CResult_Typedef I2CReadData(I2C_Typedef* p_I2C,uint16_t address,uint8_t* p_data,uint16_t size);
#else
ERROR:The value of DataAddressSizeof can only be 1 or 2; //DataAddressSizeof的值只能为1或2
#endif
三、工程文件
3.1、空操作延时
/*
* 功能:I2C软件延时
* 输入:@nop_number:空操作次数
* 输出:无
* 备注:
*/
static void I2CDelay(uint16_t nop_number)
{
uint16_t i;
for(i = 0;i < nop_number;i ++)
{
__NOP();
}
}
3.2、GPIO操作函数
非HAL库,没有函数HAL_GPIO_WritePin,可以替换为对应功能的程序。
/*
* 功能:拉高SDA
* 输入:@p_I2C:I2C结构体指针
* 输出:无
* 备注:
*/
static void I2CSetSDA(I2C_Typedef* p_I2C)
{
HAL_GPIO_WritePin(p_I2C->ICGO.sda_port, p_I2C->ICGO.sda_pin, GPIO_PIN_SET); //拉高:SDA
I2CDelay(p_I2C->ICTE.sda_high_hold);
}
/*
* 功能:拉低SDA
* 输入:@p_I2C:I2C结构体指针
* 输出:无
* 备注:
*/
static void I2CResetSDA(I2C_Typedef* p_I2C)
{
HAL_GPIO_WritePin(p_I2C->ICGO.sda_port, p_I2C->ICGO.sda_pin, GPIO_PIN_RESET); //拉高:SDA
I2CDelay(p_I2C->ICTE.sda_low_hold);
}
/*
* 功能:拉高SCL
* 输入:@p_I2C:I2C结构体指针
* 输出:无
* 备注:
*/
static void I2CSetSCL(I2C_Typedef* p_I2C)
{
HAL_GPIO_WritePin(p_I2C->ICGO.scl_port, p_I2C->ICGO.scl_pin, GPIO_PIN_SET); //拉高:SDA
I2CDelay(p_I2C->ICTE.scl_high_hold);
}
/*
* 功能:拉低SCL
* 输入:@p_I2C:I2C结构体指针
* 输出:无
* 备注:
*/
static void I2CResetSCL(I2C_Typedef* p_I2C)
{
HAL_GPIO_WritePin(p_I2C->ICGO.scl_port, p_I2C->ICGO.scl_pin, GPIO_PIN_RESET); //拉高:SDA
I2CDelay(p_I2C->ICTE.scl_low_hold);
}
3.3、I2C协议函数
I2C协议函数主体,包含I2CStart、I2CStop、I2CWaitAck、I2CSendAck、I2CSendNotAck、I2CWriteByte和I2CReadByte。
/*
* 功能:开始I2C
* 输入:@p_I2C:I2C结构体指针
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CStart(I2C_Typedef* p_I2C)
{
I2CSetSDA(p_I2C); //拉高:SDA
I2CSetSCL(p_I2C); //拉高:SCL
I2CResetSDA(p_I2C); //拉低:SDA
I2CResetSCL(p_I2C); //拉低:SCL
return I2CResult_OK;
}
/*
* 功能:结束I2C
* 输入:@p_I2C:I2C结构体指针
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CStop(I2C_Typedef* p_I2C)
{
I2CResetSCL(p_I2C); //拉低:SCL
I2CResetSDA(p_I2C); //拉低:SDA
I2CSetSCL(p_I2C); //拉高:SCL
I2CSetSDA(p_I2C); //拉高:SDA
return I2CResult_OK;
}
/*
* 功能:等待应答
* 输入:@p_I2C:I2C结构体指针
* 输出:I2CResult_OK:应答成功
* I2CResult_ERROR:应答失败
* 备注:
*/
static I2CResult_Typedef I2CWaitAck(I2C_Typedef* p_I2C)
{
uint16_t i;
I2CSetSDA(p_I2C); //拉高:SDA
I2CSetSCL(p_I2C); //拉高:SCL
for(i = 0;i < p_I2C->ICTE.wait_ack;i ++)
{
if(HAL_GPIO_ReadPin(p_I2C->ICGO.sda_port, p_I2C->ICGO.sda_pin) == GPIO_PIN_RESET) //判定:应答成功
{
I2CResetSCL(p_I2C); //拉低:SCL
return I2CResult_OK;
}
}
I2CStop(p_I2C);
return I2CResult_ERROR;
}
/*
* 功能:输出应答
* 输入:@p_I2C:I2C结构体指针
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CSendAck(I2C_Typedef* p_I2C)
{
I2CResetSDA(p_I2C); //拉低:SDA
I2CSetSCL(p_I2C); //拉高:SCL
I2CResetSCL(p_I2C); //拉低:SCL
return I2CResult_OK;
}
/*
* 功能:输出非应答
* 输入:@p_I2C:I2C结构体指针
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CSendNotAck(I2C_Typedef* p_I2C)
{
I2CSetSDA(p_I2C); //拉高:SDA
I2CSetSCL(p_I2C); //拉高:SCL
I2CResetSCL(p_I2C); //拉低:SCL
return I2CResult_OK;
}
/*
* 功能:写一个字节
* 输入:@p_I2C:I2C结构体指针
* @data:写入的数据
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CWriteByte(I2C_Typedef* p_I2C,uint8_t data)
{
uint8_t i;
for(i = 0;i < 8;i ++)
{
I2CResetSCL(p_I2C); //拉低:SCL
if((data & 0x80) == 0x80)
{
I2CSetSDA(p_I2C); //拉高:SDA
}
else
{
I2CResetSDA(p_I2C); //拉低:SDA
}
I2CSetSCL(p_I2C); //拉高:SCL
data <<= 1;
}
I2CResetSCL(p_I2C); //拉低:SCL
return I2CResult_OK;
}
/*
* 功能:读一个字节
* 输入:@p_I2C:I2C结构体指针
* @p_data:读取数据的缓存
* 输出:I2CResult_OK
* 备注:
*/
static I2CResult_Typedef I2CReadByte(I2C_Typedef* p_I2C,uint8_t* p_data)
{
uint8_t i;
*p_data = 0;
I2CSetSDA(p_I2C); //拉高:SDA
for(i = 0;i < 8;i ++)
{
I2CSetSCL(p_I2C); //拉高:SCL
*p_data <<= 1;
if(HAL_GPIO_ReadPin(p_I2C->ICGO.sda_port, p_I2C->ICGO.sda_pin) == GPIO_PIN_SET) //判定:SDA为高
{
*p_data += 1;
}
I2CResetSCL(p_I2C); //拉低:SCL
}
return I2CResult_OK;
}
3.4、接口函数
“2.4、接口函数声明”的函数主体。
/*
* 功能:初始化I2C
* 输入:@p_I2C:I2C结构体指针
* @SetI2C:设置参数结构体
* 输出:无
* 备注:
*/
void I2CInit(I2C_Typedef* p_I2C,I2C_Typedef SetI2C)
{
*p_I2C = SetI2C;
I2CSetSDA(p_I2C); //拉高:SDA
I2CSetSCL(p_I2C); //拉高:SCL
}
#if DataAddressSizeof == 1
/*
* 功能:I2C写数据
* 输入:@p_I2C:I2C结构体指针
* @address:写入地址
* @p_data:写入数据缓存
* @size:写入数据大小
* 输出:I2CResult_OK:写入成功
* I2CResult_ERROR:写入失败
* 备注:
*/
I2CResult_Typedef I2CWriteData(I2C_Typedef* p_I2C,uint8_t address,uint8_t* p_data,uint8_t size)
{
uint8_t i = 0;
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.write); //执行:写入I2C写指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address); //执行:写入地址
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
for(i = 0;i < size;i ++)
{
I2CWriteByte(p_I2C,*(p_data + i)); //执行:写入1个字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
}
I2CStop(p_I2C); //执行:停止I2C
return I2CResult_OK;
}
/*
* 功能:I2C读数据
* 输入:@p_I2C:I2C结构体指针
* @address:读取地址
* @p_data:读取数据缓存
* @size:读取数据大小
* 输出:I2CResult_OK:读取成功
* I2CResult_ERROR:读取失败
* 备注:
*/
I2CResult_Typedef I2CReadData(I2C_Typedef* p_I2C,uint8_t address,uint8_t* p_data,uint8_t size)
{
uint8_t i = 0;
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.write); //执行:写入I2C写指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address); //执行:写入地址高字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.read); //执行:写入I2C读指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C应答
{
return I2CResult_ERROR;
}
for(i = 0;i < size;i ++)
{
I2CReadByte(p_I2C,(p_data + i)); //执行:读取1个字节
if(i < (size - 1)) //判定:未读取完成
{
I2CSendAck(p_I2C); //执行:正应答
}
else
{
I2CSendNotAck(p_I2C); //执行:负应答
}
}
I2CStop(p_I2C); //执行:停止I2C
return I2CResult_OK;
}
#elif DataAddressSizeof == 2
/*
* 功能:I2C写数据
* 输入:@p_I2C:I2C结构体指针
* @address:写入地址
* @p_data:写入数据缓存
* @size:写入数据大小
* 输出:I2CResult_OK:写入成功
* I2CResult_ERROR:写入失败
* 备注:
*/
I2CResult_Typedef I2CWriteData(I2C_Typedef* p_I2C,uint16_t address,uint8_t* p_data,uint16_t size)
{
uint16_t i = 0;
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.write); //执行:写入I2C写指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address / 256); //执行:写入地址高字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address % 256); //执行:写入地址低字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
for(i = 0;i < size;i ++)
{
I2CWriteByte(p_I2C,*(p_data + i)); //执行:写入1个字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
}
I2CStop(p_I2C); //执行:停止I2C
return I2CResult_OK;
}
/*
* 功能:I2C读数据
* 输入:@p_I2C:I2C结构体指针
* @address:读取地址
* @p_data:读取数据缓存
* @size:读取数据大小
* 输出:I2CResult_OK:读取成功
* I2CResult_ERROR:读取失败
* 备注:
*/
I2CResult_Typedef I2CReadData(I2C_Typedef* p_I2C,uint16_t address,uint8_t* p_data,uint16_t size)
{
uint16_t i = 0;
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.write); //执行:写入I2C写指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address / 256); //执行:写入地址高字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CWriteByte(p_I2C,address % 256); //执行:写入地址低字节
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C非应答
{
return I2CResult_ERROR;
}
I2CStart(p_I2C); //执行:开启I2C
I2CWriteByte(p_I2C,p_I2C->ICCB.read); //执行:写入I2C读指令
if(I2CWaitAck(p_I2C) != I2CResult_OK) //判定:I2C应答
{
return I2CResult_ERROR;
}
for(i = 0;i < size;i ++)
{
I2CReadByte(p_I2C,(p_data + i)); //执行:读取1个字节
if(i < (size - 1)) //判定:未读取完成
{
I2CSendAck(p_I2C); //执行:正应答
}
else
{
I2CSendNotAck(p_I2C); //执行:负应答
}
}
I2CStop(p_I2C); //执行:停止I2C
return I2CResult_OK;
}
#endif