Files
archery/power.py
2026-04-28 16:58:51 +08:00

165 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
电源管理模块INA226
提供电压、电流监测和充电状态检测
"""
import config
from logger_manager import logger_manager
from maix import time as maix_time
_INA226_PRESENT = None
def _ina226_ready() -> bool:
"""
是否允许访问 INA226。
重要:
- 这里刻意不做任何 I2C 探测/读写。
- 经验上,在 INA226 未供电/未响应时I2C 的 readfrom_mem 可能直接触发底层崩溃SIGSEGVtry/except 无法拦截。
- 因此只在开机 init_ina226() 成功后才允许后续读电压/电流。
"""
return bool(getattr(config, "INA226_ENABLE", True)) and (_INA226_PRESENT is True)
def write_register(reg, value):
"""写入INA226寄存器"""
from hardware import hardware_manager
logger = logger_manager.logger
data = [(value >> 8) & 0xFF, value & 0xFF]
# 某些底层驱动在失败时只打印 “write failed” 并返回 -1而不是抛异常
# 为避免误判“初始化成功”导致后续 readfrom_mem SIGSEGV这里把失败显式转成异常。
ret = hardware_manager.bus.writeto_mem(config.INA226_ADDR, reg, bytes(data))
if isinstance(ret, int) and ret < 0:
if logger:
logger.error(f"[INA226] writeto_mem 失败: addr=0x{config.INA226_ADDR:02X} reg=0x{reg:02X} ret={ret}")
raise OSError(ret)
def read_register(reg):
"""读取INA226寄存器"""
from hardware import hardware_manager
data = hardware_manager.bus.readfrom_mem(config.INA226_ADDR, reg, 2)
return (data[0] << 8) | data[1]
def init_ina226():
"""初始化 INA226 芯片:配置模式 + 校准值"""
global _INA226_PRESENT
logger = logger_manager.logger
if not getattr(config, "INA226_ENABLE", True):
if logger:
logger.info("[INA226] INA226_ENABLE=False跳过初始化与 I2C 探测")
# 显式标记不可用,避免后续误读
_INA226_PRESENT = False
return False
try:
# 仅通过“写寄存器成功”来判定可用,避免额外的读操作触发底层崩溃
write_register(config.REG_CONFIGURATION, 0x4527)
write_register(config.REG_CALIBRATION, config.CALIBRATION_VALUE)
_INA226_PRESENT = True
return True
except Exception as e:
_INA226_PRESENT = False
if logger:
logger.error(f"[INA226] 初始化失败:{e}")
return False
def get_bus_voltage():
"""读取总线电压单位V。未探测到 INA226 或读失败时返回 0.0(上报用,避免 null"""
logger = logger_manager.logger
if not _ina226_ready():
return 0.0
try:
raw = read_register(config.REG_BUS_VOLTAGE)
return raw * 1.25 / 1000
except Exception as e:
if logger:
logger.error(f"[INA226] 读取电压失败:{e}")
return 0.0
def get_current():
"""
读取电流单位mA
正数表示充电,负数表示放电
INA226 电流计算公式:
Current = (Current Register Value) × Current_LSB
Current_LSB = 0.001 × CALIBRATION_VALUE / 4096
"""
try:
if not _ina226_ready():
return 0.0
raw = read_register(config.REG_CURRENT)
# INA226 电流寄存器是16位有符号整数
# 最高位是符号位0=正充电1=负(放电)
# 计算 Current_LSB根据 CALIBRATION_VALUE
current_lsb = 0.001 * config.CALIBRATION_VALUE / 4096 # 单位A
# 处理有符号数如果最高位为1转换为负数
if raw & 0x8000: # 最高位为1表示负数放电
signed_raw = raw - 0x10000 # 转换为有符号整数
else: # 最高位为0表示正数充电
signed_raw = raw
# 转换为毫安
current_ma = signed_raw * current_lsb * 1000
return current_ma
except Exception as e:
logger = logger_manager.logger
if logger:
logger.error(f"[INA226] 读取电流失败: {e}")
else:
print(f"[INA226] 读取电流失败: {e}")
return 0.0
def is_charging(threshold_ma=10.0):
"""
检测是否在充电(通过电流方向判断)
Args:
threshold_ma: 电流阈值毫安超过此值认为在充电默认10mA
Returns:
True: 正在充电
False: 未充电或读取失败
"""
try:
current = get_current()
is_charge = current > threshold_ma
return is_charge
except Exception as e:
logger = logger_manager.logger
if logger:
logger.error(f"[CHARGE] 检测充电状态失败: {e}")
else:
print(f"[CHARGE] 检测充电状态失败: {e}")
return False
def voltage_to_percent(voltage):
"""根据电压估算电池百分比(查表插值)"""
if voltage is None:
return 0
points = [
(4.20, 100), (4.10, 95), (4.05, 85), (4.00, 75), (3.95, 65),
(3.90, 55), (3.85, 45), (3.80, 35), (3.75, 25), (3.70, 15),
(3.65, 5), (3.60, 0)
]
if voltage >= points[0][0]:
return 100
if voltage <= points[-1][0]:
return 0
for i in range(len(points) - 1):
v1, p1 = points[i]
v2, p2 = points[i + 1]
if voltage >= v2:
ratio = (voltage - v1) / (v2 - v1)
percent = p1 + (p2 - p1) * ratio
return max(0, min(100, int(round(percent))))
return 0