【MPS探索营+创意实现】+多功能雨伞手柄05调试

上个帖子中介绍了PCB设计过程,接下来介绍PCB的调试过程,重点说一下MP2651的程序调试过程。
焊接完成后的PCBA如图1,首先进行常规检查,确保整个基板没有短路现象。确认无误后进行上电测试,使用5V USB供电测试,测量输出电压值为8.4V,MP2651默认配置是两节电池,所以默认输出电压是8.4V是OK的,如图2。图3是规格书截图,由于此时MP2651的输入电压是5V,所以MP2651工作在Boost模式。
我的设计是用单节电池供电,所以首先需要更改MP2651的电池数量配置。根据以往经验,ST官方自带的I2C驱动库不是很好用,尤其是针对私有协议,调试起来很麻烦,所以我直接使用IO口模拟I2C总线,这样自由度比较高。正好论坛里面已经有人发了MP2731的驱动程序代码,我就直接在这个代码基础上进行修改。
此部分代码如下:
//i2c.c

#include “i2c/bsp_i2c.h”

static void i2c_Delay(void)
{
uint8_t i;
for (i = 0; i < 4; i++);
}
//开始信号
void i2c_Start(void)
{
// 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}

//停止信号
void i2c_Stop(void)
{
// 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号
I2C_SDA_0();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();
}
//发送一个字节
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;

// 先发送字节的高位bit7 
for (i = 0; i < 8; i++)
{
	if (_ucByte & 0x80)
	{
		I2C_SDA_1();
	}
	else
	{
		I2C_SDA_0();
	}
	i2c_Delay();
	I2C_SCL_1();
	i2c_Delay();
	I2C_SCL_0();
	if (i == 7)
	{
		 I2C_SDA_1(); 	// 释放总线
	}
	_ucByte <<= 1;		// 左移一个bit 
	i2c_Delay();
}

}
//读取一个字节
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;

/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
	value <<= 1;
	I2C_SCL_1();
	i2c_Delay();
	if (I2C_SDA_READ())
	{
		value++;
	}
	I2C_SCL_0();
	i2c_Delay();
}
return value;

}

//等待应答信号
uint8_t i2c_WaitAck(void)
{
uint8_t re;

I2C_SDA_1();	/* CPU释放SDA总线 */

// i2c_Delay();
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay();

if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
{
	re = 1;
}
else
{
	re = 0;
}

I2C_SCL_0();
i2c_Delay();
return re;

}
//应答信号
void i2c_Ack(void)
{
I2C_SDA_0(); /* CPU驱动SDA = 0 /
i2c_Delay();
I2C_SCL_1(); /
CPU产生1个时钟 /
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /
CPU释放SDA总线 /
}
void i2c_Nack(void)
{
I2C_SDA_1(); /
CPU驱动SDA = 1 /
i2c_Delay();
I2C_SCL_1(); /
CPU产生1个时钟 /
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /
CPU释放SDA总线 */
}

uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;

if (I2C_SDA_READ() && I2C_SCL_READ())
{
	i2c_Start();		/* 发送启动信号 */

	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
	i2c_SendByte(_Address | 0);
	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */

	i2c_Stop();			/* 发送停止信号 */

	return ucAck;
}
return 1;	/* I2C总线异常 */

}
//MP2731
uint8_t Device_ReadData(uint8_t DeciveAddr,uint8_t RegAddr,uint8_t *Data,int size)
{
uint8_t ucAck;

int count=size;
	uint8_t *pData=Data;
	//起始信号
i2c_Start();    
//发送器件地址                         
i2c_SendByte(DeciveAddr<<1);       
//等待应答
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_SendByte(RegAddr);
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_Start(); 
i2c_SendByte((DeciveAddr<<1)+1); 
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
while(count--)
	{
		//发送数据
		*pData++ = i2c_ReadByte();   
        if(count)
        {
           i2c_Ack(); 
        }
        else
        {
            i2c_Nack(); //end nack
        }
	}			 
                   
//结束信号
i2c_Stop(); 
return ucAck; 

}
//MP2731
uint8_t Device_WriteData(uint8_t DeciveAddr,uint8_t RegAddr,uint8_t *Data,int size)
{
uint8_t ucAck;

int count=size;
	uint8_t *pData=Data;
	//起始信号
i2c_Start();    
//发送器件地址                         
i2c_SendByte(DeciveAddr<<1);  //     
//等待应答
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_SendByte(RegAddr);       //
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;

while(count--)
	{
		//发送数据
		i2c_SendByte(*pData++);                     
		//等待应答
        ucAck = i2c_WaitAck(); 
        if(ucAck)  return ucAck;   
	}			 
                   
//结束信号
i2c_Stop(); 
return ucAck; 

}

/////////////////////////////////////////////////////////////////
//MP2651
uint8_t Device_SingleWordRead(uint8_t DeciveAddr,uint8_t RegAddr,uint16_t *Data)
{
uint8_t ucAck;

int count=2;
uint8_t pData[2] = {0};

	//起始信号
i2c_Start();    
//发送器件地址                         
i2c_SendByte(DeciveAddr<<1);       
//等待应答
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_SendByte(RegAddr);
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_Start(); 
i2c_SendByte((DeciveAddr<<1)+1); 
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
while(count--)
	{
		//接收数据
		pData[count] = i2c_ReadByte();   
        if(count)
        {
           i2c_Ack(); 
        }
        else
        {
            i2c_Nack(); //end nack
        }
	}			 
                   
//结束信号
i2c_Stop(); 
*Data = pData[1] + pData[0]*256;
return ucAck; 

}
//MP2651
uint8_t Device_SingeWordWrite(uint8_t DeciveAddr,uint8_t RegAddr,uint16_t *Data)
{
uint8_t ucAck;

int count=2;
uint8_t pData[2] = {0};

pData[1] = *Data % 256;
pData[0] = *Data / 256;
	//起始信号
i2c_Start();    
//发送器件地址                         
i2c_SendByte(DeciveAddr<<1);  //     
//等待应答
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;
i2c_SendByte(RegAddr);       //
ucAck = i2c_WaitAck(); 
if(ucAck)  return ucAck;

while(count--)
	{
		//发送数据
		i2c_SendByte(pData[count]);                     
		//等待应答
        ucAck = i2c_WaitAck(); 
        if(ucAck)  return ucAck;   
	}			 
                   
//结束信号
i2c_Stop(); 
return ucAck;  

}

经过反复尝试,成功实现了MP2651的读写操作。读取代码如下:
temp = Device_SingleWordRead(I2C_ADDRESS,0x10,&rbuf[0]);//读取电池数量设置
temp = Device_SingleWordRead(I2C_ADDRESS,0x15,&rbuf[1]);//读取电池限压设置
temp = Device_SingleWordRead(I2C_ADDRESS,0x16,&rbuf[2]);//读取状态寄存器0

开始不好使还一度怀疑是芯片没焊好,费了不少功夫,这里要吐槽一下,官方设计了很齐全的辅助工具,却偏偏没有MP2651 I2C Evaluation GUI软件。如果有的话就可以先用这个软件和USB-I2C调试器确认硬件好坏,免得还需要各方验证到底是哪里的问题。图4是读取的部分寄存器值,在调试的时候直接显示在watch窗口。
根据我自己的设计需求,通过以下代码将配置修改为与我设计相符:单节电池,充电限制电压4.2V。代码如下:
uint16_t sbuf[5]={0x0840,0x1A40};

temp = Device_SingeWordWrite(I2C_ADDRESS,0x10,&sbuf[0]);//写入电池数量设置

temp = Device_SingeWordWrite(I2C_ADDRESS,0x15,&sbuf[1]);//写入电池限压设置

写完后读取到的寄存器数值如图5。验证写入成功。

用万用表测量,输出电压为4.2V左右,芯片配置成功,如图6。

到此为止,MP2651的驱动部分就调试完成,不过我发现一个问题,MP2651掉电后设置的配置信息都复位了,下次上电还写重新写一遍,这个比较麻烦,不知道是不是必须出厂时用OTP方式固化好才行。

接下来按照预期需求把DS18B20、发光管、电加热元件焊上,简单调下程序就实现了相关功能。这部分都是以前做过的比较简单,不再赘述。

整个作品调试完如图7所示,基本功能在一个名片大小的板上都实现了,这个做的是单面布件的PCB,如果对体积有要求,还可以双面布件做的更小一些。

通过的本次创意实现,在了解了MP2651这个芯片的强大功能之后,可以看出,对于大多数基于锂电池的应用场景,一片MP2651加一片最简单配置的MCU就可以满足基本的需求。同时,MP2651自带的各种电压、电流、温度等监测功能,既能节省MCU资源,还高度集成,节省PCB空间,提高可靠性,是一个不可多得多功能充电IC。
最后感谢MPS和EEWORLD组织的这次活动,希望以后举办更多类似的活动,让广大工程师了解新技术,有更多方案可以选择。

2 个赞

您好,感谢分享,非常精彩。
关于MP2651的寄存器配置,MP2651是OTP芯片,即寄存器内容只能在出厂时做一次烧录固化,如需个性化配置需要联系MPS FAE生成专属料号尾缀。
或者每次使用前在上电时对芯片寄存器按照需求,使用上位机通过I2C接口做初始化配置。

1 个赞

如果使用的是非2节电池的情况,每次上电再配置,会不会在上电瞬间,MCU配置之前电压不对,烧掉芯片?

您好,MP2651本身的pin脚耐压都是支持到4节电池的,OTP配置为2节电池应用场景下如果在BATT端接入3节串联或4节串联电池,引起的只会是工作状态报错(如BATT OV),并不存在烧芯片的问题,除非强行将芯片应用在超规格的工况下。

如果接单节电池,芯片上电瞬间会输出充电电流吗?如果有电流会导致电池保护

您好,MP2651规格书有给出default BATT UVLO阈值:


单节电池电压最高4.2V或4.35V,小于两节电池配置下的BATT_UVLO阈值5V,并不会给电池充电。

1 个赞