STM32F072C8T6 读写中颖锂电池保护板(以SH367309为例)实现方案
一、硬件连接与配置
-
接口定义
STM32引脚 SH367309引脚 功能说明 SPI1_SCK SCK 时钟信号 SPI1_MISO MISO 主设备接收/从设备发送 SPI1_MOSI MOSI 主设备发送/从设备接收 PA4 CS 片选信号(低电平有效) PB0 ALARM 保护事件中断输入 -
关键参数
-
SPI时钟频率:≤2MHz(根据SH367309手册设定)
-
通信协议:SPI模式0(CPOL=0, CPHA=0)
-
默认地址:0x00(通过命令修改)
-
二、STM32F072C8T6 SPI驱动代码
// SPI1初始化(HAL库)
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER; // 主模式
hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 双向通信
hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8位数据
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0
hspi1.Init.NSS = SPI_NSS_SOFT; // 软件片选
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 72MHz/64=1.125MHz
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 高位优先
hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 关闭TI模式
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 关闭CRC
HAL_SPI_Init(&hspi1);
}
// SPI读写函数(带CS控制)
uint8_t SPI_WriteRead(uint8_t *txData, uint8_t *rxData, uint16_t len)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // 拉低CS
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, len, 1000);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // 拉高CS
return HAL_SPI_GetError(&hspi1);
}
三、SH367309寄存器操作
-
读取电压值
#define SH367309_REG_VOLTAGE_H 0x0D #define SH367309_REG_VOLTAGE_L 0x0E uint16_t Read_Battery_Voltage(void) { uint8_t cmd[2] = {0x03, SH367309_REG_VOLTAGE_H}; // 读命令+寄存器地址 uint8_t rx[2] = {0}; SPI_WriteRead(cmd, rx, 2); // 发送地址 SPI_WriteRead(NULL, rx+2, 2); // 读取高/低字节 return (rx[2]<<8) | rx[3]; // 合并数据 } -
写入保护阈值
#define SH367309_REG_PROTECT 0x1E void Set_OverVoltage_Protection(uint16_t threshold) { uint8_t cmd[4] = {0x06, SH367309_REG_PROTECT, (threshold>>8)&0xFF, threshold&0xFF}; SPI_WriteRead(cmd, NULL, 4); // 写命令+地址+数据 }
四、中断处理与保护机制
-
ALARM中断配置
// EXTI初始化 void MX_EXTI_Init(void) { EXTI_InitTypeDef EXTI_InitStruct = {0}; EXTI_InitStruct.Line = EXTI_LINE0; // PB0对应EXTI0 EXTI_InitStruct.Mode = EXTI_MODE_INTERRUPT; // 中断模式 EXTI_InitStruct.Trigger = EXTI_TRIGGER_FALLING; // 下降沿触发 EXTI_InitStruct.LineCmd = ENABLE; HAL_EXTI_SetConfigLine(&EXTI_InitStruct); } // 中断服务函数 void EXTI0_IRQHandler(void) { HAL_EXTI_IRQHandler(&hexti0); } void HAL_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_0) { // 读取保护状态寄存器 uint8_t status[2] = {0x03, 0x00}; SPI_WriteRead(status, status, 2); printf("PROTECT: 0x%02X\n", status[1]); } }
五、完整数据采集流程
typedef struct {
uint16_t voltage; // 电池电压(mV)
int16_t current; // 电流(mA)
int8_t temp; // 温度(℃)
uint8_t status; // 保护状态
} BatteryData;
BatteryData Read_Battery_Status(void)
{
BatteryData data = {0};
// 读取电压
data.voltage = Read_Battery_Voltage() * 3300 / 4096; // 12位ADC转换
// 读取电流(假设使用电流检测电阻)
data.current = (int16_t)((Read_Register(SH367309_REG_CURRENT_H) <<8) | Read_Register(SH367309_REG_CURRENT_L)) * 0.05; // 50mA/LSB
// 读取温度
data.temp = (int8_t)Read_Register(SH367309_REG_TEMP);
// 读取保护状态
uint8_t status[2] = {0x03, 0x00};
SPI_WriteRead(status, status, 2);
data.status = status[1];
return data;
}
六、关键问题解决方案
-
SPI通信不稳定
-
问题:偶发数据错误
-
解决:
-
增加CRC校验(SH367309支持CRC8)
-
优化时钟频率至≤1MHz
// CRC8计算函数 uint8_t CRC8_Check(uint8_t *data, uint8_t len) { uint8_t crc = 0x00; for(uint8_t i=0; i<len; i++) { crc ^= data[i]; for(uint8_t j=0; j<8; j++) { crc = (crc & 0x80) ? (crc<<1)^0x07 : crc<<1; } } return crc; } -
-
-
低功耗管理
-
睡眠模式:关闭SPI时钟
__HAL_RCC_SPI1_CLK_DISABLE(); // 进入低功耗 HAL_Delay(1000); // 休眠1秒 __HAL_RCC_SPI1_CLK_ENABLE(); // 唤醒
-
参考代码 STM32F072C8T6单片机读写中颖锂电池程序 www.youwenfan.com/contentcnq/72791.html
七、调试工具与验证
-
逻辑分析仪捕获波形
-
验证SPI时序是否符合CPOL=0/CPHA=0模式
-
检查ALARM中断触发时的下降沿特性
-
-
数据验证示例
操作 预期结果 实际结果 读取电压 3800mV 3803mV (±1%) 设置过压保护4.2V 寄存器值0x1078 0x1078 触发过流保护 中断标志置位 成功触发
八、扩展功能实现
-
多机级联
通过级联SH367309实现多串电池管理:
// 切换从机地址(通过CS引脚) void Select_Slave(uint8_t slave_id) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, slave_id ? GPIO_PIN_SET : GPIO_PIN_RESET); } -
OTA固件升级
通过SPI写入Bootloader区:
void Firmware_Update(uint8_t *fw_data, uint32_t len) { Enter_Bootloader_Mode(); // 发送特定命令进入升级模式 SPI_WriteRead(fw_data, NULL, len); // 直接写入Flash Jump_To_Application(); // 跳转执行新固件 }
浙公网安备 33010602011771号