LTC6802是一款完整的电池监视 IC,它内置一个 12 位 ADC、一个精准电压基准、一个高电压输入多工器和一个串行接口。每个 LTC6802-2 能够在总输入电压高达 60V 的情况下测量 12 个串接电池的电压。所有 12 个输入通道上的电压测量都能在 13ms 的时间之内完成。可以把多个 LTC6802-2 器件串联起来,以监视长串串接电池中每节电池的电压。每个 LTC6802-2 具有一个可单独寻址的串行接口,因而允许把多达 16 个 LTC6802-2 器件连接至一个控制处理器并同时运作。LTC6802采用一个三线或者四线的SPI的接口与MCU进行通讯,程序代码如下
//本程序主要完成单体电池电压采集,电池组均衡、保护,SOC计算以及与外部设备的通讯等功能
//-----------------------------------------------------------------------------
// Includes(包含头文件)
//-----------------------------------------------------------------------------
#include <compiler_defs.h>
#include <C8051F500_defs.h> // SFR declarations
#include <stdio.h>
#include <math.h>
//-----------------------------------------------------------------------------
// Global Constants(全局常量定义)
//-----------------------------------------------------------------------------
#define SYSCLK 24000000 // 系统时钟
#define INT_DEC 50 // 电流采样数据累加计数值
#define ANALOG_INPUTS 15 // 采集数据的数组维数
#define SPI_CLOCK 500000 // SPI最大时钟
#define SPI_WRITE 0x01 // 写配置寄存器
#define SPI_READ 0x02 // 读配置寄存器
#define RDCV 0x04 // 读电压寄存器
#define SPI_READ_BUFFER 0x20 // 从控制器向主控制器发送一些列字节
#define ERROR_OCCURRED 0x40 // 从控制器告诉主控制器发生错误的指示符
#define CFGR0 0x19 // 初始化LTC6802参数
#define CFGR1 0x00 // 初始化LTC6802参数
#define CFGR2 0x00 // 初始化LTC6802参数
#define CFGR3 0xF0 // 初始化LTC6802参数
#define CFGR4 0x00 // 初始化LTC6802参数
#define CFGR5 0xFF // 初始化LTC6802参数
#define STCVAD 0x10 // 初始化LTC6802参数,电压开始转换
#define STTMPAD 0x30 // 初始化LTC6802参数,温度开始转换
#define RDTMP 0x08 // 读取LTC6802存储的温度寄存器命令
#define PLADC 0x40 // 向LTC6802写入查询ADC转换状态命令
//-----------------------------------------------------------------------------
// Pin Declarations(引脚申明)
//-----------------------------------------------------------------------------
sbit SW1 = P1^5; // SW1 ='0' means switch pressed
sbit SW2 = P1^6; // SW2 ='0' means switch pressed
sbit Poll_IF=P1^1;
//-----------------------------------------------------------------------------
// Global Variables(全局变量定义)
//-----------------------------------------------------------------------------
unsigned int idata RESULT[ANALOG_INPUTS-5]; // 采集所得的数据的数组
unsigned long idata Data_Current; // 采集的电流值
unsigned char idata IF = 0; // AD采样一个周期结束的标志
float idata PWM_Dutyratio; // PWM输出占空比
unsigned char SPI_Array[ANALOG_INPUTS-9]={0x19,0x00,0x00,0xF0,0x00,0xFF}; // 采集所得的数据的数组
U8 Command = 0x00; // 命令传输字节
U8 SPI_Data_Array[ANALOG_INPUTS+20] = {0}; // 从LTC6802读取的数据
U8 SPI_Data_Temp[ANALOG_INPUTS] = {0}; // 从LTC6802读取的数据
U32 SPI_Data_Current; // 电流采集值
U16 SPI_Data[ANALOG_INPUTS-5] = {0}; // 单体电压采样数组
U8 data_convert=0; // 数据处理的中间变量
//-----------------------------------------------------------------------------
// Global Function Prototypes (全局功能函数定义)
//-----------------------------------------------------------------------------
//...初始化功能函数...//
void PORT_Init (void); // 端口初始化
void OSCILLATOR_Init (void); // 时钟初始化
void PCA0_Init (void); // PCA计数器初始化
void SPI0_Init (void); // SPI0通讯初始化
void ADC0_Init(void); // ADC0数据采集初始化
void LTC6802_Init (void); // LTC6802初始化
void Init_Device (void); // 设备初始化
//...采集功能函数...//
void AD_Data_acquisition (void); // 电池单体电压,温度采集程序
void SPI_AD (void); // 从LTC6802-1读取采集数据
void SPI_Byte_Write (char Command); // 向LTC6802-1写字节命令
void SPI_Array_Read (void); // 从LTC6802-1读取数据
void AD_Current (void); // 采集总电流
void Poll_AD (void); // 查询AD转换是否完成
void AD_Data_process (void); // 对采集的数据进行处理
//...保护功能函数...//
void Protection_function (void); // 保护功能函数
void Charge_switch_OFF (void); // 过充时断开充电开关函数
void Charge_switch_ON (void); // 未过充时闭合充电开关函数
void Discharge_switch_OFF (void); // 过放时断开放电开关函数
void Discharge_switch_ON (void); // 未过放时闭合放电开关函数
//...均衡智能控制函数...//
void Electricity_Balance (void); // 均衡控制算法
void Blance_PWM(void); // PWM输出
void PORT_Rest(void); // 端口复位函数
//-----------------------------------------------------------------------------
// main() Routine (主功能函数)
//-----------------------------------------------------------------------------
void main (void)
{
PCA0MD = 0x00; // Disable the Watchdog Timer
Init_Device (); // 硬件设备初始化
EA = 1; // 全局开中断
while(1)
{
AD_Data_acquisition (); // 数据采集
AD_Data_process(); // 数据处理
}
}
//-----------------------------------------------------------------------------
// Init_Device (设备初始化)
//-----------------------------------------------------------------------------
void Init_Device (void)
{
PCA0_Init ();
PORT_Init ();
OSCILLATOR_Init ();
SPI0_Init ();
ADC0_Init ();
}
//-----------------------------------------------------------------------------
// PCA0_Init (PCA计数器初始化)
//-----------------------------------------------------------------------------
void PCA0_Init (void)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
PCA0MD = 0x00; // Disable the Watchdog Timer
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// PORT_Init (端口初始化)
//-----------------------------------------------------------------------------
void PORT_Init (void)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = CONFIG_PAGE;
P0MDIN = 0xF0;
P0MDOUT = 0x40; // P0.6 (CAN0 TX) is push-pull,P0.7 (CAN0 RX) is open-drain
P0SKIP = 0x3F; // Skip P0.0 (VREF),P0.2,P0.3
P1MDIN = 0xEF; // P1.4被配置为模拟输入
P1MDOUT = 0xED; // P1.0,P1.2,P1.3 is push-pull,P1.1 is open-drain
P1SKIP = 0x10; // Skip P1.4 (电流检测端口)
P1 = 0x7F; // 设置高电平输出
P2MDIN |= 0xFF;
P2MDOUT |= 0xFF; // P2.0,P2.1,P2.2,P2.3,P2.4,P2.5,P2.6,P2.7 is push-pull
P2SKIP = 0x00;
P2 = 0x80; // 设置高电平输出
P3MDIN |= 0xFF;
P3MDOUT |= 0xFF; // P2.0,P2.1,P2.2,P2.3,P2.4,P2.5,P2.6,P2.7 is push-pull
P3SKIP = 0x00;
P3 &= 0x00; // 设置高电平输出
XBR0 = 0x06; // Enable CAN0和SPI0
XBR2 = 0x40; // Enable crossbar and weak pull-ups
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// OSCILLATOR_Init (时钟初始化)
//-----------------------------------------------------------------------------
void OSCILLATOR_Init (void)
{
int i = 0;
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = CONFIG_PAGE;
P0 |= 0x0C;
OSCXCN = 0x67;
for (i = 0; i < 3000; i++); // Wait 1ms for external oscillator initialization
while ((OSCXCN & 0x80) == 0);
CLKMUL = 0x01;
CLKSEL = 0x01;
OSCICN = 0x07;
SFRPAGE = ACTIVE_PAGE;
}
//-----------------------------------------------------------------------------
// SPI0_Init (SPI初始化)
//-----------------------------------------------------------------------------
void SPI0_Init()
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
SPI0CFG = 0x73; // Enable the SPI as a Master,CKPHA = '1', CKPOL = '1'
SPI0CN = 0x0D; // 4-wire Single Master, SPI enabled
SPI0CKR = (SYSCLK / (2 * SPI_CLOCK)) - 1;
ESPI0 = 1; // Enable SPI interrupts
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// ADC0_Init (主CPU AD数据采集初始化)
//-----------------------------------------------------------------------------
void ADC0_Init (void)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
ADC0CF |= 0x01; // Set GAINEN = 1
ADC0H = 0x04; // Load the ADC0GNH address
ADC0L = 0x6C; // Load the upper byte of 0x6CA to
// ADC0GNH
ADC0H = 0x07; // Load the ADC0GNL address
ADC0L = 0xA0; // Load the lower nibble of 0x6CA to
// ADC0GNL
ADC0H = 0x08; // Load the ADC0GNA address
ADC0L = 0x01; // Set the GAINADD bit
ADC0CF &= ~0x01; // Set GAINEN = 0
ADC0CN = 0x00; // ADC0 disabled, normal tracking,
// 向AD0BUSY写1启动ADC0
REF0CN = 0x32; // Enable on-chip VREF and buffer
// Set voltage reference to 2.048V
ADC0MX = 0x0C; // Set ADC input to P1.4
ADC0CF = ((SYSCLK / 3000000) - 1) << 3; // Set SAR clock to 3MHz
EIE1 |= 0x04; // Enable ADC0 conversion complete int.
AD0EN = 1; // Enable ADC0
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// LTC6802_Init (LTC6802初始化)
//-----------------------------------------------------------------------------
void LTC6802_Init (void)
{
int array_m;
char array_i;
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
NSSMD0=0; // 片选置低
SPIF = 0;
Command = SPI_WRITE; // LTC6802写寄存器WRCFG配置
SPI0DAT = Command;
while (TXBMT != 1) // Wait until the command is moved into
{ // the XMIT buffer
}
for(array_i=0;array_i<6;array_i++)
{
SPI0DAT = SPI_Array[array_i];
while (TXBMT != 1) // Wait until the data is moved into
{ // the XMIT buffer
}
SPIF=0;
for(array_m=0;array_m<500;array_m++);
}
NSSMD0=1; // 片选置高
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// AD_Data_acquisition (单体电池电压、温度等数据采集程序)
//-----------------------------------------------------------------------------
void AD_Data_acquisition (void)
{
int AD_i;
U8 array_index = 0;
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE; // Set for SPI
AD_Current (); // 采集总电流
//电压转换//
LTC6802_Init ();
Command = STCVAD;
SPI_Byte_Write (Command); // 向LTC6802写入控制命令
for(AD_i=0;AD_i<3000;AD_i++);
while(Poll_IF==0) // 查询ADC转换是否完成
{
Poll_AD ();
}
SPI_Array_Read (); // 读取LTC6802电压
Poll_IF=0; // 复位标志位
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// SPI_Byte_Write (向LTC6802写命令)
//-----------------------------------------------------------------------------
void SPI_Byte_Write (char Command)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
NSSMD0=0; // 片选置低
SPIF = 0;
SPI0DAT = Command;
while (SPIF!=1);
SPIF=0;
NSSMD0=1;
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// SPI_Array_Read (向LTC6802写命令)
//-----------------------------------------------------------------------------
void SPI_Array_Read (void)
{
int mm;
U8 array_index = 0;
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
NSSMD0=0; // 片选置低
SPIF = 0;
Command = RDCV; // 向LTC6802写入读取电压寄存器配置
SPI0DAT = Command;
while (TXBMT != 1) // Wait until the command is moved into
{ // the XMIT buffer
}
while (SPIF!=1);
SPIF=0;
for(array_index = 0;array_index <(ANALOG_INPUTS-6);array_index++)
{
Command = 0x00; // 向LTC6802写入读取电压寄存器配置
SPI0DAT = Command;
while (TXBMT != 1) // Wait until the command is moved into
{ // the XMIT buffer
}
while (SPIF!=1);
SPIF=0;
for(mm=0;mm<3000;mm++);
SPI_Data_Array[array_index] = SPI0DAT;
SPIF=0;
}
SPIF=0;
NSSMD0=1; // 片选置高
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// AD_Current (采集总的电流)
//-----------------------------------------------------------------------------
void AD_Current (void)
{
char s;
static U32 accumulator = 0;
for(s=0;s<100;s++) // 数据累加10次
{
AD0BUSY=1; // 启动ADC0
while((ADC0CN&0x20)!=0x20); // 查询是否完成一次数据转换
EA=0;
accumulator += ADC0; // 累加计数
EA=1;
AD0INT = 0; // 复位中断
AD0BUSY=0;
}
SPI_Data_Current=(accumulator/100);
accumulator = 0;
}
//-----------------------------------------------------------------------------
// Poll_AD (查询ADC是否完成)
//-----------------------------------------------------------------------------
void Poll_AD (void)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
NSSMD0=0; // 片选置低
SPIF = 0;
Command = PLADC; // 向LTC6802写寄存器查询命令
SPI0DAT = Command;
while (!SPIF); // Wait until the SPI is free, in case it's already busy
SPIF = 0;
NSSMD0=1;
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
// AD_Data_process (对采集数据进行处理)
//-----------------------------------------------------------------------------
void AD_Data_process (void)
{
char data_i=1;
char data_j=0;
char data_k=0;
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
while(data_i<=13)
{
data_convert=SPI_Data_Array[data_i]; // 将存在双数据的字节SPI_Data_Array[data_i]放入中间变量
data_convert=(data_convert<<4); // 左移4位
SPI_Data[data_j]=(((U16)data_convert)<<4); // 将数据放入16位变量中,然后左移4位
SPI_Data[data_j]=(SPI_Data[data_j]+((U16)SPI_Data_Array[data_i-1])); // 相加得到一节单体电池的电压值(16位)
data_convert=0;
data_convert=SPI_Data_Array[data_i]; // 将存在双数据的字节SPI_Data_Array[data_i]放入中间变量
data_convert=(data_convert>>4); // 右移4位
SPI_Data[data_j+1]=(((U16)(SPI_Data_Array[data_i+1]))<<4); // 将数据放入16位变量中,然后左移8位
SPI_Data[data_j+1]=(SPI_Data[data_j+1]+((U16)data_convert)); // 相加得到一节单体电池的电压值(16位)
data_convert=0;
data_i=(data_i+3);
data_j=(data_j+2);
}
data_i=1; // 复位
data_j=0;
for(data_k=0;data_k<6;data_k++) // 采样值转换计算
{
RESULT[data_k]=(SPI_Data[data_k]*3/2); // 转换LTC6802的电压采样数据
SPI_Data[data_k]=0;
}
Data_Current=(SPI_Data_Current*2048/4095); // 电流采样值转换
SFRPAGE = SFRPAGE_save;
}
|