[单片机框架][device层] fuelgauge 电量计
通过 DEVICE_INITCALL(“dev-gauge”, fuelgauge_dev_init); 注册驱动,自动在main初始化中运行。
优点:耦合少,可移植性强,适用大团队模块化开发
缺点:抽象度高,小团队开发时间耗时久
battery管理我觉得可以分开为两个部分,一个是电池监控(fuelgauge),另一个是充放电管理(charger),所以我们在内核中也是把它分成了两个驱动来管理。
电池监控(fuelgauge)驱动主要是负责向上层android系统提供当前电池的电量以及健康状态信息等等,另外除了这个以外,它也向charger驱动提供电池的相关信息;
充放电管理(charger)驱动主要负责电源线的插拔检测,以及充放电的过程管理。对于battery管理,硬件上有电量计IC和充放电IC。
对比SW_FG 和HW_FG在硬件及软件上的部分差异,分析电量误差形成的一些原因和已经采取的消除误差的措施。这个算法的思路是这样的:
最终通过开路电压oam_v_ocv_1查ZCV表得到当前的电量值 ->
开路电压需要通过闭路电压v_bat 和 闭路电流oam_i_2 去回溯电池内阻逐次逼近 –>
oam_i_2 通过 另一种方式 电量积分更新的电压oam_v_ocv_2.
通过fuelgauge_drv_match_ids结构体可以同时适配多种电量计
软电量计具体过程:硬件ADC读取Battery的各路信息:包括温度,电压等。
开发的电量算法分析得到的数据。
Kernel层将电量信息通过写文件节点的方式更新,并通过UEVENT通知上层。
上层Service开启UEVENT LISTENER,监听到UEVENT后,读取battery相关文件节点,获取电量信息。
Service更新数据后,通过Broadcast通知所有开启了相关listener的activities.
硬件电量计:采用cw2015,导入电池模型,进行积分累计,每次充放电都能消除部分误差。±3%剩余电量误差,内部14-bit模数转换器进行温度和电压检测,无需检流电阻,无库伦计累积误差, 无学习周期,低电量警示中断
驱动初始化代码:
#include "typedefs.h"
#include "log.h"
#include "modules.h"
extern initcall_t __initcall_start;
extern initcall_t __initcall_end;
void modules_setup(void) {
initcall_t *call;
for (call = &__initcall_start; call < &__initcall_end; call++) {
// LOG_C("[modules]:%s init\r\n", call->name);
call->initcall();
}
}
/**
* @brief Main function.
*/
int main(void) {
hardware_init();
modules_setup(); // 将驱动、设备、app层依次初始化
LOG_C("[main]:kernel start...\r\n");
start_kernel();
while (1) {
}
}
驱动代码:
fuelgauge_dev.c
#include <string.h>
#include "typedefs.h"
#include "errorno.h"
#include "modules.h"
#include "log.h"
#include "fuelgauge_dev.h"
static char* fuelgauge_drv_match_ids[] = {
"cw2015", // 硬件电量计
"gauge-adc", // 软件电量计
};
static int32_t fuelgauge_dev_open(device_t *dev)
{
uint8_t i = 0;
fuelgauge_driver_t *pdrv_gauge;
if (dev == NULL)
{
return RETVAL(E_NULL);
}
if (dev->dev_drv != NULL)
{
return RETVAL(E_OK);
}
for (i=0; i<ARRAY_SIZE(fuelgauge_drv_match_ids); i++)
{
pdrv_gauge = fuelgauge_driver_find(fuelgauge_drv_match_ids[i]);
if (pdrv_gauge)
{
if (fuelgauge_driver_probe(pdrv_gauge) == RETVAL(E_OK))
{
dev->dev_drv = (void*)pdrv_gauge;
fuelgauge_driver_init((fuelgauge_driver_t*)dev->dev_drv);
return RETVAL(E_OK);
}
}
}
return RETVAL(E_FAIL);
}
static int32_t fuelgauge_dev_ioctl(device_t *dev, uint32_t cmd, void *args)
{
fuelgauge_cmd_argv_t *cmd_argv = args;
switch (cmd_argv->cmd)
{
case FUELGAUGE_IOCTRL_CMD_WORK_MODE:
{
fuelgauge_driver_set_work_mode((fuelgauge_driver_t*)dev->dev_drv, (fuelgauge_work_mode_t)cmd_argv->argv);
}
break;
case FUELGAUGE_IOCTRL_CMD_CHARGE_STS:
{
fuelgauge_driver_set_charge_state((fuelgauge_driver_t*)dev->dev_drv, (fuelgauge_charge_state_t)cmd_argv->argv, 0);
}
default:
break;
}
return RETVAL(E_OK);
}
static int32_t fuelgauge_dev_read(device_t *dev, char *buffer, xsize_t size, xloff_t *pos)
{
int32_t ret;
ret = fuelgauge_driver_get_info((fuelgauge_driver_t*)dev->dev_drv, (fuelgauge_info_t*)buffer);
if (ret < 0)
{
LOG_E("[fuelgauge_dev][read]:get_info failed ret=%d\r\n", ret);
}
return RETVAL(E_OK);
}
const device_ops_t fuelgauge_dev_ops =
{
/* (*open) */ fuelgauge_dev_open,
/* (*close) */ NULL,
/* (*read) */ fuelgauge_dev_read,
/* (*write) */ NULL,
/* (*ioctl) */ fuelgauge_dev_ioctl,
};
static void fuelgauge_dev_init(void)
{
static device_t fuelgauge_dev;
int32_t ret;
memset(&fuelgauge_dev, 0, sizeof(device_t));
fuelgauge_dev.type = DEVICE_CLASS_CHAR;
fuelgauge_dev.dev_ops = &fuelgauge_dev_ops;
ret = device_register(&fuelgauge_dev, "dev-gauge", DEVICE_FLAG_WRONLY);
if (ret < 0)
{
LOG_E("[fuelgauge_dev][init]:register failed ret=%d\r\n", ret);
}
}
DEVICE_INITCALL("dev-gauge", fuelgauge_dev_init);
fuelgauge_dev.h
#ifndef _FUELGAUGE_DEV_H_
#define _FUELGAUGE_DEV_H_
#include "../../object/device.h"
#include "fuelgauge.h"
#endif // _FUELGAUGE_DEV_H_