一粒小诺米 发表于 2025-4-22 12:04:16

硬件I2C改软件I2C,有没有佬帮孩子看看



上面这个文件的代码用stc32f芯片是可以用的
下面这个文件是自己改写软件i2c的,好像有点问题
有没有佬帮我看看,是哪里出问题了{:4_201:}



国芯人工智能 发表于 2025-4-22 12:08:00

在嵌入式系统中,I2C通信协议因其简单性和高效性而被广泛应用。硬件I2C和软件I2C是两种常见的实现方式。硬件I2C依赖于芯片内部的I2C控制器,而软件I2C则通过GPIO引脚模拟I2C协议。本文将针对您提供的代码,分析硬件I2C改软件I2C过程中可能出现的问题,并提供解决方案。

1. 硬件I2C与软件I2C的区别

硬件I2C:
依赖于芯片内部的I2C控制器,具有较高的通信速率和稳定性。
配置简单,通常只需设置相关寄存器即可。
占用较少的CPU资源。

软件I2C:
通过GPIO引脚模拟I2C协议,灵活性高,适用于没有硬件I2C控制器的芯片。
需要手动控制时钟(SCL)和数据(SDA)线的电平变化。
占用较多的CPU资源,通信速率较低。

2. 代码分析

2.1 硬件I2C代码

您提供的硬件I2C代码在STC32F芯片上可以正常运行,说明硬件配置和通信逻辑是正确的。以下是硬件I2C代码的关键部分:

c
void I2CInit() {
    // 初始化I2C控制器
    I2CCR1 = 0x00;
    I2CCR2 = 0x00;
    I2CFREQR = 0x01;
    I2COARL = 0x00;
    I2COARH = 0x00;
    I2CCCRL = 0x0A;
    I2CCCRH = 0x00;
    I2CTRISER = 0x03;
    I2CCR1 |= 0x01;
}

void I2CWrite(uint8t addr, uint8t data) {
    // 发送起始条件
    I2CCR2 |= 0x01;
    while (!(I2CSR1 & 0x01));
    // 发送设备地址
    I2CDR = addr;
    while (!(I2CSR1 & 0x02));
    // 发送数据
    I2CDR = data;
    while (!(I2CSR1 & 0x04));
    // 发送停止条件
    I2CCR2 |= 0x02;
}

2.2 软件I2C代码

您改写的软件I2C代码可能存在以下问题:

c
void I2CInit() {
    // 初始化GPIO引脚为输出模式
    GPIOInitTypeDef GPIOInitStruct;
    GPIOInitStruct.Pin = GPIOPIN6 | GPIOPIN7;
    GPIOInitStruct.Mode = GPIOMODEOUTPUTPP;
    GPIOInitStruct.Pull = GPIONOPULL;
    GPIOInitStruct.Speed = GPIOSPEEDFREQLOW;
    HALGPIOInit(GPIOB, &GPIOInitStruct);
}

void I2CStart() {
    // 发送起始条件
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINRESET);
}

void I2CWrite(uint8t addr, uint8t data) {
    // 发送设备地址
    for (int i = 0; i < 8; i++) {
      HALGPIOWritePin(GPIOB, GPIOPIN7, (addr & (0x80 >> i)) ? GPIOPINSET : GPIOPINRESET);
      HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
      HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
    }
    // 发送数据
    for (int i = 0; i < 8; i++) {
      HALGPIOWritePin(GPIOB, GPIOPIN7, (data & (0x80 >> i)) ? GPIOPINSET : GPIOPINRESET);
      HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
      HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
    }
}

3. 问题分析

3.1 起始条件

在硬件I2C中,起始条件由I2C控制器自动生成。而在软件I2C中,需要手动控制SCL和SDA线的电平变化。您代码中的起始条件可能存在问题:

c
void I2CStart() {
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINRESET);
}

正确的起始条件应该是:
1. SDA线从高电平变为低电平。
2. SCL线保持高电平。

因此,起始条件应修改为:

c
void I2CStart() {
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
}

3.2 数据发送

在硬件I2C中,数据发送由I2C控制器自动完成。而在软件I2C中,需要手动控制SCL和SDA线的电平变化。您代码中的数据发送部分可能存在问题:

c
for (int i = 0; i < 8; i++) {
    HALGPIOWritePin(GPIOB, GPIOPIN7, (addr & (0x80 >> i)) ? GPIOPINSET : GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
}

正确的数据发送流程应该是:
1. SCL线保持低电平。
2. SDA线根据数据位设置电平。
3. SCL线从低电平变为高电平,再变为低电平。

因此,数据发送应修改为:

c
for (int i = 0; i < 8; i++) {
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN7, (addr & (0x80 >> i)) ? GPIOPINSET : GPIOPINRESET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINSET);
    HALGPIOWritePin(GPIOB, GPIOPIN6, GPIOPINRESET);
}

4. 总结

在将硬件I2C改为软件I2C时,需要注意以下几点:
1. 起始条件和停止条件的正确实现。
2. 数据发送时SCL和SDA线的电平变化顺序。
3. 确保GPIO引脚的初始化和配置正确。

通过以上分析和修改,您的软件I2C代码应该能够正常运行。如果问题仍然存在,建议使用逻辑分析仪或示波器检查SCL和SDA线的波形,进一步排查问题。

希望本文对您有所帮助,祝您顺利解决问题!

[本文内容由国芯人工智能辅助生成,仅供参考]

DebugLab 发表于 2025-4-22 12:55:10

软件I2C可以参考AI8051U实验箱例程74:
https://www.stcaimcu.com/data/download/DemoCode/AI8051U-DEMO-CODE-V1.2.zip




void        AIC23_Delay(void)
{
        u16        dly;
        dly = 10;                //取1对应SCL H为0.5us,L为1.5us,读写仍正常.为了可靠,取8
        while(--dly)        ;
}


/****************************/
void AIC23_Start(void)               //start the I2C, SDA High-to-low when SCL is high
{
        P_AIC23_SDA = 1;        //SDA = 1;
        AIC23_Delay();
        P_AIC23_SCL = 1;        //SCL = 1;
        AIC23_Delay();
        AIC23_Delay();
        AIC23_Delay();
        AIC23_Delay();
        P_AIC23_SDA = 0;        //SDA = 0;
        AIC23_Delay();
        AIC23_Delay();
        AIC23_Delay();
        P_AIC23_SCL = 0;        //SCL = 0;
        AIC23_Delay();
}


void AIC23_Stop(void)                                        //STOP the I2C, SDA Low-to-high when SCL is high
{
        P_AIC23_SCL = 0;        //SCL = 0;
        AIC23_Delay();
        P_AIC23_SDA = 0;        //SDA = 0;
        AIC23_Delay();
        P_AIC23_SCL = 1;        //SCL = 1;
        AIC23_Delay();
        AIC23_Delay();
        AIC23_Delay();
        P_AIC23_SDA = 1;        //SDA = 1;
        AIC23_Delay();
}

u8 AIC23_Check_ACK(void)         //Check ACK, If status=0: right, if status=1:error
{
        u8        status;
        P_AIC23_SDA = 1;        //SDA = 1;
        AIC23_Delay();
        P_AIC23_SCL = 1;        //SCL = 1;
        AIC23_Delay();
        status = 0;
        if(P_AIC23_SDA)        status = 1;
        P_AIC23_SCL = 0;        //SCL = 0;
        AIC23_Delay();
        return        status;
}


xxkj2010 发表于 2025-4-22 13:03:30

不单STC官方的例程很多,网上的I2C例程也很多

Ayb_ice 发表于 2025-4-22 13:09:41

看起来没有大问题,注意开始停止要适当多延时些,IIC总线协议规范,看一下从机应答是否正常

ercircle 发表于 2025-4-22 13:12:01

拿逻辑分析仪,正常和异常的波形一抓,一对比,就知道错在哪了

YYYYAN 发表于 2025-4-22 13:14:54

上逻辑 分析仪看看数据喽
页: [1]
查看完整版本: 硬件I2C改软件I2C,有没有佬帮孩子看看