☰
ESP32 读准单节电池电量:BQ27220 电量计快速接入与标定指南
2026/10/21 1:36:08 网站建设 项目流程

ESP32 读准单节电池电量:BQ27220 电量计快速接入与标定指南

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

想让 ESP32 电池电量报表精确到每一度,光靠 ADC 分压远远不够。BQ27220 是一颗单节锂离子电池电量计芯片,能直接算出 SoC、剩余容量和电芯健康度。esp-iot-solution 已把它封装成开箱即用的驱动组件。读完这篇,你手里会有三样东西:一套最小接线代码、两张配置表的完整解读,以及一套采出自家电芯 CEDV 参数的闭环流程。

🔌 这个组件能读出什么数据

芯片内部持续采集电压、电流、温度,再换算成"还剩多少电",主机侧只需轮询读取。

指标API单位与说明
荷电状态 SoCbq27220_get_state_of_charge()0~100%
剩余容量bq27220_get_remaining_capacity()mAh
满充容量(已学习)bq27220_get_full_charge_capacity()mAh,实际可用容量
设计容量bq27220_get_design_capacity()mAh
健康状态 SoHbq27220_get_state_of_health()满充容量 ÷ 设计容量,0~100%
电池电压bq27220_get_voltage()mV
瞬时电流bq27220_get_current()mA,充电为正、放电为负
平均电流bq27220_get_avgcurrent()mA
温度bq27220_get_temperature()原始值 0.1°K,转摄氏需/10 - 273
循环次数bq27220_get_cycle_count()次
平均功率bq27220_get_average_power()mW,充电为正、放电为负
预计放空/充满时间bq27220_get_time_to_empty()/bq27220_get_time_to_full()分钟
电池状态 / 运行状态bq27220_get_battery_status()/bq27220_get_operation_status()位域,可直接取DSG(放电中)、FC(满充)、SEC(安全等级)等位

数据接口只是入口。真正决定这些数字准不准的,是烧进芯片里的电池参数。

三步接上它

接线、建总线、建句柄,三步走完驱动就跑起来了。

第一步:硬件接线。SCL、SDA 两根线接芯片,开启内部上拉(GPIO_PULLUP_ENABLE)。示例默认用 GPIO1(SCL)与 GPIO2(SDA),按你的板子改。I2C 从机地址是固定的0x55,写死在 bq27220.c 里,接线时不用管它,但排查总线问题时要用。

第二步:创建 400 kHz 的 I2C 总线。组件依赖espressif/i2c_bus 1.4.*,必须先拿到总线句柄再交给驱动。

第三步:带参数调用bq27220_create()。把 CEDV 参数表和 Gauging 配置一起传进去,最小代码如下:

// I2C 总线:开内部上拉,400 kHz 主模式 i2c_config_t conf = { .mode = I2C_MODE_MASTER, .scl_io_num = GPIO_NUM_1, // 按板子修改 .sda_io_num = GPIO_NUM_2, .scl_pullup_en = GPIO_PULLUP_ENABLE, .sda_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 400 * 1000, }; i2c_bus_handle_t bus = i2c_bus_create(I2C_NUM_0, &conf); // CEDV 参数与 Gauging 配置一起交给驱动(取值见后文两张配置表) bq27220_config_t cfg = { .i2c_bus = bus, .cedv = &my_cedv, // parameter_cedv_t,你的电芯参数 .cfg = &my_gauging_cfg, // gauging_config_t }; bq27220_handle_t gauge = bq27220_create(&cfg);

拿到句柄之后,前面表格里的 API 就都能调了。但别小看这次create,它背后做了一整套设备验证和参数烧写。

一次 create,参数自动烧进去

bq27220_create()不只是分配内存,而是一条"验明正身—解锁—按需写参—落锁"的完整流程。

  1. 参数校验。检查config、i2c_bus、cedv、cfg四个指针非空,分配数据结构,在总线上创建0x55地址的设备句柄。
  2. 核对设备号。通过Control()子命令CONTROL_DEVICE_NUMBER(0x0001)发起请求,再从 MAC scratch 空间(地址0x40)读回,必须等于0x0220,否则直接失败。这一步专门防止 I2C 上挂错了器件还硬写参数。
  3. 读版本。固件版本与硬件版本各读一次并打日志,例如Firmware Version 2002/Hardware Version 0004。
  4. 解除密封。依次发送两把解锁密钥0x0414(UNSEALKEY1)与0x3672(UNSEALKEY2,定义见 bq27220_reg.h),进入未密封状态,配置数据才可读写。
  5. 先比对,再决定写不写。驱动回读芯片里现存的整套电池参数与传入值逐字段比对。完全一致就打印Skip battery profile update,直接进入下一步——所以设备反复重启不会重复烧写。不一致才进入配置更新模式。
  6. 烧写参数。先申请 Full Access,再发CONTROL_ENTER_CFG_UPDATE(0x0090)进入 Config Update 模式。数据存储器按块写入:用Select Subclass(0x3E)选中地址块并暂存,再用MAC Data Sum(0x60)提交。每个 16 位参数按 [地址低、地址高、值高、值低] 4 个字节发送,随后附上对这 4 字节求反的校验和(0xFF - 累加和)以及固定长度6,芯片校验通过才落盘。写入的内容包括 Gauging Configuration、容量类参数、EDV、阻抗因子和整张 DOD 电压表。
  7. 回读校验并密封。发CONTROL_EXIT_CFG_UPDATE_REINIT(0x0091)退出并复位,等待 CFGUPDATE 位清零,全字段回读确认无误后执行bq27220_seal()。密封相当于调参完把门反锁,防止后续运行中配置被意外改动。

细节上,TC与C1两个 8 位字段会被打包成一个 16 位字(TC << 8) | C1,写入数据存储器0x92B1,正好对应表里 TC(0x92B1)与 C1(0x92B2)相邻的布局。

烧写流程解决"参数怎么进去",接下来看"参数本身怎么写才对"。

🔢 决定电表准不准的两张配置表

CEDV 表描述"你这颗电芯长什么样",Gauging Config 描述"电量计用什么算法算",两张表都对了,SoC 才可信。

CEDV:这颗电芯的"油量对照表"

parameter_cedv_t(bq27220.h)定义如下,仓库示例值对应一颗 650 mAh 电芯:

容量类

字段示例值含义
full_charge_cap650已学习的满充容量(mAh),芯片会自行修正
design_cap650设计容量(mAh)
reserve_cap0保留容量(mAh)
near_full200接近充满阈值(mAh)
self_discharge_rate20自放电率索引(0~255),每天自放电百分比 = 值 × 0.0025%

EDV 类

字段示例值含义
EDV034900% SoC 时的放电终止电压(mV)
EDV135113% SoC 时的放电终止电压(mV)
EDV23535低电量百分比对应的放电终止电压(mV)
EMF3670无负载电芯电压,需高于计算出的最高 EDV 阈值(mV)
C0115无负载、容量相关的 EDV 调整因子
C10在 EDV0 处期望保留的剩余容量

阻抗补偿类

字段示例值含义
R0968一阶倍率依赖因子,补偿电池阻抗
T04547阻抗随温度变化的调整量
R14764阻抗随容量变化的调整量
TC11冷态(T < 23°C)阻抗变化调整量

DOD 电压表

DOD0~DOD100共 11 个点,按 10% 步进给出 0%~100% 放电深度对应的电压(mV),示例从满电4147(DOD0)平滑降到放空3490(DOD100),与 EDV0 相接。中间如DOD50 = 3824、DOD80 = 3677。

示例值只用来跑通流程,量产前务必用 GPCCEDV 为自家电芯重新生成。不同化学体系、不同厂家的电芯,这条电压-容量曲线差异很大。

Gauging Config:9 个算法开关

gauging_config_t是一个 16 位位域联合体,落在数据存储器地址0x929B(地址映射见 bq27220_data_memory.h)。仓库默认配置开启了CCT、SC、FCC_LIM、FC_FOR_VDQ、IGNORE_SD五个位:

位默认含义
CCT10 = CC 按 DesignCapacity() 百分比计算(默认);1 = 按 FullChargeCapacity() 百分比
CSYNC0有效充电终止时,将 RemainingCapacity() 与 FullChargeCapacity() 同步
EDV_CMP0EDV 补偿使能
SC1平滑引擎(Smoothing Engine)使能
FIXED_EDV00固定 EDV0 使能
FCC_LIM1满充容量限制
FC_FOR_VDQ1使用满充电压用于 VDQ
IGNORE_SD1忽略自放电
SME00平滑引擎模式选择

配置表讲完,剩最后一件事:怎么让表里的数字和真实电芯对上。

📈 四步标出自己的电芯

组件自带sample-data-app工具(tools/sample-data-app/),整条标定路径分四步。

第一步:menuconfig 定四个参数。定义在 Kconfig.projbuild:

配置项默认值作用
CONFIG_I2C_SCL_IO1I2C SCL 引脚
CONFIG_I2C_SDA_IO2I2C SDA 引脚
CONFIG_DISCHARGE_ENABLE_IO4放电使能 GPIO,低电平关断放电
CONFIG_END_DISCHAGE_VOLTAGE3300结束放电电压(mV)

第二步:跑一次受控放电采样。流程见 main.c:先把放电使能 IO 拉低切断负载,挂上 SPIFLASH 里 FAT 格式的/log分区(partitions.csv 中 500K),再拉高该 IO 开始放电。之后每2 秒采一组电压、电流、温度,直接以 CSV 打到控制台,电压低于截止值(默认 3300 mV)即停止并卸载文件系统:

// 每 2 秒采样一次:电压 / 电流 / 温度 uint16_t mv = bq27220_get_voltage(gauge); int16_t ma = bq27220_get_current(gauge); float c = bq27220_get_temperature(gauge) / 10.0f - 273.0f; // 0.1K 转摄氏 printf("%lu, %u, %d, %.1f\n", elapsed_s, mv, ma, c);

控制台输出长这样(节选):

ElapsedTime, Voltage, Current, Temperature 0, 4078, -33, 27.8 2, 4077, -32, 27.7 4, 4077, -32, 27.7

第三步:喂给 GPCCEDV。把完整 CSV 交给 TI 的 Gauging Parameter Calculator(GPCCEDV)工具,得到这颗电芯专属的 CEDV 参数集。

第四步:参数回填闭环。把生成值填进parameter_cedv_t,重新走一次bq27220_create(),驱动比对失败会自动烧写新参数并密封。标定闭环完成。

工程上这个工具通过EXTRA_COMPONENT_DIRS "../../../bq27220"(CMakeLists.txt)直接复用组件源码,不需要额外维护一份驱动拷贝。

参数就位后,日常读取靠什么接口,一张表查完。

API 速查

只读接口全部走标准命令直接读 2 字节,实现集中在 bq27220.c,按功能分四组:

状态类

API说明
bq27220_get_voltage()电池电压(mV)
bq27220_get_current()/bq27220_get_avgcurrent()瞬时 / 平均电流(mA)
bq27220_get_temperature()温度(0.1°K)
bq27220_get_charge_voltage()/bq27220_get_charge_current()充电电压(mV)/ 充电电流(mA)
bq27220_get_battery_status()状态位域:DSG、FC、FD、OCVGD等
bq27220_get_operation_status()运行位域:SEC、CFGUPDATE、INITCOMP等
bq27220_get_cycle_count()充放电循环次数

容量类

API说明
bq27220_get_state_of_charge()SoC(0~100%)
bq27220_get_remaining_capacity()剩余容量(mAh)
bq27220_get_full_charge_capacity()补偿后的满充容量(mAh)
bq27220_get_design_capacity()设计容量(mAh)
bq27220_get_state_of_health()SoH = 满充容量 ÷ 设计容量(%)

时间类

API说明
bq27220_get_time_to_empty()预计放空时间(分钟)
bq27220_get_time_to_full()预计充满时间(分钟)
bq27220_get_maxload_current()/bq27220_get_standby_current()最大负载 / 待机电流(mA)
bq27220_get_average_power()平均功率(mW)

配置读写类

API说明
bq27220_seal()/bq27220_unseal()密封 / 解除密封配置访问
bq27220_set_parameter_u16()/bq27220_get_parameter_u16()按数据存储器地址读写 16 位参数
bq27220_get_fw_version()/bq27220_get_hw_version()固件 / 硬件版本
bq27220_delete()释放句柄并删除 I2C 设备

接口都齐了,合入产品前还有几处坑要绕开。

⚠️ 上线前注意这些

  • 先跑一遍自带测试。test_apps 基于 Unity,bq27220 basic information query test走"创建→全量读取→删除"完整链路(bq27220_test.c)。setUp/tearDown里对比 8-bit 与 32-bit 堆空闲量,泄漏阈值首次为-110 字节、之后为0,create/delete 循环零泄漏才算过。
  • 依赖版本要对。idf_component.yml 声明espressif/i2c_bus ^1.4(public 依赖)、cmake_utilities 0.*,要求 IDF>=4.4。组件管理器会自动拉取;CMake 侧编译只依赖driver,priv_include不对外暴露。
  • 留意版本线。v0.1.0(2025-08-12)为初始版本;v0.1.1(2025-09-22)把 i2c_bus 依赖改为 public 以修复编译错误;当前仓库版本已推进到 v0.1.2(2026-09-07),补齐了写参校验与全量回读。详见 CHANGELOG.md。
  • 许可合规。驱动源码以 Apache-2.0 / CC0-1.0 形式发布,见各文件 SPDX 头与 license.txt。
  • CEDV 别抄示例。650 mAh 那套参数只为示例电芯服务,自家电芯必须走一遍采样标定。

现在就做三件事

  1. 今天:按"三步接上它"把板子连好,跑一次bq27220_create(),确认日志里出现设备号校验通过,且读到真实的电压与 SoC。
  2. 本周:用sample-data-app完整放一次电,CSV 喂给 GPCCEDV,把生成参数回填parameter_cedv_t,观察 SoC 与电压的吻合度。
  3. 合入前:在 test_apps 的模式下过一遍 create/delete 循环,确认堆空闲量无回退,依赖锁定espressif/i2c_bus ^1.4与 IDF>=4.4。

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询