LTC6803电压采集实战:STM32 SPI驱动与BMS级联隔离设计
2026/9/16 4:48:28 网站建设 项目流程

简介:面向LTC6803在电池管理系统(BMS)中的实际应用,压缩包提供了一套完整的电压采集程序,包内仅有2个文件——一个C源文件和一个头文件,体积仅5KB,却涵盖了芯片初始化、SPI通信、电压与温度读取等关键模块。目前已有632人学习下载。程序基于SPI协议实现主控与LTC6803的数据交互,涉及寄存器配置、采样率设置、异常电压判断以及数据滤波处理,可针对多节串联电池组进行实时监测,适用电动汽车、储能系统等需高可靠电压监控的场景。LTC6803的2.5μV高分辨率、过欠压保护、自供电模式以及温度检测功能在代码中均有体现,开发者还能借助LTC8584扩展更多通道,构成更完整的BMS监测方案。整体代码短小精悍、模块划分清晰,作为BMS开发的入门参考或直接移植都非常适合,能帮助读者理解从底层通信到应用层数据处理的高精度电池监控完整流程。

1. LTC6803 电压采集:一块 12 节电池监测芯片如何撑起 BMS

把 12 节串联锂电池每一节的电压差分地采回来,是 BMS 电压采集单元最核心的活。LTC6803 一片最多测 12 节电芯,转换结果存在 16 位寄存器里,SPI 直连单片机,逐节放电均衡也在这颗芯片上。它和后来的 LTC6804 最大的区别是没有内置 isoSPI,跨板通信的隔离要在外围自己加,很多工程师第一版程序读数乱跳,根源就在这条链路上。下面按寻址与寄存器布局、STM32F103 上的最小采集程序、多片扩展与滤波模式、现场验证四段推进。适合正在用 STM32 写 BMS 从板程序、以及准备把 12 节以上电池组拆成多片 LTC6803 级联的开发者。标题里的 ltc8584 大概率是文件名笔误,选型认准 LTC6803 系列即可。

2. LTC6803 的命令长什么样:寻址、命令码与 PEC 三段式

2.1 四种后缀怎么选:-1/-3 命令寻址,-2/-4 引脚寻址

LTC6803 按后缀分成四颗料:LTC6803-1/-3 的设备地址写在命令字节里,多片可以挂同一条 SPI 总线,主机靠命令字里的地址位区分从机,适合少片级联;LTC6803-2/-4 用 A0-A3 四根引脚把地址硬件固定死,一片一个片选,接线直观,但片选 GPIO 会随片数线性增长。此外 -3/-4 比 -1/-2 多了滤波 ADC 模式,采样速度和噪声的取舍更灵活,这也是新设计直接选 -3 的常见理由。单芯片起步阶段选 -1 或 -3 都行,网上例程数量最多的是 -1,教程密度决定了排错效率。

实际项目里常见做法是不管后缀一律把地址定为 0:单芯片场景下,-1/-3 的命令字节地址位留空,-2/-4 把 A0-A3 全部接地。这样换来同一套驱动代码在四颗料上都能跑,差异只在多片时体现:-1/-3 靠软件写命令地址,-2/-4 靠布线给硬件地址。我一般建议优先选 -3,既保留命令寻址的级联能力,又拿到滤波模式,成本和 -1 几乎拉不开差距。

2.2 命令格式与 PEC:为什么每个命令后面都要带一个 CRC

LTC6803 的 SPI 报文是固定三段式:1 字节命令码加 1 字节 PEC;写配置或回读数据时,数据段之后再补 1 字节 PEC。PEC 是多项式 x^8+x^2+x+1、初始种子 0x41 的 CRC-8,与 SMBus 的 CRC 同源。种子取 0x41 而不是 0x00,是为了避免全零数据算出全零校验结果,这样 PEC 还能顺手当“从机是否在线”的判定依据:

/* LTC6803 的 PEC 计算:种子 0x41,多项式 0x07,逐字节异或后逐位反馈 */ uint8_t ltc6803_pec(uint8_t *data, uint8_t len) { uint8_t crc = 0x41; /* 每段报文重新播种,不能连续累加 */ for (uint8_t i = 0; i < len; i++) { crc ^= data[i]; /* 先异或当前字节 */ for (uint8_t bit = 0; bit < 8; bit++) { if (crc & 0x80) crc = (uint8_t)((crc << 1) ^ 0x07); /* 最高位为 1 做多项式反馈 */ else crc <<= 1; } } return crc; }

这段代码的逻辑是先把本字节异或进 crc,再按位做移位反馈。参数上要注意两点:len 是当前段的字节数,命令段传 1,数据段传数据长度;函数每次进来都用 0x41 重新播种,不能在两个段之间复用上一次的 crc 值。

注意:命令段和数据段的 PEC 各自独立计算。发送 WRCFG 时命令段 1 字节、数据段 6 字节,但两段的种子都是 0x41。最常见的移植错误是把 PEC 做成整包连续计算,结果第一包能通过,第二包起全部报错。

常用命令码在数据手册里有一整张表,工程上先记下面这几个就够:

命令命令码方向用途
WRCFG0x01写 6 字节配置寄存器
RDCFG0x02回读配置,校验写入是否生效
STCVAD0x10启动 12 节电芯电压转换
RDCVA/B/C/D0x04/0x06/0x08/0x0A分四组回读 12 节电压
STOWAD0x28启动开路检测,排查采样线断线
STSAD0x20自检转换,定位 ADC 硬件故障

表中的命令码是地址为 0 时的取值。用 -1/-3 级联时,地址位按数据手册的位映射叠进命令字节,地址写错的典型现象是回读全是 0xFF 且 PEC 不过,靠这一条就能把寻址问题从信号完整性里摘出来。

2.3 CFG 寄存器组:全零能开机,但均衡和关断要单独配

WRCFG 发的是 6 字节配置寄存器,加上数据段 PEC 共 7 字节。6 字节里,CFG1 的 bit6:5 是 ADC 模式选择,bit7 是关断控制,后续字节放着 GPIO 配置和 12 个放电控制位(DCC)。大部分例程的初始配置就是六个 0x00,对应 7kHz 模式、放电全关、GPIO 输出低,这个配置在常温下能正常工作,也是调通第一路电压前最稳妥的选择。

uint8_t cfg[6] = {0}; /* 7kHz 模式,放电全部关闭,不上电就均衡 */ uint8_t frame[9]; frame[0] = 0x01; /* WRCFG,地址 0 */ frame[1] = ltc6803_pec(frame, 1); memcpy(&frame[2], cfg, 6); frame[8] = ltc6803_pec(&frame[2], 6); /* 数据段独立算 PEC */ ltc6803_cs_low(); for (int i = 0; i < 9; i++) spi_write(frame[i]); ltc6803_cs_high();

这里 frame 的布局是命令码、命令 PEC、6 字节配置、数据 PEC,一共 9 字节,CS 必须在整个发送期间保持低电平。参数上要改的是 cfg 数组:需要低功耗休眠时把 CFG1 的 bit7 置 1,需要均衡某节电芯时把对应 DCC 位置 1。注意 DCC 打开期间均衡电流会流过测量路径,读数偏低,所以采集电压前必须先清 DCC 再启动转换,这个顺序写进主循环就排掉了一大半现场问题。

3. STM32F103 上的 LTC6803 最小采集程序:从 SPI 初始化到 12 路电压解包

3.1 SPI 初始化:Mode 0、分频压到 1MHz 以下

LTC6803 的 SCLK 上限是 1MHz,这是数据手册给死的参数。网上不少例程直接拿 STM32F103 的 SPI 默认分频跑出几兆赫兹,表现通常是“偶尔能读出来,读多几轮就 PEC 报错”。SPI 初始化把引脚配成复用推挽,模式选 Mode 0,即 CPOL=0、CPHA=0,数据在上升沿锁存、下降沿移出,分频系数按 PCLK2=72MHz 选 /128,得到约 562.5kHz,留足沿余量:

/* SPI1:PA5=SCK,PA6=MISO,PA7=MOSI;片选用普通 GPIO 手动控制 */ void ltc6803_spi_init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN; GPIOA->CRL = (GPIOA->CRL & ~0xFFF00000) | 0x00B33333; SPI1->CR1 = SPI_CR1_MSTR | (0x06 << SPI_CR1_BR_Pos) /* BR=0b110,PCLK2/128=562.5kHz */ | SPI_CR1_SSM | SPI_CR1_SSI; /* NSS 软件管理,片选交给 GPIO */ SPI1->CR1 |= SPI_CR1_SPE; }

分频位 BR[2:0] 置 0b110 就是 /128,四舍五入后仍低于 1MHz 上限。这里把 NSS 配成软件管理,片选脚另用普通 GPIO 的原因是:LTC6803 要求 CS 在整个报文期间保持低电平,报文结束后再拉高,硬件 NSS 在这种半双工时序里反而制造毛刺。初始化完成后可以先发一条 RDCFG,能回出 7 字节说明 SPI 通路基本通了。

提示:量产板想省成本把分频提上去之前,先确认 SPI 信号经过数字隔离器后沿没有变缓。隔离器引入的传播延时会让有效采样窗口变窄,SCLK 余量只小不大。

3.2 启动转换与四组回读:STCVAD 之后要等,不是发了就收

STCVAD 命令只管“启动”,ADC 跑完需要时间,7kHz 模式下 12 节电芯全转换的典型时间在毫秒级。LTC6803 没有专门的 busy 引脚,常见做法是延时取最大值再加余量,工程上先用 20ms 固定延时,跑通后再用示波器压时间:

/* 启动转换:2 字节报文(命令码 + PEC),CS 低电平期间发完 */ void ltc6803_start_conv(void) { uint8_t frame[2]; frame[0] = 0x10; /* STCVAD,地址 0 */ frame[1] = ltc6803_pec(frame, 1); ltc6803_cs_low(); for (int i = 0; i < 2; i++) spi_write(frame[i]); ltc6803_cs_high(); delay_ms(20); /* 7kHz 模式留足转换与建立时间 */ }

转换完成后,12 节电芯的原始码值被拆进四个寄存器组,每组 3 节、每节 16 位。回读分四次进行,RDCVA 到 RDCVD 各收 6 字节数据加 1 字节 PEC:

命令覆盖电芯回读字节数数据格式
RDCVA 0x04第 1~3 节7每节 2 字节,LSB first
RDCVB 0x06第 4~6 节7同上
RDCVC 0x08第 7~9 节7同上
RDCVD 0x0A第 10~12 节7同上
/* 读 12 节电压原始码;成功返回 0,PEC 失败返回 -1 */ int ltc6803_read_cells(uint16_t cells[12]) { static const uint8_t rdcv[4] = {0x04, 0x06, 0x08, 0x0A}; for (int g = 0; g < 4; g++) { uint8_t cmd[2] = {rdcv[g], 0}; uint8_t buf[7]; cmd[1] = ltc6803_pec(cmd, 1); ltc6803_cs_low(); for (int i = 0; i < 2; i++) spi_write(cmd[i]); for (int i = 0; i < 7; i++) buf[i] = spi_read(); /* 半双工读,MISO 上的波形才是数据 */ ltc6803_cs_high(); if (ltc6803_pec(buf, 6) != buf[6]) return -1; /* 数据段 PEC 不过,整包丢弃 */ for (int i = 0; i < 3; i++) { uint16_t v = buf[i * 2] | (buf[i * 2 + 1] << 8); /* LSB first */ cells[g * 3 + i] = v; } } return 0; }

回读期间主机要把 MOSI 持续输出固定电平,从机在这段时间只往 MISO 上推数据。解包时先收低字节再收高字节,这是小端序,和 LTC6804 保持一致;如果你移植的驱动源头是早起 LTC6802 的汇编例程,端序往往是反的,表现是电压值和真实值差很多但不是毫无规律。PEC 不过时不要盲目重试三次以上,先停下来抓波形,否则会把线缆接触问题误判成算法问题。

3.3 换算与上报:码值乘 1.5mV,端序先搞清楚

LTC6803 的分辨率是 1.5mV/LSB,16 位寄存器存的是原始码值,但实际量程 0~5V 只用到约 3333 个码。换算成毫伏直接用乘加:

uint16_t mv = cells[n] * 15 / 10; /* 码值 x 1.5mV,先乘后除减少截断误差 */

后续上报建议把 mv 结果放在 uint16_t 数组里,主循环固定 100ms 轮询一次,不要放在定时器中断里实时换算,也不要每次循环都重新发 STCVAD。采集周期固定之后,均衡算法里的电压变化率计算才不会被抖动干扰。整包电压要么在 BMS 主控里直接比较过压欠压门限,要么走 Modbus RTU 上送给上位机看曲线,两种方式都能用,关键是把 12 路电压当作一个整体快照对外发布,别逐路单独上报,否则上位机拼包时总会差一个周期。

4. 从 12 节到 96 节:LTC6803 级联、SPI 隔离与 ADC 滤波模式怎么选

4.1 多片级联的两种接法:共用 CS 还是逐片片选

电池包超过 12 节串联后,方案就变成多片 LTC6803 叠加。工程上分两派:一派用 -1/-3,多颗芯片共用一条 SPI 总线,靠命令字节里的地址位分别访问,CS 共用一条线;另一派用 -2/-4,每片一个 CS,主机通过片选轮询。前者省 GPIO、报文结构统一,后者排查直观,单点故障不会拖垮整条总线。

我实际做过的储能项目里,16 串和 24 串的从板都是两片 LTC6803 打底,用 -3 的共用 CS 接法最省事。上电自检时逐地址发 RDCFG,能收到回包的地址在系统里标记在线,后续轮询只访问在线的片,这个步骤要放在开机自检里做,因为电池模组在维护现场会整组插拔,固件不能假设第二片一定在。

4.2 SPI 隔离放在哪一侧:跨板通信先解决地环路

LTC6803 没有 isoSPI,跨板走 SPI 必须加数字隔离器。选型看三件事:通道数、方向配比、隔离耐压。SPI 四条线里 SCK、MOSI、CS 是主机到从机方向,MISO 是从机到主机方向,所以至少需要一条反向通道,常见的 4 通道 3+1 配置可以直接用。隔离器两侧的电源也必须完全分开,两侧参考地之间除了隔离器内部寄生电容外不应该有任何导通回路。

踩过一个具体现场:读数在电芯均衡启动的瞬间整体偏高 30mV 以上,定位到最后是隔离器两侧共地没彻底切开,均衡电流在地环路上的压降被叠加进了采样参考。检查方法很简单,量隔离器两侧参考地之间的直流阻抗,应该接近开路。这个点在原理图评审阶段就要确认,等 PCB 打样回来再改就要飞线了。

4.3 ADC 三档采样节奏:快扫、慢扫、滤波扫各干各的

-3/-4 的 CFG1 里有两位 ADC 模式选择,工程上当三档用,参数见下表:

MD1:MD0采样节奏典型耗时用途
007kHz 默认约 4ms/次常规巡检,平衡功耗与速度
0126kHz 快速约 1.5ms/次均衡开启前的快速快照
102kHz 滤波约 12ms/次静态搁置时的终值确认
111kHz 滤波约 24ms/次高精度分容、出厂校验

滤波模式本质是 ADC 前端做多次采样平均,代价是转换时间拉长。项目里典型节奏是:正常运行用 7kHz 扫全组,发现某节电压掉得快时切 26kHz 快扫确认是不是瞬时负载误报,等电池组静置后再切 2kHz 滤波档做终值。模式切换的关键是写 CFG 之后必须重新发 STCVAD,只改寄存器不重启转换,采到的还是旧模式的数据:

uint8_t cfg[6]; ltc6803_read_cfg(cfg); /* 先读回当前配置 */ cfg[0] = (cfg[0] & ~0x60) | (md << 5); /* MD 位写入 CFG1 的 bit6:5 */ ltc6803_write_cfg(cfg); ltc6803_start_conv(); /* 模式变了,转换必须重新启动 */

这里 md 取值 0~3,对应上表四档。注意 -1/-2 没有滤波模式,写入 10 或 11 不会生效,移植代码时要做后缀判断。另外转换期间电池侧的基准退耦电容不要省,用 X7R 介质、容值不小于 1µF,否则低字节会随机跳动,表现是电压在 1.5mV 的分辨率步进上来回抖,查到最后往往不是程序问题。

5. 现场最容易翻车的三个检查点:LTC6803 的 SPI 波形、PEC 边界和移植差异

5.1 CS 与 SCLK 的相对时序

示波器两通道同时抓 CS 和 SCLK,确认 CS 拉低到第一个 SCLK 上升沿之间至少留一个时钟周期的建立时间,报文结束时 SCLK 停在空闲电平期间再拉高 CS。很多“第一包好、第二包坏”的问题,其实是 CS 提前拉高把最后一字节 PEC 截断了。这个检查在冷启动抓包时看不到,要等到连续跑 10 秒以上再抓,时序裕量不足时故障是间歇性的。

5.2 PEC 报错先查三处

第一处是端序,RDCV 回读先收到低字节;第二处是分帧,命令段和数据段的 PEC 各自从 0x41 种子重算;第三处是 MISO 线上的负载。如果板子在 MISO 上加了 RC 滤波且串阻过大,回读的最后一字节会慢半拍,把容值降到百皮法以下或减小串阻即可。另外,测试时不要把探头直接搭在 SDO 引脚上超过几分钟,探头电容会和走线电容叠加,改变信号边沿,导致误判成芯片问题。

5.3 从 LTC6804 例程移植的差异

LTC6804 例程里的 PEC 算法和 RDCV 分组结构可以直接用,但命令码不同,启动转换的流程也不同。6803 的 STCVAD 不带 isoSPI 地址,直接把启动命令对应到 RDCV 读取流程即可;最容易漏的是把 6804 的滤波转换等待时间照搬到 6803 的快扫上,白白浪费巡检周期。BMS 测试里最典型的判定是:等压差在阈值附近反复横跳时,先确认采样节奏是不是被旧的延时参数拖慢了。真要压时间,就把 STCVAD 后的固定延时改成长度可调的宏,再对着示波器的转换完成时刻把 20ms 一点点压下去——这一步做完,整个巡检周期的余量才算真正到了你自己手里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询