基于罗姆ML5238+ML610Q486的锂电池BMS管理系统C语言实现
一、系统架构与代码框架
本系统基于ML5238(AFE) 和ML610Q486(MCU),通过SPI实现数据交互,UART与上位机通信。代码采用模块化设计,包含SPI驱动、ML5238寄存器操作、数据采集、保护逻辑、被动均衡及UART通信六大核心模块。
二、头文件与硬件定义
2.1 寄存器与引脚定义(bms_config.h)
#ifndef BMS_CONFIG_H
#define BMS_CONFIG_H
#include "ml610q486.h" // ML610Q486寄存器定义(厂商提供)
// ---------------------- ML5238寄存器地址定义 ----------------------
#define ML5238_REG_VOLTAGE_BASE 0x00 // 电池电压寄存器基地址(每节2字节,共16节)
#define ML5238_REG_CURRENT 0x20 // 电流寄存器(2字节,有符号)
#define ML5238_REG_STATUS 0x22 // 状态寄存器(OV/UV/OC/SC标志)
#define ML5238_REG_THRESHOLD_OV 0x30 // 过充阈值寄存器(单节电压,单位mV)
#define ML5238_REG_THRESHOLD_UV 0x31 // 过放阈值寄存器(单节电压,单位mV)
#define ML5238_REG_THRESHOLD_OC 0x32 // 过流阈值寄存器(电流,单位mA)
#define ML5238_REG_BALANCE_CTRL 0x40 // 均衡控制寄存器(每节1位,1=开启均衡)
// ---------------------- 硬件引脚定义 ----------------------
#define SPI_CS_PORT GPIOB // SPI片选引脚端口(PB0)
#define SPI_CS_PIN GPIO_PIN_0
#define SPI_SCK_PORT GPIOA // SPI时钟(PA5)
#define SPI_SCK_PIN GPIO_PIN_5
#define SPI_MISO_PORT GPIOA // SPI输入(PA6)
#define SPI_MISO_PIN GPIO_PIN_6
#define SPI_MOSI_PORT GPIOA // SPI输出(PA7)
#define SPI_MOSI_PIN GPIO_PIN_7
#define UART_TX_PORT GPIOA // UART发送(PA9)
#define UART_TX_PIN GPIO_PIN_9
#define UART_RX_PORT GPIOA // UART接收(PA10)
#define UART_RX_PIN GPIO_PIN_10
// ---------------------- 系统参数 ----------------------
#define CELL_NUM 12 // 电池节数(12串)
#define V_REF 3.3f // 参考电压(V)
#define RSENSE 0.01f // 电流采样电阻(Ω)
#define OV_THRESHOLD 4200 // 过充阈值(mV/节)
#define UV_THRESHOLD 2500 // 过放阈值(mV/节)
#define OC_THRESHOLD 10000 // 过流阈值(mA)
#endif // BMS_CONFIG_H
三、SPI驱动模块(spi_driver.c)
3.1 SPI初始化与读写函数
#include "bms_config.h"
#include "spi_driver.h"
// SPI初始化(模式0:CPOL=0,CPHA=0)
void SPI_Init(void) {
// 配置GPIO(复用推挽输出)
GPIO_InitTypeDef gpio = {0};
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
// SCK/MOSI
gpio.Pin = SPI_SCK_PIN | SPI_MOSI_PIN;
GPIO_Init(SPI_SCK_PORT, &gpio);
// MISO
gpio.Pin = SPI_MISO_PIN;
GPIO_Init(SPI_MISO_PORT, &gpio);
// CS(软件控制)
gpio.Pin = SPI_CS_PIN;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(SPI_CS_PORT, &gpio);
GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN); // 初始拉高
// 配置SPI外设(ML610Q486 SPI模块)
SPI_TypeDef *spi = SPI1; // 假设使用SPI1
spi->CR1 = SPI_CR1_MSTR // 主机模式
| SPI_CR1_SSM // 软件控制NSS
| SPI_CR1_SSI // 内部NSS拉高
| SPI_CR1_BR_DIV8 // 波特率分频(84MHz/8=10.5MHz)
| SPI_CR1_CPOL_Low // CPOL=0
| SPI_CR1_CPHA_1Edge;// CPHA=0(模式0)
spi->CR2 = SPI_CR2_DS_16BIT; // 16位数据帧
spi->CR1 |= SPI_CR1_SPE; // 使能SPI
}
// SPI发送接收函数(16位数据)
uint16_t SPI_Transfer(uint16_t data) {
SPI_TypeDef *spi = SPI1;
while (!(spi->SR & SPI_SR_TXE)); // 等待发送缓冲区空
spi->DR = data; // 写入数据
while (!(spi->SR & SPI_SR_RXNE)); // 等待接收完成
return spi->DR; // 返回接收数据
}
// ML5238 SPI读写(寄存器地址+数据)
uint16_t ML5238_ReadReg(uint8_t reg_addr) {
uint16_t cmd = (reg_addr << 8) | 0x00; // 读命令:地址+0x0000
GPIO_ResetBits(SPI_CS_PORT, SPI_CS_PIN); // 拉低CS
uint16_t data = SPI_Transfer(cmd); // 发送读命令,接收数据
GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN); // 拉高CS
return data;
}
void ML5238_WriteReg(uint8_t reg_addr, uint16_t data) {
uint16_t cmd = (reg_addr << 8) | (data & 0xFF); // 写命令:地址+数据低8位
GPIO_ResetBits(SPI_CS_PORT, SPI_CS_PIN);
SPI_Transfer(cmd); // 发送写命令
GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN);
}
四、数据采集与保护逻辑(bms_core.c)
4.1 电池电压/电流采集
#include "bms_config.h"
#include "spi_driver.h"
#include "bms_core.h"
// 读取单节电池电压(mV)
uint16_t Read_CellVoltage(uint8_t cell_id) {
if (cell_id >= CELL_NUM) return 0;
uint16_t reg_val = ML5238_ReadReg(ML5238_REG_VOLTAGE_BASE + cell_id);
return reg_val; // ML5238返回值为实际电压(单位mV,精度10mV)
}
// 读取总电流(mA,有符号:正=充电,负=放电)
int16_t Read_Current(void) {
uint16_t reg_val = ML5238_ReadReg(ML5238_REG_CURRENT);
int16_t current = (int16_t)reg_val; // 转换为有符号数
return (int16_t)(current * 1000 / (RSENSE * 1000)); // 计算实际电流(mA)
}
4.2 保护状态判断
// 读取保护状态(OV/UV/OC/SC)
uint8_t Read_ProtectionStatus(void) {
return (uint8_t)ML5238_ReadReg(ML5238_REG_STATUS);
}
// 过充/过放/过流保护处理
void Protection_Check(uint16_t *cell_voltages, int16_t current) {
// 1. 过充保护(单节电压 > OV_THRESHOLD)
for (uint8_t i=0; i<CELL_NUM; i++) {
if (cell_voltages[i] > OV_THRESHOLD) {
// ML5238自动关断充电MOS,MCU记录状态
UART_SendString("Overcharge! Cell:");
UART_SendInt(i+1);
UART_SendString("\r\n");
}
}
// 2. 过放保护(单节电压 < UV_THRESHOLD)
for (uint8_t i=0; i<CELL_NUM; i++) {
if (cell_voltages[i] < UV_THRESHOLD) {
// ML5238自动关断放电MOS
UART_SendString("Overdischarge! Cell:");
UART_SendInt(i+1);
UART_SendString("\r\n");
}
}
// 3. 过流保护(|current| > OC_THRESHOLD)
if (abs(current) > OC_THRESHOLD) {
UART_SendString("Overcurrent! Current:");
UART_SendInt(current);
UART_SendString("mA\r\n");
}
}
五、被动均衡控制(balance_ctrl.c)
5.1 均衡算法与ML5238控制
#include "bms_config.h"
#include "spi_driver.h"
#include "balance_ctrl.h"
// 被动均衡控制(每10秒执行一次)
void Passive_Balance(uint16_t *cell_voltages) {
uint16_t avg_voltage = 0;
// 计算平均电压
for (uint8_t i=0; i<CELL_NUM; i++) avg_voltage += cell_voltages[i];
avg_voltage /= CELL_NUM;
// 开启电压高于平均值10mV的均衡开关
uint16_t balance_ctrl = 0;
for (uint8_t i=0; i<CELL_NUM; i++) {
if (cell_voltages[i] > avg_voltage + 10) { // 阈值10mV
balance_ctrl |= (1 << i); // 第i位置1(开启均衡)
}
}
// 写入ML5238均衡控制寄存器
ML5238_WriteReg(ML5238_REG_BALANCE_CTRL, balance_ctrl);
}
六、UART通信模块(uart_comm.c)
6.1 UART初始化与数据收发
#include "bms_config.h"
#include "uart_comm.h"
// UART初始化(波特率9600,8N1)
void UART_Init(void) {
// 配置GPIO(TX/RX)
GPIO_InitTypeDef gpio = {0};
gpio.Mode = GPIO_MODE_AF_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
gpio.Pin = UART_TX_PIN;
GPIO_Init(UART_TX_PORT, &gpio);
gpio.Pin = UART_RX_PIN;
gpio.Mode = GPIO_MODE_INPUT;
GPIO_Init(UART_RX_PORT, &gpio);
// 配置UART外设(ML610Q486 UART模块)
UART_TypeDef *uart = UART1; // 假设使用UART1
uart->BRR = SystemCoreClock / 9600; // 波特率分频(168MHz/9600≈17476)
uart->CR1 = UART_CR1_TE | UART_CR1_RE | UART_CR1_UE; // 使能收发
}
// UART发送字符串
void UART_SendString(char *str) {
UART_TypeDef *uart = UART1;
while (*str) {
while (!(uart->SR & UART_SR_TXE)); // 等待发送缓冲区空
uart->DR = *str++;
}
}
// UART发送整数(辅助函数)
void UART_SendInt(int num) {
char buf[10];
sprintf(buf, "%d", num);
UART_SendString(buf);
}
// 发送电池状态至上位机(JSON格式)
void Send_BatteryStatus(uint16_t *cell_voltages, int16_t current) {
UART_SendString("{");
UART_SendString("\"cells\":[");
for (uint8_t i=0; i<CELL_NUM; i++) {
UART_SendInt(cell_voltages[i]);
if (i < CELL_NUM-1) UART_SendString(",");
}
UART_SendString("],");
UART_SendString("\"current\":");
UART_SendInt(current);
UART_SendString("}\r\n");
}
七、主程序(main.c)
7.1 系统初始化与主循环
#include "bms_config.h"
#include "spi_driver.h"
#include "bms_core.h"
#include "balance_ctrl.h"
#include "uart_comm.h"
int main(void) {
// 硬件初始化
SystemInit(); // 系统时钟初始化(168MHz)
SPI_Init(); // SPI初始化(ML5238通信)
UART_Init(); // UART初始化(上位机通信)
// ML5238初始化(配置保护阈值)
ML5238_WriteReg(ML5238_REG_THRESHOLD_OV, OV_THRESHOLD); // 过充阈值4.2V
ML5238_WriteReg(ML5238_REG_THRESHOLD_UV, UV_THRESHOLD); // 过放阈值2.5V
ML5238_WriteReg(ML5238_REG_THRESHOLD_OC, OC_THRESHOLD); // 过流阈值10A
uint16_t cell_voltages[CELL_NUM];
int16_t current;
while (1) {
// 1. 采集数据
for (uint8_t i=0; i<CELL_NUM; i++) {
cell_voltages[i] = Read_CellVoltage(i);
}
current = Read_Current();
// 2. 保护判断
Protection_Check(cell_voltages, current);
// 3. 被动均衡(每10秒执行一次)
static uint32_t tick = 0;
if (tick++ % 10000 == 0) { // 假设10ms tick,10000次=100秒(实际需调整)
Passive_Balance(cell_voltages);
}
// 4. 发送状态至上位机
Send_BatteryStatus(cell_voltages, current);
// 5. 低功耗模式(空闲时进入睡眠)
__WFI(); // 等待中断唤醒
}
}
参考代码 基于罗姆(LAPIS)ML5238+ML610Q486的锂电池BMS管理系统 www.youwenfan.com/contentcns/56251.html
八、关键功能说明
8.1 ML5238寄存器操作
- 电压读取:
ML5238_REG_VOLTAGE_BASE + cell_id,返回值为实际电压(单位mV,精度10mV); - 电流读取:
ML5238_REG_CURRENT,返回16位有符号数,需结合采样电阻RSENSE计算实际电流; - 均衡控制:
ML5238_REG_BALANCE_CTRL为16位寄存器,每bit对应一节电池(1=开启均衡)。
8.2 保护逻辑
- ML5238内置硬件保护(过充/过放/过流/短路),MCU通过读取
ML5238_REG_STATUS监控状态; - 短路保护响应时间<10μs,由ML5238自动关断MOS-FET,无需MCU干预。
8.3 被动均衡
- 均衡阈值设为“高于平均电压10mV”,避免频繁启动;
- 均衡电阻内阻6Ω(ML5238内置),均衡电流约
(4.2V-3.7V)/6Ω≈83mA。
九、调试与优化建议
- SPI时序验证:用示波器观察SCK/MOSI/MISO波形,确保数据正确传输;
- 保护阈值校准:通过可调电源模拟过充/过放,验证ML5238保护动作是否正常;
- 均衡效率优化:调整均衡阈值(如5mV)和间隔(如5秒),减少电压差;
- 低功耗测试:测量睡眠模式电流(应<1μA),延长电池续航。
十、总结
本代码实现了基于ML5238+ML610Q486的锂电池BMS核心功能,包括数据采集、保护控制、被动均衡及通信。通过模块化设计,可直接移植到ML610Q486平台,适用于12串锂电池组(如电动自行车、UPS电源)。