简介:本资源是面向西门子TIA博途V15平台开发者的实用功能库,专为解决工业自动化编程中频繁出现的二进制位统计需求——即快速计算整数(INT)或WORD类型变量中二进制表示下“1”的个数(Hamming重量)。该功能在通信校验、状态字解析、故障码识别等PLC逻辑设计场景中具有高频应用价值,适合具备基础TIA博途编程能力的工程师与自动化专业学习者。压缩包共9个文件,含6个XML(定义FC接口与逻辑结构)、1个PLF(项目库封装文件)、1个IDX(索引支持全局调用)、1个AL15(TIA V15专用编译单元),总大小290KB,结构规范,可直接导入项目作为全局函数块复用。已有313人学习下载,用户可立即获得完整可运行的FC实现,包含输入/输出参数定义、高效位移+掩码计数算法及标准化错误处理逻辑,显著减少重复编码,提升程序可靠性与工程一致性。
1. 项目背景与核心需求:为什么需要一个“数1”的FC?
在工业自动化编程,尤其是西门子TIA Portal(博途)环境下,我们经常需要处理来自传感器、编码器或通讯协议的各种原始数据。这些数据常常以整数(INT, DINT)或字(WORD, DWORD)的形式存储在PLC的存储器中。很多时候,这些整数值的二进制位(Bit)状态本身就承载着关键信息。
一个非常典型且高频的需求是:快速统计一个整数或一个字(WORD)的二进制表示中,值为“1”的位的数量。这个操作在专业上被称为“计算汉明重量”(Hamming Weight)或“种群计数”(Population Count)。
你可能会问,这个操作具体有什么用?让我举几个我亲身经历过的场景:
- 设备状态字解析:许多智能仪表或伺服驱动器通过PROFINET或Modbus TCP通讯返回的状态字(Status Word)是一个16位的WORD。其中每一位可能代表一个特定的报警或状态,例如:Bit0=过流,Bit1=过温,Bit2=通讯异常……有时,我们不需要知道具体是哪个位报警,只需要快速知道“当前有多少个故障位被激活了”,作为设备健康度的一个快速指标。手动去逐位判断并累加,在梯形图(LAD)或功能块图(FBD)里会非常繁琐。
- 校验与纠错算法:在一些简单的校验或自定义通讯协议中,计算数据块的汉明重量可以作为校验和的一种补充,用于快速判断数据在传输过程中是否发生了大量位的跳变。
- 位图(Bitmap)资源管理:如果你用一组BOOL位(比如一个WORD或DWORD)作为一个简单的资源池或任务队列的标志位,统计其中“1”的数量,就能立刻知道当前有多少资源被占用或多少任务待处理。
在没有现成指令的情况下,新手工程师可能会写一个循环,用移位和位逻辑操作逐位检查。这在功能上没问题,但代码效率不高,可读性差,而且每次遇到类似需求都要重写一遍。这正是我们需要一个封装好的、可重用的全局函数(FC)的原因。它就像工具箱里的一把专用扳手,需要时直接调用,输入一个数,输出其中“1”的个数,干净利落。
本项目分享的,正是这样一个针对TIA Portal V15环境封装好的全局FC库文件。它解决了从基础需求到高效实现的最后一公里问题。
2. 核心算法解析:如何高效地“数1”?
在深入TIA Portal的具体实现之前,我们先聊聊背后的算法。这有助于理解FC内部的逻辑,而不仅仅是把它当做一个黑盒。
最直观的算法是循环移位法:假设我们有一个16位的WORD,我们循环16次,每次将其右移一位,然后检查最低位(LSB)是否为1,如果是则计数器加1。
输入: 一个整数 `input_value` 计数器 count = 0 循环 i 从 0 到 (位数-1): if (input_value & 1) == 1: // 检查最低位 count = count + 1 input_value = input_value >> 1 // 逻辑右移一位 输出: count这个方法简单易懂,但循环次数与数据位宽成正比(16位循环16次,32位循环32次)。在PLC的扫描周期内,虽然也能接受,但有没有更高效的方法?
有的,这就是Brian Kernighan算法。它是一个非常巧妙的算法,其核心思想是:对于一个数n,n & (n-1)这个操作会消去n的二进制表示中最右边的一个“1”。
让我们举个例子,假设n = 13(二进制 1101):
- 第一轮: n = 13 (1101), n-1 = 12 (1100)。 n & (n-1) = 1101 & 1100 = 1100 (12)。看,最右边的1(第0位)被消掉了。
- 第二轮: n = 12 (1100), n-1 = 11 (1011)。 n & (n-1) = 1100 & 1011 = 1000 (8)。又消掉了一个1(第2位)。
- 第三轮: n = 8 (1000), n-1 = 7 (0111)。 n & (n-1) = 1000 & 0111 = 0000 (0)。最后一个1也被消掉了。
算法停止。我们进行了3次n = n & (n-1)操作,正好对应13(1101)中有3个“1”。这个算法的循环次数,等于该数字中“1”的个数,而不是其位宽。对于稀疏的“1”(比如只有一两个位是1),这个算法效率优势明显。
在PLC中,我们可以用循环来实现这个算法。虽然TIA Portal的SCL语言也能实现,但为了更好的兼容性和可移植性(比如在仅支持LAD/FBD的PLC上使用),我们提供的FC主要采用基础的位逻辑和算术运算指令来构建,其核心思想与上述算法一脉相承。
注意:西门子S7-1200/1500系列PLC的指令集中,从特定固件版本开始,其实提供了原生指令
POPCNT(Population Count) 用于计算字节、字、双字中“1”的个数,效率最高。但考虑到兼容性(如老版本PLC、S7-300/400移植项目)和作为学习案例的价值,自己实现一个FC仍然非常有意义。
3. FC库文件详解:接口、内部逻辑与使用指南
接下来,我们拆解这个名为FC_CountOnes(名称可能略有不同,但功能一致)的全局函数块。
3.1 FC的接口定义(Input/Output)
一个设计良好的FC,其接口应该清晰明了。这个FC的接口大致如下:
输入(Input):
InputValue(IN): 要计算的数据。这里通常被定义为ANY类型或者Variant,以支持多种数据类型(WORD, INT, DWORD, DINT, 甚至BYTE)。但在V15及更注重稳定性的场景下,更常见的做法是提供多个重载的FC,或者用一个INT型输入,然后在内部根据另一个“数据类型选择”参数进行处理。为了简化,我们假设本FC主要处理WORD和DINT。DataType(IN) (可选): 数据类型选择。0代表WORD/INT(16位),1代表DWORD/DINT(32位)。这是一个非常实用的设计,让一个FC能处理两种常见情况。
输出(Output):
CountOfOnes(OUT): 计算结果,即“1”的个数。对于16位输入,结果范围0-16,可以用INT存储;对于32位输入,结果范围0-32,也需要一个INT(因为32<32767)。Error(OUT) (可选): 错误代码。0=无错误,1=输入数据类型不支持等。良好的错误处理是工业程序健壮性的体现。
临时变量(Temp):
- 用于存储中间计算过程,如循环索引、原始数据的副本等。
3.2 FC内部逻辑实现(以LAD/STL混合为例)
由于原始项目未提供具体代码,我将基于最常见的实现方式,描述其在TIA Portal V15中的可能逻辑。这里假设它处理16位(WORD)和32位(DWORD)两种情况。
核心步骤:
- 初始化:将输出
CountOfOnes清零。将输入值InputValue复制到一个临时变量TempValue中,避免破坏原始输入数据。 - 循环计算(基于Brian Kernighan算法思想):
- 使用一个循环(例如,对于16位,最大循环16次;对于32位,最大循环32次,但实际会提前退出)。
- 循环条件:
TempValue != 0。 - 循环体内:
TempValue := TempValue AND (TempValue - 1)。这就是核心操作,每次消除最右边的一个“1”。CountOfOnes := CountOfOnes + 1。
- 当
TempValue变为0时,循环结束。此时CountOfOnes即为结果。
- 数据类型分支处理:根据
DataType输入,在循环前可能需要将数据转换为双字(DWORD)进行计算,以确保32位操作的正确性。或者,直接准备两套逻辑。
在TIA Portal中的具体指令可能涉及:
MOVE: 数据传输。AND_DW,SUB_DI: 双字与运算和双整数减法,用于实现n & (n-1)。CMP ==0: 比较指令,用于判断循环是否结束。JMP/LABEL: 跳转指令实现循环。或者使用LOOP指令(如果使用STL语言)。
3.3 如何导入与使用这个FC库文件
你下载到的通常是一个.zip压缩包,解压后里面包含一个.zal(TIA Portal库归档文件) 或直接是项目文件.ap15。
导入步骤:
- 打开TIA Portal V15:确保你的TIA Portal版本是V15或兼容版本。高版本(如V17, V18)通常可以向下兼容打开低版本库,但可能需要迁移。
- 创建或打开你的项目:在你要使用这个FC的项目中操作。
- 导入库:
- 在项目树中,找到“库”视图。
- 右键点击“全局库”或“项目库”,选择“从文件系统导入库...”。
- 浏览并选择解压后的
.zal文件,按照向导完成导入。
- 在程序中调用:
- 打开你的OB、FB或FC块。
- 在指令树中,展开“全局库”下你刚导入的库,找到
FC_CountOnes。 - 将其拖拽到你的程序段中,就像使用标准的
MOVE或ADD指令一样。 - 填写输入管脚(连接你的数据变量),输出管脚连接到一个结果变量。
一个简单的使用示例:假设你从一台设备读取到一个状态字DB1.Static_1.StatusWord(Word类型),你想知道当前有多少个故障位激活。
// 在你的FC或OB中 CALL “FC_CountOnes” ( InputValue := “DB1”.Static_1.StatusWord, // 输入:状态字 DataType := 0, // 0表示16位数据 CountOfOnes => “故障数量”, // 输出:故障位总数 Error => “计数错误” // 输出:错误状态 );实操心得:导入库后,最好在离线状态下先测试一下这个FC。创建一个测试OB,用几个已知的数值(比如
16#000F(二进制0000 0000 0000 1111,有4个1) 或16#A5A5(交替的1010 0101,有8个1))调用它,验证输出是否正确。这能避免在生产程序中因库文件问题而排查半天。
4. 性能考量与高级应用场景延伸
当我们把基础功能跑通后,作为工程师,我们自然会思考:这个方案的性能如何?有没有边界情况?还能用在什么地方?
4.1 循环算法 vs 查表法 vs 原生指令
我们实现的循环算法(特别是优化后的Brian Kernighan算法)在大多数PLC应用场景下性能是足够的。一次计算通常在几个到几十个微秒内完成,对于扫描周期在几十毫秒的PLC来说微不足道。
但对于极端性能要求,或者在频繁调用的高速循环中,我们可以考虑其他方法:
查表法(Look-up Table):这是空间换时间的经典策略。我们可以预先计算好所有8位值(0-255)中“1”的个数,并存入一个包含256个字节的数组常量中。对于一个16位WORD,我们可以将其拆分成高8位和低8位,分别查表,然后将两个结果相加。对于一个32位DWORD,则拆分成4个字节查表后累加。
- 优点:计算速度极快,只有几次内存访问和加法。
- 缺点:占用额外的数据块空间(256字节),并且FC的实现逻辑会稍复杂。在TIA Portal中创建和维护这个常量表需要一些工作量。
使用CPU原生指令:如前所述,新型S7-1500 CPU的指令集包含
POPCNT。如果你的项目确定运行在支持该指令的硬件和固件上,直接使用它是最优解。你可以在SCL中直接调用系统函数,或者用STL编写。
选择建议:对于绝大多数项目,我们提供的这个通用循环FC已经完全够用。它的优势在于通用性、可读性和可维护性。查表法更适合作为知识储备,在遇到确切的性能瓶颈时再考虑引入。
4.2 边界情况与错误处理
一个健壮的FC必须考虑边界情况:
- 有符号整数(INT, DINT)的处理:我们的算法是基于位运算的,对于二进制位模式来说,
-1(DINT,二进制全为1) 和4294967295(DWORD,二进制全为1) 在内存中的表示是一样的。因此,我们的FC如果设计为按位处理,那么输入-1也会得到32个“1”的结果。这通常是符合“计算1的个数”的本意的。但如果你需要的是“计算值为1的位的个数”,那么输入数据的符号类型(SINT/INT/DINT)还是无符号类型(USINT/UINT/UDINT)对算法没有影响。关键在于接口文档要说明清楚。 - 输入参数验证:如果FC设计了
DataType参数,必须检查其值是否在允许范围内(如0或1)。如果不是,应通过Error输出位报告错误,并将CountOfOnes输出置为0或一个安全值。 - 临时变量溢出:
CountOfOnes是INT类型,最大32767。计算32位数据的“1”的个数,最大为32,远小于此限,安全。但如果未来扩展用于字节数组,则需要考虑结果变量的范围。
4.3 扩展应用场景
掌握了这个核心FC,你可以将其思想扩展到更多有趣的应用中:
- 计算多个字的总体“1”的密度:如果你有一个数据块(Array of Word),可以循环调用此FC,累加所有结果,用于评估整个数据块的“活跃度”或“变化率”。
- 自定义轻量级校验:对一段关键数据(比如配方数据)计算其所有字的“1”的总数,作为一个简易的校验和存储。虽然强度远不如CRC,但在某些内部数据一致性检查中可能有用。
- 位图资源管理:如前所述,用一个DWORD(32位)管理32个布尔资源(如工位状态、设备可用性)。
FC_CountOnes可以直接告诉你当前有多少资源被占用。结合FIND_BIT指令(查找第一个为1或0的位),可以构建一个简单的资源分配/回收管理器。 - 教学与调试:在调试复杂的状态字或标志位组合时,快速查看“1”的数量可以帮助你判断是否有多余的位被意外置位,是一个快速的健康检查工具。
5. 从V15到高版本:兼容性、迁移与最佳实践
你拿到的是V15版本的库。在当今V18甚至更新版本成为主流的环境下,如何使用和迁移它?
5.1 版本兼容性
- 高版本打开低版本库:TIA Portal具有很好的向下兼容性。你可以直接用TIA Portal V18打开这个V15的库文件(
.zal或项目文件)。软件会提示你需要进行“项目迁移”。这是一个单向升级过程,迁移后,库将转换为V18格式,无法再被V15打开。迁移过程通常是平滑的,但强烈建议先备份原文件。 - 低版本打开高版本:绝对不行。V15无法打开V18创建或迁移后的项目或库。
- 功能兼容性:这个FC使用的指令(MOVE, AND, SUB, CMP, JMP等)都是最基础的指令,在所有版本的S7-1200/1500 PLC中都完全支持。因此,功能本身是跨版本兼容的。
5.2 迁移操作步骤与注意事项
- 备份:复制一份原始的
.zip或.zal文件。 - 在TIA Portal V18中操作:不要直接双击文件。先打开TIA Portal V18,通过“项目” -> “迁移项目...”或“库”视图中的“导入”功能来操作。
- 处理迁移警告:迁移后,仔细查看迁移报告。对于这样一个简单的FC库,通常不会有错误,最多有一些“信息”类提示。
- 测试:迁移完成后,在新的V18项目中创建一个测试环境,调用迁移后的FC,用几组测试数据验证其功能是否正常。这是必不可少的一步。
5.3 创建与维护自定义库的最佳实践
通过这个项目,我们可以总结出一些创建和维护自己全局FC库的好习惯:
- 清晰的命名与注释:FC名称要见名知意,如
FC_CountBitsOn、FB_ScaleAnalogInput。在FC属性里,详细填写“标题”、“注释”,对每个输入输出参数也添加注释。这样其他工程师(或未来的你)使用时一目了然。 - 版本管理:在库的属性中,可以设置版本号(如V1.0.0)。当你对FC进行优化或修复Bug后,更新版本号。甚至可以将库文件用Git等工具管理起来。
- 最小依赖:尽量让FC独立,不要依赖于特定DB块中的全局变量。所有数据通过Input/Output接口传递。这保证了FC的纯粹性和可重用性。
- 提供示例:在库中,可以添加一个“Examples”文件夹,里面放一个专门用于演示如何调用这个FC的FB或OB块。这对于库的推广和使用非常有帮助。
- 标准化接口:考虑为类似功能的FC设计统一的接口风格。例如,错误输出都用
Error: Bool和ErrorID: Word,使调用者能形成一致的错误处理模式。
这个“计算1的个数”的FC,虽然功能聚焦,但它完美地体现了一个可重用软件组件的价值:封装复杂逻辑,提供简单接口,提升开发效率,保证代码质量。下次当你在程序中需要处理位计数时,希望这个现成的工具能让你事半功倍。
本文还有配套的精品资源,点击获取