Files
jiacun-20s-200A/README/readme.txt
T
2026-08-25 17:30:40 +08:00

5039 lines
144 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
2022.4.19 将默认HSE-PLL 72M系统时钟改为 HSI-PLL 36M时钟
2022.4.21 Modbus F03调试成功,注意限制长度及可靠性测试,还存在大小端问题
//第一阶段计划
2022.5.11 完成CAN发送接收测试,完成上位机读写功能;
2022.5.12-2022.5.13 完成BMS 基本功能;
2022.5.14-2022.5.15 完成BMS CAN Sol-Ark 协议;
2022.5.16 整体测试;
2022.5.17 PCB发客户测试;
//第二阶段计划
/////////////////////////////////SOC 增加部分3.17
(GasGuage.c 130-133行)
if(bmsMem.packVoltage > 56000)
{
bmsMem.rcc = bmsMem.fcc;
}
(GasGuage.c 151-154行)
if(bmsMem.packVoltage < 43000)
{
bmsMem.rcc = 0;
}
////////////////////CAN协议汇总////////////////////
Sol-Ark
GoodWe 3.6
Koyoe 3.16
Aiswei 3.17
SMA 3.21
Sorotec 3.27
//////////更改
1.固德威的温度输出修改回bmsMem.mcu_T1
2.添加容量传输的协议,仿真注意看一下
Sol-Ark 0x379
Koyoe 0x18FAD201 (后加)
Aiswei 0x379
SMA 0x35F (后加)
Sorotec 0x35F
3.将TxMessage[CAN_SendCount]使用更全面,避免TxMessage[1]一类
///////////屏幕第6个协议的选择相关
void SDWA_Init(void)增加
SDWA_DispProcotol(0x03,0x05,0x00);
void SDWA_RecvData(void)
else if((sdwaBuf[2]==0x06) && (sdwaBuf[3]==0x83) && (sdwaBuf[4]==0x03) && (sdwaBuf[5]==0x15))
{
if(sdwaBuf[8]==0x01)
{
if(protocol !=0x06)
protocol = 0x06;
}
else if(sdwaBuf[8]==0x00)
{
if(protocol ==0x06)
protocol = 0x00;
}
}
///////////Growatt CAN协议,暂放在第1页第3个,代替Koyoe协议 4.3
///////////并联电流,取值位置0x42
/////////////////////////////////放电过流保护显示4.4
//在上位机中新增的放电过流保护功能,给定值输入给变量bmsMem.mcu_ocd
//bmsMem.temperaStatus的bit5是用来新增判断放电过流保护的标志位
SDWA.c中改动显示的判断(L538)
if( ((bmsMem.bStatus1 & 0x0c) != 0) || ((bmsMem.temperaStatus & 0x20) != 0) )
//放电过流报警的屏幕代码 A5 5A 05 82 01 94 00 01
AFE_SH367309.c改动报警判断(L566)
void AFE_ProtectProcess(void)
将bmsMem.temperaStatus & 0x0f改为0x2f
if( ( (bmsMem.bStatus1 & 0x7f)!=0) || ( (bmsMem.bStatus2 & 0x0f) !=0) || ((bmsMem.bStatus3 & 0x18) !=0) || (bmsMem.temperaStatus & 0x2f) !=0)
can.c改动报警判断
(L158 - Sol-Ark)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
(L461 - GoodWe)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
(L867 - Growatt)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
(L1231 - Aiswei)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
(L1603 - SMA)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
(L1892 - Sorotec)
if( ((bmsMem.bStatus1 & 0x2c) !=0) || ((bmsMem.temperaStatus & 0x20) !=0) )
/////////////////////////////////充电过流保护显示4.4
//在上位机中新增的充电过流保护功能,给定值输入给变量bmsMem.mcu_occ
//bmsMem.temperaStatus的bit4是用来新增判断充电过流保护的标志位
SDWA.c中改动显示的判断(L538)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
//充电过流报警的屏幕代码 A5 5A 05 82 01 94 00 01
AFE_SH367309.c改动报警判断(L566)
void AFE_ProtectProcess(void)
将bmsMem.temperaStatus & 0x2f改为0x3f
if( ( (bmsMem.bStatus1 & 0x7f)!=0) || ( (bmsMem.bStatus2 & 0x0f) !=0) || ((bmsMem.bStatus3 & 0x18) !=0) || (bmsMem.temperaStatus & 0x3f) !=0)
can.c改动报警判断
(L210 - Sol-Ark)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
(L513 - GoodWe)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
(L858 - Growatt)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
(L1283 - Aiswei)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
(L1614 - SMA)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
(L1903 - Sorotec)
if( ((bmsMem.bStatus1 & 0x10) !=0) || ((bmsMem.temperaStatus & 0x10) !=0) )
/////////////////////////////////过流延时恢复功能4.4
【adc.c】(L348)
if( (bmsMem.temperaStatus & BIT4) !=0 || (bmsMem.temperaStatus & BIT5) !=0 )
/////////////////////////////////DIDO中断MOS 4.20
//1.【gpio.c】修改PB15的定义变量名,把初始化改为输入脚
(L35)
#define PIN_DIDO GPIO_Pin_15 //一键开关MOS
(L250)
//DIDO
GPIO_InitStructure.GPIO_Pin = PIN_DIDO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//2.【gpio.c】将原主动均衡功能改为PA8引脚,初始化改为输出脚,输出信号启动主动均衡板
(L24)
#define PIN_BAL_OUT GPIO_Pin_8 //主动均衡板启动信号,高电平启动
(L257)
//BAL
GPIO_InitStructure.GPIO_Pin = PIN_BAL_OUT;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
(L255->L261)
GPIO_ResetBits(GPIOA, PIN_BAL_OUT); //将原来初始化PIN_BAL时置0的操作移动给现在的引脚
(L161\L166)//从PB15到PA8
GPIO_SetBits(GPIOA, PIN_BAL_OUT);
GPIO_ResetBits(GPIOA, PIN_BAL_OUT);
//3.增加对DIDO口的反应
【global.h】(L401)
//DIDO
extern uint8_t DIDO(void);//4.17新增一键开关MOS
extern void DIDO_TIM_Moni(void);
【gpio.c】
(L43)
uint8_t DIDOMoniCount = 0;
(L198)
//DIDO(PB15) 输入-按下是0
uint8_t DIDO(void)
{
uint8_t status;
status = GPIO_ReadInputDataBit(GPIOB,PIN_DIDO);
return status;
}
void DIDO_TIM_Moni(void) // 通过改变tmperaStatus标志的bit6来控制MOS
{
(略)
}
//4.【AFE_SH367309.c】只亮报警灯不跳转屏幕的判断条件增加bit6(L583)
else if( (bmsMem.temperaStatus & 0x40) !=0)
{
LED_ALARM_On();
}
//5.10ms中断速度太快,需特意改为只执行一次输出置高或置低
【global.h】(L404)
extern void DIDO_CTRL_On(void);
extern void DIDO_CTRL_Off(void);
【gpio.c】
(L44)
uint8_t DIDOFlag = 0;
(L150)
void DIDO_CTRL_On(void)
{
if(DIDOFlag == 1)
{
GPIO_SetBits(GPIOB, PIN_CTLC);
DIDOFlag=0;
}
}
void DIDO_CTRL_Off(void)
{
if(DIDOFlag == 0)//初始为0,当执行一次关MOS后就置为1,使引脚只控制1次
{
GPIO_ResetBits(GPIOB, PIN_CTLC);
DIDOFlag=1;
}
}
//6.AFE控制函数void AFE_Ctrl(void)分化为中断内执行函数和主循环执行函数
【global.h】(L406)
extern void DIDO_Ctrl(void);
【AFE_SH367309.c】(L427)
void DIDO_Ctrl(void)//10ms中断
{
if((bmsMem.temperaStatus & 0x3f) == 0) //不能影响BIT0-5功能的运作
{
(略)
}
}
void AFE_Ctrl(void)//主循环1s
{
if((bmsMem.temperaStatus & 0x40) == 0) //不能影响BIT6功能的运作
{
(略)
}
}
//7.【tim.c】void TIM3_IRQHandler(void)增加
DIDO_TIM_Moni();
DIDO_Ctrl();
////////////////////SOC判0 4.21
【GasGauge.c】(L156)
if(bmsMem.packVoltage < 43000)
{
bmsMem.rcc = 0;
}
////////////////////FLASH写入默认值的修改 4.23
【global.c】(L70)
bmsMem.E2uiDsgEndVol = 2500;
【flash.c】(L92-97/L138-143)
0xEE, //过压保护电压 3750mV
0x62,
0xC6, //过压保护释放 3550mV
0x7D, //欠压保护电压 2500mV
0x87, //欠压保护释放 2700mV
0xAF, //平衡开启电压 3500mV
/////////////////////////////////增加显示屏CAN协议选择共4页33项 4.26
void CAN_UpdateData(void)
增加若干项
else if(protocol_Index == 7){}
void SDWA_Init(void)
注意修改中间的判断条件,增加若干项
SDWA_DispProcotol(0x03,0x06,0x00);
void SDWA_RecvData(void)
增加若干项
else if((sdwaBuf[2]==0x06) && (sdwaBuf[3]==0x83) && (sdwaBuf[4]==0x03) && (sdwaBuf[5]==0x16))
{
if(sdwaBuf[8]==0x01)
{
if(protocol !=0x07)
protocol = 0x07;
}
else if(sdwaBuf[8]==0x00)
{
if(protocol ==0x07)
protocol = 0x00;
}
}
////////////////////屏幕急停 4.28
【L690】void SDWA_UpdateData(void)
if( (bmsMem.temperaStatus & 0x40) !=0)
{
SDWA_Send_VAR(0x0100, 5);
}
else
{
if(bCHGING ==1)
{
//SDWA_Send_Charging(0x0100);
SDWA_Send_VAR(0x0100, 0);
}
else
{
//SDWA_Send_Discharging(0x0100);
SDWA_Send_VAR(0x0100, 1);
}
}
////////////////////温度差保护 5.12
【adc.c】
(L59)
uint8_t Tdifference_count;
(L113)
int16_t T[4];
uint8_t i;
uint16_t max,min;
(L158)
//计算电芯温度最大最小值
T[0] = bmsMem.mcu_T1;
T[1] = bmsMem.mcu_T2;
T[2] = bmsMem.mcu_T3;
T[3] = bmsMem.mcu_T4;
max = T[0];
min = T[0];
for(i=0;i<4;i++)
{
if(max<T[i])
{
max = T[i];
}
if(min>T[i])
{
min = T[i];
}
}
(L177)
//温差报警
if((bmsMem.temperaStatus & BIT7) ==0)
{
if( (max-min)>100 ) //温差超过100*0.1摄氏度
{
Tdifference_count++;
if(Tdifference_count> 3)
{
bmsMem.temperaStatus |= BIT7; //连续发生低温,保护
Tdifference_count = 0;
}
}
else
{
Tdifference_count = 0;
}
}
(L405)
//温差保护释放
if((bmsMem.temperaStatus & BIT7) !=0)
{
if( (max-min)<100 ) //温差不再超过100*0.1摄氏度,确认3遍后执行释放
{
Tdifference_count++;
if(Tdifference_count > 3)
{
bmsMem.temperaStatus &= ~BIT7; //连续发生低温,保护
Tdifference_count = 0;
}
}
else
{
Tdifference_count = 0;
}
}
【AFE_SH367309.c】添加新报警判断-只让报警灯亮,而且不影响其他报警显示
//注意当温差保护时,报警灯亮但是屏幕不显示"Fault"字样
void AFE_ProtectProcess(void)
(L603)
else if( (bmsMem.temperaStatus & 0xC0) !=0)
/////////////////////////////////上位机改变通信地址5.15
1.将bmsMem更新,多加两个变量,此时叠加电流的地址42变为43【需要测试】
【golbal.h】(L282)
//20230411---------------
uint8_t mcu_address; //通过上位机改变板子的通信地址
uint8_t addr_crc;
//20230411---------------
【RS485_Modbus.c】(L471)
modbusBuf[3] = 0x43;
2.SDWA新增,进行通信位改变
【SDWA.c】(L737)
if(bmsMem.mcu_address !=0)
{
EEPROM_WrMulByte(0,0,1,&bmsMem.mcu_address);
delay_ms(5);
bmsMem.E2_485Addr = bmsMem.mcu_address;
bmsMem.mcu_address = 0;
}
/////////////////////////////////上位机修改电池容量显示5.23
【global.h】(L287)
//20230523---------------
uint8_t writeCapacity; //通过上位机改变电池容量显示
uint8_t capacity_crc;
//20230523---------------
【RS485_Modbus.c】(L471)
modbusBuf[3] = 0x44;
【GasGauage.c】
void InitGasGauge(void)(L34)
uint8_t tmpRd;
EEPROM_RdMulByte(7,1,1,&tmpRd);
bmsMem.fcc = 3600 * 1000*tmpRd;
// bmsMem.fcc = 3600 * 100000; //系统满充容量暂定100AH = 100,000mAH = 360,000,000mAS
void GaugeManage(void)(L126) //写入的Ah容量存放在EEPROM内
if(bmsMem.writeCapacity !=0)
{
EEPROM_WrMulByte(7,1,1,&bmsMem.writeCapacity);
delay_ms(5);
bmsMem.fcc = 3600 * 1000*bmsMem.writeCapacity;
bmsMem.writeCapacity = 0;
}
(L152)
bmsMem.rcc = bmsMem.fcc - bmsMem.fcc/100;
(L178)
bmsMem.rcc = bmsMem.fcc/100;
/////////////////////////////////主机与上位机通信,冲突时上位机优先级更高
【RS485_Modbus.c】
(L241)
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x03) ) //读数据处理
{
MODBUS_F03_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x10) ) //写数据处理
{
MODBUS_F10_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xaa) ) //CADC零点校准处理
{
MODBUS_Faa_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xbb) ) //CADC增益校准处理
{
MODBUS_Fbb_Rx(MODBUS_MEM_PT);
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
(L489)
//如果主机在发送阶段收到了数据,说明有上位机在总线上请求数据,此时延后200ms再发送
if(modbusBufIndex > 2)
{
delay_ms(200);
}
//////////代码量太大,修改优化编译等级level0 -> level3
////////////////////MUST逆变器通讯通讯速率为100K 5.6
void uf_CAN1_Init(void)程序下修改
原:
CAN_InitStructure.CAN_Prescaler = 8; //36MHz/8/(1+6+2)=500kbs
改:
if(Protocol != 7)
CAN_InitStructure.CAN_Prescaler = 40; //36MHz/40/(1+6+2)=100kbs
////////////////////SOFAR逆变器协议更新 6.5
////////////////////增加网口1的Modbus协议-硕日////////////////////
上位机/主从机功能:PA9、PA10 (USART1)
网口1PB10、PB11 (USART3)
**********第1步 移植相关函数**********
【uart.c】
void uf_UART3_Init( u32 bound )
void USART3_SendMulByte(uint8_t *p, uint8_t size)
void USART3_IRQHandler(void)
【tim.c】在TIM4_IRQHandler函数中增加
(L95)MODBUS1_IT_TIMUpdate();
【main.c】在main函数中增加
(L63)MODBUS1_Init();
(L132)MODBUS1_IQ_Transmit();
(L120)Protocol_UpdateData();
【global.h】(L453)
//Modbus1 新加
extern void uf_UART3_Init( u32 bound );
extern void USART3_SendMulByte(uint8_t *bufPT, uint8_t size);
extern void MODBUS1_IT_Receive(void);
extern void MODBUS1_IQ_Transmit(void);
extern void MODBUS1_IT_TIMUpdate(void);
extern void MODBUS1_F03_Rx(uint8_t *mem);
extern void MODBUS1_Init(void);
extern void Protocol_UpdateData(void);
【RS485_Modbus_Inverter.c】加入工程下文件夹MOUDLE
**********第2步 起始地址到一个新的类中,参数按照bmsMem的来**********
【global.h】新建结构体
(L315)typedef struct{}PROTOCOL_SENE_MEMORY ;
extern PROTOCOL_SENE_MEMORY SENEMem;
【global.c】
(L21)PROTOCOL_SENE_MEMORY SENEMem;
////////////////////增加网口1的Modbus协议-日月元////////////////////
**********第1步 结构体放入**********
【global.h】新建结构体
(L348)typedef struct{}PROTOCOL_VOLTRONIC_MEMORY ;
(L448)extern PROTOCOL_VOLTRONIC_MEMORY VoltronicMem;
【global.c】
(L22)PROTOCOL_VOLTRONIC_MEMORY VoltronicMem;
【RS485_Modbus_Inverter.c】
(L12)#define MODBUS1_VoltronicMEM_PT (uint8_t *)&VoltronicMem.rsvd1[0]
**********第2步 数据更新的选择框架放入**********
【RS485_Modbus_Inverter.c】
extern uint8_t protocol;
//Modbus协议数据更新
void Protocol_UpdateData(void)
{
if(protocol == 13) MOD_Protocol_SENE();
else if(protocol == 14) MOD_Protocol_Voltronic();
}
//SENE 硕日
void MOD_Protocol_SENE(void)(略)
**********第3步 网口1输出的起始地址改动**********
void MODBUS1_IT_TIMUpdate(void)
(L115)
if(protocol == 13) MODBUS1_F03_Rx(MODBUS1_SENEMEM_PT);
else if(protocol == 14) MODBUS1_F03_Rx(MODBUS1_VoltronicMEM_PT);
////////////////////增加网口1的Modbus协议-华倚太////////////////////
**********第1步 结构体放入**********
【global.h】新建结构体
(L399)typedef struct{}PROTOCOL_WAET_MEMORY ;
(L500)extern PROTOCOL_WAET_MEMORY WAETMem;
【global.c】
(L23)PROTOCOL_WAET_MEMORY WAETMem;
【RS485_Modbus_Inverter.c】
(L13)#define MODBUS1_WAETMEM_PT (uint8_t *)&WAETMem.rsvd1[0]
**********第2步 数据更新的选择框架放入**********
【RS485_Modbus_Inverter.c】
void Protocol_UpdateData(void)
else if(protocol == 15) MOD_Protocol_WAET();
void MOD_Protocol_WAET(void)
(略)
**********第3步 网口1输出的起始地址改动**********
void MODBUS1_IT_TIMUpdate(void)
else if(protocol == 15) MODBUS1_F03_Rx(MODBUS1_WAETMEM_PT);
////////////////////增加网口1的Modbus协议-三科////////////////////
**********第1步 结构体放入**********
【global.h】新建结构体
(L399)typedef struct{}PROTOCOL_SAKO_MEMORY ;
(L500)extern PROTOCOL_SAKO_MEMORY WAETMem;
【global.c】
(L23)PROTOCOL_SAKO_MEMORY WAETMem;
【RS485_Modbus_Inverter.c】
(L13)#define MODBUS1_SAKOMEM_PT (uint8_t *)&SAKOMem.rsvd1[0]
**********第2步 数据更新的选择框架放入**********
【RS485_Modbus_Inverter.c】
void Protocol_UpdateData(void)
else if(protocol == 31) MOD_Protocol_SAKO();
void MOD_Protocol_SAKO(void)
(略)
**********第3步 网口1输出的起始地址改动**********
void MODBUS1_IT_TIMUpdate(void)
else if(protocol == 31) MODBUS1_F03_Rx(MODBUS1_SAKOMEM_PT);
////////////////////容量显示不对,会将原有未写入容量的板子识别为255
void InitGasGauge(void)(L33)
uint8_t tmpRd;
EEPROM_RdMulByte(7,1,1,&tmpRd);
if(tmpRd>0 && tmpRd<255) //如果之前写过容量值,就按照之前的值显示,否则显示100Ah
{
bmsMem.fcc = 3600 * 1000*tmpRd;
}
else
{
bmsMem.fcc = 3600 * 100000; //系统满充容量暂定100AH = 100,000mAH = 360,000,000mAS
tmpRd = 100;
EEPROM_WrMulByte(7,1,1,&tmpRd);
}
////////////////////485网口1的升级功能
当网口1收到报文
清除最后一PAGE回到IAP程序
【RS485_Modbus_Inverter.c】
(L134)
/*******************************
******* 清空PAGE存放信息 *******
** M: A7 55 0f 02 03 04 5A A5 **
** S: A7 65 0f 02 03 04 5A A5 **
********************************/
void MODBUS1_F0f_Rx(void)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal_Inverter(modbus1Buf, 6);
// if( (modbus1Buf[7] == crc16.b8[1]) || (modbus1Buf[6] == crc16.b8[0]) )
if( (modbus1Buf[7] == 0XA5) || (modbus1Buf[6] == 0X5A) )
{
//1.
FLASH_Unlock(); //解锁
FLASH_ErasePage(FLASH_PAGE_ADDR); //擦除Flash的最后一page,0x0800FC00-0x0800FFFF
FLASH_Lock();//上锁
//2.
//FLASH_WrData(FLASH_PAGE_ADDR,0,8);
/*回送接收正确信号到PC*/
modbus1Buf[0] = 0xA7;
modbus1Buf[1] = 0X65;
modbus1Buf[2] = 0X03;
modbus1Buf[3] = 0X02;
modbus1Buf[4] = 0X03;
modbus1Buf[5] = 0X04;
modbus1Buf[6] = 0X5A;
modbus1Buf[7] = 0XA5;
modbus1F0fRxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
}
(L18)
#define FLASH_PAGE_ADDR 0x0800FC00//要擦除的FLASH页地址
(L22)
uint8_t modbus1F0fRxFlg; //升级信息接收正确标记
(L135)
else //485从机
{
if(modbus1BufIndex > 2)
{
if((modbus1Buf[0] == 0xA7) && (modbus1Buf[1] == 0x55))
{
MODBUS1_F0f_Rx(); //485升级操作
}
else
{
MODBUS1_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
else
{
MODBUS1_Init();
}
}
(L101)
if(modbus1FffRxFlg == 1)
{
modbus1FffRxFlg = 0;
modbus1BufIndex = 0;
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
MODBUS1_UART_IT_RX_ENABLE;
delay_ms(1000);
NVIC_SystemReset(); //软件复位
}
else
【global.h】(L667)
extern void MODBUS1_F0f_Rx(void);
//接收清除PAGE指令并回复,测试成功
//////////为方便测试需要,程序均取消了以下改动,注意发货前要修改
0x8000000 ——> 0x8001000
SCB->VTOR = FLASH_BASE | 0x1000;
$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L
////////////////////上位机修改限流值
//原来变量
#define CHG_LIMIT_VALUE 120000 //单位A 出厂设100A
#define CHG_LIMIT_COUNT 5 //过流5S, 启动限流模块
#define CHG_LIMIT_RELEASE_COUNT 600 //限流模块工作60S, 恢复主充电 //出厂设600S
(为统一命名,将先前的
bmsMem.mcu_address改为bmsMem.write_Addr
bmsMem.writeCapacity改为bmsMem.write_Capacity)
//新加上
【global.h】(L290)
//20230614---------------
uint8_t write_CHGLimit_Value; //通过上位机改变限流板保护电流 A
uint8_t write_CHGLimit_Count; //通过上位机改变限流板保护时间 S
uint16_t write_CHGLimit_ReleaseCount; //通过上位机改变限流板保护恢复时间 S
//20230614---------------
【AFE_SH367309.c】
void CHG_LIMIT_Ctrl(void)
//CHG_LIMIT_VALUE -> bmsMem.write_CHGLimit_Value
//CHG_LIMIT_COUNT -> bmsMem.write_CHGLimit_Count
//CHG_LIMIT_RELEASE_COUNT -> bmsMem.write_CHGLimit_ReleaseCount
【flash.c】
(L132)/(L185)
//20230614
100, //保护电流
5, //保护时间
0x58, //保护释放时间 0x0258 = 600
0x02,
uint8_t MEMORY_UpdateFlash(uint32_t addr)
//40改为44,一共2处
//20改为22,一共1处
uint8_t FLASH_ReadCheck(uint32_t addr)
//40改为44,一共3处
////////////////////满充判定条件的总电压值,根据上位机的AFE配置决定
if(bmsMem.packVoltage > 56000) //3.5*16 //3.75*num -4
if(bmsMem.packVoltage < 43000) //2.6875*16 //2.5*num +3
原计算方式:
固定值56V = 单芯3.5V * 串数16
固定值43V = 单芯2.6875V * 串数16
新计算方式:
[模块内个数cellNum]
cellNum = bmsMem.ee_sconf1 & 0x0F;
if( cellNum < 5 ) //其他代表16串,一般是0000
{
cellNum = 16;
}
[单体过压值cell_OV] = ( (bmsMem.ee_ovt_ldrt_ovh & 0x03) <<8 | bmsMem.ee_ovl ) * 5;
[单体欠压值cell_UV] = bmsMem.ee_uv * 20;
[模块过压值pack_OV] = cell_OV * cellNum -4000;
[模块欠压值pack_UV] = cell_UV * cellNum +3000;
/** 20230704增加补丁 **/
////////////////////滤波改小为2,可监测到小电流200mA
void AFE_CurrentProcess(void)
////////////////////最低电压
【global.h】(L65)
extern uint8_t cellNum; //模块内个数
【AFE_SH367309.c】(L362)
for(i=0;i<cellNum;i++)
////////////////////限流板电流*1000
////////////////////华倚太&三科的程序直接用日月元的
//[改变]
//WAET 华倚太
void MOD_Protocol_WAET(void)
{
MOD_Protocol_Voltronic();
}
//SAKO 三科
void MOD_Protocol_SAKO(void)
{
MOD_Protocol_Voltronic();
}
else if(protocol == 15) MODBUS1_F03_Rx(MODBUS1_VoltronicMEM_PT);
else if(protocol == 31) MODBUS1_F03_Rx(MODBUS1_VoltronicMEM_PT);
//[删除]
#define MODBUS1_WAETMEM_PT (uint8_t *)&WAETMem.rsvd1[0]
#define MODBUS1_SAKOMEM_PT (uint8_t *)&SAKOMem.rsvd1[0]
PROTOCOL_WAET_MEMORY WAETMem;
PROTOCOL_SAKO_MEMORY SAKOMem;
extern PROTOCOL_WAET_MEMORY WAETMem;
extern PROTOCOL_SAKO_MEMORY SAKOMem;
//逆变器通信WAET 结构体
typedef struct
{
}PROTOCOL_WAET_MEMORY ;
//逆变器通信SAKO 结构体
typedef struct
{
}PROTOCOL_SAKO_MEMORY ;
////////////////////把UART1和UART3放在不同的定时器内
【tim.c】
//增加(L70)
//10MS
void uf_TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 10000-1;
TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2, TIM_IT_Update,ENABLE );
TIM_Cmd(TIM2, DISABLE); //使能TIMx外设
}
//增加(L123)
//10ms中断
//10ms串口3无数据接收,进入定时器2中断处理接收数据
void TIM2_IRQHandler(void) //TIM2中断
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源
MODBUS1_IT_TIMUpdate();
TIM_Cmd(TIM2,DISABLE);
}
}
//删除(L118)
MODBUS1_IT_TIMUpdate();
【global.h】(L529)
extern void uf_TIM2_Init(void);
【main.c】(L58)
uf_TIM2_Init(); //10MS
【RS485_Modbus_Inverter.c】
#define MODBUS1_UART_TIM TIM2
////////////////////简化程序,CRC校验码出处唯一
【RS485_Modbus_Inverter.c】
//删掉(L30)
const uint16_t CRC16Table_Inverter[256]=
{
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
// CRC 校验函数,返回CRC
BYTE2 CRC16_Cal_Inverter(uint8_t *pdata, uint16_t len)
{
uint16_t i;
BYTE2 CRCData;
CRCData.b16=0XFFFF;
for(i=0;i<len;i++){
CRCData.b16 = CRC16Table_Inverter[CRCData.b8[0]^ *pdata++] ^ CRCData.b8[1];
}
return CRCData;
}
【global.h】
//增加(L481)
extern BYTE2 CRC16_Cal(uint8_t *pdata, uint16_t len);
////////////////////返回IAP程序替换,并简化为5A A5
【RS485_Modbus_Inverter.c】
//增加(L26)
/****************************************
**** 擦除IAP 升级标记 *****
** M: A7 55 20 23 06 18 0X5A 0XA5 **
** S: A7 65 04 0X5A**
*****************************************/
void UART_EraseIAP(void)
{略}
//替换
【RS485_Modbus_Inverter.c】
(L151)
UART_EraseIAP();
(L92)
else if(cmdRxIapFlg == 1)
{
cmdRxIapFlg = 0;
(L163/L184)
CRC16_Cal_Inverter -> CRC16_Cal
【global.h】(L677)
extern void UART_EraseIAP(void);
//删掉
【RS485_Modbus_Inverter.c】(L20)
uint8_t modbus1F0fRxFlg;
//增加
【RS485_Modbus_Inverter.c】
(L21)
uint8_t cmdRxIapFlg; //升级信息 接收正确标记
(L249)
cmdRxIapFlg = 0;
//删掉
【global.h】(L674)
extern void MODBUS1_F0f_Rx(void);
//增加
【global.h】(L579)
extern void UART_EraseIAP(void);
////////////////////0x44 -> 0x46
/////////////////////////////////主机与上位机通信,冲突时上位机优先级更高
【RS485_Modbus.c】
(L467)
//如果主机在发送阶段收到了数据,说明有上位机在总线上请求数据,此时延后200ms再发送
if(modbusBufIndex > 2)
{
delay_ms(200);
}
(L241)
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x03) ) //读数据处理
{
MODBUS_F03_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x10) ) //写数据处理
{
MODBUS_F10_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xaa) ) //CADC零点校准处理
{
MODBUS_Faa_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xbb) ) //CADC增益校准处理
{
MODBUS_Fbb_Rx(MODBUS_MEM_PT);
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
////////////////////滤波改小为0
void AFE_CurrentProcess(void)
////////////////////函数协议名写错,已改SENE -> SRNE
////////////////////地址1也可进行485升级
/***** 7.14汇总 *****/
**********主从机通信数据相关**********
*****1.MCU_TemperaProcess函数增加对最大最小温度和平均温度的计算
//新建温度的全局变量
int16_t TemperatureAverage; // 平均温度
int16_t TemperatureMax; // 最高温度
int16_t TemperatureMin; // 最低温度
uint16_t TemperatureMaxIndex; // 最高温度序号
uint16_t TemperatureMinIndex; // 最低温度序号
//优化MCU_TemperaProcess函数
//从各协议中简化相关计算:
//替换各协议中最高最低温度的取值,节约代码量
//删除每个协议里的比大小的代码
//替换协议函数内的局部变量名
//在传递该函数的代码增加赋值
//有的函数没有加can_protect_byte的相关赋值,已补全
//统一把报警值改为总数据值
//把所有单传温度值的通信改为平均温度
//485类协议增加若不是地址1就只生成报警保护值的代码
*****2.总数据的获取:
//bmsMem.can_cur电流的单位统一0.01A
//电流输出时再根据协议要求变化
int16_t tempCur; //总电流
tempCur = (int16_t)(canMem[0].cur/10); //单位0.1
//电流温度,主机获取的汇总数据可能会很大,改为int32_t类型
int32_t cur;
int32_t temp;
canMem[1].cur = (int32_t) bmsMem.can_cur;
canMem[1].temp = (int32_t) bmsMem.can_temp;
//MODBUS_MASTER_Tx函数收集数据的代码补全
//数据赋值就放在数据获取之后
电流:AFE_CurrentProcess函数
bmsMem.can_cur = (int16_t) (bmsMem.packCurrent/10); //sum of all packs,统一数值单位0.01A
温度:MCU_TemperaProcess函数
bmsMem.can_temp = TemperatureAverage; //sum of all packs
SOC、SOHGaugeManage函数
bmsMem.can_soc = bmsMem.soc; //sum of all packs
bmsMem.can_soh = bmsMem.soh; //sum of all packs
**********逆变器通信协议相关**********
*****简化MUST逆变器的波特率问题
//1.删去MUSTflag相关部分
//2.在CAN1_Init中根据协议选择值更改
if(protocol == 7) //MUST协议的波特率是100k
{
CAN_InitStructure.CAN_Prescaler = 40; //36MHz/8/(1+6+2)/5=100kbs
}
else //其他协议是500k
{
CAN_InitStructure.CAN_Prescaler = 8; //36MHz/8/(1+6+2)=500kbs
}
////////////////////协议函数修改
*****1.协议还是原来共31个选择,25个可用,但内容重新梳理一遍,全换成总数据:
//"protect_byte"的4个统一传递总数据
//SOC统一换成平均值:bmsMem.soc -> canMem[0].soc
//SOH统一换成平均值:bmsMem.soh -> canMem[0].soh
//温度统一换成平均值:TemperatureAverage -> canMem[0].temp
//DONNERGY逆变器内电流单位0.01A,已修改
*****协议保护值变成总数据测试
1.除了首航、硕日、日月元,其他都正常变成了总数据传输
2.首航:与早期版本不符,以更早版本替换原有内容
硕日:保护报警共2个16位类型变量,拆分成4个8位类型传输再合并
日月元:保护报警共4个16位类型变量,把用到的部分摘出,化为4个8位类型,再进行相应放回
////////////////////逆变器相关值可通过上位机修改
//增加
【global.h】(L305)
//20230705---------------
uint16_t inverter_chgVolLimit; //通过上位机修改通信给逆变器的限压限流值
uint16_t inverter_dsgVolLimit; //单位0.1V
uint16_t inverter_chgCurLimit;
uint16_t inverter_dsgCurLimit; //单位0.1A
//20230705---------------
【flash.c】(L138/L201)
//20230705
0x40, //逆变器充电电压限制 0x0240
0x02,
0xa0, //逆变器放电电压限制 0x01a0
0x01,
0xe8, //逆变器充电电流限制 0x03e8
0x03,
0xe8, //逆变器放电电流限制 0x03e8
0x03,
//替换
【flash.c】
uint8_t MEMORY_UpdateFlash(uint32_t addr)
//44改为52,一共2处
//22改为26,一共1处
uint8_t FLASH_ReadCheck(uint32_t addr)
//44改为52,一共3处
【ProtocolSwitch_P1/P2/P3/P4.c】
chgCurLimit = 1000 * bmsMem.E2_485Snum; //充电电流限制100.0A,后面做出可配置
dsgCurLimit = 1000 * bmsMem.E2_485Snum; //放电电流限制100.0A,后面做出可配置
chgVolLimit = 576; //充电电压限制单节3.6BMS保护可以设置3.65
dsgVolLimit = 416; //放电电压限制单节2.6BMS保护可以设置2.50
——>
chgCurLimit = bmsMem.inverter_chgCurLimit; //充电电流限制,上位机配置,默认值100A
dsgCurLimit = bmsMem.inverter_dsgCurLimit; //放电电流限制,上位机配置,默认值100A
chgVolLimit = bmsMem.inverter_chgVolLimit; //充电电压限制,上位机配置,默认值57.6V
dsgVolLimit = bmsMem.inverter_dsgVolLimit; //放电电压限制,上位机配置,默认值41.6V
////////////////////0x46->0x4A
////////////////////小电流乱跳,会让充满电时,很快从100->99
//增加对小电流范围1.2,-1.-2的延迟判定
uint8_t DSGcount; //小电流放电计数
uint8_t DSGminiFlag; //小电流放电标志
uint8_t CHGcount;
uint8_t CHGminiFlag;
if(cali.tempCur < (-2)) //判断电池充放电状态
{
bDSGING = 1;
}
else if(cali.tempCur > 2)
{
bCHGING = 1;
}
else if( (cali.tempCur >= (-2)) && (cali.tempCur < 0) ) //小电流放电,需要延迟考虑,只有连续三秒有小电流才会显示
{
if(DSGminiFlag == 0)
{
DSGcount++;
if(DSGcount > 3)
{
DSGminiFlag = 1;
DSGcount = 0;
}
else
{
bmsMem.packCurrent = 0;
}
}
}
else if( (cali.tempCur > 0) && (cali.tempCur <= 2) ) //小电流充电,需要延迟考虑
{
if(CHGminiFlag == 0)
{
CHGcount++;
if(CHGcount > 3)
{
CHGminiFlag = 1;
CHGcount = 0;
}
else
{
bmsMem.packCurrent = 0;
}
}
}
else //若即不充电,也不放电,则电流更新为0
{
bDSGING = 1;
bmsMem.packCurrent = 0;
DSGcount = 0; //小电流与0之间切换,当切回0,重新开始计数
CHGcount = 0;
DSGminiFlag = 0;
DSGminiFlag = 0;
}
////////////////////上位机读取版本号
//总数据存于新建结构体VersionMem
typedef struct
{
uint8_t Hardware[3];
uint8_t Software[4];
uint8_t Screen;
uint8_t Batch_No[10];
}VERSION_MEMORY ;
VERSION_MEMORY VersionMem;
//可先用03报文直接读取结构体,但要注意报文有区分,比如bmsMem.E2_485Addr 55
#define MODBUS_VERSIONMEM_PT (uint8_t *)&VersionMem.Hardware[0]//上位机请求版本号通讯内存开始地址
else if((modbusBuf[0] == 0xAA) && (modbusBuf[1] == 0x03) ) //上位机读取版本号
{
MODBUS_F03_Rx(MODBUS_VERSIONMEM_PT);
}
////////////////////SOC精度改进
1.在开机初始化读取时,把oldsoc也同时赋值,避免每次一上电就要进行一次写入SOC
oldsoc = tmp;
2.bmsMem.soc与oldsoc相差值大于1就会进行EEPROM的写入操作
//SOC write to eeprom
if(bmsMem.soc > oldsoc)
{
// if( (bmsMem.soc - oldsoc) >= 5)
// {
oldsoc = bmsMem.soc;
tempW = bmsMem.soc;
EEPROM_WrMulByte(0,EEPROM_SOC_ADDR,1,&tempW);
// }
}
else if(bmsMem.soc < oldsoc)
{
// if((oldsoc - bmsMem.soc) >= 5 )
// {
oldsoc = bmsMem.soc;
tempW = bmsMem.soc;
EEPROM_WrMulByte(0,EEPROM_SOC_ADDR,1,&tempW);
// }
}
3.bmsMem.soc的显示:0Ah->0% 0.1Ah->1%
bmsMem.soc = bmsMem.rcc/(bmsMem.fcc/100);
//如果实际容量百分比和soc相比至少高了0.1Ahsoc+1
if( ( bmsMem.rcc%(bmsMem.fcc/100) ) /360000 ) //取精度0.1%作为判断标准 360000mAS = 0.1*1000*3600 = 0.1Ah
{
bmsMem.soc += 1;
}
////////////////////硕日协议更新充放电MOS状态
if((bmsMem.bStatus3 & 0x02) !=0) //充电MOS状态
{
statusByte |= 0x0400;
}
else
{
statusByte &= 0xFBFF;
}
if((bmsMem.bStatus3 & 0x01) !=0) //放电MOS状态
{
statusByte |= 0x0800;
}
else
{
statusByte &= 0xF7FF;
}
////////////////////平均温度计算有误,已修改
//计算总数时加上,清空原值
TemperatureAverage = 0;
////////////////////SOC还未到满充时,充电会先跳到99Ah,99%,改成跳到99.1Ah,100%应该会更美观
bmsMem.rcc = bmsMem.fcc - (bmsMem.fcc*9/1000);
////////////////////消除最后一个警告
if(((int)tmp>=0) && (tmp<=100)) //在比较操作中使用类型转换不会改变变量本身的类型
////////////////////上位机写入时间 6.25
//文件夹内增加【rtc.h】
//工程内替换【rtc.c】内代码
//工程头文件路经增加"..\BSP"
//【main.c】增加
(L25)
#include "rtc.h"
#include "soe.h"
(L78)
uf_RTC_Init(); //SOE
RTC_Get();
(L130)
uf_RTC_Update(); //因为上位机的输入而修改时间
RTC_Get(); //SOE,读时间
//【i2c.c】增加/替换
(L19)
#include "soe.h"
(L45)
void uf_I2C1_Init(void){(略)}
//可先用10报文进行写入结构体,但要注意报文有区分,比如bmsMem.E2_485Addr 66
【RS485_Modbus.c】
(L20)
#include "rtc.h"
#include "soe.h"
(L32)
#define Time_MEM_PT (uint8_t *)&calendar_Write.sec
(L268/307)
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x66) ) //上位机写时间
{
MODBUS_F10_Rx(Time_MEM_PT);
}
////////////////////记录功能之在屏幕上的报警记录,同时暂时没有上位机记录 7.20 [V1.25.18.0]
//思路:此前已写的记录功能基本完善,分为“MCU将记录写入EEPROM”、“上位机校准时间”、“上位机写入定时参数”、“上位机通过MCU读取记录”四部分
//现在只添加第一和第二部分,“MCU将记录写入EEPROM”(已完成)、“上位机校准时间”(已完成)
//同时把记录条数改为50条,目前只在屏幕上进行报警记录的查看
//增加屏幕显示最新报警记录的功能;同时会因为序号变动而切换读取;有按钮可以清空所有记录
//PS:IIC可能还会出现BUSY卡死现象,注意多测试,需要的话增加相关退出BUSY的代码
/********** 1.记录和时间功能移植 **********/
//文件夹内增加【SOE.c】【SOE.h】
//工程内添加【SOE.c】
/********** 2.修改记录数量 **********/
if(soe.pc == 0x1C80) //0x1000-0x1C80共50条
{
soe.pc = RECORD_START_ADDR;
}
if(soe.num==50)
{
soe.num = 50;
}
/********** 3.报警记录功能 **********/
【AFE_SH367309.c】
(L20)
#include "soe.h"
(L660)AFE_ProtectProcess()函数
//soe记录判断
soe.bsNew[0] = bmsMem.bStatus1;
soe.bsNew[1] = bmsMem.bStatus2;
soe.bsNew[2] = bmsMem.bStatus3;
soe.bsNew[3] = bmsMem.temperaStatus;
soe.bsNew[4] = bmsMem.balanceStatus;
soe.bsNew[5] = bmsMem.packStatus;
//电压报警备份
if( ((soe.bsOld[0] & 0x43) ==0) && ((soe.bsOld[2] & 0x08) ==0) ) //原来是正常状态
{
if((soe.bsNew[0] & 0x43) != 0) //报警触发
{
soe.bsOld[0] = soe.bsNew[0]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
if((soe.bsNew[2] & 0x08) != 0) //报警触发
{
soe.bsOld[2] = soe.bsNew[2]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
}
else //已经出现报警
{
if((soe.bsNew[0] & 0x43) == 0) //没有报警,上次清除
{
soe.bsOld[0] &= 0XBC; //~0X43
}
if((soe.bsNew[2] & 0x08) == 0) //没有报警,上次清除
{
soe.bsOld[2] &= 0XF7; //~0X08
}
}
//电流报警备份
if( ((soe.bsOld[0] & 0x3c) ==0) && ((soe.bsOld[3] & 0x30) ==0) ) //原来是正常状态
{
if((soe.bsNew[0] & 0x3c) != 0) //报警触发
{
soe.bsOld[0] = soe.bsNew[0]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
if((soe.bsNew[3] & 0x30) != 0) //报警触发
{
soe.bsOld[3] = soe.bsNew[3]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
}
else //已经出现报警
{
if((soe.bsNew[0] & 0x3c) == 0) //没有报警,上次清除
{
soe.bsOld[0] &= 0XC3; //~0X3c
}
if((soe.bsNew[3] & 0x30) == 0) //没有报警,上次清除
{
soe.bsOld[3] &= 0XCF; //~0X30
}
}
//温度报警备份
if( ((soe.bsOld[1] & 0x0f) ==0) && ((soe.bsOld[3] & 0x8f) ==0) ) //原来是正常状态
{
if((soe.bsNew[1] & 0x0f) != 0) //报警触发
{
soe.bsOld[1] = soe.bsNew[1]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
if((soe.bsNew[3] & 0x8f) != 0) //报警触发
{
soe.bsOld[3] = soe.bsNew[3]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
}
else //已经出现报警
{
if((soe.bsNew[1] & 0x0f) == 0) //没有报警,上次清除
{
soe.bsOld[1] &= 0XF0; //~0X0f
}
if((soe.bsNew[3] & 0x8f) == 0) //没有报警,上次清除
{
soe.bsOld[3] &= 0X70; //~0X8f
}
}
//DI急停备份
if( ((soe.bsOld[3] & 0x40) ==0) ) //原来是正常状态
{
if((soe.bsNew[3] & 0x40) != 0) //报警触发
{
soe.bsOld[3] = soe.bsNew[3]; //如果已经执行记录,就不会再执行
soe.bkType = BKTYPE_ALARM;
}
}
else //已经出现报警
{
if((soe.bsNew[3] & 0x40) == 0) //没有报警,上次清除
{
soe.bsOld[3] &= 0XBF; //~0X40
}
}
if(soe.bkType !=0) //当出现情况,进行记录
{
SOE_BkData(soe.bkType); //函数内部清除BKTYPE标记
}
/********** 4.报警记录的屏幕显示(其他) **********/
1.时间显示-00A9【SDWA.c】
#include "rtc.h"
//0x00A9
//A5 5A 0A 82 00 A9 year month date week hour min sec
void SDWA_Send_Time(uint16_t addr){(略)}
2.星期数有误,会晚一天,在计算时多给1,此时数字“0”代表周日,数字"1"代表周一,以此类推【rtc.c】
temp2=temp2+day+table_week[month-1]+1;
3.删除报警记录的按钮【SDWA.c】
#include "soe.h"
//直接放延时函数有可能影响其他进程,换成Flag/Count++的方法:
(L787)
if(clearFlag != 0) //删除记录的显示
{
SDWA_Send_VAR(0x0420, 0);
SDWA_Send_VAR(0x0400, 1);
clearFlag++;
if(clearFlag > 3)
{
clearFlag = 0;
SDWA_Send_VAR(0x0400, 0);
}
}
(L1513)
//删除记录按钮
if((sdwaBuf[2]==0x06) && (sdwaBuf[3]==0x83) && (sdwaBuf[4]==0x04) && (sdwaBuf[5]==0x10)) //当按钮按下
{
uint16_t i;
uint16_t pc; //待写入地址
uint8_t adrh,adrl;
uint8_t wrBuf[64];
SDWA_Send_VAR(0x0420, 1);
soe.index = 0; //起始序号0
soe.pc = 0x1000; //起始地址0x1000
soe.num = 0; //起始数量0
wrBuf[0] = (soe.index >> 24) & 0xff ;
wrBuf[1] = (soe.index >> 16) & 0xff ;
wrBuf[2] = (soe.index >> 8) & 0xff ;
wrBuf[3] = (soe.index >> 0) & 0xff ;
wrBuf[4] = (soe.pc >>8)&0XFF ;
wrBuf[5] = soe.pc & 0xff ;
wrBuf[6] = (soe.num >>8)&0XFF ;
wrBuf[7] = soe.num & 0xff ;
EEPROM_WrMulByte(0x08,0x00,8,wrBuf);
delay_ms(10);
pc = 0x1000;
for(i=0;i<52;i++)
{
wrBuf[i] = 0xff;
}
for(i=0;i<50;i++)
{
adrh = (pc>>8) & 0xff;
adrl = pc & 0xff;
EEPROM_WrMulByte(adrh,adrl,52,wrBuf);
delay_ms(10);
pc += 0x40;
}
read_index = 0;
clearFlag = 1;
}
/********** 5.报警记录的屏幕显示(内容) **********/
//在uf_I2C1_Init函数中有对于当前记录序号的读取函数,注意soe.pc和soe.index指向[下一个]可写地址和即将给与的序号
1.显示当页的记录
uint8_t i;
uint8_t adrh,adrl;
uint32_t ee_index[3];
uint16_t ee_pc[3];
//报警记录序号,初始默认0,1,2
ee_index[0] = read_index;
ee_pc[0] = 0x1000 + 0x0040 * ee_index[0];
ee_index[1] = read_index+1;
ee_pc[1] = 0x1000 + 0x0040 * ee_index[1];
ee_index[2] = read_index+2;
ee_pc[2] = 0x1000 + 0x0040 * ee_index[2];
USART_SendVarToSDWA(0x0701, ee_index[0]+1);
USART_SendVarToSDWA(0x0702, ee_index[1]+1);
USART_SendVarToSDWA(0x0703, ee_index[2]+1);
//显示序号对应的报警记录
if(read_index < soe.num)//为减少读写EEPROM次数,只当序号在soe.num范围内时可用
{
for(i=0;i<3;i++)
{
adrh = (ee_pc[i]>>8) & 0xff;
adrl = ee_pc[i] & 0xff;
EEPROM_RdMulByte(adrh,adrl,52,recordBuf);
delay_ms(10);
if(recordBuf[0]==0xff && recordBuf[1]==0xff && recordBuf[2]==0xff && recordBuf[3]==0xff)//序号数据全是0xff,表示无数据
{
SDWA_Send_VAR(0x0705 + 0x0001*i,1);
}
else
{
SDWA_Send_VAR(0x0705 + 0x0001*i,0);
SDWA_Send_RecordTime(0x0710 + 0x0100*i);
SDWA_Send_Record(0x0790 + 0x0100*i);
}
}
}
else
{
for(i=0;i<3;i++)
{
SDWA_Send_VAR(0x0705 + 0x0001*i,1);
}
}
2.发送报警记录时间的报文:
void SDWA_Send_RecordTime(uint16_t addr){(略)}
3.发送报警记录内容的报文:
void SDWA_Send_Record(uint16_t addr){(略)}
4.报警记录个数的显示:
USART_SendVarToSDWA(0x0708, soe.num ); //记录个数的显示
5.报警记录增加后,增加刷新SDWA的函数
SDWA_Init();
6.屏幕中之前写入的会被保留,增加填充空白的函数
void SDWA_Send_Blank(uint16_t addr){(略)}
////////////////////当同类型报警先后出现,不会再记录后一个报警,已修改7.25 [V1.25.18.1]
//将同类型的报警拆分开更细
////////////////////报警记录功能去掉序号51,始终显示1~50 [V1.25.18.2]
//SDWA_Init函数中:
if(ee_index[2] != 50)
{
SDWA_Send_VAR(0x0709, 0);
USART_SendVarToSDWA(0x0703, ee_index[2]+1);
}
else
{
SDWA_Send_VAR(0x0709, 1);
}
////////////////////屏幕报警显示与跳转分开 [V1.25.18.3]
1.把原来的SDWA_JumpToAlarm函数拆分成
SDWA_ShowAlarm函数和SDWA_JumpToAlarm函数
2.将SDWA_ShowAlarm函数用到SDWA_UpdateData函数中:
if(bAlarmFlag == 0) //无屏幕上的报警
{
if( (bmsMem.temperaStatus & 0x40) !=0) //急停
{
SDWA_Send_VAR(0x0100, 5); //“Emergency”
}
else
{
if(bCHGING ==1) //充电状态
{
//SDWA_Send_Charging(0x0100);
SDWA_Send_VAR(0x0100, 0); //“Charge”
}
else //放电状态
{
//SDWA_Send_Discharging(0x0100);
SDWA_Send_VAR(0x0100, 1); //“Discharge”
}
}
SDWA_ClearAlarm();
}
else //出现了屏幕上的报警
{
//SDWA_Send_Fault(0x0100);
SDWA_Send_VAR(0x0100, 2); //“Fault”
if(bAlarmFlagOld ==0)
{
bAlarmFlagOld = 1;
SDWA_JumpToAlarm();
}
SDWA_ShowAlarm();
}
////////////////////屏幕修改总压值 [V1.25.18.4]
1.接收屏幕报文->把数据赋值给变量:
//修改逆变器充电限压:A5 5A 06 83 13 10 01 dataH dataL
if((sdwaBuf[2]==0x06) && (sdwaBuf[3]==0x83) && (sdwaBuf[4]==0x13) && (sdwaBuf[5]==0x10))
{
uint16_t tmp;
tmp = sdwaBuf[7]<<8 | sdwaBuf[8];
bmsMem.inverter_chgVolLimit = tmp;
}
2.在屏幕更新时显示值:
USART_SendVarToSDWA(0x1310, bmsMem.inverter_chgVolLimit );
(在初始化函数中也加入显示:)
USART_SendVarToSDWA(0x1310, bmsMem.inverter_chgVolLimit );
USART_SendVarToSDWA(0x0708, soe.num ); //记录个数的显示
3.修改的值保存在Flash内:
PSbmsMem.ee_sconf1~inverter_dsgCurLimit,是上位机写入配置参数的52位数据,皆会保存在Flash内
只有这之外的数据,才会专门存入EEPROM中,所以该值选择存入Flash中
#include "AFE_SH367309.h"
//bmsMem中数据更新到FLASH A区和B区和AFE EEPORM
if(MEMORY_UpdateFlash(FLASH_DATA_A_BASE) ==0 && MEMORY_UpdateFlash(FLASH_DATA_B_BASE) ==0)
{
staPack.bits.flashUpdate= 0;
if(AFE_UpdateConfig() ==0)
{
staPack.bits.eepromUpdate = 0;
}
else
{
staPack.bits.eepromUpdate = 1;
}
}
else
{
staPack.bits.flashUpdate= 1;
}
bmsMem.packStatus = staPack.halfword;
////////////////////温差保护没有让MOS停下,是因为AFE_Ctrl里少了这个值 [V1.25.18.5]
AFE_Ctrl函数:
if((bmsMem.temperaStatus & 0xBf) != 0) //BIT0-5.7
DIDO_Ctrl函数:
if((bmsMem.temperaStatus & 0xBf) == 0) //不能影响BIT0-5功能的运作
////////////////////修改协议Victrion [V1.25.18.6]
Victrion协议内代码取消注释——协议书中标明但未落实的内容,并修改使其适配现在的变量。
void CAN_Protocol_Victrion(void){(略)}
////////////////////修改通信协议:总容量的传输,因容量可写入修改,故协议传输也要改为变量 [V1.25.18.7]
【global.h】
extern uint8_t capacity_Ah;
【GasGauge.c】
(L28)
uint8_t capacity_Ah; //单板容量,用于计算总容量传给上位机 单位1Ah
(L45)
if(tmpRd>0 && tmpRd<255) //如果之前写过容量值,就按照之前的值显示,否则显示100Ah
{
bmsMem.fcc = 3600 * 1000*tmpRd;
capacity_Ah = tmpRd;
}
else
{
bmsMem.fcc = 3600 * 100000; //系统满充容量暂定100AH = 100,000mAH = 360,000,000mAS
capacity_Ah = 100;
tmpRd = 100;
EEPROM_WrMulByte(7,1,1,&tmpRd);
}
(L146)
if(bmsMem.write_Capacity !=0)
{
EEPROM_WrMulByte(7,1,1,&bmsMem.write_Capacity);
delay_ms(5);
capacity_Ah = bmsMem.write_Capacity;
bmsMem.fcc = 3600 * 1000*bmsMem.write_Capacity;
bmsMem.write_Capacity = 0;
}
//协议中统一修改:
totalCapacity = 10000 * bmsMem.E2_485Snum; -> totalCapacity = 100 * capacity_Ah * bmsMem.E2_485Snum; //单位0.01Ah
totalCapacity = 100 * bmsMem.E2_485Snum; -> totalCapacity = capacity_Ah * bmsMem.E2_485Snum; //单位1Ah
////////////////////修改通信协议:上位机修改的逆变器限流值,只代表该单板本身的,传输值仍然要乘以个数 [V1.25.18.8]
chgCurLimit = bmsMem.inverter_chgCurLimit; //充电电流限制,上位机配置,默认值100A
dsgCurLimit = bmsMem.inverter_dsgCurLimit; //放电电流限制,上位机配置,默认值100A
——>
chgCurLimit = bmsMem.inverter_chgCurLimit * bmsMem.E2_485Snum; //充电电流限制,上位机配置,默认值100A
dsgCurLimit = bmsMem.inverter_dsgCurLimit * bmsMem.E2_485Snum; //放电电流限制,上位机配置,默认值100A
////////////////////平均SOH相关 [V1.25.18.9]
1.canMem[0]汇总信息也有soh,只是不用传给逆变器
2.通信协议:SOH固定99,不是平均值
////////////////////Modbus主从机传输数据汇总的canMem变量增大范围 [V1.25.18.10]
uint8_t soc;
uint8_t soh;
——>
uint16_t soc;
uint16_t soh;
////////////////////Modbus传输数据有出现过卡死问题,将初始化函数的值更多些 [V1.25.18.11]
//注意,SOC清零的话会影响逆变器正常工作,所以要注释掉
void MODBUS_Init(void)
{
modbusCurSta = 0; //主机当前采集状态
modbusCurDev = 2; //主机当前采集设备,从从机2开始
modbusCurRevdFlag = 0;//
modbusCurSlaveNum = 0;
canMem[0].protect_byte1 = 0;
canMem[0].protect_byte2 = 0;
canMem[0].alarm_byte1 = 0;
canMem[0].alarm_byte2 = 0;
canMem[0].cur = 0;
canMem[0].temp = 0;
// canMem[0].soc = 0;
// canMem[0].soh = 0;
modbusBufIndex = 0;
modbusF03RxFlg = 0;
modbusF10RxFlg = 0;
modbusFaaRxFlg = 0;
modbusFbbRxFlg = 0;
modbusMoniCount = MODBUS_MON_CNT;
uf_UART_Init(9600);
}
////////////////////记录条数会超过50;新记录不覆盖旧的 [V1.25.18.12]
//总个数计算修改(Total)
if(soe.num >= 50)
{
soe.num = 50;
}
//新增最新报警所在显示(Latest)
////////////////////充电状态的小电流延时,没有显示充电状态,已修改 [V1.25.18.13]
//AFE_CurrentProcess()函数
bDSGING = 1;
bCHGING = 1;
////////////////////通信时有符号类型的浮动会让汇总的数据或有出错,在主机汇总数据时增加限额+/-500A [V1.25.18.14]
////////////////////数据转换问题 int16->int32 [V1.25.18.15]
//问题根源:收到从机数据后,未能正确转换类型
原应该 -1.06A = -(006A) = 1111 1111 1111 1111 1111 1111 1001 0110
现在传654.30A = 0000FF96 = 0000 0000 0000 0000 1111 1111 1001 0110
////////////////////Growatt协议更新,适配手头测试的Growatt逆变器 [V1.25.18.16]
////////////////////屏幕暗了后,主动返回第一页(中显版) [V0.0.0.17]
1.MCU检测屏幕亮度SDWA_DetectBright()
2.屏幕返回变暗的对应数据,MCU进行页数的跳转
//息屏再打开,跳转首页
//屏幕亮度20: A5 5A 04 81 01 01 20
//跳转设置界面指令: A5 5A 04 80 03 00 01
if((sdwaBuf[2]==0x04) && (sdwaBuf[3]==0x81) && (sdwaBuf[6]==0x20))
{
USART_SendData(USART2, 0xA5);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x5A);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x04);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x80);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x03);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x00);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x01);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
////////////////////修改急停对应英文"Emergency"->"RPSD_Activated" [V0.0.0.18]
if( ((temperaStatus & 0x40) !=0) ) //急停 RPSD_Activated
{
sendcount +=15;
*p = 'R'; p++;
*p = 'P'; p++;
*p = 'S'; p++;
*p = 'D'; p++;
*p = '_'; p++;
*p = 'A'; p++;
*p = 'c'; p++;
*p = 't'; p++;
*p = 'i'; p++;
*p = 'v'; p++;
*p = 'a'; p++;
*p = 't'; p++;
*p = 'e'; p++;
*p = 'd'; p++;
*p = ' '; p++;
}
**9.5**
////////////////////屏幕亮度检测,应不会导致执行SDWA_Init()函数,已修改 [0.0.18.19]
////////////////////屏幕写入增多,加一层判断以减少写入协议选择的次数
【SDWA.c】
uint8_t protocol_Index;
protocol_Index = protocol;
protocol -> protocol_Index
if(protocol != protocol_Index)
{
protocol = protocol_Index;
EEPROM_WrMulByte(7,0,1,&protocol);
delay_ms(5);
}
////////////////////SOC满电,不会轻易掉到99.1Ah
//去掉=号
////////////////////默认值修改105A [0.0.18.20]
** 9.25逐步增加排查BUG **
【1】
////////////////////添加第5页的协议选择勾选相关 No.34~42
void SDWA_Init(void)
注意修改中间的判断条件,增加若干项
SDWA_DispProcotol(0x03,0x06,0x00);
void SDWA_RecvData(void)
增加若干项
else if((sdwaBuf[2]==0x06) && (sdwaBuf[3]==0x83) && (sdwaBuf[4]==0x03) && (sdwaBuf[5]==0x16))
{
if(sdwaBuf[8]==0x01)
{
if(protocol !=0x07)
protocol = 0x07;
}
else if(sdwaBuf[8]==0x00)
{
if(protocol ==0x07)
protocol = 0x00;
}
}
////////////////////新增协议 [0.29.0.0]
17.SMK 485
32.COSUPER 485 SRNE
33.Sunway 485 SMK
30.Amensolar CAN Megarevo
新建数据处理函数,同时加上额外定义
void MOD_Protocol_COSUPER(void){}
extern void MOD_Protocol_COSUPER(void);
void CAN_UpdateData(void)
增加若干项
else if(protocol_Index == 32){}
void Protocol_UpdateData(void)和void MODBUS1_IT_TIMUpdate(void)
增加若干项
else if(protocol_Index == 32){}
【2】
////////////////////初始化时,从机数量置0,主从机数量也应该置1
【RS485_Modbus.c】(L461)
bmsMem.E2_485Snum = 1;
////////////////////屏幕没有的报警也会在主页显示“Fault” [0.0.18.21]
////////////////////MOS关闭还有2A以上电流,启动DO关断 [0.0.18.22]
平常继电器关闭,
当放电MOS关闭,仍存在放电电流,说明放电MOS可能损坏,需开启继电器,在外部进行电路的断开
此时若电流处在放电或者待机的值,无需开启继电器
当充电MOS关闭,仍存在充电电流,说明充电MOS可能损坏,需开启继电器,在外部进行电路的断开
此时若电流处在放电或者待机的值,无需开启继电器
*报警灯亮,但不跳转屏幕
*需要2bit标志位,表示充电MOS故障、放电MOS故障[bmsMem.bStatus2的bit7.8]
*传给上位机,上位机显示具体报警内容
*报警记录C-MOS_Fault、D-MOS_Fault
//把所有的DIDO->DI,DI_CTRL->DI
//DO对应PB14,使能函数为DO_On()和DO_Off()
//标志位用的是bmsMem.bStatus2的bit6和bit7,因输入参数bmsMem.packCurrent是1s计算一次,故检测执行代码一起直接放在AFE_ProtectProcess()中执行
bmsMem.bStatus2 = temp[1] & 0x0f; //bit4~7用在其他地方
//MOS故障的报警记录显示
【3】
////////////////////充电过压不报警 [0.0.18.23]
(当处于充电状态,)当SOC大于99%,
此时触发过压报警,不会跳转屏幕,也不会亮报警灯
*正常98 ->触发过压->灯亮+跳转+显示过压 ->其他报警->灯亮+跳转+显示过压
*正常98 ->其他报警->灯亮+跳转+不显示过压 ->触发过压->灯亮+跳转+显示过压
*正常100 ->其他报警->灯亮+跳转+不显示过压 ->触发过压->灯亮+不跳+不显示过压
*正常100 ->触发过压->不亮+不跳+不显示过压 ->其他报警->灯亮+跳转+不显示过压
//报警灯和屏幕跳转相关进行SOC判断
【4】
////////////////////待机状态Nomal [0.0.18.24]
//将待机状态认为是独立于充放电状态的值
【AFE_SH367309.c】
//在电流值计算后,若小于100mA且大于-100mA,就算待机,充放电状态不会因此置0
【SDWA】
//只影响屏幕显示,排在充放电状态前
【GasGauge.c】
//但是少于100mA的电流仍不会计入容量减少中
SDWA_Send_VAR(0x0100, 6);
////////////////////古顶与硕日分开进行数据更新,古顶要适配8串也适配16串 [0.0.18.25]
////////////////////485协议的容量值改为变量 [0.0.18.26]
////////////////////DO控制的脱扣器,一旦给高电平就会启动,且不会自行返回 [0.0.18.27]
//增加对-2A~2A之间的DO动作
//增加延时判断变量DODSGcount和DOCHGcount
////////////////////[0.0.18.26]的后续优化 [0.0.18.28]
1.485协议内的容量
//bmsMem.fcc/3600*bmsMem.E2_485Snum/10 -> 100*capacity_Ah*bmsMem.E2_485Snum
//bmsMem.fcc/3600*bmsMem.E2_485Snum -> 1000*capacity_Ah*bmsMem.E2_485Snum
2.并机个数
//(日月元协议内)固定1 -> bmsMem.E2_485Snum
3.
//统一整改电芯个数:
//删去变量cellNum的定义,(GaugeManage()函数中)替换成bmsMem.ucCellNum
//(COSUPER协议内)bmsMem.ee_sconf1&0x0f -> bmsMem.ucCellNum
//(日月元协议内+全局范围的串数相关值)固定16 -> bmsMem.ucCellNum
for(i=0;i<16;i++) -> for(i=0;i<bmsMem.ucCellNum;i++)
4.修改命名:
Protocol_UpdateData() -> MODBUS1_UpdateData()
把CAN_UpdateData在主程序中朝后排,与MODBUS1_UpdateData放一起
5.合并同类项:OUCO和MagerevoSunsynk和Deye
6.命名Victrion->Victron
7.补上485协议17.SMK的输出
////////////////////屏幕输入参数充电限压,增加写入的上下限54~58V,超出则显示原值 [0.0.18.29]
////////////////////增加协议Afore,因逆变器通讯速率要求,特改为0.5s一个 [0.30.0.0]
////////////////////SDWn屏幕相关-地址上限0x03ff、息屏跳转不同 [0.0.0.0.n]
注意:
·屏幕息屏跳转需要主程序和全局变量共同完成
·无实际报警记录那页,会先显示上次数据再刷新掉,需要把该页也纳入执行函数:if(read_index < soe.num+3)
·因SDWn的特性,报警清除成功图标的显示,会因为图标不可透明,需要用其他方式刷新掉:SDWA_Init();
////////////////////容量变量限值变大255->65535 [0.0.18.30]
1.上位机写入capacity
//去掉crc8校验,扩大write_Capacity为u16
//增大capacity_Ah
//初始化/写入时,存放到EEPROM地址的执行扩大成2位(7,1,2)
//收到要写入的容量后,判断是小于1000才会写入,否则置0
//根据bmsMem.fcc = 3600 * 1000 * capacity_Ah,而bmsMem.fcc是u32的,0xFFFFFFFF对应的最大容量约1,193.04Ah,设置上位机上限为1000Ah
2.容量相关的应用过程
////////////////////Pylon和盛能杰的总容量是uint32_t类型,输出应占4Byte,已修改
////////////////////增加GSSTES精石逆变器的协议No.36
////////////////////待机状态相关优化 [0.0.18.31]
//注释统一改为Standby
//待机状态时,电流值存在。因干扰值过多不参与容量计算,但参与电流校准
【AFE_SH367309.c】
//在电流值计算后,若小于100mA且大于-100mA,就算待机(屏幕最小显示0.1A)
//充放电状态此时置0
【GasGauge.c】
//改动AFE_CurrentProcess()函数
//少于100mA的电流不会计入容量计算中
//电流积分法和判断满充满放,都不需要额外判断充放电状态
//满充跳转:100%->99.1% 改为 100%->99.5% 更合理
//满放跳转:0%->0.9% 改为 0%->0.5%
////////////////////新版本号 [0.0.18.32]
硬件显示5.0.G
发货的软件显示类似:V1.24.A0.0
测试/软件内部的程序,软件版本号显示:V0.0.0.0
(屏幕分类暂时隐藏,不进行区分)
////////////////////CAN协议的数组CAN ID个数从10改为20,个别(Sigineer\Schneider\Pylon)需要
CanTxMsg TxMessage[20];
uint8_t TxMailBox[20];
////////////////////MOS故障与限流板使用有冲突,增加判断条件curLimitFlag=0进行修正 11.7 [0.0.18.33]
////////////////////uf_UART_Init() -> uf_UART1_Init()
////////////////////测试旧上位机能否读删报警记录50条,暂时不加上定时记录 [V0.0.18.34]
1.【global.h】
extern void UART1_ReadRecord(void);
extern void UART1_ClearRecord(void);
2.【RS485_Modbus.c】
MODBUS_IT_TIMUpdate()函数内增加:
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0Xdd) ) //读取记录
{
UART1_ReadRecord();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xee) ) //清除记录
{
UART1_ClearRecord();
}
MODBUS_IQ_Transmit()函数内增加:
else if(modbusFddRxFlg == 1) //按顺序读记录
{
modbusFddRxFlg = 0;
modbusBufIndex = 0;
EEPROM_RdMulByte(modbusBuf[2],modbusBuf[3],52, &modbusBuf[4]);
crc16 = CRC16_Cal(modbusBuf, 56);
modbusBuf[56] = crc16.b8[0];
modbusBuf[57] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 58);
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusFeeRxFlg == 1) //清除记录
{
modbusFeeRxFlg = 0;
modbusBufIndex = 0;
MODBUS_UART_SendMulByte(modbusBuf, 8 );
MODBUS_UART_IT_RX_ENABLE;
}
新加:
uint8_t modbusFddRxFlg; //读取记录 接收正确标记
uint8_t modbusFeeRxFlg; //清除记录 接收正确标记
/****************************************
**** 按顺序读记录 *****
** M: A1 dd 10 00 00 00 CRCL H **
** S: A1 dd 10 00 d0-51 CRCL H **
*****************************************/
void UART1_ReadRecord(void)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) || (modbusBuf[6] == crc16.b8[0]) )
{
modbusFddRxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/****************************************
**** 清除EEPROM内的记录 *****
** M: A2 ee 00 55 00 aa CRCL H **
** S: A2 ee 00 55 00 aa CRCL H **
*****************************************/
void UART1_ClearRecord(void)
{
uint16_t i;
uint16_t pc; //待写入地址
uint8_t adrh,adrl;
uint8_t wrBuf[64];
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) || (modbusBuf[6] == crc16.b8[0]) )
{
soe.index = 0; //起始序号0
soe.pc = 0x1000; //起始地址0x1000
soe.num = 0; //起始数量0
wrBuf[0] = (soe.index >> 24) & 0xff ;
wrBuf[1] = (soe.index >> 16) & 0xff ;
wrBuf[2] = (soe.index >> 8) & 0xff ;
wrBuf[3] = (soe.index >> 0) & 0xff ;
wrBuf[4] = (soe.pc >>8)&0XFF ;
wrBuf[5] = soe.pc & 0xff ;
wrBuf[6] = (soe.num >>8)&0XFF ;
wrBuf[7] = soe.num & 0xff ;
EEPROM_WrMulByte(0x08,0x00,8,wrBuf);
delay_ms(10);
pc = 0x1000;
for(i=0;i<52;i++)
{
wrBuf[i] = 0xff;
}
for(i=0;i<50;i++)
{
adrh = (pc>>8) & 0xff;
adrl = pc & 0xff;
EEPROM_WrMulByte(adrh,adrl,52,wrBuf);
delay_ms(10);
pc += 0x40;
}
EEPROM_RdMulByte(0x08,0x04,4,&modbusBuf[2]); //当前序号
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
modbusFeeRxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
////////////////////上位机修改Soc [V0.0.18.35]
【global.h】
//20231109---------------
uint8_t write_Soc; //通过上位机改变板子的通信地址
uint8_t soc_crc;
//20231109---------------
【RS485_Modbus.c】
0x4A -> 0x4B
【GasGauge.c】
if(bmsMem.write_Soc !=0)
{
EEPROM_WrMulByte(0,EEPROM_SOC_ADDR,1,&bmsMem.write_Soc);
delay_ms(5);
bmsMem.soc = bmsMem.write_Soc;
bmsMem.rcc = (bmsMem.fcc/100) * bmsMem.soc;
bmsMem.write_Soc = 0;
}
////////////////////电芯个数关系到最大最小电压的判定,放到电压检测函数AFE_VoltageProcess()中
bmsMem.ucCellNum = bmsMem.ee_sconf1 & 0x0F;
if( bmsMem.ucCellNum < 5 ) //其他代表16串,一般是0000
{
bmsMem.ucCellNum = 16;
}
////////////////////只有4路环境温度进行平均温度、最高最低温度的计算 [Vx.x.18.36]
////////////////////后续去掉温差这一功能,temperatStatus.bit7空置预留 [Vx.x.18.37]
////////////////////和维克托逆变器实体对过后,修改协议内容 [Vx.x.18.38]
////////////////////改动轮询总数据的报警值流程 [Vx.x.18.39]
总数据的报警值从协议的改为传输bStatus1,2,3和temperaStatus
【global.h】改名:
BMS_MEMORY结构体:bmsMem.can_protect_byte1 -> bmsMem.can_status_byte1
CAN_MEMORY结构体:canMem[i].protect_byte1 -> canMem[i].status_byte1
【AFE_SH367309.c】
AFE_ProtectProcess()函数增加:
//sum of all packs
bmsMem.can_status_byte1 = bmsMem.bStatus1;
bmsMem.can_status_byte2 = bmsMem.bStatus2;
bmsMem.can_status_byte3 = bmsMem.bStatus3;
bmsMem.can_status_byte4 = bmsMem.temperaStatus; //温度报警判断已在此之前执行
【RS485_Modbus.c】和【ProtocolSwitch_P1/2/3/4/5.c】
原来:bmsMem.bStatus1 -> 协议函数protectByte1 -> bmsMem.can_protect_byte1 -> canMem[i].protect_byte1 -> 协议函数输出
现在:bmsMem.bStatus1 -> bmsMem.can_status_byte1 -> canMem[i].status_byte1 -> 协议函数protectByte1 -> 协议函数输出
////////////////////IIC通信优化 [Vx.x.18.40]
////////////////////小电流滤波值调到3,使3以下的延迟显示 [Vx.x.18.41]
////////////////////温度电阻表一直未更新,已修改 [Vx.x.18.42]
////////////////////单板使用时,发给逆变器的电流数据一直会清空,有问题 [Vx.x.18.43]
//将数据清空操作改为赋值主机数据
////////////////////Deye协议,增加强充标志的输出 [Vx.x.18.44]
//判断条件按客户给的Pylon协议(很相似),以5%和10%为准
////////////////////网口1的新上位机 [V0.0.19.00]
//读数据功能为了和逆变器区分,改为0x33
【RS485_Modbus_Inverter.c】
//增加
(L5)
#include "rtc.h"
#include "soe.h"
(L14)
#define MODBUS1_MEM_PT (uint8_t *)&bmsMem.vCell[0] //上位机通讯内存开始地址
#define MODBUS1_VersionMEM_PT (uint8_t *)&VersionMem.Hardware[0]//上位机请求版本号通讯内存开始地址
#define MODBUS1_TimeMEM_PT (uint8_t *)&calendar_WRITE.sec //上位机时间校准通讯内存开始地址
(L29)
uint8_t modbus1F10RxFlg; //写数据接收正确标记
uint8_t modbus1FaaRxFlg; //CADC零点校准数据接收正确标记
uint8_t modbus1FbbRxFlg; //CADC增益校准数据接收正确标记
uint8_t modbus1FddRxFlg; //读取记录 接收正确标记
uint8_t modbus1FeeRxFlg; //清除记录 接收正确标记
(L88)
BYTE2 crc16;
(L97)
else if(modbus1F10RxFlg == 1)
{
modbus1F10RxFlg = 0;
modbus1BufIndex = 0;
//bmsMem中数据更新到FLASH A区和B区和AFE EEPORM
if(MEMORY_UpdateFlash(FLASH_DATA_A_BASE) ==0 && MEMORY_UpdateFlash(FLASH_DATA_B_BASE) ==0)
{
staPack.bits.flashUpdate= 0;
if(AFE_UpdateConfig() ==0)
{
staPack.bits.eepromUpdate = 0;
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
}
else
{
staPack.bits.eepromUpdate = 1;
}
}
else
{
staPack.bits.flashUpdate= 1;
}
bmsMem.packStatus = staPack.halfword;
MODBUS1_UART_IT_RX_ENABLE;
}
else if(modbus1FaaRxFlg == 1)
{
modbus1FaaRxFlg = 0;
modbus1BufIndex = 0;
if(cali.flagZeroCaliFail ==0) //ok
{
// 01 AA 5A A5 03 04 CRCL H
modbus1Buf[0] = bmsMem.E2_485Addr;
modbus1Buf[1] = 0xaa;
modbus1Buf[2] = 0x5a;
modbus1Buf[3] = 0xa5;
modbus1Buf[4] = 0x03;
modbus1Buf[5] = 0x04;
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
}
else //fail
{
// 01 AA 5A A5 05 06 CRCL H
modbus1Buf[0] = bmsMem.E2_485Addr;
modbus1Buf[1] = 0xaa;
modbus1Buf[2] = 0x5a;
modbus1Buf[3] = 0xa5;
modbus1Buf[4] = 0x05;
modbus1Buf[5] = 0x06;
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
}
MODBUS1_UART_IT_RX_ENABLE;
}
else if(modbus1FbbRxFlg == 1)
{
modbus1FbbRxFlg = 0;
modbus1BufIndex = 0;
if(cali.flagGainCaliFail ==0) //ok
{
// 01 AA 5A A5 03 04 CRCL H
modbus1Buf[0] = bmsMem.E2_485Addr;
modbus1Buf[1] = 0xbb;
modbus1Buf[2] = 0x5b;
modbus1Buf[3] = 0xb5;
modbus1Buf[4] = 0x03;
modbus1Buf[5] = 0x04;
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
}
else //fail
{
// 01 AA 5A A5 05 06 CRCL H
modbus1Buf[0] = bmsMem.E2_485Addr;
modbus1Buf[1] = 0xbb;
modbus1Buf[2] = 0x5b;
modbus1Buf[3] = 0xb5;
modbus1Buf[4] = 0x05;
modbus1Buf[5] = 0x06;
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
MODBUS1_UART_SendMulByte(modbus1Buf, 8);
}
MODBUS1_UART_IT_RX_ENABLE;
}
else if(modbus1FddRxFlg == 1) //按顺序读记录
{
modbus1FddRxFlg = 0;
modbus1BufIndex = 0;
EEPROM_RdMulByte(modbus1Buf[2],modbus1Buf[3],52, &modbus1Buf[4]);
crc16 = CRC16_Cal(modbus1Buf, 56);
modbus1Buf[56] = crc16.b8[0];
modbus1Buf[57] = crc16.b8[1];
MODBUS1_UART_SendMulByte(modbus1Buf, 58);
MODBUS1_UART_IT_RX_ENABLE;
}
else if(modbus1FeeRxFlg == 1) //清除记录
{
modbus1FeeRxFlg = 0;
modbus1BufIndex = 0;
MODBUS1_UART_SendMulByte(modbus1Buf, 8 );
MODBUS1_UART_IT_RX_ENABLE;
}
(L245)
else
{
MODBUS1_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x33) ) //上位机读数据处理
{
MODBUS1_F03_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x10) ) //写数据处理
{
MODBUS1_F10_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xaa) ) //CADC零点校准处理
{
MODBUS1_Faa_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xbb) ) //CADC增益校准处理
{
MODBUS1_Fbb_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x55) ) //上位机读取版本号
{
MODBUS1_F03_Rx(MODBUS1_VersionMEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x66) ) //上位机写时间
{
MODBUS1_F10_Rx(MODBUS1_TimeMEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0Xdd) ) //读取记录
{
UART3_ReadRecord();
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xee) ) //清除记录
{
UART3_ClearRecord();
}
(L303)
if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x33) ) //读数据处理
{
MODBUS1_F03_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x10) ) //写数据处理
{
MODBUS1_F10_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xaa) ) //CADC零点校准处理
{
MODBUS1_Faa_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xbb) ) //CADC增益校准处理
{
MODBUS1_Fbb_Rx(MODBUS1_MEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x55) ) //上位机读取版本号
{
MODBUS1_F03_Rx(MODBUS1_VersionMEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0x66) ) //上位机写时间
{
MODBUS1_F10_Rx(MODBUS1_TimeMEM_PT);
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0Xdd) ) //读取记录
{
UART3_ReadRecord();
}
else if((modbus1Buf[0] == bmsMem.E2_485Addr) && (modbus1Buf[1] == 0xee) ) //清除记录
{
UART3_ClearRecord();
}
else
(L360)
/***********************************************
**** MODBUS1 F10 写多个寄存器报文解析 *****
** 01 10 ADRH L 0 LENTH 2LETH DATAn CRCL H **
** 01 10 ADRH L 0 LENTH CRCL CRCH **
************************************************/
//mem point to bmsMem
void MODBUS1_F10_Rx(uint8_t *mem)
{
BYTE2 crc16;
uint8_t adr;
uint8_t len;
len = modbus1Buf[6];
adr = modbus1Buf[3] <<1;
//CRC判断
crc16 = CRC16_Cal(modbus1Buf, len+7);
if( (modbus1Buf[len+8] == crc16.b8[1]) && (modbus1Buf[len+7] == crc16.b8[0]) )
{
memcpy(mem+adr, &modbus1Buf[7], len); //将modbus1Buf数组数据COPY到bmsMem结构体,注意大小端
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
modbus1F10RxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
}
/****************************************
**** MODBUS FAA CADC零点校准 *****
** M: 01 AA 5A A5 01 02 CRCL H **
** S: 01 AA 5A A5 03 04 CRCL H OK **
** S: 01 AA 5A A5 05 06 CRCL H FAIL **
*****************************************/
void MODBUS1_Faa_Rx(uint8_t *mem)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbus1Buf, 6);
if( (modbus1Buf[7] == crc16.b8[1]) || (modbus1Buf[6] == crc16.b8[0]) )
{
cali.cmdZero = 1;
// if( (modbus1Buf[2] == 0x5a) && (modbus1Buf[3] == 0xa5) && (modbus1Buf[4] == 0x01) && (modbus1Buf[5] == 0x02) )
// {
// cali.cmdZero = 1;
// }
// crc16 = CRC16_Cal(modbus1Buf, 6);
// modbus1Buf[6] = crc16.b8[0];
// modbus1Buf[7] = crc16.b8[1];
// modbus1FaaRxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
}
/****************************************
**** MODBUS Fbb CADC增益校准 *****
** M: 01 BB D1 D2 D3 D4 CRCL H **
** S: 01 BB 5B B5 03 04 CRCL H OK **
** S: 01 BB 5B B5 05 06 CRCL H FAIL**
*****************************************/
void MODBUS1_Fbb_Rx(uint8_t *mem)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbus1Buf, 6);
if( (modbus1Buf[7] == crc16.b8[1]) || (modbus1Buf[6] == crc16.b8[0]) )
{
cali.current = modbus1Buf[2]<<24 | modbus1Buf[3]<<16 | modbus1Buf[4]<<8 | modbus1Buf[5];
cali.cmdGain = 1;
// crc16 = CRC16_Cal(modbus1Buf, 6);
// modbus1Buf[6] = crc16.b8[0];
// modbus1Buf[7] = crc16.b8[1];
// modbus1FbbRxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
}
(L486)
modbus1F10RxFlg = 0;
modbus1FaaRxFlg = 0;
modbus1FbbRxFlg = 0;
modbus1FddRxFlg = 0;
modbus1FeeRxFlg = 0;
(L64)
/****************************************
**** 按顺序读记录 *****
** M: A1 dd 10 00 00 00 CRCL H **
** S: A1 dd 10 00 d0-51 CRCL H **
*****************************************/
void UART3_ReadRecord(void)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbus1Buf, 6);
if( (modbus1Buf[7] == crc16.b8[1]) || (modbus1Buf[6] == crc16.b8[0]) )
{
modbus1FddRxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
else
{
MODBUS1_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/****************************************
**** 清除EEPROM内的记录 *****
** M: A2 ee 00 55 00 aa CRCL H **
** S: A2 ee 00 55 00 aa CRCL H **
*****************************************/
void UART3_ClearRecord(void)
{
uint16_t i;
uint16_t pc; //待写入地址
uint8_t adrh,adrl;
uint8_t wrBuf[64];
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbus1Buf, 6);
if( (modbus1Buf[7] == crc16.b8[1]) || (modbus1Buf[6] == crc16.b8[0]) )
{
soe.index = 0; //起始序号0
soe.pc = 0x1000; //起始地址0x1000
soe.num = 0; //起始数量0
wrBuf[0] = (soe.index >> 24) & 0xff ;
wrBuf[1] = (soe.index >> 16) & 0xff ;
wrBuf[2] = (soe.index >> 8) & 0xff ;
wrBuf[3] = (soe.index >> 0) & 0xff ;
wrBuf[4] = (soe.pc >>8)&0XFF ;
wrBuf[5] = soe.pc & 0xff ;
wrBuf[6] = (soe.num >>8)&0XFF ;
wrBuf[7] = soe.num & 0xff ;
EEPROM_WrMulByte(0x08,0x00,8,wrBuf);
delay_ms(10);
pc = 0x1000;
for(i=0;i<52;i++)
{
wrBuf[i] = 0xff;
}
for(i=0;i<50;i++)
{
adrh = (pc>>8) & 0xff;
adrl = pc & 0xff;
EEPROM_WrMulByte(adrh,adrl,52,wrBuf);
delay_ms(10);
pc += 0x40;
}
EEPROM_RdMulByte(0x08,0x04,4,&modbus1Buf[2]); //当前序号
crc16 = CRC16_Cal(modbus1Buf, 6);
modbus1Buf[6] = crc16.b8[0];
modbus1Buf[7] = crc16.b8[1];
modbus1FeeRxFlg = 1;
MODBUS1_UART_IT_RX_DISABLE;
}
else
{
MODBUS1_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
(L583)
//一定时间没有接收到数据,初始化MODBUS1通讯
void MODBUS1_TIM_Moni(void)
{
modbus1MoniCount--;
if(modbus1MoniCount == 0)
{
MODBUS1_Init();
}
}
【global.h】//增加
(L643)
extern void UART3_ReadRecord(void);
extern void UART3_ClearRecord(void);
(L645)
extern void MODBUS1_F10_Rx(uint8_t *mem);
extern void MODBUS1_Faa_Rx(uint8_t *mem);
extern void MODBUS1_Fbb_Rx(uint8_t *mem);
(L58)
extern uint8_t modbus1FaaRxFlg; //CADC零点校准数据接收正确标记
extern uint8_t modbus1FbbRxFlg; //CADC增益校准数据接收正确标记
(L641)
extern void MODBUS1_TIM_Moni(void);
【AFE_SH367309.c】
CALI_CurrentProcess()加上
modbus1FaaRxFlg
modbus1FbbRxFlg
【tim.c】(L103)
MODBUS1_TIM_Moni();
////////////////////高温会让均衡状态消失 [V0.0.19.01]
////////////////////容量可以写1~1000Ah,但发现此时写SOC=100%会失败 [V0.0.19.02]
if(bmsMem.rcc >= bmsMem.fcc+1000000)
//将rcc的类型改为uint32_t,当rcc被减到小于0时,会以极大值显示,故在满充容量上增加1000A的误差值后,以进行是否小于0的判断
1.17
////////////////////原本一直用Ctrl引脚来控制MOS关闭 [V0.0.19.03]
//现在换用写RAM方式进行MCU控制的保护,且只关单边
//急停还是使用CTRL,但可以删去判断自身是否报警这一个了
////////////////////温度保护的触发和当前状态无关 [V0.0.19.04]
//但电流判断是要根据实际状态的
////////////////////轮询卡在地址2通信上的BUG修复 [V0.0.19.05]
////////////////////充放电MOS故障,原来只走DO动作 [V0.0.19.06]
//自身对充放电MOS故障也要行动,在出现故障后,就算之前把MOS关闭的其他原因消失了,也会一直维持控制MOS关闭的状态
//奇怪,如果在置标志位后计数清零,故障又会消失和出现来回跳
//所以要清零也是在报警条件消失后
Program Size: Code=56158 RO-data=3430 RW-data=156 ZI-data=3988
56158+3430+156=59,744≈58.3K
////////////////////地址1才进行逆变器通信
1.19
////////////////////屏幕代码量优化 [V0.0.19.07]
之前
Program Size: Code=56138 RO-data=3430 RW-data=156 ZI-data=3988
之后
Program Size: Code=54958 RO-data=3430 RW-data=156 ZI-data=3988
1.23
////////////////////所有已有协议,在SOC<=10%时触发禁放 [V0.0.19.08]
【已加】
CAN:
Sol-Ark,GoodWe固德威,Growatt古瑞瓦特,Aiswei爱士惟,MUST美世乐,JOHNRAY晶锐鸿,Pylon派能,Sacolar尚科,Luxpower深圳鹏城,Schneider施耐德,Sigineer赛吉纳,Senergy盛能杰,Victron维克托,Megarevo迈格瑞能,Afore艾伏,GSSTES精石
MODBUS:
Voltronic日月元,SMK
【没有相关状态位的】
CAN:SMA艾思玛,Sorotec索瑞德,DONNERGY大能,SOFAR首航,Deye德业
MODBUS:SRNE硕日
思考:MUST的强充强放指令删去;Pylon的强充指令对应特定型号US2000B所以暂不加;精石和Deye是Pylon的缩略版
////////////////////将所有协议改为1s发送1包 [V0.0.19.09]
//艾伏协议放到1s里面而不是0.5s
Program Size: Code=56714 RO-data=3430 RW-data=160 ZI-data=3984
56714+3430+160 = 60304
////////////////////日月元协议回复优化 [V0.0.19.10]
日月元协议和标准485协议不同:
当是日月元协议时,数据长度占2个字节
日月元协议里的协议类型和协议版本,数据为00 00 01 00(前几个地址的数据都补充完整)
通信都是uint8_t类型,通信结构是“先高字节再低字节”
若定义的变量是
2个uint8_t组成2字节,那要先低后高(会在发送前在通信函数里调换)
1个uint16_t组成2字节,那正常输出
2个uint16_t组成4字节,那要先高后低(不受通信函数调换影响,正常先高后低)
////////////////////优化代码 [V0.0.19.11]
Program Size: Code=56714 RO-data=3430 RW-data=160 ZI-data=3984
·温度保护的判断:用最低最高温度而不是4路温度都比较
Program Size: Code=56610 RO-data=3430 RW-data=160 ZI-data=3984
·写封装好的CAN报文生成函数send
所有标准帧CAN协议完成:
扩展帧也包含在函数里 (MUST是扩展帧,只有这1个)
把CAN_SendCount++;放到Send函数里
Program Size: Code=46146 RO-data=4030 RW-data=160 ZI-data=3984
46146+4030+160 = 50336
加上[V0.0.19.10]后的代码量:
Program Size: Code=46258 RO-data=3918 RW-data=160 ZI-data=3984
46258+3918+160 = 50336
////////////////////HpPlus改为Xindun
////////////////////上位机选协议 [V0.0.19.12]
·参考03读数据的报文,但是实际功能是特殊的
·上位机发送协议序号和协议名字符最大个数16
·BMS回复协议序号和协议名表示选择成功;回复空数据表示无法选择/选择失败;长时间不收到回复表示超时
·(目前只在V2.x上位机上可用)
Program Size: Code=46710 RO-data=3466 RW-data=740 ZI-data=3988
46710+3466+740 = 50916
////////////////////增加CAN定时器函数,超时不收到305数据就会初始化 [V0.0.19.13]
Program Size: Code=46782 RO-data=3718 RW-data=740 ZI-data=3988
46782+3718+740 = 51240
////////////////////Sorotec协议有缺,全部协议复查一遍 [V0.0.19.14]
复查完成,只有Sorotec的电压/电流/平均温度没有正常赋值
Program Size: Code=46794 RO-data=3706 RW-data=740 ZI-data=3988
46794+3706+740 = 51240
////////////////////若CRC校验错误,但没有初始化,会导致通信状态异常,无法接收下一帧数据 [V0.0.19.15]
03 10 aa bb的函数里都要加上
Program Size: Code=46826 RO-data=3674 RW-data=740 ZI-data=3988
////////////////////整理单独一个函数用于落实上位机的写地址功能 [V0.0.19.16]
////////////////////MODBUS_Init使用情况整理 [V0.0.19.17]
·if(modbusBufIndex > 2)对应的else部分都删去,以免不必要的初始化
·具体表现应该是插拔时候就会初始化
////////////////////Growatt-485协议 [V0.33.0.0]
Sorotec相同
////////////////////扩充轮询数据 [V0.0.19.18]
·主机要收集从机的最大最小温度,才能知道实际最大温度是多少
·最高最低电压能获得所有数据的之后,就把逆变器协议里的相关赋值都改掉
////////////////////3.4网口的初始化,标志位清零完善
////////////////////轮询出现未连从机报警值出现问题 [V0.0.19.19]
·轮询获得从机报警时,先再次询问,
若仍有回复,那就记录更新并继续轮询下一个
若没有回复,说明可能是电平波动导致,清空当前从机的数据
////////////////////MODBUS的地址大小问题 [V0.0.19.20]
·03和10的求取地址,因为在处理时要<<1,所以实际得到的值应该存放在uint16_t中
·CRC校验,要&&而不是||
////////////////////通信协议里的平均温度,应该传递的是所有机子的平均温度 [V0.0.19.21]
//额外改动了一下Growatt的内容的先后
////////////////////提高轮询速度 [V0.0.19.22]
·主从机轮询时间1s一个实在有点慢,串口的通信波特率9600,其实很快就传完了
发02 03 00 4B 00 0D F4 2A 8BIT
收02 03 1A 00 00 00 03 63 44 FF FA 0B 79 0C 97 0C 87 00 0D 00 0F 0B 82 0B 71 00 02 00 01 1E 50 31BIT
总共就0.0040625s
·采用400ms完成一个从机的轮询,前200ms发,后200ms处理收并回复
(试过100s一个收发的,但是时间太短处理不了数据,200ms的客户测试过也不太行)
·删去轮询函数里,若总线上有数据就延时的动作,3.4网口纯作为主从机通信用
·将初始化的延时等待统一定为10s
·分析TIMER_IsOut函数:
使用格式是
if(TIMER_IsOut(tmrTemp[a],b))
{
tmrTemp[a] = TIMER_Update();
...
}
a:定义了一个数组,用以区分不同的时间间隔
b:时间间隔 = 10ms x b
tmrTemp[a]:到了时间进入判断时,更新为此时的tmrSys值
uint32_t TIMER_IsOut(uint32_t cnt, uint32_t tmr)
{
uint32_t tmp = tmrSys;
tmp = cnt > tmp ? ( (uint32_t)(-1) - cnt + tmp ) : ( tmp-cnt );
if(tmp>=tmr)
return 1;
else
return 0;
}
比较上次进入判断时的tmrSys值和现在的tmrSys值,差值足够大就回复1
若上次的大,说明值很可能溢出了:
(uint32_t)(-1) - cnt + tmp:用0xFFFF-上次的tmrSys值 + 现在的tmrSys值,得到差值
若这次的大(一般都是这次大)
tmp-cnt:得到差值
////////////////////SOC满充校准 逆变器限压值(57.6V)+2A [V0.0.19.23]
////////////////////protocol的额外定义放在global.h中
////////////////////设定一个结构体,专门放置特定测试写入和更改 [V0.0.19.24]
结构体:
功能码:f3->03 f4->10
保存方式:EEPROM,从地址0x0400开始
·要改多少就向EEPROM里写多少
////////////////////主动均衡启动参数,上位机可设
*采用弹窗形式,单独写(毕竟不是所有人都要配置主动均衡)
主动均衡 默认值 变量名
开启电压 50mV act_bal_startV uint8_t
释放电压 30mV act_bal_stopV uint8_t
释放延时 600s act_bal_stopT uint16_t
////////////////////休眠时间和启用标志
默认值
0 代表启用
60min 代表休眠时间60min
////////////////////主动均衡参数值放入实际使用中 [V0.0.19.25]
////////////////////休眠功能 [V0.0.19.26]
不充电不放电状态(2A以下)持续1h,且无任何通信设备接入,启用休眠模式
正常状态下,只有充放电会更新休眠计时;
休眠模式下,出现充电/通信/点亮屏幕/操作屏幕操作,更新休眠计时,退出休眠
或许加上:
执行休眠前,会在5s内检测是否有通信/屏幕操作,若有的话就更新休眠计时,不进入休眠;
(这样的好处是:休眠计时过程中,进行短暂的通信或屏幕操作,不影响休眠计时;
只有在要进入休眠的时候,若正在用通信维持板子正常运行,就不用进入休眠再出来,直接更新休眠时间)
enable标志会控制sleepFlag是否可以成为1
////////////////////ParaMem在EEPROM的地址问题和读取问题 [V0.0.19.27]
·IIC初始化中,读取有点问题,要改正
·ParaMem写入EEPROM中时,地址是uint8类型对应的,原来写的有问题,要改正
因为上位机的报文已经有过了,所以ParaMem不动,只修改EEPROM的存放地址
·EEPROM可用地址有0x0400~0x06FF,每个地址对应一个uint8_t字符
·ParaMem用于MODBUS通信,通信时的每个地址对应一个uint16_t字符,LENTH对应16时的长度,2*LENTH对应8时的长度
////////////////////单体欠压报警时放电MOS关闭 [V0.0.19.28]
·增加手动按钮,按下后把原AFE值写入EEPROM,然后将现AFE欠压报警值赋值500mV,并计时5min,到时间后读出EEPROM的值并写入AFE中
·屏幕地址:按钮按下0291 按钮变色0290 计时文本02A0
·状态位使用bmsMem.balanceStatus的bit5:0x20
(注意协议中带平衡标志位的,赋值改动)
////////////////////状态位中增加部分状态显示 [V0.0.19.29]
限流板启动状态
bmsMem.balanceStatus |= 0x0010;
手动关欠压启动状态
bmsMem.balanceStatus |= 0x0020;
DO继电器状态
bmsMem.temperaStatus |= 0x0080;
(预充状态)
bmsMem.bStatus3 |= 0x0020;
(预充失败)
bmsMem.bStatus2 |= 0x0020;
6.26
////////////////////版本号用0而不是0x00,防止之后超过9的数字写错。只有特殊需要'A'之类的再用0xA0
////////////////////特殊屏幕按钮,按下会将EEPROM的0x0200~0x2000都清空 [V0.0.19.30]
6.28
////////////////////因为更换了采样电阻,增益校准值要改动,原3225,现在改为7000 [V0.0.19.31]
7.1
////////////////////限流板启用前,只关闭充电MOS,放电MOS保持开启以防止出现问题 [V0.0.19.32]
////////////////////重整MOS和限流板的控制逻辑 [V0.0.19.33]
·重点:只让急停和充电相关报警,会关闭限流
休眠时会关闭放电MOS,但保持充电MOS和限流板当前状态,使可以在常态时充电唤醒
·[触发急停]充电MOS、放电MOS、限流板都关闭
·[进入休眠]关闭放电MOS,不影响充电MOS和限流板状态
·[触发充电相关报警]充电MOS关闭,限流板关闭,不影响放电MOS状态
·[触发放电相关报警]放电MOS关闭,不影响充电MOS和限流板状态
·[触发限流]充电MOS关闭,限流板打开,不影响放电MOS状态
·[正常状态]充放MOS打开,限流板关闭
(充电相关报警:单体过压,充电过流,充电高温,充电低温,充电MOS故障)
(放电相关报警:单体欠压,放电过流,短路保护,放电高温,放电低温,放电MOS故障)
·AFE的低功耗模式/仓运模式,会将充放电MOS都关闭,所以不能走这个
·AFE正常工作时,如果AFE检测到过压、充电过流、充电过温,也会把充电MOS关闭,所以休眠保持充电MOS开启,也要考虑到是否满足充电条件
7.4
////////////////////把EEPROM读写的调用代码中的变量值统一#define在一起,方便之后管理 [V0.0.19.34]
·初始化时,若EEPROM里没有数据,赋默认值,但不进行写操作
·若值有明确的范围,则判断条件直接是是否在范围内;若没有,则判断条件是EEPROM里都是0xff;电流校准值特殊,是判断另一个值是不是^的值
·soe.tim和soe.timEn的EEPROM读写相关,都删掉
////////////////////零点校准和增益校准的地址和个数都要变 [V0.0.19.35]
·放在不更新区中
·个数保留4位,前2位有效位,后2位是取异或^
·在按下特殊EEPROM清空按钮后,程序会根据当前(3,0,4)有没有数据来决定是否要搬移零点+增益校准数据到现在程序的对应位置
////////////////////整理EEPROM的地址,重新放置 [V0.0.19.36]
·容量、SOC、协议是可能需要之后改默认值的
·执行逻辑的过程量也很可能是变动值
ParaMem里的值最好要升级即改
[0x0000~0x01FF]——不会因任何动作,而初始化的值
·bmsMem.E2_485Addr 485通讯地址 0x0000 1 位置不变
·Update_Index IAP标志位 0x0001 1 位置不变
·bmsMem.ee_uv AFE欠压保护值 0x0004 1 位置不变
·bmsMem.cadcZero 零点校准 0x0008 4 旧位置(2, 0, 8)
·bmsMem.cadcGain 增益校准 0x000C 4 旧位置(3, 0, 8)
·待增加:BMS生产批次,PACK生产批次
[0x0200~0x03FF]——程序升级不会变动的值,但可以通过特定屏幕的特殊按钮,初始化为程序里想要的初始值
·capacity_Ah 容量Ah 0x0200 2 旧位置(7, 1, 2)
·bmsMem.soc SOC 0x0202 1 旧位置(0,64, 1)
·protocol 协议选择 0x0203 1 旧位置(7, 0, 1)
·待增加:累计容量、循环次数
[0x0400~0x07FF]——程序升级就会刷新此处存放数据,一般都是过程量
·paraMem.act_bal_… 主动均衡参数 0x0400 4 位置不变
·paraMem.sleep_… 定时休眠参数 0x0420 2 位置不变
·在Flash里也找个地方存放该值,(但因为需要单独参数读写,所以最好放在EEPROM里)
程序烧录后,uf_FLASH_Init()函数读EEPROM值和Flash新烧录的值比对,若不同就将Flash里的值写入EEPROM中
·待增加:SOH计算系数:当cycCount小于等于_a_次时,SOH保持100%,当cycCount大于_a_且小于等于最大限制_b_时,SOH=99-(99-c)*(cycCount-a)/(b-a)SOH最低降到_c_%
////////////////////优化Para写操作时的define,不局限在0x04
#define EE_PARA_WRITE adrh,adrl,len
7.8
////////////////////EEPROM存放数据的范围 [V0.0.19.37]
·当初始化读EEPROM的数据时,要检验是否在适用范围内
·当通过通信/屏幕写入数据时,要检验是否在适用范围内
bmsMem.E2_485Addr在if(bmsMem.write_Addr !=0)以外,还要判断是否在可选范围内
bmsMem.soc在if(bmsMem.write_Soc !=0)以外,还要判断是否在可选范围内
·ee_uv的写入地址错了
·清除前的电流增益值的写入写成了读出
·容量的初始化值,不会在第一次读非法值就写入100
////////////////////SOC=0的报文是 [V0.0.19.38]
Adr 10 [00 4a 00 01 02 00 00] crc1 crc2
在程序里特意做补丁,如果是特定的这个报文,直接和bmsMem.write_soc一样的操作,写SOC=0
////////////////////休眠功能,之前都写少了一部分,就是电流更新! [V0.0.19.39]
//存在充电或放电,休眠起始点更新
if((bmsMem.packCurrent <= (-2000)) || (bmsMem.packCurrent >= 2000))
{
if((paraMem.sleep_min_disable & 0x8000) == 0) //启用
{
sleeptimecount=RTC_GetCounter();
sleep_flag = 0;
}
}
////////////////////电量低于10%,触发强充 [V0.0.19.40]
·原触发禁放功能的变量,现在也触发强充(之后可写入时再改分开)
////////////////////增加锦浪协议和Pylon电总协议 [V0.35.0.0]
·锦浪有强充标志,补上
·Pylon电总的强充禁放标志都要补上
////////////////////协议里的温度值应该取canMem[0]. [V0.0.19.41]
·平均温度,应该取canMem[0].temp而不是TemperatureAverage
·最高最低温度,应该取canMem[0].TempMax……
////////////////////协议里的电压值应该取canMem[0]. [V0.0.19.42]
·最高最低电压,应该取canMem[0].VolMax……
////////////////////汇总最高最低温度电压 的序号 的计算,已修正 [V0.0.19.43]
////////////////////当电池放电时,限流状态关闭 [V0.0.19.44]
·经实验,放电时若限流板保持开启,会导致限流板的电感特别热
////////////////////限压+2A程序,限压*100 [V0.0.19.45]
////////////////////Pylon电总协议的回复还没加上,现在已加 [V0.0.19.46]
////////////////////上位机选协议相关-锦浪协议 [V0.0.19.47]
7.15
////////////////////EEPROM特定清除按钮,范围减少只有0x0200-0x03FF [V0.0.19.48]
这部分保存电池的基本信息,包括满充容量、SOC、当前协议、累计容量、循环次数……
在出货前最好清零,为了
////////////////////读旧电流值 [V0.0.19.49]
·初始化获取电流校准值前,没有进行容量、SOC、协议选择的写入
·先获得现在地址的校准值,若是无效值,说明此前没校准过/是旧程序
·再读取旧地址的校准值,若存在,则赋值给新校准值的地址,并把旧地址的电流校准值清空
·赋值
////////////////////当电池SOC在15%以上,电池电压降到50V,并持续2分钟,SOC需重置校准到15%(低于15%不做处理) [0.0.19.50]
当总电压小于等于50V且SOC大于15%Cali_Soc_Flag = 1; 启用延时;若总电压恢复到50V以上或SOC已经小于15%Cali_Soc_flag = 0; 重新计时
延时持续2*60*100个10ms后,说明持续了2分钟,此时若SOC大于15,让bmsMem.soc = 15;
////////////////////AFE写入时,不跳电流 [V0.0.19.51]
·之前在更新AFE时,电流值会跳动,导致有电流值显示,容易影响100%跳99%
·当更新AFE操作执行后,置标志位1,使之后执行的计算赋值电流时值直接=0,再下一秒就按照正常执行
////////////////////Flash_A/B中也存放EEPROM里的ParaMem参数变量 [V0.0.19.52]
·因为修改Flash会把其余位置更新为0xFF,所以Flash相关操作要一起做
初始化时Flash更新到结构体,AFE和para一起做
在写入数据后,AFE和para更新也要一起做
·如果升级程序,会通过更新Flash_A/B的方式更新ParaMem的值
·平时改在ParaMem里的参数,会通过先改Flash值再改EEPROM的值操作
·原来在uf_I2C1_Init()的读默认值操作,和在MODBUS1_F10_Rx()的记录写Para地址并在Config_WritePara()进行烧录EEPROM操作相关,都没用了删掉,变量也删掉
·AFE_UpdateConfig()函数更名为AFE_UpdatePara()
****初始化参数****
////////////////////100A+6.0.0(默认)
////////////////////50mV192us
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [08] [0b] [03] [0a] 50 64 46 ec f6 64 46 ec f6 [f5]
41 37 02 05 41 37 fb fe [69] [69] 08 08 1e [23]
[64] 05 58 02
40 02 a0 01 [e8] [03] [e8] [03]
////////////////////150A+6.0.0
////////////////////50mV192us
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [08] [0b] [03] [0a] 50 64 46 ec f6 64 46 ec f6 [f5]
41 37 02 05 41 37 fb fe [9b] [9b] 08 08 1e [c1]
[96] 05 58 02
40 02 a0 01 [dc] [05] [dc] [05]
////////////////////200A+6.0.P
////////////////////80mV192us
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [18] [1b] [13] [1a] 50 64 46 ec f6 64 46 ec f6 [24]
41 37 02 05 41 37 fb fe [cd] [cd] 08 08 1e [f8]
[c8] 05 58 02
40 02 a0 01 [d0] [07] [d0] [07]
////////////////////休眠默认启用+24*60min [V0.0.19.53]
·休眠最大可设32767min=546h=22d
·24*60=1440=0x05A0
////////////////////满充的总压判定-6V,防止有些性能稍差的电池无法充满 [V0.0.19.54]
8.8
////////////////////重新整理GPIO初始化电平
////////////////////限流关闭逻辑优化 [V0.0.19.55]
·之前控制限流关闭的充电报警,只囊括了MCU本身的充电报警,
AFE的充电过流保护、充电过压保护、充电高温/低温保护等都应当可以关闭限流,在CHG_LIMIT_Ctrl()函数里
if(((bmsMem.bStatus1 & 0x51) != 0) || ((bmsMem.bStatus2 & 0x83) != 0) || ((bmsMem.temperaStatus & 0x15) != 0))
////////////////////更完善的预充逻辑 [V0.0.19.56]
·预充因为短路引起
1.短路报警刚发生时:
关闭所有MOS,启动预充。
2.短路报警持续显示时:
充放MOS保持关闭,预充电路保持开启,注意此时的实时电流。
正常预充过程中,电流会越来越小。当连续4s有超过1.9A的电流,判断应该是真短路,关闭预充电路,并保持关闭充放MOS,显示“预充失败”。
3.短路报警消失后:
充放MOS保持关闭,预充MOS从开启变为关闭,测量负载端电压。
若负载端电压约等于电池电压
预充结束,开启充放MOS
若负载端电压仍低于电池电压(可能性应该较小)
充放MOS保持关闭
启动预充,继续给负载端充电,每持续4s后关闭预充进行1次检测
若检测到,负载端电压约等于电池电压,预充结束,开启充放MOS
若持续10次检测到,负载端电压仍然低于电池电压,充放MSO保持关闭,显示“预充失败”
*8.16补充:预充电流值=电池总压/30欧姆,短路判断在这之上-0.1A即可,4s暂时不改
预充失败放入报警记录,屏幕显示是PCHG_Fail,预充失败会亮报警灯和屏幕显示报警
*8.20补充:短路判断不用电流值算了,现在是:
预充开启后先充1s然后关上,检测负载电压,若低于10V(测试看到的短路情况是5V)说明是真短路,显示预充失败
预充计时变量的赋值优化了下,会在判断启动预充的时候初始化
////////////////////预充状态和预充失败显示 [V0.0.19.57]
·预充状态:bStatus3 bit5
bmsMem.bStatus3 = temp[2] & 0xdb; //bit2、bit5用在其他地方
bmsMem.bStatus3 |= 0x0020; //预充状态开启
·预充MOS状态:bStatus3 bit2 (屏蔽原来AFE的预充MOS状态,放入MCU控制的)
bmsMem.bStatus3 = temp[2] & 0xdb; //bit2、bit5用在其他地方
bmsMem.bStatus3 |= 0x0004; //预充MOS打开
·预充失败:bStatus2 bit5
bmsMem.bStatus2 |= 0x0020; //预充失败
*8.30补充:轮询数据的报警状态判断,要增加预充失败报警
////////////////////屏幕reset按键相关,不需要移动电流校准值了
////////////////////bmsMem限流变量去掉write_
////////////////////packStatus内容修改,删去EEPROM写入失败标志,缩减EEPROM更新标志为1bit
////////////////////增加禁放强充标志的释放值,20% [V0.0.19.58]
·Deye的特殊值:<=5%启动强充>=10%释放,是Deye特有,和标准版分开来
标准版:<10%启动>20%释放
·之前协议里只有启用没有强充标志的释放,现在把强充加上
////////////////////没有禁放标志的协议,也可以通过改动放电限流值来进行禁放 [V0.0.19.59]
·这样所有协议都至少带禁放功能了
////////////////////Pylon电总内容更新:逆变器充放电限压限流 [V0.0.19.60]
////////////////////Pylon CAN协议的最大最小温度/10,单位1K而不是0.1K [V0.0.19.61]
////////////////////在SOC<99时,达到满充过压条件,会跳转屏幕报警页后再校准SOC,已修复 [V0.0.19.62]
·把单体过压的跳转单独拎出来,在满充校准之后进行判定
////////////////////轮询汇总后计算最值时,要进行在线的判断 [V0.0.19.63]
****初始化参数****
【短路改为50mV0us,最大程度保护板子】
////////////////////100A+6.0.0(默认)
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [08] [0b] [00] [0a] 50 64 46 ec f6 64 46 ec f6 [d4]
41 37 02 05 41 37 fb fe [69] [69] 08 08 1e [23]
[64] 05 58 02
40 02 a0 01 [e8] [03] [e8] [03]
////////////////////150A+6.0.0
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [08] [0b] [00] [0a] 50 64 46 ec f6 64 46 ec f6 [d4]
41 37 02 05 41 37 fb fe [9b] [9b] 08 08 1e [c1]
[96] 05 58 02
40 02 a0 01 [dc] [05] [dc] [05]
////////////////////200A+6.0.P
70 0f 6e ee 62 c6 7d 87 af 78 64 c3 [18] [1b] [00] [1a] 50 64 46 ec f6 64 46 ec f6 [f2]
41 37 02 05 41 37 fb fe [cd] [cd] 08 08 1e [f8]
[c8] 05 58 02
40 02 a0 01 [d0] [07] [d0] [07]
9.4
////////////////////在休眠前存在报警,休眠后也会关闭报警灯 [V0.0.19.64]
9.10
////////////////////增加盛弘协议 [V0.36.0.0]
函数、调用、上位机选择
9.11
////////////////////MOS故障判定持续30s [V0.0.19.65]
·只修改MOS故障判定时间30s,故障释放仍然3s
9.18
///////////////////屏幕协议选择的优化 [V0.0.19.66]
9.19
///////////////////清空IAP底层标志位的操作,优化为根据EEPROM的值操作 [V0.0.19.67]
9.24
////////////////////在换协议后,更新CAN波特率 [V0.0.19.68]
9.27
///////////////////删记录之前,判断是否有可删除的记录 [V0.0.19.69]
///////////////////上位机/屏幕删记录后,屏幕也会更新 [V0.0.19.70]
8.12
////////////////////屏幕设置参数,参考美国版屏幕 [V0.0.20.00]
*9.12 短路参数50mV0us配合最新预充;过压保护和过压释放电压只修改电压相关,其他的延时都不变
8.13
////////////////////报警记录上限100条 [V0.0.20.01]
·屏幕上屏蔽101和102的代码还要加上
////////////////////报警记录重新整理,包含单芯电压和温度 [V0.0.20.02]
·屏幕上只显示是什么报警,如果EE_SOE结构体变化,程序里自己改动即可,不担心兼容问题
·上位机通过功能码0xdd求取报警记录:
——发送报文不改动,根据对回复的判断,采用不同的解析方式
上位机: SlaveID DD addrH addrL 00 00 CRCL CRCH
BMS回复:SlaveID DD addrH addrL DATA0~51 CRCL CRCH
·addr=0x1000+k*0x40
50条:0x1000~0x1C40
100条:0x1000~0x28C0
·EEPROM一个扇区是0x40=64个uint8类型大小,
目前已用52个,还剩12个是不够放单芯电压的
重新整理wrBuf[64]
*9.2 存储格式更正,使具体数据可以正常在上位机显示
////////////////////能通过判断兼容之前的报警记录格式 [V0.0.20.03]
////////////////////屏幕时间格式 [V0.0.20.04]
////////////////////计算循环次数 [V0.0.20.05]
·每当充电让rcc增加1Ah,累积容量也增加1Ah
·累计容量超过总容量的90%,增加一次循环次数
////////////////////计算SOH和paraMem里的过程变量 [V0.0.20.06]
·在循环次数<=200次时,保持SOH=100%
·在循环次数>200次时,SOH从99%开始降低,当达到最大6000次时,对应SOH=70%,再下降也不会超过6000次了
·SOH值不需要存储,只需要随时根据当前循环次数和计算过程变量来得出即可
////////////////////上位机读写循环次数、paraMem里的过程变量 [V0.0.20.07]
·循环次数通过采集可以读出来,主要就是写入——干脆直接修改原值算了,就当只能上位机可写,写超过最大值也会改成最大值
·SOH不可写,但是存在并因为各种参数而改动——和packStatus挤一挤,能够采集到,也只有packStatus这里可以挤
·ParaMem的过程变量只要对应好位置即可
////////////////////SN号的使用,和屏幕显示版本号和SN号 [V0.0.20.08]
·版本号共2x9位,再加上一个crc8校验码,存放在EEPROM中
若crc校验出错,则值为0,上位机读出该值不显示,但屏幕正常显示(不好空着)
·屏幕地址:
电池ID 02C0
BMS ID 02CD
硬件版本 02D6
软件版本 02DE
*9.11 SN号不需要0x30+
////////////////////上位机读写SN号 [V0.0.20.09]
·上位机读0x55中的长度增加,将SN号都囊括
若收到的SN号值都是0,就清空不显示
·收到写入的SN号后,在回复时写入EEPROM
·将F10的回复做一个判断,根据功能码来做存储
////////////////////屏幕设置参数增加范围判断 [V0.0.20.10]
·若设置保护值,默认同步修改释放值
·若设置释放值,不能超过保护值
·涉及参数:单体过压和释放、单体欠压和释放、充电低温/高温和释放、放电低温/高温和释放
////////////////////上位机连主机,获取在线信息 [V0.0.20.11]
·上位机向主机间隔请求并机信息,应当是个结构体
·接主机1口,若上位机切换询问地址,主机可能会当成对自己连接的从机信息的求取,故要把对主机请求从机信息的功能码改动,以做区分
////////////////////程序已超过59K,标志消除对应地址变为128K [V0.0.20.12]
////////////////////上位机显示屏幕版本号,仍保持原来的1字节 [V0.0.20.13]
·0xFF=1111 1111
2进制里,最高两位表示型号,01对应035,10对应043,11对应070(或就当特殊种来用,070先只处理前8个位置),00预留暂表示不含屏幕(如果要用也要避开0x00.0x01.0x02.0x03这些)
2进制的剩下6位表示序号Index,范围0~63,显示为1~64。
注意!没写过的时候,显示按04301来,得出的值是【0x80=1000 0000】。而读出0xFF不算数,即070的情况下,序号只能1~63
·版本号和屏幕号的位置不能动,这涉及到上位机的读取,动了就和以前的上位机不兼容
01 56 00 01 00 00 01 41 4b a2
////////////////////参数初始化按钮变更 [V0.0.20.14]
·参数初始化按钮
0x01A4 图标颜色
0x0125->0x01A5 指令地址
////////////////////屏幕写SOC [V0.0.20.15]
·SOC校准
0X0126 SOC输入和显示
////////////////////优化屏幕选协议部分 [V0.0.20.16]
////////////////////屏幕进行电流校准 [V0.0.20.17]
·只显示
0X01A8~01A9 数据变量 pack电流值
·增益系数,可改
0X01A1~01A2 数据变量 增益系数
·按钮
0X012A 变量图标 零点校准
0X012B 增量调节
0X012C 变量图标 增益校准
0X012D 增量调节
////////////////////切换语言 [V0.0.20.18]
·语言切换
0X01A0 变量图标
0X0127 菜单的2个语言的按钮
·报文
A5 5A 06 83 01 27 01 00 00/01[中文/英文]
////////////////////优化屏幕参数初始化参数 [V0.0.20.19]
·短路电流固定50mV0us
·过压和过压释放都只改变自身值,在一起的延迟时间等不影响
【都在前面加上了】
////////////////////盛弘CAN协议
////////////////////休眠时报警灯灭
////////////////////MOS故障的判断改为30s,恢复判断保持3s
////////////////////屏幕选择协议优化
////////////////////清除IAP标志位优化
////////////////////上位机清除记录后,屏幕也刷新显示
////////////////////报警记录100条,屏幕左上角显示最新所在,50->100
9.19
////////////////////一屏多机+按钮分配地址
·注意:因为已经有和上位机的通信逻辑,
若存在上位机的轮询报文或者单独请求从机数据的报文,此时若正在屏幕读取从机数据时
·程序
新增变量1
extern uint8_t sdwa_RdData_Index;//屏幕显示数据的地址,默认是自身addr,只有addr=1可以变化为其他地址
extern uint8_t sdwa_RdRecord_Flg;//屏幕只可以查看自身记录;当记录更新/清空/收到清空指令/进记录页面/上下翻动时,才会读EEPROM更新一次屏幕记录内容
extern uint8_t sdwa_WrAddr; //屏幕通过主机写从机地址的值
extern uint8_t sdwa_WrAddr_Flg; //屏幕通过主机写从机地址的标志 0不在写 1尝试中 2成功 3失败
extern uint8_t assignAddr_State; //按钮分配地址的状态 0未开始 1进行中 2结束
extern uint8_t assignAddr_Step; //自动分配地址当前步骤
新增变量2:(不在global.h中)
uint8_t modbusCurF03RxFlag;//主机收到03回复 接收正确标记
uint8_t modbusCurF10RxFlag;//主机收到10回复 接收正确标记
uint8_t modbusCurStatus; //主机当前执行的功能——每当变化,会初始化收发状态 0:基础轮询 1:屏幕持续读从机 2:上位机持续读从机 3:主机写从机地址 4:自动分配地址
uint8_t sdwa_WrAddr_Failcount; //连续写地址失败的计数
uint8_t assignAddr_Failcount; //连续分配地址失败的计数
uint8_t bAlarmFlagOld_slave; //主机屏幕显示从机报警的跳转
uint8_t assignAddr_Flag; //分配地址完成的显示
#define PIN_ADDR_IN GPIO_Pin_8 //自动分配地址输入脚IO1
#define PIN_ADDR_OUT GPIO_Pin_9 //自动分配地址输出脚IO2
新增函数1
//分配地址
extern void IO2_OUTSet(void); //分配中先置高让下一个变99,再置低变PACK_NUM+1
extern void IO2_OUTReset(void);//正常置低
extern uint8_t IO1_IN(void); //检测输入电平
extern void IO1_TIM_Moni(void);//根据不同的原地址和输入电平,选择要修改的特定地址
extern void MODBUS_Poll_Init(void); //轮询汇总初始化,只有主机数据
extern void MODBUS_MASTER_F03_Rx(void); //处理从机对读指令的回复
extern void MODBUS_MASTER_F10_Rx(void); //处理从机对写指令的回复
extern void MODBUS_MASTER_Polling_Tx(void);//主机轮询获总数据
extern void MODBUS_AutoAssign_Tx(void); //主机自动分配地址
extern void MODBUS_Screen_RdSlave_Tx(void); //主机因屏幕读从机数据
extern void MODBUS_Screen_WrSlaveAddr_Tx(void);//主机因屏幕写从机地址
新增函数2:(不在global.h中)
void SDWA_ShowAlarm_Slave(void) //读取的从机报警数据的显示
////////////////////一屏多显 屏幕地址
0x01A6 PACK页跳转总数据页按钮
0x0270 单板数据回PACK页按钮
////////////////////一屏多机优化,不在线的从机,不可进行点击跳转
////////////////////上位机多显和屏幕多显兼容
·当屏亮,而上位机在通过主机看从机,屏幕查看地址返回原值
·当网口1超过10s不再收到上位机报文,将上位机求取对象地址初始化
////////////////////增加总容量的屏幕可改
地址01AA
////////////////////循环次数重启恢复,需要加上old值来判断写入
9.23
////////////////////全自动分配地址=自动变主从机+自动分配地址
·新增变量1
extern uint8_t assignAddr_relay; //主机开机后延时2s再开始通信,等待地址变化
extern uint8_t assignAddr_485num; //分配地址后的在线个数,若存在从机才进行下发队列标志
extern uint8_t assignAddr_WrIndex_Flg; //主机执行下发队列标志数的标志
uint16_t bmsMem.can_ArrayIndex; // 主机下发队列序号,与自动分配地址相关
uint16_t paraMem.addr_FREE_Flg; //0x11 地址手动控制标志,默认0
·新增变量2
#define PIN_ADDR_RANK GPIO_Pin_7 //自动分配地址输入脚IO3,当输入低电平时,说明自身是从机
uint16_t ADDR_Moni_Count; //自动分配地址前,因为短接脚而该改变自身的地址 每次10ms
uint8_t IO1_INH_Count;
uint8_t IO1_INL_Count;
uint8_t modbusCurSlaveLastAddr; //最后一个在线从机的地址
uint8_t assignAddr_485num; //分配地址后的在线个数,若存在从机(此值>2)才下发队列标志
uint16_t assignAddr_random; //主机完成分配后生成的随机数(PACK_NUM+1~65535),用来识别队列
uint8_t assignAddr_WrIndex_Flg;//主机执行下发队列标志数的标志
·新增函数:
extern uint16_t get_random(void);
extern uint8_t IO3_IN(void); //检测输入电平
extern void ADDR_Rank_Moni(void); //根据IO3的不同电平,确定自身是主机/从机
extern void MODBUS_WrIndex_Tx(void); //主机广播下发队列标志
extern void MODBUS_WrIndex_Rx(void); //从机接收该次分配地址的队列标志
·暂时需要把轮询时间改为1s,后期可以测试下0.4s的效果
·启动情况分为:
【√】1.主机刚启动时,等待2s后启动分配(等待可能的地址变化)
【√】2.主机轮询完,连续2次(约40s)发现有掉线地址,比如地址4在线,但地址3不在线,启动分配
【√】3.主机轮询完,连续6次(约2min)找不到任何从机,启动分配
【√】4.主机已在正常轮询,此时有增加新从机(地址都是2),
若轮询到[分配标志]为0的从机,说明总线上有了没经过分配的从机,在该轮结束后启动分配
【√】5.主机已在正常轮询,此时有增加从其他地方拆来的从机(地址任意但肯定在2~PACK_NUM内)
若轮询到[分配标志]是旧程序中的bmsMem.E2_485Addr的值:2~PACK_NUM,在该轮结束后启动分配
若轮询到[分配标志]和当前主机上次下发的[分配标志]不同的从机,在该轮结束后启动分配
(若是旧程序地址1,会和主机打架总线整个会受影响,而且不会轮询该地址,所以不考虑)
【√】6.原来是从机的BMS,地址刚变成1时,启动分配
比如把前面几个BMS板拆了,比如拆掉前3个后,原地址4会变1,而此时地址5及以后的值不变,需要启动分配
*[分配标志]:轮询数据中新增一个变量,内容是主机分配地址后广播的随机数,范围在PACK_NUM+1~65535(避免和旧程序中的地址值相撞)
////////////////////在换协议后,更新CAN波特率(前面已加)
////////////////////地址变化逻辑
·开机后,会首先默认读出存在EEPROM里的地址
·从机若被分配过地址,会在被分配后存储该地址,若再次上电时保持IO3短接状态,便暂时维持该地址
·主从机都会将队列标志存储,若自身地址被更改,该队列标志清零,否则重启后仍保留上次的队列标志
优点:可以固定搭配后,减少开机后等待时间
////////////////////一屏多机的主机显示从机,满充过压报警不显示
9.30
////////////////////删记录相关x2(前面已加)
////////////////////进入参数设置的密码x2
已经加上的优化
////////////////////屏幕点击恢复出厂参数/修改欠压值时,需要把欠压强制复位也消掉
////////////////////轮询若发现对【在线总个数以外的地址】有回复但是乱码,多次后启动自动分配
////////////////////开机后的在线个数及时更新 [分配地址功能]
////////////////////报警记录个数的显示统一为100及以下
////////////////////报警记录屏幕显示的兼容,改以旧记录的特点为判断条件
要加上的优化
////////////////////
////////////////////
////////////////////
////////////////////
////////////////////
10.8
////////////////////除旭尊外都删去急停 [V0.0.19.71]
·DI执行函数和引脚定义 ——> 删去
·带急停的注释相关 ——> 删去
·temperaStatus & 0x4 ——> 删去
·temperaStatus & 0x7 ——> 改为0x3
·status_byte4 & 0x7 ——> 改为0x3
////////////////////时钟超时做故障但是可以正常使用,把相应影响的部分同步增加修改 [V0.0.19.72]
·导致卡死的函数是RTC_WaitForSynchro(); //等待RTC寄存器同步,都要对应改为新的带超时返回的函数RTC_GetSynchro()
·当超时失败时,报警标志LSEErrFlag=1
·影响下列操作:
上位机写时间,不执行也不进行回复(要不要加上Modbus初始化)
屏幕不进行时间显示
·此时要出现的屏幕报警记录会一直记录2000-00-00 00:00:00,也直接不显示
·休眠和欠压功能原来在RTC里,如果遇到LSE故障,要移动到定时器里启用
10.11
////////////////////晶振问题再优化
·晶振等待时间从250延长到1000
·写时间要进行一系列写操作,或可能超出等待时间,不再参与问题判断
·但若RTC_Get()函数里,通过RTC_GetCounter获得的时钟一直不走,证明RTC功能异常,
判定标志位=1
////////////////////增加温度线初始化后,采集一次数据获得平均温度用来生成默认的并机数据
////////////////////把MODBUS_Init()拆分为MODBUS_Init()和MODBUS_Poll_Init()
10.17
////////////////////写时间操作函数还原 [V0.0.19.73]
////////////////////时间校准的优化 [V0.0.19.74]
·会在启动休眠时,把休眠起始时间=当前值-(原时间-原休眠起始值),使不影响休眠的正常运行
·会在正在欠压强制复位时,把复位倒计时=300s-(原时间-原复位起始值),使不影响休眠的正常运行
·晶振存在问题的判断:①开机的初始化超时了也没退出来 ②将高低频动作不匹配:高频时钟持续10s,低频的RTC也不走动作
////////////////////Config_WritePara()改名ParaChange()
////////////////////优化:每秒屏幕更新时,也同时更新记录最新所在序号
10.22
////////////////////预充逻辑再优化 [V0.0.19.75]
1.连续3次短路报警(每次间隔<=5s),直接锁定充放MOS,显示“预充失败”
2.当短路报警消失、进行预充关闭判断时,若此时仍存在电流(>=100mA),则不符合关闭条件,继续预充(每4s检测一次,直到10次检测结束后仍未正常,显示“预充失败”)
3.负载端电压检测重新写,更贴近实际电压
·新增变量:
uint8_t scOccurFlag; //短路出现过的标志
uint8_t scRepeatTime; //等待短路倒计时,最大2s =>对应短路消失后2s内未再次出现,说明正常
uint8_t scRepeatCount; //短路重复的计数,最大2次 =>对应连续3次出现短路 =>会在第三次短路去执行预充函数时,直接执行预充失败相关
////////////////////同步V3结构体,防止上位机读写位置冲突
////////////////////预充参数可设 [V0.0.19.76]
·新增变量:
pchg_enable 0/1 //预充功能是否开启
pchg_scShow 0/1 //预充开启后,AFE短路报警是否显示(是否保留旧现象_习惯)
pchg_dif_V 12 //预充完成的压差判定,只有负载和电池的压差小于该值,才认为这次预充结束(太小不容易充上来,太大)
pchg_T,pchgNum 4,10 //短路消失时继续预充,预充的持续时间和持续次数,次数溢出后说明当前很难带起负载,显示“预充失败”
scWait_T,scWaitNum+1 5,3 //短路消失且预充完成后,防范下一次短路的等待时间和等待次数,次数溢出后说明已连续短路多次,显示“短路锁定”
·AFE短路保护可隐藏,可选择
短路对应bStatus1,bit5
所以,bStatus1 & 0x7 -> bStatus1 & 0x5
////////////////////增加短路锁定 [V0.0.19.77]
·在“预充失败”以外,新增“短路锁定”的显示,此时bit4、bit5标志位都置1,上位机在一个框里显示
·控制亮灯,因为关预充时会只有“短路锁定”
·控制屏幕显示Fault
·报警记录内容解读+1
·生成报警记录的条件+1
且短路生成记录需要判断是否不隐藏
·主机轮询从机报警时候
预充失败bStatus2,bit5
短路锁定bStatus2,bit4
所以,status_byte2 & 0xe -> status_byte2 & 0xf
////////////////////MOS控制指令,长期和一次性的 [V0.0.19.78]
uint16_t CTRL_Order; //一次性
uint16_t paraMem.ctrl_disable; //长期
////////////////////轮询对从机报警的再次询问,满充时的充电过压不列入条件
////////////////////强制欠压时,当欠压值被其他方式修改不再是500mV,就释放强制欠压
10.21 增加若干优化
////////////////////[分配地址]·开机后的在线个数及时更新
////////////////////补丁:报警记录个数的显示统一为100及以下
10.24 增加若干优化
////////////////////协议里传输当前SOH
////////////////////屏幕PACK在线页,显示轮询中/分配中
////////////////////上位机修改了欠压值,也会停止欠压强制复位功能
////////////////////重新检查删除记录的清空大小
////////////////////软硬件版本号放入EEPROM
//软件;读出和程序的不同,就换当前这个写入EEPROM
//硬件:读出是0xff,默认写6.1.0,否则就是这个数
////////////////////欠压2700和2900
70 0f 6e ee 62 c6 [87] [91] af 78 64 c3 08 0b 00 0a 50 64 46 ec f6 64 46 ec f6 [57]
****焰能屏幕优化相关
////////////////////增加温度线初始化后,采集一次数据获得平均温度用来生成默认的并机数据
////////////////////[分配地址]·自动分配地址条件4
////////////////////[一屏多机+分配地址]·虚地址也显示,即不考虑从机的范围
////////////////////[分配地址]·虚地址持续10s无任何改动,则恢复原地址
////////////////////[分配地址]·写地址代码放入while循环里,加快反应速度
////////////////////[一屏多机]·从机地址数据也应该每秒更新显示
////////////////////[一屏多机]·屏幕显示Addr更新更及时
////////////////////[一屏多机]·主机显示从机的过压保护,要单拎出来判断
////////////////////[分配地址·自动]自动变化地址,要进行地址写入
10.28
////////////////////PACK_SN根据客户需求,可输入字母和数字,不包括符号
10.29
////////////////////限流启动后,运行灯常亮
10.30
////////////////////当电压低于45V时,默认优先走限流
·当电压低于45V时,一旦出现100mA以上电流,开启限流持续10min
11.18
////////////////////Flash的初始值修正
·SOH计算参数添加默认值
////////////////////oldrcc_Ah的同步减少优化
·不然只会和rcc_Ah同步增加而不会同步减少
////////////////////Delay_ms的优化,防止万一和中断冲突,导致卡死
·增加超时退出
////////////////////勾选Free导致协议变化的问题
·MEMORY_UpdateEEPROM函数增加延时
////////////////////屏幕显示限流状态(balanceStatus & 0x10)
·当进行充电显示时,若此时在限流中,则显示限流而不是充电
////////////////////适配RCT6的2K一页 [Vx.x.22.0]
·芯片选型RCT6,J-Link的下载速度选择High_density_512K
·启动文件换成/CORE/startup_stm32f10x_hd.s,魔术棒的C/C++里要改成STM32F10X_HD
·FlashAB的1024改为2048
·main函数和程序开始改为0x1800
////////////////////时间刚写入后,获取的休眠起始点很可能无效(为0) [Vx.x.22.1]
·通过判断使准备好后再进行休眠判断,使不会误入休眠状态
////////////////////RTC时间备份 [Vx.x.22.2]
·若遇到了某些意外情况,导致RTC寄存器里的时间消失(BKP_DR1也不再是0x5050),则执行写入备份时间的程序
·备份时间默认是2023-06-25,但是也会时刻备份:每次开机(且时间正常)时/待机每过1h记录一次/充放电开始时记录一次/充放电时每5min记录一次
////////////////////若干统一更新 [Vx.x.22.3]
[优化]累积容量的单位改为0.1Ah
[优化]计算1循环次数对应的容量时,需先判断百分比参数是否为0(毕竟是可读写的参数)
[优化]初始化时,在读出累积容量后进行一次转化循环次数的判断
[优化]上位机修改循环次数时,同时会清空累积容量
////////////////////补充更新(按道理该有的但是实际没有的) [Vx.x.22.4]
·补充[Vx.x.19.78]:充放MOS关闭标志启用的代码
·补充[Vx.x.19.72]:充放电更新休眠起始点,加RTC是否正常的判断
·补充:先删去45V启动限流这一步,删去充电过流不启动限流的逻辑
////////////////////屏幕在设置短路时间时,若点击Exit,会使短路电压改为50mV,已修复 [Vx.x.22.5]
·同时把其他写入的可能超范围的参数,值规范下,超出范围的不处理
·强制欠压复位是写到500mV,但如果是自己改到500mV,会出现5100mV的情况
——修复:在EEPROM存的欠压值,在读取出后就写入0xff,避免对手动写的干扰
//优化屏幕代码书写
·去掉一些多余的括号和空格
·【补全】短路锁定相关显示
·【补全】报警记录若时间全0,说明是RTC失效,此时不显示时间
////////////////////优化屏幕参数写入范围 [Vx.x.22.6]
·考虑写入极限值,会触发报警闪烁,在所有可能的参数上增加限制
·欠压原来写入不了500mV,现已修改
12.31
////////////////////若连续2次监测到不在充电状态后,停止限流 [Vx.x.22.7]
1.3
////////////////////屏幕协议选择 [Vx.x.22.8]
·因为屏幕0x0345、0x0346被占用,所以协议需要跳过这两项
·被占用是被用于一屏多显主页的跳转按钮的显示
////////////////////充放MOS控制信号位反了,已改正 [Vx.x.22.9]
3.5
////////////////////当并机时,若有某从机出现(过压以外的)报警,该从机下线 [Vx.x.22.10]
·协议里:对应减去该从机的限流值、总容量值、并机个数、电池串数等
·并机数据汇总里,除报警记录以外的数值都要修改计算范围
·屏幕显示总数据里的总容量,也改为*在线数量OnlineNum
·发送给上位机的数据,Online[0]的值从并机个数,改为正常工作个数
·最新新增byte2的0x10,短路锁定,有部分报警判断没加,这里已完善
·Growatt改动16为bmsMem.ucCellNum
·Sigineer改动16为bmsMem.ucCellNum
·Sacolar改动16为bmsMem.ucCellNum
·DONNERGY改动16为bmsMem.ucCellNum
////////////////////总压判断-8V+5V [Vx.x.22.11]
3.13
////////////////////增加禁充逻辑 [Vx.x.22.12]
·当SOC=100%时,充电限流=0
·电总协议里的充/放电允许位,判断条件写错了,已改正
////////////////////根据并机数据修改限流值 [Vx.x.22.13]
·逻辑:
主机在轮询完成、处理并机数据时,对每个在线PACK的soc单独进行判断,调控充电限流值和放电限流值
比如,4台PACK并机,其中3台在线,1台因报警下线。
当在线的PACK
3台都是soc=100%,充电限流值=0
1台soc=100%,2台soc<=99%,充电限流值=100A*2
3台都是soc<=99%,充电限流值=100A*3。
·禁充+禁放强充标志,仍然保留通过平均SOC控制(否则会很复杂)
////////////////////参与设定禁充禁放强充标志的soc值,可设 [Vx.x.22.14]
①启用禁充的标志+SOCx2
②启用禁放的标志和SOCx2
③启用强充的标志和SOCx2
uint16_t requestFlg_enable;
uint8_t chg_forbid_Soc;
uint8_t chg_forbid_reSoc;
uint8_t dsg_forbid_Soc;
uint8_t dsg_forbid_reSoc;
uint8_t dsg_force_Soc;
uint8_t dsg_force_reSoc;
·并机报警掉线功能,在传递给上位机的online[0]的内容,没有注意区分只有1个电池的情况,已完善
3.20
////////////////////满充时,不仅BMS不报警,也不向逆变器上传过压报警 [Vx.x.22.15]
//BIT5 cell ov
if((canMem[0].status_byte1 & 0x01) !=0)
{
if(canMem[0].soc < 99)
{
protectByte1 |= 0x20;
}
else
{
protectByte1 &= 0xDF;
}
}
else
{
protectByte1 &= 0xDF;
}
3.24
////////////////////屏幕选协议的代码没有全部优化,目前已完善
3.26
////////////////////小电流延迟显示时间,从3s改为5s [Vx.x.22.16]
////////////////////一直以来,屏显报警都是直接读EEPROM,有则显示。优化为和个数挂钩 [Vx.x.22.17]
·if(read_index < soe.num+3)优化为if(read_index < soe.num)else内容改动
////////////////////上位机切换语言,Free方框消失,是跳转页码错误,已改正 [Vx.x.22.18]
////////////////////检查上位机控制MOS标志位时,发现预充允许没有完整,增加paraMem的临时控制位 [Vx.x.22.19]
if(((CTRL_Order & 0x04) == 0) && ((paraMem.ctrl_disable & 0x04) == 0))
*有个写成了||,应该改成&&
////////////////////设容量优化 [Vx.x.22.20]
·之前缺了更新bmsMem.rcc
宁化时代要求
////////////////////休眠功能 [+1]
·paraMem
//20240513 长时间待机定时休眠
uint16_t sleep_min_disable; //0x10 纯定时休眠 bit15不执行休眠1使能 bit0~14休眠时间
//20250402 低电压待机定时休眠
uint16_t sleep_vol_disable2;//0x11 低电压休眠 bit15不执行休眠2使能 bit0~14休眠延时
·执行逻辑更改
倒计时仍然根据时间;启用条件判断变复杂。
·注意RTC和不走RTC的都要加
////////////////////整理ParaMem的参数,少量改动“未发布部分”以满足需求
////////////////////真短路保护/预充失败触发后,延迟5min解除 [+2]
·真短路释放延时:5min,暂不可设
·RTC和定时器版都要写
////////////////////修改满充判断逻辑 [+3]
·paraMem增加参数:
uint8_t fcc_methods; //满充方式使能 bit0 单芯过压 bit1 总体过压 bit3 逆变器限压+小电流 bit4 满充电压+截止电流
uint8_t fcc_cur; //截止电流默认5A,单位0.1A
uint16_t fcc_vol; //满充电压默认56V,单位0.1V
·满充方式4启用时,若同时满足电压和电流,关闭充电MOS,停止充电
·增加标志位 bmsMem.bStatus2 & 0x0100 ,表示满充停止充电
////////////////////paraMem参数cyc_改为sohcali_
////////////////////增加总压保护 [+4]
·增加参数
uint16_t pack_ovv; //总体过压保护默认57.6V 单位0.1V
uint16_t pack_ovrv; //总体过压保护释放默认54V 单位0.1V
uint16_t pack_uvv; //总体欠压保护默认45.6V 单位0.1V
uint16_t pack_uvrv; //总体欠压保护释放默认49.6V 单位0.1V
uint8_t pack_ovt; //总体过压保护延时默认1s 单位1s
uint8_t pack_uvt; //总体过压保护延时默认1s 单位1s
·总体过压关充电MOS,总体欠压关放电MOS
·补上了总体过压执行满充
////////////////////增加放电过流2保护 [+5]
·增加参数
uint16_t mcu_occ2; //放电过流2保护电流 250A 单位A
uint16_t mcu_occ2_t; //放电过流2保护延时 30*10ms 单位10ms
·因为执行放电过流2的0.3s保护,改CTL设置为关放电MOS,然后通过定时器启动(走急停的流程)
////////////////////增加环境温度保护 [+6]
·环境温度指的是MCU温度
·增加参数
int8_t am_otc; //0x27 环境充电高温
int8_t am_otcr; // 环境充电高温释放
int8_t am_utc; //0x28 环境充电低温
int8_t am_utcr; // 环境充电低温释放
int8_t am_otd; //0x29 环境放电高温
int8_t am_otdr; // 环境放电高温释放
int8_t am_utd; //0x2A 环境放电低温
int8_t am_utdr; // 环境放电低温释放
·【注意MOS温度保护的设值,要比环境温度大】
////////////////////补全新增保护的显示、并机和记录逻辑 [+7]
·因为并机收集的只有bStatus1/2/3和temperaStatus,所以新增的保护和报警都要尽量放在这里
标志位位置相比之前有改动,需要整体看一下
·单体过压保护和总体过压保护,若在SOC不高时都会屏幕弹出提醒
·并机采集必然被改动,
并机状态改为uint16_t,内容对应改动
·预充使用了CTRL引脚控制MOS开关,那就不能让放电过流2使用CTRL引脚,改为定时器立刻写入关闭放电MOS控制,同时在1s一次的写入控制时保持
·满充条件4关闭充电,并机需要知道,放入bStatus & 0x0800
·记录相关补全,注意存储的标志位只有0xff
报警不记录,但有7个保护需要记录但是标志位不在0x00ff范围里
这7个使用packStatus位置
·满充判定4,增加充电状态的判断
·休眠两种方式要同时可用
1、待机时间超过 24 小时(无通信、无充放电 ,无市电),即进入休眠状态
2、最低单体电压低于休眠电压,且同时满足无通信、无保护、无均衡、无电流,才开始休眠计时
把休眠1时间改为小时单位,占用0x7F00
休眠时间2时间改为0x00FF,分钟单位
////////////////////增加报警 [+8]
·增加报警标志:
MOS温度告警*4,电芯温度告警*4,环境温度告警*4,电压告警*4,电流告警*3
·增加参数:
//20250408
uint16_t alarm_cov; //0x30 单体过压告警
uint16_t alarm_cuv; //0x31 单体欠压告警
uint16_t alarm_pov; //0x32 总体过压告警
uint16_t alarm_puv; //0x33 总体欠压告警
uint8_t alarm_occ; //0x34 充电过流告警
uint8_t alarm_ocd1; // 放电过流1告警
int8_t alarm_mcu_otc; //0x35 电芯充电高温告警
int8_t alarm_mcu_utc; // 电芯充电低温告警
int8_t alarm_mcu_otd; //0x36 电芯放电高温告警
int8_t alarm_mcu_utd; // 电芯放电低温告警
int8_t alarm_am_otc; //0x37 环境充电高温告警
int8_t alarm_am_utc; // 环境充电低温告警
int8_t alarm_am_otd; //0x38 环境放电高温告警
int8_t alarm_am_utd; // 环境放电低温告警
int8_t alarm_afe_otc; //0x39 MOS充电高温告警
int8_t alarm_afe_utc; // MOS充电低温告警
int8_t alarm_afe_otd; //0x3A MOS放电高温告警
int8_t alarm_afe_utd; // MOS放电低温告警
·【逆变器协议里的报警和新增的保护,由任世兴负责】
////////////////////增加报警的闪烁 [+9]
////////////////////补充优化 [+10]
·bStatus3的充放电状态由mcu给的
·休眠参数值改动
////////////////////保护解除时,也同步解除告警
·告警解除不是因为恢复到了告警值以上,而是恢复到保护释放值以上
////////////////////新增电流保护超限锁定 [+11]
·充电过流/放电过流1/放电过流2/短路保护
·对于浪涌保护,也执行5次锁定,也等待60s,修改执行结果为浪涌短路状态保持
·上位机写入解除锁定:使用一次性写入的位置
bit4 用于解锁电流次数超限锁定
////////////////////在设定参数时,新增对应关系 [+12]
①当设定充/放电过流告警电流时,逆变器充/放电限流=告警电流减10A。
②当设定总体过放告警电压时,逆变器放电限压=告警电压。
////////////////////屏幕改动MCU电流保护值时,会同步调控AFE电流保护值 [+13]
////////////////////改动Flash和屏幕初始化时的默认值
////////////////////告警和保护的触发,解除,单独放在文件Status.c里 [+14]
////////////////////各种保护的特殊解除项 [+15]
·单体过压
SOC<96%,放电电流>3A
·单体欠压
接入充电器(进入充电状态)
·总体过充
SOC<96%,放电电流>3A
·总体过放
接入充电器(进入充电状态)
·充电过流
放电电流>2A
·放电过流1
充电电流>2A
·放电过流2
充电电流>2A
·真短路保护
充电电流>1A,负载断开
////////////////////单体过压和欠压由MCU控制
·思路:
上位机大批量读写要连续发两条报文来读写寄存器,
第一条是原报文33/10且内容保持不变(只不过因为上位机画面优化而有些改为固定值),新增f3/f4发送新增的功能参数
单片机在收到33/10报文,准备更新afe参数值时,
该程序会单独把原有的单体过压和单体欠压保护值改为固定不可启动的值(500mV=0x19*20,5115mV=0x3FF*5),且延时拉到最大
然后把实际保护值放入paraMem的参数位置(在第二天报文的更新EEPROM值时进行存储),
第二条是新的f3/f4报文,收到后按内容放入paraMem的对应位置
*bmsMem结构体不用改变,强制恢复欠压值的操作也不需要改动,
因为一直到Flash里存放的值,都和原来一样,只有更新给AFE的值变化
MCU程序里有根据过压欠压值执行保护和保护释放的执行代码,有关闭MOS的执行代码,单体过压和欠压标志位不接受AFE的更新
*paraMem已有部分基本不需要变动,
新增部分根据地址来读写
////////////////////单体过压/欠压的保护的特殊解除项
////////////////////增加开路电压法 [+17]
·增加参数:
uint16_t ocv_cpBuf[15];
uint16_t ocv_dpBuf[15];
uint16_t ocv_min_enable;
uint8_t ocv_caliRange_soc;
uint8_t ocv_caliRange_T;
////////////////////锁定最大次数,暂不做可设
////////////////////休眠方案2的位置朝后移,两种休眠分开也不能影响已有的位置
////////////////////和上位机通信时的一次性总读写参数,注意要放在一起
////////////////////逆变器协议内容更新 [+18]
·报警向逆变器发送报警标志,和保护区分开
·单体过压和总体过压不上报
·SMK协议无报警和保护
古顶=硕日轻微改动
/** 宁化时代 **/
*单体过压不触发?
——电压保护的特殊解除项,应该多等待几轮才能触发,暂时是>1
——电压保护条件满足清零计数变量,之前未加现在已加上
*过流保护后立刻显示MOS故障?
充放电状态的赋值对象是bStatus3,之前是2现在已改
*屏幕上显示保护记录,增加新增的保护
(启动记录的放电过流2有个5写成了3,已改)
·协议内告警和保护有点要改的
/** [V3.00.00.12]+[6.2.P] **/
////////////////////程序发给客户后,对程序的检查
·满充条件4会关闭MOS,但没有明确释放条件,参考过压的释放条件,现在增加了满充条件4的释放条件 [+1]
·出现单体过压或总体过压告警后,充电请求电流下降为40A,之前没写,现在已加 [+2]
·修改总体欠压告警电压后,逆变器放电限压值=该值,之前变量名写成了Cur电流,现在已改 [+3]
////////////////////程序发给客户后,对程序的检查2
·并机数据canMem结构体的标志位没改为uint16_t,现在已改 [+4]
·发生单体/总体过压告警后,逆变器限流值改为固定40A,原来是对应小波动,现在改为和禁充类似的标志位,此时限流值总共为40A [+5]
·客户需要更及时的逆变器限流改动,原来主机和并机总值的计算,还有状态判断是轮询一轮结束后执行,现在放在1s一次的函数进行判断,及时更新 [+6]
·真短路保护的自动恢复,之前没写只写了特殊解除,现在已加 [+7]
·预充超时失败与真短路分开状态,并改为和真短路保护一样在5min后自动解除,解除时注意打开Ctrl引脚。有特殊解除项,但没有次数超限锁定 [+8]
·浪涌短路和预充过充的参数有问题需要调换flash里的默认值 [+9]
·屏幕强制解除欠压时,修改的是过压保护值,但是因为不写给AFE了不会自动初始化状态,所以需要通过代码将过压保护关掉,又因为修改为500mV所以打不开 [+10]
·总体过压/欠压的保护位,应该是bStatus1不是bStatus2,已改 [+11]
·SOC=100%时,单体过压告警/保护,总体过压告警/保护,灯和屏幕都不显示 [+12]
·当真短路和预充失败时,MCU也会维持MOS关闭 [+13]
·放电过流2,原来的启用判断,电流写成充电电流判断,bStatus1写成了2,现在已改 [+14]
·当主机在因为屏幕/上位机去单独读某个从机的整体数据时,同步更新canMem结构体对应的值 [+15]
·环境温度保护,应该在temperaStatus而不是balanceStatus,现在已改 [+16]
////////////////////检查所有保护和报警标志位的正确,包括次数超限锁定
·浪涌短路跳转预充的判断里,应该是BIT6但写成了BIT10,现在已改
·真短路保护和预充失败,去掉特殊解除项:负载移除,因为板子做不到检测该条件
*已通过断开B+线的方式,测出真短路保护超5次后锁定,正常执行
·真短路保护的解除,应该先判断是否在锁定中,减少不必要的计数
·充电过流锁定时,会关闭放电MOS?——之前写了锁定标志也会关MOS,且标志位不对,这里改正后注释到,因为没必要再因此关闭
////////////////////预充逻辑的思考和测试
·连接逆变器空开闭合,执行浪涌短路保护:
(出现后8s自动解除,若连续出现5次执行锁定)
·预充第1s,判断正在真短路,执行真短路保护:
(出现后就锁定不可解除)
·在浪涌短路解除后,预充连续40s还是没有成功打开逆变器,执行预充超时失败
(出现后5min自动解除,若连续出现5次执行锁定)
////////////////////原“真短路保护次数超限锁定”,改为“预充超时失败次数超限锁定”,
·真短路在出现后不可解除,预充超时失败改为5次后锁定
////////////////////定时的变量检查,特别是休眠
·休眠2,定时器版之前忘加了;应该在唤醒时也更新休眠2的起始点
////////////////////开路电压法重启后也可用
·开路电压法没有做关机后也保存定时起点的功能
·开路电压法对电流的看法,放到1A以内而不是0.1A
////////////////////开路电压法,必须是在之前真的充放电后,才执行校准,且只执行一次不需要重启
·OCV_choice是待机状态0/1,不启动计时
·执行一次后,刷新OCV_choice=1,除非有充放电否则不再计时
////////////////////预充逻辑,尽量成为和原逻辑相同
·同时有个scr_Moni_Count = SCR_MON_CNT;写成了UVOff_MON_CNT,现在已改
·之前的真短路定时恢复不需要定时器函数和RTC函数,现在已删去
////////////////////为了能够节省轮询时间,将PACK_NUM改为可设 [+26]
·默认2台,可改范围1-20
·当PACK_NUM=1时,主机不会轮询。
·3.4网口的超时初始化,等待时间需根据PACK_NUM调整,1=》固定30s 2~20=》PACK_NUM*1.5s+2s
·地址在赋值和改动PACK_NUM时,需要检测是否符合Addr<=PACK_NUM,不符合时,恢复到2(若PACK_NUM=1则恢复到1)
·定义一个值作为PACK_NUM的最大值,全程序内通用,目前对应20
自动分配地址的虚地址地址不是PACK_NUM+1,而是这个值+1
////////////////////测试需要,把真短路也改为5min自动释放,连续三次后锁定
·标志位和浪涌短路共用一个
////////////////////对于并机总数,做出如下优化: [+29][+30]
·只对主机轮询个数有效,在设置自身地址时,仍然在1~20都可以
·改为在paraMem里,能整体读写
·当地址是从机时,可以写入这个值,但是是在成为主机时生效
并联轮询总数:
单独使用
并机2台
////////////////////真短路五次锁定,实测未锁定,排查原因,已优化 [+31]
////////////////////主机分配地址受自身的并机总数影响 [+32]
·主要是从机不会因为自身的该值,而改动自身地址,实际可被分配的范围仍然是2~20,可手动改的范围仍然是1~20
·主机轮询和分配地址都受并机总数影响
////////////////////休眠问题和总体欠压校准问题 [+33]
以下都是[+34]
////////////////////改动充电过压报警后电流40A,变为40A*没有保护的个数
////////////////////开路电压法的改动
·判定开启静置倒计时的电流是3A
·校准倒计时设定默认值30min
·校准之后会更新静置倒计时恢复原值,使得在静置时一直会校准SOC
·不再需要记录旧状态,只要在静置状态下,一直定时校准
·不再区分充放电曲线,程序只使用DP值,所以EEPROM不再需要记录状态,而时间始终存在和持续更新
·不允许OCV校准时,OCV_step=0
·允许OCV校准时,
正在充放电,OCV_step=0,但是会每隔30min记录一次时间
刚开机没有时间/刚从充放电状态回来,OCV_step:0->3
若刚从充放电状态回来
·倒计时起点,在EEPROM保存异常时更新;在从充放电状态回到静置状态时更新
正在充放电时,每30min更新一次,
·单芯值,小数点后一位进行四舍五入
////////////////////放电请求电流:当PACK电压=放电请求电压时,BMS发送禁放指令
·此时就算soc在20%以上,也会禁放
////////////////////取消MOS充放电低温的告警和保护的判断赋值,参数暂时保留,标志位的执行暂时保留 [+35]
////////////////////浪涌短路默认参数110mV 64us [+36]
·把AFE过流参数调到30mV40mV30mV(200A的设置)然后保持固定值,SDWA的同步改动我也去掉,上位机的之后去掉
·110mV 64us
70 0e 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 21 1a 50 64 46 ec f6 64 46 ec f6 60
////////////////////SOC与容量 [+37]
·原总容量,现在名称改为[额定容量]bmsMem.fcc->bmsMem.ncc (nominal:名义上的)
·新建[满充容量]:fcc,断电可保存,精确到1mAS
·在RTC晶振正常时,若12h内关机,也可以记住开始时间和是否要计时
在RTC晶振异常时,若12h内关机,开机后需要重新开始欠压到过压且中间不关机,才能校准总容量
·逻辑:若刚开始充电时,SOC=0/1%,则允许校准满充容量,同步计时12h超过则不再校准。
若12h内,满足了满充判定的任一条件而使SOC=100%,在基本停止充电(电流<2A)后执行容量校准:赋值[满充容量]=此时的剩余容量并保存。
若12h外,满足了满充判定的任一条件而使SOC=100%,不会变动满充容量。
剩余容量可以一直增长,若超过了原来的满充容量,SOC保持100%不再增长。
·上位机显示[额定容量]和[剩余容量],在充满时剩余容量=[总容量]。
·上位机或屏幕写入容量时,既更新[额定容量],也更新[满充容量],[剩余容量]=[满充容量]*SOC,但不影响此前的满放充电置位的校准标志
上位机或屏幕写入SOC时,会将该标志清零
·计算SOC时,因为满充容量精量化,SOC每1%对应的是1mAS级别,所以要
只要还有0.1AH都向上显示1%
在rcc>=fcc/100*99+0.1AH的值后,SOC固定为100%
·[满充容量]保存在EEPROM,单位mAS
[满充容量校准等待时间]保存在EEPROM,使晶振正常时重启不影响进程
·发给逆变器/屏幕显示的并机总容量,暂时还是*额定容量,后面提出了再改
·发给逆变器的并机剩余容量,暂时也还是*额定容量
6.4
////////////////////满充前SOC最高锁定99% [+38]
** 下面是标准版更新:[V3.36.22.21]~[V3.36.22.28] **
5.13
////////////////////3.4口可以和V3上位机通信 [Vx.x.22.21]
·建立通信时,主机的轮询暂停(主机发现有其他报文,也会主动暂停)
6.16
////////////////////将禁放强充判断增加"=",防止设置0%时出现问题 [Vx.x.22.22]
////////////////////未放空维持1%,就像未充满维持99%类似 [Vx.x.22.23]
·因为宁化时代增加了总容量可变,所以未放空是在0.1Ah~0.5Ag徘徊
////////////////////更新温度值表 [Vx.x.22.24]
·温度线的范围从-40~110改为-55~125
·板贴温度更精确(不过原来的也可以用,非极限值的实际差距不大0.1左右)
////////////////////更新预充压差从12V改为19V [Vx.x.22.25] (之前已加)
6.30
////////////////////协议内容矫正 [Vx.x.22.26] (之前已加)
·Growatt.485协议,CV_Vol = bmsMem.inverter_chgVolLimit * 10;
7.3
////////////////////所有逆变器协议,注释上报均衡 [Vx.x.22.27]
7.16
////////////////////整理canMem一类结构体 [Vx.x.22.28]
·cur和temp的赋值,需要加上转换:(int16_t)
·同时把最大最小温度的变量类型改回uint16_t,都是K氏温度
7.24
////////////////////屏幕显示屏幕版本号优化 [Vx.x.22.29]
·可兼容028xx,从0x04开始
·如果屏幕版本号为0,不显示
8.5
////////////////////屏幕显示软件版本号有误,已优化 [Vx.x.22.30]
** 宁化时代更新:[+39~+41] **
[+39]强制关总体欠压优化:
·宁化的单体欠压和总体欠压,都由MCU自身执行
·原有“强制关单体欠压”的执行相关,删去:
恢复默认参数时,关闭功能;修改单体欠压保护值时,关闭功能;
对单体欠压参数=25的恢复
·新逻辑:当按下强制关欠压后,总体欠压和单体欠压保护的标志位保持置0
倒计时结束可恢复,重启可恢复
[+40]关闭满充校准总容量设置_真正起效:
之前的关闭设置没有实际启用,现在改为可用
·关闭后,rcc不能能超过fcc
·关闭后,fcc=ncc
[+41]出现总体过压或单体过压的时候,充电限流固定40A,改动为:
100A板子的限流值应为20A
[++1]为了适配更广泛的客户:
*宁化时代参数,但所有电流相关改为100A对应值
bmsMem
70 0e 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 00 1a 50 64 46 ec f6 64 46 ec f6 96
3c 37 00 05 41 3c ec f1 69 69 01 01 3c 85
64 05 3c 00
40 02 e8 01 e8 03 e8 03
paraMem
40 02 1c 02 c8 01 f0 01 01 01 82 00 04 00 46 41 ec f1 46 41 ec f1 60 60 02 00 00 00 00 00 00 00 c0 0d ea 0b 30 02 e8 01 50 64 37 05 3c f1 41 f1 41 f1 5f f1 5f f1
*一系列设定:
1.关闭SOC电压定时校准
2.关闭满充校准总容量
3.并机16台
paraMem
40 02 1c 02 c8 01 f0 01 01 01 82 00 04 00 46 41 ec f1 46 41 ec f1 60 60 [10] 00 00 00 00 00 00 00 c0 0d ea 0b 30 02 e8 01 50 64 37 05 3c f1 41 f1 41 f1 5f f1 5f f1
**标准更新**
11.19
////////////////////Growatt CAN优化 [Vx.x.23.1]
////////////////////新预充 [Vx.x.23.2]
////////////////////屏幕亮度范围20~40都可识别 [Vx.x.23.3]
11.20
////////////////////宁化时代版,屏幕设置总个数有问题,无法有效设置
·原来存EEPROM,后面做成上位机可设的paraMem,需要改存储方式
////////////////////增加电流告警自动释放
**标准更新**
////////////////////对于关闭预充时,也要在AFE短路时测试是否是真短路 [Vx.x.23.3]
11.27
////////////////////原[Vx.x.23.2]的新预充逻辑,实测会有问题,暂搁置并使用原有逻辑,把8s后直接退出预充
·原有预充逻辑,但AFE短路结束后直接退出预充
·退出预充后可能立刻触发AFE短路,这里要将AFE短路多次锁定的判断参数在预充结束时同步更新
**标准更新12.5**
////////////////////AFE温度保护值问题 [Vx.x.23.6]
////////////////////过流报警释放问题
**** 硬件6.3.L ****
//均衡控制,PA8换到PC1 √
//PB12做输入,PB13做输出默认低 √
//PC10/PC11做蓝牙通信,参考驻启程序 √
//PC12做蓝牙复位脚,参考驻启程序 √
//调整优先级,参考最近给恒格改的 √
//优先级分组函数放出来 √
//先把限流操作内容清空,留给之后写详细控制 √
1.13
//蓝牙完善
·主动上传数据共5包
·读写的参数暂时就1个;回复增加ProtocolName;格式为未来多个参数的读写做准备;更进一步规范格式,""和{}/[]都要检查
·在正在连接时改名称,需在+++后等待0.5s
1.15
移植功能:
//长按开机/复位/关机
·开机后,当KEY收到2s高电平,控制POWER保持输出高电平
·按键再次按下后,若摁了2s执行复位,若继续摁了4s执行关机
·LED状态:
开机时,所有灯全亮,然后在while循环中变成实际状态
执行复位但还没有执行关机时,所有灯闪烁后全亮一会
执行关机,所有灯熄灭
·复位包括:CAN,串口x4,蓝牙模块,各种保护/报警/故障标志,看门狗
·关机包括:灯全灭,MOS全关,限流/均衡/预充全关,通信停止
·如果系统卡住了,那岂不是关不了机?——加上看门狗
1.19
//PA8用于输出PWM波以控制限流
·PWM频率20kHz,占空比50%-95%
·限流10A
·基准电压暂时换53V,最好能取到52V时候的
53.5V 94.0f
53.0V 92.8f
·改为100.00单位试试,0.1f的波动值有点大
·将PWM波的微调,放到计算电流函数里,能立刻反应——需要看效果
·根据电流差值调整占空比,变化越大电流波动越大
似乎占空比改动后,对电流的影响会持续1.2s——改成2s反应1次试试
·需要测试当电流小于10A以后,PWM波会怎么样
1.20
//蓝牙-屏幕版本号在存在时才上报
1.21
//优化:
·CAN错误中断加上
·休眠后,串口超时初始化的倒计时也关闭
·报警记录的地址,要判断是否正常
·自动分配地址,定时器里对地址的变化,暂不执行写入EEPROM,之后用write_Addr写进去
·自动分配地址,把时序安排好,主机收到回复后间隔1次再发下一个分配,从机回复和置IO2都直接在while循环里
·地址为虚地址时,快速闪烁而不是常亮
·之后做优化代码量,和封装函数
1.22
//晶振还是要用外部晶振,才能用纽扣电池供电
1.30
//之前把清空队列标志操作,和根据地址输出IO1电平功能,放到了(write_Addr != 0)的判断里,现在放出来
/**硬件6.3.LW**/
//通信新蓝牙MBO26A
·AT命令后跟着的是<CR><LF>=0x0d 0x0a
·波特率9600
·连接/断开发的数据是+CONNECTED和+DISCONN
·最大字节数244
//蓝牙字节数上限小,分包更多
//引脚:
·PC13 电源维持
·PC0 按键状态检测
·PB12 LED1电量灯
·PB9 限流PWM波
·PC1 外接电阻检测,以控制地址变化
2.27
//限流因停止充电而停止,之前注释了,现在恢复
2.27
//按钮分长按和短按模式,根据实际需要选择
//自动分配地址功能,相关执行代码只有在启用时才执行
//小板增加蓝牙/WIFI/4G所有代码,可根据实际需要选择执行其中一个
2.28
//4G通信完善
*该芯片开机等待只需0.8s
//Flash参数改100A的
3.2
//4G通信完善
·该芯片开机等待虽说只需0.8s,但实际发AT+CSQ在开机时会回复99,需要延时通信
·还差最后2个函数的正常通信内容、事件相关、参数表
事件
休眠
读写函数
服务函数大框架
定时上报属性删减,
原版:
LTE_CombineStr("\"ChgLimitStatus\":%s,", Status[ChgLimitStatus]); //14+4 + 5
LTE_CombineStr("\"BalanceStatus\":%s,", Status[BalanceStatus]); //13+4 + 5
//特殊状态
LTE_CombineStr("\"LockOCC\":%s,", Status[LockOCC]); //7+4 + 5
LTE_CombineStr("\"LockOCD1\":%s,", Status[LockOCD1]); //8+4 + 5
LTE_CombineStr("\"LockOCD2\":%s,", Status[LockOCD2]); //8+4 + 5
LTE_CombineStr("\"LockSP\":%s,", Status[LockSP]); //6+4 + 5
LTE_CombineStr("\"LockSC\":%s,", Status[LockSC]); //6+4 + 5
LTE_CombineStr("\"ChgMosFault\":%s,", Status[ChgMosFault]); //11+4 + 5
LTE_CombineStr("\"DsgMosFault\":%s,", Status[DsgMosFault]); //11+4 + 5
LTE_CombineStr("\"DOStatus\":%s,", Status[DOStatus]); //8+4 + 5
LTE_CombineStr("\"ForceOffUV\":%s,", Status[ForceOffUV]); //10+4 + 5
//保护
LTE_CombineStr("\"PackOV\":%s,", Status[PackOV]); //6+4 + 5
LTE_CombineStr("\"PackUV\":%s,", Status[PackUV]); //6+4 + 5
LTE_CombineStr("\"CellOV\":%s,", Status[CellOV]); //6+4 + 5
LTE_CombineStr("\"CellUV\":%s,", Status[CellUV]); //6+4 + 5
LTE_CombineStr("\"PF\":%s,", Status[PF]); //2+4 + 5
LTE_CombineStr("\"L0V\":%s,", Status[L0V]); //3+4 + 5
LTE_CombineStr("\"OCC\":%s,", Status[OCC]); //3+4 + 5
LTE_CombineStr("\"OCD1\":%s,", Status[OCD1]); //4+4 + 5
LTE_CombineStr("\"OCD2\":%s,", Status[OCD2]); //4+4 + 5
LTE_CombineStr("\"SP\":%s,", Status[SP]); //2+4 + 5
LTE_CombineStr("\"SC\":%s,", Status[SC]); //2+4 + 5
LTE_CombineStr("\"McuOTC\":%s,", Status[McuOTC]); //6+4 + 5
LTE_CombineStr("\"McuOTD\":%s,", Status[McuOTD]); //6+4 + 5
LTE_CombineStr("\"McuUTC\":%s,", Status[McuUTC]); //6+4 + 5
LTE_CombineStr("\"McuUTD\":%s,", Status[McuUTD]); //6+4 + 5
LTE_CombineStr("\"AmbientOTC\":%s,", Status[AmbientOTC]); //10+4 + 5
LTE_CombineStr("\"AmbientOTD\":%s,", Status[AmbientOTD]); //10+4 + 5
LTE_CombineStr("\"AmbientUTC\":%s,", Status[AmbientUTC]); //10+4 + 5
LTE_CombineStr("\"AmbientUTD\":%s,", Status[AmbientUTD]); //10+4 + 5
LTE_CombineStr("\"MosOTC\":%s,", Status[MosOTC]); //6+4 + 5
LTE_CombineStr("\"MosOTD\":%s,", Status[MosOTD]); //6+4 + 5
LTE_CombineStr("\"MosUTC\":%s,", Status[MosUTC]); //6+4 + 5
LTE_CombineStr("\"MosUTD\":%s,", Status[MosUTD]); //6+4 + 5
//报警
LTE_CombineStr("\"PackOVWarning\":%s,", Status[PackOVWarning]); //6+4 + 5
LTE_CombineStr("\"PackUVWarning\":%s,", Status[PackUVWarning]); //6+4 + 5
LTE_CombineStr("\"CellOVWarning\":%s,", Status[CellOVWarning]); //6+4 + 5
LTE_CombineStr("\"CellUVWarning\":%s,", Status[CellUVWarning]); //6+4 + 5
LTE_CombineStr("\"OCCWarning\":%s,", Status[OCCWarning]); //3+4 + 5
LTE_CombineStr("\"OCDWarning\":%s,", Status[OCDWarning]); //3+4 + 5
LTE_CombineStr("\"McuOTCWarning\":%s,", Status[McuOTCWarning]); //6+7+4 + 5
LTE_CombineStr("\"McuOTDWarning\":%s,", Status[McuOTDWarning]); //6+7+4 + 5
LTE_CombineStr("\"McuUTCWarning\":%s,", Status[McuUTCWarning]); //6+7+4 + 5
LTE_CombineStr("\"McuUTDWarning\":%s,", Status[McuUTDWarning]); //6+7+4 + 5
LTE_CombineStr("\"AmbientOTCWarning\":%s,", Status[AmbientOTCWarning]); //10+7+4 + 5
LTE_CombineStr("\"AmbientOTDWarning\":%s,", Status[AmbientOTDWarning]); //10+7+4 + 5
LTE_CombineStr("\"AmbientUTCWarning\":%s,", Status[AmbientUTCWarning]); //10+7+4 + 5
LTE_CombineStr("\"AmbientUTDWarning\":%s,", Status[AmbientUTDWarning]); //10+7+4 + 5
LTE_CombineStr("\"MosOTCWarning\":%s,", Status[MosOTCWarning]); //6+7+4 + 5
LTE_CombineStr("\"MosOTDWarning\":%s,", Status[MosOTDWarning]); //6+7+4 + 5
// LTE_CombineStr("\"MosUTCWarning\":%s,", Status[MosUTCWarning]); //6+7+4 + 5
// LTE_CombineStr("\"MosUTDWarning\":%s,", Status[MosUTDWarning]); //6+7+4 + 5
改为:
if(ChgLimitStatus) LTE_CombineStr("\"ChgLimitStatus\":true,"); //14+4 + 5
if(BalanceStatus) LTE_CombineStr("\"BalanceStatus\":true,"); //13+4 + 5
//特殊状态
if(LockOCC) LTE_CombineStr("\"LockOCC\":true,"); //7+4 + 5
if(LockOCD1) LTE_CombineStr("\"LockOCD1\":true,"); //8+4 + 5
if(LockOCD2) LTE_CombineStr("\"LockOCD2\":true,"); //8+4 + 5
if(LockSP) LTE_CombineStr("\"LockSP\":true,"); //6+4 + 5
if(LockSC) LTE_CombineStr("\"LockSC\":true,"); //6+4 + 5
if(ChgMosFault) LTE_CombineStr("\"ChgMosFault\":true,"); //11+4 + 5
if(DsgMosFault) LTE_CombineStr("\"DsgMosFault\":true,"); //11+4 + 5
if(DOStatus) LTE_CombineStr("\"DOStatus\":true,"); //8+4 + 5
if(ForceOffUV) LTE_CombineStr("\"ForceOffUV\":true,"); //10+4 + 5
//保护
if(PackOV) LTE_CombineStr("\"PackOV\":true,"); //6+4 + 5
if(PackUV) LTE_CombineStr("\"PackUV\":true,"); //6+4 + 5
if(CellOV) LTE_CombineStr("\"CellOV\":true,"); //6+4 + 5
if(CellUV) LTE_CombineStr("\"CellUV\":true,"); //6+4 + 5
if(PF) LTE_CombineStr("\"PF\":true,"); //2+4 + 5
if(L0V) LTE_CombineStr("\"L0V\":true,"); //3+4 + 5
if(OCC) LTE_CombineStr("\"OCC\":true,"); //3+4 + 5
if(OCD1) LTE_CombineStr("\"OCD1\":true,"); //4+4 + 5
if(OCD2) LTE_CombineStr("\"OCD2\":true,"); //4+4 + 5
if(SP) LTE_CombineStr("\"SP\":true,"); //2+4 + 5
if(SC) LTE_CombineStr("\"SC\":true,"); //2+4 + 5
if(McuOTC) LTE_CombineStr("\"McuOTC\":true,"); //6+4 + 5
if(McuOTD) LTE_CombineStr("\"McuOTD\":true,"); //6+4 + 5
if(McuUTC) LTE_CombineStr("\"McuUTC\":true,"); //6+4 + 5
if(McuUTD) LTE_CombineStr("\"McuUTD\":true,"); //6+4 + 5
if(AmbientOTC) LTE_CombineStr("\"AmbientOTC\":true,"); //10+4 + 5
if(AmbientOTD) LTE_CombineStr("\"AmbientOTD\":true,"); //10+4 + 5
if(AmbientUTC) LTE_CombineStr("\"AmbientUTC\":true,"); //10+4 + 5
if(AmbientUTD) LTE_CombineStr("\"AmbientUTD\":true,"); //10+4 + 5
if(MosOTC) LTE_CombineStr("\"MosOTC\":true,"); //6+4 + 5
if(MosOTD) LTE_CombineStr("\"MosOTD\":true,"); //6+4 + 5
//if(MosUTC) LTE_CombineStr("\"MosUTC\":true,"); //6+4 + 5
//if(MosUTD) LTE_CombineStr("\"MosUTD\":true,"); //6+4 + 5
//报警
if(PackOVWarning) LTE_CombineStr("\"PackOVWarning\":true,"); //6+4 + 5
if(PackUVWarning) LTE_CombineStr("\"PackUVWarning\":true,"); //6+4 + 5
if(CellOVWarning) LTE_CombineStr("\"CellOVWarning\":true,"); //6+4 + 5
if(CellUVWarning) LTE_CombineStr("\"CellUVWarning\":true,"); //6+4 + 5
if(OCCWarning) LTE_CombineStr("\"OCCWarning\":true,"); //3+4 + 5
if(OCDWarning) LTE_CombineStr("\"OCDWarning\":true,"); //3+4 + 5
if(McuOTCWarning) LTE_CombineStr("\"McuOTCWarning\":true,"); //6+7+4 + 5
if(McuOTDWarning) LTE_CombineStr("\"McuOTDWarning\":true,"); //6+7+4 + 5
if(McuUTCWarning) LTE_CombineStr("\"McuUTCWarning\":true,"); //6+7+4 + 5
if(McuUTDWarning) LTE_CombineStr("\"McuUTDWarning\":true,"); //6+7+4 + 5
if(AmbientOTCWarning) LTE_CombineStr("\"AmbientOTCWarning\":true,"); //10+7+4 + 5
if(AmbientOTDWarning) LTE_CombineStr("\"AmbientOTDWarning\":true,"); //10+7+4 + 5
if(AmbientUTCWarning) LTE_CombineStr("\"AmbientUTCWarning\":true,"); //10+7+4 + 5
if(AmbientUTDWarning) LTE_CombineStr("\"AmbientUTDWarning\":true,"); //10+7+4 + 5
if(MosOTCWarning) LTE_CombineStr("\"MosOTCWarning\":true,"); //6+7+4 + 5
if(MosOTDWarning) LTE_CombineStr("\"MosOTDWarning\":true,"); //6+7+4 + 5
//if(MosUTCWarning) LTE_CombineStr("\"MosUTCWarning\":true,"); //6+7+4 + 5
//if(MosUTDWarning) LTE_CombineStr("\"MosUTDWarning\":true,"); //6+7+4 + 5
3.5
//浪涌短路锁定,报警灯常亮显示
//均衡状态不做事件
3.9
//增加默认更新:
////////////////////钰泰均衡控制逻辑
////////////////////钰泰均衡控制关闭的脉冲改短,防止超过了50us
////////////////////上位机可读协议,通过选择协议0,会把当前协议读出
////////////////////浪涌短路锁定,报警灯常亮显示
////////////////////默认关异常高压保护,防止误报
宁化100A70 [1e] 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 00 1a 50 64 46 ec f6 64 46 ec f6 [f8]
宁化100A70 [1e] 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 21 1a 50 64 46 ec f6 64 46 ec f6 [0e]
3.18
////////////////////限流优化
·初始占空比=(电池电压+1V)/默认充电器电压60V * 100%
·非充电状态关闭限流,等待8s
3.20
////////////////////4G优化
//根据家储更新驻启的4G后,驻启又在测试中增加了一些更新,现在同步到家储里
////////////////////适配新IAP_V2.5
////////////////////OCV只减不增
////////////////////优化代码量
Program Size: Code=125592 RO-data=6808 RW-data=1504 ZI-data=12160
125592+6808+1504 = 133904 = 130K超出上限120K
·不选择三选一小板的代码量
Program Size: Code=87534 RO-data=5962 RW-data=1300 ZI-data=7004
87534+5962+1300 = 94796 = 92K
133904-94796 = 39108 >38K
·Pylon电总协议应该以字符串格式填写,但是目前是硬写的
Program Size: Code=84898 RO-data=5962 RW-data=1300 ZI-data=7004
84898+5962+1300 = 92160
94796-92160 = 2636 >2K
·【优化方案】
①合并大量重复的参数处理逻辑,采用表驱动方式重构:
定义参数信息结构体:
·写函数改良:
Program Size: Code=126948 RO-data=6804 RW-data=1520 ZI-data=12160
126948+6804+1520 = 135272
Program Size: Code=123196 RO-data=7552 RW-data=1524 ZI-data=12164
123196+7552+1524 = 132272
135272-132272 = 3000
·读函数改良
Program Size: Code=123196 RO-data=7552 RW-data=1524 ZI-data=12164
123196+7552+1524 = 132272
Program Size: Code=121096 RO-data=7556 RW-data=1524 ZI-data=12164
121096+7556+1524 = 130176
132272-130176 = 2096
·在LTE_4G_IT_Update()和LTE_4G_IQ_Transmit()中,大量if(LTE_status == xxx)结构
建立状态函数指针数组,循环调用判断
·字符串常量重复,
一致的统一使用宏定义,相似的抽取出一个通用函数使用宏定义
////////////////////另外,采样电阻是8个2mΩ
对应电流=电压*4
//AFE保护值要改为60mV 70mV 60mV(保存时的固定值)
//Flash保存值:
//异常高压已关闭
原值:70 1e 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 00 1a 50 64 46 ec f6 64 46 ec f6 f8
新值:70 1e 6e d0 62 a8 8c 9b af 78 64 c3 48 4b 00 4a 50 64 46 ec f6 64 46 ec f6 46
//增益校准参数默认值已改7000
////////////////////OTA升级
·IAP标志使用IIC存储,因为换了板子所以不需要兼容旧IAP,不用考虑擦除跳转标志
·但是写跳转标志的操作可以保留,这样能兼容直接烧写Flash时无法跳转用户程序的问题
////////////////////减少已有协议
原有:
Program Size: Code=122080 RO-data=7556 RW-data=1544 ZI-data=12160
122080+7556+1544 = 131180
去掉全部协议后:
Program Size: Code=98212 RO-data=7552 RW-data=1540 ZI-data=12164
98212+7552+1540 = 107304 = 104K
131180-107304 = 23876
启用Sol-Ark,Growatt,Pylon,Voltronic
Program Size: Code=103472 RO-data=7556 RW-data=1540 ZI-data=12164
103472+7556+1540 = 112568
再启用德业,锦浪
Program Size: Code=104840 RO-data=7556 RW-data=1544 ZI-data=12160
103472+7556+1544 = 112572 = 109K
对应序号:1.3.11.12.14.37
////////////////////低压休眠是关闭电源
·若开启1min无任何充电电流,会继续进入低压休眠
——低压休眠时间调1min
·低压休眠条件删去均衡判断,删去充放电判断,删去对其他报警的判断
只判断是否有充电电流
////////////////////开机后,5V运行灯亮
·长按后灯亮,表示开始运行
////////////////////加热逻辑,和华富一样
·充电低温加热释放+2,欠压禁止加热
·控制引脚是PC6
////////////////////IAP跳转时,若初始LED灯置高,会在短按时也瞬闪一下
·长按版,初始LED默认置低,之后再置高即可
·关机前留给复位的过程
////////////////////复位功能先不启用
////////////////////报警灯灭的比较晚,最好在运行灯那里一起执行
{"request_id":"gca20ac1f929j4c51834","params":["Protocol","InverterChgVol","InverterDsgVol","InverterChgCur","InverterDsgCur","POV_Vol","POVR_Vol","PUV_Vol","PUVR_Vol","COV_Vol","COVR_Vol","CUV_Vol","CUVR_Vol","SC_Cur","SC_Delay","OCC_Cur","OCC_Delay","OCD1_Cur","OCD1_Delay","OTC_Temp","OTCR_Temp","OTD_Temp","OTDR_Temp","UTC_Temp","UTCR_Temp","UTD_Temp","UTDR_Temp","SOC","CYC","Capacity"]}
Program Size: Code=107008 RO-data=7556 RW-data=1552 ZI-data=14376
////////////////////3.23新版打回,从8个2mΩ改为9个1mΩ
//对应电流=电压*9
//增益校准参数默认值改10000
//AFE保护值要改为30mV 40mV 30mV(保存时的固定值)
//Flash保存值:
70 1e 6e d0 62 a8 8c 9b af 78 64 c3 18 1b 00 1a 50 64 46 ec f6 64 46 ec f6 f8
///////////////////事件和各种回复,都有计数,超时则返回
·上报完成后则清零计数
///////////////////事件上报删去预警
///////////////////4G通信数组增大到1000
Program Size: Code=106676 RO-data=7552 RW-data=1556 ZI-data=17524
106676+7552+1556 = 115784
///////////////////HardFault_Handler()重启
(长按版会关机)
////////////////////Wh版屏幕,要同时兼容VTc和SDWn,所以部分地址要改
部分超出0x3FF但是VTc还要支持的地址:
0x05A9
0x05AA
0x05B5
0x05B6
部分改为两者兼容的地址:
0x05A0 -> 0x0366
0x05A3 -> 0x0361
0x05A4 -> 0x0100(原地址)
0x05A5 -> 0x0362
0x05A7 -> 0x0364
0x05A8 -> 0x0365
0x05B1 -> 0x0371
0x05B2 -> 0x0372
0x05B3 -> 0x0373
0x05B4 -> 0x0374
0x05B8 -> 0x0375
0x05B9 -> 0x0376
0x05BA -> 0x0377
0x05BB -> 0x0378
参考旭尊RE版,
不加变色,时间显示不用空3个空格,报警记录仍然1页3条,
首页没有总消耗功率,充放电状态图标用原版
Program Size: Code=107980 RO-data=7552 RW-data=1556 ZI-data=16708
107980+7552+1556 = 117088 =114K
////////////////////因为有时候会触发重启,所以考虑将长按按钮才置电源高,改为读Flash标志
Flash标志存于第128+2K,第128K存OTA信息了,第128+4K存原有程序了
4.2
//预充逻辑更新:
·开机预充:
开机保持MOS关闭,预充1s(可设)后释放,并打开MOS
·开放电MOS前开预充:
当放电MOS此前关闭,要打开时,先维持不要打开,预充8s(可设)后释放,并打开放电MOS
·浪涌短路改为普通保护,会亮灯会提醒会上报事件
4.6
//屏幕点亮不能退出休眠?
因为加了休眠判断,现已删去
//退出休眠后4G不上线?
因为休眠时加了4G休眠,但退出休眠时没有加退出4G休眠
//150A参数
70 1e 6e d0 62 a8 87 91 af 78 64 bb 18 1b 10 1a 50 64 46 ec f6 64 46 ec f6 8f 3c 37 00 05 41 3c ec f1 9b 9b 01 01 1e 89 96 05 3c 00
30 02 1c 02 cc 01 e5 01 01 01 a0 00 04 00 46 41 ec f1 46 41 ec f1 60 60 10 00 00 00 00 00 00 00 de 0d f0 0a 2b 02 e0 01 8c 8c 37 05 3c f1 41 f1 41 f1 5f f1 5f f1
4.7
//屏幕显示时间要timecount-8h
中国固定T8时区
//OCV校准用3A不对,需要改0.5A
//看门狗时间放长到16s,并把喂狗提前
//因为是长按,软件重启时要保持MOS控制不能启动预充
//使用强制关闭欠压的倒计时文本,显示4G当前状态
4.13
//增加平台执行电流校准
4.14
//增加4G掉线定期检查,防止在线后却没有实际发送任何内容
4.15
//对检查订阅的步骤,增加超时计算,连续3次无响应则执行下一步
//若在回复处理函数中持续累积次数,在4G模块不回复时会无法继续累加
4.16
//加热继电器改为可选开关功能,默认不带,不影响充电低温保护直接执行
4.20
//4G模块通信太快会导致4G异常
4.22
//上位机改SN号后,上报的主题里的SN号没有更新——要在修改SN号之后更新SN号的字符串,并且使可继续通信
//屏幕在4G无回复时显示Null
//单芯电压纠正
·上报4G/蓝牙/WIFI的单芯电压,若超出5119mV,更正显示负值
·逆变器协议里,需要单芯电压的也改为输出cellVol[]
·汇总数据过程,仍保留原值,但收到并处理时,存入另外的变量里
//保护记录中,单芯电压范围0~4095
//OCV校准,校准检查+静置电流改0.5A+等待时间改4h+关机时间不算
//增加更多4G报文显示在屏幕,囊括connect
//优化LTE_lastFlag为LTE_ResendDelay,延后时间改2s,并且把ERROR里的计数改/2
4.27
//OCV时间存储恢复
//并机数据更新函数,放计算SOC后面
//Pylon电总协议,温度补充
4.28
//并机计算负值电芯电压,[0]的值没赋值,恢复
//SDWA屏幕显示,不能显示负值,小于的直接显示0
5.6
//参数检查
把paraMem.fcc_改为paraMem.soc100_
paraMem.ctrl_disable只控制bit0/1/2
//写SN号时更新函数调用错了,已改
5.8
//发现屏幕写逆变器值,有3个重启后会变回去
·因为和告警电流的联系,导致一直更新
·其实不需要将paraMem写入EEPROM,问题也是出在这里,已经超出了1次写的最大长度
·staPack.bits.eepromUpdate保留,可后续用于显示写EEPROM错误,目前只显示写AFE的EEPROM错误
5.12
//4G报文优化:
屏幕显示rssi值和network值
CGREG收到2,不算异常,等待3s后才再次询问
CFUN=0/1之后,要等3s后询问
CSQ回复ERROR,持续20s就重启4G模块(因为这个不太应该的)
屏幕显示报文,有的长度太长改短一点
5.12
//4G报文优化:
屏幕显示rssi值和network值
CGREG收到2,不算异常,等待3s后才再次询问
CFUN=0/1之后,要等3s后询问
CSQ回复ERROR,持续20s就重启4G模块(因为这个不太应该的)
屏幕显示报文,有的长度太长改短一点
network改net
5.13
//自动分配启动条件优化
当从机回复轮询,收到的字节数比正常多,记录1次
//协议改为24个,其中6个实际可用
//移植陶晶驰屏幕相关
//自动分配地址优化
1.自动分配地址,时序从0.5s改1s发1次,毕竟是写地址要更新的
2.自动分配地址启动条件,增加轮询收到回复,但无法解析,超过10次执行自动分配
//屏幕的24个协议,序号对应
1."Sol-Ark", 2."GoodWe", 30."Megarevo", 12."Pylon",
11."Deye", 7."MUST", 37."solis", 3."Growatt",
4."Aiswei", 35."Afore", 27."Victron", 6."Sorotec",
5."SMA", *."Sunways", 23."Luxpower", 24."Schneider",
*"AlpSolarr", 13."SRNE", 14."Voltronic", 32."COSUPER",
17."SMK", 31."SAKO", 18."SNADI", 21."invt",
//屏幕显示充放电剩余时间,最少是5*0.1A而不是500*0.1A
预计更新:
//屏幕显示4G当前状态,改一个新值存储,不占用原值
//屏幕改陶晶驰相关
//屏幕协议用新版本
//4G模块无回复时是否还要加延时?
//未来优化:
·CAN错误中断加上
·4G当前状态,显示在上位机