/** * BQ25756 I2C 底层优化 — 替代 bq25756.c 中的裸 I2C 函数 * * 新增功能: * 1. I2C 总线句柄化(可切 I2C1/I2C2) * 2. 总线恢复(9 SCL 脉冲解锁 SDA) * 3. NACK 自动重试 * 4. 16-bit 寄存器读写优化 * * 使用方法: * 替换 bq25756.c 中的 I2C 初始化代码,用 BQ_I2C_Init(&I2C_Bus_I2C1) * 选择 I2C1,然后把所有 BQ25756_WriteReg/ReadReg 改为通过 i2c_handle 调用 * * 简化版用法(保持原 API 兼容): * 不改变 bq25756.h 的函数签名,只替换 bq25756.c 的实现 */ #include "stm32f10x.h" /*=========================================================================== * I2C 总线句柄 *===========================================================================*/ #define I2C_RETRY_MAX 3 #define I2C_TIMEOUT_US 10000 typedef struct { I2C_TypeDef *instance; GPIO_TypeDef *scl_port; uint16_t scl_pin; GPIO_TypeDef *sda_port; uint16_t sda_pin; uint32_t speed; uint8_t initialized; } I2C_Bus; /* 预定义总线 */ static I2C_Bus I2C_Bus_I2C1 = { .instance = I2C1, .scl_port = GPIOB, .scl_pin = GPIO_Pin_6, .sda_port = GPIOB, .sda_pin = GPIO_Pin_7, .speed = 400000, }; static I2C_Bus I2C_Bus_I2C2 = { .instance = I2C2, .scl_port = GPIOB, .scl_pin = GPIO_Pin_10, .sda_port = GPIOB, .sda_pin = GPIO_Pin_11, .speed = 400000, }; static I2C_Bus *g_bus = &I2C_Bus_I2C1; /* 默认 I2C1 */ /*=========================================================================== * 总线选择 *===========================================================================*/ void BQ_I2C_SelectBus(I2C_Bus *bus) { g_bus = bus; } I2C_Bus* BQ_I2C_GetBus(void) { return g_bus; } /*=========================================================================== * GPIO 时钟使能 *===========================================================================*/ static void I2C_GPIO_ClockEnable(GPIO_TypeDef *port) { if (port == GPIOA) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); else if (port == GPIOB) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); else if (port == GPIOC) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); else if (port == GPIOD) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); else if (port == GPIOE) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); } static void I2C_InstanceClockEnable(I2C_TypeDef *i2c) { if (i2c == I2C1) RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); if (i2c == I2C2) RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); } /*=========================================================================== * 总线初始化 *===========================================================================*/ void BQ_I2C_Init(I2C_Bus *bus) { GPIO_InitTypeDef gpio; I2C_InitTypeDef i2c_cfg; if (!bus) return; I2C_GPIO_ClockEnable(bus->scl_port); I2C_GPIO_ClockEnable(bus->sda_port); I2C_InstanceClockEnable(bus->instance); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->scl_pin | bus->sda_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(bus->scla_port==bus->sda_port ? bus->scl_port : bus->scl_port, &gpio); /* 如果 SCL/SDA 在不同端口,分别初始化 */ if (bus->scl_port != bus->sda_port) { /* SCL */ GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->scl_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(bus->scl_port, &gpio); /* SDA */ GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->sda_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(bus->sda_port, &gpio); } I2C_StructInit(&i2c_cfg); i2c_cfg.I2C_Mode = I2C_Mode_I2C; i2c_cfg.I2C_DutyCycle = I2C_DutyCycle_2; i2c_cfg.I2C_OwnAddress1 = 0x00; i2c_cfg.I2C_Ack = I2C_Ack_Enable; i2c_cfg.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; i2c_cfg.I2C_ClockSpeed = bus->speed; I2C_Init(bus->instance, &i2c_cfg); I2C_Cmd(bus->instance, ENABLE); bus->initialized = 1; } /*=========================================================================== * 总线恢复 — SDA 被从机拉死时,发 9 个 SCL 脉冲释放 *===========================================================================*/ #define I2C_BUS_RECOVERY_SCL_PULSES 9 static int I2C_BusRecovery(I2C_Bus *bus) { GPIO_InitTypeDef gpio; /* 先关掉 I2C 外设 */ I2C_Cmd(bus->instance, DISABLE); /* SCL 强制输出推挽 */ GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->scl_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(bus->scl_port, &gpio); /* SDA 设输入上拉,检测是否有卡死 */ GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->sda_pin; gpio.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(bus->sda_port, &gpio); for (uint8_t i = 0; i < I2C_BUS_RECOVERY_SCL_PULSES; i++) { GPIO_ResetBits(bus->scl_port, bus->scl_pin); /* 延时 ~5us */ volatile uint32_t dly = 60; while (dly--) { __NOP(); } GPIO_SetBits(bus->scl_port, bus->scl_pin); dly = 60; while (dly--) { __NOP(); } /* 如果 SDA 恢复高电平了,说明恢复成功 */ if (GPIO_ReadInputDataBit(bus->sda_port, bus->sda_pin)) break; } /* 恢复 AF_OD 模式 */ GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->scl_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(bus->scl_port, &gpio); GPIO_StructInit(&gpio); gpio.GPIO_Pin = bus->sda_pin; gpio.GPIO_Speed = GPIO_Speed_10MHz; gpio.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(bus->sda_port, &gpio); /* 重新使能 I2C */ I2C_Cmd(bus->instance, ENABLE); /* 复位 I2C 外设状态 */ I2C_SoftwareResetCmd(bus->instance, ENABLE); I2C_SoftwareResetCmd(bus->instance, DISABLE); /* 发 STOP 释放总线 */ I2C_GenerateSTOP(bus->instance, ENABLE); return (GPIO_ReadInputDataBit(bus->sda_port, bus->sda_pin) != 0); } /*=========================================================================== * 带重试的 I2C 写 *===========================================================================*/ int BQ_I2C_WriteRetry(uint8_t dev_addr, const uint8_t *data, uint8_t len) { I2C_Bus *bus = g_bus; int retry = I2C_RETRY_MAX; while (retry--) { uint32_t to = I2C_TIMEOUT_US; I2C_GenerateSTART(bus->instance, ENABLE); /* wait SB */ while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_SB)); if (!to) { I2C_BusRecovery(bus); continue; } I2C_Send7bitAddress(bus->instance, dev_addr, I2C_Direction_Transmitter); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_ADDR)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } /* Clear ADDR */ (void)bus->instance->SR1; (void)bus->instance->SR2; /* 发送数据 */ int ok = 1; for (uint8_t i = 0; i < len; i++) { to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_TXE)); if (!to) { ok = 0; break; } I2C_SendData(bus->instance, data[i]); } if (!ok) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } /* wait BTF */ to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_BTF)); I2C_GenerateSTOP(bus->instance, ENABLE); return 0; } return -1; /* 重试耗尽 */ } /*=========================================================================== * 带重试的 I2C 读 * data_buf[0] = 寄存器地址(输出时覆盖为读回数据) *===========================================================================*/ int BQ_I2C_ReadRetry(uint8_t dev_addr, uint8_t reg, uint8_t *buf, uint8_t len) { I2C_Bus *bus = g_bus; int retry = I2C_RETRY_MAX; while (retry--) { uint32_t to; /* Phase 1: 写寄存器地址 */ I2C_GenerateSTART(bus->instance, ENABLE); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_SB)); if (!to) { I2C_BusRecovery(bus); continue; } I2C_Send7bitAddress(bus->instance, dev_addr, I2C_Direction_Transmitter); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_ADDR)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } (void)bus->instance->SR1; (void)bus->instance->SR2; to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_TXE)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } I2C_SendData(bus->instance, reg); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_BTF)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } /* Phase 2: Repeated Start + 读 */ I2C_GenerateSTART(bus->instance, ENABLE); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_SB)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } I2C_Send7bitAddress(bus->instance, dev_addr, I2C_Direction_Receiver); to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_ADDR)); if (!to) { I2C_GenerateSTOP(bus->instance, ENABLE); continue; } (void)bus->instance->SR1; (void)bus->instance->SR2; for (uint8_t i = 0; i < len; i++) { if (i == len - 1) I2C_AcknowledgeConfig(bus->instance, DISABLE); /* 最后一个不发 ACK */ to = I2C_TIMEOUT_US; while (to-- && !I2C_GetFlagStatus(bus->instance, I2C_FLAG_RXNE)); if (!to) break; buf[i] = I2C_ReceiveData(bus->instance); } I2C_GenerateSTOP(bus->instance, ENABLE); I2C_AcknowledgeConfig(bus->instance, ENABLE); return 0; } return -1; } /*=========================================================================== * 兼容原来 bq25756.c 的简化接口 * 直接替换 WriteReg / ReadReg / WriteReg16 / ReadReg16 的实现 *===========================================================================*/ int BQ_I2C_WriteByte(uint8_t dev, uint8_t reg, uint8_t val) { uint8_t buf[2] = {reg, val}; return BQ_I2C_WriteRetry(dev, buf, 2); } int BQ_I2C_ReadByte(uint8_t dev, uint8_t reg, uint8_t *val) { return BQ_I2C_ReadRetry(dev, reg, val, 1); } int BQ_I2C_Write2Bytes(uint8_t dev, uint8_t reg, uint16_t val) { uint8_t buf[3] = {reg, (uint8_t)(val & 0xFF), (uint8_t)((val >> 8) & 0xFF)}; return BQ_I2C_WriteRetry(dev, buf, 3); } int BQ_I2C_Read2Bytes(uint8_t dev, uint8_t reg, uint16_t *val) { uint8_t buf[2] = {0, 0}; int ret = BQ_I2C_ReadRetry(dev, reg, buf, 2); *val = ((uint16_t)buf[1] << 8) | buf[0]; return ret; }