1049 lines
30 KiB
C
1049 lines
30 KiB
C
/**
|
|
******************************************************************************
|
|
* @file afe_comm.c
|
|
* @author AMG Energy Solution Team
|
|
* @version V1.0.0
|
|
* @date 21-Jul-2025
|
|
* @brief This file provides all the headers of afe communication functions.
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
|
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
|
|
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
|
|
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
|
|
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
|
|
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
|
*
|
|
* <h2><center>© COPYRIGHT 2025 AMG</center></h2>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/** @addtogroup mBMS3
|
|
* @{
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include <includes.h>
|
|
#include "afe_comm.h"
|
|
#include "spi.h"
|
|
#include "ADBMS181x.h"
|
|
#include "ADBMS1818.h"
|
|
#include "usart.h"
|
|
#include "asutil.h"
|
|
#include "malloc.h"
|
|
#include "delay.h"
|
|
#include "device_param.h"
|
|
#include "brtc.h"
|
|
#include "console.h"
|
|
#include "io.h"
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
typedef struct
|
|
{
|
|
uint16_t cvolt[DEVICE_MAX_CELL];
|
|
} TPackBuf, *PPackBuf;
|
|
|
|
/* Private define ------------------------------------------------------------*/
|
|
#define ENABLED 1
|
|
#define DISABLED 0
|
|
|
|
#define DATALOG_ENABLED 1
|
|
#define DATALOG_DISABLED 0
|
|
|
|
#define BATT_TASK_TIME 30 // 30ms TASK
|
|
|
|
#define DEFAULT_HOST_BALANCING_VOLTAGE 3000
|
|
|
|
/**********************************************************
|
|
Setup Variables
|
|
The following variables can be modified to
|
|
configure the software.
|
|
***********************************************************/
|
|
const uint8_t TOTAL_IC = 1;//!<number of ICs in the daisy chain
|
|
|
|
/*************************************************************************
|
|
ADC Command Configurations. See LTC681x.h for options
|
|
**************************************************************************/
|
|
const uint8_t ADC_OPT = ADC_OPT_DISABLED; //!< ADC Mode option bit
|
|
const uint8_t ADC_CONVERSION_MODE = MD_7KHZ_3KHZ; //!< ADC Mode
|
|
const uint8_t ADC_DCP = DCP_ENABLED; //!< Discharge Permitted
|
|
const uint8_t CELL_CH_TO_CONVERT = CELL_CH_ALL; //!< Channel Selection for ADC conversion
|
|
const uint8_t AUX_CH_TO_CONVERT = AUX_CH_ALL; //!< Channel Selection for ADC conversion
|
|
const uint8_t STAT_CH_TO_CONVERT = STAT_CH_ALL; //!< Channel Selection for ADC conversion
|
|
const uint8_t NO_OF_REG = 0; //REG_ALL; //!< Register Selection
|
|
|
|
const uint16_t MEASUREMENT_LOOP_TIME = 500; //!< Loop Time in milliseconds(ms)
|
|
|
|
//Under Voltage and Over Voltage Thresholds
|
|
const uint16_t OV_THRESHOLD = 41000; //!< Over voltage threshold ADC Code. LSB = 0.0001 ---(4.1V)
|
|
const uint16_t UV_THRESHOLD = 20000; //!< Under voltage threshold ADC Code. LSB = 0.0001 ---(3V)
|
|
|
|
//Loop Measurement Setup. These Variables are ENABLED or DISABLED. Remember ALL CAPS
|
|
const uint8_t WRITE_CONFIG = DISABLED; //!< Loop Measurement Setup
|
|
const uint8_t READ_CONFIG = DISABLED; //!< Loop Measurement Setup
|
|
const uint8_t MEASURE_CELL = ENABLED; //!< Loop Measurement Setup
|
|
const uint8_t MEASURE_AUX = ENABLED; //!< Loop Measurement Setup
|
|
const uint8_t MEASURE_STAT = DISABLED; //!< Loop Measurement Setup
|
|
const uint8_t PRINT_PEC = DISABLED; //!< Loop Measurement Setup
|
|
/************************************
|
|
END SETUP
|
|
*************************************/
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
/*******************************************************
|
|
Global Battery Variables received from 681x commands
|
|
These variables store the results from the LTC6813
|
|
register reads and the array lengths must be based
|
|
on the number of ICs on the stack
|
|
******************************************************/
|
|
|
|
cell_asic bms_ic[TOTAL_IC]; //!< Global Battery Variable
|
|
|
|
/*************************************************************************
|
|
Set configuration register. Refer to the data sheet
|
|
**************************************************************************/
|
|
|
|
bool REFON = true; //!< Reference Powered Up Bit
|
|
bool ADCOPT = false; //!< ADC Mode option bit
|
|
|
|
bool gpioBitsA[5] = { 0, 0, 0, 0, 0 };
|
|
bool gpioBitsB[4] = { 0, 0, 0, 0 };
|
|
//uint8_t gpioBits_a = 0x1F; //!< GPIO Pin Control // Gpio 1,2,3,4,5
|
|
//uint8_t gpioBits_b = 0x0F; //!< GPIO Pin Control // Gpio 6,7,8,9
|
|
uint16_t UV = UV_THRESHOLD; //!< Under voltage Comparison Voltage
|
|
uint16_t OV = OV_THRESHOLD; //!< Over voltage Comparison Voltage
|
|
|
|
bool dccBitsA[12] = { 0, };
|
|
bool dccBitsB[7] = { 0, };
|
|
//uint16_t dccBits_a = 0; // 12bits !< Discharge cell switch //Dcc 1,2,3,4,5,6,7,8,9,10,11,12
|
|
//uint16_t dccBits_b = 0; // 7bits !< Discharge cell switch //Dcc 13,14,15,16,17,18, GPIO9
|
|
|
|
bool dctoBits[4] = { 0, 0, 0, 0 };
|
|
//uint8_t dctoBits = 1; // 4bits !< Discharge time value //Dcto 0,1,2,3 // Programed for 0.5 min
|
|
|
|
/*Ensure that Dcto bits are set according to the required discharge time. Refer to the data sheet */
|
|
bool FDRF = false; //!< Force Digital Redundancy Failure Bit
|
|
bool DTMEN = true; //!< Enable Discharge Timer Monitor
|
|
bool psBits[2]= {false,false}; //!< Digital Redundancy Path Selection//ps-0,1
|
|
|
|
#define PACK_BUF_MAX 20
|
|
|
|
uint16_t PackBufCh = 0;
|
|
TPackBuf PackBuf[PACK_BUF_MAX];
|
|
|
|
uint8_t PackBuffFlag = false;
|
|
|
|
uint32_t OneBuffTime = 0;
|
|
uint32_t AllBuffTime = 0;
|
|
|
|
uint32_t bakOneBuffTime = 0;
|
|
uint32_t bakAllBuffTime = 0;
|
|
|
|
uint32_t Balance_Avg, disBalance_Avg;
|
|
uint8_t CellBalancingFlag = false, disCellBalancingFlag;
|
|
uint8_t CellBalancingOff = false;
|
|
|
|
uint16_t HostBalancingVoltage = 0;
|
|
uint16_t HostBalancingVoltageDiff = 0;
|
|
|
|
uint8_t CB_Rest = 1;
|
|
uint32_t CB_Tick = 0;
|
|
uint32_t disCB_Tick = 0;
|
|
|
|
uint32_t CbRestAlarmTick = 0;
|
|
|
|
uint32_t ManualBalValue = 0;
|
|
uint8_t ManualBalFlag = 0;
|
|
|
|
uint32_t bakManualBalValue = 0;
|
|
|
|
uint32_t cBal = 0;
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
void setup(void);
|
|
void cell_balancing_init(void);
|
|
void ReadCellVoltage(uint8_t cNo, cell_asic *ic);
|
|
|
|
void CellVoltagePutBuf(uint8_t cNo, cell_asic *ic);
|
|
void MakeCellAvgData(void);
|
|
|
|
void TempVoltagePutBuf(uint8_t cNo, cell_asic *ic);
|
|
|
|
void WriteBalData(uint32_t wBal);
|
|
void AutoCellBalancing(uint32_t rBal);
|
|
|
|
void ManualCellBalancing(uint32_t rBal);
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
/*!**********************************************************************
|
|
\brief Initializes Port
|
|
@return void
|
|
***********************************************************************/
|
|
void init_battery_interface(void)
|
|
{
|
|
stc_port_init_t stcPortInit;
|
|
|
|
/* configure structure initialization */
|
|
MEM_ZERO_STRUCT(stcPortInit);
|
|
|
|
stcPortInit.enPinMode = Pin_Mode_Out;
|
|
PORT_Init(SPI1_NSS_PORT, SPI1_NSS_PIN, &stcPortInit);
|
|
CS_PIN_HIGH();
|
|
|
|
Spi1_Config();
|
|
|
|
wakeup_sleep(1);
|
|
|
|
setup();
|
|
cell_balancing_init();
|
|
device_status.battery_status.bit.ACB = true;
|
|
}
|
|
|
|
/*!**********************************************************************
|
|
\brief Initializes hardware and variables
|
|
@return void
|
|
***********************************************************************/
|
|
void setup(void)
|
|
{
|
|
|
|
ADBMS1818_init_cfg(TOTAL_IC, bms_ic);
|
|
ADBMS1818_init_cfgb(TOTAL_IC, bms_ic);
|
|
for (uint8_t current_ic = 0; current_ic < TOTAL_IC; current_ic++) {
|
|
ADBMS1818_set_cfgr (current_ic, bms_ic, REFON, ADCOPT, gpioBitsA, dccBitsA , dctoBits, UV, OV);
|
|
ADBMS1818_set_cfgrb(current_ic, bms_ic, FDRF , DTMEN , psBits , gpioBitsB, dccBitsB);
|
|
}
|
|
ADBMS1818_reset_crc_count(TOTAL_IC, bms_ic);
|
|
ADBMS1818_init_reg_limits(TOTAL_IC, bms_ic);
|
|
}
|
|
|
|
/**
|
|
* @brief Application Task for AFE (Analog Front End) Communication
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void App_TaskAfeComm(void)
|
|
{
|
|
init_battery_interface();
|
|
|
|
while (DEF_TRUE) {
|
|
ReadCellVoltage(0, bms_ic);
|
|
|
|
delay_os_ms(BATT_TASK_TIME);
|
|
}
|
|
}
|
|
|
|
/*!**********************************************************************
|
|
\brief Initialize cell balancing
|
|
@return void
|
|
***********************************************************************/
|
|
void reset_dccbitA(void)
|
|
{
|
|
for (int i = 0; i < 12; i++) { dccBitsA[i] = 0; }
|
|
}
|
|
|
|
void reset_dccbitB(void)
|
|
{
|
|
for (int i = 0; i < 7; i++) { dccBitsB[i] = 0; }
|
|
}
|
|
|
|
void cell_balancing_init(void)
|
|
{
|
|
for (int i = 0; i < 18; i++)
|
|
{
|
|
wakeup_sleep(TOTAL_IC);
|
|
wakeup_idle(TOTAL_IC);
|
|
WriteBalData((uint32_t)(1 << i));
|
|
delay_os_ms(200);
|
|
}
|
|
wakeup_sleep(TOTAL_IC);
|
|
wakeup_idle(TOTAL_IC);
|
|
WriteBalData((uint32_t)(0xFFFFFFFF));
|
|
delay_os_ms(1000);
|
|
|
|
reset_dccbitA();
|
|
reset_dccbitB();
|
|
ADBMS1818_set_cfgr (0, bms_ic, REFON, ADCOPT, gpioBitsA, dccBitsA , dctoBits, UV, OV);
|
|
ADBMS1818_set_cfgrb(0, bms_ic, FDRF , DTMEN , psBits , gpioBitsB, dccBitsB);
|
|
|
|
wakeup_sleep(TOTAL_IC);
|
|
wakeup_idle(TOTAL_IC);
|
|
ADBMS1818_wrcfg (TOTAL_IC, bms_ic);
|
|
ADBMS1818_wrcfgb(TOTAL_IC, bms_ic);
|
|
ADBMS1818_adcv(ADC_CONVERSION_MODE, ADC_DCP, CELL_CH_TO_CONVERT);
|
|
}
|
|
|
|
/*!*********************************************************************
|
|
\brief Cell Voltage Read
|
|
@return void
|
|
***********************************************************************/
|
|
void ReadCellVoltage(uint8_t cNo, cell_asic *ic)
|
|
{
|
|
int8_t error;
|
|
int8_t errorA, errorB;
|
|
|
|
wakeup_sleep(TOTAL_IC);
|
|
wakeup_idle(TOTAL_IC);
|
|
|
|
errorA = ADBMS1818_rdcfg (TOTAL_IC, ic);
|
|
errorB = ADBMS1818_rdcfgb(TOTAL_IC, ic);
|
|
if ((errorA != -1) && (errorB != -1)) {
|
|
// Process cell balancing
|
|
uint32_t bal = 0;
|
|
|
|
bal = (((ic[0].config.rx_data[4] << 0) & 0x000000FF)
|
|
|((ic[0].config.rx_data[5] << 8) & 0x00000F00)
|
|
|((ic[0].configb.rx_data[0] << 8) & 0x0000F000)
|
|
|((ic[0].configb.rx_data[1] << 16) & 0x00030000)
|
|
);
|
|
|
|
device_status.cellbalance.value = bal;
|
|
|
|
if (ManualBalFlag == true) {
|
|
ManualCellBalancing(bal);
|
|
} else {
|
|
AutoCellBalancing(bal);
|
|
}
|
|
}
|
|
|
|
/* Always attempt a fresh measurement; check the read result before using it. */
|
|
wakeup_sleep(TOTAL_IC);
|
|
wakeup_idle(TOTAL_IC);
|
|
ADBMS1818_adcv(ADC_CONVERSION_MODE, ADC_DCP, CELL_CH_TO_CONVERT);
|
|
uint16_t conv_time = ADBMS1818_pollAdc();
|
|
wakeup_idle(TOTAL_IC);
|
|
error = ADBMS1818_rdcv(NO_OF_REG, TOTAL_IC, ic); // Set to read back all cell voltage registers
|
|
|
|
if (error != -1) {
|
|
CellVoltagePutBuf(0, ic);
|
|
MakeCellAvgData();
|
|
}
|
|
}
|
|
|
|
void inc_afe_bufftime(uint32_t tick)
|
|
{
|
|
OneBuffTime += tick;
|
|
AllBuffTime += tick;
|
|
}
|
|
|
|
void SaveOneBuffTime(void)
|
|
{
|
|
bakOneBuffTime = OneBuffTime;
|
|
OneBuffTime = 0;
|
|
}
|
|
|
|
void SaveAllBuffTime(void)
|
|
{
|
|
bakAllBuffTime = AllBuffTime;
|
|
AllBuffTime = 0;
|
|
}
|
|
|
|
uint32_t GetOneBuffTime(void)
|
|
{
|
|
return bakOneBuffTime;
|
|
}
|
|
|
|
uint32_t GetAllBuffTime(void)
|
|
{
|
|
return bakAllBuffTime;
|
|
}
|
|
|
|
// Data Buffering
|
|
void CellVoltagePutBuf(uint8_t cNo, cell_asic *ic)
|
|
{
|
|
int32_t ch, tmp;
|
|
|
|
ch = PackBufCh;
|
|
|
|
// data buffering
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
if (ic[cNo].cells.c_codes[i] > 50000) {
|
|
return;
|
|
}
|
|
if (i == 0) { ic[cNo].cells.c_codes[i] += 0; }
|
|
if (i == (DEVICE_MAX_CELL - 1)) { ic[cNo].cells.c_codes[i] += 0; }
|
|
}
|
|
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
PackBuf[ch].cvolt[i] = ic[cNo].cells.c_codes[i] / 10; // Cell n
|
|
}
|
|
|
|
if (PackBuffFlag == false) {
|
|
PackBuffFlag = true;
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
for (int j = 0; j < PACK_BUF_MAX; j++) {
|
|
PackBuf[j].cvolt[i] = ic[cNo].cells.c_codes[i] / 10; // Cell n
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t max, min;
|
|
// Volt Avg
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
tmp = 0;
|
|
for (int j = 0; j < PACK_BUF_MAX; j++) {
|
|
if (j == 0) {
|
|
max = min = PackBuf[j].cvolt[i];
|
|
} else {
|
|
if (max < PackBuf[j].cvolt[i]) { max = PackBuf[j].cvolt[i];}
|
|
if (min > PackBuf[j].cvolt[i]) { min = PackBuf[j].cvolt[i];}
|
|
}
|
|
tmp += PackBuf[j].cvolt[i];
|
|
}
|
|
if ((GetCB_Rest() == true) || (device_status.battery_status.bit.DCP == true)) {
|
|
device_value.Cell.voltage[i] = (uint16_t)((tmp - max - min) / (PACK_BUF_MAX - 2));
|
|
}
|
|
}
|
|
|
|
SaveOneBuffTime();
|
|
if (PackBufCh == 0) { SaveAllBuffTime(); }
|
|
|
|
PackBufCh++;
|
|
PackBufCh %= PACK_BUF_MAX;
|
|
}
|
|
|
|
void MakeCellAvgData(void)
|
|
{
|
|
uint32_t sum;
|
|
TMinMax min, max;
|
|
|
|
sum = 0;
|
|
memset(&min, 0x00, sizeof(TMinMax));
|
|
memset(&max, 0x00, sizeof(TMinMax));
|
|
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
if (i == 0) {
|
|
max.value = device_value.Cell.voltage[i];
|
|
max.num = i;
|
|
min.value = device_value.Cell.voltage[i];
|
|
min.num = i;
|
|
}
|
|
if (max.value < device_value.Cell.voltage[i]) {
|
|
max.value = device_value.Cell.voltage[i];
|
|
max.num = i;
|
|
}
|
|
if (min.value > device_value.Cell.voltage[i]) {
|
|
min.value = device_value.Cell.voltage[i];
|
|
min.num = i;
|
|
}
|
|
sum += device_value.Cell.voltage[i];
|
|
}
|
|
|
|
device_value.BatVoltage = sum / 10;
|
|
|
|
device_value.Cell.avg.Max.value = max.value;
|
|
device_value.Cell.avg.Max.num = max.num;
|
|
device_value.Cell.avg.Min.value = min.value;
|
|
device_value.Cell.avg.Min.num = min.num;
|
|
|
|
device_value.Cell.avg.diff = max.value - min.value;
|
|
device_value.Cell.avg.avg = (uint16_t)(sum / DEVICE_MAX_CELL);
|
|
}
|
|
|
|
// Cell Balancing Flag
|
|
void clear_cb_value(void)
|
|
{
|
|
CB_Tick = 0;
|
|
CB_Rest = 1;
|
|
CbRestAlarmTick = (1 * 1000 / 5);
|
|
}
|
|
|
|
void dec_cb_tick(uint16_t tick)
|
|
{
|
|
if (CB_Tick > tick) {
|
|
CB_Tick -= tick;
|
|
} else {
|
|
CB_Tick = 0;
|
|
}
|
|
|
|
if (CbRestAlarmTick > tick) {
|
|
CbRestAlarmTick -= tick;
|
|
} else {
|
|
CbRestAlarmTick = 0;
|
|
}
|
|
}
|
|
|
|
uint8_t GetCB_Rest(void)
|
|
{
|
|
if (((CB_Rest == 1) || (CB_Rest == 3)) && (CbRestAlarmTick == 0)) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void CheckAutoBalancingEnable(void)
|
|
{
|
|
PDEVICE_PARAM param = &device_param;
|
|
|
|
// Check Cell balanicng Mode (Balancing active or not)
|
|
if (CellBalancingFlag == true) {
|
|
// Before cell balancing status is ON
|
|
if (device_value.Cell.avg.Max.value < param->chg_ctrl.cell_bal_cfg.Cell_Balance_Threshold) {
|
|
CellBalancingFlag = false;
|
|
} else if (device_value.Cell.avg.diff < (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Min - 5)) {
|
|
if (device_status.warning.bit.cell_over_voltage == 0) {
|
|
CellBalancingFlag = false;
|
|
}
|
|
}
|
|
} else {
|
|
// Before cell balancing status is OFF
|
|
if (device_value.Cell.avg.Max.value > (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Threshold + param->chg_ctrl.cell_bal_cfg.Cell_Balance_Window)) {
|
|
if (device_value.Cell.avg.diff > param->chg_ctrl.cell_bal_cfg.Cell_Balance_Min) {
|
|
CellBalancingFlag = true;
|
|
} else if (device_status.warning.bit.cell_over_voltage) {
|
|
CellBalancingFlag = true;
|
|
} else {
|
|
// Host balancing control prio
|
|
if (HostBalancingVoltage > DEFAULT_HOST_BALANCING_VOLTAGE) {
|
|
CellBalancingFlag = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CellBalancingFlag == false) {
|
|
if ((HostBalancingVoltage > DEFAULT_HOST_BALANCING_VOLTAGE) && (HostBalancingVoltageDiff > 0)) {
|
|
CellBalancingFlag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t CalcBalancingVolt(void)
|
|
{
|
|
uint32_t balancing_avg = 0;
|
|
|
|
if (device_value.Cell.avg.diff > 100) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 20;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 20;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 80) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 15;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 15;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 60) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 10;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 10;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 40) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 5;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 5;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 30) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 2;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 2;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 20) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 2;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 2;
|
|
}
|
|
} else if (device_value.Cell.avg.diff > 10) {
|
|
if ((device_value.Cell.avg.Max.value - device_value.Cell.avg.avg) > (device_value.Cell.avg.avg - device_value.Cell.avg.Min.value)) {
|
|
balancing_avg = device_value.Cell.avg.avg + 1;
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg - 1;
|
|
}
|
|
} else {
|
|
balancing_avg = device_value.Cell.avg.avg;
|
|
}
|
|
|
|
if ((HostBalancingVoltage > DEFAULT_HOST_BALANCING_VOLTAGE) && (device_value.Cell.avg.diff < HostBalancingVoltageDiff)) {
|
|
balancing_avg = HostBalancingVoltage;
|
|
}
|
|
return balancing_avg;
|
|
}
|
|
|
|
#define REST_TIME_ADD 2000
|
|
|
|
uint8_t CheckCbRest(void)
|
|
{
|
|
uint8_t setBal = false;
|
|
|
|
if (CellBalancingFlag == true) {
|
|
if (CB_Tick == 0) {
|
|
switch (CB_Rest) {
|
|
case 0: // Work to Rest
|
|
CB_Rest = 1;
|
|
setBal = true;
|
|
CbRestAlarmTick = (1 * 1000 / 2);
|
|
CB_Tick = 1 * 1000 + REST_TIME_ADD;
|
|
break;
|
|
case 1: // Rest to Work
|
|
CB_Rest = 2;
|
|
setBal = true;
|
|
if ((device_value.Cell.avg.diff < 10) && (device_value.Cell.avg.Min.value > 3400)) {
|
|
CB_Tick = 1 * 1000 / 5;
|
|
} else {
|
|
CB_Tick = 1 * 1000 / 2;
|
|
}
|
|
break;
|
|
case 2: // Work to Rest
|
|
CB_Rest = 3;
|
|
setBal = true;
|
|
CbRestAlarmTick = (1 * 1000 / 2);
|
|
CB_Tick = 1 * 1000 + REST_TIME_ADD;
|
|
break;
|
|
case 3: // Rest to Work
|
|
CB_Rest = 0;
|
|
setBal = true;
|
|
if ((device_value.Cell.avg.diff < 10) && (device_value.Cell.avg.Min.value > 3400)) {
|
|
CB_Tick = 1 * 1000 / 5;
|
|
} else {
|
|
CB_Tick = 1 * 1000 / 2;
|
|
}
|
|
break;
|
|
default: // Rest to Work
|
|
CB_Rest = 0;
|
|
setBal = true;
|
|
CbRestAlarmTick = (1 * 1000 / 2);
|
|
CB_Tick = 1 * 1000 + REST_TIME_ADD;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if ((CB_Rest == 0) || (CB_Rest == 2)) {
|
|
CB_Rest = 1;
|
|
CbRestAlarmTick = (1 * 1000 / 2);
|
|
CB_Tick = 1 * 1000 + REST_TIME_ADD;
|
|
setBal = true;
|
|
}
|
|
}
|
|
return setBal;
|
|
}
|
|
|
|
uint8_t CheckCbRest1(void)
|
|
{
|
|
uint8_t setBal = false;
|
|
PDEVICE_PARAM param = &device_param;
|
|
|
|
if (CellBalancingFlag == true) {
|
|
if (CB_Tick == 0) {
|
|
switch (CB_Rest) {
|
|
case 0: // Work to Rest
|
|
CB_Rest = 1;
|
|
setBal = true;
|
|
CbRestAlarmTick = (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5);
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 + REST_TIME_ADD;
|
|
break;
|
|
case 1: // Rest to Work
|
|
CB_Rest = 2;
|
|
setBal = true;
|
|
if ((device_value.Cell.avg.diff < 10) && (device_value.Cell.avg.Min.value > 3400)) {
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5;
|
|
} else {
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 2;
|
|
}
|
|
break;
|
|
case 2: // Work to Rest
|
|
CB_Rest = 3;
|
|
setBal = true;
|
|
CbRestAlarmTick = (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5);
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 + REST_TIME_ADD;
|
|
break;
|
|
case 3: // Rest to Work
|
|
CB_Rest = 0;
|
|
setBal = true;
|
|
if ((device_value.Cell.avg.diff < 10) && (device_value.Cell.avg.Min.value > 3400)) {
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5;
|
|
} else {
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 2;
|
|
}
|
|
break;
|
|
default: // Rest to Work
|
|
CB_Rest = 0;
|
|
setBal = true;
|
|
CbRestAlarmTick = (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5);
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 + REST_TIME_ADD;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if ((CB_Rest == 0) || (CB_Rest == 2)) {
|
|
CB_Rest = 1;
|
|
CbRestAlarmTick = (param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000 / 5);
|
|
CB_Tick = param->chg_ctrl.cell_bal_cfg.Cell_Balance_Interval * 1000;
|
|
setBal = true;
|
|
}
|
|
}
|
|
return setBal;
|
|
}
|
|
|
|
void AutoCellBalancing(uint32_t rBal)
|
|
{
|
|
uint32_t bal = rBal;
|
|
uint8_t setBal = false;
|
|
|
|
setBal = CheckCbRest();
|
|
|
|
// Check Cell balanicng Mode (Balancing active or not)
|
|
CheckAutoBalancingEnable();
|
|
|
|
device_status.battery_status.bit.CB = CellBalancingFlag;
|
|
|
|
if (CellBalancingFlag == true) {
|
|
if (setBal == true) {
|
|
setBal = false;
|
|
if ((CB_Rest == 0) || (CB_Rest == 2)) {
|
|
Balance_Avg = CalcBalancingVolt();
|
|
|
|
bal = 0;
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
if ((device_value.Cell.voltage[i] > Balance_Avg) || ((device_status.cov_warning_cell.uValue >> i) == 1)) {
|
|
bal = bal | (1 << i);
|
|
}
|
|
}
|
|
// Write Balancing
|
|
cBal = bal;
|
|
WriteBalData(cBal);
|
|
} else {
|
|
// CB Rest time
|
|
bal = 0;
|
|
cBal = bal;
|
|
WriteBalData(cBal);
|
|
}
|
|
}
|
|
} else {
|
|
if (setBal) {
|
|
WriteBalData(0);
|
|
setBal = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteBalData(uint32_t wBal)
|
|
{
|
|
// dccBits_a = (wBal >> 0) & 0x0FFF; // Cell #1 - #12
|
|
// dccBits_b = (wBal >> 12) & 0x0007; // Cell #13 - #15
|
|
|
|
for (int i = 0; i < 12; i++) { if ((wBal >> (i + 0)) & 0x0001) dccBitsA[i] = 1; else dccBitsA[i] = 0; }
|
|
for (int i = 0; i < 3; i++) { if ((wBal >> (i + 12)) & 0x0001) dccBitsB[i + 1] = 1; else dccBitsB[i + 1] = 0; }
|
|
|
|
ADBMS1818_set_cfgr (0, bms_ic, REFON, ADCOPT, gpioBitsA, dccBitsA , dctoBits, UV, OV);
|
|
ADBMS1818_set_cfgrb(0, bms_ic, FDRF , DTMEN , psBits , gpioBitsB, dccBitsB);
|
|
ADBMS1818_wrcfg (TOTAL_IC, bms_ic);
|
|
ADBMS1818_wrcfgb(TOTAL_IC, bms_ic);
|
|
}
|
|
|
|
void SetManualBalancing(uint8_t mode, uint8_t flag, uint32_t cNo)
|
|
{
|
|
ManualBalFlag = mode;
|
|
|
|
if (ManualBalFlag == true) {
|
|
if (cNo == 0) {
|
|
ManualBalValue = 0;
|
|
} else {
|
|
if (flag) {
|
|
ManualBalValue |= (1 << (cNo - 1));
|
|
} else {
|
|
ManualBalValue &= ~(1 << (cNo - 1));
|
|
}
|
|
}
|
|
} else {
|
|
ManualBalValue = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void ManualCellBalancing(uint32_t rBal)
|
|
{
|
|
uint32_t bal = 0;
|
|
|
|
for (int i = 0; i < DEVICE_MAX_CELL; i++) {
|
|
if ((rBal >> i) & 0x00000001) {
|
|
bal |= (uint32_t)(1 << i);
|
|
}
|
|
}
|
|
|
|
if (ManualBalFlag == true) {
|
|
if (bakManualBalValue != ManualBalValue) {
|
|
WriteBalData(ManualBalValue);
|
|
bakManualBalValue = ManualBalValue;
|
|
}
|
|
} else {
|
|
if (bal != 0) {
|
|
WriteBalData(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Public Status Function ************************************************************/
|
|
uint16_t GetBalanceAvg(void)
|
|
{
|
|
return Balance_Avg;
|
|
}
|
|
|
|
uint16_t GetBalanceFlag(void)
|
|
{
|
|
return device_status.battery_status.bit.CB;
|
|
}
|
|
|
|
/* Display Function ******************************************************************/
|
|
#ifdef CONSOLE_DEBUG
|
|
void AfeDisplayStatus(uint8_t p);
|
|
|
|
void ODisplayBalancingValue(uint8_t p)
|
|
{
|
|
float aParam;
|
|
|
|
if (p || (disBalance_Avg != Balance_Avg))
|
|
{
|
|
aParam = (float)Balance_Avg / 1000;
|
|
FloatNormalOutIntXY(71, 10 , aParam, 5, 3);
|
|
disBalance_Avg = Balance_Avg;
|
|
}
|
|
|
|
if (p || (disCellBalancingFlag != GetBalanceFlag()))
|
|
{
|
|
if (GetBalanceFlag() == true)
|
|
YellowOutXY(68, 11, (uint8_t *)"O");
|
|
else
|
|
GreenOutXY(68, 11, (uint8_t *)"X");
|
|
disCellBalancingFlag = GetBalanceFlag();
|
|
}
|
|
|
|
if (p || ((disCB_Tick / 1000) != (CB_Tick / 1000)))
|
|
{
|
|
aParam = (float)(disCB_Tick / 1000);
|
|
FloatNormalOutIntXY(70, 11,aParam, 6, 0);
|
|
disCB_Tick = CB_Tick;
|
|
}
|
|
//
|
|
// AfeDisplayStatus(p);
|
|
}
|
|
|
|
const char AfeMODE_STR[][84] =
|
|
{
|
|
// 1 2 3 4 5 6 7 8
|
|
// 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
"\r\n+--[Battery : ]---+---+---+---+---+---+---+---+---+---+--------------------+", // 2
|
|
"\r\n| | B |OCA|TCA|TDA|AFE|RSV|ZVC|F-C|F-D| Warning ( )HEX|", // 3
|
|
"\r\n| HeartB ( ) | S |INI|MD |FC |FD |RXM|ZVC|F-C|F-D| Protection( )HEX|", // 4
|
|
"\r\n+---------------------+---+---+---+---+---+---+---+---+---+-[CELL AVG ]--------+", // 5
|
|
"\r\n| B-Volt ( . )V | W |RSV|RSV|RSV|L-C|LTD|LTC|HTD|HTC| C-MAX ( . )V |", // 6
|
|
"\r\n| Current ( . )A | N |RSV|RSV|OCD|OCC|CUV|SUV|COV|SOV| C-AVG ( . )V |", // 7
|
|
"\r\n| SOC ( . )% +---+---+---+---+---+---+---+---+---+ C-MIN ( . )V |", // 8
|
|
"\r\n| | P |AFE|RSV|SC |RSV|LTD|LTC|HTD|HTC| C-DIFF ( . )V |", // 9
|
|
"\r\n+---------------------+ T |RSV|RSV|OCD|OCC|CUV|SUV|COV|SOV| CB AVG ( . )V |", // 0
|
|
"\r\n| F-Capa ( . )AH +---+---+---+---+---+---+---+---+---+ [ ] |", // 1
|
|
"\r\n+---------------------+ AFE STATUS ( ) UV TRIP ( ) +-[TEMP AVG ]--------+", // 2
|
|
"\r\n| Operate( )| CTRL1 ( ) OV TRIP ( ) | T-MAX ( . )C |", // 3
|
|
"\r\n| Status ( )| CTRL2 ( ) | T-AVG ( . )C |", // 4
|
|
"\r\n| | PROTECT( - - ) | T-MIN ( . )C |", // 5
|
|
"\r\n+--[Cell Volt]--------+ M( )| T-DIFF ( . )C |", // 6
|
|
"\r\n|( . )( . )( . )+---------+---------+---------+-----+---+----------------+", // 7
|
|
"\r\n|( . )( . )( . )|T1( . )|T3( . )|F1( . )| +-<INFORMATION>--+", // 8
|
|
"\r\n|( . )( . )( . )| | | | |DATE: / / |", // 9
|
|
"\r\n|( . )( . )( . )|T2( . )|T4( . )|F2( . )| |TIME: : : |", // 0
|
|
"\r\n|( . )( . )( . )| | | | |L-REBOOT ( )|", // 1
|
|
"\r\n+---------------------+---------+---------+---------+---------+----------------+", // 2
|
|
"\r\n s : SETTING d : DIAGNOSTIC " // 3
|
|
};
|
|
|
|
void AfemodeScreen(void)
|
|
{
|
|
for (int i = 0; i < 22; i++)
|
|
{
|
|
printf("%s", AfeMODE_STR[i]);
|
|
delay_os_ms(2);
|
|
}
|
|
}
|
|
|
|
int dis_numberOfCells;
|
|
uint8_t dis_AfeStatus;
|
|
uint8_t dis_AfeCtrl1;
|
|
uint8_t dis_AfeCtrl2;
|
|
uint8_t dis_AfeUvTrip;
|
|
uint8_t dis_AfeOvTrip;
|
|
|
|
uint8_t dis_AfeProtect[3];
|
|
|
|
//int dis_cellVoltages[MAX_NUMBER_OF_CELLS]; // mV
|
|
//uint32_t dis_batVoltage; // mV
|
|
//int dis_temperatures[MAX_NUMBER_OF_THERMISTORS]; //
|
|
//uint8_t dis_balStatus[3];
|
|
//int dis_adcGain = 0; // uV/LSB
|
|
//int dis_adcOffset = 0; // mV
|
|
|
|
//extern int adcGain; // uV/LSB
|
|
//extern int adcOffset; // mV
|
|
|
|
//void AfeDisplayStatus(uint8_t p)
|
|
//{
|
|
// uint8_t text[20];
|
|
// uint32_t sts = 0;
|
|
//
|
|
// sts = getAfeStatus();
|
|
// if (p || (dis_AfeStatus != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(35, 12, text);
|
|
// dis_AfeStatus = sts;
|
|
// }
|
|
// sts = getAfeCtrl1();
|
|
// if (p || (dis_AfeCtrl1 != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(35, 13, text);
|
|
// dis_AfeCtrl1 = sts;
|
|
// }
|
|
// sts = getAfeCtrl2();
|
|
// if (p || (dis_AfeCtrl2 != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(35, 14, text);
|
|
// dis_AfeCtrl2 = sts;
|
|
// }
|
|
//
|
|
// for (int i = 0; i < 3; i++)
|
|
// {
|
|
// sts = getAfeProtect(i);
|
|
// if (p || (dis_AfeProtect[i] != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(29 + (i * 3), 15, text);
|
|
// dis_AfeProtect[i] = sts;
|
|
// }
|
|
// }
|
|
//
|
|
// sts = getAfeUvTrip();
|
|
// if (p || (dis_AfeUvTrip != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(55, 12, text);
|
|
// dis_AfeUvTrip = sts;
|
|
// }
|
|
// sts = getAfeOvTrip();
|
|
// if (p || (dis_AfeOvTrip != sts))
|
|
// {
|
|
// sprintf((char *)text, "%02X", sts);
|
|
// GreenOutXY(55, 13, text);
|
|
// dis_AfeOvTrip = sts;
|
|
// }
|
|
//
|
|
// sts = getBatteryVoltage();
|
|
// if (p || (dis_batVoltage != sts))
|
|
// {
|
|
// sprintf((char *)text, "%6.3f", (float)sts / 1000);
|
|
// GreenOutXY(51, 14, text);
|
|
// dis_batVoltage = sts;
|
|
// }
|
|
//
|
|
// for (int i = 0; i < 3; i++)
|
|
// {
|
|
// if (p || (dis_device_value.SectVoltage[i] != device_value.SectVoltage[i]))
|
|
// {
|
|
// sprintf((char *)text, "%7.3f", (float)device_value.SectVoltage[i] / 1000);
|
|
// GreenOutXY(38 + (i * 7), 15, text);
|
|
// dis_device_value.SectVoltage[i] = device_value.SectVoltage[i];
|
|
// }
|
|
// }
|
|
//
|
|
// if (p || (dis_adcGain != adcGain))
|
|
// {
|
|
// sprintf((char *)text, "%04X", adcGain);
|
|
// GreenOutXY(32, 16, text);
|
|
// dis_adcGain = adcGain;
|
|
// }
|
|
|
|
// if (p || (dis_adcOffset != adcOffset))
|
|
// {
|
|
// sprintf((char *)text, "%04X", adcOffset);
|
|
// GreenOutXY(40, 16, text);
|
|
// dis_adcOffset = adcOffset;
|
|
// }
|
|
//}
|
|
|
|
uint8_t AfeSetBal(uint8_t *text, uint8_t aDevice)
|
|
{
|
|
uint16_t size, i, j;
|
|
int32_t aParam[4];
|
|
uint8_t temp[10];
|
|
uint8_t ret;
|
|
|
|
ret = 0;
|
|
size = 0;
|
|
|
|
while (text[size] != 0x00) size++; /* Measure total string length */
|
|
|
|
switch (aDevice) {
|
|
case '0' : // Cell Balance Parameter Setting
|
|
i = 0;
|
|
if (size < 1 ) return(1);
|
|
|
|
j = FIND_CHAR((uint8_t *)&text[i], temp, size - i, ' ');
|
|
i = i + j + 1;
|
|
temp[j] = 0x00;
|
|
aParam[0] = StrToInt(temp, j);
|
|
|
|
// SetManualBalancing(true, aParam[0]);
|
|
break;
|
|
case '1': // Cellbal off
|
|
// SetManualBalancing(false, text[2] - 0x30);
|
|
break;
|
|
default:
|
|
ret = 1;
|
|
break;
|
|
}
|
|
return(ret); /* Setting value OK */
|
|
}
|
|
|
|
/* Command processing in SETTING STATE */
|
|
int8_t Process_DataAfeModeSet(uint8_t *text, uint16_t size)
|
|
{
|
|
uint16_t i, ret;
|
|
|
|
/* Invalid command if space is in first position */
|
|
if (size > 1) {if (text[1] != ' ') return(0);}
|
|
text[size] = 0x00;
|
|
ret = 1;
|
|
|
|
switch (text[0])
|
|
{
|
|
case 'o': /* Switch to operating mode */
|
|
screenmode = OP_MODE;
|
|
ViewScreen();
|
|
break;
|
|
case 's': /* Switch to setting status */
|
|
screenmode = SET_MODE;
|
|
ViewScreen();
|
|
break;
|
|
case 'd': /* Switch to diagnostic mode */
|
|
screenmode = DIAG_MODE;
|
|
ViewScreen();
|
|
break;
|
|
case '0': // OCV Alarm Parameter Setting
|
|
case '1': // OCV Alarm Parameter Setting
|
|
i = AfeSetBal(&text[2], text[0]);
|
|
if (i == 0)
|
|
UserMessage("COMMAND COMPLETE");
|
|
else if (i == 1)
|
|
UserMessage("COMMAND FORMAT ERROR"); /* setting format error */
|
|
else
|
|
UserMessage("COMMAND LIMIT ERROR"); /* setting limit error */
|
|
break;
|
|
default :
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
#endif // #ifdef CONSOLE_DEBUG
|
|
|
|
/*******************(C)COPYRIGHT 2022 AMG ********************END OF FILE******/
|