一 充电管理原理
话说Linux充电电池管理需要做什么呢?。。。好像一下没想到哦,这个没有标准答案。这个真的是要根据电池的特性来看,不同类型的电池,甚至不同型号的电池可能管理策略都不同。看了一下这次买的电池。4400mAh可充Li-ion锂电池。
查了一下锂电池大概有这些内容。
1. 设置充电参数
充电电压:充电结束电压应设置为4.2V。虽然供电电压为5V,但需要通过充电控制器来精确调节至4.2V以防止过充。
充电电流:常规充电电流可以设置为电池容量的0.5C至1C,即2200mA到4400mA。初始阶段可以使用较高电流(如1C),当电压接近4.2V时降低电流。
2. 充电阶段
恒流充电(CC):开始时使用固定的充电电流(例如4400mA),直到电池电压升至4.2V。
恒压充电(CV):当电池电压达到4.2V时,调整充电器输出,维持电压恒定在4.2V,同时电流会逐渐减小。当充电电流下降至电池容量的10%以下,如440mA,可以认为充电完成。
3. 放电参数
放电截止电压:通常设置为3.0V到3.2V。当电池电压低于此值时,应停止放电以避免过放。
最大放电电流:应依据电池规格设定,通常不应超过1C,除非电池明确支持更高放电率。
4. 保护措施
短路保护:在电池管理系统中集成短路保护,以防意外发生时电池受损。
温度监控:锂电池对温度敏感,充电和放电时应监控电池温度,避免在极端温度下操作。
过充和过放保护:确保电池管理系统可以在电压超出安全范围时断开电池连接。
总结一下就是:
1.涓流充电:涓流充电主要用于对完全放电的电池单元进行预充电。当电池电压低于约3V时,采用涓流充电。其中,C表示电池额定容量对电流的表示,比如电池的容量是1000mAh,1C就是1000mA的充电电流。
2.恒流充电:当电池电压升至涓流充电阈值以上时,开始采用恒流充电。恒流充电电流一般在0.2C-1.0C之间,不一定要求很精确,准恒流即可。
3.恒压充电:当电池电压升至4.2V时,恒压充电开始。恒压充电电流不再改变,此时电池的电压保持恒压。
4.充电终止:随着充电过程的继续,充电电流会逐渐减小。当其降低到0.01C时,可以考虑充电终止。
好像还有过充和过放的管理,以及设备启动电压的管理,这里就不多说啦。
二 实现方案
那么关注的地方就有两块,一个是电池状态的监控,还有一个就是充电电流电压的控制。之前看过一个ESP32的原理图(看看原理图(以ESP32游戏机为例)-CSDN博客),电池管理用的是一颗TP4056,大概是这样的。
用TP4056当然是简单方便,但是也存在设置不灵活的问题,回想最近做的商业级项目,都是可以直接在设备树中配置,应该是没用这种方法。于是参照了GPT的回答,用INA219来做一下精确控制。在这里,状态监控就是INA219,先从取状态说起吧。(怎么控制输出电流电压还没想好。。。)
手焊好INA219,往面包板上一怼,还是用老朋友树莓派3B,接线不麻烦,就不多说了。接上线就开始弄。(如果没有负载,电池的负极是接在树莓派的地,就是最下面黄色那根。切记!)
三 树莓派I2C的使用
这次用了一下树莓派带的I2C工具(可能linux,Android下都能用),真的方便极了。分别是i2cdetect和i2cdump。
先用sudo raspi-config打开I2C。
安装:
sudo apt-get install -y i2c-tools
查看挂载的所有I2C设备命令如下:
sudo i2cdetect -y 1
结果:
这个好,可以看出地址就是0x40,连设备地址都不用去查了。
然后直接倒0x40的数据:
sudo i2cdump -f -y 1 0x40
结果:
这部分直接裸看就不行了,要么看数据手册,要么要看别人怎么操作的。
这里想用python来解析,但是运行说没有INA219的库,安装又报错。
这样要切换到虚拟环境装就可以了。、
python3 -m venv ~/venv
source ~/venv/bin/activate
pip install pi-ina219
~/venv/bin/python battery.py
battery.py
from ina219 import INA219
from ina219 import DeviceRangeError
SHUNT_OHMS = 0.1 # 根据你的分流电阻修改此值
ina = INA219(SHUNT_OHMS)
ina.configure()
try:
print("Bus Voltage: %.3f V" % ina.voltage())
print("Bus Current: %.3f mA" % ina.current())
print("Power: %.3f mW" % ina.power())
print("Shunt voltage: %.3f mV" % ina.shunt_voltage())
except DeviceRangeError as e:
# 当前流超出INA219的测量范围
print(e)
这样很快就可以读出来电压:
这里有个要注意的地方。只是测量电压的话,电池的正极是接INA219的VIN+,但是负极不是接VIN-(!!!),有负载的话才能接VIN-。没有负载的话是接在树莓派的地。。。刚刚就是接错了差点没把手烫起泡。。。
四 做成内核驱动
最后还是换成内核驱动来做吧。
battery.dts
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835";
fragment@0 {
target = <&i2c1>; // 确认I2C接口,可能需要根据实际情况调整
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
ina219@40 {
compatible = "ti,ina219";
reg = <0x40>; // INA219设备的I2C地址
shunt-resistor = <10000>; // 分流电阻的值,单位微欧姆
};
};
};
};
battery.c
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#define INA219_ADDR 0x40 // INA219 I2C地址
#define INA219_REG_BUS_VOLTAGE 0x02 // INA219电压寄存器地址
static struct i2c_client *ina219_client;
// 读取电压函数
static int read_battery_voltage(void)
{
int ret;
u16 voltage_raw;
struct i2c_msg msgs[2];
u8 reg = INA219_REG_BUS_VOLTAGE;
u8 buf[2];
// 向INA219的电压寄存器发送读取请求
msgs[0].addr = ina219_client->addr;
msgs[0].flags = 0; // 写
msgs[0].len = 1;
msgs[0].buf = ®
// 读取返回的数据
msgs[1].addr = ina219_client->addr;
msgs[1].flags = I2C_M_RD; // 读
msgs[1].len = 2;
msgs[1].buf = buf;
ret = i2c_transfer(ina219_client->adapter, msgs, 2);
if (ret < 0) {
pr_err("read INA219 Voltage failed.\n");
return ret;
}
// 计算电压值
voltage_raw = (buf[0] << 8) | buf[1];
voltage_raw >>= 3; // 去掉3位右移
// 返回以毫伏为单位的电压
return voltage_raw * 4;
}
struct ina219_data {
struct i2c_client *client;
int shunt_resistor;
};
static int ina219_probe(struct i2c_client *client) {
/*struct ina219_data *data;
printk(KERN_CRIT "ina219_probe.\n");
data = kzalloc(sizeof(struct ina219_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
// Assuming device property read is already correct
if (device_property_read_u32(&client->dev, "shunt-resistor", &data->shunt_resistor)) {
data->shunt_resistor = 10000; // default value
}
i2c_set_clientdata(client, data);
printk(KERN_INFO "INA219 at address %x initialized\n", client->addr); */
ina219_client = client;
pr_info("INA219 ina219_probe\n");
// 读取并打印电压
int voltage_mv = read_battery_voltage();
if (voltage_mv >= 0) {
pr_info("Bus Voltage: %d mV\n", voltage_mv);
}
return 0;
}
static void ina219_remove(struct i2c_client *client) {
struct ina219_data *data = i2c_get_clientdata(client);
kfree(data);
printk(KERN_INFO "INA219 at address %x removed\n", client->addr);
}
static const struct of_device_id ina219_of_match[] = {
{ .compatible = "ti,ina219" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ina219_of_match);
static const struct i2c_device_id ina219_id[] = {
{ "ina219", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina219_id);
static struct i2c_driver ina219_driver = {
.driver = {
.name = "ina219",
.of_match_table = of_match_ptr(ina219_of_match),
.owner = THIS_MODULE,
},
.probe = ina219_probe,
.remove = ina219_remove,
.id_table = ina219_id,
};
module_i2c_driver(ina219_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fanged");
MODULE_DESCRIPTION("INA219 Driver for Raspberry Pi");
Makefile
obj-m += battery.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
执行命令如下:
make
sudo dtc -I dts battery.dts -o battery.dtbo
sudo dtoverlay battery.dtbo
sudo insmod battery.ko
sudo rmmod battery.ko
马上就能看到电池的电压:
详细关于这个I2C的部分,可以参考我之前写的:总线学习1--I2C_ssd1315 修改i2c从机地址-CSDN博客
此外,关于Linux内核中的I2C使用,可以参考这个:
=================================================
五 遗漏的部分
因为重点还是在软件,毕竟本人还是软件出身,而且现在也主要是学习为主,所以这次只做了电池电压监控。查了一下,如果要做充电的电源和电压控制,还要继续采购和外挂设备。
首先就是DC-DC,如果是高端一点的芯片,那么可以走I2C来控制。如果是普通的,那么就要全部走MOS管来控制,就是图中下面那个。
MOS管是通过PWM来控制,说是可以同时控制电流和电压,具体怎么搞还不是很清楚。
专业点的可以看看树莓派的电池HAT,PiJuice。
PiJuice/Hardware at master · PiSupply/PiJuice · GitHub
用的STM32来做主控,不能更高端了。
- Microcontroller is an ST Micro STM32F030CCT6 ARM Cortex-M0, 48MHz, F64KB, R8KB, I2C, SPI, USART, 2.4-3.6V
- Charge IC - BQ24160RGET Charger IC Lithium-Ion/Polymer, 2.5A, 4.2-10V
- Fuel gauge IC - LC709203FQH-01TWG Battery Fuel Gauge, 1-Cell Li-ion, 2.8%
- High side power-distribution switch - NCP380LMUAJAATBG Fixed/Adjustable Current‐Limiting Power‐Distribution Switch
- EEPROM - CAT24C32WI-GT3 EEPROM, I2C, 32KBIT, 400KHZ, 1V7-5V5
六 参考
在树莓派3B上使用电池供电需要考虑多个方面,包括电源管理、硬件连接和软件配置。以下是详细的步骤和建议:
1. 选择合适的电池
树莓派3B需要5V的电源供电,通常电流需求在2.5A左右。常见的电池选择包括:锂聚合物电池(LiPo):需要一个降压转换器(如UBEC)将电压降到5V。
USB电池组(Power Bank):大多数USB电池组输出5V,可以直接使用。
2. 硬件连接
使用锂聚合物电池
选择电池:选择一个3.7V的LiPo电池。
降压转换器:使用一个降压转换器(如UBEC)将电池电压降到5V。
连接电池和转换器:将电池连接到降压转换器的输入端。
连接树莓派:将降压转换器的输出端连接到树莓派的5V和GND引脚。
使用USB电池组
选择电池组:选择一个输出5V、2.5A的USB电池组。
连接树莓派:使用USB线将电池组连接到树莓派的电源接口。
3. 电源管理
为了更好地管理电池供电,可以考虑以下措施:电池监控:使用电池监控模块(如INA219)监控电池电压和电流。
低电量警告:设置低电量警告,防止电池过度放电。
4. 软件配置
安装电池监控软件
如果使用电池监控模块,可以安装相关的软件包来读取电池状态。
sudo apt-get updatesudo apt-get install python3-smbus i2c-tools
启用I2C接口
编辑配置文件:sudo raspi-config
选择接口选项:
选择 Interfacing Options
选择 I2C
选择 Yes 启用I2C接口
重启树莓派:sudo reboot
编写电池监控脚本
以下是一个使用Python读取INA219模块数据的示例脚本:
import smbusimport time
# INA219 I2C地址
INA219_ADDRESS = 0x40
# 初始化I2C总线
bus = smbus.SMBus(1)
def read_voltage():
# 读取电压寄存器
voltage_reg = bus.read_word_data(INA219_ADDRESS, 0x02)
# 交换字节顺序
voltage = ((voltage_reg & 0xFF) << 8) | (voltage_reg >> 8)
# 转换为电压值
voltage = voltage * 0.001
return voltage
while True:
voltage = read_voltage()
print(f"Battery Voltage: {voltage:.2f} V")
time.sleep(1)
5. 优化电源消耗
为了延长电池寿命,可以采取以下措施:禁用不必要的服务:禁用不必要的系统服务和进程。
降低CPU频率:通过动态频率调节(DVFS)降低CPU频率。
关闭未使用的外设:关闭未使用的外设(如HDMI、WiFi、蓝牙)。
禁用不必要的服务sudo systemctl disable <service_name>
降低CPU频率
编辑 /boot/config.txt 文件,添加以下行:
arm_freq=600关闭HDMI
/opt/vc/bin/tvservice -o总结
通过选择合适的电池、正确连接硬件、安装电池监控软件和优化电源消耗,可以在树莓派3B上实现稳定的电池供电。上述步骤提供了一个全面的指南,帮助你在树莓派上实现电池供电。