Files
2026-08-25 17:30:40 +08:00

1938 lines
49 KiB
C

/**
******************************************************************************
* @file RS485_Modbus.c
* @author
* @version
* @date
* @brief
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
#include "string.h"
#include "rtc.h"
#include "soe.h"
#define MODBUS_UART USART1
#define MODBUS_UART_SendMulByte USART1_SendMulByte //发送多字节
#define MODBUS_UART_TIM TIM4
#define MODBUS_UART_IT_RX_DISABLE USART_ITConfig(USART1, USART_IT_RXNE, DISABLE)
#define MODBUS_UART_IT_RX_ENABLE USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)
#define MODBUS_MEM_PT (uint8_t *)&bmsMem.vCell[0] //通讯内存开始地址
#define MODBUS_VersionMEM_PT (uint8_t *)&VersionMem.Hardware[0]//上位机请求版本号通讯内存开始地址
#define MODBUS_TimeMEM_PT (uint8_t *)&calendar_WRITE.sec
#define MODBUS_ParaMEM_PT (uint8_t *)&paraMem.act_bal_startV//上位机弹窗读写配置
#define MODBUS_CtrlMEM_PT (uint8_t *)&CTRL_Order //上位机控制MOS关闭(一次性的,永久的在paraMem里)
#define MODBUS_MON_CNT paraMem.PACK_NUM==1?MODBUS_MON_CNT1:MODBUS_MON_CNT2
#define MODBUS_MON_CNT1 3000 //3000*10ms = 30s(因为轮询速度加快,该时间也加快)
#define MODBUS_MON_CNT2 paraMem.PACK_NUM*150+200 //最大20对应32s,最小2对应5s
#define MODBUS_BUF_LEN 220
#define MODBUS_ASSIGN_INADDR AddrMax+1
uint8_t modbusBuf[MODBUS_BUF_LEN];
uint8_t modbusBufIndex;
uint16_t modbusMoniCount;
uint8_t modbusF03RxFlg; //读数据接收正确标记
uint8_t modbusF10RxFlg; //写数据接收正确标记
uint8_t modbusFaaRxFlg; //CADC零点校准数据接收正确标记
uint8_t modbusFbbRxFlg; //CADC增益校准数据接收正确标记
uint8_t modbusFddRxFlg; //读取记录 接收正确标记
uint8_t modbusFeeRxFlg; //清除记录 接收正确标记
uint8_t modbusFf1RxFlg; //选择协议 接收正确标记
uint8_t modbusCurF03RxFlag;//主机收到03回复 接收正确标记
uint8_t modbusCurF10RxFlag;//主机收到10回复 接收正确标记
uint8_t modbusCurStatus; //主机当前执行的功能——每当变化,会初始化收发状态 0:刚开机/刚初始化过 1:基础轮询 2:自动分配地址 3:屏幕持续读从机 4:屏幕写从机地址 5:上位机持续读从机
uint8_t modbusCurDev; //主机当前的轮询对象
uint8_t modbusCurSta; //主机当前的收发状态
uint8_t modbusCurLastAddr; //最后一个能通信到的并机地址
uint8_t chg_forbidFlg; //禁充标志,SOC大于100%的时候启用
uint8_t dsg_forbidFlg; //禁放标志,SOC小于10%的时候启用
uint8_t chg_forceFlg; //强充标志,SOC小于10%的时候启用
uint16_t RequestFlag; //充放电允许位
uint8_t chg_curlimitFlg; //充电限流(固定40A)的标志,出现总体过压或单体过压的时候启用
uint8_t ReAskFlag; //主机轮询遇报警,再次询问的标志
uint8_t sleepOFFcount; //3.4网口的主从机轮询,可能有个时间差
uint8_t sdwa_WrAddr; //屏幕通过主机写从机地址的值
uint8_t sdwa_WrAddr_Flg; //屏幕通过主机写从机地址的标志 0不在写 1尝试中 2成功 3失败
uint8_t sdwa_WrAddr_Failcount; //连续写地址失败的计数
#if Addr_SetAuto
uint8_t assignAddr_relay; //主机开机后延时2s再开始通信,等待地址变化
uint8_t assignAddr_State; //自动分配地址的状态 0未开始 1进行中 2结束
uint8_t assignAddr_Step; //自动分配地址当前步骤
uint8_t assignAddr_Failcount; //连续分配地址失败的计数
uint8_t assignAddr_485num; //分配地址后的在线个数,若存在从机(此值>2)才下发队列标志
uint16_t assignAddr_random; //主机完成分配后生成的随机数(AddrMax+1~65535),用来识别队列
uint8_t assignAddr_WrIndex_Flg;//主机执行下发队列标志数的标志
uint8_t assign_ready_count1;//主机再次分配的启用条件计数,1.最后一个在线从机的序号不等于总在线个数,说明中间有地址缺失,发现2次就执行
uint8_t assign_ready_count2;//主机再次分配的启用条件计数,2.始终找不到任何从机,发现3次就执行
uint8_t assign_ready_count3;//主机再次分配的启用条件计数,3.从机的队列标志和主机的不同,发现2次就执行
uint8_t assign_ready_count4;//主机再次分配的启用条件计数,4.在线总个数外的地址有不符合的乱码
uint8_t assign_ready_count5;//主机再次分配的启用条件计数,5.在线总个数内的地址有回复但字节数超出
#endif
uint8_t AnswerFlag1; //轮询在线总个数外的地址有不符合的乱码
uint8_t AnswerFlag2; //轮询在线总个数内的地址有回复但字节数超出
uint8_t PollStop_flag; //主机暂停发送轮询的标志位。在1口,除了7E/03/10/控制MOS的/升级的,其他报文都会恢复不再暂停。在3.4口,
uint8_t PollStop_count; //主机暂停发送轮询的倒计时,再次收到则清零,30s
const uint16_t CRC16Table[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(uint8_t *pdata, uint16_t len)
{
uint16_t i;
BYTE2 CRCData;
CRCData.b16=0XFFFF;
for(i=0;i<len;i++){
CRCData.b16 = CRC16Table[CRCData.b8[0]^ *pdata++] ^ CRCData.b8[1];
}
return CRCData;
}
/****************************************
**** 按顺序读记录 *****
** 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)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
EEPROM_RdMulByte(EE_SOE_NUM,&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(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/****************************************
**** 选择协议 *****
** M: Addr f1 00 num 00 10 CRCL H **
** Succ: Addr f1 nameLen name[0]~name[15] CRCL H **
** Fail: Addr f1 0 CRCL CRCH **
*****************************************/
void UART1_ProtocolSwitch(void)
{
uint8_t i;
BYTE2 crc16;
uint8_t num;
uint8_t namelen;
char name[16];
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
num = modbusBuf[3];
if((int)num>=0 && num<=protocolNum)
{
if(num==0) //2026.2.10增加读协议
{
num = protocol;
}
protocolSwitchFail = 0; //存在该协议,选中,并返回协议名称
protocol = num;
//将选中的字符串复制到一个新的字符数组中,获取字符串的长度
strcpy(name,protocolStrings[num-1]);
namelen = strlen(name);
//生成输出报文
modbusBuf[2] = namelen;
for(i=0;i<namelen;i++)
{
modbusBuf[i+3] = name[i];
}
crc16 = CRC16_Cal(modbusBuf, namelen+3);
modbusBuf[namelen+3] = crc16.b8[0];
modbusBuf[namelen+4] = crc16.b8[1];
}
else
{
protocolSwitchFail = 1; //不存在
}
modbusFf1RxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
}
//接收广播MOS控制标志
void MODBUS_CtrlMOS_Rx(uint8_t *mem)
{
BYTE2 crc16;
uint8_t len;
len = modbusBuf[6];
//CRC判断
crc16 = CRC16_Cal(modbusBuf, len+7);
if( (modbusBuf[len+8] == crc16.b8[1]) && (modbusBuf[len+7] == crc16.b8[0]) )
{
memcpy(mem, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_Init(); //因为是广播,所以不考虑回复
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
//UART中断接收数据
void MODBUS_IT_Receive(void)
{
modbusMoniCount = MODBUS_MON_CNT;
//接收数据
modbusBuf[modbusBufIndex] = USART_ReceiveData(MODBUS_UART);
modbusBufIndex++;
//防止数组溢出
if(modbusBufIndex >= MODBUS_BUF_LEN )
{
modbusBufIndex = 0;
}
//每次接收数据中断清除定时器计数器并启动计数器
//如定时器溢出中断,表示一帧数据接收完成
TIM_SetCounter(MODBUS_UART_TIM, 0);
TIM_Cmd(MODBUS_UART_TIM, ENABLE);
}
//查询发送数据
void MODBUS_IQ_Transmit(void)
{
BYTE2 crc16;
if(modbusF03RxFlg == 1)
{
modbusF03RxFlg = 0;
modbusBufIndex = 0;
MODBUS_UART_SendMulByte(modbusBuf, (5 + modbusBuf[2]) );
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusF10RxFlg == 1)
{
modbusF10RxFlg = 0;
modbusBufIndex = 0;
if(modbusBuf[1] == 0x10) //写参数需要存入flash
{
//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(MEMORY_UpdateAFE() ==0) //更新AFE EEPROM内容
{
staPack.bits.eepromUpdate = 0;
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
else
{
staPack.bits.eepromUpdate = 1;
}
}
else
{
staPack.bits.flashUpdate= 1;
}
bmsMem.packStatus = staPack.byte;
}
else if(modbusBuf[1] == 0xf4)
{
if((MEMORY_UpdateFlash(FLASH_DATA_A_BASE) == 0) && (MEMORY_UpdateFlash(FLASH_DATA_B_BASE) == 0))
{
staPack.bits.flashUpdate= 0;
if(MEMORY_UpdateAFE() ==0)
{
staPack.bits.eepromUpdate = 0;
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
else
{
staPack.bits.eepromUpdate = 1;
}
}
else
{
staPack.bits.flashUpdate= 1;
}
bmsMem.packStatus = staPack.byte;
}
else if(modbusBuf[1] == 0x56) //写SN号需要写入EEPROM //写4G通信凭证需要存入flash
{
uint8_t i;
uint8_t *data;
uint8_t tmpWr[16];
if(modbusBuf[3] == 0x00) //对应硬件版本号的地址
{
MODBUS_UART_SendMulByte(modbusBuf, 8);
EEPROM_WrMulByte(EE_Hardware,VersionMem.Hardware);
delay_ms(5);
//填充HardwareVersion
Refresh_HardwareVersion();
}
else if(modbusBuf[3] == 0x01) //对应屏幕号的地址
{
MODBUS_UART_SendMulByte(modbusBuf, 8);
EEPROM_WrMulByte(EE_Screen,&VersionMem.Screen);
delay_ms(5);
//填充ScreenVersion
Refresh_ScreenVersion();
}
else if(modbusBuf[3] == 0x04) //对应BMS的SN号的地址
{
data = (uint8_t *)&VersionMem.BMS_SN[0];
for(i=0;i<9+1;i++)
{
tmpWr[i] = *data;
data++;
}
MODBUS_UART_SendMulByte(modbusBuf, 8);
EEPROM_WrMulByte(EE_BMS_SN,tmpWr);
delay_ms(5);
/*三选一模块*/
#if BLE_Conn
BLE_WriteName(); //锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
#endif
#if LTE_Conn
LTE_UNSUB_Flag = 1; //因为订阅主题变动,需要对此前的订阅进行取消绑定
EEPROM_WrMulByte(EE_UNSUB,&LTE_UNSUB_Flag);
delay_ms(5);
if(LTE_LINK_flag == 1) //若原来已连接4G服务器,断开并重新正确连接
{
LTE_LINK_flag = 0; //已连接标志清零
MQTT_RST_flag = 1; //断开再连接MQTT服务器
MQTT_RST_step = 0;
MQTT_START_flag = 0;
MQTT_READY_flag = 0;
MQTT_timed_count = 0; //清零定时上报倒计时
LTE_OTA_Flag = 0; //OTA升级标志清零
}
LTE_4G_Domain_ChangeSN(); //更新4G通信凭证
#endif
}
else if(modbusBuf[3] == 0x09) //对应PACK的SN号的地址
{
data = (uint8_t *)&VersionMem.PACK_SN[0];
for(i=0;i<15+1;i++)
{
tmpWr[i] = *data;
data++;
}
MODBUS_UART_SendMulByte(modbusBuf, 8);
EEPROM_WrMulByte(EE_PACK_SN,tmpWr);
delay_ms(5);
//填充PACK_SN
Refresh_PACK_SN();
}
#if LTE_Conn
else if(modbusBuf[3] == 0x20) //对应4G通信凭证的起始地址
{
if(LTE_LINK_flag == 1)
{
LTE_LINK_flag = 0; //已连接标志清零
MQTT_RST_flag = 1; //断开再连接MQTT服务器
MQTT_RST_step = 0;
MQTT_START_flag = 0;
MQTT_READY_flag = 0;
MQTT_timed_count = 0; //清零定时上报倒计时
LTE_OTA_Flag = 0; //OTA升级标志清零
}
//计算CRC校验值
static uint8_t temp[HOSTMEM_LEN];
memcpy(temp, &VersionMem.host[0], HOSTMEM_LEN);
VersionMem.hostAll_crc = CRC8_Cal(&temp[0], 153);
//更新Flash
if((MEMORY_UpdateFlash(FLASH_DATA_A_BASE) == 0) && (MEMORY_UpdateFlash(FLASH_DATA_B_BASE) == 0))
{
staPack.bits.flashUpdate = 0;
}
else
{
staPack.bits.flashUpdate = 1;
}
bmsMem.packStatus = staPack.byte;
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
#endif
}
else
{
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusFaaRxFlg == 1)
{
modbusFaaRxFlg = 0;
modbusBufIndex = 0;
if(cali.flagZeroCaliFail ==0) //ok
{
// 01 AA 5A A5 03 04 CRCL H
modbusBuf[0] = bmsMem.E2_485Addr;
modbusBuf[1] = 0xaa;
modbusBuf[2] = 0x5a;
modbusBuf[3] = 0xa5;
modbusBuf[4] = 0x03;
modbusBuf[5] = 0x04;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
else //fail
{
// 01 AA 5A A5 05 06 CRCL H
modbusBuf[0] = bmsMem.E2_485Addr;
modbusBuf[1] = 0xaa;
modbusBuf[2] = 0x5a;
modbusBuf[3] = 0xa5;
modbusBuf[4] = 0x05;
modbusBuf[5] = 0x06;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusFbbRxFlg == 1)
{
modbusFbbRxFlg = 0;
modbusBufIndex = 0;
if(cali.flagGainCaliFail ==0) //ok
{
// 01 AA 5A A5 03 04 CRCL H
modbusBuf[0] = bmsMem.E2_485Addr;
modbusBuf[1] = 0xbb;
modbusBuf[2] = 0x5b;
modbusBuf[3] = 0xb5;
modbusBuf[4] = 0x03;
modbusBuf[5] = 0x04;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
else //fail
{
// 01 AA 5A A5 05 06 CRCL H
modbusBuf[0] = bmsMem.E2_485Addr;
modbusBuf[1] = 0xbb;
modbusBuf[2] = 0x5b;
modbusBuf[3] = 0xb5;
modbusBuf[4] = 0x05;
modbusBuf[5] = 0x06;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
}
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusFddRxFlg == 1) //按顺序读记录
{
uint8_t adrh,adrl;
modbusFddRxFlg = 0;
modbusBufIndex = 0;
adrh = modbusBuf[2];
adrl = modbusBuf[3];
EEPROM_RdMulByte(EE_SOE, &modbusBuf[4]);
//read extend cell17~20 if new format(0xA6)
if(modbusBuf[4+63] == 0xA6)
{
uint16_t rec_addr = ((uint16_t)modbusBuf[2] << 8) | modbusBuf[3];
uint16_t ext_addr = 0x2900 + (rec_addr - 0x1000) * 6 / 64;
adrh = (ext_addr>>8) & 0xff;
adrl = ext_addr & 0xff;
EEPROM_RdMulByte(EE_SOE_EXT, &modbusBuf[4+64]);
}
else
{
memset(&modbusBuf[4+64], 0, 6);
}
crc16 = CRC16_Cal(modbusBuf, 70+4);
modbusBuf[70+4] = crc16.b8[0];
modbusBuf[70+5] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 70+6);
MODBUS_UART_IT_RX_ENABLE;
}
else if(modbusFeeRxFlg == 1) //清除记录
{
uint16_t i;
uint16_t pc; //待写入地址
uint8_t adrh,adrl;
uint8_t wrBuf[64];
if(soe.num != 0) //存在记录,才去删除记录
{
//先清空具体内容
pc = 0x1000;
for(i=0;i<64;i++)
{
wrBuf[i] = 0xff;
}
for(i=0;i<100;i++)
{
adrh = (pc>>8) & 0xff;
adrl = pc & 0xff;
EEPROM_WrMulByte(EE_SOE,wrBuf);
delay_ms(10);
pc += 0x40;
}
//clear extend cell17~20 area
pc = 0x2900;
{
uint8_t extClear[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
for(i=0;i<100;i++)
{
adrh = (pc>>8) & 0xff;
adrl = pc & 0xff;
EEPROM_WrMulByte(EE_CLEAR_EXT,extClear);
delay_ms(10);
pc += 6;
}
}
//再清空统计数据
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(EE_SOE_INF,wrBuf);
delay_ms(10);
}
modbusFeeRxFlg = 0;
modbusBufIndex = 0;
MODBUS_UART_SendMulByte(modbusBuf, 8 );
MODBUS_UART_IT_RX_ENABLE;
scr_RdRecord_Flg = 1;
}
else if(modbusFf1RxFlg == 1) //选择通信协议
{
modbusFf1RxFlg = 0;
modbusBufIndex = 0;
if(protocolSwitchFail == 0) //锟斤拷锟截讹拷应协锟斤拷
{
MODBUS_UART_SendMulByte(modbusBuf, (5 + modbusBuf[2]) );
EEPROM_WrMulByte(EE_PROTOCOL,&protocol);
delay_ms(5);
uf_CAN1_Init();//CAN的波特率更新
//SCR_DispProcotol(); //屏幕显示更新
}
else
{
//失败的回复
modbusBuf[0] = bmsMem.E2_485Addr;
modbusBuf[1] = 0xf1;
modbusBuf[2] = 0x00;
crc16 = CRC16_Cal(modbusBuf, 3);
modbusBuf[3] = crc16.b8[0];
modbusBuf[4] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 5 );
}
MODBUS_UART_IT_RX_ENABLE;
}
}
//接收一帧数据,帧间断小于x ms仍然认为是一帧
//定时器中断里进行接收数据解析并准备回送数据
void MODBUS_IT_TIMUpdate(void)
{
if(bmsMem.E2_485Addr == 1 )//485主机模式
{
if(modbusBufIndex > 2)
{
//要休眠,却还收到了通信,可能是有时间差,多确认几次再退出
if(sleep_flag == 1)
{
sleepOFFcount++;
}
else
{
sleepOFFcount=0;
}
//网口3.4确定有通讯,退出休眠且更新计时起点
if(sleepOFFcount >= 5)
{
sleep_flag = 0;
sleepOFFcount=0;
SLEEP_Refresh();
SLEEP2_Refresh();
}
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);
}
/*V3上位机*/
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x33) ) //上位机读数据处理
{
PollStop_flag = 1; //3.4口兼容V3
PollStop_count = 0;
ConfigData_Index = 1; //主机看从机值更新回1
scr_RdData_Index = 1;
MODBUS_F03_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x55) ) //上位机读取版本号
{
MODBUS_F03_Rx(MODBUS_VersionMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x56) ) //上位机写硬件版本号/屏幕号/SN号
{
MODBUS_F10_Rx(MODBUS_VersionMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x66) ) //上位机写时间
{
if(LSEErrFlag!=1) MODBUS_F10_Rx(MODBUS_TimeMEM_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 if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0Xdd) ) //读取记录
{
UART1_ReadRecord();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xee) ) //清除记录
{
UART1_ClearRecord();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf1) ) //F1 选择逆变器协议
{
UART1_ProtocolSwitch();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf2) ) //F2 控制MOS强制关闭
{
MODBUS_F10_Rx(MODBUS_CtrlMEM_PT);
}
else if((modbusBuf[0] == 0xff) && (modbusBuf[1] == 0xf2) ) // 广播控制MOS强制关闭
{
MODBUS_CtrlMOS_Rx(MODBUS_CtrlMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf3) ) //F3 弹窗读参数
{
MODBUS_F03_Rx(MODBUS_ParaMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf4) ) //F4 弹窗写参数
{
MODBUS_F10_Rx(MODBUS_ParaMEM_PT);
}
//上位机持续询问指定从机数据
else if((ConfigData_Index>=2) && (ConfigData_Index<=paraMem.PACK_NUM))
{
/*主机收集[上位机]指定从机数据*/
if((modbusBuf[0] == ConfigData_Index) && (modbusBuf[1] == 0x03) ) //从机返回的读数据处理
{
MODBUS_MASTER_F03_Rx();
}
#if Addr_SetAuto
/*自动分配地址 的回复*/
else if((modbusBuf[0] == MODBUS_ASSIGN_INADDR) && (modbusBuf[1] == 0x10) ) //写数据回复的处理
{
MODBUS_MASTER_F10_Rx();
}
#endif
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
//屏幕查看指定从机数据
else if((scr_RdData_Index>=2) && (scr_RdData_Index<=paraMem.PACK_NUM))
{
/*主机收集[屏幕]指定从机数据*/
if((modbusBuf[0] == scr_RdData_Index) && (modbusBuf[1] == 0x03) ) //单独请求从机,返回的读数据处理
{
MODBUS_MASTER_F03_Rx();
}
else if((modbusBuf[0] == scr_RdData_Index) && (modbusBuf[1] == 0x10) ) //从机对修改地址成功的回复
{
MODBUS_MASTER_F10_Rx();
}
#if Addr_SetAuto
/*自动分配地址 的回复*/
else if((modbusBuf[0] == MODBUS_ASSIGN_INADDR) && (modbusBuf[1] == 0x10) ) //写数据回复的处理
{
MODBUS_MASTER_F10_Rx();
}
#endif
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
//正常轮询
else
{
/*主机轮询*/
if((modbusBuf[0] == modbusCurDev) && (modbusBuf[1] == 0x03) ) //从机返回的读数据处理
{
MODBUS_MASTER_F03_Rx();
}
#if Addr_SetAuto
/*自动分配地址 的回复*/
else if((modbusBuf[0] == MODBUS_ASSIGN_INADDR) && (modbusBuf[1] == 0x10) ) //写数据回复的处理
{
MODBUS_MASTER_F10_Rx();
}
else
{
//轮询在询问但回复是乱码,作为自动分配启动条件
if((paraMem.addr_FREE_Flg == 0) && (modbusCurStatus == 1) && (modbusCurDev >= 2) && (modbusCurDev <= bmsMem.E2_485Snum) && (modbusBufIndex > 10))
{
AnswerFlag2 = 1;
}
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
#else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
#endif
}
}
}
else //485从机
{
if(modbusBufIndex > 2)
{
//要休眠,却还收到了通信,可能是有时间差,多确认几次再退出
if(sleep_flag == 1)
{
sleepOFFcount++;
}
else
{
sleepOFFcount=0;
}
//网口3.4确定有通讯,退出休眠且更新计时起点
if(sleepOFFcount >= 5)
{
sleep_flag = 0;
sleepOFFcount=0;
SLEEP_Refresh();
SLEEP2_Refresh();
}
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);
}
/*V3上位机*/
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x33) ) //上位机读数据处理
{
MODBUS_F03_Rx(MODBUS_MEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x55) ) //上位机读取版本号
{
MODBUS_F03_Rx(MODBUS_VersionMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x56) ) //上位机写硬件版本号/屏幕号/SN号
{
MODBUS_F10_Rx(MODBUS_VersionMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0x66) ) //上位机写时间
{
if(LSEErrFlag!=1) MODBUS_F10_Rx(MODBUS_TimeMEM_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 if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0Xdd) ) //读取记录
{
UART1_ReadRecord();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xee) ) //清除记录
{
UART1_ClearRecord();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf1) ) //F1 选择逆变器协议
{
UART1_ProtocolSwitch();
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf2) ) //F2 控制MOS强制关闭
{
MODBUS_F10_Rx(MODBUS_CtrlMEM_PT);
}
else if((modbusBuf[0] == 0xff) && (modbusBuf[1] == 0xf2) ) // 广播控制MOS强制关闭
{
MODBUS_CtrlMOS_Rx(MODBUS_CtrlMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf3) ) //F3 弹窗读参数
{
MODBUS_F03_Rx(MODBUS_ParaMEM_PT);
}
else if((modbusBuf[0] == bmsMem.E2_485Addr) && (modbusBuf[1] == 0xf4) ) //F4 弹窗写参数
{
MODBUS_F10_Rx(MODBUS_ParaMEM_PT);
}
#if Addr_SetAuto
else if((modbusBuf[0] == 0xff) && (modbusBuf[1] == 0x10) ) //主机广播写队列标志位
{
MODBUS_WrIndex_Rx();
}
#endif
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
}
}
/****************************************
**** MODBUS F03 读报文 *****
** M: 01 03 00 addr 00 lenth CRCL H **
** S: 01 03 2*lenth DATA0-N CRCL H **
*****************************************/
void MODBUS_F03_Rx(uint8_t *mem)
{
uint8_t i;
BYTE2 crc16;
uint16_t adr;
uint8_t len;
uint8_t *data;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
adr = (uint16_t)modbusBuf[3] <<1;
len = modbusBuf[5] <<1;
modbusBuf[2] = len;
//高字节在前
data = mem+adr;
for(i=0;i<len;)
{
modbusBuf[4+i] = *data;
data++;
modbusBuf[3+i] = *data;
data++;
i++;
i++;
}
//低字节在前
// memcpy(&modbusBuf[3], mem+adr, len);
crc16 = CRC16_Cal(modbusBuf, len+3);
modbusBuf[len+3] = crc16.b8[0];
modbusBuf[len+4] = crc16.b8[1];
modbusF03RxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/***********************************************
**** MBUS_PUTN 写多个寄存器报文解析 *****
** 01 10 ADRH L 0 LENTH 2LETH DATAn CRCL H **
** 01 10 ADRH L 0 LENTH CRCL CRCH **
************************************************/
//mem point to bmsMem
void MODBUS_F10_Rx(uint8_t *mem)
{
BYTE2 crc16;
uint16_t adr;
uint8_t len;
adr = (uint16_t)modbusBuf[3] <<1;
len = modbusBuf[6];
//CRC判断
crc16 = CRC16_Cal(modbusBuf, len+7);
if( (modbusBuf[len+8] == crc16.b8[1]) && (modbusBuf[len+7] == crc16.b8[0]) )
{
uint8_t i;
uint8_t *data;
uint8_t tmpWr[16];
//上位机写BMS_SN,固定9+1字节,需要检测CRC8
if((modbusBuf[1] == 0x56) && (modbusBuf[3] == 0x04))
{
data = (uint8_t *)&modbusBuf[7];
for(i=0;i<9;i++)
{
tmpWr[i] = *data;
data++;
}
tmpWr[9] = CRC8_Cal(&tmpWr[0],9);
if(tmpWr[9] == modbusBuf[7+9]) //crc校验正确
{
memcpy(mem+adr, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
modbusF10RxFlg = 1;
}
}
//上位机写PACK_SN,固定15+1字节,需要检测CRC8
else if((modbusBuf[1] == 0x56) && (modbusBuf[3] == 0x09))
{
data = (uint8_t *)&modbusBuf[7];
for(i=0;i<15;i++)
{
tmpWr[i] = *data;
data++;
}
tmpWr[15] = CRC8_Cal(&tmpWr[0],15);
if(tmpWr[15] == modbusBuf[7+15]) //crc校验正确
{
memcpy(mem+adr, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
modbusF10RxFlg = 1;
}
}
else
{
//上位机写SOC=0的补丁 Adr 10 00 4a 00 01 02 00 00 crc1 crc2
if((modbusBuf[1] == 0x10) && (modbusBuf[2] == 0x00) && (modbusBuf[3] == 0x4a) && (modbusBuf[7] == 0x00) && (modbusBuf[8] == 0x00))
{
//这个报文的说明是写SOC且写的是0,需要特殊处理
bmsMem.write_Soc = 0xAA;
}
//上位机写循环次数,把累积容量也清空 Adr 10 00 25 00 01 02 numL numH crc1 crc2
else if((modbusBuf[1] == 0x10) && (modbusBuf[2] == 0x00) && (modbusBuf[3] == 0x25))
{
cumuliCapClear_flag = 1;
memcpy(mem+adr, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
}
//上位机写屏幕版本号,注意只有1字节
else if((modbusBuf[1] == 0x56) && (modbusBuf[3] == 0x01))
{
adr += 1;
memcpy(mem+adr, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
}
else
{
uint16_t sc_mode_old = paraMem.sc_mode;
memcpy(mem+adr, &modbusBuf[7], len); //将modbusBuf数组数据COPY到置定结构体,注意大小端
if(paraMem.sc_mode == 0) //被意外置0,恢复
{
paraMem.sc_mode = sc_mode_old;
}
}
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
modbusF10RxFlg = 1;
}
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/****************************************
**** 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 MODBUS_Faa_Rx(uint8_t *mem)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
cali.cmdZero = 1;
// if( (modbusBuf[2] == 0x5a) && (modbusBuf[3] == 0xa5) && (modbusBuf[4] == 0x01) && (modbusBuf[5] == 0x02) )
// {
// cali.cmdZero = 1;
// }
// crc16 = CRC16_Cal(modbusBuf, 6);
// modbusBuf[6] = crc16.b8[0];
// modbusBuf[7] = crc16.b8[1];
// modbusFaaRxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
/****************************************
**** 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 MODBUS_Fbb_Rx(uint8_t *mem)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
cali.current = modbusBuf[2]<<24 | modbusBuf[3]<<16 | modbusBuf[4]<<8 | modbusBuf[5];
cali.cmdGain = 1;
// crc16 = CRC16_Cal(modbusBuf, 6);
// modbusBuf[6] = crc16.b8[0];
// modbusBuf[7] = crc16.b8[1];
// modbusFbbRxFlg = 1;
MODBUS_UART_IT_RX_DISABLE;
}
else
{
MODBUS_Init(); //没加这个时,换地址通讯连接不上,待监控时间到重新初始化之后才恢复连接
}
}
//初始化MODBUS通讯
void MODBUS_Init(void)
{
modbusCurSta = 0; //主机当前采集状态
modbusCurDev = 2; //主机当前采集设备,从从机2开始
modbusCurF03RxFlag = 0;
modbusCurF10RxFlag = 0;
modbusBufIndex = 0;
modbusF03RxFlg = 0;
modbusF10RxFlg = 0;
modbusFaaRxFlg = 0;
modbusFbbRxFlg = 0;
modbusFddRxFlg = 0;
modbusFeeRxFlg = 0;
modbusFf1RxFlg = 0;
modbusMoniCount = MODBUS_MON_CNT;
uf_UART1_Init(9600);
}
//初始化MODBUS轮询数据
void MODBUS_Poll_Init(void)
{
canMem[0].status_byte1 = bmsMem.can_status_byte1;
canMem[0].status_byte2 = bmsMem.can_status_byte2;
canMem[0].status_byte3 = bmsMem.can_status_byte3;
canMem[0].status_byte4 = bmsMem.can_status_byte4;
canMem[0].soc = bmsMem.soc;
canMem[0].soh = bmsMem.soh;
canMem[0].cur = (int16_t)bmsMem.can_cur;
canMem[0].temp = (int16_t)bmsMem.can_temp;
canMem[0].VolMax = bmsMem.can_VolMax;
canMem[0].VolMin = bmsMem.can_VolMin;
canMem[0].VolMaxIndex = bmsMem.can_VolMaxIndex;
canMem[0].VolMinIndex = bmsMem.can_VolMinIndex;
canMem[0].TempMax = bmsMem.can_TempMax;
canMem[0].TempMin = bmsMem.can_TempMin;
canMem[0].TempMaxIndex = bmsMem.can_TempMaxIndex;
canMem[0].TempMinIndex = bmsMem.can_TempMinIndex;
//Wh版屏幕
canMem[0].cumuliCap = bmsMem.can_cumuliCap;
canMem[0].cycleCnt = bmsMem.can_cycleCnt;
//负值矫正
canMem[0].cellVoltageMax = (int16_t)(canMem[0].VolMax*32/5)*5/32;
canMem[0].cellVoltageMin = (int16_t)(canMem[0].VolMin*32/5)*5/32;
OnlineNum = 1;
bmsMem.E2_485Snum = 1;
#if Addr_SetAuto
assignAddr_485num = 1;
#endif
}
//一定时间没有接收到数据,初始化MODBUS通讯
void MODBUS_TIM_Moni(void)
{
modbusMoniCount--;
if(modbusMoniCount == 0)
{
MODBUS_Poll_Init();
MODBUS_Init();
}
}
/****************************************
**** MODBUS F03 读报文 *****
** M: 01 03 00 addr 00 lenth CRCL H **
** S: 01 03 2*lenth DATA0-N CRCL H **
*****************************************/
void MODBUS_MASTER_F03_Rx(void)
{
BYTE2 crc16;
uint8_t len;
len = modbusBuf[2];
//CRC判断
crc16 = CRC16_Cal(modbusBuf, len+3);
if( (modbusBuf[len+4] == crc16.b8[1]) && (modbusBuf[len+3] == crc16.b8[0]) )
{
modbusCurF03RxFlag = 1;
MODBUS_UART_IT_RX_DISABLE;
}
#if Addr_SetAuto
else
{
/*若轮询大于当前在线个数的地址时有不符合的乱码,记录为启动自动分配条件*/
if((paraMem.addr_FREE_Flg == 0) && (modbusCurStatus == 1) && (modbusCurDev > bmsMem.E2_485Snum) && (modbusBufIndex > 10))
{
AnswerFlag1 = 1;
}
}
/*若在CRC校验码之后还有报文,记录为启动自动分配条件*/
if((paraMem.addr_FREE_Flg == 0) && (modbusCurStatus == 1) && (modbusBuf[len+5] != 0) && (modbusBuf[len+6] != 0))
{
AnswerFlag2 = 1;
}
#endif
}
/***********************************************
**** MODBUS F10 写报文回复报文的解析 *****
** M: 01 10 ADRH L 0 LENTH 2LETH DATAn CRCL H **
** S: 01 10 ADRH L 0 LENTH CRCL CRCH **
************************************************/
void MODBUS_MASTER_F10_Rx(void)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 6);
if( (modbusBuf[7] == crc16.b8[1]) && (modbusBuf[6] == crc16.b8[0]) )
{
modbusCurF10RxFlag = 1;
MODBUS_UART_IT_RX_DISABLE;
}
}
//设为主机时,500MS调用1次
void MODBUS_MASTER_Polling_Tx(void)
{
uint8_t i;
uint16_t tmp;
BYTE2 crc16;
if(modbusCurStatus != 1)
{
modbusCurStatus = 1;
MODBUS_Init();
}
//主机接收上一帧的数据并处理
if(modbusCurSta == 1)
{
modbusCurSta = 0;
if(modbusCurF03RxFlag == 1) //收到数据且CRC校验正确
{
modbusCurF03RxFlag = 0;
canMem[modbusCurDev].status_byte1 = modbusBuf[3]<<8 | modbusBuf[4];
canMem[modbusCurDev].status_byte2 = modbusBuf[5]<<8 | modbusBuf[6];
canMem[modbusCurDev].status_byte3 = modbusBuf[7]<<8 | modbusBuf[8];
canMem[modbusCurDev].status_byte4 = modbusBuf[9]<<8 | modbusBuf[10];
canMem[modbusCurDev].soc = modbusBuf[12];
canMem[modbusCurDev].soh = modbusBuf[11];
canMem[modbusCurDev].cur = (int16_t)(modbusBuf[13]<<8 | modbusBuf[14]);
canMem[modbusCurDev].temp = (int16_t)(modbusBuf[15]<<8 | modbusBuf[16]);
canMem[modbusCurDev].VolMax = modbusBuf[17]<<8 | modbusBuf[18];
canMem[modbusCurDev].VolMin = modbusBuf[19]<<8 | modbusBuf[20];
canMem[modbusCurDev].VolMaxIndex = modbusBuf[21]<<8 | modbusBuf[22];
canMem[modbusCurDev].VolMinIndex = modbusBuf[23]<<8 | modbusBuf[24];
canMem[modbusCurDev].TempMax = modbusBuf[25]<<8 | modbusBuf[26];
canMem[modbusCurDev].TempMin = modbusBuf[27]<<8 | modbusBuf[28];
canMem[modbusCurDev].TempMaxIndex = modbusBuf[29]<<8 | modbusBuf[30];
canMem[modbusCurDev].TempMinIndex = modbusBuf[31]<<8 | modbusBuf[32];
//Wh版屏幕
canMem[modbusCurDev].cumuliCap = modbusBuf[35]<<8 | modbusBuf[36];
canMem[modbusCurDev].cycleCnt = modbusBuf[37]<<8 | modbusBuf[38];
//负值矫正
canMem[modbusCurDev].cellVoltageMax = (int16_t)(canMem[modbusCurDev].VolMax*32/5)*5/32;
canMem[modbusCurDev].cellVoltageMin = (int16_t)(canMem[modbusCurDev].VolMin*32/5)*5/32;
tmp = modbusBuf[33]<<8 | modbusBuf[34];
if(tmp > AddrMax) //队列标志的取值范围在AddrMax+1~65535
{
canMem[modbusCurDev].com = tmp; //赋值队列标志
}
else
{
canMem[modbusCurDev].com = 1; //接收数据OK
}
}
else //未收到回复
{
canMem[modbusCurDev].status_byte1 = 0;
canMem[modbusCurDev].status_byte2 = 0;
canMem[modbusCurDev].status_byte3 = 0;
canMem[modbusCurDev].status_byte4 = 0;
canMem[modbusCurDev].soc = 0;
canMem[modbusCurDev].soh = 0;
canMem[modbusCurDev].cur = 0;
canMem[modbusCurDev].temp = 0;
canMem[modbusCurDev].VolMax = 0;
canMem[modbusCurDev].VolMin = 0;
canMem[modbusCurDev].VolMaxIndex = 0;
canMem[modbusCurDev].VolMinIndex = 0;
canMem[modbusCurDev].TempMax = 0;
canMem[modbusCurDev].TempMin = 0;
canMem[modbusCurDev].TempMaxIndex = 0;
canMem[modbusCurDev].TempMinIndex = 0;
//Wh版屏幕
canMem[modbusCurDev].cumuliCap = 0;
canMem[modbusCurDev].cycleCnt = 0;
//负值矫正
canMem[modbusCurDev].cellVoltageMax = 0;
canMem[modbusCurDev].cellVoltageMin = 0;
canMem[modbusCurDev].com = 0;//接收数据FAIL
}
//无报警正常轮询下一个,有报警的话再询问一遍(为简单,对满充时的充电过压不报警不做区分,这里不考虑)
if(((canMem[modbusCurDev].status_byte1 & 0x067e) != 0) || ((canMem[modbusCurDev].status_byte2 & 0x00ff) != 0) || ((canMem[modbusCurDev].status_byte3 & 0x0008) != 0) || ((canMem[modbusCurDev].status_byte4 & 0x0f7f) != 0))
{
if(ReAskFlag == 0)
{
//再问一遍,此时modbusCurDev不变
ReAskFlag = 1;
}
else
{
ReAskFlag = 0;
modbusCurDev++;
}
}
else
{
ReAskFlag = 0;
modbusCurDev++;
}
//当轮询02~paraMem.PACK_NUM结束后,汇总数据、分析数据
if(modbusCurDev > paraMem.PACK_NUM)
{
/*收集最后一个在线BMS的地址*/
bmsMem.E2_485Snum = 1;
modbusCurLastAddr = 1;
for(i=2;i<=paraMem.PACK_NUM;i++)
{
if(canMem[i].com != 0) //引入队列标志位后的改动
{
bmsMem.E2_485Snum++;
modbusCurLastAddr = i;
}
}
#if Addr_SetAuto
/**** 分配地址启用条件判断 ****/
if(paraMem.addr_FREE_Flg == 0)
{
//1.当在线个数和最后一个在线的通信地址的值不同,说明中间有地址不在,需要启动分配地址
if(bmsMem.E2_485Snum != modbusCurLastAddr)
{
assign_ready_count1++;
if(assign_ready_count1 >= 2) //出现2次后触发,约20s
{
assignAddr_State = 1;
assign_ready_count1 = 0;
}
}
else
{
assign_ready_count1 = 0;
}
//2.当持续搜不到从机时,需要启动分配地址
if(bmsMem.E2_485Snum == 1)
{
assign_ready_count2++;
if(assign_ready_count2 >= AddrMax-paraMem.PACK_NUM+2) //防止在并联数很少的情况时,因找不到从机而反复启动分配
{
assignAddr_State = 1;
assign_ready_count2 = 0;
}
}
else
{
assign_ready_count2 = 0;
}
//3.当发现在线但 没有标志位/标志位和主机上次分配时不同时,需要启动分配地址
uint8_t IndexFlag = 0;//用来遍历可能在线的从机,判断是否有队列标志不同的在线从机
for(i=2;i<=paraMem.PACK_NUM;i++)
{
if((canMem[i].com != 0) && (canMem[i].com != canMem[1].com))
{
IndexFlag = 1;
}
}
if(IndexFlag == 1)
{
assign_ready_count3++;
if(assign_ready_count3 >= 2) //出现2次后触发,约20s
{
assignAddr_State = 1;
assign_ready_count3 = 0;
}
}
else
{
assign_ready_count3 = 0;
}
//4.当发现对在线总个数以外的地址有回复但是乱码时,需要启动分配地址
if(AnswerFlag1 == 1)
{
AnswerFlag1 = 0;
assign_ready_count4++;
if(assign_ready_count4 >= 2) //出现2次后触发,约20s
{
assignAddr_State = 1;
assign_ready_count4 = 0;
}
}
else
{
assign_ready_count4 = 0;
}
//5.当发现对在线总个数内的地址有回复但字节数超出时,需要启动分配地址
if(AnswerFlag2 == 1)
{
AnswerFlag2 = 0;
assign_ready_count5++;
if(assign_ready_count5 >= 10) //出现10次后触发,约100s
{
assignAddr_State = 1;
assign_ready_count5 = 0;
}
}
else
{
assign_ready_count5 = 0;
}
}
#endif
modbusCurDev = 2;
}
}
//主机发送对下一个从机的轮询
if(modbusCurSta == 0)
{
modbusBufIndex = 0;
modbusBuf[0] = modbusCurDev;
modbusBuf[1] = 0x03;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x4B;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x12; //增加最大最小值相关 //增加队列标志 //标志位变u16 //Wh版屏幕,增加循环次数和累计容量
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
modbusCurSta = 1;
modbusCurF03RxFlag = 0;
MODBUS_UART_IT_RX_ENABLE;
}
}
#if Addr_SetAuto
//主机先置高OUT初始化所有从机地址,再置低OUT让第一个从机为21,然后向地址21传正确地址
void MODBUS_AddrAssign_Tx(void)
{
uint8_t tmp[2];
BYTE2 crc16;
if(assignAddr_Step == 0)
{
IO2_OUTSet(); //OUT引脚置高,让从机1地址99,输出置高
assignAddr_Step = 1;
}
else if(assignAddr_Step == 1)
{
IO2_OUTReset(); //OUT引脚置低,让从机1地址AddrMax+1
assignAddr_Step = 2;
}
else if((assignAddr_Step >= 2) && (assignAddr_Step <= paraMem.PACK_NUM)) //分配地址,和并机总数有关
{
if(modbusCurStatus != 2)
{
modbusCurStatus = 2;
MODBUS_Init();
}
/*写地址时间稍长,真正时序是1s1次*/
//主机接收上一帧的数据并处理
if(modbusCurSta == 2) //主机接收数据处理
{
modbusCurSta = 0;
if(modbusCurF10RxFlag == 1) //收到数据且CRC校验正确,准备询问下一个
{
modbusCurF10RxFlag = 0;
assignAddr_Failcount = 0;
if(assignAddr_Step < paraMem.PACK_NUM) //非最后1个
{
assignAddr_Step++;
}
else
{
//当可用地址的最后一个也分配成功,已经配置完所有在线从机
assignAddr_State = 2;//已执行过分配
assignAddr_485num = assignAddr_Step;
assignAddr_Step = 0; //清空
MODBUS_Init(); //初始化
assignAddr_random = get_random();
assignAddr_WrIndex_Flg = 1;
return; //退出,不执行发送
}
}
else //未接收数据或数据错误,继续发送原写寄存器报文
{
assignAddr_Failcount++;
if(assignAddr_Failcount > 5)
{
assignAddr_Failcount = 0;
//发送多次还未收到正确回复,认为已经配置完所有在线从机
assignAddr_State = 2;//已执行过分配
assignAddr_485num = assignAddr_Step-1;
assignAddr_Step = 0; //清空
MODBUS_Init(); //初始化
assignAddr_random = get_random();
if(assignAddr_485num > 1) //存在从机才要下发队列标志
{
assignAddr_WrIndex_Flg = 1;
}
return; //退出,不执行发送
}
}
}
//主机发送对下一个从机的分配地址
if(modbusCurSta == 0)
{
tmp[0] = assignAddr_Step;
tmp[1] = CRC8_Cal(tmp,1);
modbusBufIndex = 0;
modbusBuf[0] = MODBUS_ASSIGN_INADDR;
modbusBuf[1] = 0x10;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x48;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x01;
modbusBuf[6] = 0x02;
modbusBuf[7] = tmp[0];
modbusBuf[8] = tmp[1];
crc16 = CRC16_Cal(modbusBuf, 9);
modbusBuf[9] = crc16.b8[0];
modbusBuf[10] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 11);
modbusCurSta = 1;
modbusCurF10RxFlag = 0;
MODBUS_UART_IT_RX_ENABLE;
}
//1,给从机反应时间
else
{
modbusCurSta++;
}
}
}
//主机广播队列标志
void MODBUS_WrIndex_Tx(void)
{
BYTE2 crc16;
modbusBufIndex = 0;
modbusBuf[0] = 0xff;
modbusBuf[1] = 0x10;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x00;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x01;
modbusBuf[6] = 0x02;
modbusBuf[7] = assignAddr_random & 0xff;
modbusBuf[8] = (assignAddr_random>>8) & 0xff;
crc16 = CRC16_Cal(modbusBuf, 9);
modbusBuf[9] = crc16.b8[0];
modbusBuf[10] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 11);
MODBUS_UART_IT_RX_ENABLE;
}
//从机收到广播的队列标志
void MODBUS_WrIndex_Rx(void)
{
BYTE2 crc16;
//CRC判断
crc16 = CRC16_Cal(modbusBuf, 2+7);
if( (modbusBuf[2+8] == crc16.b8[1]) && (modbusBuf[2+7] == crc16.b8[0]) )
{
if(assignAddr_State == 2) //经历过自动分配地址
{
uint8_t tmp[2];
bmsMem.can_ArrayIndex = modbusBuf[8]<<8 | modbusBuf[7];
tmp[0] = modbusBuf[8];
tmp[1] = modbusBuf[7];
EEPROM_WrMulByte(EE_ASSIGN,tmp);
delay_ms(5);
}
MODBUS_Init();
}
}
#endif
//主机因[屏幕]获取固定从机数据,并显示在屏幕上
void MODBUS_Screen_RdSlave_Tx(void)
{
uint8_t *data;
uint8_t len;
uint8_t i;
BYTE2 crc16;
if(modbusCurStatus != 3)
{
modbusCurStatus = 3;
MODBUS_Init();
}
//主机接收上一帧的数据并处理
if(modbusCurSta == 1)
{
modbusCurSta = 0;
if(modbusCurF03RxFlag == 1) //收到数据且CRC校验正确
{
data = (uint8_t *)&bmsMem_slave.vCell[0];
len = modbusBuf[2];
for(i=0;i<len;)//高字节在前
{
*data = modbusBuf[4+i];
data++;
*data = modbusBuf[3+i];
data++;
i++;
i++;
}
//更新该从机对应的canMem结构体,方便进行并机总值的计算
if(canMem[scr_RdData_Index].com != 0)
{
canMem[scr_RdData_Index].status_byte1 = bmsMem_slave.bStatus1;
canMem[scr_RdData_Index].status_byte2 = bmsMem_slave.bStatus2;
canMem[scr_RdData_Index].status_byte3 = bmsMem_slave.bStatus3;
canMem[scr_RdData_Index].status_byte4 = bmsMem_slave.temperaStatus;
}
modbusCurF03RxFlag = 0;
}
else
{
data = (uint8_t *)&bmsMem_slave.vCell[0];
for(i=0;i<92;i++)//清空结构体
{
*data = 0;
data++;
}
}
}
//主机发送对固定从机的读取
if(modbusCurSta == 0)
{
modbusBufIndex = 0;
modbusBuf[0] = scr_RdData_Index;
modbusBuf[1] = 0x03;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x00;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x2e;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
modbusCurSta = 1;
modbusCurF03RxFlag = 0;
MODBUS_UART_IT_RX_ENABLE;
}
}
//主机持续向指定从机写地址,收到写入成功的回复后,才退出
void MODBUS_Screen_WrSlaveAddr_Tx(void)
{
uint8_t tmp[2];
BYTE2 crc16;
if(modbusCurStatus != 4)
{
modbusCurStatus = 4;
MODBUS_Init();
}
//主机接收上一帧的数据并处理
if(modbusCurSta == 1)
{
modbusCurSta = 0;
if(modbusCurF10RxFlag == 1) //收到数据且CRC校验正确
{
modbusCurF10RxFlag = 0;
sdwa_WrAddr_Flg = 2;
}
else
{
sdwa_WrAddr_Failcount++;
if(sdwa_WrAddr_Failcount>3)
{
sdwa_WrAddr_Failcount = 0;
sdwa_WrAddr_Flg = 3;
}
}
}
//主机发送对固定从机的写地址
if(modbusCurSta == 0)
{
tmp[0] = sdwa_WrAddr;
tmp[1] = CRC8_Cal(tmp,1);
modbusBufIndex = 0;
modbusBuf[0] = scr_RdData_Index;
modbusBuf[1] = 0x10;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x48;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x01;
modbusBuf[6] = 0x02;
modbusBuf[7] = tmp[0];
modbusBuf[8] = tmp[1];
crc16 = CRC16_Cal(modbusBuf, 9);
modbusBuf[9] = crc16.b8[0];
modbusBuf[10] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 11);
modbusCurSta = 1;
modbusCurF10RxFlag = 0;
MODBUS_UART_IT_RX_ENABLE;
}
}
//主机因[上位机]获取固定从机数据,并在下一次上位机询问时回复
void MODBUS_Config_RdSlave_Tx(void)
{
uint8_t *data;
uint8_t len;
uint8_t i;
BYTE2 crc16;
if(modbusCurStatus != 5)
{
modbusCurStatus = 5;
MODBUS_Init();
}
//主机接收上一帧的数据并处理
if(modbusCurSta == 1)
{
modbusCurSta = 0;
if(modbusCurF03RxFlag == 1) //收到数据且CRC校验正确
{
Online_Flag = 1; //下次上位机询问,可以回复
data = (uint8_t *)&bmsMem_slave.vCell[0];
len = modbusBuf[2];
for(i=0;i<len;)//高字节在前
{
*data = modbusBuf[4+i];
data++;
*data = modbusBuf[3+i];
data++;
i++;
i++;
}
//更新该从机对应的canMem结构体,方便进行并机总值的计算
if(canMem[ConfigData_Index].com != 0)
{
canMem[ConfigData_Index].status_byte1 = bmsMem_slave.bStatus1;
canMem[ConfigData_Index].status_byte2 = bmsMem_slave.bStatus2;
canMem[ConfigData_Index].status_byte3 = bmsMem_slave.bStatus3;
canMem[ConfigData_Index].status_byte4 = bmsMem_slave.temperaStatus;
}
modbusCurF03RxFlag = 0;
}
else
{
Online_Flag = 0; //下次上位机询问,不回复
data = (uint8_t *)&bmsMem_slave.vCell[0];
for(i=0;i<92;i++)//清空结构体
{
*data = 0;
data++;
}
}
}
//主机发送对固定从机的读取
if(modbusCurSta == 0)
{
modbusBufIndex = 0;
modbusBuf[0] = ConfigData_Index;
modbusBuf[1] = 0x03;
modbusBuf[2] = 0x00;
modbusBuf[3] = 0x00;
modbusBuf[4] = 0x00;
modbusBuf[5] = 0x2e;
crc16 = CRC16_Cal(modbusBuf, 6);
modbusBuf[6] = crc16.b8[0];
modbusBuf[7] = crc16.b8[1];
MODBUS_UART_SendMulByte(modbusBuf, 8);
modbusCurSta = 1;
modbusCurF03RxFlag = 0;
MODBUS_UART_IT_RX_ENABLE;
}
}