245 lines
5.2 KiB
C
245 lines
5.2 KiB
C
#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 id; // 通信地址
|
||
uint8_t cmd; // 功能码
|
||
uint8_t index; // 包序号
|
||
uint8_t all; // 包数
|
||
// 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 BUFF[100]; // 响应包
|
||
uint8_t err = 0; // 错误代码
|
||
uint8_t len = 0; // 发送数据长度
|
||
|
||
uint16_t id; // 通信地址
|
||
uint16_t reg; // 数据地址
|
||
uint16_t cnt; // 数据个数
|
||
uint16_t crc;
|
||
|
||
// 检查通信地址
|
||
if(req->id != dcBuff.configBottle.addr && req->id != 0)
|
||
return;
|
||
|
||
// 清空发送帧
|
||
memset(BUFF, 0, sizeof(BUFF));
|
||
|
||
BUFF[len++] = dcBuff.configBottle.addr; // 从机地址
|
||
BUFF[len++] = req->cmd; // 命令代码
|
||
|
||
// reg = ntohs(req->reg); // 数据寄存器
|
||
// if(req->cmd == 0x03 || req->cmd == 0x06)
|
||
// reg += 40001;
|
||
// else if(req->cmd == 0x04)
|
||
// reg += 30001;
|
||
// else
|
||
// err = MODBUS_ERR_ILF; // 无效功能码
|
||
//
|
||
if(err == 0)
|
||
{
|
||
if(req->cmd == 0x03) // 读保持数据
|
||
{
|
||
// cnt = ntohs(req->cnt); // 参数个数
|
||
// if(reg != MODBUS_ADR_REG || cnt != 1)
|
||
// err = MODBUS_ERR_NAK; // 命令无法执行
|
||
// else
|
||
// {
|
||
// // 用最新的通讯地址刷新缓冲
|
||
// id = htons(dcBuff.configBottle.addr);
|
||
// BUFF[len++] = 2; // 数据长度
|
||
// memmove(BUFF + len, (uint8_t *) &id, 2);
|
||
// len += 2;
|
||
// }
|
||
}
|
||
}
|
||
//
|
||
// if(err != 0)
|
||
// {
|
||
// BUFF[1] |= 0x80; // 错误指示
|
||
// BUFF[len++] = err; // 错误代码
|
||
// }
|
||
|
||
// 计算CRC
|
||
crc = MODBUS_RTU_CRC16(BUFF, len);
|
||
crc = htons(crc);
|
||
|
||
memmove(BUFF + len, &crc, 2);
|
||
len += 2;
|
||
|
||
if(req->id != 0)
|
||
{
|
||
// 发送应答
|
||
RS485_SendDataByte(BUFF, len);
|
||
}
|
||
}
|
||
|
||
// 任务主体
|
||
void ReadModbus_Task(uint8_t c)
|
||
{
|
||
static uint8_t BUFF[sizeof(modbus_request_t)] = {0};
|
||
static uint8_t len = 0;
|
||
|
||
// 取数据
|
||
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);
|
||
}
|
||
}
|
||
}
|