模拟IIc通讯失败解决办法&&通讯线长度问题

调试SQ52205的模拟IIC通信记录

——————————————————————————————————————————

先说结论:

问题:IIC通讯失败,IIC从机未有ACK回应

问题点:IIC通讯线过长

解决办法:剪短IIC从通讯线

——————————————————————————————————————————

1.可以直接调试:将口拉高拉低

如果可以正常拉高拉低,说明硬件上IIC需要的上拉电阻没有问题+配置的引脚没有问题

如果不正常:

(1)检查模拟IIC引脚是否配置有问题
(2)检查模拟IIC是否正常初始化

可以调用一下函数直接操作GPIO观察示波器SDA和SCL的波形

 gpio_bit_set(GPIO_I2C3_SCL_GPIO_PORT, GPIO_I2C3_SCL_PIN);
 gpio_bit_set(GPIO_I2C3_SDA_GPIO_PORT, GPIO_I2C3_SDA_PIN);
 gpio_bit_reset(GPIO_I2C3_SCL_GPIO_PORT, GPIO_I2C3_SCL_PIN);
 gpio_bit_reset(GPIO_I2C3_SDA_GPIO_PORT, GPIO_I2C3_SDA_PIN);

在经过模拟IIC读取芯片的数据过程后,blRet的返回值时FALSE,直接进入程序的return FALSE步骤

BOOL SQ52205_GetCurrent(P_U16 pu16ReadData)
{
    U8 u8ReadBuf[2] = {0};
    U16 u16ReadData = 0 ;
    BOOL blRet = FALSE;

    blRet = HAL_SQ52205_READ_ARRAY(SQ52205_CURRENT, u8ReadBuf, 2);

    if(blRet == FALSE)
    {
        return FALSE;
    }
    
    //电流计算方法   (04h寄存器的十进制)* LSB (mA) = A      LSB = 0.00512/(05h的十进制)/分流器阻值(Ω)

    u16ReadData = u8ReadBuf[0]<<8 | u8ReadBuf[1];

    *pu16ReadData = u16ReadData  * SQ52205_LSB;      //单位0.1A

    return TRUE;
}
2.KEIL代码调试分析
(1)进入失败的语句:HAL_SQ52205_READ_ARRAY

blRet = HAL_SQ52205_READ_ARRAY(SQ52205_CURRENT, u8ReadBuf, 2)

BOOL HAL_SQ52205_READ_ARRAY(U16 u16RegAddr, U8* pu8ReadData, U8 u8RdLen)
{
    I2C_Status enuRet = I2C_FAIL;

    xSemaphoreTake(g_GPIO_I2C3_MutexSemaphore, portMAX_DELAY);
        enuRet = GPIO_I2C_READ(GPIO_I2C_3, SQ52205_ADDRESS, u16RegAddr, 0, pu8ReadData, u8RdLen);
    xSemaphoreGive(g_GPIO_I2C3_MutexSemaphore);

    if(I2C_FAIL == enuRet)
    {
        return FALSE;
    }

    return TRUE;
}

发现在GPIO_I2C_READ这里就会失败

(2)进入GPIO_I2C_READ

GPIO_I2C_READ(GPIO_I2C_3, SQ52205_ADDRESS, u16RegAddr, 0, pu8ReadData, u8RdLen);

I2C_Status GPIO_I2C_READ(uint32_t I2Cx, uint8_t addr, uint16_t start_Addr, uint8_t Addr_Length, uint8_t *buf, uint8_t Data_Length)
{
    int i = 0;
    GPIO_I2C_START(I2Cx);  
    GPIO_I2C_SEND_BYTE(I2Cx, addr);       
    if(I2C_FAIL == GPIO_I2C_WAIT_ACK(I2Cx))
    {
        return I2C_FAIL;
    }

    if(2 == Addr_Length)//Á½×Ö½ÚµØÖ·
    {
        GPIO_I2C_SEND_BYTE(I2Cx, (uint8_t)((start_Addr & 0xFF00) >> 8));
        if(I2C_FAIL == GPIO_I2C_WAIT_ACK(I2Cx))
        {
            return I2C_FAIL;
        }    

        GPIO_I2C_SEND_BYTE(I2Cx, (uint8_t)(start_Addr & 0x00FF));
        if(I2C_FAIL == GPIO_I2C_WAIT_ACK(I2Cx))
        {
            return I2C_FAIL;
        }    
    }

发现在GPIO_I2C_WAIT_ACK这里就会失败

分析原因是:

(1)MCU的查询报文未正常发送过去

(2)采集芯片未作正常的回应

3.示波器采集

IIC通讯示意图:

示波器采集思路:

(1)连接MCU端进行SCL和SDA波形采集

图形分析:

已经正常发送地址信号0x80,然后由于从机未回应ACK信号,就出现绿色框的等待时间,最终等待超时出现STOP信号

(2)连接采集芯片端进行SCL和SDA波形采集

图形分析:

有起始信号、正常发送地址信号0x80,从机回应ACK信号,接着发出地址数据信号,波形正常

(3)综合分析

两侧波形不一致,怀疑中间信号传递出现丢失

先怀疑的隔离芯片,但是测试发现正常,经过大佬指点排查,发现是测试台架IIC通讯线束过长【大约2米】,将线束改短之后通讯正常

————————————————————————————————————————

4.附录:模拟IIC代码
#include <stdio.h>
#include <stdbool.h>
#include "stm32f10x.h"

// 定义IIC总线的SDA和SCL引脚
#define IIC_SCL_Pin GPIO_Pin_10
#define IIC_SDA_Pin GPIO_Pin_11
#define IIC_GPIO GPIOB

// 定义IIC总线的时钟延迟时间
#define IIC_Delay_Time 5

// 初始化IIC总线
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟

    GPIO_InitStructure.GPIO_Pin = IIC_SCL_Pin | IIC_SDA_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 配置GPIO为开漏输出模式
    GPIO_Init(IIC_GPIO, &GPIO_InitStructure);

    GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin | IIC_SDA_Pin); // 初始化SCL和SDA为高电平
}

// 发送IIC总线的起始信号
void IIC_Start(void)
{
    GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin | IIC_SDA_Pin); // 先将SCL和SDA都设置为高电平
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_ResetBits(IIC_GPIO, IIC_SDA_Pin); // 将SDA拉低
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_ResetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉低
    Delay_us(IIC_Delay_Time); // 延时一段时间
}

// 发送IIC总线的停止信号
void IIC_Stop(void)
{
    GPIO_ResetBits(IIC_GPIO, IIC_SDA_Pin); // 先将SDA设置为低电平
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉高
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_SetBits(IIC_GPIO, IIC_SDA_Pin); // 将SDA拉高
    Delay_us(IIC_Delay_Time); // 延时一段时间
}

// 检测IIC总线的应答信号
bool IIC_CheckAck(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    bool ack;
  
    GPIO_InitStructure.GPIO_Pin = IIC_SDA_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 配置GPIO为上拉输入模式
    GPIO_Init(IIC_GPIO, &GPIO_InitStructure);

    GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉高
    Delay_us(IIC_Delay_Time); // 延时一段时间
    if (GPIO_ReadInputDataBit(IIC_GPIO, IIC_SDA_Pin)) {
        ack = false; // 如果SDA为高电平则表示无应答
    } else {
        ack = true; // 如果SDA为低电平则表示有应答
    }

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 配置GPIO为开漏输出模式
    GPIO_Init(IIC_GPIO, &GPIO_InitStructure);

    GPIO_ResetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉低
    Delay_us(IIC_Delay_Time); // 延时一段时间
    
    return ack;
}

// 发送IIC总线的一个字节数据
void IIC_WriteByte(uint8_t dat)
{
    uint8_t i;

    for (i = 0; i < 8; i++) {
        if (dat & 0x80) {
            GPIO_SetBits(IIC_GPIO, IIC_SDA_Pin); // 如果dat的最高位为1则将SDA拉高
        } else {
            GPIO_ResetBits(IIC_GPIO, IIC_SDA_Pin); // 如果dat的最高位为0则将SDA拉低
        }

        Delay_us(IIC_Delay_Time); // 延时一段时间
        GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉高
        Delay_us(IIC_Delay_Time); // 延时一段时间
        GPIO_ResetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉低
        Delay_us(IIC_Delay_Time); // 延时一段时间
        dat <<= 1;
    }
}

// 接收IIC总线的一个字节数据
uint8_t IIC_ReadByte(bool ack)
{
    uint8_t i, dat = 0;

    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = IIC_SDA_Pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 配置GPIO为上拉输入模式
    GPIO_Init(IIC_GPIO, &GPIO_InitStructure);

    for (i = 0; i < 8; i++) {
        dat <<= 1;
        GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉高
        Delay_us(IIC_Delay_Time); // 延时一段时间
        if (GPIO_ReadInputDataBit(IIC_GPIO, IIC_SDA_Pin)) {
            dat |= 0x01; // 如果SDA为高电平则将dat的最低位设为1
        }
        GPIO_ResetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉低
        Delay_us(IIC_Delay_Time); // 延时一段时间
    }

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 配置GPIO为开漏输出模式
    GPIO_Init(IIC_GPIO, &GPIO_InitStructure);

    if (ack) {
        GPIO_ResetBits(IIC_GPIO, IIC_SDA_Pin); // 如果需要应答则将SDA拉低
    } else {
        GPIO_SetBits(IIC_GPIO, IIC_SDA_Pin); // 如果不需要应答则将SDA拉高
    }
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_SetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉高
    Delay_us(IIC_Delay_Time); // 延时一段时间
    GPIO_ResetBits(IIC_GPIO, IIC_SCL_Pin); // 将SCL拉低
    Delay_us(IIC_Delay_Time); // 延时一段时间

    return dat; // 返回读取的数据
}

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值