LAD和LEAD函数

lad
请添加图片描述

lead
请添加图片描述
原表
请添加图片描述

<think>我们之前讨论的问题:将AIW64中的A/D值(0~27648)转换为对应温度值(0~800摄氏度,单位为0.1摄氏度),结果存放到MW30中,触发条件为I0.2的上升沿。 转换公式:温度值(单位0.1℃) = (A/D值 × 8000) / 27648 简化公式:温度值 = (A/D值 × 125) / 432 (因为8000/27648 = 125/432) 现在需要用两种PLC编程语言表示: 1. LAD(梯形图) 2. SCL(结构化控制语言) 注意:使用整数运算,避免浮点数。 由于在LAD中不能直接写表达式,我们需要用基本指令来实现乘法除法,并使用32位整数运算(因为乘法可能溢出16位整数范围:27648*125=3,456,000,在16位整数范围-32768~32767之外,但在32位整数范围内)。 步骤: 1. 检测I0.2的上升沿(使用正跳沿检测指令)。 2. 将AIW64(即IW64)的值读入,转换为双整数(32位)。 3. 乘以125(32位乘法)。 4. 除以432(32位除法)。 5. 将结果转换为整数(16位)并存入MW30(因为结果在0~8000之间,不会超过16位整数的正数范围)。 在LAD中,我们可以使用以下指令: - 正跳沿检测:P指令(或使用边沿检测存储器位) - 数据传送:MOVE - 转换指令:ITD(整数转双整数) - 算术指令:MUL_DI(双整数乘法),DIV_DI(双整数除法) - 转换指令:DTI(双整数转整数)或者直接取低16位(但建议用转换指令,因为结果在16位范围内) 在SCL中,我们可以直接使用表达式。 注意:由于我们使用双整数运算,除法会得到双整数结果(截断小数部分),但因为公式设计时27648能被432整除,所以不会产生小数,因此结果是精确的。 下面分别给出LADSCL的实现。 由于LAD的表示需要图形,这里我们用文字描述梯形图的网络结构,以及对应的指令。 同时,由于在SCL中可以直接写表达式,所以代码会更简洁。 另外,根据引用[3],在SCL中可以使用赋值语句算术运算符。 在编写程序时,我们需要注意: - 在LAD中,我们使用一个网络来检测边沿并执行转换。 - 在SCL中,我们可以使用IF语句配合边沿检测函数(如上升沿函数)来实现。 开始编写: 1. LAD(梯形图)实现: 网络1: I0.2 P M0.0 // 检测I0.2的上升沿,用M0.0存储边沿状态(或者使用临时变量) |--------|P|-------( ) // 实际上,P指令可以直接用于条件跳转或置位一个中间位,这里我们用M0.0作为中间标志 网络2: M0.0 |-------[MOV] IW64 -> MW10 // 将AIW64的值读取到MW10(16位整数) |-------[ITD] MW10 -> MD14 // 将MW10中的整数转换为双整数,存入MD14(32位) |-------[MUL] MD14 * 125 -> MD18 // MD14乘以125,结果存入MD18(注意:MUL_DI指令) |-------[DIV] MD18 / 432 -> MD22 // MD18除以432,结果存入MD22(双整数除法) |-------[DTI] MD22 -> MW30 // 将MD22中的双整数转换为整数并存入MW30(因为结果在0~8000,可以安全转换) 但是,由于LAD中一个网络不能放太多指令,我们可能需要拆分网络,并且注意每个指令都需要使能(EN)使能输出(ENO)连接。 更准确的表示(使用使能流): 网络1:边沿检测 I0.2 P M0.0(线圈) // 当I0.2有上升沿时,M0.0在一个扫描周期内为1 网络2:读取并转换 M0.0 |-----| |----- [EN] MOV_ENO | |----- [MOV] IN: IW64, OUT: MW10 // 将IW64移动到MW10 MOV_ENO |-----| |----- [EN] ITD_ENO | |----- [ITD] IN: MW10, OUT: MD14 // 转换整数为双整数 ITD_ENO |-----| |----- [EN] MUL_ENO | |----- [MUL_DI] IN1: MD14, IN2: 125, OUT: MD18 // 乘以125 MUL_ENO |-----| |----- [EN] DIV_ENO | |----- [DIV_DI] IN1: MD18, IN2: 432, OUT: MD22 // 除以432 DIV_ENO |-----| |----- [EN] DTI_ENO | |----- [DTI] IN: MD22, OUT: MW30 // 双整数转整数,存入MW30 注意:在实际编程中,每一步的ENO连接到下一步的EN,形成链式调用。如果某一步出错(如溢出),则后续步骤不执行。 2. SCL实现: 在SCL中,我们可以使用以下代码(在OB1中,或者在一个FC/FB中,然后在OB1中调用)。这里我们假设在OB1中编写: ``` // 定义一个边沿检测的临时变量 VAR_TEMP Last_I0_2 : BOOL; END_VAR // 在代码中检测上升沿 IF I0.2 AND NOT Last_I0_2 THEN // 读取AIW64(在SCL中,IW64可以用符号名或直接地址) // 注意:在SCL中,直接使用IW64可能会被当作输入过程映像区,因此确保在程序开始更新了过程映像。 // 转换: MW30 := DINT_TO_INT( ( INT_TO_DINT(IW64) * 125 ) / 432 ); END_IF; // 更新Last_I0_2 Last_I0_2 := I0.2; ``` 或者,使用系统提供的上升沿函数(如P_TRIG),但需要实例化一个边沿检测功能块。 另一种方式(使用静态变量做边沿存储,如果在FC中则需要INOUT变量,如果在FB中则用静态变量): 例如,在OB1中,我们可以这样: ``` // 静态变量用于存储上一次扫描的I0.2状态(在OB1中,如果多次使用,需要单独管理,或者使用FB) // 但是OB1没有静态变量,所以可以在全局DB中定义一个位,或者使用临时变量(但临时变量每次扫描都初始化,所以需要保存到全局) // 因此,更推荐在OB1中定义一个全局DB的变量,或者使用M区。 // 这里假设我们使用M0.1作为上次状态存储(在程序中使用前初始化一次?) // 或者,我们可以在OB1的临时变量中定义,但是需要每次扫描保存,下次扫描使用,这需要OB1有保持的存储区,但临时变量没有保持性。 // 所以,我们使用M0.1(或者全局DB中的变量)来存储上一次的状态。 // 定义:假设已经有一个全局DB,或者我们使用M区,例如MB0的位0(M0.0用于边沿输出,M0.1用于上一次状态)?但之前LAD中用了M0.0,这里避免冲突用M0.1。 // 实际上,我们可以使用一个单独的边沿检测功能块(如R_TRIG)实例。 // 为了简单,我们使用全局M位:M0.1存储上一次状态。 // 在程序开始,检测上升沿: IF I0.2 AND NOT "M0.1" THEN // 执行转换 "MW30" := ( INT_TO_DINT("IW64") * 125 ) / 432; // 由于结果是双整数,但范围在0-8000,可以直接赋值给MW30(16位整数) // 注意:在SCL中,除法运算符对于整数是截断的,但这里我们使用双整数运算,除法也是整数除法,符合要求。 END_IF; // 更新上一次状态 "M0.1" := I0.2; ``` 但是,为了避免使用全局变量,我们可以使用边沿检测功能块。在SCL中,我们可以这样: ``` // 在变量定义区定义: VAR EdgeDetector : R_TRIG; // 上升沿检测功能块实例 END_VAR // 在代码中: EdgeDetector(CLK := I0.2); IF EdgeDetector.Q THEN "MW30" := ( INT_TO_DINT("IW64") * 125 ) / 432; END_IF; ``` 注意:在SCL中,地址可以直接用双引号括起来,或者使用符号名。 由于问题要求用LADSCL表示,我们给出两种语言的代码。 但是,请注意:在LAD中,我们使用了一个中间标志位M0.0来触发转换,并且每一步使用使能流连接。在SCL中,我们使用边沿检测功能块或直接检测上升沿。 下面,我们分别写出: LAD实现(梯形图)的详细步骤: 网络1:检测I0.2的上升沿 使用P指令:当I0.2从0变1时,产生一个扫描周期的脉冲。 指令:---|P|-------(M0.0) // M0.0线圈接通一个扫描周期 网络2:用M0.0触发,开始转换 使用MOVE指令将IW64移动到MW10(16位整数) M0.0 ----| |------ [MOV_EN] MOV: IN -> IW64, OUT -> MW10 如果MOV_EN成功(ENO),则触发ITD(整数转双整数)指令 MOV_ENO ----| |------ [ITD_EN] ITD: IN -> MW10, OUT -> MD14 ITD_ENO ----| |------ [MUL_DI_EN] MUL_DI: IN1 -> MD14, IN2 -> 125, OUT -> MD18 MUL_DI_ENO ----| |------ [DIV_DI_EN] DIV_DI: IN1 -> MD18, IN2 -> 432, OUT -> MD22 DIV_DI_ENO ----| |------ [DTI_EN] DTI: IN -> MD22, OUT -> MW30 注意:在LAD中,每一步的使能(EN)由前一步的使能输出(ENO)M0.0共同触发?实际上,网络2中每一步都需要M0.0吗?不,我们使用链式结构:M0.0只触发第一步,然后每一步的完成触发下一步。 所以,网络2可以这样: M0.0 EN ENO | |-------- [MOV] IW64 -> MW10 ----|------| | | | | | |------ [ITD] MW10 -> MD14 ... | | | EN ENO | | |--------|-----| | | | |------ [MUL_DI] MD14, 125 -> MD18 ... | | | EN ENO | | |------------|-----| | | | |------ [DIV_DI] MD18, 432 -> MD22 ... | | | EN ENO | | |------------|-----| | | | |------ [DTI] MD22 -> MW30 | | | EN ENO | | |------------|-----| (可以连接到下一步或空) 但是,在梯形图中,我们通常将前一个指令的ENO连接到下一个指令的EN,形成一个链。而第一个指令的EN由触发条件M0.0使能。 因此,网络2的梯形图可以表示为: (开始) M0.0 EN MOV ----| |-------[MOV: IW64 -> MW10]---(ENO)----[ITD: MW10 -> MD14]---(ENO)----[MUL_DI: MD14, 125 -> MD18]---(ENO)----[DIV_DI: MD18, 432 -> MD22]---(ENO)----[DTI: MD22->MW30]---(ENO)---- (结束) 每个指令盒的EN端由前一个指令盒的ENO连接,第一个指令盒的EN由M0.0控制。 注意:在梯形图中,指令盒的执行依赖于EN,如果EN为1则执行,执行成功则ENO为1(否则为0并报错)。我们这里假设没有错误,所以链式执行。 在SCL中,我们使用边沿检测功能块(R_TRIG)来实现: VAR R_TRIG_I0_2: R_TRIG; // 定义一个上升沿检测功能块实例 END_VAR R_TRIG_I0_2(CLK := I0.2); IF R_TRIG_I0_2.Q THEN "MW30" := (INT_TO_DINT("IW64") * 125) / 432; END_IF; 或者,如果不想使用功能块,也可以使用静态变量存储上一次的状态(在FB中),或者在全局DB中定义一个变量。这里以在FB中为例(如果放在FC中,则需要INOUT参数存储上一次状态)。 由于问题没有指定放在OB还是FB/FC,我们以在OB1中使用全局变量存储上一次状态为例: // 全局DB中定义"Last_I0_2"(如"DB1".Last_I0_2),或者使用M区(如M0.1) IF I0.2 AND NOT "M0.1" THEN "MW30" := (INT_TO_DINT("IW64") * 125) / 432; END_IF; "M0.1" := I0.2; 但注意,在SCL中,我们更推荐使用功能块R_TRIG,因为它封装了边沿检测,更可靠。 最后,我们输出两种语言的代码。 由于LAD无法直接以文本形式显示,我们以指令序列的文字描述连接方式给出。SCL可以直接给出代码。 另外,根据引用[1][2],LADSCL各有优势,这里我们按照要求提供两种实现。 下面,我们正式写出答案。 </think>### LAD(梯形图)实现 在LAD中,使用基本指令链实现转换过程。程序结构如下: **网络1:I0.2上升沿检测** ```plaintext I0.2──┬──P┬─(M0.0) // 检测I0.2上升沿,置位M0.0(一个扫描周期) └───┘ ``` **网络2:温度转换逻辑** ```plaintext M0.0──[MOV_IW64]─┬─[ITD]─┬─[MUL_DI:125]─┬─[DIV_DI:432]─┬─[DTI]──(MW30) │ │ │ │ │ └──(MD10) └──(MD14) └──(MD18) └──(MW8) ``` **指令说明**: 1. **MOV_IW64**:将`IW64`值移动到`MW8`(16位) 2. **ITD**:将`MW8`转换为双整数存入`MD10`(32位) 3. **MUL_DI**:`MD10 * 125` → 结果存`MD14` 4. **DIV_DI**:`MD14 / 432` → 结果存`MD18` 5. **DTI**:将`MD18`转换回16位整数存入`MW30` > **关键点**: > - 使用`P`指令确保仅在I0.2上升沿触发一次 > - 所有运算使用双整数(32位)避免溢出(最大中间值 27648×125=3,456,000) > - 最终结果范围0~8000(对应0.0~800.0°C),可直接存入`MW30`[^1][^2] --- ### SCL(结构化控制语言)实现 ```scala // 定义边沿检测功能块实例 VAR R_TRIG_I0_2 : R_TRIG; // 上升沿检测块 END_VAR // 主逻辑 R_TRIG_I0_2(CLK := I0.2); // 检测I0.2上升沿 IF R_TRIG_I0_2.Q THEN // 上升沿触发 MW30 := INT_TO_DINT(IW64) * 125 / 432; // 整数运算公式 END_IF; ``` **代码说明**: 1. 使用`R_TRIG`功能块检测I0.2上升沿 2. 直接应用简化公式: $$ \text{MW30} = \frac{\text{IW64} \times 125}{432} $$ 3. 隐含数据类型转换: - `INT_TO_DINT(IW64)` 确保32位运算 - 结果自动截断为16位整数(范围0~8000) > **优势**: > - 代码简洁(仅5行),接近数学公式表达 > - 无需中间变量,编译器自动优化数据类型 > - 支持结构化编程,适合复杂算法[^1][^3] --- ### 验证示例 | IW64值 | 计算过程 | MW30结果 | 实际温度 | |--------|------------------------|----------|----------| | 0 | (0×125)/432 = 0 | 0 | 0.0°C | | 13824 | (13824×125)/432 = 4000 | 4000 | 400.0°C | | 27648 | (27648×125)/432 = 8000 | 8000 | 800.0°C | > 注:公式设计保证整除无余数(如27648÷432=64),结果精确无误差[^1]。 --- ### 相关问题 1. **如何在PLC中实现非线性传感器(如热电偶)的温度补偿?** > 需结合查表法或多项式拟合(引用[4]的线性化技术)[^4] 2. **SCL语言相比LAD在处理复杂数学运算时有哪些优势?** > 支持直接代数表达式、循环语句(`FOR`/`WHILE`)数组操作[^3] 3. **若需扩展为多通道采集(如8路PT100),程序结构如何优化?** > 可通过FB封装转换逻辑,结合Modbus模块实现(引用[2]方案)[^2] [^1]: LAD是PLC编程使用最广泛的语言...SCL也一定会壮大起来的,因为它有自己优势 [^2]: 8通道Pt100/Pt1000转RS-485/232...通讯方式采用ASCII码通讯协议或MODBUS RTU通讯协议 [^3]: 函数/块(FC、FB)的SCL编程无需在OB1中调用...FOR:用于循环执行一段代码 [^4]: 在博途中,如何在LAD/FBD/STL SCL语言中注释掉所选代码?...被注释的指令在程序中就不起作用了
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值