第04章 BQ769接口层
约 868 字大约 3 分钟
2026-07-21
4.1 相关概念
① 重置BQ769
STM32靠外部电压供电,而BQ769靠电池供电,无论是烧录程序、复位键、BMS驱动板接电源都不会影响BQ769。只要电池还能持续供电,BQ769就不会停止工作。
所以驱动板初始化的第一件事就是重置当前的BQ769,分为两个步骤:
- 使BQ769进入SHIP模式,重置寄存器
- 再把BQ769唤醒进入Normal模式
② 两种模式
SHIP模式(船载/运输模式)
一种低功耗状态。进入该状态芯片中的寄存器内容基本都会丢失,不接受I2C指令,相当于软关闭。
进入SHIP模式的方法:
- BQ769上电(重装电池)会自动进入SHIP模式
- 从Normal模式进入SHIP模式需要向芯片发送I2C指令
Normal模式
从SHIP模式进入Normal模式需要:
- TS1引脚大于1000mV(高电平)
- TS1引脚拉高维持至少2000μs


③ CRC校验
BQ芯片的I2C读写操作都需要进行CRC8校验(多项式: x^8 + x^2 + x + 1,初始值: 0x00)。

static uint8_t Int_BQ769_CRC8(const uint8_t *data, size_t length) {
uint8_t crc = 0x00;
for (size_t i = 0; i < length; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0x07;
} else {
crc <<= 1;
}
}
crc &= 0xFF;
}
return crc;
}④ 读写操作规则
写寄存器:使用设备地址(0x10)、寄存器地址和数据(1字节)来生成CRC校验字节。多字节写入时,第一个数据字节的CRC同单字节写入,后面数据字节的CRC只需数据字节本身生成。
读寄存器:返回的结果第一个字节使用设备地址(0x11)和数据字节生成CRC,之后的每个字节都使用数据字节生成CRC。


4.2 原理图和HAL库设置
原理图
BQ769的TS1引脚(唤醒引脚)连接单片机PA15引脚。


HAL库设置
配置PA15引脚为GPIO_Output模式,用于控制BQ769的唤醒。

4.3 代码实现
Int_BQ769.h
#ifndef __INT_BQ769_H__
#define __INT_BQ769_H__
#include "gpio.h"
#include "i2c.h"
#include "Com_Debug.h"
#include "FreeRTOS.h"
#include "task.h"
#include "Int_BQ769_BSP.h"
#define BQ769_I2C_ADDR 0x10
#define BQ769_I2C_ADDR_W 0x10
#define BQ769_I2C_ADDR_R 0x11
void Int_BQ769_Init(void);
void Int_BQ769_WriteReg(uint8_t reg_addr, uint8_t reg_val);
uint8_t Int_BQ769_ReadRegs(uint8_t reg_addr, uint8_t reg_num, uint8_t *reg_vals);
#endifInt_BQ769.c 核心函数
进入SHIP模式
static void Int_BQ769_EnterShipMode(void)
{
// 1. SysCtrl1寄存器: SHUT_A=0, SHUT_B=1
bq769_reg_group.SysCtrl1.SysCtrl1Bit.SHUT_A = 0;
bq769_reg_group.SysCtrl1.SysCtrl1Bit.SHUT_B = 1;
Int_BQ769_WriteReg(BQ_SYS_CTRL1, bq769_reg_group.SysCtrl1.SysCtrl1Byte);
// 2. SysCtrl1寄存器: SHUT_A=1, SHUT_B=0
bq769_reg_group.SysCtrl1.SysCtrl1Bit.SHUT_A = 1;
bq769_reg_group.SysCtrl1.SysCtrl1Bit.SHUT_B = 0;
Int_BQ769_WriteReg(BQ_SYS_CTRL1, bq769_reg_group.SysCtrl1.SysCtrl1Byte);
HAL_Delay(100);
}唤醒芯片
static void Int_BQ769_Wakeup(void)
{
// 拉高唤醒引脚,持续至少2ms(这里持续10ms)
HAL_GPIO_WritePin(BQ769_WKUP_GPIO_Port, BQ769_WKUP_Pin, GPIO_PIN_SET);
HAL_Delay(10);
// 再拉低唤醒引脚电平
HAL_GPIO_WritePin(BQ769_WKUP_GPIO_Port, BQ769_WKUP_Pin, GPIO_PIN_RESET);
HAL_Delay(1000); // 等待芯片完全复位
}写寄存器(含CRC校验)
void Int_BQ769_WriteReg(uint8_t reg_addr, uint8_t reg_val)
{
uint8_t crc_buf[3];
crc_buf[0] = BQ769_I2C_ADDR_W;
crc_buf[1] = reg_addr;
crc_buf[2] = reg_val;
uint8_t crc_byte = Int_BQ769_CRC8(crc_buf, 3);
uint8_t tx_buf[2] = {reg_val, crc_byte};
taskENTER_CRITICAL();
HAL_I2C_Mem_Write(&hi2c2, BQ769_I2C_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, tx_buf, 2, HAL_MAX_DELAY);
taskEXIT_CRITICAL();
}读寄存器(含CRC校验)
uint8_t Int_BQ769_ReadRegs(uint8_t reg_addr, uint8_t reg_num, uint8_t *reg_vals)
{
uint8_t rx_buf[reg_num * 2];
taskENTER_CRITICAL();
HAL_I2C_Mem_Read(&hi2c2, BQ769_I2C_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, rx_buf, reg_num * 2, HAL_MAX_DELAY);
taskEXIT_CRITICAL();
for (uint8_t i = 0; i < reg_num; i++)
{
if (i == 0) {
uint8_t crc_buf[2] = {BQ769_I2C_ADDR_R, rx_buf[0]};
if (rx_buf[1] != Int_BQ769_CRC8(crc_buf, 2)) {
DEBUG_PRINTLN("第%d个校验失败 \n", i);
return 1;
}
} else {
if (rx_buf[i * 2 + 1] != Int_BQ769_CRC8(&rx_buf[i * 2], 1)) {
DEBUG_PRINTLN("第%d个校验失败 \n", i);
return 1;
}
}
reg_vals[i] = rx_buf[i * 2];
}
return 0;
}文档生成于: 2026-07-21
