/** * BQ25756 值校验层 — 在底层 I2C 和寄存器操作之间加一层值保护 * * 核心思想(来自 LiPow):写寄存器前检查值是否在合法范围内,不在则 clamp。 * 电机控制/电源系统中,非法寄存器值可能直接损坏电池或 MOSFET。 * * 用法: * 替代原来的 BQ25756_WriteReg16(0x02, raw_value) * 改为 BQ25756_WriteChargeCurrent(mA),内部自动计算寄存器值 + 范围检查 */ #include "bq25756_regs.h" #include "bq_i2c_opt.h" /*=========================================================================== * 充电参数范围常量 *===========================================================================*/ /* 充电电流:50mA/LSB,范围 400~20000mA */ #define CHG_CURRENT_MIN_MA 400 #define CHG_CURRENT_MAX_MA 20000 /* 输入电流 DPM */ #define IIN_CURRENT_MIN_MA 400 #define IIN_CURRENT_MAX_MA 20000 /* 输入电压 DPM:20mV/LSB,范围 4200~65000mV */ #define VIN_DPV_MIN_MV 4200 #define VIN_DPV_MAX_MV 65000 /* 预充电流:50mA/LSB,范围 250~10000mA */ #define PRECHG_MIN_MA 250 #define PRECHG_MAX_MA 10000 /* 终止电流:同上 */ #define TERM_MIN_MA 250 #define TERM_MAX_MA 10000 /*=========================================================================== * 值钳位 *===========================================================================*/ static inline uint32_t clamp_u32(uint32_t v, uint32_t lo, uint32_t hi) { if (v < lo) return lo; if (v > hi) return hi; return v; } /*=========================================================================== * 带校验的写入函数 * 返回值:0=成功,-1=I2C 错误 *===========================================================================*/ int BQ25756_WriteChargeCurrent(uint32_t mA) { mA = clamp_u32(mA, CHG_CURRENT_MIN_MA, CHG_CURRENT_MAX_MA); uint16_t raw = (uint16_t)((mA / 50) << 2); return BQ_I2C_Write2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_CHG_CURRENT, raw); } int BQ25756_WriteInputCurrentDPM(uint32_t mA) { mA = clamp_u32(mA, IIN_CURRENT_MIN_MA, IIN_CURRENT_MAX_MA); uint16_t raw = (uint16_t)((mA / 50) << 2); return BQ_I2C_Write2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_INPUT_CURRENT_DPM, raw); } int BQ25756_WriteInputVoltageDPM(uint32_t mV) { mV = clamp_u32(mV, VIN_DPV_MIN_MV, VIN_DPV_MAX_MV); uint16_t raw = (uint16_t)(mV / 20); return BQ_I2C_Write2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_INPUT_VOLTAGE_DPM, raw); } int BQ25756_WritePrechargeCurrent(uint32_t mA) { mA = clamp_u32(mA, PRECHG_MIN_MA, PRECHG_MAX_MA); uint16_t raw = (uint16_t)((mA / 50) << 2); return BQ_I2C_Write2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_PRECHG_CURRENT, raw); } int BQ25756_WriteTermCurrent(uint32_t mA) { mA = clamp_u32(mA, TERM_MIN_MA, TERM_MAX_MA); uint16_t raw = (uint16_t)((mA / 50) << 2); return BQ_I2C_Write2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_TERM_CURRENT, raw); } /*=========================================================================== * ADC 读数 — 带 scale/offset 换算(LiPow 风格) * * 核心改进:驱动层返回物理量(mV/mA),不是原始寄存器值 * 调用者不需要知道 2mV/LSB 还是 0.8mA/LSB 这些细节 *===========================================================================*/ /** * @brief 读取电池电压,返回 mV * @return <0 表示 I2C 错误,>=0 表示电压值 */ int32_t BQ25756_ReadVBAT_mV(void) { uint16_t raw = 0; if (BQ_I2C_Read2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_VBAT_ADC, &raw) != 0) return -1; return (int32_t)((uint32_t)raw * 2); /* 2mV/LSB */ } /** * @brief 读取充电电流,返回 mA(正值=充电,负值=放电) * @return I2C 失败返回 -32768 */ int32_t BQ25756_ReadIBAT_mA(void) { uint16_t raw = 0; if (BQ_I2C_Read2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_IBAT_ADC, &raw) != 0) return -32768; return (int32_t)((int16_t)raw) * 2; /* 2mA/LSB,2s 补码 */ } /** * @brief 读取输入电压,返回 mV */ int32_t BQ25756_ReadVAC_mV(void) { uint16_t raw = 0; if (BQ_I2C_Read2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_VAC_ADC, &raw) != 0) return -1; return (int32_t)((uint32_t)raw * 2); /* 2mV/LSB */ } /** * @brief 读取输入电流,返回 mA * 注意:BQ25756 IAC_ADC = 0.8mA/LSB */ int32_t BQ25756_ReadIAC_mA(void) { uint16_t raw = 0; if (BQ_I2C_Read2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_IAC_ADC, &raw) != 0) return -1; /* 0.8mA/LSB: raw * 0.8 = raw * 8 / 10 */ return (int32_t)((int16_t)raw) * 8 / 10; } /** * @brief 读取 FB 反馈电压,返回 mV(1mV/LSB,范围 0-2047mV) */ int32_t BQ25756_ReadVFB_mV(void) { uint16_t raw = 0; if (BQ_I2C_Read2Bytes(BQ25756_I2C_ADDR, BQ25756_REG_VFB_ADC, &raw) != 0) return -1; return (int32_t)(raw); /* 1mV/LSB */ } /*=========================================================================== * 8 位寄存器控制函数 — 读改写模式(避免误改其他位) *===========================================================================*/ int BQ25756_SetEN_CHG(uint8_t enable) { uint8_t val; if (BQ_I2C_ReadByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, &val) != 0) return -1; if (enable) val |= BQ25756_EN_CHG; else val &= ~BQ25756_EN_CHG; return BQ_I2C_WriteByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, val); } int BQ25756_SetEN_HIZ(uint8_t enable) { uint8_t val; if (BQ_I2C_ReadByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, &val) != 0) return -1; if (enable) val |= BQ25756_EN_HIZ; else val &= ~BQ25756_EN_HIZ; return BQ_I2C_WriteByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, val); } int BQ25756_ResetWD(void) { uint8_t val; if (BQ_I2C_ReadByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, &val) != 0) return -1; val |= BQ25756_WD_RST; return BQ_I2C_WriteByte(BQ25756_I2C_ADDR, BQ25756_REG_CHARGER_CTRL, val); } /** * @brief 写一个字节到任意 8 位寄存器(读改写,保护其他位) */ int BQ25756_ModifyReg(uint8_t reg, uint8_t mask, uint8_t value) { uint8_t val; if (BQ_I2C_ReadByte(BQ25756_I2C_ADDR, reg, &val) != 0) return -1; val = (val & ~mask) | (value & mask); return BQ_I2C_WriteByte(BQ25756_I2C_ADDR, reg, val); }