STM32用GPIO模拟I2C主机程序库

该文详细介绍了如何使用STM32单片机的HAL库,通过GPIO模拟I2C主机时序来实现与从机设备的数据通信。文中定义了结构体用于配置I2C设备参数,包括时序设置、GPIO端口及引脚信息,并提供了I2C初始化、数据读写等函数的声明和实现。此外,还包含了等待应答、发送应答等I2C协议关键操作的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、基础信息

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洪恒远

感君意气无所惜,一为歌行歌主客

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值