Modbus Slave学习笔记

2023-05-16

第一章 初识Modbus

1.1什么是Modbus

简而言之,Modbus通信协议是工业领域通信协议的业界标准,并且是当前工业电子设备之间相当常用的连接方式之一。

​ Modbus 协议是应用于电子控制器上的一种通用语言。通过此协议,可以实现控制器相互之间、控制器经由网络和其他设备之间的通信。它已经成为一种通用的工业标准,有了它,不同厂商生产的控制设备可以连接成工业网络,进行集中监控。此协议定义了一个控制器能够认识使用的消息结构,而不管它们是经过何种网络进行通信的;而且描述了控制器请求访问其他设备的过程,如何应答来 自
其他设备的请求,以及怎样侦测错误并记录;并制定了统一的消息域的结构和内容。

​ 当在 Modbus 网络上通信时,此协议决定了每个控制器必须要知道它们的设备地址,识别按地址发来的消息,决定要产生何种行为。如果需要回应,控制器将生成反馈信息并通过 Modbus 协议发送。

​ Modbus 通信协议具有以下几个特点∶
​ ● Modbus 协议标准开放、公开发表并且无版税要求。用户可以免费获取并使用 Modbus 协议,不需要交纳许可证费,也不会侵犯知识产权。
​ ●Modbus 协议可以支持多种电气接口,如 RS232、RS485、TCP/IP 等;还可以在各种介质上传输,如双绞线、光纤、红外、无线等。
​ ● Modbus 协议消息帧格式简单、紧凑、通俗易懂。用户理解和使用简单,厂商容易开发和集成,方便形成工业控制网络。

1.2模型

​ Modbus 是 OSI 模型第 7 层之上的应用层报文传输协议,它在不同类型总线或网络设备之间提供主站设备/从站设备(或客户机/服务器)通信。自从 1979 年发布并成为工业串行链路通信的事实标准以来,Modbus 使成千上万的自动化设备能够通信。目前,为了继续增加对简单而优雅的 Modbus 通信协议的支持,国际互联网组织规定并保留 了 TCP/IP 协议栈上的系统 502 端口,专门用于访问 Modbus 设备。Modbus 协议栈模型如图所示。
在这里插入图片描述

1.3 协议版本

​ Modbus 通信协议目前存在用于串行链路、TCP/IP 以太网以及其他支持互联网协议的网络版本。大多数 Modbus 设备通信通过串口(RS232/RS485)或 TCP/IP物理层进行连接。

​ 对于 Modbus 串行链路连接,存在两个变种,它们在协议细节上略有不同,主要区别是传输数据的字节表示上的不同。这两个变种包括 RTU 模式和 ASCII 模式。ModbusRTU 模式是一种紧凑的,采用二进制表示数据的方式;而 ModbusASCII 模式是一种人类可读的、冗长的表示方式。这两个变种都使用串行链路通信(Serial Communication)方式,为了确保数据传输的完整性和准确性,RTU 模式下消息格式命令和数据带有循环冗余校验的校验和,而 ASCII 模式下消息格式采用纵向冗余校验的校验和,而且被配置为 RTU 模式的节点不能与配置为 ASCII模式的节点通信,反之亦然。
​ 对于通过 TCP/IP(例如以太 网)物理层的连接,存在多个 Modbus/TCP 变种,这种方式不需要校验和的计算。
​ 对于以上这 3 种通信模式,在数据模型和功能调用上都是相同的,只有传输报文封装方式是不同的。

1.4 通信设备

​ 通常情况下,Modbus 协议是一个主/从(Master/Slave)或客户 端/服务器(Client/Server)架构的协议。通信网络中有一个节点是 Master 节点;其他使用 Modbus 协议参与通信的节点是 Slave 节点,每一个 Slave 设备都有一个唯一的地址。在串行和 MB十网络中,只有被指定为主节点的节点可以启动一个命令(在以太网上,任何一个设备都能发送一个 Modbus 命令,但是通常也只有一个主节点设备用以引导指令)。
​ 一个 Modbus 命令包含了准备执行指令的设备的 Modbus 地址。线路上所有设备都会收到命令,但只有指定地址的设备会执行并回应指令(地址 0 例外,指定地址 0的指令是广播指令,所有收到指令的设备都会运行,不过不用回应指令)。所有 Modbus 传输报文都包含了错误校验码,以确定到达的命令是否完整。例如,基本的 Modbus 命令能指示一个 Modbus RTU设备改变它的寄存器的某个值,控制或者读取一个I/O 端口,以及指挥设备回送一个或者多个其寄存器中的数据。

1.5帧结构

​ Modbus 是一个请求/应答协议,并且提供统一的功能码用于数据传输服务。 Modbus 功能码是 Modbus 请求/应答 PDU(即 PROTOCOL DATA UNIT,协议数据单元)的元素之一,所谓的 PDU 是 Modbus 协议定义的一个与基础通信层无关的简单协议数据单元。而在特定总线或网络上,Modbus 协议则通过ADU(即APPLICATION DATA UNIT,应用数据单元)引入一些附加域,以实现完整而准确的数据传输。
​ 为了寻求一种简洁的通信格式,Modbus 协议定义了 PDU 模型,即功能码+数据的格式;而为了适应多种传输模式,在 PDU 的基础上增加了必要的前缀(如地址域)和后缀(如差错校验),形成了 ADU模型。

​ ADU与PDU 之间的关系如图所示。

在这里插入图片描述

1.6 事务处理

​ 主机设备(或客户端)创建 Modbus 应用数据单元形成查询报文,其中功能码标识了向从机设备(或服务器端)指示将执行哪种操作。功能码占用一个字节,有效的码字范围是十进制 1~255(其中 128~255 为异常响应保留)。香询报文创建完毕,主机设备(或客户端)向从机设备(或服务器端)发送报文,从机设备(或服务器端)接收报文后,根据功能码做出相应的动作,并将响应报文返回给主机设备(或客户端),如图 所示。
在这里插入图片描述

​ 如果在一个正确接收的 Modbus ADU中,不出现与请求 Modbus 功能有关的差错》那么从机设备(或服务器端)将返回正常的响应报文。如果出现与请求 Modbus 功能有关的差错,那么响应报文的功能码域将包括一个异常码,主机设备(或客户端)能够根据异常码确定下一个执行的操作。
​ 如图所示,对于异常响应,从机设备(或服务器端)将返回一个与原始功能码等同的码值,但设置该原始功能码的最高有效位为逻辑 1,用干通知主机设备(或客户端)。

在这里插入图片描述

第二章 Modbus开发环境

2.1 VSPD

2.2 Modbus Poll

2.3 Modbus Slave

第三章 Modbus 协议相关

3.1 协议概要

​ 简而言之,Modbus 协议是一种单主/多从的通信协议,其特点是在同一时间,总线上只能有—个主设备,但可以有一个或者多个(最多 247 个)从设备。Modbus通信总是由主设备发起,当从设备没有收到来自主设备的请求时,不会主动发送数据。从设备之间不能相互通信,主设备同时只能启动一个 Modbus 访问事务处理。

​ 主设备可以采用两种方式向从设备发送 Modbus 请求报文,即主设备可以对指定的单个从设备或者线路上所有的从设备发送请求报文,而从设备 只能被动接收请求报文后给出响应报文即应答。这两种模式分别如下;

在这里插入图片描述

​ ● 单播模式∶ 主设备仅仅寻址单个从设备。从设备接收并处理完请求之后,向主设备返回一个响应报文,即应答。在这种模式下,一个 Modbus 事务处理包含两个报文∶一个是主设备的请求报文,一个是从设备的响应报文。每个从设备必须有唯一的地址(地址范围 1~247),这样才能区别于其他从设备从而可以独立被寻址,而主设备不占用地址。
​ ● 广播模式∶此种模式下,主设备可以向所有的从设备发送请求指令。而从设备在接收到广播指令后,仅仅进行相关指令的事务处理而不要求返回应答。

​ 基于此,广播模式下,请求指令必须是 Modbus 标准功能中的写指令。
​ 根据 Modbus 标准协议的要求,所有从设备必须接收广播模式下的写指令,且地址 0被保留用来识别广播通信。

​ 1.请求
​ 主设备发送的请求报文主要包括从设备地址(或者广播地址 0)、功能码、传输的数据以及差错检测字段。
​ 查询消息中的功能码告之被选中的从设备要执行何种功能。数据段包含 了从设备要执行功能的任何附加信息。 例如功能代码 03 要求从设备读保持寄存器
并返回其内容。
​ 数据段必须包含要告之从设备的信息∶ 从何寄存器开始读取及要读取的寄存器数量。差错检测域为从设备提供了—种验证消息内容是否正确的方法。

​ 2.应答
​ 从设备的应答报文包括地址、功能码、差错检测域等。
​ 如果从设备产生一个正常的回应,则在回应消息中的功能代码是在查询消息中的功能代码的回应。数据段包括了从设备收集的数据,如寄存器值或状态。
如果有错误发生,功能代码将被修改以用于指出 回应消息是错误的,同时数据段包含了描述此错误信息的代码。差错检测域允许主设备确认消息内容是否可用。
​ 对于串行链路来说,又存在两种传输帧模式∶ ASCII(American Standard Code
for Information Interchange,美 国标 准 信 息 交换码)模式 和 RTU(Remote Terminal Unit)模式。但是,对于同一网络或链路来说,所有的设备必须保持统一,要么统一为 ASCII模式,要么统一为 RTU 模式,不可共存。相对来说,RTU模式传输效率更高,因此,在当前普遍的生产环境中 RTU 模式获得了广泛应用,而 ASCII 模式只作为特殊情况下的可选项。

3.2寄存器

基本表对象类型访问类型注释
离散量输入单个位只读I/O系统可提供这种类型数据
线圈单个位读写通过应用程序而可以改变这种类型数据
输入寄存器16位字只读I/O系统可提供这种类型数据
保持寄存器16位字读写通过应用程序而可以改变这种类型数据

3.3串行消息帧格式

3.3.1 ASCII消息帧格式

​ 当控制器设为在 Modbus 网络上以 ASCII 模式通信时,在消息中每个 8 位(bit)的字节都将作为两个 ASCII 字符发送。这种方式的主要优点是字符发送的
时间间隔可达到 1秒而不产生错误。
​ 在 ASCII模式下,消息以冒号(∶)字符(ASCII 码 0x3A)开始,以回车换行符结束(ASCII 码 0x0D,0x0A)。消息帧的其他字段(域)可以使用的传输字符是十六进制的 0…9,A…F。处于网络上的 Modbus 设备不断侦测"∶"字符,当有一个冒号接收到时,每个设备进入解码阶段,并解码下一个字段(地址域)来判断是否是发给自 已的。消息帧中的字符间发送的时间间隔最长不能超过 1 秒,否则接收的设备将认为发生传输错误。
​ 一个典型的 ASCII 消息帧格式如表所示。
在这里插入图片描述

3.3.2 RTU消息帧格式

​ 传输设备(主/从设备)将 Modbus 报文放置在带有已知起始和结束点的消息帧中,这就要求接收消息帧的设备在报文的起始处开始接收,并且要知道报文传
输何时结束。另外还必须能够检测到不完整的报文,且能够清晰地设置 错误标志。
​ 在 RTU 模式中,消息的发送和接收以至少 3.5 个字符时间的停顿间隔为标志。实际使用中,网络设备不断侦测网络总线,计算字符间的停顿间隔时间,判断
消息帧的起始点。 当接收到第—个域(地址域)时,每个设备都进行解码以判断是否是发给自己的。在最后一个传输字符结束之后,一个至少 3.5个字符时间的停
顿标定了消息的结束,而一个新的消息可在此停顿后开始。另外,在一帧报文中,必须以连续的字符流发送整个报文帧。如果两个字符之间的空闲间隔大于 1.5个字符时间,那么认为报文帧不完整,该报文将被丢弃。
​ 需要记住的是∶
​ ● 3.5 时间间隔目的是作为区别前后两帧数据的分隔符。

​ ● 3.5 时间间隔只对 RTU模式有效。

​ 如图 3-2 所示,Modbus 通信时规定主机发送完一组命令必须间隔 3.5个字符再发送下一组新命令,这 3.5 个字符主要用来告诉其他设备这次命令(数据)已结束。这 3.5个字符的时间间隔采用以下方式计算∶
​ 通常情况下在串行通信中,1 个字符包括1 位起始位、8 位数据位、1 位校验位(或者没有)、1 位停止位(一般情况下)。这样一般情况下 1 个字符就包括 11 位,那么 3.5个字符就是3.5×11=38.5 位。
​ 而串行通信中波特率的含义是每秒传输的二进制位的个数。例如波特率为9600bps,则意思就是说每 1s(也就是 1000ms)传输 9600 个位的数据;反过来说传输 9600 个二进制位的数据需要 1000ms,那么传输 38.5 个二进制位的数据需要的时间就是∶
​ 38.5×(1000/9600)=4.0104167ms
​ 如图 3-2 所示,Modbus RTU 要求相邻两帧数据的起始和结束之间至少有大于等于3.5 个字符的时间间隔,那么在波特率为 9600bps 的情况下,只要大于 4.0104167ms 即可!

在这里插入图片描述

3.3.3 地址域

​ 所谓地址域,指的是 Modbus 通信帧中的地址字段,其内容为从设备地址。 Modbus 消息帧的地址域包含 2 个字符(ASCII模式)或者1个字节(RTU 模式)。
​ 消息帧中可能的从设备地址是 0~247(十进制),单个设备的实际地址范围是1~247(参见表 3-4)。主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。当从设备发送回应消息时,它把自己已的地址放入回应的地址域中,以便主设备知道是哪一个设备做出回应。
​ 地址 0用作广播地址,以使所有的从设备都能认识。当 Modbus 协议用于更高级别的网络时,广播方式可能不被允许或以其他方式代替。
在这里插入图片描述

3.3.4 功能码域

​ 功能码在 Modbus 协议中用于表示消息帧的功能。
​ 功能码域由 1 个字节构成,因此其取值范围为 1~255(十进制)。例如,常用的功能码有 03、04、06、16 等,其中 03 功能码的作用是读保持寄存器内容,04 功能码的作用是读输入寄存器内容(输入寄存器和保持寄存器的区别可参考上一节的内容),06 功能码的内容是预置单个保持寄存器,16 功能码的内容则是预置多个保持寄存器。
​ 从设备根据功能码执行对应的功能,执行完成后,正常情况下则在返回的响应消息帧中设置同样的功能码;如果出现异常,则在返回的消息帧中将功能码最
高位(MSB)设置为 1。据此,主设备可获知对应从设备的执行情况。
​ 另外,对于主设备发送的功能码,则从设备根据具体配置来决定是否支持此功能码。如果不支持,则返回异常响应。

3.3.5 数据域

​ 数据域与功能码紧密相关,存放功能码需要操作的具体数据。数据域以字节为单位,长度是可变的,对于有些功能码,数据域可以为空。

3.4差错检验

​ 1.ASCII 模式
​ 在 ASCII 模式中,报文包含一个错误校验字段。该字段由两个字符组成,其值基于对全部报文内容执行的纵向冗余校验(Longitudinal Redundancy Check, LRC)计算的结果而来,计算对象不包括起始的冒号(;)和回车换行符号(CRLF)。
​ 2.RTU模式
​ 在 RTU 模式中,报文同样包含一个错误校验字段。与 ASCII 模式不同的是,该字段由 16 个比特位共两个字节组成。其值基于对全部报文内容执行的循环冗
余校验(Cyclical Redundancy Check,CRC)计算的结果而来,计算对象包括校验域之前的所有字节。

3.4.1 LRC校验

​ (1)对消息帧中的全部字节相加(不包括起始";“和结束符"CR-LF”),并把结果送入 8 位数据区,舍弃进位。
​ (2)由 0xFF(即全 1)减去最终的数据值,产生 1的补码(即二进制反码)。

​ (3)加"1"产生二进制补码。

3.4.2 CRC校验

​ (1)预置一个值为 0xFFFF 的 16 位寄存器,此寄存器为 CRC寄存器。
​ (2)把第 1 个 8 位二进制数据(即通信消息帧的第 1 个字节)与 16 位的 CRC寄存器的相异或,异或的结果仍存放于该 CRC 寄存器中。
​ (3)把 CRC寄存器的内容右移一位,用 0 填补最高位,并检测移出位是 0 还是1。
​ (4)如果移出位为零,则重复步骤(3)(再次右移一位);如果移出位为 1,则CRC 寄存器与0xA001 进行异或。
​ (5)重复步骤(3)和(4),直到右移 8 次,这样整个 8 位数据全部进行了处理。(6)重复步骤(2)~(5),进行通信消息帧下一个字节的处理。
​ (7)将该通信消息帧所有字节按上述步骤计算完成后,得到的 16 位 CRC 寄存器的高、低字节进行交换。即发送时首先添加低位字节,然后添加高位字节。
​ (8)最后得到的 CRC 寄存器内容即为 CRC 校验码。

第四章 功能码

​ 简而言之,Modbus 功能码占用一个字节,取值范围是 1~127 。之所以 127以上不能使用,是因为 Modbus 规定当出现异常时,功能码士0x80(十进制 128)代表异常状态,因此 129(1+128)~255(127+128)的取值代表异常码。
​ Modbus 标准协议中规定了有 3 类 Modbus 功能码,分别是∶

​ 1.公共功能码
​ (1)被明确定义的功能码;

​ (2)保证唯一性;
​ (3)由 Modbus 协会确认,并提供公开的文档;

​ (4)可进行一致性测试;
​ (5)包括协议定义的功能码和保留将来使用的功能码。

​ 2.用户自定义功能码
​ (1)有两个用户自定义功能码区域,分别是65~72 和 100~110;
​ (2)用户自定义,不保证唯一性。

​ 3.保留功能码
​ 保留功能码是因为历史遗留原因,某些公司的传统产品上现行使用的功能码不作为公共使用。

在这里插入图片描述在这里插入图片描述

​ 功能码可分为位操作和字操作两类。位操作的最小单位为一位(bit),字操作的最小单位为两个字节。
​ ● 位操作指令;读线圈状态功能码 01,读(离散)输入状态功能码 02,写单个线圈功能码 06 和写多个线圈功能码 15。
​ ●字操作指令∶读保持寄存器功能码 03,读输入寄存器功能码 04,写单个保持寄存器功能码 06,写多个保持寄存器功能码 16。

4.1 01 (0x01)读线圈应用方法

读线圈01 (0x01)
请求
功能码1字节0x01
起始地址2字节0x0000至0xFFFF
线圈数量2字节1至2000(0x7D0)
响应
功能码1字节0x01
字节计数1字节N
线圈状态n字节n = N或N + 1
N = 寄存器数量/8,余数不等0,那么N = N + 1
错误
功能码1字节功能码 + 0x80
异常码1字节01或02或03或04

4.2 03 (0x03)读保持寄存器应用方法

请求
功能码1个字节0x03
起始地址2个字节0x0000 to 0xFFFF
寄存器数量2个字节1 to 125 (0x7D)
响应
功能码1个字节0x03
字节数1个字节2 x N*
寄存器值N* x 2个字节
*N = 寄存器数量
错误
差错码1个字节0x83
异常码1个字节01或02或03或04

4.3 06 (0x06)写单个寄存器应用方法

4.4 15 (0x0F) 写多个线圈应用方法

4.5 16 (0x10) 写多个寄存器应用方法

第五章 libmobus库

​ libmodbus 是一个免费的跨平台的支持 RTU和 TCP 的 Modbus 库,遵循LGPLv2.1+ 协议。libmodbus 支持 Linux、Mac OS X、FreeBSD、QNX 和
Windows 等操作系统。libmodbus 可以向符合 Modbus 协议的设备发送和接收数据,并支持通过串口或者 TCP 网络进行连接。

​ https://github.com/stephane/libmodbus.git

在这里插入图片描述

libmodbus库与应用程序的基本关系。

在这里插入图片描述

第六章 libmodbus源码解析

第七章 RTU Slave开发

RTU-Slave的代码流程:
1.初始化并生成modbus_t 结构体;
2.设置从机端的ID;
3.起点调试模式;
4.建立modbus连接;
5.modbus_mapping_new()初始化寄存器,返回一个modbus_mapping_t 指针;
6.调用modbus_receive()函数判断串口的接收数据,负责接收和分析;
7.调用modbus_reply()函数,对接收到的请求指示发送响应(回复);
8.释放modbus_mapping_t 结构体;
9.关闭modbus连接;
10.释放modbus_t 结构体。
在这里插入图片描述

(1) 初始化

ctx = modbus_new_rtu("COM4", 9600, 'N', 8, 1); //创建一个RTU类型的容器,即创建一个COM口为C0M4 波特率9600bit/s

MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity, ​ int data_bit, int stop_bit);

设置从机地址

modbus_set_slave(ctx, SERVER_ID);

/* Define the slave number */
int modbus_set_slave(modbus_t *ctx, int slave)
{
    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    return ctx->backend->set_slave(ctx, slave);
}

int modbus_get_slave(modbus_t *ctx)
{
    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    return ctx->slave;
}

(2) 建立连接

int modbus_connect(modbus_t *ctx)
{
    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    return ctx->backend->connect(ctx);
}

(3)申请内存块用作4种寄存器数据存放

mb_mapping = modbus_mapping_new(500, 500, 500, 500);

(4)循环查询和响应

	for (;;)
	{
		//MODBUS_TCP_MAX_ADU_LENGTH,RTU帧格式最大数据字符数
		uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
		int rc;

		rc = modbus_receive(ctx, query);
		if (rc >= 0)
		{
			/*rc is the query size*/
			modbus_reply(ctx, query, rc, mb_mapping);
		}
		else
		{
			//connection closed by the client or error
			printf("Connection Closed\n");
		}
	}

(5)释放结构体,关闭连接

	modbus_mapping_free(mb_mapping);
	modbus_close(ctx);
	modbus_free(ctx);

7.1 modbus_receive

/* Waits a response from a modbus server or a request from a modbus client.
   This function blocks if there is no replies (3 timeouts).

   The function shall return the number of received characters and the received
   message in an array of uint8_t if successful. Otherwise it shall return -1
   and errno is set to one of the values defined below:
   - ECONNRESET
   - EMBBADDATA
   - EMBUNKEXC
   - ETIMEDOUT
   - read() or recv() error codes
*/

int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
{
    int rc;
    fd_set rset;
    struct timeval tv;
    struct timeval *p_tv;
    int length_to_read;
    int msg_length = 0;
    _step_t step;

    if (ctx->debug) {
        if (msg_type == MSG_INDICATION) {
            printf("Waiting for an indication...\n");
        } else {
            printf("Waiting for a confirmation...\n");
        }
    }

    /* Add a file descriptor to the set */
    FD_ZERO(&rset);
    FD_SET(ctx->s, &rset);

    /* We need to analyse the message step by step.  At the first step, we want
     * to reach the function code because all packets contain this
     * information. */
    step = _STEP_FUNCTION;
    length_to_read = ctx->backend->header_length + 1;

    if (msg_type == MSG_INDICATION) {
        /* Wait for a message, we don't know when the message will be
         * received */
        if (ctx->indication_timeout.tv_sec == 0 && ctx->indication_timeout.tv_usec == 0) {
            /* By default, the indication timeout isn't set */
            p_tv = NULL;
        } else {
            /* Wait for an indication (name of a received request by a server, see schema) */
            tv.tv_sec = ctx->indication_timeout.tv_sec;
            tv.tv_usec = ctx->indication_timeout.tv_usec;
            p_tv = &tv;
        }
    } else {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }

    while (length_to_read != 0) {
        rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read);
        if (rc == -1) {
            _error_print(ctx, "select");
            if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
                int saved_errno = errno;

                if (errno == ETIMEDOUT) {
                    _sleep_response_timeout(ctx);
                    modbus_flush(ctx);
                } else if (errno == EBADF) {
                    modbus_close(ctx);
                    modbus_connect(ctx);
                }
                errno = saved_errno;
            }
            return -1;
        }

        rc = ctx->backend->recv(ctx, msg + msg_length, length_to_read);
        if (rc == 0) {
            errno = ECONNRESET;
            rc = -1;
        }

        if (rc == -1) {
            _error_print(ctx, "read");
            if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
                (errno == ECONNRESET || errno == ECONNREFUSED ||
                 errno == EBADF)) {
                int saved_errno = errno;
                modbus_close(ctx);
                modbus_connect(ctx);
                /* Could be removed by previous calls */
                errno = saved_errno;
            }
            return -1;
        }

        /* Display the hex code of each character received */
        if (ctx->debug) {
            int i;
            for (i=0; i < rc; i++)
                printf("<%.2X>", msg[msg_length + i]);
        }

        /* Sums bytes received */
        msg_length += rc;
        /* Computes remaining bytes */
        length_to_read -= rc;

        if (length_to_read == 0) {
            switch (step) {
            case _STEP_FUNCTION:
                /* Function code position */
                length_to_read = compute_meta_length_after_function(
                    msg[ctx->backend->header_length],
                    msg_type);
                if (length_to_read != 0) {
                    step = _STEP_META;
                    break;
                } /* else switches straight to the next step */
            case _STEP_META:
                length_to_read = compute_data_length_after_meta(
                    ctx, msg, msg_type);
                if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
                    errno = EMBBADDATA;
                    _error_print(ctx, "too many data");
                    return -1;
                }
                step = _STEP_DATA;
                break;
            default:
                break;
            }
        }

        if (length_to_read > 0 &&
            (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
            /* If there is no character in the buffer, the allowed timeout
               interval between two consecutive bytes is defined by
               byte_timeout */
            tv.tv_sec = ctx->byte_timeout.tv_sec;
            tv.tv_usec = ctx->byte_timeout.tv_usec;
            p_tv = &tv;
        }
        /* else timeout isn't set again, the full response must be read before
           expiration of response timeout (for CONFIRMATION only) */
    }

    if (ctx->debug)
        printf("\n");

    return ctx->backend->check_integrity(ctx, msg, msg_length);
}

/* Receive the request from a modbus master */
int modbus_receive(modbus_t *ctx, uint8_t *req)
{
    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    return ctx->backend->receive(ctx, req);
}

7.2modbus_reply

/* Send a response to the received request.
   Analyses the request and constructs a response.

   If an error occurs, this function construct the response
   accordingly.
*/
int modbus_reply(modbus_t *ctx, const uint8_t *req,
                 int req_length, modbus_mapping_t *mb_mapping)
{
    int offset;
    int slave;
    int function;
    uint16_t address;
    uint8_t rsp[MAX_MESSAGE_LENGTH];
    int rsp_length = 0;
    sft_t sft;

    if (ctx == NULL) {
        errno = EINVAL;
        return -1;
    }

    offset = ctx->backend->header_length;
    slave = req[offset - 1];
    function = req[offset];
    address = (req[offset + 1] << 8) + req[offset + 2];

    sft.slave = slave;
    sft.function = function;
    sft.t_id = ctx->backend->prepare_response_tid(req, &req_length);

    
    
    /* Data are flushed on illegal number of values errors. */
    /********************************************************/
    
    switch (function) {
    case MODBUS_FC_READ_COILS:
    case MODBUS_FC_READ_DISCRETE_INPUTS: {
        unsigned int is_input = (function == MODBUS_FC_READ_DISCRETE_INPUTS);
        int start_bits = is_input ? mb_mapping->start_input_bits : mb_mapping->start_bits;
        int nb_bits = is_input ? mb_mapping->nb_input_bits : mb_mapping->nb_bits;
        uint8_t *tab_bits = is_input ? mb_mapping->tab_input_bits : mb_mapping->tab_bits;
        const char * const name = is_input ? "read_input_bits" : "read_bits";
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        /* The mapping can be shifted to reduce memory consumption and it
           doesn't always start at address zero. */
        int mapping_address = address - start_bits;

        if (nb < 1 || MODBUS_MAX_READ_BITS < nb) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal nb of values %d in %s (max %d)\n",
                nb, name, MODBUS_MAX_READ_BITS);
        } else if (mapping_address < 0 || (mapping_address + nb) > nb_bits) {
            rsp_length = response_exception(
                ctx, &sft,
                MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in %s\n",
                mapping_address < 0 ? address : address + nb, name);
        } else {
            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
            rsp_length = response_io_status(tab_bits, mapping_address, nb,
                                            rsp, rsp_length);
        }
    }
        break;
    
    /*****************************************HOLDING_REGISTERS*************************************/
    case MODBUS_FC_READ_HOLDING_REGISTERS:
    case MODBUS_FC_READ_INPUT_REGISTERS: {
        unsigned int is_input = (function == MODBUS_FC_READ_INPUT_REGISTERS);
        int start_registers = is_input ? mb_mapping->start_input_registers : mb_mapping->start_registers;
        int nb_registers = is_input ? mb_mapping->nb_input_registers : mb_mapping->nb_registers;
        uint16_t *tab_registers = is_input ? mb_mapping->tab_input_registers : mb_mapping->tab_registers;
        const char * const name = is_input ? "read_input_registers" : "read_registers";
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        /* The mapping can be shifted to reduce memory consumption and it
           doesn't always start at address zero. */
        int mapping_address = address - start_registers;

        if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal nb of values %d in %s (max %d)\n",
                nb, name, MODBUS_MAX_READ_REGISTERS);
        } else if (mapping_address < 0 || (mapping_address + nb) > nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in %s\n",
                mapping_address < 0 ? address : address + nb, name);
        } else {
            int i;

            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            rsp[rsp_length++] = nb << 1;
            for (i = mapping_address; i < mapping_address + nb; i++) {
                rsp[rsp_length++] = tab_registers[i] >> 8;
                rsp[rsp_length++] = tab_registers[i] & 0xFF;
            }
        }
    }
        break;
            
    /***********************************************************************************/
    case MODBUS_FC_WRITE_SINGLE_COIL: {
        int mapping_address = address - mb_mapping->start_bits;

        if (mapping_address < 0 || mapping_address >= mb_mapping->nb_bits) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_bit\n",
                address);
        } else {
            int data = (req[offset + 3] << 8) + req[offset + 4];

            if (data == 0xFF00 || data == 0x0) {
                mb_mapping->tab_bits[mapping_address] = data ? ON : OFF;
                memcpy(rsp, req, req_length);
                rsp_length = req_length;
            } else {
                rsp_length = response_exception(
                    ctx, &sft,
                    MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, FALSE,
                    "Illegal data value 0x%0X in write_bit request at address %0X\n",
                    data, address);
            }
        }
    }
        break;
    case MODBUS_FC_WRITE_SINGLE_REGISTER: {
        int mapping_address = address - mb_mapping->start_registers;

        if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft,
                MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_register\n",
                address);
        } else {
            int data = (req[offset + 3] << 8) + req[offset + 4];

            mb_mapping->tab_registers[mapping_address] = data;
            memcpy(rsp, req, req_length);
            rsp_length = req_length;
        }
    }
        break;
    case MODBUS_FC_WRITE_MULTIPLE_COILS: {
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        int nb_bits = req[offset + 5];
        int mapping_address = address - mb_mapping->start_bits;

        if (nb < 1 || MODBUS_MAX_WRITE_BITS < nb || nb_bits * 8 < nb) {
            /* May be the indication has been truncated on reading because of
             * invalid address (eg. nb is 0 but the request contains values to
             * write) so it's necessary to flush. */
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal number of values %d in write_bits (max %d)\n",
                nb, MODBUS_MAX_WRITE_BITS);
        } else if (mapping_address < 0 ||
                   (mapping_address + nb) > mb_mapping->nb_bits) {
            rsp_length = response_exception(
                ctx, &sft,
                MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_bits\n",
                mapping_address < 0 ? address : address + nb);
        } else {
            /* 6 = byte count */
            modbus_set_bits_from_bytes(mb_mapping->tab_bits, mapping_address, nb,
                                       &req[offset + 6]);

            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            /* 4 to copy the bit address (2) and the quantity of bits */
            memcpy(rsp + rsp_length, req + rsp_length, 4);
            rsp_length += 4;
        }
    }
        break;
    case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: {
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        int nb_bytes = req[offset + 5];
        int mapping_address = address - mb_mapping->start_registers;

        if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb || nb_bytes != nb * 2) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal number of values %d in write_registers (max %d)\n",
                nb, MODBUS_MAX_WRITE_REGISTERS);
        } else if (mapping_address < 0 ||
                   (mapping_address + nb) > mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_registers\n",
                mapping_address < 0 ? address : address + nb);
        } else {
            int i, j;
            for (i = mapping_address, j = 6; i < mapping_address + nb; i++, j += 2) {
                /* 6 and 7 = first value */
                mb_mapping->tab_registers[i] =
                    (req[offset + j] << 8) + req[offset + j + 1];
            }

            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            /* 4 to copy the address (2) and the no. of registers */
            memcpy(rsp + rsp_length, req + rsp_length, 4);
            rsp_length += 4;
        }
    }
        break;
    case MODBUS_FC_REPORT_SLAVE_ID: {
        int str_len;
        int byte_count_pos;

        rsp_length = ctx->backend->build_response_basis(&sft, rsp);
        /* Skip byte count for now */
        byte_count_pos = rsp_length++;
        rsp[rsp_length++] = _REPORT_SLAVE_ID;
        /* Run indicator status to ON */
        rsp[rsp_length++] = 0xFF;
        /* LMB + length of LIBMODBUS_VERSION_STRING */
        str_len = 3 + strlen(LIBMODBUS_VERSION_STRING);
        memcpy(rsp + rsp_length, "LMB" LIBMODBUS_VERSION_STRING, str_len);
        rsp_length += str_len;
        rsp[byte_count_pos] = rsp_length - byte_count_pos - 1;
    }
        break;
    case MODBUS_FC_READ_EXCEPTION_STATUS:
        if (ctx->debug) {
            fprintf(stderr, "FIXME Not implemented\n");
        }
        errno = ENOPROTOOPT;
        return -1;
        break;
    case MODBUS_FC_MASK_WRITE_REGISTER: {
        int mapping_address = address - mb_mapping->start_registers;

        if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_register\n",
                address);
        } else {
            uint16_t data = mb_mapping->tab_registers[mapping_address];
            uint16_t and = (req[offset + 3] << 8) + req[offset + 4];
            uint16_t or = (req[offset + 5] << 8) + req[offset + 6];

            data = (data & and) | (or & (~and));
            mb_mapping->tab_registers[mapping_address] = data;
            memcpy(rsp, req, req_length);
            rsp_length = req_length;
        }
    }
        break;
    case MODBUS_FC_WRITE_AND_READ_REGISTERS: {
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6];
        int nb_write = (req[offset + 7] << 8) + req[offset + 8];
        int nb_write_bytes = req[offset + 9];
        int mapping_address = address - mb_mapping->start_registers;
        int mapping_address_write = address_write - mb_mapping->start_registers;

        if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write ||
            nb < 1 || MODBUS_MAX_WR_READ_REGISTERS < nb ||
            nb_write_bytes != nb_write * 2) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal nb of values (W%d, R%d) in write_and_read_registers (max W%d, R%d)\n",
                nb_write, nb, MODBUS_MAX_WR_WRITE_REGISTERS, MODBUS_MAX_WR_READ_REGISTERS);
        } else if (mapping_address < 0 ||
                   (mapping_address + nb) > mb_mapping->nb_registers ||
                   mapping_address < 0 ||
                   (mapping_address_write + nb_write) > mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data read address 0x%0X or write address 0x%0X write_and_read_registers\n",
                mapping_address < 0 ? address : address + nb,
                mapping_address_write < 0 ? address_write : address_write + nb_write);
        } else {
            int i, j;
            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            rsp[rsp_length++] = nb << 1;

            /* Write first.
               10 and 11 are the offset of the first values to write */
            for (i = mapping_address_write, j = 10;
                 i < mapping_address_write + nb_write; i++, j += 2) {
                mb_mapping->tab_registers[i] =
                    (req[offset + j] << 8) + req[offset + j + 1];
            }

            /* and read the data for the response */
            for (i = mapping_address; i < mapping_address + nb; i++) {
                rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
                rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF;
            }
        }
    }
        break;

    default:
        rsp_length = response_exception(
            ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_FUNCTION, rsp, TRUE,
            "Unknown Modbus function code: 0x%0X\n", function);
        break;
    }

    /* Suppress any responses when the request was a broadcast */
    return (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU &&
            slave == MODBUS_BROADCAST_ADDRESS) ? 0 : send_msg(ctx, rsp, rsp_length);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Modbus Slave学习笔记 的相关文章

  • Linux应用学习——多线程

    多线程 PART1 线程相关概念 线程时参与系统调度的最小单位 被包含在进程之中 xff0c 是进程中的实际运行单位 一个进程可以创建多个线程 xff0c 多个线程实现并发运行 xff0c 每个线程执行不同的任务 线程时最基本的运行单位 x
  • Linux编程小工具

    编程小技巧 1 函数 结构体查询 需求背景 xff1a 安装了ctag安装使用ctag可以通过vim t 34 i2c client 34 来查询i2c client结构体的信息 不方便的地方 xff1a 需要进入指定的目录下才行 Git
  • Linux应用学习_网络开发

    Linux应用学习 网络开发 1 TCP套接字编程 1 1 套接字 1 1 1 通用套接字sockaddr span class token keyword struct span span class token class name s
  • Linux应用学习——高级I/O复用

    高级I O 阻塞I O 阻塞 xff0c 进入休眠状态 xff0c 让出CPU的使用权 xff0c 等待阻塞的条件为真普通文件一定是以非阻塞的方式进行I O操作文件默认是通过阻塞式操作的 fd span class token operat
  • 《从0到1大话操作系统》--(2)纸质内存中的数据结构

    你一定听过那句著名的等式 xff1a 程序 61 数据结构 43 算法 你可能不相信 xff0c 一个叫尼古拉斯 沃斯 xff08 Niklaus Wirth xff09 的瑞士计算机科学家 xff0c 凭这句话 xff0c 获得了图灵奖
  • 《从0到1大话操作系统》--(1)万物起源:编码

    古希腊毕达哥拉斯学派的世界观人为世界的本质是数 在计算机的世界中 xff0c 所有的一切也确实是数 万物起源 xff1a 编码 1 万物皆可编码2 ASCII编码3 互不兼容4 Unicode编码 1 万物皆可编码 曾经有一位懵懂的少年 x
  • 【安全】如何关闭Windows系统的137, 139, 445端口?

    4月14日晚 xff0c 一个叫Shadow Brokers的黑客组织泄露了一大波Windows的远程漏洞利用工具 xff0c 事件细节可以参照运维派前面的文章 Windows血崩 xff0c 一波大规模 0day 攻击泄漏 恩威科技 am
  • Linux中断、系统调用处理流程与进程切换

    1 中断与系统调用的过程 1 程序控制流 正常程序的控制流只有next xff08 执行下一条指令 xff09 和goto xff08 跳转到另一条指令执行 xff09 除了正常控制流之外 xff0c 还有异常控制流 xff0c 广义上异常
  • 开篇叙事

    开篇叙事 我为什么会在这儿我的主题我的经历 我为什么会在这儿 作为我第一次踏足CSDN xff0c 当然 xff0c 我说的是在CSDN上写博客 xff0c 我在CSDN上阅读博客已经有很长一段时间了 xff0c 这里有非常多的非常棒的博主
  • Redhat(红帽) Linux软件包管理rpm概述

    1 软件包管理的内涵 习惯了Windows上安装软件的方式 下载安装包 xff0c 双击运行 xff0c 点击几次下一步后 xff0c 软件就安装完了 我也是常年使用Windows xff0c 习惯了Windows的方式 xff0c 却从来
  • FreeRTOS(一):裸机系统与多任务系统

    裸机系统与多任务系统 3 1裸机系统3 1 1轮询系统3 1 2前后台系统 3 2多任务系统总结 3 1裸机系统 裸机 xff1a 英文名是Bare machine Bare metal xff0c 指没有配置操作系统和其他软件的电子计算机
  • FreeRTOS(二):数据结构—列表与列表项

    数据结构 列表与列表项 4 列表与列表项4 1链表4 1 1单向链表4 1 2双向链表4 1 3链表与数组的对比 4 2FreeRTOS中链表的实现4 2 1实现链表节点1 定义链表节点数据结构2 链表节点初始化 4 2 2实现链表根节点1
  • Cordova技术初次学习

    文章目录 前言一 Cordova 是什么 xff1f 二 关于Cordova环境搭建前提要求 xff1a 1 利用npm 安装2 创建应用3 添加平台4 运行 三 结语 前言 这是一篇关于Cordova入门学习的记录文章 一 Cordova
  • 修改datax,Java获取mongo,Document转换成json,遇到的类型问题,用JsonWriterSettings解决

    java numberlong 解决方法 在修改datax插件的时候遇到了问题 问题 xff1a 我们存在mongo中带有数据类型 xff0c 如图 我们直接将Document转json会带上类型 且在mongo java driver的j
  • VirtualBox 主机ping不通虚拟机的解决办法

    visualBox的主机是ping不通虚拟机的 但是虚拟机能ping通主机 要先进行以下配置 第一个网卡为NAT Network 在网络配置中 xff0c 新建一个网卡2为桥接网卡 新建共享文件夹 国外一个老哥说的 xff0c 不新建共享文
  • 基于python实现相关模板匹配跟踪之SSDA算法

    基于python实现相关模板匹配跟踪之SSDA算法 1 单张图片匹配 1 1代码 1 2 效果 2 视频跟踪 2 1代码 2 2 效果 2 3 优化思路 3 参考链接 相关模板匹配跟踪和质心跟踪都是传统算法 相较于AI算法 具有极快的处理速
  • AGV小车经典算法设计及应用

    1 AGV小车的发展背景 在现代化工业的发展中 xff0c 提倡高效 xff0c 快速 xff0c 可靠 xff0c 提倡将人从简单的工作中解放出来 机器人逐渐替代了人出现在各个工作岗位上 机器人具有可编程 可协调作业和基于传感器控制等特点
  • 扒完社交网络关系才明白,《权力的游戏》凭什么是神作

    摘要 xff1a 凡人皆有一死 没错 xff0c 但那是 凡人 会数据的 xff0c 都不是凡人 DT君当你看冰火的时候 xff0c 你在看什么 xff1f 作为一个 冰与火之歌 的资深迷弟 xff0c 看到如今的冰火电视 Valar Mo
  • 数字IC设计入门(1)初识电路板和芯片

    本文为入门级同学和零电子基础同学准备 xff0c 有一定电子基础的请直接忽略 xff0c 芯片从本质上说还是电路 xff0c 因此有必要从电路板开始了解芯片工作过程 xff0c 下面将简单介绍一块电脑主机板 电路板已在我们生活中无处不在 x
  • 数电4_4——常见组合逻辑电路(3)数据选择器

    数据选择器 1 1 工作原理1 1 1 电路图1 1 2 写出逻辑表达式 1 1 3 对应真值表 1 2 应用1 2 1 用双四选一设计八选一1 2 2 用数据选择器设计组合逻辑电路1 2 2 1 用四选一实现1 2 2 2 用八选一实现1

随机推荐

  • 机器学习实战之科比数据集分析(随机森林寻最优值参数)

    文章目录 总体思路分为三部1 查看数据 xff0c 对数据进行清洗 xff0c 规约1 1 查看数据1 2 数据清洗 规约1 3 删除不相关的特征1 4 数据one hot处理 2 建立模型 xff0c 挑选出最优参数2 1 准备数据集 训
  • 一文读懂anchor-base和anchor-free

    1 从Faster RCNN看Anchor Faster RCNN相对于Fast RCNN的一个改进是引入了RPN网络 xff0c RPN用于区域推荐 xff0c 替换了此前的SS算法使得网络在整体上更加的CNN化 那么RPN是怎么进行区域
  • 用MATLAB提取字符串中的特定类别(数字等)

    如果一个字符串中包含多种格式的字符 xff0c 想把其中特定的一种提取出来 xff0c 有一个函数可以轻松解决 xff1a TF 61 isstrprop str category 确定输入文本中的字符是否为指定的类别 xff0c 如字母
  • 云计算基础与应用 第三章 云服务器

    以下为自己个人做的笔记 xff0c 不带有商业性质 xff0c 纯粹交流分享学习资料 xff0c 如有侵权 xff0c 请联系作者 xff0c 作者看到会第一时间删除 xff0c 如有侵权敬请见谅 文章目录 3 1 云服务器概述3 2 云服
  • 工作记录:NX系统烧录

    一 固件 BSP下载 https developer nvidia com embedded L4T r32 Release v4 2 t186ref release aarch64 Tegra Linux Sample Root File
  • YOLO v5获取目标的坐标位置,以及将crop融合到全黑背景中

    可能很多人不知道我这个标题后面这句话的含义 xff0c 因为这个场景可能很多地方压根用不到 xff0c 暂时不细说 我是上个月开始接触yolov5 v6 2版本做目标识别的 xff0c 也没有什么深度学习和pytorch的概念 xff0c
  • slam的简单操作以及原理

    slam的原理以及应用 1 新建工作文件夹 使用mkdir命令新建文件夹 slambook和ch1文件夹 xff0c 并利用cd命令进入ch1文件下 xff0c 截图如 2 编写helloslam小程序 使用vim命令创建helloslam
  • 遗传算法实例解析

    遗传算法实例及MATLAB程序解析 遗传算法Genetic Algorithms xff0c GA xff09 是一种基于自然选择原理和自然遗传机制的搜索 xff08 寻优 xff09 算法 xff0c 它是模拟自然界中的生命进化机制 xf
  • nginx 反向代理 获取客户端真实IP和域名以供日志分析

    场景 xff1a 客户网站或者移动APP在请求到达后端服务之前 xff0c 会经过层层代理的转发 典型的是CDN xff08 反向代理 43 Squid缓存 xff09 xff0c 如果是带有安全或者WAF功能的则结构要更为复杂些 一般的解
  • Linux服务器上安装配置VNC Server

    前言 项目需要在Linux上进行数据抽取的相关任务 xff0c 使用的是Kettle这一ETL工具 xff0c 但是服务器就一个主机 xff0c 这里对应Kettle的job和trans等相关配置就无法进行 xff0c 所以需要使用远程图形
  • 几种简单的插值方法与实例

    插值方法及MATLAB实例解析 插值 xff1a 已知有限个已知数据点 xff0c 求得的插值函数必须过这些点 xff0c 然后在这个范围内用插值函数求得未知数据点的值 拟合 xff1a 已知有限个已知数据点 xff0c 但拟合函数不用过每
  • 模拟退火算法及实例解析

    模拟退火算法及MATLAB实例 同遗传算法一样 xff0c 模拟退火算法也是现代优化算法的一种 他对于解决组合优化问题 xff0c 如TSP xff0c JSP等问题效果较好 关于模拟退火算法的详细介绍 xff0c 可以参考这里模拟退火算法
  • MATLAB图像的读取和显示

    MATLAB图像的读取和显示 更新日期2021 06 08更新内容修正一些文字错误 xff0c 优化排版 直接看代码吧 xff0c 注释写的很清楚了 span class token operator span 图像读取 xff0c 参数为
  • 图像的点运算1

    图像的点运算MATLAB实例代码 span class token operator span span class token operator span 图像的点运算 span class token number 1 span spa
  • 二维插值-MATLAB

    二维插值与MATLAB实例解析 一 插值节点为网格节点二 插值节点为散乱节点Addition xff1a xff08 1 xff09 学会查看matlab帮助文档 xff08 2 xff09 matlab脚本类型 xff08 3 xff09
  • 曲线拟合-最小二乘法

    线性最小二乘法及matlab例程 线性最小二乘法 曲线拟合 xff1a 已知平面上一组横坐标互不相同的点 xff0c 寻求一个函数 xff0c 使其与所有数据点最为接近 拟合函数 xff1a f x 61
  • 时间序列分析

    确定性时间序列分析方法 确定性时间序列分析方法 一 移动平均法二 指数平滑法1 一次指数平滑法2 二次指数平滑法3 三次指数平滑法 三 差分指数平滑法1 一阶差分指数平滑法2 二阶差分指数平滑模型 四 具有季节特点的时间序列的预测 时间序列
  • Linux系统下用Ventoy制作Windows启动盘

    Ubuntu 18 04 制作Windows10企业版启动盘 xff08 U盘 xff09 概要 ubuntu18 04 xff0c ros对应的是melodic版本 转回Windows下使用Robot Studio时 xff0c 由于系统
  • Ubuntu 18.04开机卡在grub,引导修复的2种实用方法

    解决ubuntu18 04开机卡在grub命令行界面的问题 前情概要 xff1a 由于windows重装系统 xff0c 导致ubunru18 04的引导出了问题 下面提供两种解决方案 xff0c 本人采用第二种方式成功修复 知识补充 xf
  • Modbus Slave学习笔记

    第一章 初识Modbus 1 1什么是Modbus 简而言之 xff0c Modbus通信协议是工业领域通信协议的业界标准 xff0c 并且是当前工业电子设备之间相当常用的连接方式之一 Modbus 协议是应用于电子控制器上的一种通用语言