#define MB_ADU_LEN ? ? ? ? ? ? ? ? ? ? ?256 #define MB_PDU_LEN ? ? ? ? ? ? ? ? ? ? ?(MB_ADU_LEN - 3) #define MB_ADU_REQ_LEN_MIN ? ? ? ? ? ? ?8 #define MB_ADU_RSP_LEN_MIN ? ? ? ? ? ? ?5 #define MB_ADU_ADDR_INDEX ? ? ? ? ? ? ? 0 #define MB_ADU_FUNC_INDEX ? ? ? ? ? ? ? (MB_ADU_ADDR_INDEX + 1) #define MB_ADU_START_ADDR_INDEX ? ? ? ? (MB_ADU_FUNC_INDEX + 1) #define MB_ADU_QUANTITY_INDEX ? ? ? ? ? (MB_ADU_START_ADDR_INDEX + 2) #define MB_ADU_COUNT_INDEX ? ? ? ? ? ? ?(MB_ADU_FUNC_INDEX + 1) #define MB_ADU_INVALID_FUNC ? ? ? ? ? ? 0 #define MB_ADU_RSP_FUNC_EXCEPTION ? ? ? 0x80 #define MB_COILS_QUANTITY_MAX ? ? ? ? ? ? ? 2000 ?//0x7D0 #define MB_DISCRETE_INPUTS_QUANTITY_MAX ? ? 2000 ?//0x7D0 #define MB_INPUT_REGISTER_QUANTITY_MAX ? ? ?125 ?//0x7D #define MAX_DATA_LEN ? ?48 typedef enum MB_function_code {? ?//常用的 Function Code,其它暂不实现 ? ? MB_READ_COILS = 01, ? ? MB_READ_DISCRETE_INPUTS, ? ? MB_READ_HOLDING_REGISTERS, ? ? MB_READ_INPUT_REGISTER, ? ? MB_WRITE_SINGLE_COIL, ? ? MB_WRITE_SINGLE_REGISTER, ? ? MB_WRITE_MULTIPLE_COILS = 15, ? ? MB_WRITE_MULTIPLE_REGISTERS, } tsModbusFunctionCode_t; typedef struct MB_comm { ? ? uint8_t sendLen;? // request len ? ? uint8_t recvLen;? // expect response len ? ? uint8_t actRecvLen;? //actual response len ? ? uint8_t send[MAX_DATA_LEN];? // request buffer ? ? uint8_t recv[MAX_DATA_LEN];? // response buffer } tsModbusComm_t; tsModbusComm_t gModbusComm = {0}; uint16_t modbusCrc16(uint8_t *data, uint8_t dataLen) { ? ? uint8_t i, j; ? ? uint16_t crc = 0xFFFF; ? ? for (j = 0; j < dataLen; j++) { ? ? ? ? crc = crc ^ data[j]; ? ? ? ? for (i = 0; i < 8; i++) { ? ? ? ? ? ? if ((crc & 0x0001) > 0) { ? ? ? ? ? ? ? ? crc = crc >> 1; ? ? ? ? ? ? ? ? crc = crc ^ 0xA001; ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? crc = crc >> 1; ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? return crc; } void modbusAddCrc16(uint8_t *data, uint8_t dataLen) { ? ? uint16_t crc16 = modbusCrc16(data, dataLen); ? ? data[dataLen] = LOW_BYTE(crc16); ? ? data[dataLen + 1] = HIGH_BYTE(crc16); } bool modbusCheckCrc16(uint8_t *data, uint8_t dataLen) { ? ? uint16_t crc16 = modbusCrc16(data, dataLen - 2); ? ? if (crc16 == HIGH_LOW_TO_INT(data[dataLen - 1], data[dataLen - 2])) { ? ? ? ? return true; ? ? } ? ? dbgWarning("CRC Error!!!"); ? ? return false; } bool modbusCheckRequestAdu(void) { ? ? uint16_t quantity; ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? uint8_t func = pComm->send[MB_ADU_FUNC_INDEX]; ? ? if (!modbusCheckCrc16(pComm->send, pComm->sendLen)) { ? ? ? ? dbgWarning("crc error !\n"); ? ? ? ? return false; ? ? } ? ? if (func == MB_ADU_INVALID_FUNC) { ? ? ? ? dbgWarning("invalid function code !\n"); ? ? ? ? return false; ? ? } ? ? switch (func) { ? ? case MB_READ_COILS: ? ? case MB_READ_DISCRETE_INPUTS: ? ? ? ? if (pComm->sendLen == MB_ADU_REQ_LEN_MIN) { //addr(1), func(1), start addr(2), quantity(2), crc(2) ? ? ? ? ? ? quantity = HIGH_LOW_TO_INT(pComm->send[MB_ADU_QUANTITY_INDEX], pComm->send[MB_ADU_QUANTITY_INDEX + 1]); ? ? ? ? ? ? if (quantity <= MB_COILS_QUANTITY_MAX) { ? ? ? ? ? ? ? ? pComm->recvLen = MB_ADU_RSP_LEN_MIN; ? //addr(1), func(1), count(1), crc(2) ? ? ? ? ? ? ? ? pComm->recvLen += quantity / 8; ? ? ? ? ? ? ? ? pComm->recvLen += (quantity % 8) ? (1) : (0); ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? break; ? ? case MB_READ_HOLDING_REGISTERS: ? ? case MB_READ_INPUT_REGISTER: ? ? ? ? if (pComm->sendLen == MB_ADU_REQ_LEN_MIN) { //addr(1), func(1), start addr(2), quantity(2), crc(2) ? ? ? ? ? ? quantity = HIGH_LOW_TO_INT(pComm->send[MB_ADU_QUANTITY_INDEX], pComm->send[MB_ADU_QUANTITY_INDEX + 1]); ? ? ? ? ? ? if (quantity <= MB_INPUT_REGISTER_QUANTITY_MAX) { ? ? ? ? ? ? ? ? pComm->recvLen = MB_ADU_RSP_LEN_MIN; ? //addr(1), func(1), count(1), crc(2) ? ? ? ? ? ? ? ? pComm->recvLen += quantity * 2; ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? break; ? ? case MB_WRITE_SINGLE_COIL: ? ? case MB_WRITE_SINGLE_REGISTER: ? ? ? ? if (pComm->sendLen == MB_ADU_REQ_LEN_MIN) { //addr(1), func(1), start addr(2), value(2), crc(2) ? ? ? ? ? ? pComm->recvLen = pComm->sendLen; ? ? ? ? ? ? return true; ? ? ? ? } ? ? ? ? break; ? ? case MB_WRITE_MULTIPLE_COILS: ? ? case MB_WRITE_MULTIPLE_REGISTERS: ? ? ? ? if (pComm->sendLen >= MB_ADU_REQ_LEN_MIN) { ? ? ? ? ? ? pComm->recvLen = 8; //addr(1), func(1), start addr(2), quantity(2), crc(2) ? ? ? ? ? ? return true; ? ? ? ? } ? ? ? ? break; ? ? default: ? ? ? ? break; ? ? } ? ? return false; } ? bool modbusCheckResponseAduEx(void) { ? ? uint8_t count; ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? uint8_t func = pComm->send[MB_ADU_FUNC_INDEX]; ? ? switch (func) { ? ? case MB_READ_COILS: ? ? case MB_READ_DISCRETE_INPUTS: ? ? case MB_READ_HOLDING_REGISTERS: ? ? case MB_READ_INPUT_REGISTER: ? ? ? ? count = pComm->recv[MB_ADU_COUNT_INDEX]; ? ? ? ? if ((pComm->actRecvLen == pComm->recvLen) && ? ? ? ? ? ? ? ? (count == pComm->recvLen - 5)) { ? ? ? ? ? ? return true; ? ? ? ? } ? ? ? ? break; ? ? case MB_WRITE_SINGLE_COIL: ? ? case MB_WRITE_SINGLE_REGISTER: ? ? ? ? if (pComm->actRecvLen == pComm->recvLen) { ? ? ? ? ? ? if (memcmp(pComm->send, pComm->recv, pComm->actRecvLen) == 0) { ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? break; ? ? case MB_WRITE_MULTIPLE_COILS: ? ? case MB_WRITE_MULTIPLE_REGISTERS: ? ? ? ? if (pComm->actRecvLen == pComm->recvLen) { ? ? ? ? ? ? if (memcmp(pComm->send, pComm->recv, 6) == 0) { //addr(1), func(1), start addr(2), quantity(2) ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? break; ? ? default: ? ? ? ? return true; ? ? } ? ? return false; } bool modbusCheckResponseAdu(void) { ? ? uint8_t i; ? ? uint8_t tempLen; ? ? uint8_t temp[MAX_DATA_LEN] = {0}; ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? uint8_t len = pComm->actRecvLen; ? ? uint8_t *data = pComm->recv; ? ? for (i = 0; i < len; i++) { ? ? ? ? if ((data[i] == pComm->send[MB_ADU_ADDR_INDEX]) && ? ? ? ? ? ? ? ? (data[i + 1] == pComm->send[MB_ADU_FUNC_INDEX])) { ? ? ? ? ? ? if (len - i < MB_ADU_RSP_LEN_MIN) { ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? for (tempLen = len - i; tempLen >= MB_ADU_RSP_LEN_MIN; tempLen--) { ? ? ? ? ? ? ? ? if (modbusCheckCrc16(&data[i], tempLen)) { ? ? ? ? ? ? ? ? ? ? memcpy(temp, &data[i], tempLen); ? ? ? ? ? ? ? ? ? ? memcpy(data, temp, tempLen); ? ? ? ? ? ? ? ? ? ? pComm->actRecvLen = tempLen; ? ? ? ? ? ? ? ? ? ? return modbusCheckResponseAduEx(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } else if ((data[i] == pComm->send[MB_ADU_ADDR_INDEX]) && ? ? ? ? ? ? ? ? ? ?(data[i + 1] == pComm->send[MB_ADU_FUNC_INDEX] + MB_ADU_RSP_FUNC_EXCEPTION)) { ? ? ? ? ? ? if (len - i < MB_ADU_RSP_LEN_MIN) { ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? tempLen = MB_ADU_RSP_LEN_MIN; ? ? ? ? ? ? if (modbusCheckCrc16(&data[i], tempLen)) { ? ? ? ? ? ? ? ? memcpy(temp, &data[i], tempLen); ? ? ? ? ? ? ? ? memcpy(data, temp, tempLen); ? ? ? ? ? ? ? ? pComm->actRecvLen = tempLen; ? ? ? ? ? ? ? ? dbgLog("MODBUS RESP EXCEPTION: 0x%02x\n", pComm->recv[MB_ADU_FUNC_INDEX]); ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? return false; } void modbusRequestAndWaitResponse(void) { ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? if (modbusCheckRequestAdu()) { ? ? ? ? dbgLog("send: \n"); ? ? ? ? dbgPrintData(pComm->send, pComm->sendLen); ? ? ? ? write_ex((uint8_t *)pComm->send, (uint32_t)pComm->sendLen); ? ? ? ? pComm->actRecvLen = read_ex((uint8_t *)pComm->recv, MAX_DATA_LEN); ? ? ? ? if (pComm->actRecvLen != -1) { ? ? ? ? ? ? dbgLog("recv: \n"); ? ? ? ? ? ? dbgPrintData(pComm->recv, pComm->actRecvLen); ? ? ? ? ? ? if (!modbusCheckResponseAdu()) { ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? } ? ? ? ? ? ? dbgLog("recv_checked: \n"); ? ? ? ? ? ? dbgPrintData(pComm->recv, pComm->actRecvLen); ? ? ? ? } ? ? } } #define MAX_SAMPLE_NUM ? 8 uint8_t gTestSamples[][MAX_DATA_LEN] = {? ?// 协议上的示例 ? ? {0x08, 0x01, 0x01, 0x00, 0x13, 0x00, 0x13, 0x8C, 0x02}, ?//read coils request ? ? {0x08, 0x01, 0x01, 0x03, 0xCD, 0x6B, 0x05, 0x42, 0x82}, ?//read coils response ? ? {0x05, 0x01, 0x81, 0x01, 0x81, 0x90}, ?//read coils response exception ? ? {0x08, 0x01, 0x02, 0x00, 0xC4, 0x00, 0x16, 0xB8, 0x39}, ?//read discrete input request ? ? {0x08, 0x01, 0x02, 0x03, 0xAC, 0xDB, 0x35, 0x22, 0x88}, ?//read discrete input response ? ? {0x05, 0x01, 0x82, 0x01, 0x81, 0x60}, ?//read discrete input response exception ? ? {0x08, 0x01, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x74, 0x17}, ?//read register request ? ? {0x0B, 0x01, 0x03, 0x06, 0x02, 0x2B, 0x00, 0x00, 0x00, 0x64, 0x05, 0x7A}, ?//read register response ? ? {0x05, 0x01, 0x83, 0x01, 0x80, 0xF0}, ?//read register response exception ? ? {0x08, 0x01, 0x04, 0x00, 0x08, 0x00, 0x01, 0xB0, 0x08}, ?//read input registers request ? ? {0x07, 0x01, 0x04, 0x02, 0x00, 0x0A, 0x39, 0x37}, ?//read input registers response ? ? {0x05, 0x01, 0x84, 0x01, 0x82, 0xC0}, ?//read input registers response exception ? ? {0x08, 0x01, 0x05, 0x00, 0xAC, 0xFF, 0x00, 0x4C, 0x1B}, ?//write single coil request ? ? {0x08, 0x01, 0x05, 0x00, 0xAC, 0xFF, 0x00, 0x4C, 0x1B}, ?//write single coil response ? ? {0x05, 0x01, 0x85, 0x01, 0x83, 0x50}, ?//write single coil response exception ? ? {0x08, 0x01, 0x06, 0x00, 0x01, 0x00, 0x03, 0x98, 0x0B}, ?//write single register request ? ? {0x08, 0x01, 0x06, 0x00, 0x01, 0x00, 0x03, 0x98, 0x0B}, ?//write single register response ? ? {0x05, 0x01, 0x86, 0x01, 0x83, 0xA0}, ?//write single register response exception ? ? {0x0B, 0x01, 0x0F, 0x00, 0x13, 0x00, 0x0A, 0x02, 0xCD, 0x01, 0x72, 0xCB}, ?//write multiple coil request ? ? {0x08, 0x01, 0x0F, 0x00, 0x13, 0x00, 0x0A, 0x24, 0x09}, ?//write multiple coil response ? ? {0x05, 0x01, 0x8F, 0x01, 0x85, 0xF0}, ?//write multiple coil response exception ? ? {0x0D, 0x01, 0x10, 0x00, 0x01, 0x00, 0x02, 0x04, 0x00, 0x0A, 0x01, 0x02, 0x92, 0x30}, ?//write multiple register request ? ? {0x08, 0x01, 0x10, 0x00, 0x01, 0x00, 0x02, 0x10, 0x08}, ?//write multiple register response ? ? {0x05, 0x01, 0x90, 0x01, 0x8D, 0xC0}, ?//write multiple register response exception }; void modbusRequestAndWaitResponseSample(void) {? // 测试代码,调用前准备好 send, recv 数据 ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? if (modbusCheckRequestAdu()) { ? ? ? ? dbgLog("send: \n"); ? ? ? ? dbgPrintData(pComm->send, pComm->sendLen); ? ? ? ? if (pComm->actRecvLen != -1) { ? ? ? ? ? ? dbgLog("recv: \n"); ? ? ? ? ? ? dbgPrintData(pComm->recv, pComm->actRecvLen); ? ? ? ? ? ? if (!modbusCheckResponseAdu()) { ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? } ? ? ? ? ? ? dbgLog("recv_checked: \n"); ? ? ? ? ? ? dbgPrintData(pComm->recv, pComm->actRecvLen); ? ? ? ? } ? ? } } void modbusTest(void) {? //测试代码 ? ? uint8_t i; ? ? tsModbusComm_t *pComm = &gModbusComm; ? ? dbgEnter(); ? ? dbgLog("\r\n----------------------------------------\r\n"); ? ? for (i = 0; i < 2; i++) {? ?//测试485 Request and response,需要连接 slaver ? ? ? ? pComm->sendLen = gTestSamples[5*3+1][0]; ? ? ? ? memcpy(pComm->send, &gTestSamples[5*3+1][1], pComm->sendLen); ? ? ? ? modbusRequestAndWaitResponse(); ? ? ? ? dbgLog("\r\n----------------------------------------\r\n"); ? ? ? ? sleep(2); ? ? } ? ? for (i=0; i<MAX_SAMPLE_NUM; i++) // 测试sample,检测代码编写是否正确 ? ? { ? ? ? ? memset(pComm, 0x00, sizeof(tsModbusComm_t)); ? ? ? ? pComm->sendLen = gTestSamples[i*3][0]; ? ? ? ? memcpy(pComm->send, &gTestSamples[i*3][1], pComm->sendLen); ? ? ? ? pComm->actRecvLen = gTestSamples[i*3+1][0]; ? ? ? ? memcpy(pComm->recv, &gTestSamples[i*3+1][1], pComm->actRecvLen); ? ? ? ? modbusRequestAndWaitResponseSample(); ? ? ? ? pComm->actRecvLen = gTestSamples[i*3+2][0]; ? ? ? ? memcpy(pComm->recv, &gTestSamples[i*3+2][1], pComm->actRecvLen); ? ? ? ? modbusRequestAndWaitResponseSample(); ? ? ? ? dbgLog("\r\n----------------------------------------\r\n"); ? ? ? ? sleep(2); ? ? } ? ? dbgLeave(); } // 测试485通信时,用 C# 做了一个串口工具,收到数据后自动回复同样的数据,可以初步测试?write single register request。 |