In PLC programs, raw analog values often travel much farther than they should.
An analog input may deliver a value from 0 to 1023. The value is then passed into a PID
controller, an HMI bar graph, a valve command, or a drive command. Before long, the number
1023 appears everywhere.
valuePid := LIMIT(0.0, valuePid, 1023.0);
valueBarGraph := 100.0 * valuePid / 1023.0;The problem is not the calculation itself. The problem is the convention hidden inside it.
The code assumes that 1023 is the maximum value of the signal. If that assumption is used
in one place, it is manageable. If it is spread over ten thousand lines, the range of one
input terminal has become part of the architecture. An impressive achievement, if the goal
was to make a hardware detail everybody’s problem.
The better approach is to normalize the value once, close to the boundary of the system.
The Boundary Should Know the Raw Range
Normalization means converting one range into another common range.
For control signals, a range from 0.0 to 1.0 is often practical. It can be interpreted
as a relative command, or as a percentage divided by 100.
The raw ADC range stays near the input handling code. The rest of the application receives a normalized value.
Instead of this:
valueBarGraph := 100.0 * valuePid / 1023.0;the application can work with this:
valueBarGraph := 100.0 * valueNormalized;The important change is not saving one division. The important change is that the rest of the code no longer needs to know whether the input came from a 10-bit ADC, a 12-bit ADC, an EtherCAT terminal, a simulation value, or a fieldbus device with a vendor-specific range.
A Linear Transform
The general transform from one range to another is a linear equation.
output = (outputMax - outputMin) / (inputMax - inputMin) * (input - inputMin) + outputMinIn Structured Text, this can be implemented as a small function.
FUNCTION transformLinear : LREAL
VAR_INPUT
valueInput : LREAL;
valueInputMin : LREAL;
valueInputMax : LREAL;
valueOutputMin : LREAL;
valueOutputMax : LREAL;
limitOutput : BOOL;
END_VAR
IF ABS(valueInputMax - valueInputMin) > 1E-9 THEN
transformLinear :=
(valueOutputMax - valueOutputMin)
/ (valueInputMax - valueInputMin)
* (valueInput - valueInputMin)
+ valueOutputMin;
IF limitOutput THEN
IF valueOutputMax >= valueOutputMin THEN
transformLinear := LIMIT(
valueOutputMin,
transformLinear,
valueOutputMax);
ELSE
transformLinear := LIMIT(
valueOutputMax,
transformLinear,
valueOutputMin);
END_IF
END_IF
ELSE
transformLinear := valueOutputMin;
END_IFThe function also handles reversed output ranges and avoids division by zero. Returning the minimum output value for an invalid input range is a design choice. In production code, the caller may also need an error state or diagnostic flag. Quietly returning a number is not always enough.
A Potentiometer Example
Assume a potentiometer is connected to an analog input. The raw value is converted into a
relative value from 0.0 to 1.0.
FUNCTION_BLOCK Potentiometer
VAR
valueRelative : LREAL;
END_VAR
VAR CONSTANT
VALUE_RAW_MIN : LREAL := 0.0;
VALUE_RAW_MAX : LREAL := 1023.0;
END_VARThe update method performs the conversion.
METHOD update
VAR_INPUT
valueRaw : DINT;
END_VAR
valueRelative := transformLinear(
valueInput := TO_LREAL(valueRaw),
valueInputMin := VALUE_RAW_MIN,
valueInputMax := VALUE_RAW_MAX,
valueOutputMin := 0.0,
valueOutputMax := 1.0,
limitOutput := TRUE);The public property returns the normalized value.
PROPERTY Value : LREALGetter:
Value := valueRelative;From this point on, the rest of the application does not use 1023. It uses
potentiometer.Value.
That value has a simple contract:
0.0 = minimum
1.0 = maximumUse the Normalized Contract
A variable frequency drive can expose an operation method with relative values.
INTERFACE IDrive
METHOD executeOperation : BOOL
VAR_INPUT
speedRelative : LREAL;
torqueRelative : LREAL;
END_VARBoth inputs use the same convention:
0.0 = no command
1.0 = maximum configured commandNow the potentiometer can be connected to different meanings without changing its own implementation.
CASE choice OF
1:
// Fixed speed, variable torque.
drive.executeOperation(
speedRelative := 0.1,
torqueRelative := potentiometer.Value);
2:
// Variable speed, full torque.
drive.executeOperation(
speedRelative := potentiometer.Value,
torqueRelative := 1.0);
ELSE
drive.executeOperation(
speedRelative := 0.0,
torqueRelative := 0.0);
END_CASEThis works because both sides agree on the normalized range. The potentiometer does not need to know the maximum speed of the drive. The drive does not need to know the ADC range of the potentiometer.
The conversion back to an engineering value happens inside the drive or close to the drive.
speedSetpoint := speedRelative * speedMax;
torqueSetpoint := torqueRelative * torqueMax;The raw range and the physical range stay at the boundaries. The application logic works with the relative command.
Where Normalization Helps
Normalized values are useful when a signal represents a relative command or relative feedback.
Typical examples are analog inputs, valve positions, drive speed commands, torque limits, PID outputs, HMI sliders, and simulation inputs.
PID controllers are a good example. If the output is normalized to 0.0..1.0, it can be
connected directly to a relative valve position, torque command, or speed command. The
controller does not need to know whether the final actuator is a proportional valve, a VFD,
or a simulated load.
The same idea also helps testing. A test can set a fake potentiometer to 0.25 without
knowing the raw ADC value. The test checks behavior against the application contract, not
against one hardware terminal.
Limits
Normalization is not a reason to delete engineering units from the program.
Pressure, temperature, length, speed, and force should often be represented in engineering
units inside the application. A pressure controller should usually reason about bar or
pascals, not only about 0.0..1.0.
Normalized values are best for relative commands and generic interfaces. Engineering values are better when the physical meaning matters.
The rule is simple:
Normalize hardware-specific ranges at the boundary. Keep physical values in engineering units when the physics matters.
Also document the convention. If speedRelative = 1.0 means speedMax, then speedMax
must be visible and well defined. If a signal can exceed the normal range, decide whether
to clamp it, report it, or use it for diagnostics.
Conclusion
Normalization prevents hardware ranges from leaking through the whole PLC project.
The ADC limit belongs near the analog input. The maximum speed belongs near the drive. The application logic between them should depend on a clear contract, not on scattered constants.
For many PLC systems, 0.0..1.0 is a useful range for relative signals. It makes interfaces
simpler, tests easier, and code less dependent on one piece of hardware.
The practical rule is boring, which is usually a good sign:
Convert once at the boundary. Use the normalized value in the application. Convert back only where the physical output requires it.
