.H文件解析
#ifndef _ADBMS1818_
#define _ADBMS1818_
#include "M_SPI.h" //引入SIP底层收发
#include "CRC_Pec15.h" //引入计算crc的算法
#include "temperature.h" //引入温度转换
#include "string.h"
/*注:目前版本0.0.1 仅支持一片1818,后续按需增加*/
#define WRCFGA 0x01 //写配置寄存器A
#define WRCFGB 0x24 //写配置寄存器B
#define RDCFGA 0x02 //读配置寄存器A
#define RDCFGB 0x26 //读配置寄存器B
/*读取电压寄存器使用ADBMS181x_rdcv_reg*/
//也可使用下边的快速读取
#define RDCVA 0x04 //读取电压寄存器组A
#define RDCVB 0x06 //读取电压寄存器组B
#define RDCVC 0x08 //读取电压寄存器组C
#define RDCVD 0x0A //读取电压寄存器组D
#define RDCVE 0x09 //读取电压寄存器组E
#define RDCVF 0x0B //读取电压寄存器组F
#define RDAUXA 0x0C //读取辅助寄存器A
#define RDAUXB 0x0E //读取辅助寄存器B
#define RDAUXC 0x0D //读取辅助寄存器C
#define RDAUXD 0x0F //读取辅助寄存器D
#define RDSTATA 0x10 //读取状态寄存器A
#define RDSTATB 0x12 //读取状态寄存器B
#define WRSCTRL 0x14 //写s控制寄存器组
#define RDSCTRL 0x16 //读s控制寄存器组
//comm命令
#define WRCOMM 0x0721 // 写comm寄存器组
#define STCOMM 0x0723 // 启动IIC/SPI通讯
#define RDCOMM 0x0722 // 读取comm寄存器组
//IIC_CMD 寄存器内数据用
#define IW_Start 0x06 //开始
#define IW_Stop 0x01 //停止
#define IW_Null 0x00 //空
#define IW_Nsend 0x07 //不发送 释放总线并忽略之后的数据
#define IR_Start 0x06 //从主机开始
#define IR_Stop 0x01 //从主机停止
#define IR_LOW 0x00 //字节之间SDA为低电平
#define IR_High 0x07 //字节之间SDA为高电平
#define FW_Ack 0x00 //主机写入ACK
#define FW_Nack 0x08 //主机写入NACK
#define FW_NaS 0x09 //主机写入NACK+停止
#define FR_MAck 0x00 //主机读取ACK
#define FR_SAck 0x07 //从机读取ACK
#define FR_SNAck 0x08 //从机读取NAck
#define FR_SAckS 0x01 //从机ACK+主机停止
#define FR_SNAckS 0x09 //从机NACK+主机停止
#define BMS1818_GPIO1 0x0002 // 0x08
#define BMS1818_GPIO2 0x0004 // 0x08<<1
#define BMS1818_GPIO3 0x0008 // 0x08<<2
#define BMS1818_GPIO4 0x0010 // 0x08<<3
#define BMS1818_GPIO5 0x0020 // 0x08<<4
#define BMS1818_GPIO6 0x0040 // 0x01
#define BMS1818_GPIO7 0x0080 // 0x01<<1
#define BMS1818_GPIO8 0x0100 // 0x01<<2
#define BMS1818_GPIO9 0x0200 // 0x01<<3
#define MD_422HZ_1KHZ 0
#define MD_27KHZ_14KHZ 1
#define MD_7KHZ_3KHZ 2
#define MD_26HZ_2KHZ 3
/*CHG低三位有效*/
#define CHG_ALL 0x00 //GPIO1至GPIO5,第二基准电压,GPIO6至GPIO9
#define CHG_G1G6 0x01 //GPIO1、GPIO6
#define CHG_G2G7 0x02 //....
#define CHG_G3G8 0x03
#define CHG_G4G9 0x04
#define CHG_G5 0x05
#define CHG_VREF2 0x06
/*CH低三位有效*/
#define CH_ALL 0x00 //所有电池
#define CH_V1_7_13 0x01 //电池 1 7 13
#define CH_V2_8_14 0x02 //....
#define CH_V3_9_15 0x03
#define CH_V4_10_16 0x04
#define CH_V5_11_17 0x05
#define CH_V6_12_18 0x06
#define ADCVAX 0x046F // 0100 0110 1111 //所有电池和GPIO1 GPIO2 (这俩是掩码不能直接使用)
#define ADAX 0x0460 // 0100 0110 0000 //GPIO ADC转换
#define ADCV 0x0260 // 0010 0110 0000 //启动对18节电池的转换
typedef struct{
uint8_t CFGARx[8];
uint8_t CFGBRx[8];
uint16_t VREF2;//第二基准电压值
uint8_t COMM[8];
/*................*/
}ADBMS1818_Reg;
//测试案例
void Bms1818_tset(void);
#endif
.c代码解析
#include "ADBMS1818.h"
ADBMS1818_Reg bms1818_Reg={0};
void BMS1818_Delay_us(uint16_t nus){
uint8_t i;
while(nus --)
{
for(i =0;i<0x16;i++);
}
}
/*spi读写接口 读写长度可变
tx_Data[] 发送缓冲区指针
rx_data 接受缓冲区指针
rx_len 长度
*/
void spi_WR_byte(uint8_t tx_Data[],uint8_t *rx_data,uint8_t len){
Mspi_RT_byte(tx_Data,rx_data,len);
}
/*
第二个spi读写接口 读写一个字节
data 发送的数据
返回同时读取的数据
*/
uint8_t spi_WR_byte2(uint8_t data){
uint8_t re;
Mspi_RT_byte(&data,&re,1);
return re;
}
//计算crc校验码的接口
uint16_t pec15_calc(int len,uint8_t* data){
return pec15(data,len);
}
//extern void delay_1ms(uint32_t count);
/* 从IDlE状态唤醒isoSPI并进入READY状态*/
void wakeup_sleep_cmd(uint8_t chain_N)
{
uint8_t cmd[2],status[2];
cmd[0]=0xff;
SET_SPI0_NSS_LOW;
BMS1818_Delay_us(10);
spi_WR_byte(cmd,status,1);
if(chain_N>200)BMS1818_Delay_us(300*chain_N/1000);
else {BMS1818_Delay_us(80*chain_N);}
SET_SPI0_NSS_HIGH;
SET_SPI0_NSS_LOW;
spi_WR_byte(cmd,status,1);
BMS1818_Delay_us(10*chain_N);
spi_WR_byte(cmd,status,1);
BMS1818_Delay_us(10*chain_N);
SET_SPI0_NSS_HIGH;
}
/*传入原始数据,(注:只处理一个寄存器组也就是)将数据进行校验然后传入目标指针,如果校验失败返回0*/
uint8_t ADBMS1818_RC_Check(uint8_t* original,uint16_t* target){
uint16_t CRC_code;
CRC_code = pec15_calc(6,original);
if(CRC_code!=(original[6]<<8|original[7]))
return 0;
for(int i=0;i<3;i++){
target[i]=original[i*2]|(original[i*2+1]<<8);
}
return 1;
}
//===============================================================================================================
/* 菊花链中的IC数量写入命令并读取原始单元电压寄存器数据
reg 1: RDCVA
2: RDCVB
3: RDCVC
4: RDCVD
5: RDCVE
6: RDCVF
total_ic 菊花链中IC的数量
data 放数据的数组
*/
void ADBMS181x_rdcv_reg(uint8_t reg, uint8_t total_ic, uint8_t *data){
const uint8_t REG_LEN = 8; //Number of bytes in each ICs register + 2 bytes for the PEC
uint8_t cmd[40];
uint16_t cmd_pec;
//00 04 07 c2
if (reg == 1) //1: RDCVA
{
cmd[1] = 0x04;
cmd[0] = 0x00;
}
else if (reg == 2) //2: RDCVB
{
cmd[1] = 0x06;
cmd[0] = 0x00;
}
else if (reg == 3) //3: RDCVC
{
cmd[1] = 0x08;
cmd[0] = 0x00;
}
else if (reg == 4) //4: RDCVD
{
cmd[1] = 0x0A;
cmd[0] = 0x00;
}
else if (reg == 5) //5: RDCVE
{
cmd[1] = 0x09;
cmd[0] = 0x00;
}
else if (reg == 6) //6: RDCVF
{
cmd[1] = 0x0B;
cmd[0] = 0x00;
}
cmd_pec = pec15_calc(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
wakeup_sleep_cmd(total_ic);
SET_SPI0_NSS_LOW;
spi_WR_byte(cmd,data,4);
memset(cmd, 0xff, 8);
spi_WR_byte(cmd,data,REG_LEN*total_ic);
SET_SPI0_NSS_HIGH;
}
/*
cmd 命令
wakeup 是否唤醒
*/
void ADBMS181x_write_cmd(uint16_t cmd,uint8_t wakeup){
uint8_t Mcmd[8];
uint8_t databuf[4];
uint16_t cmd_pec;
Mcmd[0]=(uint8_t)(cmd>>8);
Mcmd[1]=(uint8_t)cmd;
cmd_pec = pec15_calc(2, Mcmd);
Mcmd[2] = (uint8_t)(cmd_pec >> 8);
Mcmd[3] = (uint8_t)(cmd_pec);
if(wakeup) wakeup_sleep_cmd(1);
SET_SPI0_NSS_LOW;
spi_WR_byte(Mcmd,databuf,4);
SET_SPI0_NSS_HIGH;
}
/*读1818寄存器*/
void ADBMS181x_read_reg(uint16_t cmd,uint8_t *databuf,uint8_t wakeup){
uint8_t Mcmd[8];
uint16_t cmd_pec;
Mcmd[0]=(uint8_t)(cmd>>8);
Mcmd[1]=(uint8_t)cmd;
cmd_pec = pec15_calc(2, Mcmd);
Mcmd[2] = (uint8_t)(cmd_pec >> 8);
Mcmd[3] = (uint8_t)(cmd_pec);
if(wakeup) wakeup_sleep_cmd(1);
SET_SPI0_NSS_LOW;
spi_WR_byte(Mcmd,databuf,4);
memset(Mcmd, 0xff, 8);
spi_WR_byte(Mcmd,databuf,8);
SET_SPI0_NSS_HIGH;
}
/*写1818寄存器*/
void ADBMS181x_write_reg(uint16_t cmd,uint8_t *databuf,uint8_t wakeup){
uint8_t Mcmd[8+4];
uint8_t buf[8];
uint16_t cmd_pec;
Mcmd[0]=(uint8_t)(cmd>>8);
Mcmd[1]=(uint8_t)cmd;
cmd_pec = pec15_calc(2, Mcmd);
Mcmd[2] = (uint8_t)(cmd_pec >> 8);
Mcmd[3] = (uint8_t)(cmd_pec);
for(int i=0;i<6;i++){
Mcmd[4+i] = databuf[i];//写入数据
}
cmd_pec = pec15_calc(6, &Mcmd[4]);
Mcmd[10] = (uint8_t)(cmd_pec >> 8);
Mcmd[11] = (uint8_t)(cmd_pec);
if(wakeup) wakeup_sleep_cmd(1);
SET_SPI0_NSS_LOW;
spi_WR_byte(Mcmd,buf,4+8);
SET_SPI0_NSS_HIGH;
}
/*
转换所有电池和GPIO1 GPIO2 的电压 建议在7K赫兹延迟8ms后读取
MD ADC模式
DCP 允许放电? 0不允许放电 1允许放电
*/
void ADBMS1818_ADCVAX(uint8_t MD,uint8_t DCP){
uint16_t cmd=0;
cmd|=(MD<<7);
cmd|=(DCP<<4);
cmd|=ADCVAX;
ADBMS181x_write_cmd(cmd,1);
}
/*
转换GPIO_ADC 转换的项目根据CHG的值来确定
在7k赫兹的情况下
转换所有GPIO 需要3.9ms 建议等待 8ms以上
转换两个GPIO 需要1ms 建议等待 2ms以上
*/
void ADBMS1818_ADAX(uint8_t MD,uint8_t CHG){
uint16_t cmd=0;
cmd|=CHG;
cmd|=(MD<<7);
cmd|=ADAX;
ADBMS181x_write_cmd(cmd,1);
}
/*转换所有电池电压
在7Khz下需要 3ms 建议等待6ms以上
MD ADC模式
CHG
*/
void ADBMS1818_ADCV(uint8_t MD,uint8_t CH,uint8_t DCP){
uint16_t cmd=0;
cmd|=(DCP<<4);
cmd|=(MD<<7);
cmd|=(CH);
cmd|=ADCV;
ADBMS181x_write_cmd(cmd,1);
}
//读取18个电池电压
void ADBMS181x_read_18V(uint16_t *bufdata){
uint8_t Mcmd[8];
ADBMS1818_ADCV(MD_7KHZ_3KHZ,CH_ALL,0);
BMS1818_Delay_us(10000);
ADBMS181x_read_reg(RDCVA,Mcmd,1);
ADBMS1818_RC_Check(Mcmd,bufdata);
ADBMS181x_read_reg(RDCVB,Mcmd,0);
ADBMS1818_RC_Check(Mcmd,bufdata+3);
ADBMS181x_read_reg(RDCVC,Mcmd,0);
ADBMS1818_RC_Check(Mcmd,bufdata+6);
ADBMS181x_read_reg(RDCVD,Mcmd,0);
ADBMS1818_RC_Check(Mcmd,bufdata+9);
ADBMS181x_read_reg(RDCVE,Mcmd,0);
ADBMS1818_RC_Check(Mcmd,bufdata+12);
ADBMS181x_read_reg(RDCVF,Mcmd,0);
ADBMS1818_RC_Check(Mcmd,bufdata+15);
}
//暂不支持多个位同时配置
uint8_t ADBMS181x_Config_GPIO(uint16_t gpio,uint8_t state){
switch(gpio){
case BMS1818_GPIO1:
(state==1)? (bms1818_Reg.CFGARx[0]|=0x08):(bms1818_Reg.CFGARx[0]&=0xF7);
break;
case BMS1818_GPIO2:
(state==1)? (bms1818_Reg.CFGARx[0]|=0x10):(bms1818_Reg.CFGARx[0]&=0xEF);
break;
case BMS1818_GPIO3:
(state==1)? (bms1818_Reg.CFGARx[0]|=0x20):(bms1818_Reg.CFGARx[0]&=0xDF);
break;
case BMS1818_GPIO4:
(state==1)? (bms1818_Reg.CFGARx[0]|=0x40):(bms1818_Reg.CFGARx[0]&=0xBF);
break;
case BMS1818_GPIO5:
(state==1)? (bms1818_Reg.CFGARx[0]|=0x80):(bms1818_Reg.CFGARx[0]&=0x7F);
break;
case BMS1818_GPIO6:
(state==1)? (bms1818_Reg.CFGBRx[0]|=0x01):(bms1818_Reg.CFGBRx[0]&=0xFE);
break;
case BMS1818_GPIO7:
(state==1)? (bms1818_Reg.CFGBRx[0]|=0x02):(bms1818_Reg.CFGBRx[0]&=0xFD);
break;
case BMS1818_GPIO8:
(state==1)? (bms1818_Reg.CFGBRx[0]|=0x04):(bms1818_Reg.CFGBRx[0]&=0xFB);
break;
case BMS1818_GPIO9:
(state==1)? (bms1818_Reg.CFGBRx[0]|=0x08):(bms1818_Reg.CFGBRx[0]&=0xF7);
break;
default:
return 0;
}
return 1;
}
//读取16个温度
void ADBMS181x_read_16T(uint16_t *Tdata){
//依次读取
uint8_t bufdata[8]={0};//存接收的数据
uint16_t Vdata[3]={0};//存校验后的数据
//uint8_t i=0;
for(uint8_t i=0;i<8;i++){//切译码器
ADBMS181x_Config_GPIO(BMS1818_GPIO7,(i&0x04)? 1:0);
ADBMS181x_Config_GPIO(BMS1818_GPIO6,(i&0x02)? 1:0);
ADBMS181x_Config_GPIO(BMS1818_GPIO3,(i&0x01)? 1:0);
bms1818_Reg.CFGARx[0]|=0x00;
ADBMS181x_write_reg(WRCFGA,bms1818_Reg.CFGARx,1);
ADBMS181x_write_reg(WRCFGB,bms1818_Reg.CFGBRx,0);
BMS1818_Delay_us(20000);//切换完成等待20ms
ADBMS1818_ADAX(MD_7KHZ_3KHZ,CHG_ALL);//转换通道1
BMS1818_Delay_us(8000);
ADBMS181x_read_reg(RDAUXA,bufdata,1);
if(ADBMS1818_RC_Check(bufdata,Vdata)){//检查读出数据是否合法
Tdata[0+i]=Vdata[0];
Tdata[8+i]=Vdata[1];
}
}
}
/*IIC主机写
SlaveAddress 从机地址 一个字节
cmd 命令字 一个字节
data 数据 固定两个字节
*/
void ADBMS1818_IIC_Write(uint8_t SlaveAddress,uint8_t cmdbyte,uint8_t* data){
bms1818_Reg.COMM[0]=((IW_Start<<4)|(SlaveAddress>>4)); //起始地址
bms1818_Reg.COMM[1]=(SlaveAddress<<4) | FW_Nack;
bms1818_Reg.COMM[2]=(IW_Null<<4) | (cmdbyte>>4); //cmd
bms1818_Reg.COMM[3]=(cmdbyte<<4) | FW_Nack;
bms1818_Reg.COMM[4]=(IW_Null<<4) | (data[0]>>4); //数据0
bms1818_Reg.COMM[5]=(data[0]<<4) | FW_Nack;
ADBMS181x_write_reg(WRCOMM,bms1818_Reg.COMM,1); //将数据装入comm寄存器
ADBMS181x_write_cmd(STCOMM,0); //启动iic数据传输
SET_SPI0_NSS_LOW;
for(int i=0;i<9;i++)//在发送72个时钟
spi_WR_byte2(0xff);
SET_SPI0_NSS_HIGH;
//memset(bms1818_Reg.COMM,0,6);//清除数据
bms1818_Reg.COMM[0]=(IW_Null<<4) | (data[1]>>4);
bms1818_Reg.COMM[1]=(data[1]<<4) | FW_NaS; //Nack信号和停止位
bms1818_Reg.COMM[2]=(IW_Nsend<<4); //释放总线
ADBMS181x_write_reg(WRCOMM,bms1818_Reg.COMM,1); //第二次装入comm寄存器
ADBMS181x_write_cmd(STCOMM,0); //启动iic数据传输
SET_SPI0_NSS_LOW;
for(int i=0;i<9;i++)//在发送72个时钟
spi_WR_byte2(0xff);
SET_SPI0_NSS_HIGH;
}
/*IIC主机读
SlaveAddress 从机地址 一个字节
cmd 命令字(寄存器地址) 一个字节
data 数据 固定两个字节
*/
void ADBMS1818_IIC_Read(uint8_t SlaveAddress,uint8_t cmdbyte,uint8_t* data){
bms1818_Reg.COMM[0]=((IW_Start<<4)|(SlaveAddress>>4)); //起始地址
bms1818_Reg.COMM[1]=(SlaveAddress<<4) | FW_Nack;
bms1818_Reg.COMM[2]=(IW_Null<<4) | (cmdbyte>>4); //cmd
bms1818_Reg.COMM[3]=(cmdbyte<<4) | FW_NaS;
bms1818_Reg.COMM[4]=((IR_Start<<4)|((SlaveAddress)>>4)); //发送从机地址 改为读
bms1818_Reg.COMM[5]=((SlaveAddress|0x01)<<4) | FW_Nack;
ADBMS181x_write_reg(WRCOMM,bms1818_Reg.COMM,1); //将数据装入comm寄存器
ADBMS181x_write_cmd(STCOMM,0); //启动iic数据传输
SET_SPI0_NSS_LOW;
for(int i=0;i<9;i++)//在发送72个时钟
spi_WR_byte2(0xff);
SET_SPI0_NSS_HIGH;
bms1818_Reg.COMM[0]=((IW_Null<<4)|0x0f); //接取两个字节
bms1818_Reg.COMM[1]=(0xf0) | FW_Ack;
bms1818_Reg.COMM[2]=(IW_Null<<4) |0x0f;
bms1818_Reg.COMM[3]= 0xf0 | FW_NaS;
bms1818_Reg.COMM[4]=(IW_Nsend<<4); //释放总线
ADBMS181x_write_reg(WRCOMM,bms1818_Reg.COMM,1); //将数据装入comm寄存器
ADBMS181x_write_cmd(STCOMM,0);
SET_SPI0_NSS_LOW;
for(int i=0;i<9;i++)//在发送72个时钟
spi_WR_byte2(0xff);
SET_SPI0_NSS_HIGH;
ADBMS181x_read_reg(RDCOMM,bms1818_Reg.COMM,1); //读取comm返回数据
data[0]=(bms1818_Reg.COMM[0]<<4)|(bms1818_Reg.COMM[1]>>4);
data[1]=(bms1818_Reg.COMM[2]<<4)|(bms1818_Reg.COMM[3]>>4);
}
//测试定义数据
uint8_t BMS1818[18]={0};
uint16_t Vdata[18];
uint16_t TVdata[16];
int16_t TP[16];
uint8_t data[8]={0xa0,0x0a};
void Bms1818_tset(void){
//==========================读取温度示例=======================
uint64_t temp;
ADBMS181x_Config_GPIO(BMS1818_GPIO1,1);//输入引脚禁用下拉
ADBMS181x_Config_GPIO(BMS1818_GPIO2,1);
ADBMS181x_Config_GPIO(BMS1818_GPIO8,1);
ADBMS181x_Config_GPIO(BMS1818_GPIO9,1);
ADBMS181x_Config_GPIO(BMS1818_GPIO4,1);
ADBMS181x_Config_GPIO(BMS1818_GPIO5,1);
bms1818_Reg.CFGARx[0]|=0x04;//基准电压保持到看门狗超时
ADBMS181x_write_reg(WRCFGA,bms1818_Reg.CFGARx,1);//配置io口
ADBMS181x_write_reg(WRCFGB,bms1818_Reg.CFGBRx,0);
ADBMS181x_read_16T(TVdata);//读取温度
for(int i=0;i<16;i++){//温度转换
temp=(33*TVdata[i])*1000/(30000-TVdata[i]);//将ad值计算出电阻值
TP[i]=GetTempChangeVol(temp/10,Rtc_50to125,Rtc_50to125_Size,50);//根据电阻值查表得出实际温度
}
//======================读取电压示例============================
// ADBMS181x_Config_GPIO(BMS1818_GPIO1,1);//输入引脚禁用下拉
// ADBMS181x_Config_GPIO(BMS1818_GPIO2,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO8,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO9,1);
// bms1818_Reg.CFGARx[0]|=0x04;//基准电压保持到看门狗超时
// ADBMS181x_write_reg(WRCFGA,bms1818_Reg.CFGARx,1);//配置io口
// ADBMS181x_write_reg(WRCFGB,bms1818_Reg.CFGBRx,0);
ADBMS181x_read_18V(Vdata);//为直接读出的ad值
//========================iic读写示例============================
// ADBMS181x_Config_GPIO(BMS1818_GPIO1,1);//输入引脚禁用下拉
// ADBMS181x_Config_GPIO(BMS1818_GPIO2,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO8,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO9,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO4,1);
// ADBMS181x_Config_GPIO(BMS1818_GPIO5,1);
// bms1818_Reg.CFGARx[0]|=0x04;//基准电压保持到看门狗超时
// ADBMS181x_write_reg(WRCFGA,bms1818_Reg.CFGARx,1);//配置io口
// ADBMS181x_write_reg(WRCFGB,bms1818_Reg.CFGBRx,0);
//
ADBMS1818_IIC_Write(0x48,0x02,data);
data[0]=0x00; data[1]=0x00;
BMS1818_Delay_us(20000);//等待20ms
ADBMS1818_IIC_Read(0x48,0x02,data);
//
//========================结束====================================
}