☰
ST语言数组与FOR循环实现PLC批量采集实战指南
2026/10/3 7:31:31 网站建设 项目流程

做现场自动化这些年,只要设备一多,“采集”这俩字就特别容易让人头疼。早期我用梯形图写过一套36个温度通道的采集程序,硬生生复制粘贴了36段几乎一模一样的网络,地址编到眼花,改一个通道的量程要翻半天程序。后来把ST语言(结构化文本)里的数组和FOR循环用起来,一个问题彻底想通了:批量采集的本质,就是用同一套逻辑反复处理结构相同的数据,而数组提供容器,FOR循环提供遍历机制,两者组合就能把几十段重复代码压成三五行。

这篇文章就围绕“ST数组 + FOR循环”这个组合,讲讲我实际做批量采集时的完整思路。不管你是刚接触ST语言的PLC工程师,还是正在把老项目从梯形图往文本语言迁移,下面这些内容都值得花十分钟看完。里面的代码示例主要以IEC 61131-3标准为基础,CODESYS、TwinCAT、汇川、信捷等主流平台基本通用。

1. 批量采集为什么让人头大:重复代码与维护噩梦

1.1 梯形图时代的“复制粘贴式”采集

如果你用梯形图写过8台甚至更多台设备的采集程序,一定经历过这种场景:一台温控仪四个参数,写成4个采集网络;8台设备就是32个网络。中间如果夹着Modbus通信指令,每个功能块实例还得单独命名,地址单独填写,错误标志单独复位。程序一长,监控的时候翻页翻到手指发酸。

问题不只是“长”,而是结构化的重复带来结构化的维护成本。某一天现场换了一台不同型号的仪表,量程从0到100摄氏度改成-20到80摄氏度,你要么逐个打开那8段网络去改,要么祈祷自己没有漏掉哪一台。批量采集这个需求本身在逻辑上极其简单——读同一类数据、做同样的处理,但梯形图的网状结构天然不适合表达这种“对集合内所有元素执行同一操作”的语义。

1.2 ST语言的循环思想为什么契合采集场景

ST语言是IEC 61131-3标准中面向过程的高级文本语言,语法上很像Pascal和C的混合体。它带来的核心转变,是从“画”程序变成“写”程序。数组能把同类型变量组织成带索引的整体,FOR循环则让“对每一个元素执行动作”变成一行代码,这种表达方式和批量采集的需求几乎是完美对齐的。

举个最直观的对比。梯形图做8台设备温度采集需要8个功能块实例,而ST数组方案只需要:

VAR arrTemperature : ARRAY[1..8] OF REAL; iIndex : INT; END_VAR FOR iIndex := 1 TO 8 DO arrTemperature[iIndex] := ReadTemperature(iIndex); END_FOR;

代码量从几十个网络变成几行,而且“8”这个数字只出现一次。以后要改成16台,改一个数组上界和循环终值即可,不用再增加半个程序页面。

1.3 哪些平台能用这套方案

现在的市场情况是,主流PLC和运动控制器基本都支持ST语言,差异只是平台之间的具体语法细节和调试手段:

平台ST语言名称数组上界写法备注
CODESYS V3ST / SFCARRAY[0..7] 或 ARRAY[1..8]支持REFERENCE,调试友好
倍福 TwinCAT 3STARRAY[0..7] 或 ARRAY[1..8]底层是C#运行时,性能强
西门子 S7-1200/1500SCLARRAY[0..7] 或 ARRAY[1..8]在TIA Portal中编辑
三菱 GX Works3ST不支持ARRAY[1..3]这种上界必须是常量数组下标必须从0开始
施耐德 SoMachine/Unity ProSTARRAY[1..8]传统命名习惯常从1开始
汇川 / 信捷等国产中型PLCST视具体平台而定兼容CODESYS语法的通常从0或1均可

这里面有一个特别容易踩的差异:数组下标到底从0开始还是从1开始。用惯了C语言的工程师到了CODESYS平台,下意识就写ARRAY[0..7],结果现场设备编号是1到8,每个索引都得做偏移处理。我的建议是:设备号、通道号在工程上通常习惯从1开始,那么数组就声明成ARRAY[1..8],循环就从1开始,代码和现场标签完全对应,排查问题的时候大脑不用做减法。

2. 写FOR循环之前,先把这几个ST语言基础夯实

2.1 数组声明和它在PLC内存里的真面目

数组在ST里本质上是连续内存区上一组同类型数据的集合。比如ARRAY[1..8] OF REAL,就是向控制器请求了8个REAL尺寸(4字节)的连续空间,相邻索引之间在内存地址上是线性相邻的。理解这一点很重要:批量采集爱用数组,不只是因为语法方便,更是因为数组的操作可以被控制器底层优化成连续内存访问,比散落的独立变量更高效。

声明数组的标准写法:

VAR arrRawValue : ARRAY[1..8] OF INT; (* 8个原始值 *) arrEngValue : ARRAY[1..8] OF REAL; (* 8个工程值 *) arrStatus : ARRAY[1..8] OF BOOL; (* 8个状态位 *) END_VAR

多维数组也是支持的,后面讲矩阵式采集时会用到。需要提醒的一点:很多平台在声明数组时必须用常量指定上下界,不能用变量。如果你确实需要“运行时决定采集多少个点”,只能声明一个足够大的固定数组,再用一个变量作为有效长度,循环时只跑到有效长度为止,而不是反复修改数组定义。

2.2 FOR循环的完整语法和几个冷门细节

ST语言的FOR循环标准语法如下:

FOR iIndex := 1 TO 8 BY 1 DO // 循环体 END_FOR;

其中BY 1是步长,为1时可以省略。步长也可以写成负数,实现从后往前遍历:

FOR iIndex := 8 TO 1 BY -1 DO // 从第8个往前处理 END_FOR;

这里藏着几个很多老手都容易忽略的细节。循环变量iIndex在循环结束后,其值并不是8,而是终值加上步长,也就是9。所以不要在循环结束之后拿循环变量去当有效值用。另外,循环变量的数据类型推荐用INT或DINT,绝不能用REAL,浮点变量的累加误差会让循环次数变得不可预测。最后,标准规定不要在循环体内修改循环变量的值,如果你发现自己正在写iIndex := 5;这种代码,几乎一定是用错了结构,应改用WHILE或者重新设计逻辑。

2.3 循环体内适合放什么,不适合放什么

这个点我觉得比语法本身更重要。FOR循环适合放无阻塞的纯逻辑操作:数值运算、赋值、比较、数组元素访问、调用没有等待行为的功能块。不适合放需要等待外部条件满足的操作,比如“发送Modbus请求后等从站回复”“等待串口返回OK字符串”。

为什么?PLC是循环扫描模型,CPU按扫描周期周期性地执行程序。如果你在FOR循环里真等外部设备的响应,几十毫秒甚至几秒的等待时间会让整个扫描周期被拖垮,轻则通信超时,重则触发看门狗复位。正确的做法是把“等待”变成状态机——每个扫描周期内只检查一次状态,条件满足则继续,不满足就跳出当前周期的处理,等下一个扫描周期再回来。这个矛盾后面在批量采集实战里会专门展开,因为它是新手最容易踩的坑。

3. 批量采集实战:FOR循环配合轮询状态机

3.1 场景设计:8台温控仪表的数据读取

假设现场有一条生产线,8台温控表通过Modbus RTU挂在一条总线上,每台表需要采集当前温度(保持寄存器地址40001)。传统做法是给每台表建一个独立通信功能块实例,挨个填写从站地址,再写8段轮询逻辑。我们用ST数组+FOR循环来做,整个采集核心代码控制在40行以内,而且后续增加从站数量时改动极小。

变量定义如下:

VAR arrSlaveAddr : ARRAY[1..8] OF BYTE; (* 从站地址表 *) arrTemp : ARRAY[1..8] OF REAL; (* 温度值数组 *) nCurStation : INT := 1; (* 当前轮询的从站编号 *) xRequestSend : BOOL; (* 发送请求标志 *) xReplyReady : BOOL; (* 回复就绪标志 *) nTimeoutCount : INT; (* 超时计数器 *) arrReadMutex : ARRAY[1..8] OF BOOL; (* 各站是否完成本周期读取 *) END_VAR

注意arrSlaveAddr这个数组:设备的Modbus从站地址规划好后,全部填在这里,后续程序逻辑不再出现任何硬编码的地址数字。这其实就是“数据驱动采集”的雏形——程序的结构和数据分离,现场改动从站地址时,只需要改这个数组的初始值,不需要动逻辑代码。

3.2 第一版直觉写法:一个FOR循环读所有设备

很多第一次接触ST的工程师会这么写:

FOR nCurStation := 1 TO 8 DO Modbus_ReadRequest(nCurStation, arrSlaveAddr[nCurStation], arrTemp[nCurStation]); // 就地问答模式:立刻等待响应 END_FOR;

漂亮是漂亮,但基本跑不通。原因就是上面说的,通信响应不是同步的:你发出请求后,从站需要几个毫秒甚至更长的时间返回数据,主站也要等待总线上的数据帧解析完成。如果这个功能块调用本身是阻塞的,扫描周期会被严重拖长;如果功能块是非阻塞的(立即返回,后台完成通信),那这个FOR循环一次扫描内连发8个请求,从站根本来不及响应,总线冲突、数据错乱是必然结果。

第一次实测时我甚至看到现象是:8台仪表中只有随机的一两台有正确数据,其他全是上一次的值或异常值。这就是“逻辑正确但时序错误”的典型案例。

3.3 状态机解法:每个扫描周期只处理一台设备

正确思路是把“对所有设备执行操作”进行时间切片:一个扫描周期内,只通过FOR循环定位到“当前该处理的设备”,其他设备等在队列里。标准做法如下:

(* 每次扫描进入此段时,先用FOR循环找到当前该发送请求的站号 *) xFound := FALSE; FOR nCurStation := 1 TO 8 DO IF arrReadMutex[nCurStation] = FALSE THEN xFound := TRUE; EXIT; (* 找到第一个未完成的站,跳出循环 *) END_IF; END_FOR; IF xFound THEN xRequestSend := TRUE; arrReadMutex[nCurStation] := TRUE; (* 先把该站标记为处理中,防止重复触发 *) Modbus_ReadRequest(arrSlaveAddr[nCurStation], arrTemp[nCurStation]); END_IF;

接下里的几个扫描周期,程序检查通信功能块的完成标志和超时时间:

IF xRequestSend AND Modbus_ReadDone THEN (* 数据已经写入 arrTemp[nCurStation],处理下一个站 *) nTimeoutCount := 0; xRequestSend := FALSE; ELSIF xRequestSend THEN (* 还没完成,累加超时时间 *) nTimeoutCount := nTimeoutCount + 1; IF nTimeoutCount > 1000 THEN arrReadMutex[nCurStation] := TRUE; (* 超时也置为已完成,避免卡死 *) arrTemp[nCurStation] := 0.0; (* 失败值按0处理 *) xRequestSend := FALSE; nTimeoutCount := 0; END_IF; END_IF;

8个站都完成后(即arrReadMutex全部为TRUE),进入复位阶段,把所有标志复位,开始下一轮批量采集周期。这种做法对总线负载很友好,每个扫描周期最多发一帧请求,也符合Modbus主站“一主多从,一问一答”的时序要求。我建议所有做串口或总线批量采集的工程师都记住这个思路:批量永远不等于“同一时刻处理全部”,而是“一段周期内按队列逐个处理”。

3.4 批量采集只是起点:数据同步归档到数据块

采集完成之后,8个温度值都在arrTemp数组里了。下一件事通常是把这些值送到HMI显示、数据库存档,或者交给另一个控制程序使用。如果这些消费方分布在程序的不同POU里,直接把arrTemp声明成全局变量,是最省事也是最合理的做法。但要注意:跨POU访问数组时,平台通常不支持把数组整体作为形参传递,要么传递数组的起始地址(TwinCAT里用REFERENCE),要么就把数组声明为全局变量,让其他POU直接按索引访问。

我还习惯在采集完成后,用FOR循环把数组内容整体搬运到“带时间戳的归档镜像数组”里,这样即使下一轮采集覆盖了arrTemp,上一轮的数据仍然保留着用于趋势分析:

FOR iIndex := 1 TO 8 DO arrTempHistory[iIndex] := arrTemp[iIndex]; END_FOR;

哪怕是这么简单的搬运,也别小看FOR循环的意义。它保证了你只需要维护一份“源数组”和一份“目标数组”,中间无论加多少个通道,循环体一行都不用改。

4. 循环里面不只有“读”:滤波、换算、归档一次搞定

4.1 滑动平均滤波:用循环把毛刺压下去

工业现场的模拟量信号,尤其是温度、压力、流量这些,多多少少都有干扰。如果有干扰的原始值直接进到逻辑控制里,轻则波动显示,重则引起设备误动作。批量采集通常和滤波是一对搭档。滑动平均滤波器实现起来很简单:维护一个“前几次的采样值”数组,每次用当前值和最近N次值做平均。

(* 假设 arrTemp 是本周期采集的原始温度 *) arrRawHistory[1..8] 的每个元素,实际上是二维结构,这里为了示例简化: *) FOR iIndex := 1 TO 8 DO arrSum := 0.0; // 把当前原始值存入历史数组末尾,同时把最老的值挤出去 FOR jIndex := 9 TO 2 BY -1 DO arrHistory[iIndex, jIndex] := arrHistory[iIndex, jIndex - 1]; END_FOR; arrHistory[iIndex, 1] := arrRawValue[iIndex]; // 求和平均 FOR jIndex := 1 TO 5 DO (* 窗口5次 *) arrSum := arrSum + arrHistory[iIndex, jIndex]; END_FOR; arrFiltered[iIndex] := arrSum / 5.0; END_FOR;

这个例子展示了FOR循环嵌套的一个典型场景:外层遍历8个通道,内层实现每个通道的历史数据搬运和平均值计算。如果你用梯形图表达这段逻辑,即便是5个窗口、8个通道,网络数量也在40个以上,而ST只用了十几行。

4.2 工程量换算:在循环里把原始值变单位

采集到的原始值往往是PLC字长对应的二进制数,比如4到20毫安电流信号对应0到27648的原始字,工程上要换成0到100摄氏度的实际值。一个通道的换算公式不复杂,麻烦的是几十个通道都要做。最常见的线性换算:

FOR iIndex := 1 TO 8 DO arrEngValue[iIndex] := (arrRawValue[iIndex] - nRawLow) / (nRawHigh - nRawLow) * (fEngHigh - fEngLow) + fEngLow; END_FOR;

这里nRawLow和nRawHigh是仪表量程对应的原始值范围,fEngLow和fEngHigh是工程值范围。把这些参数也声明成数组,就能实现每个通道独立量程:

FOR iIndex := 1 TO 8 DO arrEngValue[iIndex] := (arrRawValue[iIndex] - arrRawLow[iIndex]) / (arrRawHigh[iIndex] - arrRawLow[iIndex]) * (arrEngHigh[iIndex] - arrEngLow[iIndex]) + arrEngLow[iIndex]; END_FOR;

以后现场换了一台不同量程的表,你只改arrRawLow[iIndex]和arrRawHigh[iIndex]两个数组元素即可,每个通道独立,互不影响。我刚入行时用梯形图改量程,改32路信号要用几个小时核对地址,用数组之后这个工作量变成了“读一行INIT表格”。这种对比,做过一次就不会再想回去了。

4.3 数据归档:把一维数组写进历史记录块

当采集系统和上位机或者MES系统对接时,常常需要把一整批数据按帧整理成数据块上传。有的平台支持专用的块搬运指令(比如三菱的BMOV,西门子的MOVE_BLK),但用FOR循环写数组搬运依然是兼容性最好的做法,尤其当数据不是连续内存或需要加入校验规则时。

(* 把8个温度值和8个状态字打包成数据块 *) FOR iIndex := 1 TO 8 DO arrDataPackage[iIndex] := arrEngValue[iIndex]; arrDataPackage[iIndex + 8] := INT_TO_REAL(arrStatus[iIndex]); END_FOR;

数据的组织方式由你的自定义协议决定,FOR循环在这里扮演的角色是“顺序承包人”——它保证了你对数据块每个位置的填充逻辑是一致的。如果后续协议改了,把第8个通道的状态字挪到第18个位置,修改一行数组下标运算即可。

5. 批量采集高频踩坑记录:数组越界、扫描超时、动态长度

5.1 数组越界:循环上界和声明长度对不上

这是ST项目里最常见的崩溃来源。典型场景:程序里数组声明是ARRAY[1..6],循环写着FOR iIndex := 1 TO 8,运行到第7次时访问了不存在的元素。后果取决于平台——有些PLC在下一次循环扫描时才报错停机,有些干脆直接进入STOP模式。

排查这类问题,技巧是先怀疑循环上界,再怀疑数组声明。我自己的习惯是:凡是数组循环,必须把数组上界定义成一个符号常量或配置变量,并用它同时控制声明和循环:

VAR CONSTANT cMaxStation : INT := 8; END_VAR VAR arrTemp : ARRAY[1..cMaxStation] OF REAL; END_VAR FOR iIndex := 1 TO cMaxStation DO arrTemp[iIndex] := 0.0; END_FOR;

这样声明和循环天然一致,改设备数量时只动这一处,彻底消灭“数组改大了,循环忘改了”这类低级事故。

5.2 循环体里等“OK”:从通信死循环到看门狗复位的教训

第一次写串口仪表批量采集时,我犯过一个特别典型的错误。当时接了一批带串口输出的仪表,数据帧以OK结尾表示校验通过,我满心欢喜地在ST里写了这么一段:

FOR iIndex := 1 TO 8 DO xCommandSend := TRUE; WHILE xOKFlag = FALSE DO // 等待仪表返回OK END_WHILE; arrValue[iIndex] := nReadValue; END_FOR;

结果程序一运行,PLC直接停机报警,原因是扫描周期超时,看门狗复位。这个问题的本质是:PLC程序不是无限等待外部设备的程序,它是按固定周期反复扫描的实时系统。在循环体内等OK,等于把整个PLC钉死在那个循环里,所有其他控制逻辑全部停摆。

正确的做法我在3.3节已经演示了:把“发送请求 → 检查OK → 继续下一站”拆成三个扫描周期内的步骤,用状态标志驱动。每轮扫描只做一件事,没OK就计数超时,超时则放弃该站继续下一站。不管你是连Modbus、串口、还是以太网,凡是涉及“询问—应答”的批量采集,一律不要用阻塞等待。

5.3 循环变量被“聪明”地修改之后

还有一类隐藏很深的问题:在循环体内修改循环变量,或者把循环变量的值赋给别的变量后继续使用。比如想“跳到第4个元素跳过处理”,直接写:

FOR iIndex := 1 TO 8 DO IF iIndex = 3 THEN iIndex := 4; (* 危险做法 *) END_IF; END_FOR;

这在不同编译器上的行为完全不同,有的直接死循环,有的结果违背预期。正确做法是用条件判断跳过处理逻辑,而不是改循环变量:

FOR iIndex := 1 TO 8 DO IF iIndex = 3 THEN CONTINUE; (* 跳到下一次循环,不执行下面的代码 *) END_IF; arrValue[iIndex] := 0.0; END_FOR;

ST标准里CONTINUE(继续下一次循环)和EXIT(跳出整个循环)是安全的结构化跳转,能用这两个关键字解决的需求就不要去动循环变量本身。

5.4 运行时动态改变采集数量:上限保护不可省

前面提过,数组上界必须是常量,但实际生产又要支持“今天用6台,明天用9台”的动态需求。常见的折中方案:数组声明为最大可能数量,程序里用一个nActiveCount变量表示当前实际使用数量,FOR循环用这个变量作上界:

FOR iIndex := 1 TO nActiveCount DO arrTemp[iIndex] := ReadTemperature(iIndex); END_FOR;

此时必须加一条保险:nActiveCount 的赋值要限幅,不能超过数组声明的最大上界。否则操作员在HMI上误填一个20,程序立刻数组越界。我通常会在赋值处写:

nActiveCount := MIN(nMaxDevice, nHmiInputValue);

这样即使HMI输入异常,程序也只是按最大设计能力运行,不会崩。

6. 从一维到二维:多设备多参数的矩阵式批量采集

6.1 二维数组怎么声明和遍历

当8台设备每台要采集温度、压力、流量、液位四个参数时,一维数组的“8个温度”“8个压力”维护起来就开始繁琐了。更合理的载体是二维数组:

VAR arrProcessData : ARRAY[1..8, 1..4] OF REAL; (* 8个站点,4个参数 *) END_VAR

第一维设备编号,第二维参数编号。采集和处理的循环改成嵌套结构:

FOR iDevice := 1 TO 8 DO FOR iParam := 1 TO 4 DO arrProcessData[iDevice, iParam] := ReadProcessValue(iDevice, iParam); END_FOR; END_FOR;

嵌套循环执行顺序是:外层固定一个设备号,内层把4个参数全部读完,然后外层切换到下一个设备。在ST里,这种“行优先”的访问顺序和数组内存排布方式一致,效率上也更友好。

6.2 二维数据矩阵的批量换算与质量判断

二维数组真正的优势在批量后处理时体现得淋漓尽致。给8台设备的4个参数分别做量程换算和越限判断,只需嵌套循环加一个参数类型判断:

FOR iDevice := 1 TO 8 DO FOR iParam := 1 TO 4 DO // 量程换算 arrEngData[iDevice, iParam] := (arrRawData[iDevice, iParam] - arrRawZero[iDevice, iParam]) / (arrRawSpan[iDevice, iParam]) * (arrEngSpan[iDevice, iParam]) + arrEngZero[iDevice, iParam]; // 越限判断 IF arrEngData[iDevice, iParam] > arrHighLimit[iDevice, iParam] THEN arrAlarmFlag[iDevice, iParam] := TRUE; ELSE arrAlarmFlag[iDevice, iParam] := FALSE; END_IF; END_FOR; END_FOR;

这里每一行读起来都很“重”,但放在嵌套循环里,32个通道(8乘4)的运算一次完成。如果需要往报警逻辑里增加“参数优先级”,再增加一个二维数组存优先级权重,循环逻辑结构不会变。

6.3 指针数组:到底要不要用,什么时候用

做ST批量采集的工程师,多多少少会遇到有人提“指针数组”,尤其是有C语言背景的开发者在TwinCAT或者CODESYS里干活时。我的建议是:多数场景不要用,数组加索引已经足够。ST里数组的访问本身就是“基址加偏移”的寻址方式,你写出arrValue[iIndex]时,编译器已经在做间接寻址了,显式指针通常只带来额外的调试复杂度和越界风险。

真正需要指针或REFERENCE的场景,一般是算法要求动态切换目标数组,例如同一套处理逻辑,这一轮处理A线数据,下一轮处理B线数据。这种需求在TwinCAT里可以用REFERENCE TO ARRAY实现,但可维护性并不好。我采用过的更保守方案是:把A线和B线都声明成二维数组的两个“页”,处理时用一层循环加一个“页号”变量控制,实际上就是三维数组。这样不用指针,逻辑也完全清晰,还方便上位机把两线数据一并存档。

7. FOR循环采集的边界思考:什么时候循环是错的

写了这么多FOR循环的应用,我想补充一个“什么时候别用”的思考,这部分是很多教程不会告诉你的。FOR循环强调的是对已知数量元素的遍历,它解决的是“已知多少个,挨个处理”的问题。如果你的采集数量在生产过程中动态变化很大,比如从0到几百台设备随机接入,那更适合的数据结构是队列或链表,ST里对应的是FIFO指针块,而不是固定长度数组。

另外,大量嵌套循环会显著拉长扫描周期。假设你有20台设备、每台10个参数、每个参数的滤波窗口是50次采样,三层循环的运算量是20乘10乘50等于10000次浮点运算。对大多数PLC来说这个量级还能接受,但如果你在循环里调用通信功能块或者文件读写,时间就会指数级恶化。我通常的做法是:把“采集(I/O操作)”和“信号处理(纯运算)”拆成两个独立任务块,采集用状态机驱动,信号处理才用循环密集型计算,两者按不同的周期执行,互不阻塞。

还有一个在工程上容易被忽视的细节:不要在FOR循环里直接写HMI显示缓存的数组。显示刷新频率往往远低于采集频率,循环内每次更新显示数组,既浪费资源又可能导致画面闪烁。更好的做法是让采集循环只写采集缓冲区,再由一个慢周期任务把最新值搬运到显示区。同样是FOR循环,不同用途分配到不同的任务周期里,系统的实时性才会更健康。

回头看我这些年在批量采集上的经历,真正让我工作效率质的飞跃的,不是某个花哨指令,而是“把现场设备的编号、地址、量程、限值全部数组化,再用FOR循环驱动同一套处理逻辑”这个朴素的结构。它让程序设计从“描绘每个元件的连接”变成了“定义集合和规则”,程序篇幅缩短,修改成本下降,排查问题时的思路也肉眼可见地清晰。下一篇我会具体拆解如何用ST数组实现Modbus总线上各从站的无冲突轮询时序,以及轮询周期和设备数量之间的匹配计算,感兴趣的话可以先在项目里把数组声明和FOR循环跑熟,这一步是所有进阶玩法的基础。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询