基于罗姆ML5238+ML610Q486的锂电池BMS管理系统C语言实现

基于罗姆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寄存器操作

8.2 保护逻辑

8.3 被动均衡

九、调试与优化建议

  1. SPI时序验证:用示波器观察SCK/MOSI/MISO波形,确保数据正确传输;
  2. 保护阈值校准:通过可调电源模拟过充/过放,验证ML5238保护动作是否正常;
  3. 均衡效率优化:调整均衡阈值(如5mV)和间隔(如5秒),减少电压差;
  4. 低功耗测试:测量睡眠模式电流(应<1μA),延长电池续航。

十、总结

本代码实现了基于ML5238+ML610Q486的锂电池BMS核心功能,包括数据采集保护控制被动均衡通信。通过模块化设计,可直接移植到ML610Q486平台,适用于12串锂电池组(如电动自行车、UPS电源)。

专注于matlab/simulink,电子电路,编程