Apologies for the length of this post.. but I'd really appreciate some advice...
I have some ST code generating a program fault (major fault), and it is driving me
(CompactLogix controller, rev 15.3)
Normally, none of the code is causing a fault - it verifies correctly, and does what I expect it to do... regardless if I enter it as an online edit, or download the program to the controller... However, when the PLC is switched from program to run, or the PLC powers up, I get a major fault (type 4 code 83 - out of range / data tested not in required limits).
I have my controller fault routine clearing the fault and generating an alarm message, but I have disabled it, in order to find out where in the program the fault is happening.
I first found the problem in one of my ST routines, made some changes that seemed to fix it, and tried to apply those changes to a second ST routine - but got inconsistent results! I also narrowed down what was happening, when trying to change the second ST routine (see notes below) - I managed to get it to fault based on the value of one my index tags...
The first ST routine, original code:
- The purpose of this ST routine is to clear, then check and set, some temperature deviation alarms, based on the deviation flags from a PIDE instruction.
- This routine is called by a JSR from the parent routine.
- Tags "Dev_Alarms_Low" & "Dev_Alarms_High" are of type DINT[2]
- Tags "PID_Dev_Low_Alarms" & "PID_Dev_High_Alarms" are of type DINT[2]. One bit of each is set from a one of 32 PIDE instructions.
- The tags "Dev_Alarms_Low[0]", "Dev_Alarms_High[0]", "Dev_Alarms_Low[1]", "Dev_Alarms_High[1]" are returned to the parent routine via the JSR, as DINT tags "Alarms[4]", "Alarms[6]", "Alarms[5]", "Alarms[7]".
- The tag "Alarms" (not used inside the ST routine), is of type DINT[18]
[codebox]
(* Receive parameters from parent routine *)
SBR(Number_Zones, Current_Temp, Current_SP, Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1] ) ;
(* Clear alarm flags *)
For Index_1 := 0 To 31 By 1 Do
[faults here]
Dev_Alarms_Low[0].[Index_1] := 0 ;
Dev_Alarms_Low[1].[Index_1] := 0 ;
Dev_Alarms_High[0].[Index_1] := 0 ;
Dev_Alarms_High[1].[Index_1] := 0 ;
End_For ;
For Index_2 := 0 To (Number_Zones - 1) By 1 Do
(* Set devs if average temp greater than 200degC *)
If (Current_Temp[Index_2] > 2000) Then
(* Check devs for PIDEs 1 to 32 *)
If (Index_2 <= 31) Then
Dev_Alarms_Low[0].[Index_2] := PID_Dev_Low_Alarms[0].[Index_2] ;
Dev_Alarms_High[0].[Index_2] := PID_Dev_High_Alarms[0].[Index_2] ;
(* Check devs for PIDEs 33+ *)
ElsIf (Index_2 >= 32) Then
[faults here]
Dev_Alarms_Low[1].[Index_2 - 32] := PID_Dev_Low_Alarms[1].[Index_2 - 32] ;
Dev_Alarms_High[1].[Index_2 - 32] := PID_Dev_High_Alarms[1].[Index_2 - 32] ;
End_If ;
End_If ;
End_For ;
(* Return parameters to parent routine *)
RET(Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1]) ;
[/codebox]
- The controller faults at the first 'For...Do' loop (clearing alarms), and at the second 'For...Do' loop (setting alarms).
- So I changed the code to this:
The first ST routine, changed code:
[codebox]
(* Receive parameters from parent routine *)
SBR(Number_Zones, Current_Temp, Current_SP, Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1] ) ;
(* Clear alarm flags *)
Dev_Alarms_Zero_DINT := 0 ;
COP(Dev_Alarms_Zero_DINT,Dev_Alarms_Low[0],31) ;
COP(Dev_Alarms_Zero_DINT,Dev_Alarms_Low[1],31) ;
COP(Dev_Alarms_Zero_DINT,Dev_Alarms_High[0],31) ;
COP(Dev_Alarms_Zero_DINT,Dev_Alarms_High[1],31) ;
For Index_2 := 0 To (Number_Zones - 1) By 1 Do
(* Set devs if average temp greater than 200degC *)
If (Current_Temp[Index_2] > 2000) Then
(* Check devs for PIDEs 1 to 32 *)
If (Index_2 <= 31) Then
Dev_Alarms_Low[0].[Index_2] := PID_Dev_Low_Alarms[0].[Index_2] ;
Dev_Alarms_High[0].[Index_2] := PID_Dev_High_Alarms[0].[Index_2] ;
(* Check devs for PIDEs 33+ *)
ElsIf (Index_2 >= 32) Then
Index_3 := Index_2 - 32 ;
Dev_Alarms_Low[1].[Index_3] := PID_Dev_Low_Alarms[1].[Index_3] ;
Dev_Alarms_High[1].[Index_3] := PID_Dev_High_Alarms[1].[Index_3] ;
End_If ;
End_If ;
End_For ;
(* Return parameters to parent routine *)
RET(Dev_Alarms_Low[0], Dev_Alarms_Low[1], Dev_Alarms_High[0], Dev_Alarms_High[1]) ;
[/codebox]
- This fixed the fault for the first 'For..Do loop' (used COP instead), and seemed to fix the second 'For...Do" loop as well. All good, or so I thought.
- So I was going to apply these changes to a another ST routine that works in a similar way (that was giving me the same fault with the original code)....
Second ST routine, original code
- The structure of the tags & the logic for this routine is similar to the first.
[codebox]
If Offset_Number_Zones < Offset_Number_TCs Then
For Index_13 := Offset_Number_Zones To (Offset_Number_TCs - 1) By 1 Do
If Index_13 <= 31 Then
If (Not(Alarm[2].[Index_13])) And (Not(Alarm[12].[Index_13])) Then
Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ;
Else
Offset_Current_Temp[Index_13] := Offset_Average_Temp ;
End_If ;
ElsIf Index_13 >= 32 Then
[faults here]
If (Not(Alarm[3].[Index_13 - 32])) And (Not(Alarm[13].[Index_13 - 32])) Then
Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ;
Else
Offset_Current_Temp[Index_13] := Offset_Average_Temp ;
End_If ;
End_If ;
End_For ;
End_If ;
[/codebox]
- So I made the same changes as I did for the first ST routine:
Second ST routine, changed code:
[codebox]
If Offset_Number_Zones < Offset_Number_TCs Then
For Index_13 := Offset_Number_Zones To (Offset_Number_TCs - 1) By 1 Do
If Index_13 <= 31 Then
[faults here!!]
If (Index_13 < 31) And (Not(Alarm[2].[Index_13])) And (Not(Alarm[12].[Index_13])) Then
Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ;
Else
Offset_Current_Temp[Index_13] := Offset_Average_Temp ;
End_If ;
ElsIf Index_13 >= 32 Then
Index_26 := (Index_13 - 32) ;
If (Index_26 < 31) And (Not(Alarm[3].[Index_26])) And (Not(Alarm[13].[Index_26])) Then
Offset_Current_Temp[Index_13] := Offset_TC_Temp[Index_13] ;
Else
Offset_Current_Temp[Index_13] := Offset_Average_Temp ;
End_If ;
End_If ;
End_For ;
End_If ;
[/codebox]
- I was expecting this fix the fault... but instead, I was getting a fault in the first "If... Then' loop, rather than the second !!
- I tried setting the tag "Index_13" prior to the 'For..Do' loop, but that did not work.
- I noticed that the value of the tag "Index_13" was always 33 after the routine was executed. If I then changed the PLC from Run to Program and back to Run, the controller would fault. However, if I first manually set tag "Index_13" to zero (type in a value of zero in the tag monitoring / editing window), then changed the controller to Run mode, it would not fault!!
So...
I can't find what is wrong.
Is there something fundamental that I am doing wrong?
The easy fix is not to set any alarms in my program for this fault type & code.. but I don't like that, I'd like to fix it properly...
Can anyone help me ?!
Cheers,
Andrew W