Sample program 1 and 2 (offset/gain)
Download TwinCAT 3 project:
Program description / function:
- Calculation of an Offset (correction) value on the basis of the amplitudes of an AC input voltage (DC component ≠ 0) until a deviation of the offset smaller than "wOFFSET_MIN_VAL_REF" (in digits) is achieved.
- Calculation of a Gain correction value by presetting via "nPRESET_MAX_VAL" (in digits).
The configuration of the minimum permitted input frequency, the order of the Gain and Offset calculations, and the direct writing to the CoE directory ("PAI Scaler Settings" object) can be done in this sample program (see Variable declaration).
The following procedure is foreseen:
- Configuration of "bWriteToCoEEnable" = TRUE, i.e. on completion of the calculation of the correction values, they are written to the CoE object "PAI Scaler Settings".
- Set the terminal to "Extended Range" (0) via the object "PAI Settings Ch. 1" 0x8000:2E in the CoE directory.
- Connect a periodic signal (triangle, sine, square, …) to the terminal within the selected voltage/current range via the PAI Settings object 0x8000:01 (Interface).
- Start the program by setting "bEnable" to "TRUE".
- The end of the execution is recognizable by the variables "bScaleGainDone" and "bScaleOffsetDone", which are then both TRUE.
- If writing is enabled in the CoE ("bWriteToCoEEnable" = TRUE), the values determined should have been written to the object "PAI Scaler Settings" in the CoE directory (see variable "bError").
- If 6th was executed, the terminal can be set to "Linear" (1) via the object "PAI Settings Ch. 1" 0x8000:2E in the CoE directory. As a result, the terminal also performs the correction calculation internally (see: "nScaledSampleVal").
Comments:
Alternatively, the TC3 Analytics Library (TF3510) can be used instead of the function block "FB_GET_MIN_MAX". The function block "FB_ALY_MinMaxAvg_1Ch" can also be used for the determination of the min./max. values. The total calculation can then also be modified in this program by using the mean value provided by this function block.
In the case of the ELM350x/ ELM370x terminals , the "PAI Scaler Settings" object is 0x80n6, in addition to which the nOffset and nGain variables can also be directly written without the type conversion (REAL to DINT); scaling of the amplitude correction values with 65536 is also no longer necessary.
Example program 1 and 2 program code:
PROGRAM MAINVAR_INPUT   bEnable               :BOOL; // Start the code (Offset / Gain adjust)   nPAI_Sample AT%I*     :DINT; // Input samples of the measurement valueEND_VARVAR   // Enter your Net-Id here:   userNetId             :T_AmsNetId := 'a.b.c.d.x.y';   // Enter EtherCAT device address here:   nUserSlaveAddr        :UINT := 1002; // Check, if correct   // Configurations:   fMinFrequencyIn       :REAL:=1.5; // Hz   bScalingOrder         :BOOL:=FALSE; // TRUE: Start scale offset first   bWriteToCoEEnable     :BOOL:=FALSE; // TRUE: Enable writing to CoE   // ===============================================   // "Main" State controlling Offset/Gain adjusting:   nMainCal_State        :BYTE:=0;   // For CoE Object 0x8005 access:   fb_coe_write          :FB_EcCoESdoWrite; // FB for writing to CoE   nSTATE_WRITE_COE      :BYTE := 0;   nSubIndex             :BYTE;   nCoEIndexScaler       :WORD := 16#8005; // Use channel 1   // For ELM3xxx this is 0x8006   nSubIndScalGain       :BYTE := 16#02;   nSubIndScalOffs       :BYTE := 16#01;   nADSErrId             :UDINT; // Copy of ADS-Error ID   // ===============================================   fb_get_min_max        :FB_GET_MIN_MAX; // Min/Max values needed   // Note: you may also use "FB_ALY_MinMaxAvg_1Ch" of TwinCAT analytics)   // instead; there avg (average values can also be determinated   // Variables used for offset scaling:   nSTATE_SCALE_OFFSET   :INT := 0;   bScaleOffsetStart     :BOOL := FALSE;   bScaleOffsetDone      :BOOL := FALSE;   fOffsetDeviationVal   :REAL;   nOFFSET_MIN_VAL_REF   :WORD := 200;  // Max. limit value for offset   // Variables used for gain scaling:   nSTATE_SCALE_GAIN     :INT := 0;   bScaleGainStart       :BOOL := FALSE;   bScaleGainDone        :BOOL := FALSE;   nPRESET_MAX_VAL       :REAL := 3000000; // Target amplitude value   // ===============================================   // Variables for evaluating of gain and offset:   nOffset               :REAL := 0; // Offset value   nGain                 :REAL := 1; // Gain value   nScaledSampleVal      :REAL;   nDINT_Value           :DINT;   fb_trig_bEnable       :R_TRIG; // Trigger FB for Enable   bError                :BOOL := FALSE; // Evaluate..END_VARExecution part:
// THIS CODE IS ONLY AN EXAMPLE - YOU HAVE TO CHECK APTITUDE FOR YOUR APPLICATION// Example program 1 and 2 program code:// =====================================// 1. PAI setting of 0x80n0:2E must be "Extended Range" at first// 2. When writing of scaling values were done, switch to "Linear"// Calculation of the temporary value (..and use for ScopeView to check)nScaledSampleVal := nOffset + nGain * DINT_TO_REAL(nPAI_Sample);// Main-State Procedure:CASE nMainCal_State OF   0:      fb_trig_bEnable(CLK:=(bEnable AND NOT bError));      IF fb_trig_bEnable.Q THEN // Poll switch or button         // Initialize temporary offset and gain values:         nOffset:= 0;         nGain  := 1;         bScaleOffsetStart := bScalingOrder;         bScaleGainStart   := NOT bScalingOrder;         fb_get_min_max.nMinFreqInput := fMinFrequencyIn;         nMainCal_State := 10; // Start      END_IF   10:      IF (bScaleGainDone AND NOT bScalingOrder)        OR (bScaleOffsetDone AND bScalingOrder) THEN         bScaleOffsetStart := NOT bScalingOrder;         bScaleGainStart   := bScalingOrder;         nMainCal_State := nMainCal_State + 10;      END_IF   20:      IF bScaleGainDone AND bScaleOffsetDone THEN         nMainCal_State :=0; // All done, initalization for next start      END_IFEND_CASE// ----- Offset scaling (program 1) -----IF bScaleOffsetStart THEN   CASE nSTATE_SCALE_OFFSET OF   0:      bScaleOffsetDone := FALSE; // Initialization of confirmation flag      // Get min/max values within a period of the signal:      fb_get_min_max(nInputValue:=nScaledSampleVal);      IF fb_get_min_max.bRESULT THEN // Wait if Limit-Values are valid         // Min/Max Values valid, continue..         // calculate current offset deviation:         fOffsetDeviationVal :=         (fb_get_min_max.nMaxVal - ABS((fb_get_min_max.nMaxVal-fb_get_min_max.nMinVal)/2));         // Offset deviation check:         IF ABS(fOffsetDeviationVal) < nOFFSET_MIN_VAL_REF THEN            // Deviation in acceptable range - offset scaling done,            // now write correction value into CoE Object:            nDINT_Value := REAL_TO_DINT(nOffset);            // Initiate writing to CoE:            nSubIndex := nSubIndScalOffs;            nSTATE_WRITE_COE := 10;            nSTATE_SCALE_OFFSET := nSTATE_SCALE_OFFSET + 10;         ELSE            // Calculate new offset value (new by old with deviation)            nOffset := nOffset - fOffsetDeviationVal;         END_IF      END_IF   10:      IF(nSTATE_WRITE_COE = 0) THEN         // Scaling offset done within CoE of the device         bScaleOffsetDone := TRUE;         bScaleOffsetStart := FALSE;         nSTATE_SCALE_OFFSET := 0;        END_IF   END_CASEEND_IF// ----- Gain scaling (program 2) -----IF bScaleGainStart THEN   CASE nSTATE_SCALE_GAIN OF   0:      bScaleGainDone := FALSE; // Initialization of confirmation flag      // Get min/max values within a period of the signal:      fb_get_min_max(nInputValue:=DINT_TO_REAL(nPAI_Sample));      IF fb_get_min_max.bRESULT THEN // Wait if Limit-Values are valid         // Calculate Gain          nGain := nPRESET_MAX_VAL/ABS((fb_get_min_max.nMaxVal-fb_get_min_max.nMinVal)/2);         // ..shift gain value by 16 Bit left and convert to DINT:         nDINT_Value := REAL_TO_DINT(65536 * nGain);         //Due to 'output = gain * input + offset', the offset have to be adapted:         nOffset := nOffset * nGain;          // Initiate writing to CoE:         nSubIndex := nSubIndScalGain;         nSTATE_WRITE_COE := 10;         nSTATE_SCALE_GAIN := nSTATE_SCALE_GAIN + 10;        END_IF   10:      IF(nSTATE_WRITE_COE = 0) THEN         IF NOT (nOffset = 0) THEN            // (bScalingOrder is TRUE)            nDINT_Value := REAL_TO_DINT(nOffset);            // Initiate writing to CoE (again):            nSubIndex := nSubIndScalOffs;            nSTATE_WRITE_COE := 10;            END_IF         nSTATE_SCALE_GAIN := nSTATE_SCALE_GAIN + 10;        END_IF      20:      IF(nSTATE_WRITE_COE = 0) THEN                 // Scaling gain done within CoE of the device         bScaleGainStart := FALSE;         bScaleGainDone := TRUE;         nSTATE_SCALE_GAIN := 0; // Set initial state        END_IF   END_CASEEND_IFIF (nSTATE_WRITE_COE > 0) THEN   IF bWriteToCoEEnable THEN      CASE nSTATE_WRITE_COE OF      10:         // Prepare CoE write access         fb_coe_write(            sNetId:=     userNetId,            nSlaveAddr:= nUserSlaveAddr,            nIndex:=     nCoEIndexScaler,            bExecute:=   FALSE,            tTimeout:=   T#1S         );          nSTATE_WRITE_COE := nSTATE_WRITE_COE + 10;      20:         // Write nDINT_Value to CoE Index "Scaler":         fb_coe_write(         nSubIndex:= nSubIndex,         pSrcBuf:= ADR(nDINT_Value),         cbBufLen:= SIZEOF(nDINT_Value),         bExecute:= TRUE         );         nSTATE_WRITE_COE := nSTATE_WRITE_COE + 10;      30:         fb_coe_write();         IF NOT fb_coe_write.bBusy THEN            nSTATE_WRITE_COE := 0;         END_IF      END_CASE   ELSE      nSTATE_WRITE_COE := 0;    END_IFEND_IFIF(fb_coe_write.bError) AND NOT bError THEN   bError := TRUE;   nADSErrId := fb_coe_write.nErrId;   // CoE write acccess error occured: reset all   nSTATE_WRITE_COE := nMainCal_State := 0;   bScaleOffsetDone := bScaleOffsetStart := FALSE;   bScaleGainDone   := bScaleGainStart   := FALSE;END_IF