[r2]: / trunk / legacy / Libraries / IoDrvGPIO.library.md  Maximize  Restore  History

Download this file

1046 lines (939 with data), 25.6 kB

<?xml version="1.0" encoding="utf-8"?>enumenum---

         This function block is the base class for SPI devices controlled via the SPI device /dev/spidev0.0.

It is meant to be extended by other function blocks that overload the body and the following methods/properties and replace it with their specific implementation,
always including a call of the base implementation with super^.<MethodName>() :
- body (general handling, start-up)
- AfterReadInputs (reading input data)
- BeforeWriteOutputs (writing output data)
- Initialize [optional] (used to read parameters from the configuration)
- Operational [optional] (used to signal the status of the device)
The body of this FB is called by the methods AfterReadInputs and BeforeWriteOutputs, where _xAfterReadInputs indicates the caller.
Use _iState to control your statemachine. A value of 10 by default indicates that the drive is operational.
Do not forget to call the base implementation with super^(), where the diagnosis indicators are set according to the Operational property.

FUNCTION_BLOCK GPIO
VAR_INPUT
    aeType: ARRAY [..] OF ;
    axOutputs: ARRAY [..] OF ;
    udiPWMFrequency: UDINT;
    uiPWMDutyCycle: UINT;
END_VAR
VAR_OUTPUT
    dwInputs: DWORD;
    axInputs: ARRAY [..] OF ;
END_VAR
VAR
    _pConnector: pointer;
    _udiInstance: UDINT;
    _uiModuleType: UINT;
    _aeOldType: ARRAY [..] OF ;
    _axOldOutputs: ARRAY [..] OF ;
    _udiOldPWMFrequency: UDINT;
    _uiOldPWMDutyCycle: UINT;
    _hShmGPIOBASE: SysTypes.RTS_IEC_HANDLE;
    _iState: INT;
    _iOldState: INT;
    _eProcessorType: ProcessorType;
    _xAfterReadInputs: BOOL;
END_VAR
VAR
    BCM2708_PERI_BASE: DWORD;
    BCM2709_PERI_BASE: DWORD;
    GPIO_PWMCTL: DWORD;
    GPIO_PWMSTA: DWORD;
    GPIO_PWMDMAC: DWORD;
    GPIO_PWMRNG1: DWORD;
    GPIO_PWMDAT1: DWORD;
    GPIO_PWMFIF1: DWORD;
    GPIO_PWMRNG2: DWORD;
    GPIO_PWMDAT2: DWORD;
    GPIO_CM_PWMCTL: DWORD;
    GPIO_CM_PWMDIV: DWORD;
    PAGE_SIZE: DWORD;
    BLOCK_SIZE: DWORD;
END_VAR
VAR
    PERI_BASE: DWORD;
    GPIO_PADS: DWORD;
    GPIO_BASE: DWORD;
    GPIO_TIMER: DWORD;
    GPIO_PWMBASE: DWORD;
    GPIO_CLKBASE: DWORD;
END_VAR
IF _iState &lt;&gt; _iOldState THEN
    IF Operational THEN
        IoMgrConfigResetDiagnosis(_pConnector, ConnectorFlags.CF_CONNECTOR_ERROR);
        IoMgrConfigSetDiagnosis(_pConnector, ConnectorFlags.CF_DRIVER_AVAILABLE OR ConnectorFlags.CF_CONNECTOR_FOUND OR ConnectorFlags.CF_CONNECTOR_CONFIGURED OR ConnectorFlags.CF_CONNECTOR_ACTIVE);
    ELSE
        IoMgrConfigResetDiagnosis(_pConnector, ConnectorFlags.CF_CONNECTOR_ACTIVE);
        IoMgrConfigSetDiagnosis(_pConnector, ConnectorFlags.CF_DRIVER_AVAILABLE OR ConnectorFlags.CF_CONNECTOR_FOUND OR ConnectorFlags.CF_CONNECTOR_CONFIGURED OR ConnectorFlags.CF_CONNECTOR_ERROR);
    END_IF
    _iOldState := _iState;
END_IF

IF _iState = 0 THEN 
    init();
(*ELSIF _iState &gt;0 AND _iState &lt; 10 THEN
    ConfigurePWM();*)
END_IF

//SysShm.SysSharedMemoryRead(_hShmGPIOPWM, 16#4, ADR(udiTestSTatus), 4, ADR(result));

METHOD AfterReadInputs: INT
_xAfterReadInputs := TRUE;
THIS^();
_xAfterReadInputs := FALSE;

IF _iState = 10 THEN
    GetInputs();
END_IF

METHOD BeforeWriteOutputs: INT
IF _iState = 10 THEN
    SetType();
    SetOutputs();
END_IF
THIS^();

METHOD Initialize: UDINT
VAR_INPUT
    wModuleType: UINT;
    dwInstance: UDINT;
    pConnector: pointer;
END_VAR
VAR
    pParam: pointer;
    udiResult: UDINT;
END_VAR
_uiModuleType := wModuleType;
_udiInstance := dwInstance;
_pConnector := pConnector;

_eProcessorType := DetermineProcessorType();
DetermineConstants();

init();

METHOD ConfigurePWM: BOOL
VAR
    udi: UDINT;
    Result: UDINT;
    iCounter: INT;
END_VAR
CASE _iState OF
1: 
    udi := 0;
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMCTL, ADR(udi), 4, ADR(result));// Turn off PWM.
    IF Result = Errors.ERR_OK THEN
        _iState := _iState + 1;
    ELSE
        _iState := 1000;
    END_IF
2:
    SysShm.SysSharedMemoryRead(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result)); 
    IF Result &lt;&gt; Errors.ERR_OK THEN
        _iState := 1000;
        RETURN;
    END_IF
    udi.4 := FALSE;
    udi := udi OR 16#5A00_0000; // Turn off enable flag.
    SysShm.SysSharedMemoryWrite(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        _iState := _iState + 1;
    ELSE
        _iState := 1000;
    END_IF
3:
    SysShm.SysSharedMemoryRead(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        IF NOT udi.7 THEN
            _iState := _iState + 1;     
        END_IF
    ELSE
        _iState := 1000;
    END_IF
4:

    udi := 16#5A000000 OR SHL(DWORD#5, 12); // Configure divider.
    SysShm.SysSharedMemoryWrite(_hShmGPIOCLK, GPIO_CM_PWMDIV, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        _iState := _iState + 1;
    ELSE
        _iState := 1000;
    END_IF
5:
    udi := 16#5A00_0206; // Source=PLLD (500 MHz), 1-stage MASH.
    SysShm.SysSharedMemoryWrite(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        _iState := _iState + 1;
    ELSE
        _iState := 1000;
    END_IF
6:
    udi := 16#5A00_0216; // Enable clock.
    SysShm.SysSharedMemoryWrite(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        _iState := _iState + 1;
    ELSE
        _iState := 1000;
    END_IF
7:
    // Wait for busy flag to turn on.
    SysShm.SysSharedMemoryRead(_hShmGPIOCLK, GPIO_CM_PWMCTL, ADR(udi), 4, ADR(result));
    IF Result = Errors.ERR_OK THEN
        IF udi.7 THEN
            _iState := _iState + 1;
        END_IF
    ELSE
        _iState := 1000;
    END_IF
8:
    udi := 100;
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMRNG1, ADR(udi), 4, ADR(result));
    IF Result &lt;&gt; Errors.ERR_OK THEN
        _iState := 1000;
        RETURN;
    END_IF
    udi := 20; 
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMDAT1, ADR(udi), 4, ADR(result));
    IF Result &lt;&gt; Errors.ERR_OK THEN
        _iState := 1000;
        RETURN;
    END_IF
    udi := 16#81;
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMCTL, ADR(udi), 4, ADR(result));  // Channel 1 M/S mode, no FIFO, PWM mode, enabled.
    IF Result &lt;&gt; Errors.ERR_OK THEN
        _iState := 1000;
        RETURN;
    END_IF
    _iState := _iState + 1;
9:
    aeType[18] := ALT5;
    SetType();

    _iState := 10;
END_CASE

METHOD DetermineConstants: 
IF _eProcessorType = ProcessorType.BCM2708 THEN
    // Raspberry Pi Model B+
    PERI_BASE := BCM2708_PERI_BASE;
ELSE
    // Raspberry Pi 2 Model B
    // Raspberry Pi 3 Model B and all others
    PERI_BASE := BCM2709_PERI_BASE;
END_IF

GPIO_PADS           := (PERI_BASE + 16#00100000);
GPIO_BASE           := (PERI_BASE + 16#00200000);
GPIO_TIMER          := (PERI_BASE + 16#0000B000);

GPIO_PWMBASE        := (PERI_BASE + 16#0020C000);
GPIO_CLKBASE        := (PERI_BASE + 16#00101000);

METHOD DetermineProcessorType: ProcessorType
VAR
    Result: RTS_IEC_RESULT;
    Handle: RTS_IEC_HANDLE;
    fr: FileReader;
    str: string;
    i: INT;
    iLen: INT;
    strType: string;
    x: BOOL;
END_VAR
Handle := SysFileOpen(szFile:='/proc/cpuinfo'  , am:=ACCESS_MODE.AM_READ , pResult:= ADR(Result));
IF Result = CmpErrors.Errors.ERR_OK THEN
    fr(hFile:=Handle );
    WHILE fr.GetLine(ADR(str), SIZEOF(str)) DO
        iLen := Len(Str);
        IF iLen &gt; 10 THEN
            IF LEFT(str, 10) = 'model name' THEN
                i := find(str, ':');
                strType := RIGHT(str, iLen-i-1);
                strType := LEFT(strType, 5);
                EXIT;
            END_IF
        END_IF
    END_WHILE
END_IF
SysFileClose(Handle);


IF strType='ARMv6' THEN
    // Raspberry Pi 1 and Model B+
    DetermineProcessorType := ProcessorType.BCM2708;
ELSE
    // Raspberry Pi 2 Model B
    // Raspberry Pi 3 Model B and all others
    DetermineProcessorType := ProcessorType.BCM2709;
END_IF

METHOD Fb_exit: BOOL
VAR_INPUT
    bInCopyCode: BOOL;
END_VAR
IF _hShmGPIOBASE &lt;&gt; RTS_INVALID_HANDLE THEN
    SysShm.SysSharedMemoryClose(_hShmGPIOBASE);
    _hShmGPIOBASE := RTS_INVALID_HANDLE;
END_IF
(*
IF _hShmGPIOPWM &lt;&gt; RTS_INVALID_HANDLE THEN
    SysShm.SysSharedMemoryClose(_hShmGPIOPWM);
    _hShmGPIOPWM := RTS_INVALID_HANDLE;
END_IF

IF _hShmGPIOCLK &lt;&gt; RTS_INVALID_HANDLE THEN
    SysShm.SysSharedMemoryClose(_hShmGPIOCLK);
    _hShmGPIOCLK := RTS_INVALID_HANDLE;
END_IF*)

METHOD GetInputs: 
VAR
    Result: UDINT;
    i: INT;
END_VAR
SysShm.SysSharedMemoryRead(_hShmGPIOBase, 16#34, ADR(dwInputs), 4, ADR(result));

FOR i:=0 TO 31 DO
    axInputs[i] := (dwInputs AND SHL(UDINT#1, i)) &gt; 0;
END_FOR

METHOD GetType: 
VAR
    Result: UDINT;
    ul: UDINT;
    i: INT;
    j: UDINT;
END_VAR
j := 0;
FOR i:=0 TO 31 DO
    IF (i MOD 10) = 0 THEN
        SysSharedMemoryRead(_hShmGPIOBase, j*4, ADR(ul), 4, ADR(result));
        j := j + 1;
    END_IF

    aeType[i] := UDINT_TO_USINT(SHR(ul, INT_TO_UDINT(i MOD 10) * 3) AND 2#111);
END_FOR

_aeOldType := aeType;

METHOD SetOutputs: 
VAR
    Result: UDINT;
    ulOn: UDINT;
    ulOff: UDINT;
    i: INT;
END_VAR
FOR i:=0 TO 31 DO
    IF _axOldOutputs[i] &lt;&gt; axOutputs[i] AND aeType[i] = GPIOType.O THEN
        IF axOutputs[i] THEN
            ulOn := ulOn OR SHL(UDINT#1, i);
        ELSE
            ulOff := ulOff OR SHL(UDINT#1, i);
        END_IF
    END_IF
END_FOR
IF ulOn &gt; 0 THEN
    SysShm.SysSharedMemoryWrite(_hShmGPIOBase, 16#1C, ADR(ulOn), 4, ADR(result));
END_IF
IF ulOff &gt; 0 THEN
    SysShm.SysSharedMemoryWrite(_hShmGPIOBase, 16#28, ADR(ulOff), 4, ADR(result));
END_IF
_axOldOutputs := axOutputs;

//PWM
(*
IF udiPWMFrequency &lt;&gt; _udiOldPWMFrequency THEN
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMRNG1, ADR(udiPWMFrequency), 4, ADR(result));
    _udiOldPWMFrequency := udiPWMFrequency;
END_IF

IF uiPWMDutyCycle &lt;&gt; _uiOldPWMDutyCycle THEN
    ulOn := uiPWMDutyCycle;
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMDAT1, ADR(ulOn), 4, ADR(result));
    _uiOldPWMDutyCycle := uiPWMDutyCycle; 
    ulOn := 16#81;
    SysShm.SysSharedMemoryWrite(_hShmGPIOPWM, GPIO_PWMCTL, ADR(ulOn), 4, ADR(result));  // Channel 1 M/S mode, no FIFO, PWM mode, enabled.
END_IF*)

METHOD SetType: 
VAR
    Result: UDINT;
    ul: UDINT;
    ulOrig: UDINT;
    i: INT;
    j: UDINT;
    udiOffset: UDINT;
END_VAR
j := 0;
FOR i:=0 TO 31 DO
    IF (i MOD 10) = 0 THEN
        SysSharedMemoryRead(_hShmGPIOBase, j*4, ADR(ul), 4, ADR(result));
        ulOrig := ul;
    END_IF

    IF _aeOldType[i] &lt;&gt; aeType[i] THEN
        IF aeType[i] = GPIOType.I OR aeType[i] = GPIOType.O  THEN
            udiOffset :=  INT_TO_UDINT(i MOD 10) * 3;
            CASE aeType[i] OF
            GPIOType.I:
                ul := ul AND NOT SHL(7, udiOffset); 
            GPIOType.O:
                ul := ul AND NOT SHL(7, udiOffset);
                ul := ul OR SHL(1, udiOffset);
                _axOldOutputs[i] := axInputs[i];
            END_CASE
        END_IF
    END_IF

    IF ((i MOD 10) = 9 OR i=31) THEN
        IF ul &lt;&gt; ulOrig THEN
            SysSharedMemoryWrite(_hShmGPIOBase, j*4, ADR(ul), 4, ADR(result));
        END_IF
        j := j + 1;
    END_IF
END_FOR

_aeOldType := aeType;

METHOD init: BOOL
VAR
    Result1: UDINT;
    Result2: UDINT;
    Result3: UDINT;
    ul: UDINT;
    i: INT;
    strEmpty: string;
END_VAR
IF _hShmGPIOBASE = RTS_INVALID_HANDLE THEN
    ul := BLOCK_SIZE;
    IF _hShmGPIOBASE = RTS_INVALID_HANDLE THEN
        _hShmGPIOBASE := SysSharedMemoryCreate(pszName:=strEmpty , ulPhysicalAddress:=GPIO_BASE , pulSize:= ADR(ul), pResult:=ADR(Result1) );
    END_IF
(*  IF _hShmGPIOPWM = RTS_INVALID_HANDLE THEN
        _hShmGPIOPWM := SysSharedMemoryCreate(pszName:=strEmpty , ulPhysicalAddress:=GPIO_PWMBASE , pulSize:= ADR(ul), pResult:=ADR(Result2) );
    END_IF
    IF _hShmGPIOCLK = RTS_INVALID_HANDLE THEN
        _hShmGPIOCLK := SysSharedMemoryCreate(pszName:=strEmpty , ulPhysicalAddress:=GPIO_CLKBASE , pulSize:= ADR(ul), pResult:=ADR(Result3) );
    END_IF
*)  
    IF Result1 = 0 AND Result2 = 0 AND Result3 = 0 THEN
        init := TRUE;

        GetType();
        GetInputs();
        FOR i:=0 TO 31 DO
            IF aeType[i] = GPIOType.O THEN
                axOutputs[i] := axInputs[i];
            END_IF
        END_FOR
        _axOldOutputs := axOutputs;

        SetOutputs();

        _iState := 10;
    ELSE
        _iState := 1000;
    END_IF
END_IF

FUNCTION_BLOCK IoDrvGPIO EXTENDS IoDrvBase
VAR
    CLASSID_CCmpIoDrvTemplate: DWORD;
END_VAR
VAR
    _IIoDrv: ICmpIoDrv;
    _IIoDrvParameter: ICmpIoDrvParameter;
    _GPIO: GPIO;
    _dwInUse: DWORD;
    _xBackGroundDiagStarted: BOOL;
    _bDeactivated: BOOL;
END_VAR


METHOD FB_Exit: BOOL
VAR_INPUT
    bInCopyCode: BOOL;
END_VAR
FB_Exit_Count := FB_Exit_Count + 1;
FB_Exit := TRUE;

METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL;
    bInCopyCode: BOOL;
END_VAR
VAR
    Result: UDINT;
END_VAR
FB_Init_Count := FB_Init_Count + 1;

m_Info.szDriverName := 'IoDrvGPIO';
m_Info.szVendorName := '3S - Smart Software Solutions';
m_Info.szDeviceName := 'Raspberry GPIOs';
m_Info.wModuleType := 8000;

_IIoDrv := THIS^;
_IIoDrvParameter := THIS^;

m_IBaseItf := THIS^;
m_hInterface := IoMgrRegisterInstance2(CLASSID_CCmpIoDrvTemplate, m_IBaseItf, ADR(Result));
FB_Init := TRUE;

METHOD FB_Reinit: BOOL
FB_Reinit_Count := FB_Reinit_Count + 1;
FB_Reinit := TRUE;

METHOD Initialize: UDINT
VAR_INPUT
    wModuleType: UINT;
    dwInstance: UDINT;
    pConnector: pointer;
END_VAR
Initialize_Count := Initialize_Count + 1;

m_wModuleType := wModuleType;
m_dwInstance := dwInstance;
m_Info.wModuleType := wModuleType;

_GPIO.Initialize(wModuleType, dwInstance, pConnector);

Initialize := Errors.ERR_OK;

METHOD QueryInterface: pointer
VAR_INPUT
    iid: DWORD;
    pResult: pointer;
END_VAR
QueryInterface_Count := QueryInterface_Count + 1;
IF iid = ITFID_ICmpIoDrv THEN
    QueryInterface := ADR(_IIoDrv);
    AddRef();
    IF (pResult &lt;&gt; 0) THEN
        pResult^ := Errors.ERR_OK;
    END_IF
    RETURN; 
ELSIF iid = ITFID_ICmpIoDrvParameter THEN
    QueryInterface := ADR(_IIoDrvParameter);
    AddRef();
    IF (pResult &lt;&gt; 0) THEN
        pResult^ := Errors.ERR_OK;
    END_IF  
    RETURN; 
ELSE
    QueryInterface := SUPER^.QueryInterface(iid, pResult);
END_IF

METHOD IoDrvGetModuleDiagnosis: UDINT
VAR_INPUT
    pConnector: pointer;
END_VAR
IF pConnector = 0 THEN
    //we are called from the background task
    _xBackGroundDiagStarted := TRUE;
END_IF

//Todo: update connectorflags if something changed

IoDrvGetModuleDiagnosis_Count := IoDrvGetModuleDiagnosis_Count + 1;
IoDrvGetModuleDiagnosis := Errors.ERR_NOTIMPLEMENTED;

METHOD IoDrvIdentify: UDINT
VAR_INPUT
    pConnector: pointer;
END_VAR
IoDrvIdentify_Count := IoDrvIdentify_Count + 1;
IoDrvIdentify := Errors.ERR_NOTIMPLEMENTED;

METHOD IoDrvReadInputs: UDINT
VAR_INPUT
    pConnectorMapList: pointer;
    nCount: DINT;
END_VAR
VAR
    i: DINT;
    j: DINT;
    wSize: WORD;
    pbyIecAddress: pointer;
    bySrcValue: BYTE;
    bySrcMask: BYTE;
    wSrcIndex: WORD;
    wDestIndex: WORD;
    pdw: pointer;
END_VAR
IoDrvReadInputs_Count := IoDrvReadInputs_Count + 1;

IF pConnectorMapList = 0 OR nCount = 0 THEN
    IoDrvReadInputs := Errors.ERR_PARAMETER;
    RETURN;
END_IF

FOR i:=0 TO nCount - 1 DO
    IF (pConnectorMapList[i].dwNumOfChannels = 0) THEN
        CONTINUE;
    END_IF
    FOR j:= 0 TO UDINT_TO_UINT(pConnectorMapList[i].dwNumOfChannels) - 1 DO     
        pbyIecAddress := pConnectorMapList[i].pChannelMapList[j].pbyIecAddress;
        wDestIndex := pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset / 8;
        IF (pConnectorMapList[i].pChannelMapList[j].wSize = 1) THEN
            IF _GPIO.axInputs[pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset] THEN
            (*IF (PiFace.byIn AND SHL(BYTE#1, pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset MOD 8)) &lt;&gt; 0 THEN*)
            {IF defined (pou:SysCpuSetBit2)}
                SysCpuSetBit2(ADR(pbyIecAddress[wDestIndex]), pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8);          
            {ELSE}    
                SysCpuSetBit(ADR(pbyIecAddress[wDestIndex]), pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8);           
            {END_IF}    
            ELSE
            {IF defined (pou:SysCpuResetBit2)}
                SysCpuResetBit2(ADR(pbyIecAddress[wDestIndex]), pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8);            
            {ELSE}    
                SysCpuResetBit(ADR(pbyIecAddress[wDestIndex]), pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8);         
            {END_IF}    
            END_IF                  
        ELSIF pConnectorMapList[i].pChannelMapList[j].wSize = 32 THEN
            pdw := pbyIecAddress + wDestIndex;
            pdw^ := _GPIO.dwInputs;
        END_IF
    END_FOR
END_FOR

IoDrvReadInputs := Errors.ERR_OK;

METHOD IoDrvScanModules: UDINT
VAR_INPUT
    pConnector: pointer;
    ppConnectorList: pointer;
    pnCount: pointer;
END_VAR
IoDrvScanModules_Count := IoDrvScanModules_Count + 1;
IoDrvScanModules := Errors.ERR_NOTIMPLEMENTED;

METHOD IoDrvStartBusCycle: UDINT
VAR_INPUT
    pConnector: pointer;
END_VAR
IoDrvStartBusCycle_Count := IoDrvStartBusCycle_Count + 1;

//if background diagnosis is not active call IoDrvGetModuleDiagnosis; normally it will be called by the runtime if DRVPROP_BACKGROUND_GETDIAG is set
IF NOT _xBackGroundDiagStarted THEN
    IoDrvGetModuleDiagnosis(m_pConnector); //DRVPROP_BACKGROUND_GETDIAG not set or runtime version &lt; V3.5.1.0
END_IF

//optional: call IoDrvWatchdogTrigger or set driver property DRVPROP_WATCHDOG in UpdateConfiguration
IoDrvWatchdogTrigger(pConnector);

IF NOT _bDeactivated THEN
    _GPIO.AfterReadInputs();
    _GPIO.BeforeWriteOutputs();
END_IF

IoDrvStartBusCycle := Errors.ERR_OK;

METHOD IoDrvUpdateConfiguration: UDINT
VAR_INPUT
    pConnectorList: pointer;
    nCount: DINT;
END_VAR
VAR
    pParameter: pointer;
    pChild: pointer;
    pstConnectorVendorName: pointer;
    pstConnectorDeviceName: pointer;
    Result: RTS_IEC_RESULT;
    i: INT;
    bySetting: BYTE;
END_VAR
IoDrvUpdateConfiguration_Count := IoDrvUpdateConfiguration_Count + 1;
IoDrvUpdateConfiguration := Errors.ERR_OK;

IF (pConnectorList = 0) THEN
    // Reset application
    // TODO: Free ressources
    RETURN;
END_IF

m_pConnector := IoMgrConfigGetConnector(pConnectorList, ADR(nCount), m_wModuleType, m_dwInstance);

IF m_pConnector = 0 THEN
    IoDrvUpdateConfiguration := ERRORS.ERR_PARAMETER;
    RETURN;
END_IF

//check if device is enabled in the device tree
IF (m_pConnector^.dwFlags AND ConnectorFlags.CF_ENABLE) = 0 THEN
    _bDeactivated := TRUE;
    IoDrvUpdateConfiguration := Errors.ERR_OK;
    RETURN;
END_IF

IF m_pConnector^.hIoDrv = 0 THEN
    m_pConnector^.hIoDrv := m_hInterface;
    m_pConnector^.pFather^.hIoDrv := m_hInterface;

    {IF defined (pou:IoMgrConfigSetDiagnosis)}
    IoMgrConfigSetDiagnosis(m_pConnector,
                            ConnectorFlags.CF_DRIVER_AVAILABLE OR
                            ConnectorFlags.CF_CONNECTOR_FOUND OR
                            ConnectorFlags.CF_CONNECTOR_CONFIGURED OR
                            ConnectorFlags.CF_CONNECTOR_ACTIVE);
    {END_IF}

    {IF defined (pou:IoMgrConfigSetDiagnosis)}
    IoMgrConfigSetDiagnosis(m_pConnector^.pFather,
                            ConnectorFlags.CF_DRIVER_AVAILABLE OR
                            ConnectorFlags.CF_CONNECTOR_FOUND OR
                            ConnectorFlags.CF_CONNECTOR_CONFIGURED OR
                            ConnectorFlags.CF_CONNECTOR_ACTIVE);
    {END_IF}

    {IF defined (pou:IoMgrSetDriverProperties)}         
        IoMgrSetDriverProperties(m_hInterface, DRVPROP_CONSISTENCY OR DRVPROP_BACKGROUND_GETDIAG);
        //Note: background diagnosis property flag is optional (supported with 3.5.1.0 runtime)
    {END_IF}                                        

    _dwInUse := 0;
    FOR i:=0 TO 31 DO
        pParameter := IoMgrConfigGetParameter(m_pConnector, INT_TO_DWORD(i));       
        IF (pParameter &lt;&gt; 0) THEN
            bySetting := IoMgrConfigGetParameterValueByte(pParameter, ADR(Result));
            IF bySetting &gt;= 0 AND bySetting &lt;= 7 THEN
                _GPIO.aeType[i] := bySetting;
                _dwInUse := _dwInUse OR SHL(DWORD#1, i);
            END_IF
        END_IF
    END_FOR 

    //Setup I/O area
    pParameter := IoMgrConfigGetParameter(m_pConnector, 1000);      (* inputs *)
    IF (pParameter &lt;&gt; 0) THEN
        pParameter^.dwDriverSpecific := 0;                  (* Device offset 0 *)
    END_IF
    pParameter := IoMgrConfigGetParameter(m_pConnector, 2000);      (* outputs *)
    IF (pParameter &lt;&gt; 0) THEN
        pParameter^.dwDriverSpecific := 0;                  (* Device offset 0 *)
    END_IF
    pParameter := IoMgrConfigGetParameter(m_pConnector, 2100);      (* outputs *)
    IF (pParameter &lt;&gt; 0) THEN
        pParameter^.dwDriverSpecific := 10;                 (* Device offset 0 *)
    END_IF
    pParameter := IoMgrConfigGetParameter(m_pConnector, 2101);      (* outputs *)
    IF (pParameter &lt;&gt; 0) THEN
        pParameter^.dwDriverSpecific := 14;                 (* Device offset 0 *)
    END_IF

    (* no child devices used 
    //Go through all childs of the device
    pChild := IoMgrConfigGetFirstChild(m_pConnector, ADR(nCount), m_pConnector);
    WHILE (pChild &lt;&gt; 0) DO
        IF (pChild^.dwFlags AND ConnectorFlags.CF_ENABLE) &lt;&gt; 0 THEN
            pChild^.hIoDrv := m_hInterface; 
            {IF defined (pou:IoMgrConfigSetDiagnosis)}
            IoMgrConfigSetDiagnosis(pChild,
                                    ConnectorFlags.CF_DRIVER_AVAILABLE OR
                                    ConnectorFlags.CF_CONNECTOR_FOUND OR
                                    ConnectorFlags.CF_CONNECTOR_CONFIGURED OR
                                    ConnectorFlags.CF_CONNECTOR_ACTIVE);
            {END_IF}
        END_IF
        pChild := IoMgrConfigGetNextChild(pChild, ADR(nCount), m_pConnector);
    END_WHILE*)
END_IF

METHOD IoDrvUpdateMapping: UDINT
VAR_INPUT
    pTaskMapList: pointer;
    nCount: DINT;
END_VAR
IoDrvUpdateMapping_Count := IoDrvUpdateMapping_Count + 1;

IF (pTaskMapList = 0) THEN
    IoDrvUpdateMapping := Errors.ERR_PARAMETER;
    RETURN;
END_IF

IoDrvUpdateMapping := Errors.ERR_OK;

METHOD IoDrvWatchdogTrigger: UDINT
VAR_INPUT
    pConnector: pointer;
END_VAR
IoDrvWatchdogTrigger_Count := IoDrvWatchdogTrigger_Count + 1;
IoDrvWatchdogTrigger := Errors.ERR_OK;

METHOD IoDrvWriteOutputs: UDINT
VAR_INPUT
    pConnectorMapList: pointer;
    nCount: DINT;
END_VAR
VAR
    i: DINT;
    j: DINT;
    k: UINT;
    wSize: WORD;
    pbyIecAddress: pointer;
    bySrcValue: BYTE;
    wSrcIndex: WORD;
    bySrcMask: BYTE;
    wDestIndex: WORD;
    pdw: pointer;
    pw: pointer;
END_VAR
IoDrvWriteOutputs_Count := IoDrvWriteOutputs_Count + 1;

IF pConnectorMapList = 0 OR nCount = 0 THEN
    IoDrvWriteOutputs := Errors.ERR_PARAMETER;
    RETURN;
END_IF

FOR i:=0 TO nCount - 1 DO
    IF (pConnectorMapList[i].dwNumOfChannels = 0) THEN
        CONTINUE;
    END_IF
    FOR j:= 0 TO UDINT_TO_UINT(pConnectorMapList[i].dwNumOfChannels) - 1 DO
        wSrcIndex := pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset / 8; 
        pbyIecAddress := pConnectorMapList[i].pChannelMapList[j].pbyIecAddress;         

        CASE pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific OF
        0:  // digitals
            IF pConnectorMapList[i].pChannelMapList[j].wSize = 1 THEN
                bySrcValue := pbyIecAddress[wSrcIndex];
                k := pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset;
                IF (_dwInUse AND SHL(DWORD#1, k)) &gt; 0 THEN 
                    _GPIO.axOutputs[k] := BYTE_TO_BOOL(pbyIecAddress[wSrcIndex] AND SHL(BYTE#1, pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8));
                END_IF
            ELSIF pConnectorMapList[i].pChannelMapList[j].wSize = 32 THEN
                pdw := pbyIecAddress;
                FOR k:=0 TO 32 DO
                    IF (_dwInUse AND SHL(DWORD#1, k)) &gt; 0 THEN 
                        _GPIO.axOutputs[k] := DWORD_TO_BOOL(pdw^ AND SHL(1, k));
                    END_IF
                END_FOR
            END_IF
        10:
            pdw := pbyIecAddress;
            _GPIO.udiPWMFrequency := DWORD#100_000_000 / pdw^; 
        14:
            pw := pbyIecAddress;
            _GPIO.uiPWMDutyCycle := UDINT_TO_UINT(UINT_TO_UDINT(pw^) * _GPIO.udiPWMFrequency / UDINT#65536); 
        END_CASE
    END_FOR
END_FOR

IoDrvWriteOutputs := Errors.ERR_OK;

METHOD IoDrvGetConnector: pointer
VAR_INPUT
    pResult: pointer;
END_VAR
IF m_pConnector = 0 THEN
    IF pResult &lt;&gt; 0 THEN
        pResult^ := Errors.ERR_FAILED;
    END_IF
    IoDrvGetConnector := 0; 
    RETURN;
END_IF
IF pResult &lt;&gt; 0 THEN
    pResult^ := Errors.ERR_OK;
END_IF
IoDrvGetConnector := m_pConnector;

METHOD IoDrvReadParameter: UDINT
VAR_INPUT
    pConnector: pointer;
    pParameter: pointer;
    pData: pointer;
    dwBitSize: DWORD;
    dwBitOffset: DWORD;
END_VAR
IF  pConnector = 0 OR pParameter = 0 OR pData = 0 THEN
    IoDrvReadParameter := Errors.ERR_PARAMETER;
    RETURN; 
END_IF

// All standard parameters of our device are handled by the IO-manager!
IoDrvReadParameter := Errors.ERR_FAILED;

METHOD IoDrvWriteParameter: UDINT
VAR_INPUT
    pConnector: pointer;
    pParameter: pointer;
    pData: pointer;
    dwBitSize: DWORD;
    dwBitOffset: DWORD;
END_VAR
VAR
    pParam: pointer;
    pstDiagString: pointer;
END_VAR
IF pConnector = 0 OR pParameter = 0 OR pData = 0 THEN
    IoDrvWriteParameter := Errors.ERR_PARAMETER;
    RETURN; 
END_IF

IoDrvWriteParameter := Errors.ERR_FAILED;