#include "modbus_s.h" #ifdef MODBUS_S_PORT #include "inout.h" #include "delay.h" #include "trigger.h" //----------------------------------------------------------------- // 控制结构 u16 * mdbsReg[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {NULL}; // Modbus 从设备地址空间 ModbusCtrl g_modBusSCtrl; Rs485Ctrl g_rs485SCtrl; int ModbusSTx(Rs485Cmd * pCmd); // 功能:发送一条MODBUS协议的应答 int ModbusSRx(u8 rDat); // 功能:接收一条MODBUS协议的命令 int AddModbusSCmd(Rs485Cmd * pCmd); // 添加命令到队列中 int GetModbusSCmd(Rs485Cmd * pCmd); // 从队列中读取命令 int GetModbusSCmdBufLen(void); // 得到队列中命令个数 void ReadModbusSData(u16 addr, u16 * pBuf, u16 len); // 读数据 void WriteModbusSData(u16 addr, u16 * pBuf, u16 len); // 写数据 //----------------------------------------------------------------- /*----------------------------------------------------------------- 功能:初始化 Modbus 从设备串口 baud : 波特率 dat: 数据长度 parity: 校验位 stop: 停止位 mode: 数据模式 */ void InitModbusS(BAUD_TypeDef baud, char dat, char parity, char stop, char mode) { memset(&g_modBusSCtrl, 0, sizeof(ModbusCtrl)); if (mode == 'A' || mode == 'a') { g_modBusSCtrl.modBusMode = 1; } else { g_modBusSCtrl.modBusMode = 0; } // 延时 switch(baud) { // 数据位 + 1个起始位 + 奇偶校验位(无校验则没有) + 停止位 // 根据波特率来计算 通讯延时, 单位us = 10000000 / baud(按照一个字节10位计算, 8 N 1) case B4800: { g_modBusSCtrl.modBusWait = 2200; break; } case B9600: { g_modBusSCtrl.modBusWait = 1100; break; } case B14400: { g_modBusSCtrl.modBusWait = 750; break; } case B19200: { g_modBusSCtrl.modBusWait = 600; break; } case B38400: { g_modBusSCtrl.modBusWait = 300; break; } case B57600: { g_modBusSCtrl.modBusWait = 200; break; } case B115200: { g_modBusSCtrl.modBusWait = 100; break; } case B230400: { g_modBusSCtrl.modBusWait = 50; break; } case B460800: { g_modBusSCtrl.modBusWait = 25; break; } case B921600: { g_modBusSCtrl.modBusWait = 13; break; } default: { g_modBusSCtrl.modBusWait = 0; } } if ((dat == '8') && (parity != 'N') && (parity != 'n')) // 7位数据过滤校验位 { g_modBusSCtrl.filterChk = 1; // 过滤数据校验位标志 1, 过滤 } else { g_modBusSCtrl.filterChk = 0; } #ifndef USART1_485OutEn #define USART1_485OutEn DefOutEn #endif #ifndef USART1_485OutDis #define USART1_485OutDis DefOutDis #endif #ifndef USART2_485OutEn #define USART2_485OutEn DefOutEn #endif #ifndef USART2_485OutDis #define USART2_485OutDis DefOutDis #endif #ifndef USART3_485OutEn #define USART3_485OutEn DefOutEn #endif #ifndef USART3_485OutDis #define USART3_485OutDis DefOutDis #endif #ifndef USART4_485OutEn #define USART4_485OutEn DefOutEn #endif #ifndef USART4_485OutDis #define USART4_485OutDis DefOutDis #endif #ifndef USART5_485OutEn #define USART5_485OutEn DefOutEn #endif #ifndef USART5_485OutDis #define USART5_485OutDis DefOutDis #endif #ifndef USART6_485OutEn #define USART6_485OutEn DefOutEn #endif #ifndef USART6_485OutDis #define USART6_485OutDis DefOutDis #endif // 初始化串口 #if (MODBUS_S_PORT == COMM_USART1) InitUsart1(baud, dat, parity, stop); // 串口1 RegisterModbusCommFunc(&g_modBusSCtrl, Usart1SendData, Usart1GetData, Usart1CleanRsBuf, IsUsart1SendOver, USART1_485OutEn, USART1_485OutDis); #elif (MODBUS_S_PORT == COMM_USART2) InitUsart2(baud, dat, parity, stop); // 串口2 RegisterModbusCommFunc(&g_modBusSCtrl, Usart2SendData, Usart2GetData, Usart2CleanRsBuf, IsUsart2SendOver, USART2_485OutEn, USART2_485OutDis); #elif (MODBUS_S_PORT == COMM_USART3) InitUsart3(baud, dat, parity, stop); // 串口3 RegisterModbusCommFunc(&g_modBusSCtrl, Usart3SendData, Usart3GetData, Usart3CleanRsBuf, IsUsart3SendOver, USART3_485OutEn, USART3_485OutDis); #elif (MODBUS_S_PORT == COMM_USART4) InitUsart4(baud, dat, parity, stop); // 串口4 RegisterModbusCommFunc(&g_modBusSCtrl, Usart4SendData, Usart4GetData, Usart4CleanRsBuf, IsUsart4SendOver, USART4_485OutEn, USART4_485OutDis); #elif (MODBUS_S_PORT == COMM_USART5) InitUsart5(baud, dat, parity, stop); // 串口5 RegisterModbusCommFunc(&g_modBusSCtrl, Usart5SendData, Usart5GetData, Usart5CleanRsBuf, IsUsart5SendOver, USART5_485OutEn, USART5_485OutDis); #elif (MODBUS_S_PORT == COMM_USART6) InitUsart6(baud, dat, parity, stop); // 串口6 RegisterModbusCommFunc(&g_modBusSCtrl, Usart6SendData, Usart6GetData, Usart6CleanRsBuf, IsUsart6SendOver, USART6_485OutEn, USART6_485OutDis); #else RegisterModbusCommFunc(&g_modBusSCtrl, DefSendData, DefGetData, DefCleanRsBuf, DefIsSendOver, DefOutEn, DefOutDis); #endif g_modBusSCtrl.CommOutDis(); // 关闭发送 } /*----------------------------------------------------------------- 功能:发送一条ASCII格式MODBUS协议的应答,回应03控制字命令 */ int Modbus_TX03A(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_A]; // 待发送数据 u8 Lrc_check = 0; // 和校验 u16 data[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {0}; u8 temp; // 临时变量 u8 temp_data; // 临时变量 if (pCmd == NULL) { return -1; } pSDat[i++] = ':'; //':' temp_data = pCmd->index; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_L Lrc_check += temp_data; temp_data = pCmd->cmd; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_L Lrc_check += temp_data; //数据个数 temp_data = pCmd->rlen << 1; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Bytes Count_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Bytes Count_L Lrc_check += temp_data; //数据字节 ReadModbusSData(pCmd->addr, &data[0], pCmd->rlen); for (int j = 0; j < pCmd->rlen; j++) { temp_data = HIBYTE(data[j]); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); Lrc_check += temp_data; temp_data = LOBYTE(data[j]); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); Lrc_check += temp_data; } Lrc_check = ~Lrc_check + 1; pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L pSDat[i++] = 0x0D; //CR pSDat[i++] = 0x0A; //LF g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } /*----------------------------------------------------------------- 功能:发送一条ASCII格式MODBUS协议的应答,回应06控制字命令 */ int Modbus_TX06A(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_A]; //待发送数据 u8 Lrc_check = 0; //和校验 u8 temp; //临时变量 u8 temp_data; //临时变量 if (pCmd == NULL) { return -1; } pSDat[i++] = ':'; //':' temp_data = pCmd->index; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_L Lrc_check += temp_data; temp_data = pCmd->cmd; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_L Lrc_check += temp_data; temp_data = HIBYTE(pCmd->addr); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_L Lrc_check += temp_data; temp_data = LOBYTE(pCmd->addr); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_L Lrc_check += temp_data; temp_data = HIBYTE(pCmd->wrdat[0]); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Data_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Data_L Lrc_check += temp_data; temp_data = LOBYTE(pCmd->wrdat[0]); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Data_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Data_L Lrc_check += temp_data; Lrc_check = ~Lrc_check + 1; pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L pSDat[i++] = 0x0D; //CR pSDat[i++] = 0x0A; //LF g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } /*----------------------------------------------------------------- 功能:发送一条ASCII格式MODBUS协议的应答,回应10H控制字命令 */ int Modbus_TX10A(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_A]; //待发送数据 u8 Lrc_check = 0; //和校验 u8 temp; //临时变量 u8 temp_data; //临时变量 if (pCmd == NULL) { return -1; } pSDat[i++] = ':'; //':' temp_data = pCmd->index; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //IDX_L Lrc_check += temp_data; temp_data = pCmd->cmd; temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //CMD_L Lrc_check += temp_data; temp_data = HIBYTE(pCmd->addr); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_L Lrc_check += temp_data; temp_data = LOBYTE(pCmd->addr); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //Addr_L Lrc_check += temp_data; temp_data = HIBYTE(pCmd->wlen); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //len_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //len_L Lrc_check += temp_data; temp_data = LOBYTE(pCmd->wlen); temp = HIHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //len_H temp = LOHFBYTE(temp_data); pSDat[i++] = RtuToAscii(temp); //len_L Lrc_check += temp_data; Lrc_check = ~Lrc_check + 1; pSDat[i++] = RtuToAscii(HIHFBYTE(Lrc_check)); //LRC_H pSDat[i++] = RtuToAscii(LOHFBYTE(Lrc_check)); //LRC_L pSDat[i++] = 0x0D; //CR pSDat[i++] = 0x0A; //LF g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } /*----------------------------------------------------------------- 功能:发送一条RTU格式MODBUS协议的应答,回应03控制字命令 */ int Modbus_TX03R(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_R]; //待发送数据 u16 checkcrc; //crc校验 u16 data[MODBUS_REG_MAX - MODBUS_REG_MIN + 1] = {0}; if (pCmd == NULL) { return -1; } pSDat[i++] = pCmd->index; // ADR pSDat[i++] = pCmd->cmd; // CMD pSDat[i++] = pCmd->rlen << 1; // 字节数 ReadModbusSData(pCmd->addr, &data[0], pCmd->rlen); for (int j = 0; j < pCmd->rlen; j++) { pSDat[i++] = HIBYTE(data[j]); // DATA_H pSDat[i++] = LOBYTE(data[j]); // DATA_L } checkcrc = ModbusCrc16(&pSDat[0], i); pSDat[i++] = HIBYTE(checkcrc); // CRC_H pSDat[i++] = LOBYTE(checkcrc); // CRC_L g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } /*----------------------------------------------------------------- 功能:发送一条RTU格式MODBUS协议的应答,回应06控制字命令 */ int Modbus_TX06R(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_R]; //待发送数据 u16 checkcrc; //crc校验 if (pCmd == NULL) { return -1; } pSDat[i++] = pCmd->index; // ADR pSDat[i++] = pCmd->cmd; // CMD pSDat[i++] = HIBYTE(pCmd->addr); // ADDR_H pSDat[i++] = LOBYTE(pCmd->addr); // ADDR_L pSDat[i++] = HIBYTE(pCmd->wrdat[0]); // DATA_H pSDat[i++] = LOBYTE(pCmd->wrdat[0]); // DATA_L checkcrc = ModbusCrc16(&pSDat[0], i); pSDat[i++] = HIBYTE(checkcrc); // CRC_H pSDat[i++] = LOBYTE(checkcrc); // CRC_L g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } /*----------------------------------------------------------------- 功能:发送一条RTU格式MODBUS协议的应答,回应10H控制字命令 */ int Modbus_TX10R(Rs485Cmd * pCmd) { int i = 0; u8 pSDat[MDBSRXLEN_R]; //待发送数据 u8 checkcrc; //crc校验 if (pCmd == NULL) { return -1; } pSDat[i++] = pCmd->index; // ADR pSDat[i++] = pCmd->cmd; // CMD pSDat[i++] = HIBYTE(pCmd->addr); // ADDR_H pSDat[i++] = LOBYTE(pCmd->addr); // ADDR_L // 数据个数,单位字 pSDat[i++] = HIBYTE(pCmd->wlen); // LEN_H pSDat[i++] = LOBYTE(pCmd->wlen); // LEN_L checkcrc = ModbusCrc16(&pSDat[0], i); pSDat[i++] = HIBYTE(checkcrc); // CRC_H pSDat[i++] = LOBYTE(checkcrc); // CRC_L g_modBusSCtrl.ModBusCommSend(pSDat, i); // 将数据放入发送缓冲区 return 0; } //----------------------------------------------------------------- // 功能:发送一条MODBUS协议的回包,将数据放入发送缓冲区内 // 参数: pCmd: // 返回值: 0:发送正常 // -1:参数错误 int ModbusSTx(Rs485Cmd * pCmd) { int result = 0; u8 cmd; if (pCmd == NULL) { return -1; } cmd = pCmd->cmd; if ((cmd != MODBUS_RD) && (cmd != MODBUS_WR) && (cmd != MODBUS_WR_MULTI) && 1) { return -1; } if (pCmd->index != 0 && ((pCmd->index & 0x80) == 0 || cmd == MODBUS_RD) && 1) { if (g_modBusSCtrl.modBusMode != 0) {// ASCII码模式 if (cmd == MODBUS_RD) { Modbus_TX03A(pCmd); } else if (cmd == MODBUS_WR) { Modbus_TX06A(pCmd); } else if (cmd == MODBUS_WR_MULTI) { Modbus_TX10A(pCmd); } } else {// RTU模式 if (cmd == MODBUS_RD) { Modbus_TX03R(pCmd); } else if (cmd == MODBUS_WR) { Modbus_TX06R(pCmd); } else if (cmd == MODBUS_WR_MULTI) { Modbus_TX10R(pCmd); } } } return result; } //----------------------------------------------------------------- // 功能:接收一条MODBUS协议的命令 // 参数: // rDat: 本次收到的串口数据 // 返回值: 0: 接收正常 (收到的命令存放在 Rs485Cmd cmdbuf[MAX_485CMD] 中) int ModbusSRx(u8 rDat) { int i; int rst = -1; u32 time; static u32 timeBuf = 0; static u8 rxDat[MDBSRXLEN_MAX] = {0}; // 待接收数据缓冲区 static u8 beg = 0; // 接收到起始符 static u16 idx = 0; // 有效索引 static u16 num = 0; // 接收到字符数 static u8 rxDat_H = 0; // 接收前半字节数据 u16 checkcrc = 0; // crc校验(RTU) u8 checksum = 0; // 和校验(ASCII) Rs485Cmd newCmd; memset(&newCmd, 0, sizeof(Rs485Cmd)); if (g_modBusSCtrl.modBusMode != 0) {// ASCII码模式 if (g_modBusSCtrl.filterChk != 0) // 7位数据过滤校验位 { rDat &= (0x7F); } time = GetUsSoftTimer(); if (((time - timeBuf) > (g_modBusSCtrl.modBusWait * g_rs485SCtrl.modrsvTimout)) || // 读取超时时间 (idx > MDBSRXLEN_A) || // 超长复位 0) {// 重新接收数据包 beg = 0; idx = 0; num = 1; } else { num++; } timeBuf = time; if (((rDat == ':') || (rDat == 0x0D) || (rDat == 0x0A)) || ((rDat >= '0') && (rDat <= '9')) || ((rDat >= 'a') && (rDat <= 'f')) || ((rDat >= 'A') && (rDat <= 'F')) || 0) {// 接收到正确的数据 if (beg == 1) { idx++; } } else {// 从新接收数据包 beg = 0; idx = 0; } if (rDat == ':') {// 接收到起始符 beg = 1; idx = 0; } else if (rDat != 0x0A) {// 接收到普通数据 if ((idx & 0x01) == 0) {//接收到偶数个数据,进行数据转换 rxDat[(idx >> 1)-1] = MAKEBYTE(AsciiToRtu(rDat),AsciiToRtu(rxDat_H)); } else {//接收到奇数个数据,将数据存放到临时寄存器中 rxDat_H = rDat; } } else if (rDat == 0x0A) // \n {// 接收到终止符 if (rxDat_H == 0x0D) // \r {// 终止符正常 if ((idx & 0x01) == 0) {//数据个数基本符合要求 idx = (idx >> 1) - 2; //确定有效数据数组大小(不含校验位) //校验数据是否正确 checksum = 0; for (i = 0; i < idx; i++) { checksum += rxDat[i]; } checksum = ~checksum + 1; if(checksum == rxDat[idx]) {//lrc校验正确 u8 index = rxDat[0]; // 数据站号 int cur = 0; index &= 0x7f; for (i = 0; i < g_rs485SCtrl.idxNum; i++) {// 遍历查找是否是发给自己的命令 if ((index == 0) || (index == g_rs485SCtrl.idxList[i]) || 0) { cur = 1; break; } } if(cur == 1) {//站号正确 //判断命令 newCmd.index = rxDat[0]; // 站号 newCmd.cmd = rxDat[1]; // 命令码 newCmd.addr = MAKEWORD(rxDat[3],rxDat[2]); // 数据地址 switch(newCmd.cmd) { case MODBUS_RD: // 03H (读取保持寄存器) { newCmd.rlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度 AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } case MODBUS_WR: // 06H (写单个保持寄存器) { newCmd.wrdat[0] = MAKEWORD(rxDat[5],rxDat[4]); //数据 newCmd.wlen = 1; //数据长度 AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } case MODBUS_WR_MULTI: // 10H (写多个保持寄存器) { newCmd.wlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度 for (i = 0; i < (rxDat[6]/2); i++) { newCmd.wrdat[i] = MAKEWORD(rxDat[i*2+8],rxDat[i*2+7]); //数据 } AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } default: break; } //switch(newCmd.cmd) } //if (cur == 1) } //if (checksum == rxDat[idx]) } //if ((idx & 0x01) == 0) } //if (rxDat_H == 0x0D) idx = 0; beg = 0; // 从新接收数据包 } //if (rDat == 0x0A) } else {// RTU模式 time = GetUsSoftTimer(); if (((time - timeBuf) > (g_modBusSCtrl.modBusWait * g_rs485SCtrl.modrsvTimout)) || // 读取超时时间 (idx > (MDBSRXLEN_R)) || // 超长复位 0) {// 重新接收数据包 if (num > 1) { printf("data packet restart, old num=%d, rDat=0x%x\r\n", num, rDat); } idx = 0; num = 1; } else { num++; } timeBuf = time; int receive = 0; // 接收完成标志 if (num < 8) // 接收未完成 { rxDat[idx] = rDat; idx++; } else if (num >= 8) { u16 len; if (rxDat[1] == MODBUS_WR_MULTI) { len = 9 + rxDat[6]; } else { len = 8; } if (num < len) // 接收未完成 { rxDat[idx] = rDat; idx++; } else // 接收完成 { rxDat[idx] = rDat; receive = 1; } } if (receive == 1) { /* printf("receive a RTU modbus packet:"); for (int j = 0; j < num; j++) { printf("\t0x%x", rxDat[j]); } printf("\r\n"); */ //校验数据是否正确 checkcrc = ModbusCrc16(&rxDat[0], num-2); if (rxDat[num-2] == HIBYTE(checkcrc) && rxDat[num-1] == LOBYTE(checkcrc)) {//crc校验正确 u8 index = rxDat[0]; // 站号 int cur = 0; index &= 0x7f; // 只查找小于128的站号 for (i = 0; i < g_rs485SCtrl.idxNum; i++) {// 遍历查找是否是发给自己的命令 if ((index == 0) || (index == g_rs485SCtrl.idxList[i]) || 0) { cur = 1; break; } } if(cur == 1) {//站号正确 newCmd.index = rxDat[0]; // 站号 newCmd.cmd = rxDat[1]; // 命令码 newCmd.addr = MAKEWORD(rxDat[3],rxDat[2]); // 数据地址 switch(newCmd.cmd) { case MODBUS_RD: // 03H (读取保持寄存器) { newCmd.rlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度 AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } case MODBUS_WR: // 06H (写单个保持寄存器) { newCmd.wrdat[0] = MAKEWORD(rxDat[5],rxDat[4]); //数据 newCmd.wlen = 1; //数据长度 AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } case MODBUS_WR_MULTI: // 10H (写多个保持寄存器) { newCmd.wlen = MAKEWORD(rxDat[5],rxDat[4]); //数据长度 for (i = 0; i < newCmd.wlen; i++) { newCmd.wrdat[i] = MAKEWORD(rxDat[8+i*2], rxDat[7+i*2]); //数据 } AddModbusSCmd(&newCmd); // 增加命令 rst = 0; break; } default: break; } } else { printf("index error, index=%d\r\n", index); } } else { printf("check crc error, calc crc:0x%x 0x%x, receive crc:0x%x 0x%x\r\n", HIBYTE(checkcrc), LOBYTE(checkcrc), rxDat[num-2], rxDat[num-1]); } idx = 0; num = 0; // 重新接收数据包 } } return rst; } //----------------------------------------------------------------- // idx 从站号 void InitModbusSCtrl(u8 idx, int rsvtimout) { memset(&g_rs485SCtrl, 0, sizeof(Rs485Ctrl)); g_rs485SCtrl.init = 1; // 初始化完成 g_rs485SCtrl.idxList[g_rs485SCtrl.idxNum] = idx; // 站号 g_rs485SCtrl.idxNum++; // 读取超时时间 if (rsvtimout < 10) { g_rs485SCtrl.modrsvTimout = 10; } else if (rsvtimout > 100) { g_rs485SCtrl.modrsvTimout = 100; } else { g_rs485SCtrl.modrsvTimout = rsvtimout; } } // 注册外部响应函数 void RegModbusSCmdProc(Resv485ExProc exProc) { g_rs485SCtrl.resv485exproc = exProc; } // 增加附属从站号 void AddModbusSIdx(u8 idx) { if (g_rs485SCtrl.idxNum < MODBUS_IDXMAX) { g_rs485SCtrl.idxList[g_rs485SCtrl.idxNum] = idx; // 站号 g_rs485SCtrl.idxNum++; } } // 将命令放入缓冲区内 int AddModbusSCmd(Rs485Cmd * pCmd) { Rs485Ctrl * p485Ctrl = &g_rs485SCtrl; if (p485Ctrl != NULL && pCmd != NULL) { if (p485Ctrl->bufCmdNum < MAX_485CMD && p485Ctrl->bufTail < MAX_485CMD) { memcpy(&(p485Ctrl->cmdbuf[p485Ctrl->bufTail]), pCmd, sizeof(Rs485Cmd)); p485Ctrl->bufTail++; if (p485Ctrl->bufTail >= MAX_485CMD) { p485Ctrl->bufTail = 0; } p485Ctrl->bufCmdNum++; return p485Ctrl->bufCmdNum; } return -2; } return -1; } // 得到一条缓冲区内的命令 int GetModbusSCmd(Rs485Cmd * pCmd) { Rs485Ctrl * p485Ctrl = &g_rs485SCtrl; if (p485Ctrl != NULL && pCmd != NULL) { if (p485Ctrl->bufCmdNum > 0 && p485Ctrl->bufHead < MAX_485CMD) { memcpy(pCmd, &(p485Ctrl->cmdbuf[p485Ctrl->bufHead]), sizeof(Rs485Cmd)); p485Ctrl->bufHead++; if (p485Ctrl->bufHead >= MAX_485CMD) { p485Ctrl->bufHead = 0; } p485Ctrl->bufCmdNum--; return p485Ctrl->bufCmdNum; } else { return -2; } } return -1; } int GetModbusSCmdBufLen(void) { return g_rs485SCtrl.bufCmdNum; } // modbus从设备,异步响应函数 int ModbusSTask(void) { static u32 lastTime; // 单位us static Rs485Cmd cmd; Rs485Ctrl * p485Ctrl = &g_rs485SCtrl; if (p485Ctrl == NULL) { return -1; } if (p485Ctrl->init == 0) {// 没有初始化,直接跳出 return -1; } switch(p485Ctrl->steps) { case 0: // 待机状态 { if (1) {// 接收串口缓冲区内数据 u8 rDat; int len; int rslt; do { len = g_modBusSCtrl.ModBusCommReceive(&rDat, 1); // 从缓冲区读取数据 if (len == 1) {// 接收到串口数据 rslt = ModbusSRx(rDat); if (rslt == 0) {// 接收到一个数据包 break; } } else { break; } }while(1); } if (GetModbusSCmdBufLen() != 0)// 缓冲区有数据 { memset(&cmd, 0, sizeof(Rs485Cmd)); GetModbusSCmd(&cmd); // 得到命令 if (cmd.index != 0 && ((cmd.index & 0x80) == 0 || cmd.cmd == MODBUS_RD) && 1) {// 需要回包 lastTime = GetUsSoftTimer(); p485Ctrl->steps++; } else {// 不需要回包 p485Ctrl->steps = 5; } } break; } case 1: // 数据发送允许 { if ((GetUsSoftTimer() - lastTime) > (g_modBusSCtrl.modBusWait * 2)) { g_modBusSCtrl.CommOutEn(); lastTime = GetUsSoftTimer(); p485Ctrl->steps++; } break; } case 2: // 填充回包数据 { if (GetUsSoftTimer() - lastTime > 100) { ModbusSTx(&cmd); p485Ctrl->steps++; } break; } case 3: // 等待发送完成 { if (g_modBusSCtrl.IsModBusCommSendOver() == TRUE) { lastTime = GetUsSoftTimer(); p485Ctrl->steps++; } break; } case 4: // 等待最后一个字节发送完成 { if ((GetUsSoftTimer() - lastTime) > (g_modBusSCtrl.modBusWait * 2)) { p485Ctrl->steps++; } break; } case 5: // 执行外部函数 { g_modBusSCtrl.CommOutDis(); // 数据发送禁止 if (cmd.cmd == MODBUS_WR || cmd.cmd == MODBUS_WR_MULTI) { WriteModbusSData(cmd.addr, &cmd.wrdat[0], cmd.wlen); } if (g_rs485SCtrl.resv485exproc != NULL) { g_rs485SCtrl.resv485exproc(&cmd); } else { printf("resv485exproc is NULL\r\n"); } p485Ctrl->steps = 0; break; } default: { g_modBusSCtrl.CommOutDis(); // 数据发送禁止 p485Ctrl->steps = 0; break; } } return p485Ctrl->steps; } //------------------------------------------------------------------------------------------ // 初始化寄存器组 int InitModbusReg(u16 addr, u16 * pBuf, u16 len) { if (len != 0 && pBuf != NULL) { if ((addr + len - 1) > MODBUS_REG_MAX) { printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX); } else if (addr < MODBUS_REG_MIN) { printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN); } else { for (int i = addr; i < (addr+len); i++) { mdbsReg[i - MODBUS_REG_MIN] = pBuf++; } return len; } } return 0; } // 读数据 void ReadModbusSData(u16 addr, u16 * pBuf, u16 len) { if (len != 0 && pBuf != NULL) { if ((addr + len - 1) > MODBUS_REG_MAX) { printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX); } else if (addr < MODBUS_REG_MIN) { printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN); } else { for (int i = addr; i < (addr+len); i++) { if (mdbsReg[i - MODBUS_REG_MIN] == NULL) { printf("[Warning]: modbus addr NULL !! addr=0x%x\r\n", i); } else { *pBuf++ = *mdbsReg[i - MODBUS_REG_MIN]; } } } } } // 写数据 void WriteModbusSData(u16 addr, u16 * pBuf, u16 len) { if (len != 0 && pBuf != NULL) { if ((addr + len - 1) > MODBUS_REG_MAX) { //printf("[Warning]: modbus addr outsize !! addr=0x%x, max=0x%x\r\n", (addr + len - 1), MODBUS_REG_MAX); } else if (addr < MODBUS_REG_MIN) { //printf("[Warning]: modbus addr outsize !! addr=0x%x, min=0x%x\r\n", addr, MODBUS_REG_MIN); } else { for (int i = addr; i < (addr+len); i++) { if (mdbsReg[i - MODBUS_REG_MIN] == NULL) { //printf("[Warning]: modbus addr NULL !! addr=0x%x\r\n", i); } else { *mdbsReg[i - MODBUS_REG_MIN] = *pBuf++; } } } } } #endif