#!/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 可能直接触发底层崩溃(SIGSEGV),try/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