00. 目录

文章目录

  • 00. 目录
  • 01. RTU模式概述
  • 02. modbus-rtu-private.h文件
  • 03. modbus-rtu.h文件
  • 04. modbus-rtu.c文件
  • 05. 附录

01. RTU模式概述

RTU模式相关源码文件为modbus-rtu.h、modbus-rtu-private.h和modbus-rtu.c文件。

02. modbus-rtu-private.h文件

RTU模式私有的数据类型和函数声明

/** Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>** SPDX-License-Identifier: LGPL-2.1-or-later*/#ifndef MODBUS_RTU_PRIVATE_H
#define MODBUS_RTU_PRIVATE_H#ifndef _MSC_VER
#include <stdint.h>
#else
#include "stdint.h"
#endif#if defined(_WIN32)
#include <windows.h>
#else
#include <termios.h>
#endif#define _MODBUS_RTU_HEADER_LENGTH      1
#define _MODBUS_RTU_PRESET_REQ_LENGTH  6
#define _MODBUS_RTU_PRESET_RSP_LENGTH  2#define _MODBUS_RTU_CHECKSUM_LENGTH    2#if defined(_WIN32)
#if !defined(ENOTSUP)
#define ENOTSUP WSAEOPNOTSUPP
#endif/* WIN32: struct containing serial handle and a receive buffer */
#define PY_BUF_SIZE 512
struct win32_ser {/* File handle */HANDLE fd;/* Receive buffer */uint8_t buf[PY_BUF_SIZE];/* Received chars */DWORD n_bytes;
};
#endif /* _WIN32 */typedef struct _modbus_rtu {/* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */char *device;/* Bauds: 9600, 19200, 57600, 115200, etc */int baud;/* Data bit */uint8_t data_bit;/* Stop bit */uint8_t stop_bit;/* Parity: 'N', 'O', 'E' */char parity;
#if defined(_WIN32)struct win32_ser w_ser;DCB old_dcb;
#else/* Save old termios settings */struct termios old_tios;
#endif
#if HAVE_DECL_TIOCSRS485int serial_mode;
#endif
#if HAVE_DECL_TIOCM_RTSint rts;int rts_delay;int onebyte_time;void (*set_rts) (modbus_t *ctx, int on);
#endif/* To handle many slaves on the same link */int confirmation_to_ignore;
} modbus_rtu_t;#endif /* MODBUS_RTU_PRIVATE_H */

03. modbus-rtu.h文件

RTU模式对外开放的API函数声明

/** Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>** SPDX-License-Identifier: LGPL-2.1-or-later*/#ifndef MODBUS_RTU_H
#define MODBUS_RTU_H#include "modbus.h"MODBUS_BEGIN_DECLS/* Modbus_Application_Protocol_V1_1b.pdf Chapter 4 Section 1 Page 5* RS232 / RS485 ADU = 253 bytes + slave (1 byte) + CRC (2 bytes) = 256 bytes*/
#define MODBUS_RTU_MAX_ADU_LENGTH  256
//创建RTU类型的结构体
//device: "COM1 ~ COM9"  /dev/ttyS0 /dev/ttyUSB0 /dev/ttyUSB1等等
//baud 波特率的设置值 9600 19200 57600 115200
//parity:奇偶校验位 'N' 'E' 'O'
//data_bit: 数据位 5 6 7 8
//stop_bit: 停止位 1 2
MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity,int data_bit, int stop_bit);#define MODBUS_RTU_RS232 0
#define MODBUS_RTU_RS485 1
//设置串口模式
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
//获取串口模式
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);#define MODBUS_RTU_RTS_NONE   0
#define MODBUS_RTU_RTS_UP     1
#define MODBUS_RTU_RTS_DOWN   2
//以下函数一般不常用
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on));MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);MODBUS_END_DECLS#endif /* MODBUS_RTU_H */

04. modbus-rtu.c文件

modbus-rtu.c 通信层实现,RTU模式相关的函数定义,主要是串口的设置、连接及消息的发送和接收等等。

/** Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>** SPDX-License-Identifier: LGPL-2.1-or-later*/#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <assert.h>#include "modbus-private.h"#include "modbus-rtu.h"
#include "modbus-rtu-private.h"#if HAVE_DECL_TIOCSRS485 || HAVE_DECL_TIOCM_RTS
#include <sys/ioctl.h>
#endif#if HAVE_DECL_TIOCSRS485
#include <linux/serial.h>
#endif/* Table of CRC values for high-order byte */
static const uint8_t table_crc_hi[] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};/* Table of CRC values for low-order byte */
static const uint8_t table_crc_lo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};/* Define the slave ID of the remote device to talk in master mode or set the* internal slave ID in slave mode */
static int _modbus_set_slave(modbus_t *ctx, int slave)
{/* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */if (slave >= 0 && slave <= 247) {ctx->slave = slave;} else {errno = EINVAL;return -1;}return 0;
}/* Builds a RTU request header */
static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function,int addr, int nb,uint8_t *req)
{assert(ctx->slave != -1);req[0] = ctx->slave;    //从设备IDreq[1] = function;      //功能码req[2] = addr >> 8;     //地址高位req[3] = addr & 0x00ff; //地址低位req[4] = nb >> 8;       //数量高位req[5] = nb & 0x00ff;   //数量低位return _MODBUS_RTU_PRESET_REQ_LENGTH;
}/* Builds a RTU response header */
static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp)
{/* In this case, the slave is certainly valid because a check is already* done in _modbus_rtu_listen */rsp[0] = sft->slave;rsp[1] = sft->function;return _MODBUS_RTU_PRESET_RSP_LENGTH;
}static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
{uint8_t crc_hi = 0xFF; /* high CRC byte initialized */uint8_t crc_lo = 0xFF; /* low CRC byte initialized */unsigned int i; /* will index into CRC lookup *//* pass through message buffer */while (buffer_length--) {i = crc_hi ^ *buffer++; /* calculate the CRC  */crc_hi = crc_lo ^ table_crc_hi[i];crc_lo = table_crc_lo[i];}return (crc_hi << 8 | crc_lo);
}static int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length)
{(*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH;/* No TID */return 0;
}
//计算CRC值 填入查询消息帧
static int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
{uint16_t crc = crc16(req, req_length);req[req_length++] = crc >> 8;req[req_length++] = crc & 0x00FF;return req_length;
}#if defined(_WIN32)/* This simple implementation is sort of a substitute of the select() call,* working this way: the win32_ser_select() call tries to read some data from* the serial port, setting the timeout as the select() call would. Data read is* stored into the receive buffer, that is then consumed by the win32_ser_read()* call.  So win32_ser_select() does both the event waiting and the reading,* while win32_ser_read() only consumes the receive buffer.*/static void win32_ser_init(struct win32_ser *ws)
{/* Clear everything */memset(ws, 0x00, sizeof(struct win32_ser));/* Set file handle to invalid */ws->fd = INVALID_HANDLE_VALUE;
}/* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */
static int win32_ser_select(struct win32_ser *ws, int max_len,const struct timeval *tv)
{COMMTIMEOUTS comm_to;unsigned int msec = 0;/* Check if some data still in the buffer to be consumed */if (ws->n_bytes > 0) {return 1;}/* Setup timeouts like select() would do.FIXME Please someone on Windows can look at this?Does it possible to use WaitCommEvent?When tv is NULL, MAXDWORD isn't infinite!*/if (tv == NULL) {msec = MAXDWORD;} else {msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;if (msec < 1)msec = 1;}comm_to.ReadIntervalTimeout = msec;comm_to.ReadTotalTimeoutMultiplier = 0;comm_to.ReadTotalTimeoutConstant = msec;comm_to.WriteTotalTimeoutMultiplier = 0;comm_to.WriteTotalTimeoutConstant = 1000;SetCommTimeouts(ws->fd, &comm_to);/* Read some bytes */if ((max_len > PY_BUF_SIZE) || (max_len < 0)) {max_len = PY_BUF_SIZE;}if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) {/* Check if some bytes available */if (ws->n_bytes > 0) {/* Some bytes read */return 1;} else {/* Just timed out */return 0;}} else {/* Some kind of error */return -1;}
}static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg,unsigned int max_len)
{unsigned int n = ws->n_bytes;if (max_len < n) {n = max_len;}if (n > 0) {memcpy(p_msg, ws->buf, n);}ws->n_bytes -= n;return n;
}
#endif#if HAVE_DECL_TIOCM_RTS
static void _modbus_rtu_ioctl_rts(modbus_t *ctx, int on)
{int fd = ctx->s;int flags;ioctl(fd, TIOCMGET, &flags);if (on) {flags |= TIOCM_RTS;} else {flags &= ~TIOCM_RTS;}ioctl(fd, TIOCMSET, &flags);
}
#endifstatic ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{#if defined(_WIN32)modbus_rtu_t *ctx_rtu = ctx->backend_data;DWORD n_bytes = 0;return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? (ssize_t)n_bytes : -1;
#else
#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu = ctx->backend_data;if (ctx_rtu->rts != MODBUS_RTU_RTS_NONE) {ssize_t size;if (ctx->debug) {fprintf(stderr, "Sending request using RTS signal\n");}ctx_rtu->set_rts(ctx, ctx_rtu->rts == MODBUS_RTU_RTS_UP);usleep(ctx_rtu->rts_delay);size = write(ctx->s, req, req_length);usleep(ctx_rtu->onebyte_time * req_length + ctx_rtu->rts_delay);ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);return size;} else {#endifreturn write(ctx->s, req, req_length);
#if HAVE_DECL_TIOCM_RTS}
#endif
#endif
}static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
{int rc;modbus_rtu_t *ctx_rtu = ctx->backend_data;if (ctx_rtu->confirmation_to_ignore) {_modbus_receive_msg(ctx, req, MSG_CONFIRMATION);/* Ignore errors and reset the flag */ctx_rtu->confirmation_to_ignore = FALSE;rc = 0;if (ctx->debug) {printf("Confirmation to ignore\n");}} else {rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);if (rc == 0) {/* The next expected message is a confirmation to ignore */ctx_rtu->confirmation_to_ignore = TRUE;}}return rc;
}static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
{#if defined(_WIN32)return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
#elsereturn read(ctx->s, rsp, rsp_length);
#endif
}static int _modbus_rtu_flush(modbus_t *);static int _modbus_rtu_pre_check_confirmation(modbus_t *ctx, const uint8_t *req,const uint8_t *rsp, int rsp_length)
{/* Check responding slave is the slave we requested (except for broacast* request) */if (req[0] != rsp[0] && req[0] != MODBUS_BROADCAST_ADDRESS) {if (ctx->debug) {fprintf(stderr,"The responding slave %d isn't the requested slave %d\n",rsp[0], req[0]);}errno = EMBBADSLAVE;return -1;} else {return 0;}
}/* The check_crc16 function shall return 0 if the message is ignored and themessage length if the CRC is valid. Otherwise it shall return -1 and seterrno to EMBBADCRC. */
static int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,const int msg_length)
{uint16_t crc_calculated;uint16_t crc_received;int slave = msg[0];/* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless* CRC computing. */if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {if (ctx->debug) {printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave);}/* Following call to check_confirmation handles this error */return 0;}crc_calculated = crc16(msg, msg_length - 2);crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];/* Check CRC of msg */if (crc_calculated == crc_received) {return msg_length;} else {if (ctx->debug) {fprintf(stderr, "ERROR CRC received 0x%0X != CRC calculated 0x%0X\n",crc_received, crc_calculated);}if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {_modbus_rtu_flush(ctx);}errno = EMBBADCRC;return -1;}
}/* Sets up a serial port for RTU communications */
static int _modbus_rtu_connect(modbus_t *ctx)
{#if defined(_WIN32)DCB dcb;
#elsestruct termios tios;speed_t speed;int flags;
#endifmodbus_rtu_t *ctx_rtu = ctx->backend_data;if (ctx->debug) {printf("Opening %s at %d bauds (%c, %d, %d)\n",ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,ctx_rtu->data_bit, ctx_rtu->stop_bit);}#if defined(_WIN32)/* Some references here:* http://msdn.microsoft.com/en-us/library/aa450602.aspx*/win32_ser_init(&ctx_rtu->w_ser);/* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal* number */ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);/* Error checking */if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) {if (ctx->debug) {fprintf(stderr, "ERROR Can't open the device %s (LastError %d)\n",ctx_rtu->device, (int)GetLastError());}return -1;}/* Save params */ctx_rtu->old_dcb.DCBlength = sizeof(DCB);if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) {if (ctx->debug) {fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n",(int)GetLastError());}CloseHandle(ctx_rtu->w_ser.fd);ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;return -1;}/* Build new configuration (starting from current settings) */dcb = ctx_rtu->old_dcb;/* Speed setting */switch (ctx_rtu->baud) {case 110:dcb.BaudRate = CBR_110;break;case 300:dcb.BaudRate = CBR_300;break;case 600:dcb.BaudRate = CBR_600;break;case 1200:dcb.BaudRate = CBR_1200;break;case 2400:dcb.BaudRate = CBR_2400;break;case 4800:dcb.BaudRate = CBR_4800;break;case 9600:dcb.BaudRate = CBR_9600;break;case 14400:dcb.BaudRate = CBR_14400;break;case 19200:dcb.BaudRate = CBR_19200;break;case 38400:dcb.BaudRate = CBR_38400;break;case 57600:dcb.BaudRate = CBR_57600;break;case 115200:dcb.BaudRate = CBR_115200;break;case 230400:/* CBR_230400 - not defined */dcb.BaudRate = 230400;break;case 250000:dcb.BaudRate = 250000;break;case 460800:dcb.BaudRate = 460800;break;case 500000:dcb.BaudRate = 500000;break;case 921600:dcb.BaudRate = 921600;break;case 1000000:dcb.BaudRate = 1000000;break;default:dcb.BaudRate = CBR_9600;if (ctx->debug) {fprintf(stderr, "WARNING Unknown baud rate %d for %s (B9600 used)\n",ctx_rtu->baud, ctx_rtu->device);}}/* Data bits */switch (ctx_rtu->data_bit) {case 5:dcb.ByteSize = 5;break;case 6:dcb.ByteSize = 6;break;case 7:dcb.ByteSize = 7;break;case 8:default:dcb.ByteSize = 8;break;}/* Stop bits */if (ctx_rtu->stop_bit == 1)dcb.StopBits = ONESTOPBIT;else /* 2 */dcb.StopBits = TWOSTOPBITS;/* Parity */if (ctx_rtu->parity == 'N') {dcb.Parity = NOPARITY;dcb.fParity = FALSE;} else if (ctx_rtu->parity == 'E') {dcb.Parity = EVENPARITY;dcb.fParity = TRUE;} else {/* odd */dcb.Parity = ODDPARITY;dcb.fParity = TRUE;}/* Hardware handshaking left as default settings retrieved *//* No software handshaking */dcb.fTXContinueOnXoff = TRUE;dcb.fOutX = FALSE;dcb.fInX = FALSE;/* Binary mode (it's the only supported on Windows anyway) */dcb.fBinary = TRUE;/* Don't want errors to be blocking */dcb.fAbortOnError = FALSE;/* Setup port */if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) {if (ctx->debug) {fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n",(int)GetLastError());}CloseHandle(ctx_rtu->w_ser.fd);ctx_rtu->w_ser.fd = INVALID_HANDLE_VALUE;return -1;}
#else/* The O_NOCTTY flag tells UNIX that this program doesn't wantto be the "controlling terminal" for that port. If youdon't specify this then any input (such as keyboard abortsignals and so forth) will affect your processTimeouts are ignored in canonical input mode or when theNDELAY option is set on the file via open or fcntl */flags = O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL;
#ifdef O_CLOEXECflags |= O_CLOEXEC;
#endifctx->s = open(ctx_rtu->device, flags);if (ctx->s == -1) {if (ctx->debug) {fprintf(stderr, "ERROR Can't open the device %s (%s)\n",ctx_rtu->device, strerror(errno));}return -1;}/* Save */tcgetattr(ctx->s, &ctx_rtu->old_tios);memset(&tios, 0, sizeof(struct termios));/* C_ISPEED     Input baud (new interface)C_OSPEED     Output baud (new interface)*/switch (ctx_rtu->baud) {case 110:speed = B110;break;case 300:speed = B300;break;case 600:speed = B600;break;case 1200:speed = B1200;break;case 2400:speed = B2400;break;case 4800:speed = B4800;break;case 9600:speed = B9600;break;case 19200:speed = B19200;break;case 38400:speed = B38400;break;
#ifdef B57600case 57600:speed = B57600;break;
#endif
#ifdef B115200case 115200:speed = B115200;break;
#endif
#ifdef B230400case 230400:speed = B230400;break;
#endif
#ifdef B460800case 460800:speed = B460800;break;
#endif
#ifdef B500000case 500000:speed = B500000;break;
#endif
#ifdef B576000case 576000:speed = B576000;break;
#endif
#ifdef B921600case 921600:speed = B921600;break;
#endif
#ifdef B1000000case 1000000:speed = B1000000;break;
#endif
#ifdef B1152000case 1152000:speed = B1152000;break;
#endif
#ifdef B1500000case 1500000:speed = B1500000;break;
#endif
#ifdef B2500000case 2500000:speed = B2500000;break;
#endif
#ifdef B3000000case 3000000:speed = B3000000;break;
#endif
#ifdef B3500000case 3500000:speed = B3500000;break;
#endif
#ifdef B4000000case 4000000:speed = B4000000;break;
#endifdefault:speed = B9600;if (ctx->debug) {fprintf(stderr,"WARNING Unknown baud rate %d for %s (B9600 used)\n",ctx_rtu->baud, ctx_rtu->device);}}/* Set the baud rate */if ((cfsetispeed(&tios, speed) < 0) ||(cfsetospeed(&tios, speed) < 0)) {close(ctx->s);ctx->s = -1;return -1;}/* C_CFLAG      Control optionsCLOCAL       Local line - do not change "owner" of portCREAD        Enable receiver*/tios.c_cflag |= (CREAD | CLOCAL);/* CSIZE, HUPCL, CRTSCTS (hardware flow control) *//* Set data bits (5, 6, 7, 8 bits)CSIZE        Bit mask for data bits*/tios.c_cflag &= ~CSIZE;switch (ctx_rtu->data_bit) {case 5:tios.c_cflag |= CS5;break;case 6:tios.c_cflag |= CS6;break;case 7:tios.c_cflag |= CS7;break;case 8:default:tios.c_cflag |= CS8;break;}/* Stop bit (1 or 2) */if (ctx_rtu->stop_bit == 1)tios.c_cflag &=~ CSTOPB;else /* 2 */tios.c_cflag |= CSTOPB;/* PARENB       Enable parity bitPARODD       Use odd parity instead of even */if (ctx_rtu->parity == 'N') {/* None */tios.c_cflag &=~ PARENB;} else if (ctx_rtu->parity == 'E') {/* Even */tios.c_cflag |= PARENB;tios.c_cflag &=~ PARODD;} else {/* Odd */tios.c_cflag |= PARENB;tios.c_cflag |= PARODD;}/* Read the man page of termios if you need more information. *//* This field isn't used on POSIX systemstios.c_line = 0;*//* C_LFLAG      Line optionsISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signalsICANON       Enable canonical input (else raw)XCASE        Map uppercase \lowercase (obsolete)ECHO Enable echoing of input charactersECHOE        Echo erase character as BS-SP-BSECHOK        Echo NL after kill characterECHONL       Echo NLNOFLSH       Disable flushing of input buffers afterinterrupt or quit charactersIEXTEN       Enable extended functionsECHOCTL      Echo control characters as ^char and delete as ~?ECHOPRT      Echo erased character as character erasedECHOKE       BS-SP-BS entire line on line killFLUSHO       Output being flushedPENDIN       Retype pending input at next read or input charTOSTOP       Send SIGTTOU for background outputCanonical input is line-oriented. Input characters are putinto a buffer which can be edited interactively by the useruntil a CR (carriage return) or LF (line feed) character isreceived.Raw input is unprocessed. Input characters are passedthrough exactly as they are received, when they arereceived. Generally you'll deselect the ICANON, ECHO,ECHOE, and ISIG options when using raw input*//* Raw input */tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);/* C_IFLAG      Input optionsConstant     DescriptionINPCK        Enable parity checkIGNPAR       Ignore parity errorsPARMRK       Mark parity errorsISTRIP       Strip parity bitsIXON Enable software flow control (outgoing)IXOFF        Enable software flow control (incoming)IXANY        Allow any character to start flow againIGNBRK       Ignore break conditionBRKINT       Send a SIGINT when a break condition is detectedINLCR        Map NL to CRIGNCR        Ignore CRICRNL        Map CR to NLIUCLC        Map uppercase to lowercaseIMAXBEL      Echo BEL on input line too long*/if (ctx_rtu->parity == 'N') {/* None */tios.c_iflag &= ~INPCK;} else {tios.c_iflag |= INPCK;}/* Software flow control is disabled */tios.c_iflag &= ~(IXON | IXOFF | IXANY);/* C_OFLAG      Output optionsOPOST        Postprocess output (not set = raw output)ONLCR        Map NL to CR-NLONCLR ant others needs OPOST to be enabled*//* Raw output */tios.c_oflag &=~ OPOST;/* C_CC         Control charactersVMIN         Minimum number of characters to readVTIME        Time to wait for data (tenths of seconds)UNIX serial interface drivers provide the ability tospecify character and packet timeouts. Two elements of thec_cc array are used for timeouts: VMIN and VTIME. Timeoutsare ignored in canonical input mode or when the NDELAYoption is set on the file via open or fcntl.VMIN specifies the minimum number of characters to read. Ifit is set to 0, then the VTIME value specifies the time towait for every character read. Note that this does not meanthat a read call for N bytes will wait for N characters tocome in. Rather, the timeout will apply to the firstcharacter and the read call will return the number ofcharacters immediately available (up to the number yourequest).If VMIN is non-zero, VTIME specifies the time to wait forthe first character read. If a character is read within thetime given, any read will block (wait) until all VMINcharacters are read. That is, once the first character isread, the serial interface driver expects to receive anentire packet of characters (VMIN bytes total). If nocharacter is read within the time allowed, then the call toread returns 0. This method allows you to tell the serialdriver you need exactly N bytes and any read call willreturn 0 or N bytes. However, the timeout only applies tothe first character read, so if for some reason the drivermisses one character inside the N byte packet then the readcall could block forever waiting for additional inputcharacters.VTIME specifies the amount of time to wait for incomingcharacters in tenths of seconds. If VTIME is set to 0 (thedefault), reads will block (wait) indefinitely unless theNDELAY option is set on the port with open or fcntl.*//* Unused because we use open with the NDELAY option */tios.c_cc[VMIN] = 0;tios.c_cc[VTIME] = 0;if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {close(ctx->s);ctx->s = -1;return -1;}
#endifreturn 0;
}int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode)
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCSRS485modbus_rtu_t *ctx_rtu = ctx->backend_data;struct serial_rs485 rs485conf;if (mode == MODBUS_RTU_RS485) {// Getif (ioctl(ctx->s, TIOCGRS485, &rs485conf) < 0) {return -1;}// Setrs485conf.flags |= SER_RS485_ENABLED;if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {return -1;}ctx_rtu->serial_mode = MODBUS_RTU_RS485;return 0;} else if (mode == MODBUS_RTU_RS232) {/* Turn off RS485 mode only if required */if (ctx_rtu->serial_mode == MODBUS_RTU_RS485) {/* The ioctl call is avoided because it can fail on some RS232 ports */if (ioctl(ctx->s, TIOCGRS485, &rs485conf) < 0) {return -1;}rs485conf.flags &= ~SER_RS485_ENABLED;if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {return -1;}}ctx_rtu->serial_mode = MODBUS_RTU_RS232;return 0;}
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif}/* Wrong backend and invalid mode specified */errno = EINVAL;return -1;
}int modbus_rtu_get_serial_mode(modbus_t *ctx)
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCSRS485modbus_rtu_t *ctx_rtu = ctx->backend_data;return ctx_rtu->serial_mode;
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif} else {errno = EINVAL;return -1;}
}int modbus_rtu_get_rts(modbus_t *ctx)
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu = ctx->backend_data;return ctx_rtu->rts;
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif} else {errno = EINVAL;return -1;}
}int modbus_rtu_set_rts(modbus_t *ctx, int mode)
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu = ctx->backend_data;if (mode == MODBUS_RTU_RTS_NONE || mode == MODBUS_RTU_RTS_UP ||mode == MODBUS_RTU_RTS_DOWN) {ctx_rtu->rts = mode;/* Set the RTS bit in order to not reserve the RS485 bus */ctx_rtu->set_rts(ctx, ctx_rtu->rts != MODBUS_RTU_RTS_UP);return 0;} else {errno = EINVAL;return -1;}
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif}/* Wrong backend or invalid mode specified */errno = EINVAL;return -1;
}int modbus_rtu_set_custom_rts(modbus_t *ctx, void (*set_rts) (modbus_t *ctx, int on))
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu = ctx->backend_data;ctx_rtu->set_rts = set_rts;return 0;
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif} else {errno = EINVAL;return -1;}
}int modbus_rtu_get_rts_delay(modbus_t *ctx)
{if (ctx == NULL) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu;ctx_rtu = (modbus_rtu_t *)ctx->backend_data;return ctx_rtu->rts_delay;
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif} else {errno = EINVAL;return -1;}
}int modbus_rtu_set_rts_delay(modbus_t *ctx, int us)
{if (ctx == NULL || us < 0) {errno = EINVAL;return -1;}if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {#if HAVE_DECL_TIOCM_RTSmodbus_rtu_t *ctx_rtu;ctx_rtu = (modbus_rtu_t *)ctx->backend_data;ctx_rtu->rts_delay = us;return 0;
#elseif (ctx->debug) {fprintf(stderr, "This function isn't supported on your platform\n");}errno = ENOTSUP;return -1;
#endif} else {errno = EINVAL;return -1;}
}static void _modbus_rtu_close(modbus_t *ctx)
{/* Restore line settings and close file descriptor in RTU mode */modbus_rtu_t *ctx_rtu = ctx->backend_data;#if defined(_WIN32)/* Revert settings */if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb) && ctx->debug) {fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n",(int)GetLastError());}if (!CloseHandle(ctx_rtu->w_ser.fd) && ctx->debug) {fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n",(int)GetLastError());}
#elseif (ctx->s != -1) {tcsetattr(ctx->s, TCSANOW, &ctx_rtu->old_tios);close(ctx->s);ctx->s = -1;}
#endif
}static int _modbus_rtu_flush(modbus_t *ctx)
{#if defined(_WIN32)modbus_rtu_t *ctx_rtu = ctx->backend_data;ctx_rtu->w_ser.n_bytes = 0;return (PurgeComm(ctx_rtu->w_ser.fd, PURGE_RXCLEAR) == FALSE);
#elsereturn tcflush(ctx->s, TCIOFLUSH);
#endif
}static int _modbus_rtu_select(modbus_t *ctx, fd_set *rset,struct timeval *tv, int length_to_read)
{int s_rc;
#if defined(_WIN32)s_rc = win32_ser_select(&((modbus_rtu_t *)ctx->backend_data)->w_ser,length_to_read, tv);if (s_rc == 0) {errno = ETIMEDOUT;return -1;}if (s_rc < 0) {return -1;}
#elsewhile ((s_rc = select(ctx->s+1, rset, NULL, NULL, tv)) == -1) {if (errno == EINTR) {if (ctx->debug) {fprintf(stderr, "A non blocked signal was caught\n");}/* Necessary after an error */FD_ZERO(rset);FD_SET(ctx->s, rset);} else {return -1;}}if (s_rc == 0) {/* Timeout */errno = ETIMEDOUT;return -1;}
#endifreturn s_rc;
}static void _modbus_rtu_free(modbus_t *ctx) {if (ctx->backend_data) {free(((modbus_rtu_t *)ctx->backend_data)->device);free(ctx->backend_data);}free(ctx);
}const modbus_backend_t _modbus_rtu_backend = {_MODBUS_BACKEND_TYPE_RTU,_MODBUS_RTU_HEADER_LENGTH,_MODBUS_RTU_CHECKSUM_LENGTH,MODBUS_RTU_MAX_ADU_LENGTH,_modbus_set_slave,_modbus_rtu_build_request_basis,_modbus_rtu_build_response_basis,_modbus_rtu_prepare_response_tid,_modbus_rtu_send_msg_pre,_modbus_rtu_send,_modbus_rtu_receive,_modbus_rtu_recv,_modbus_rtu_check_integrity,_modbus_rtu_pre_check_confirmation,_modbus_rtu_connect,_modbus_rtu_close,_modbus_rtu_flush,_modbus_rtu_select,_modbus_rtu_free
};modbus_t* modbus_new_rtu(const char *device,int baud, char parity, int data_bit,int stop_bit)
{modbus_t *ctx;modbus_rtu_t *ctx_rtu;/* Check device argument */if (device == NULL || *device == 0) {fprintf(stderr, "The device string is empty\n");errno = EINVAL;return NULL;}/* Check baud argument */if (baud == 0) {fprintf(stderr, "The baud rate value must not be zero\n");errno = EINVAL;return NULL;}ctx = (modbus_t *)malloc(sizeof(modbus_t));if (ctx == NULL) {return NULL;}_modbus_init_common(ctx);ctx->backend = &_modbus_rtu_backend;ctx->backend_data = (modbus_rtu_t *)malloc(sizeof(modbus_rtu_t));if (ctx->backend_data == NULL) {modbus_free(ctx);errno = ENOMEM;return NULL;}ctx_rtu = (modbus_rtu_t *)ctx->backend_data;/* Device name and \0 */ctx_rtu->device = (char *)malloc((strlen(device) + 1) * sizeof(char));if (ctx_rtu->device == NULL) {modbus_free(ctx);errno = ENOMEM;return NULL;}strcpy(ctx_rtu->device, device);ctx_rtu->baud = baud;if (parity == 'N' || parity == 'E' || parity == 'O') {ctx_rtu->parity = parity;} else {modbus_free(ctx);errno = EINVAL;return NULL;}ctx_rtu->data_bit = data_bit;ctx_rtu->stop_bit = stop_bit;#if HAVE_DECL_TIOCSRS485/* The RS232 mode has been set by default */ctx_rtu->serial_mode = MODBUS_RTU_RS232;
#endif#if HAVE_DECL_TIOCM_RTS/* The RTS use has been set by default */ctx_rtu->rts = MODBUS_RTU_RTS_NONE;/* Calculate estimated time in micro second to send one byte */ctx_rtu->onebyte_time = 1000000 * (1 + data_bit + (parity == 'N' ? 0 : 1) + stop_bit) / baud;/* The internal function is used by default to set RTS */ctx_rtu->set_rts = _modbus_rtu_ioctl_rts;/* The delay before and after transmission when toggling the RTS pin */ctx_rtu->rts_delay = ctx_rtu->onebyte_time;
#endifctx_rtu->confirmation_to_ignore = FALSE;return ctx;
}

05. 附录

【嵌入式】Libmodbus源码分析(四)-RTU相关函数分析相关推荐

  1. 【嵌入式】Libmodbus源码分析(一)-类型和结构体

    00. 目录 文章目录 00. 目录 01. 常见类型声明 02. 常量声明 03. _modbus结构体声明 04. modbus_backend_t结构体声明 05. modbus_mapping ...

  2. 【嵌入式】Libmodbus源码分析(二)-常用接口函数分析

    00. 目录 文章目录 00. 目录 01. modbus常用接口函数概述 02. modbus辅助接口函数 03. modbus功能接口函数 04. modbus数据处理 05. 附录 01. mo ...

  3. 39 网络相关函数(七)——live555源码阅读(四)网络

    39 网络相关函数(七)--live555源码阅读(四)网络 39 网络相关函数(七)--live555源码阅读(四)网络 简介 14)readSocket从套接口读取数据 recv/recvfrom ...

  4. Android 源码 Camera2 预览流程分析四

    <Android 源码 Camera2 预览流程分析二>中进行了流启动,这是调用 QCamera3Channel start() 方法实现的,对应于 HAL_PIXEL_FORMAT_YC ...

  5. mybatis源码阅读(四):mapper(dao)实例化

    转载自   mybatis源码阅读(四):mapper(dao)实例化 在开始分析之前,先来了解一下这个模块中的核心组件之间的关系,如图: 1.MapperRegistry&MapperPro ...

  6. 老李推荐:第5章5节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 获取系统服务引用 1...

    老李推荐:第5章5节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 获取系统服务引用 上一节我们描述了monkey的命令处理入口函数run是如何调用optionPro ...

  7. 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 1...

    老李推荐:第6章1节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览 在上一章中我们有简要的介绍了事件源是怎么一回事,但是并没有进行详细的描述.那么往下的这几个 ...

  8. 一点一点看JDK源码(四)java.util.ArrayList 中篇

    一点一点看JDK源码(四)java.util.ArrayList 中篇 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 在前篇中 ...

  9. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列...

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列 事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些事件 ...

  10. Unity3D RPG角色扮演游戏源码(上下)-----源代码分析----01-----主角人物动画

    在源代码的里面有fbx格式的模型文件,发现有2个文件,一个是骨骼动画文件,可以分割为多个动画片段,还有一个是模型文件,但是没有动画,但是可以导入分割好的动画片段到动画元素里面, 按照下面的说明,分割了 ...

最新文章

  1. matlab去除图片水印_Python利用OpenCV去除图片水印
  2. 危机边缘第一季/全集Fringe迅雷下载
  3. 基于FPGA的bubble游戏开发
  4. (建议收藏)万字长文,帮你一招搞定产品经理面试-详解产品经理面试大全
  5. poj 3469(最小割)
  6. python 小甲鱼 代码_Python小代码
  7. PDF签名系列(1):PDF签名机制的漏洞分析
  8. 消息人士:欧盟下月将对英伟达收购Arm交易展开正式调查
  9. Jupyter notebook最简原型界面设计 - ipywidgets与lineup_widget
  10. Dubbox学习笔记
  11. [2018.10.10 T1] 餐馆
  12. 哈夫曼树的构造和哈夫曼编码实现详细讲解(含例题详细讲解)
  13. 【校园快递信息系统——开题报告 分享(仅供参考呀)】
  14. 假期娱乐和学习兼得型STEAM智能机器人有哪些?(2018-2019)
  15. 那年花一个钟用PS改证件照的背景色,今天用Excel我只花了60秒!
  16. python入门培训课程
  17. Qt+VTK+PCL图片转灰度图且以灰度为Y轴显示
  18. JS 进一法 四舍五入
  19. xp 计算机信息服务器,个人电脑xp搭建云服务器
  20. 未来5G物联网的安全形态,看英飞凌eSIM

热门文章

  1. 惠普微型计算机怎么装机,台式小机惠普电脑怎么装系统
  2. php递归函数名字,php递归示例 php递归函数代码
  3. 平板电脑可以装python吗_电脑上的应用程序可以装在平板电脑上吗
  4. winpe加载raid_winpe里添加raid驱动
  5. 数据分析:RFM模型
  6. java 资源描述文件_j2me新手必看-Java应用描述文件(JAD)编辑器
  7. Ext.grid.EditorGridPanel使用方法
  8. 数字图像处理 关于matlab的图像处理操作
  9. 【AI视野·今日CV 计算机视觉论文速览 第155期】Fri, 6 Sep 2019
  10. JAVA商城系统开发 VS PHP商城系统开发