当前位置: 首页 > news >正文

[单片机框架][drivers层][cw2015] fuelgauge 硬件电量计(二)

接上一篇:[单片机框架][device层] fuelgauge 电量计
[单片机框架][drivers层][cw2015] fuelgauge 硬件电量计(一)

本章是硬件电量计的使用方法,采用IIC通信。利用opt的方式操作函数读写。

在这里插入图片描述
在这里插入图片描述

I2C接口
CW2015 通过 I2C 接口进行通信。 I2C 是一种两线漏极开路接口,支持单个总线上的多个设备和主机。 一些 I2C 设备可以作为主设备或从设备,但是CW2015 只能作为从设备,只将总线拉低,从不驱动总线为高。
在标准模式下,I2C 总线上的数据可以以高达 100kbps 的速率传输。 还支持 I2C 快速模式 (400kbps) 或快速模式 plus (1 Mbps)。

设备地址
I2C 设备地址由 7 位从地址和 1 个读/写控制位组成。
CW2015 的地址固定在 0b1100010。 结合 R/W 位:
CW2015的读命令为0xC5;
CW2015 的写命令为 0xC4。

cw2015.c

#include <string.h>

#include "log.h"
#include "errorno.h"
#include "modules.h"
#include "battery/batt_model.h"
#include "sys_cmsis.h"
#include "bsp_i2c.h"

#include "fuelgauge.h"

#define CW2015_VOLTAGE_RESOLUTION       305/1000//0.305mV

#define REG_VERSION                     0x0
#define REG_VCELL                       0x2
#define REG_SOC                         0x4
#define REG_RRT_ALERT                   0x6
#define REG_CONFIG                      0x8
#define REG_MODE                        0xA
#define REG_BATINFO                     0x10

#define MODE_SLEEP_MASK                 (0x3<<6)
#define MODE_SLEEP                      (0x3<<6)
#define MODE_NORMAL                     (0x0<<6)
#define MODE_QUICK_START                (0x3<<4)
#define MODE_RESTART                    (0xf<<0)

#define CONFIG_UPDATE_FLG               (0x1<<1)

#define ATHD                            (0x0<<3)
#define ATHD_CLEAR_BIT_AREA             0x07

#define BATTERY_DOWN_MIN_CHANGE_SLEEP	1800
#define FUELGAGUE_INIT_RETRY_TIMES		3

typedef struct {
    uint8_t usb_plugin;
    uint32_t soc;
    uint32_t volt;
    uint8_t alt;
} batt_info_t;

batt_info_t batt_info;

i2c_transfer_t i2c2_transfer = {
    .bus = BSP_I2C_BUS2,
    .slave_addr = 0x62,
};

uint32_t allow_charger_always_zero = 0;
uint8_t if_quick_start = 0;
uint8_t reset_loop = 0;

static sys_timer_id_t g_gauge_timer_id;
static sys_timer_def_t g_gauge_timer;

static fuelgauge_info_t g_fg_info;
static fuelgauge_charge_state_t g_set_charge_state = FUELGAUGE_CHARGE_STATE_DISCHARGE;
static uint16_t g_set_current = 0;

static void fuelgague_cw2015_delay10us(volatile uint32_t us) {
    volatile uint32_t i;
    for (i = 0; i < us; i++);
}

static int32_t fuelgague_cw2015_cfg_batt_model(void) {
    int32_t ret = 0;
    uint8_t i;
    uint8_t reset_val;
    uint8_t reg_val;

    /* make sure no in sleep mode */
    ret = bsp_i2c_read(&i2c2_transfer, REG_MODE, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    if ((reg_val & MODE_SLEEP_MASK) == MODE_SLEEP)
        return RETVAL(E_STATE);
    /* update new battery info */
    for (i = 0; i < SIZE_BATINFO; i++) {
        reg_val = batt_model[i];
        ret = bsp_i2c_write(&i2c2_transfer, REG_BATINFO+i, &reg_val, 1);
        if (ret == RETVAL(E_BUS))
            return RETVAL(E_BUS);
    }
    /* readback & check */
    for (i = 0; i < SIZE_BATINFO; i++) {
        ret = bsp_i2c_read(&i2c2_transfer, REG_BATINFO + i, &reg_val, 1);
        if (ret == RETVAL(E_BUS))
            return RETVAL(E_BUS);
        if (reg_val != batt_model[i])
            return RETVAL(E_SET);
    }
    /* set cw2015/cw2013 to use new battery info */
    ret = bsp_i2c_read(&i2c2_transfer, REG_CONFIG, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    reg_val |= CONFIG_UPDATE_FLG;   /* set UPDATE_FLAG */
    reg_val &= ATHD_CLEAR_BIT_AREA; /* clear ATHD */
    reg_val |= ATHD;                /* set ATHD */
    ret = bsp_i2c_write(&i2c2_transfer, REG_CONFIG, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    /* reset */
    reset_val = MODE_NORMAL;
    reg_val = MODE_RESTART;
    ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    fuelgague_cw2015_delay10us(1000);
    ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reset_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    fuelgague_cw2015_delay10us(1000);
    return RETVAL(E_OK);
}

static int32_t gague_cw2015_cfg(void) {
    int32_t ret;
    uint8_t i;
    uint8_t reg_val = MODE_NORMAL;

    /* wake up cw2015/13 from sleep mode */
    ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    /* check ATHD if not right */
    ret = bsp_i2c_read(&i2c2_transfer, REG_CONFIG, &reg_val, 1);//
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    if ((reg_val & 0xf8) != ATHD) {
        /* the new ATHD need set */
        /* clear ATHD */
        reg_val &= 0x07;
        /* set ATHD */
        reg_val |= ATHD;
        ret = bsp_i2c_write(&i2c2_transfer, REG_CONFIG, &reg_val, 1);
        if (ret == RETVAL(E_BUS))
            return RETVAL(E_BUS);
    }
    /* check config_update_flag if not right */
    ret = bsp_i2c_read(&i2c2_transfer, REG_CONFIG, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    if(!(reg_val & CONFIG_UPDATE_FLG)) {
        /* update flag for new battery info need set */
        ret = fuelgague_cw2015_cfg_batt_model();
        if (ret != RETVAL(E_OK))
            return ret;
    } else {
        for(i = 0; i < SIZE_BATINFO; i++) {
            ret = bsp_i2c_read(&i2c2_transfer, REG_BATINFO +i, &reg_val, 1);
            if (ret == RETVAL(E_BUS))
                return RETVAL(E_BUS);
            if (batt_model[i] != reg_val)
                break;
        }
        if (i != SIZE_BATINFO) {
            //"update flag for new battery info need set"
            ret = fuelgague_cw2015_cfg_batt_model();
            if (ret != 0)
                return ret;
        }
    }
    return RETVAL(E_OK);
}

static int32_t fuelgague_cw2015_reboot(void) {
    int32_t ret = 0;
    uint8_t reset_val = MODE_SLEEP;

    ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reset_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    fuelgague_cw2015_delay10us(10);
    reset_val = MODE_NORMAL;
    ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reset_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    fuelgague_cw2015_delay10us(10);
    ret = gague_cw2015_cfg();
    if (ret != 0)
        return ret;
    return RETVAL(E_OK);
}

static int32_t fuelgague_cw2015_get_vol(void) {
    int32_t ret = 0;
    uint8_t get_ad_times = 0;
    uint8_t reg_val[2] = {0 , 0};
    unsigned long ad_value = 0;
    uint32_t ad_buff = 0;
    uint32_t ad_value_min = 0;
    uint32_t ad_value_max = 0;

    for(get_ad_times = 0; get_ad_times < 3; get_ad_times++) {
        ret = bsp_i2c_read(&i2c2_transfer, REG_VCELL, &reg_val[0],2);
        if (ret == RETVAL(E_BUS))
            return RETVAL(E_BUS);
        ad_buff = (reg_val[0] << 8) + reg_val[1];

        if (get_ad_times == 0) {
            ad_value_min = ad_buff;
            ad_value_max = ad_buff;
        }
        if (ad_buff < ad_value_min)
            ad_value_min = ad_buff;
        if (ad_buff > ad_value_max)
            ad_value_max = ad_buff;
        ad_value += ad_buff;
    }
    ad_value -= ad_value_min;
    ad_value -= ad_value_max;
    ad_value = ad_value  * CW2015_VOLTAGE_RESOLUTION;

    return(ad_value);
}

static int32_t fuelgague_cw2015_get_soc(void) {
    int32_t ret = 0;
    uint8_t reg_val;
    uint8_t cw_soc;

    ret = bsp_i2c_read(&i2c2_transfer, REG_SOC, &reg_val, 1);
    if (ret == RETVAL(E_BUS))
        return RETVAL(E_BUS);
    cw_soc = reg_val;
    if ((cw_soc < 0) || (cw_soc > 100)) {
        reset_loop ++;
        if (reset_loop > 5) {
            ret = fuelgague_cw2015_reboot();
            if(ret != 0)
                return ret;
            reset_loop = 0;
        }
        return batt_info.soc;
    }
    else
        reset_loop = 0;

    if ((batt_info.usb_plugin > 0) && (cw_soc == 0)) {
        allow_charger_always_zero ++;
        if ((allow_charger_always_zero >= BATTERY_DOWN_MIN_CHANGE_SLEEP) && (if_quick_start == 0)) {
            ret = fuelgague_cw2015_reboot();
            if (ret != 0)
                return ret;
            if_quick_start = 1;
            allow_charger_always_zero = 0;
        }
    }
    else if ((if_quick_start == 1)&&(batt_info.usb_plugin == 0))
        if_quick_start = 0;
    return(cw_soc);
}

static int32_t fuelgague_cw2015_update_vol(void) {
    uint32_t cw_volt;
    cw_volt = fuelgague_cw2015_get_vol();
    if (cw_volt == 1)
        batt_info.volt = batt_info.volt;
    else if (batt_info.volt != cw_volt)
        batt_info.volt = cw_volt;
    return batt_info.volt;
}

static int32_t fuelgague_cw2015_update_soc(void) {
    int32_t cw_soc;
    cw_soc = fuelgague_cw2015_get_soc();
    if ((cw_soc >= 0) && (cw_soc <= 100) && (batt_info.soc != cw_soc)) {
        batt_info.soc = cw_soc;
        return batt_info.soc;
    }
    else {
        return RETVAL(E_FAIL);
    }
}

static void fuelgague_info_reset(void) {
    batt_info.usb_plugin = 0;
    batt_info.soc = 0;
    batt_info.volt = 0;
    batt_info.alt = 0;
}

static void gague_cw2015_init(void) {
    int32_t ret = 0;
    uint8_t retry_times = 0;

    for (retry_times = 0; retry_times < FUELGAGUE_INIT_RETRY_TIMES; retry_times ++) {
        ret = gague_cw2015_cfg();
        if (ret == RETVAL(E_OK))
            break;
    }

    switch (ret)
    {
        case RETVAL(E_BUS):
        {
            LOG_D("[fuelgague][init][error]i2c bus err\r\n");
        }
        break;

        case RETVAL(E_STATE):
        {
            LOG_D("[fuelgague][init][error]ic is sleeping\r\n");
        }
        break;

        case RETVAL(E_SET):
        {
            LOG_D("[fuelgague][init][error]cfg info set fail\r\n");
        }
        break;

        default:
        break;
    }

    fuelgague_info_reset();
}

static void gauge_timer_power_stable_check(void const *arg) {
    g_fg_info.state = g_set_charge_state;
    g_fg_info.current = g_set_current;
}

static void gauge_process(void) {
    int32_t ret = 0;
    g_fg_info.ccv = fuelgague_cw2015_update_vol();
    ret = fuelgague_cw2015_update_soc();
    if (ret != RETVAL(E_FAIL))
        g_fg_info.soc = ret;
}

static int32_t gauge_set_power(fuelgauge_driver_t *drv, fuelgauge_work_mode_t mode) {
    return RETVAL(E_OK);
}

static int32_t gauge_init(void) {
    memset(&g_fg_info, 0, sizeof(g_fg_info));
    g_gauge_timer.ptimer = gauge_timer_power_stable_check;
    g_gauge_timer_id = sys_timer_create(&g_gauge_timer, SYS_TIMER_ONCE, NULL);
    if (!g_gauge_timer_id)
        return RETVAL(E_FAIL);
    uint32_t temp;
    temp = sys_enter_critical();
    gague_cw2015_init();
    sys_exit_critical(temp);
    return RETVAL(E_OK);
}

int32_t gauge_get_info(fuelgauge_driver_t *drv, fuelgauge_info_t *info) {
    gauge_process();
    info->ccv = g_fg_info.ccv;
    info->soc = g_fg_info.soc;
    return RETVAL(E_OK);
}

static int32_t gague_set_batt_type(fuelgauge_driver_t *drv, uint32_t type) {
    int32_t ret;
    ret = fuelgague_cw2015_cfg_batt_model();
    if (ret != RETVAL(E_OK)) {
        LOG_E("[fuelgague][driver]:fuelgague cfg batt model fail %d\r\n", ret);
        return ret;
    }
    return RETVAL(E_OK);
}

static int32_t gauge_set_charge_state(fuelgauge_driver_t *drv, fuelgauge_charge_state_t state, uint32_t current) {
    if (state == FUELGAUGE_CHARGE_STATE_CHARGE)
        batt_info.usb_plugin = 1;
    else if (state == FUELGAUGE_CHARGE_STATE_DISCHARGE)
        batt_info.usb_plugin = 0;
    if ((state == g_fg_info.state) && (current == g_fg_info.current))
        return RETVAL(E_OK);

    g_set_charge_state = state;
    g_set_current = current;

    sys_timer_stop(g_gauge_timer_id);
    sys_timer_start(g_gauge_timer_id, 3000);

    return RETVAL(E_OK);
}

static int32_t gauge_set_work_mode(fuelgauge_driver_t *drv, fuelgauge_work_mode_t mode) {
    int32_t ret;
    uint8_t reg_val = 0;

    switch (mode)
    {
        case FUELGAUGE_WORK_MODE_NORMAL:
        {
            reg_val = MODE_NORMAL;
            ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reg_val, 1);
            if (ret == RETVAL(E_BUS))
                return RETVAL(E_BUS);
        }
        break;

        case FUELGAUGE_WORK_MODE_SLEEP:
        {
            reg_val = MODE_SLEEP;
            ret = bsp_i2c_write(&i2c2_transfer, REG_MODE, &reg_val, 1);
            if (ret == RETVAL(E_BUS))
                return RETVAL(E_BUS);
        }
        break;

        default:
        break;
    }

    return RETVAL(E_OK);
}

static int32_t gauge_probe(void) {
    return RETVAL(E_OK);
}

static int32_t gauge_remove(void) {
    return RETVAL(E_OK);
}

static int32_t gauge_shutdown(driver_t *drv) {
    return RETVAL(E_OK);
}

static int32_t gauge_suspend(driver_t *drv) {
    return RETVAL(E_OK);
}

static int32_t gauge_resume(driver_t *drv) {
    return RETVAL(E_OK);
}

static driver_pm_ops_t gauge_pm =
{
    .shutdown = gauge_shutdown,
    .suspend = gauge_suspend,
    .resume = gauge_resume,
};

static fuelgauge_driver_t gauge = {
    .drv = {
        .probe = gauge_probe,
        .remove = gauge_remove,
        .pm_ops = &gauge_pm,
    },
    .ops = {
        .init = gauge_init,
        .get_info = gauge_get_info,
        .set_batt_type = gague_set_batt_type,
        .set_charge_state = gauge_set_charge_state,
        .set_work_mode = gauge_set_work_mode,
        .set_power = gauge_set_power,
    }
};

static void gauge_register(void) {
    int32_t ret;

    ret = fuelgauge_driver_register("cw2015", &gauge);
    if (ret != RETVAL(E_OK)) {
    }
}

DRIVER_INITCALL("cw2015", gauge_register);

batt_model.h 这个模型数据一般由电池厂商提供

#ifndef __BATT_MODEL_H__
#define __BATT_MODEL_H__

#define SIZE_BATINFO 64

uint8_t batt_model[SIZE_BATINFO] =
{
    0x17, 0xC8, 0x62, 0x64, 0x69, 0x66, 0x66,
    0x65, 0x60, 0x5E, 0x5A, 0x5E, 0x65, 0x4C,
    0x42, 0x41, 0x38, 0x31, 0x2A, 0x32, 0x37,
    0x41, 0x47, 0x46, 0x1D, 0x99, 0x06, 0x66,
    0x27, 0x47, 0xA9, 0x97, 0x9E, 0xA5, 0x9C,
    0x9E, 0x40, 0x1B, 0x64, 0x53, 0x0C, 0x61,
    0x10, 0x42, 0x7B, 0x96, 0xA4, 0x30, 0x4F,
    0x78, 0x9B, 0xC3, 0x80, 0x97, 0x8B, 0xFF,
    0x2F, 0x00, 0x64, 0xA5, 0xB5, 0xC1, 0x50,
    0x77
};

#endif // __BATT_MODEL_H__

相关文章:

  • TypeScript详解十五:模块与命名空间
  • 快排,代码思路详解
  • 屏幕录像软件camtasia2022汉化版好用的录屏软件
  • SpringBoot开发之SpringMVC
  • KMP算法详解,3000字详解,带你学会next数组
  • 如果不富有,那就像有钱人一样去行动吧_《有钱人和你想的不一样》读书笔记
  • C++头文件 库函数 以及 vector相关问题
  • MySQL-InnoDB引擎-架构和事务原理
  • LeetCode笔记:Weekly Contest 312
  • 基于黑马程序员瑞吉外卖开发的瑞吉外卖cloud项目
  • Java各版本发行时间表
  • matlab 四分之一非线性车辆参数蒙特卡洛方法优化
  • 动手学习TCP中
  • C#命名空间 System.IO思维导图
  • 【数据结构】栈和队列重点知识汇总(附有OJ题)
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • 【Linux系统编程】快速查找errno错误码信息
  • Apache的基本使用
  • CentOS6 编译安装 redis-3.2.3
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • php面试题 汇集2
  • Python_OOP
  • React as a UI Runtime(五、列表)
  • sessionStorage和localStorage
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 免费小说阅读小程序
  • 前端面试题总结
  • 通过git安装npm私有模块
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (4)logging(日志模块)
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)jQuery 基础
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .Net中的设计模式——Factory Method模式
  • [ Linux ] Linux信号概述 信号的产生
  • [C]整形提升(转载)
  • [CF482B]Interesting Array
  • [CISCN2019 华东北赛区]Web2
  • [HDU] 1054 Strategic Game 入门树形DP
  • [InnoDB系列] -- SHOW INNODB STATUS 探秘
  • [Kubernetes]8. K8s使用Helm部署mysql集群(主从数据库集群)
  • [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表
  • [leetcode]114. Flatten Binary Tree to Linked List由二叉树构建链表
  • [MYSQL数据库]- 索引
  • [MySQL数据库部署及初始化相关]