前言
最近在做EC方面的项目,客户需求带电池的方案。EC芯片用的是ITE的IT5571。
本人刚毕业,对EC方面很多都不懂,以下内容是我在网上找的各种资料经过自己的理解所总结。如有错误请指正谢谢!
1.智能电池组成
智能电池的构造简单来说主要分为电芯(cell)、电池保护板(bms)、电池外壳这几部分。
电芯仅仅用来提供电压电流。几个电芯串联起来就是电池包(package)。若一个电芯的电压为3.7V,电池包为3个电芯串联,则电池包电压为3.7X3=11.1V。
电池保护板则负责电池的充放电管理以及和OS数据通信。
2.智能电池和EC以及OS之间通信
智能电池是一个SMBus设备,通过SMBus总线来进行数据传输。电池信息数据到操作系统的数据流向:SMART BATTERY->EC SMBUS->EC 62/66 PORT->BIOS->OS。既然智能电池是SMBus设备,那么要和智能电池通信就要知道其相对应的设备地址。以bq29330+bq20z70电池保护板为例,查询芯片手册可知bq20z70的从设备地址为0x16。
下图为bq20z70 datasheet中提到的SMBus设备地址:
3.智能电池与EC连接
下图为某智能电池的保护电路原理图。我们需要注意PRES、SMBC、SMBD这三个引脚。SMBC与SMBD和EC上的一组SMBus channel直接相连。
PRES(System Present Flag)脚的作用是用作EC检测电池是否插入的判断脚。PRES连接EC的GPIO脚,GPIO脚配置为输入模式,初始上拉为高电平,需要提前去抖。当智能电池接入座子,则EC检测到PRES脚拉低,在代码中通过判断得知电池的存在。
保护电路板原理图:
4.智能电池标准SBS命令
所有的智能电池都符合Smart Battery System(SBS)规范。在此规范中统一定义了智能电池标准的查询信息命令,这些标准命令是所有电池公用的。不同的电池除了标准的SBS命令以外还会有自己独有的命令,在手册中查询Extended SBS Commands来查看相对应功能。
下图为智能电池规范中截取的标准SBS命令:
通过命令获取电池所需充电电流和充电电压值,写入charger的寄存器中即可实现智能电池的智能充电。
下图为获取电池所需充电电流和充电电压命令:
下图为截取的部分bq20z70 Extended SBS Commands:
bq20z70生产厂商为德州仪器,在官网可以下载专门调试电池的软件bqstudio。不知道是不是型号太老的原因,bq20z70会被识别为bq40z50。
5.EC代码
首先在EC的H2RAM中将电池相关的寄存器写入。
关于H2RAM,在相关的文档中找到这段话:
载板 EC 应该启用 256 个字节的 H2RAM 窗口以供 MMIO 中的 BIOS 访问 0xFE41_0400~0xFE41_04FF。
在 ACPI OS 中,在收到 ACPI_ENABLE 0x86 EC 命令之后,载板 EC 固件必须公开以下必需的内存 MMIO,并可以选择公开 UCSI/WMI MMIO 以进行 BIOS 访问:
• 强制 128 字节只读 ELM_EC MMIO 区域 0xFE41_0400~0xFE41_047F。
• 强制 128 字节读/写 ELM_EC MMIO 区域 0xFE41_0480~0xFE41_04FF。
• 载板 EC 固件和软件可以使用 0xFE41_0000~0xFE41_3FFF 之间的任何未使用区域。 BIOS 将不会访问未定义的 EC MMIO 区域。
Oem_memory.h
//batt1--------------------------------------------------------------------
extern ECRegW xwBatt1Temperature ;//_at_ (MEM_RO_CONFIG+0x50);
extern ECRegW xwBatt1Voltage ;//_at_ (MEM_RO_CONFIG+0x52);
extern ECRegW xwBatt1Current ;//_at_ (MEM_RO_CONFIG+0x54);
extern ECRegW xwBatt1RemainingCapacity ;//_at_ (MEM_RO_CONFIG+0x56);
extern ECRegW xwBatt1FullChargeCapacity ;//_at_ (MEM_RO_CONFIG+0x58);
extern ECRegW xwBatt1DesignCapacity ;//_at_ (MEM_RO_CONFIG+0x5A);
extern ECRegW xwBatt1RelativeStateOfCharge ;//_at_ (MEM_RO_CONFIG+0x5C);
extern ECRegW xwBatt1ChargingCurrent ;//_at_ (MEM_RO_CONFIG+0x5E);
extern ECRegW xwBatt1ChargingVoltage ;//_at_ (MEM_RO_CONFIG+0x60);
extern XBITS_8 xbBatt1Status ;//_at_ (MEM_RO_CONFIG+0x62);
#define _xbBatt1Status xbBatt1Status.byte
#define Batt1_AC_Present xbBatt1Status.field.bit0
#define Batt1_Batt_Present xbBatt1Status.field.bit1
#define Batt1_FullCharge xbBatt1Status.field.bit2
#define Batt1_Chaging xbBatt1Status.field.bit3
#define Batt1_Discharging xbBatt1Status.field.bit4
#define Batt1_Critical_low xbBatt1Status.field.bit5
#define Batt1_Over_Temperature xbBatt1Status.field.bit6
#define Batt1_Lid_Open xbBatt1Status.field.bit7
//batt1--------------------------------------------------------------------
Oem_memory.c
//batt1-------------------------------------------------------------
ECRegW xwBatt1Temperature _at_ (MEM_RO_CONFIG+0x50);
ECRegW xwBatt1Voltage _at_ (MEM_RO_CONFIG+0x52);
ECRegW xwBatt1Current _at_ (MEM_RO_CONFIG+0x54);
ECRegW xwBatt1RemainingCapacity _at_ (MEM_RO_CONFIG+0x56);
ECRegW xwBatt1FullChargeCapacity _at_ (MEM_RO_CONFIG+0x58);
ECRegW xwBatt1DesignCapacity _at_ (MEM_RO_CONFIG+0x5A);
ECRegW xwBatt1RelativeStateOfCharge _at_ (MEM_RO_CONFIG+0x5C);
ECRegW xwBatt1ChargingCurrent _at_ (MEM_RO_CONFIG+0x5E);
ECRegW xwBatt1ChargingVoltage _at_ (MEM_RO_CONFIG+0x60);
XBITS_8 xbBatt1Status _at_ (MEM_RO_CONFIG+0x62);
//batt1-------------------------------------------------------------
在battery.h文件中定义SBS命令,电池地址和SMBuschannel。
Oem_battery.h
#ifndef OEM_BATTERY_H
#define OEM_BATTERY_H
#define Battery_Addr 0x16
#define Battery_Channel SMbusCh2
#define _SCIEVT_BATTERY 0x09 //battery Q EVENT
//****************************************
// SBS command list
//****************************************
#define _CMD_Temperature 0x08
#define _CMD_Voltage 0x09
#define _CMD_Current 0x0a
#define _CMD_RemainingCapacity 0x0f
#define _CMD_FullChargeCapacity 0x10
#define _CMD_DesignCapacity 0x18
#define _CMD_RelativeStateOfCharge 0x0d
#define _CMD_ChargingCurrent 0x14
#define _CMD_ChargingVoltage 0x15
extern void vBattery_Init(void);
extern void vBattery_GetInfo(void);
#endif
在Oem_memory.c文件中写查询信息函数。由于SMBus查询函数已经写好,只要填入相关参数调用即可。
#include <CORE_INCLUDE.H>
#include <OEM_INCLUDE.H>
int vCheck_BattPlugIn(void)
{
if(IS_MASK_CLEAR(GPDRA, BIT(0)) // 这里应检测pres对应脚是否位低电平,如果为低则表示电池接入
{
Batt1_Batt_Present = 1;
WriteBatterySCI_Buffer(_SCIEVT_BATTERY);
}
else
{
Batt1_Batt_Present = 0;
}
return Batt1_Batt_Present;
}
void vBattery_Init(void)
{
vCheck_BattPlugIn();
}
void vBattery_GetInfo(void)
{
if(Batt1_Batt_Present)
{
//函数参数分别为channel,protocol,addr,CMD,*var
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_Temperature,&xwBatt1Temperature,SMBus_NoPEC))
{
//读取电池温度
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_Voltage,&xwBatt1Voltage,SMBus_NoPEC))
{
//读取电压
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_Current,&xwBatt1Current,SMBus_NoPEC))
{
//读取电流
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_RemainingCapacity,&xwBatt1RemainingCapacity,SMBus_NoPEC))
{
//读取剩余容量
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_FullChargeCapacity,&xwBatt1FullChargeCapacity,SMBus_NoPEC))
{
//读取充满电容量
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_DesignCapacity,&xwBatt1DesignCapacity,SMBus_NoPEC))
{
//读取设计容量
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_RelativeStateOfCharge,&xwBatt1RelativeStateOfCharge,SMBus_NoPEC))
{
//读取相对容量
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_ChargingCurrent,&xwBatt1ChargingCurrent,SMBus_NoPEC))
{
//读取充电电流
}
if(bRWSMBus(Battery_Channel,SMbusRW,Battery_Addr,_CMD_ChargingVoltage,&xwBatt1ChargingVoltage,SMBus_NoPEC))
{
//读取充电电压
}
}
}
初始化SMBus主设备,在相关文档找到SMBus的操作频率:
在init文件中写入电池SMBus主设备初始化。
Oem_init.c
void Init_SMBus_Regs(void)
{
vSMBus_SW_Timing(_i2c_100k);
vSMBus_Master_Enable(_SMBusChC, _SMB2, _i2c_fix_100k, _FifoSize_256_Byte);//Battery
}
一切都写好,正当我沾沾自喜时,突然出现一个致命的问题,在系统下读取的电池温度,容量等等数值都是不正确的!用ITE专门的调试软件NewWinECU连接EC后,直接查看H2RAM中关于电池寄存器的值。发现数值读取出来怪怪的不对劲。琢磨了很久终于发现是高低位调换了所导致读数不正确!
正常情况下应该在代码中将高低位的数值调换一下,不过我选择了一个很投机取巧的方法(笑)将电池所用的这组channel中的数据寄存器高低位直接调换即可。
Oem_smbus.c
const sSMBus code asSMBus[]=
{
{ &HOCTL_C, &TRASLA_C, &HOCMD_C, &HOSTA_C, &D1REG_C, &D0REG_C, &HOBDB_C, &IER2, &ISR2, Int_SMBUS2, &PECERC_C},//用的这个channel接的电池,将数据寄存器高低位调换位置,即D1REG_C和D0REG_C
{ &HOCTL_D, &TRASLA_D, &HOCMD_D, &HOSTA_D, &D0REG_D, &D1REG_D, &HOBDB_D, &IER0, &ISR0, Int_SMBUS3, &PECERC_D},//这是没改的
};
大功告成。这次确实读数正确了! 关于电池充电方面后续研究。目前这样写是能够在windows下读取到电池的信息了。
6.关于各种电量指标理解
下图为在acpi config power interface spec文档中找到各种电量的示意图:
关于相对容量和绝对容量的计算方法:
Relative State Of Charge = Remaining Capacity/Full Charge Capacity
Absolute State Of Charge = Remaining Capacity/Design Capacity