调试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; // 返回读取的数据
}