#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); } } }