This FB can be used to sample analog data by using two
GPIO Pins:
- one digital input
- one digital output
There are also other circuits out there, which only
need one single GPIO. But they have the clear disadvantage
that they need to reconfigure this GPIO constantly. Additionally
you have to take care about the VRef, which you are using, as it
will be pulled to ground by the GPIO acting as an output.
You need to take care to not destroy your board with that. In the
Circuit, recommended here, it is to my understanding much safer,
as we simply use the digital output as our VRef. Therefore we can
easily and safely pull it to ground.
Wirering::
Digital Output (VRef)
v
R1 (2,2kOhm)
v
Transistor / Poti / LDR / ...
|---> Input Pin
C1 (1uF)
v
GND
Scheduling:
The accuracy of the measurement relies in some aspects on the scheduling.
So it is recommended to schedule the task, which is driving this FB, as
frequent as possible. It is generally OK to combine this FB with the SoftPWM.
Just the sampling accuracy will slightly vary, depending on the frequency
of the PWM.
FUNCTION_BLOCK SoftAIN
VAR_INPUT
xInput: BOOL;
END_VAR
VAR_OUTPUT
xOutput: BOOL;
rVal: REAL;
END_VAR
VAR
xSampling: BOOL;
tStart: SYSTIME;
tCurrent: SYSTIME;
xInit: BOOL;
END_VAR
VAR
ctDischarge: SYSTIME;
crScale: REAL;
END_VAR
SysTimeGetUs(tCurrent);
IF xInit THEN
xInit := FALSE;
tStart := tCurrent;
END_IF
IF xSampling THEN
// Reading the sampling pin, and measure the time
IF xInput THEN
/// capacitor is full, stop sampling, and discharge
xSampling := FALSE;
rVal := LWORD_TO_REAL(tCurrent - tStart) * crScale;
tStart := tCurrent;
END_IF
ELSE
// Discharge the capacitor
IF tCurrent - tStart > ctDischarge THEN
// start sampling
tStart := tCurrent;
xOutput := TRUE;
END_IF
END_IF
This FB can be used to implement a Soft PWM, based
on the scheduler of a Linux system.
Note, that this function block should be used in a
a high priority task. The interval of the task will
constantly change.
FUNCTION_BLOCK SoftPWM
VAR_INPUT
dwFrequ: DWORD;
rDutyCycle: REAL;
END_VAR
VAR_OUTPUT
xPWM: BOOL;
END_VAR
VAR
hTask: SysTask.RTS_IEC_HANDLE;
ulInterval: DWORD;
END_VAR
VAR
cMinInterval: DWORD;
END_VAR
(* If current task is not, yet, determined, get it *)
IF hTask = SysTask.RTS_INVALID_HANDLE THEN
SysTask.SysTaskGetCurrent(ADR(hTask));
END_IF
(* if task is determined, adjust next cycle *)
IF hTask <> SysTask.RTS_INVALID_HANDLE THEN
IF xPWM THEN
ulInterval := LREAL_TO_DWORD((LREAL#1000000 / DWORD_TO_LREAL(dwFrequ)) * rDutyCycle);
ELSE
ulInterval := LREAL_TO_DWORD((LREAL#1000000 / DWORD_TO_LREAL(dwFrequ)) * (LREAL#1 - rDutyCycle));
END_IF
(* if we are near the boundary values, we snap the output either to TRUE or FALSE *)
IF ulInterval < cMinInterval THEN
SysTask.SysTaskSetInterval(hTask, DWORD#1000000 / dwFrequ);
ELSE
SysTask.SysTaskSetInterval(hTask, ulInterval);
xPWM := NOT xPWM;
END_IF
END_IF