3. ADS 端口与符号寻址

在 TwinCAT ADS 通信中,真正决定“能不能读到、读到什么”的,往往不是 IP 地址,而是端口号(Port)与符号寻址(Symbol Addressing)这两层机制。前者决定你与哪个 ADS 服务器对话,后者决定你访问的是 PLC 运行时中的哪一个变量。理解这两层,才能把“通信成功”与“数据正确”区分开。

一、端口号:ADS 服务器的逻辑地址

ADS 端口号是一个 16 位无符号整数,用于标识目标设备上的某个 ADS 服务器实例。它不随网卡或 IP 变化,而是由设备内部的服务注册决定。常见端口分配如下:

端口号服务对象典型用途
851TwinCAT 3 PLC 运行时(默认实例)读写 PLC 变量、调用 PLC 方法
852TwinCAT 3 PLC 运行时(第二实例)多 PLC 项目并行时的第二个运行时
10000TwinCAT 系统服务(System Service)系统状态、路由、事件通知
10001TwinCAT 实时服务器(Real-Time)实时任务、CPU 负载信息
500本地 ADS 路由器(Router)本机内部通信、路由转发

端口号不是“随便填”的。若目标设备上运行的是 TwinCAT 3 PLC,默认端口就是 851;若项目配置了多个 PLC 实例,则第二个实例通常为 852,依此类推。在 ADS 客户端代码中,端口号与 AMS Net ID 一起构成目标地址:AMS Net ID + Port。例如,本机 PLC 的地址可写作 5.1.2.3.1.1:851,其中冒号前是 AMS Net ID,冒号后是端口号。

如果连接失败并返回错误码 0x20(ADSERR_DEVICE_NOTFOUND),优先检查端口号是否与目标设备上实际运行的 ADS 服务器一致,而不是先怀疑网络。

二、符号名称解析:从变量名到运行时地址

PLC 变量在源代码中有一个人类可读的符号名,例如 MAIN.nSpeed、MAIN.bStart、MAIN.stAxis.fPos。但 ADS 读写并不直接使用这个字符串,而是使用一个由运行时分配的句柄(Handle)。符号名称解析就是“把符号名翻译成句柄”的过程。

解析由目标设备上的 ADS 符号服务器完成。客户端发送符号名,服务器在已下载的 PLC 符号表中查找,若找到则返回一个 32 位句柄。这个句柄在本次连接会话内有效,后续读写都通过句柄进行,不再重复解析字符串。这样做的好处是:读写路径短、效率高,且符号名变化不会影响已获取句柄的读写操作。

符号名的书写必须与 PLC 项目中的声明一致,包括大小写和层级分隔符。例如,全局变量 MAIN.nSpeed 中,MAIN 是程序实例名,nSpeed 是变量名,中间用点号连接。结构体成员则继续用点号展开,如 MAIN.stAxis.fPos。数组元素可用 MAIN.aData[3] 形式访问。

三、句柄获取与释放:成对出现的资源管理

句柄是一种有限资源。每次调用 AdsSyncReadWriteReq 或 AdsSyncReadReq 之前,通常需要先通过 AdsSyncReadWriteReq 向端口 0xF003(ADS 符号服务器)发送符号名,获取句柄。获取成功后,句柄值通常是一个大于 0 的整数;若返回 0 或错误码,说明符号名不存在或符号服务器未就绪。

释放句柄通过向同一端口发送释放请求完成。释放后,该句柄不再可用,继续使用会导致读写失败。因此,句柄的获取与释放必须成对出现,尤其是在长时间运行的客户端程序中,避免句柄泄漏。一个典型的生命周期是:连接建立 → 获取句柄 → 多次读写 → 释放句柄 → 断开连接。

// 示例:获取句柄(伪代码,参数为示意值)
nPort = 851;
szSym = "MAIN.nSpeed";
nHandle = AdsSyncReadWriteReq(port=0xF003, ...);
// 若 nHandle > 0,则获取成功

// 使用句柄读取变量
AdsSyncReadReq(port=851, handle=nHandle, ...);

// 释放句柄
AdsSyncReadWriteReq(port=0xF003, release, handle=nHandle);

四、变量读写基本流程

一次完整的 ADS 变量读写,可以拆成四个阶段:连接、解析、传输、释放。连接阶段确定 AMS Net ID 与端口号;解析阶段获取符号句柄;传输阶段按句柄读写数据;释放阶段归还句柄。下图展示了这一流程中客户端与 ADS 服务器之间的交互关系。

ADS 客户端 (本机或远程程序) 1. 建立连接 2. 请求符号句柄 3. 按句柄读写 4. 释放句柄 ADS 服务器 (TwinCAT 运行时) 端口 851 监听 符号表查找 按句柄访问内存 句柄回收 AMS Net ID + Port 851 符号名 "MAIN.nSpeed" 返回句柄 nHandle 读/写请求 + 句柄 返回数据 / 写入确认 释放句柄请求 句柄在会话内有效,读写阶段不再重复解析符号名

在读写阶段,客户端需要指定数据长度和数据类型。例如,读取 MAIN.nSpeed(DINT,4 字节)时,请求长度应为 4;读取 MAIN.bStart(BOOL,1 字节)时,长度为 1。若长度不匹配,服务器可能返回数据截断或错误码。写入时同样需要保证长度与变量类型一致,否则可能写入错误字节或触发异常。

句柄不是永久有效的。PLC 重新下载、运行时重启或符号表变化后,旧句柄会失效。此时应重新获取句柄,而不是继续使用旧值。错误码 0x700(ADSERR_INVALID_HANDLE)通常表示句柄已失效。

五、实践中的判断依据

在实际调试中,可以按以下顺序判断问题出在哪一层:

  • 连接失败:检查 AMS Net ID、端口号、路由是否可达。错误码 0x20 指向设备未找到,0x21 指向端口未监听。
  • 句柄获取失败:检查符号名拼写、大小写、层级路径是否与 PLC 项目一致;确认 PLC 已运行且符号已下载。
  • 读写失败:检查句柄是否有效、数据长度是否匹配、变量是否被优化或映射到非 ADS 可访问区域。
  • 数据异常:检查字节序、数据类型对齐、数组索引是否越界。