--- 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. ~~~ST 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 ~~~ ~~~ST 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. ~~~ST 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 ~~~ ~~~ST (* 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 ~~~