ShipCentralControl/Anjiehui7_DTU/User/modbus_slave.c

362 lines
9.0 KiB
C
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.

#include "includes.h"
// Modbus(RS485接口工作模式单向发送向上位机发送数据, 每30秒向上位机发送一个心跳。
// modbus通讯处理
#pragma pack(push, 1)
// RF串口发送帧的循环缓冲
#define MODBUS_SENDM_DATA_COUNT 512
loopbuff_t Modbus_SendM;
uint8_t Modbus_SendM_Data[sizeof(ext_data_t) * (MODBUS_SENDM_DATA_COUNT + 1)] = {0};
// RF串口接收的消息通知
SemaphoreHandle_t Modbus_SendQ = NULL; // 二值信号量句柄
typedef struct
{
uint8_t head_L;
uint8_t head_S;
uint8_t id; // 通信地址
uint8_t cmd; // 功能码
uint8_t len_H;
uint8_t len_L;
uint8_t index; // 包序号
uint8_t all; // 包数
uint8_t PSN[7];
uint8_t BOX[12];
// union
// {
// uint16_t cnt; // 读取:寄存器数量
// uint16_t data; // 写入:寄存器数据
// };
uint16_t crc;
} modbus_request_t;
#pragma pack(pop)
// 发送心跳报文缓冲
#define HEART_SENDBUFF_SIZE 64
uint8_t Heart_sendBuff[HEART_SENDBUFF_SIZE];
// 发送数据报文缓冲
uint8_t Modbus_sendBuff[TASK_SENDBUFF_SIZE];
void ReadModbus_Task(uint8_t c);
static void RS485_SendDataByte(uint8_t *pu8TxBuf, uint32_t u32WriteBytes)
{
/* Send data */
UART_Transmit(&huart4, pu8TxBuf, u32WriteBytes);
}
void Slave_IRQHandler(USART_Handle *huart)
{
uint8_t u8DTU = (uint8_t) huart->Instance->DR;
/* Handle received data */
ReadModbus_Task(u8DTU);
}
void Modbus_Open()
{
// 创建消息队列
LoopBuff_Create(&Modbus_SendM, sizeof(ext_data_t), MODBUS_SENDM_DATA_COUNT, 0, (uint32_t) Modbus_SendM_Data);
// 创建信号量
Modbus_SendQ = xSemaphoreCreateBinary();
}
uint8_t pack_heart_send_data()
{
uint8_t i;
uint16_t crc;
// 打包上报数据按modbus从机响应的格式
memset(Heart_sendBuff, 0, HEART_SENDBUFF_SIZE);
i = 0;
// PSN
memmove(Heart_sendBuff + i, dcBuff.configBottle.PSN, 6);
i += 6;
Heart_sendBuff[i++] = 0x02; // 命令码:心跳
// 数据长度先预设为0最后再修改位置为Heart_sendBuff[7]
Heart_sendBuff[i++] = 0;
// 硬件版本
Heart_sendBuff[i++] = dcBuff.powerInfo.hardVer.minor;
// 软件版本
Heart_sendBuff[i++] = dcBuff.powerInfo.softVer.minor;
// 软件日期
Heart_sendBuff[i++] = dcBuff.powerInfo.softDate.year;
Heart_sendBuff[i++] = dcBuff.powerInfo.softDate.month;
Heart_sendBuff[i++] = dcBuff.powerInfo.softDate.day;
// 射频自检状态
Heart_sendBuff[i++] = RF_initStatus;
// 网口自检状态
Heart_sendBuff[i++] = Ethernet_initStatus;
// 网络连接状态
Heart_sendBuff[i++] = Ethernet_IsConnected();
// GPRS入网状态
Heart_sendBuff[i++] = dcBuff.dtuData.networked;
// GPRS信号强度
Heart_sendBuff[i++] = dcBuff.dtuData.rssi;
// GPRS连接状态
Heart_sendBuff[i++] = dcBuff.dtuData.connected;
// 保留备用
i += 41;
// 修改数据长度
Heart_sendBuff[7] = i - 8;
// 校验码
crc = MODBUS_RTU_CRC16((uint8_t *) Heart_sendBuff, i);
*(uint16_t *)(Heart_sendBuff + i) = htons(crc);
i += 2;
// 结束符
Heart_sendBuff[i++] = 0x0d;
Heart_sendBuff[i++] = 0x0a;
return i;
}
// 任务主体
void Modbus_Task(void *p_arg)
{
uint8_t i;
uint32_t heartTick = 0;
ext_data_t Gprs;
while(TRUE)
{
// 本任务读取发送队列的记录
// 这条语句用于任务切换(让其它任务有机会得到执行)
xSemaphoreTake(Modbus_SendQ, 1000);
if(IsTickOut(heartTick))
{
heartTick = GetDelayTick(30000); // 30秒以后再发
// TODO: 发送心跳到上位机,不等待返回
i = pack_heart_send_data();
RS485_SendDataByte(Heart_sendBuff, i);
}
if(LoopBuff_GetCount(&Modbus_SendM))
{
// // 取数据
memmove(&Gprs, LoopBuff_GetDataPtr(&Modbus_SendM, Modbus_SendM.info.rdPtr), sizeof(ext_data_t));
LoopBuff_RemoveItems(&Modbus_SendM, 1);
i = pack_modbus_tran_data(&Gprs);
// TODO: 发送数据到上位机,不等待返回
RS485_SendDataByte(Modbus_sendBuff, i);
}
}
}
// 处理modbus rtu命令已经验证过长度和CRC的包
void MODBUS_RTU_CMD(modbus_request_t *req)
{
static uint8_t BUFF2[100]; // 响应包
uint8_t err = 0; // 错误代码
uint8_t len = 0; // 发送数据长度
ext_bottle_t bottleRec;
uint16_t count; // 通信地址
// uint16_t reg; // 数据地址
uint16_t cnt; // 数据个数
uint16_t crc;
ext_box_t boxRec;
ext_data_t Gprs;
uint8_t i;
char* psn;
// // 检查通信地址
// 添加记录
// boxCnt.count = 5;
// for(i = 0; i < boxCnt.count; i++)
// {
// memset(&boxRec, 0, sizeof(ext_box_t));
// boxRec.PSN[0] = 26;
// boxRec.PSN[3] = (0 + i * 1) / 1000;
// boxRec.PSN[4] = ((0 + i * 1) % 1000) >> 8;
// boxRec.PSN[5] = ((0 + i * 1) % 1000) & 0xFF;
// strcpy(boxRec.TGGU, "TGGU2000000");
// boxRec.TGGU[7] = '0' + (0 + i) / 100;
// boxRec.TGGU[8] = '0' + (0 + i) % 100 / 10;
// boxRec.TGGU[9] = '0' + (0 + i) % 10;
// FRAM_BufferWrite(FRAM_BOX_DATA_BASE + sizeof(ext_box_t) * i, (uint8_t *) &boxRec, sizeof(ext_box_t));
// }
// FRAM_SaveInfo(FRAM_BOX_INFO_BASE, (uint8_t *) &boxCnt, sizeof(ext_count_t));
if(req->cmd == 0x02) // 读保持数据
{
cnt = req->index; // 参数个数
memset(&boxRec, 0, sizeof(ext_box_t));
memset(&bottleRec, 0, sizeof(ext_bottle_t));
boxRec.PSN[0] = req->PSN[0];
boxRec.PSN[1] = req->PSN[1];
boxRec.PSN[2] = req->PSN[2];
boxRec.PSN[3] = req->PSN[3];
boxRec.PSN[4] = req->PSN[4];
boxRec.PSN[5] = req->PSN[5];
bottleRec.PSN[0] = req->PSN[0];
bottleRec.PSN[1] = req->PSN[1];
bottleRec.PSN[2] = req->PSN[2];
bottleRec.PSN[3] = req->PSN[3];
bottleRec.PSN[4] = req->PSN[4];
bottleRec.PSN[5] = req->PSN[5];
for(int i=0; i<11; i++)
boxRec.TGGU[i]=req->BOX[i];
boxRec.TGGU[11]=0;
if(req->index>0)count=req->index-1;
FRAM_BufferWrite(FRAM_BOX_DATA_BASE + sizeof(ext_box_t) * count, (uint8_t *) &boxRec, sizeof(ext_box_t));
// // 更新索引信息
bottleRec.lastData = -1;
FRAM_BufferWrite(FRAM_BOTTLE_DATA_BASE + sizeof(ext_bottle_t) * count, (uint8_t *) &bottleRec, sizeof(ext_bottle_t));
memset(BUFF2, 0, sizeof(BUFF2));
BUFF2[len++]='L';
BUFF2[len++]='S';
BUFF2[len++] = dcBuff.configBottle.addr; // 从机地址
BUFF2[len++] = 2;//req->cmd; // 命令代码
BUFF2[len++] = 0;
BUFF2[len++] = 1; //LEN
BUFF2[len++] = 1;
// 计算CRC
crc = MODBUS_RTU_CRC16(BUFF2, len);
crc = htons(crc);
memmove(BUFF2 + len, &crc, 2);
len += 2;
RS485_SendDataByte(BUFF2, len);
if(req->index==req->all)
{
boxCnt.count=req->all;
bottleCnt.count = req->all;
FRAM_SaveInfo(FRAM_BOX_INFO_BASE, (uint8_t *) &boxCnt, sizeof(ext_count_t));
FRAM_SaveInfo(FRAM_BOTTLE_INFO_BASE, (uint8_t *) &bottleCnt, sizeof(ext_count_t));
osDelay(500);
HAL_NVIC_SystemReset();
}
}
else if(req->cmd == 0x05)
{
if(LoopBuff_GetCount(&Modbus_SendM))
{
// 取数据
memmove(&Gprs, LoopBuff_GetDataPtr(&Modbus_SendM, Modbus_SendM.info.rdPtr), sizeof(ext_data_t));
LoopBuff_RemoveItems(&Modbus_SendM, 1);
i = pack_modbus_tran_data(&Gprs);
// TODO: 发送数据到上位机,不等待返回
RS485_SendDataByte(Modbus_sendBuff, i);
}
}
}
// 任务主体
uint8_t BUFF[1000] = {0};
void ReadModbus_Task(uint8_t c)
{
static uint8_t len = 0;
static uint16_t data_len = 0;
static uint16_t data_len2 = 0;
// // 取数据
// if((len==0)&&(c=='L')) len++;
// else return;
// if((len==1)&&(c=='S')) len++;
// else return;
//
// BUFF[len++] = c;
//
// // 验证长度和CRC值
// if(len == sizeof(modbus_request_t))
// {
// if(MODBUS_RTU_CRC16(BUFF, sizeof(modbus_request_t)) == 0)
// {
// MODBUS_RTU_CMD((modbus_request_t *) BUFF);
// len = 0;
// }
// else
// {
// // 丢掉第一个字节
// memmove(BUFF, BUFF + 1, --len);
// }
// }
switch(len)
{
case 0:
BUFF[len]=c;
if(c=='L') len++;
else len=0;
break;
case 1:
BUFF[len]=c;
if(c=='S') len++;
else len=0;
break;
case 2:
BUFF[len++]=c;
break;
case 3:
BUFF[len]=c;
if(c==2) len++;
else if(c==5)len+=2;
else len=0;
break;
case 4:
BUFF[len]=c;
data_len=c<<8;
len++;
break;
case 5:
BUFF[len]=c;
data_len|=c;
len++;
break;
case 6:
BUFF[6+data_len2++]=c;
if(data_len2+1>data_len)
len++;
break;
case 7:
BUFF[6+data_len2++]=c;
len++;
break;
case 8:
BUFF[6+data_len2]=c;
len=0;
break;
default:
len=0;
break;
}
if((len==0)&&(data_len2>1))
{
if(MODBUS_RTU_CRC16(BUFF, 7+data_len2) == 0)
{
MODBUS_RTU_CMD((modbus_request_t *) BUFF);
}
data_len2=0;
}
else if(BUFF[3]==5)
{
MODBUS_RTU_CMD((modbus_request_t *) BUFF);
data_len2=0;
}
}