LTC6804是一款专门用来做多节电池电池组的监测芯片,最高可监测12节电池,官方误差是低于1.2mv,12 个通道的最快采样速度可以达到290us。 芯片分为两种型号,6804-1和6804-2,区别在于,6804-1采用级联的形式(级联控制),6804-2采用并联形式(分开控制)。 除开硬件连接之外,这两种型号的操作都是大同小异,基本可以视为一样,本文以6804-1为例进行讲解。 原理图和手册中的推荐一样,就不贴出来了,MCU与芯片的通信方式采用四线SPI,这种通信方式很常见,各种MCU的驱动也好找。(看了数据手册,芯片似乎还支持IIC和2线通信,不过我没有用)
+ u/ w" x7 Q- B6 ]! v5 d! M( n
! f8 V/ h3 Q4 h( y3 ] ?5 j2 R; _ 下载下来的代码是C++文件,不能直接在STM32上使用,需要进行一些修改。 首先修改后缀名,改成C文件,然后打开LTC6804.c文件。
* Z* p- r$ b( s8 N9 o第一步进行驱动的移植: - void spi_write_array(uint8_t len, // Option: Number of bytes to be written on the SPI port
; q8 K7 a2 n$ d0 a3 [' [0 Q - uint8_t data[] //Array of bytes to be written on the SPI port
9 ]; y. j- g' ]6 {& g( ?8 Z; s - )
( q1 y' f4 b0 n" J) w h1 X - {+ t) v6 q- v9 ]1 b. B# |6 ^5 O: w
- uint8_t i;9 c, I/ ^& ?% Q) J1 p
- $ l& U$ b1 A# {; Q
- for(i = 0; i < len; i++), o9 T4 c* x/ @* t# _, N& {! C; V$ L9 J
- {6 Z- X% Q7 p* m# a0 s% z3 E
- SPI2_Send_byte((int8_t)data[i]);# n' e. ?1 |0 B1 I% I: [ h" G
- }
6 m% v; M: g- L- L - }
复制代码- void spi_write_read(uint8_t tx_Data[],//array of data to be written on SPI port o. L( n L; l4 y( h5 P) V
- uint8_t tx_len, //length of the tx data arry! A$ q" K! {0 `, q: k2 W7 \) `
- uint8_t *rx_data,//Input: array that will store the data read by the SPI port0 p! B$ Q6 g$ l8 u
- uint8_t rx_len //Option: number of bytes to be read from the SPI port, g+ T+ H2 X3 S/ t9 A p
- )( `6 ~' W# x2 R# |7 i6 L7 }
- {
4 f/ r, K, ] s ~! w - uint8_t i;: q; e5 T1 L1 S& i& w: q0 J
5 u0 X5 W4 F; @$ y j- e f5 w K- for(i = 0; i < tx_len; i++)( r! P1 E9 p% A7 u( O1 g1 y
- {1 T+ h+ E% B. R3 ]$ N4 @ u* I6 }' t
- SPI2_Send_byte(tx_Data[i]);4 U! z0 c3 B) J+ r+ }# n2 e) B& D$ x
- }' |: X0 W! E% O. d( H" A# M2 ?2 n
7 [# h2 h7 ^4 d: P1 ^) ], A C- for(i = 0; i < rx_len; i++)
( M* v& I' A% }+ V* s - {% k- r+ ~5 L9 m7 i0 ?" Y
- rx_data[i] = (uint8_t)SPI2_Receive_byte();
7 S8 [6 @6 S3 Y Z3 @0 O; L; d - }* k+ E- m. n+ A
6 ~( Q% G0 Q8 i l9 J$ W- }
复制代码
1 @6 b* W- }$ B5 t% Z4 ~+ Q只需要把自己的SPI驱动替换上去就可以了。 * C. A3 `' G4 k" g! ^
- void wakeup_idle()
0 L: @ N$ Z* K1 ?5 u - {
8 M W" v# f- T! A. { - output_low(LTC6804_CS);
# @9 M3 L* P6 \- N0 Q9 g; O - delayMicroseconds(2); //Guarantees the isoSPI will be in ready mode
5 O2 T$ }. T2 F2 d, V2 A m8 Q+ M+ B - output_high(LTC6804_CS);
5 f( P- D& }4 {4 o5 n. b" Z {1 W - }
复制代码 " |. f! h9 d' `; g, Z% F5 ~
自己把这个函数中间的部分实现,或者替换成自己的函数: - void wakeup_idle(void)
6 V) F7 x) u+ [2 m3 z% K8 o( Q - {! t$ a6 N# } ], v; p k# {
- output_low(LTC6804_CS);
- M5 x# n, }/ F# h" R - delay_ms(4); //Guarantees the isoSPI will be in ready mode$ E( ~8 B7 F0 F1 @& U
- output_high(LTC6804_CS);
" R* C4 K8 Q" _% c - }
复制代码
9 N% W- n, `3 i' p& F* P下面是原版的初始化函数: - void LTC6804_initialize()6 e( t2 n% d; a" ~, v
- {; N& A+ `" ?: b% \0 ?; A
- quikeval_SPI_connect();1 [7 O6 p& M% q" y6 C
- spi_enable(SPI_CLOCK_DIV16); // This will set the Linduino to have a 1MHz Clock
& y' ~' x& Z0 J0 W1 b3 p0 V1 A - set_adc(MD_NORMAL,DCP_DISABLED,CELL_CH_ALL,AUX_CH_ALL);
, a! S' I! K [3 L" u - }
复制代码
2 s m* h4 Q+ D4 o& T/ K我把它根据自己的实际情况修改一下 : - void LTC6804_initialize(void)
& p6 Q% E. @! A2 _- T% T) i4 s - {! k% S- M5 a2 J. K& L
- Drive_LTC6804_Spi_Init();//SPI外设初始化# X# k9 m: r. r7 k
- init_cfg(); //配置LTC6804的寄存器
& K! t" g( K% U' `9 |, W - set_adc(MD_FILTERED,DCP_DISABLED,CELL_CH_ALL,AUX_CH_GPIO1);//设置转换方式等
/ F4 Y8 Q, H4 [8 |/ t2 U h - wakeup_sleep();//唤醒芯片
; V9 b8 `$ ~4 w9 H - LTC6804_wrcfg(TOTAL_IC,tx_cfg);//把上面的设置写入芯片
/ b# d& H) d$ V+ G - if (LTC6804_rdcfg(TOTAL_IC,rx_cfg) == -1) //检查一下到底有没有配置成功& x: ?" R2 }( U3 j% w# I( p9 _$ v
- {, r2 ^7 s" o. k5 t: }4 i
- printf("LTC6804_MODULAR INIT NG!\n\r");
- m/ f& M/ d6 K: @! z% i& D! f - }- f: z7 Z' k& E! B; _; s
- else
/ r7 S6 B" W+ x" I- M; ] - {
/ \6 Q3 v' L) K2 d, ~7 g - printf("LTC6804_MODULAR INIT OK!\n\r");
+ x- I+ l+ e! t; }( Z - }
+ Z4 i$ ]9 O. I. K6 J
# T# x% U) G' z( Q0 ~: u- }
复制代码- //手册第49页
0 {9 _$ \2 }% c X& Q - /* 寄存器 8 7 6 5 4 3 2 1 */
' X# W! ]: `& O. J - //CFGR0 RD/WR GPIO5 GPIO4 GPIO3 GPIO2 GPIO1 REFON SWTRD ADCOPT, a, Y# V9 C- p
- //CFGR1 RD/WR VUV[7] VUV[6] VUV[5] VUV[4] VUV[3] VUV[2] VUV[1] VUV[0]
" ?$ L/ q0 X w ~) K8 M& F' q/ i' F - //CFGR2 RD/WR VOV[3] VOV[2] VOV[1] VOV[0] VUV[11] VUV[10] VUV[9] VUV[8]- }# [! A* w. B6 ^8 A+ d5 C
- //CFGR3 RD/WR VOV[11] VOV[10] VOV[9] VOV[8] VOV[7] VOV[6] VOV[5] VOV[4]
& g$ |* t4 d' O W- r - //CFGR4 RD/WR DCC8 DCC7 DCC6 DCC5 DCC4 DCC3 DCC2 DCC1, a6 l6 F9 t3 R# s; d7 i' y- y
- //CFGR5 RD/WR DCTO[3] DCTO[2] DCTO[1] DCTO[0] DCC12 DCC11 DCC10 DCC93 M+ Y( Y/ q- Z4 ?& Q @% k; _! h
- void init_cfg(void)4 F) }6 `' x' I- T. q$ n
- {5 s8 C/ j0 [9 ^5 p
- int i;+ q9 T: k8 t; N4 \
- ) {8 B3 B9 d4 D& Z7 ?, Z/ g
- for(i = 0; i<TOTAL_IC;i++); F5 U9 Z1 ^. |0 o: Z+ F, @2 [
- {8 i4 B5 o4 P' {' c7 X3 p
- tx_cfg[i][0] = 0xFE ; //GPIO引脚下拉电路关断(bit8~bit4) | 基准保持上电状态(bit3) | SWTEN处于逻辑1(软件定时器) | ADC模式选择为0: S1 ?* H" L4 g9 d3 v& Y k
- tx_cfg[i][1] = 0x00 ; //不使用欠压比较功能
- Y# P5 P( u' R3 E - tx_cfg[i][2] = 0x00 ; //不使用过压比较功能% J! w- o8 O/ q# M3 r) `( J# ^
- tx_cfg[i][3] = 0x00 ;
6 V- C" A* \4 C* m: A - tx_cfg[i][4] = 0x00 ; //不使用电池放电功能
: X& G% |' d6 O* S; I8 m - tx_cfg[i][5] = 0x00 ; //放电超时时间
' T% C, z# D5 E7 I - }, R, B% Y7 c: O
- }
复制代码
+ ]$ p6 C- o' x6 J3 q$ w5 o3 Y8 o, G
; \, R. L8 O% [+ J# V3 ]# e, ]这里只使用了最基本的电压采集,其他的功能都没有用。 上面的代码里面有一个宏 :TOTAL_IC。 这个宏是用来定义一共有几片LTC6804-1的,比如我这次使用了2片,那么它的值就是2. 上面的寄存器设置,可以参考手册: 比如需要追加一些别的功能,便可以根据手册的寄存器进行设置,比如设置报警,设置均衡,设置其他的功能。 以上初始化部分就完成了,或者说移植就完成了,然后便不必对代码文件进行任何修改就可以直接使用,接下来是采集部分: 我是把采集放在main中进行。 - /* 唤醒6804 */0 X6 |9 Z/ m6 m; e7 q* O6 T
- wakeup_sleep();& F% t) a* n _1 H9 D" J+ J
- /* 启动电压采集 */" ~5 h9 J0 d# J
- LTC6804_adcv();5 F" w6 I# m% M4 E, o' a" h
- delay_ms(50);& j" s- v$ ]4 v4 v
- /* 读取电压 */
4 c& x9 k# `5 _& b( _) w - res = LTC6804_rdcv(0, TOTAL_IC, vol_buff);
复制代码
; _7 z5 P* M! n5 F 以上便可以采集出电压了。
$ g: I/ [" _/ k% X4 i! ^) p# I※如果发现几个级联起来的6804,有些片子可以运行正常通信,有些又不可以运行甚至无法通信,在排除硬件的原因以后,可以查看一下这个地方:wakeup_sleep(),试着把唤醒时间设置的长一些。 7 e7 |# h0 \, y3 C, n3 X
最后:可以看出来,精度还是不错的~ 9 j2 H A% o/ U* Y0 L
|