[r2]: / branches / 2.0.0.1 / +CTRLBYTE / Monarco HAT library for codesys 2.0.0.1.library.md  Maximize  Restore  History

Download this file

1266 lines (1125 with data), 31.9 kB

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

FUNCTION_BLOCK IoDrvMonarco EXTENDS IoDrvBase
VAR
    _IIoDrv: ICmpIoDrv;
    _IIoDrvParameter: ICmpIoDrvParameter;
    Monarco: MonarcoItf;
    _dwInUse: DWORD;
    _xBackGroundDiagStarted: BOOL;
    _bDeactivated: BOOL;
END_VAR


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

m_Info.szDriverName := 'IoDrvMonarco';
m_Info.szVendorName := 'Monarcio.IO';
m_Info.szDeviceName := 'Monarco HAT';
m_Info.wModuleType := 501;

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

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

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

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

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 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;

Monarco.Initialize(wModuleType, dwInstance, pConnector);

Initialize := Errors.ERR_OK;

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;

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 IoDrvIdentify: UDINT
VAR_INPUT
    pConnector: pointer;
END_VAR
IoDrvIdentify_Count := IoDrvIdentify_Count + 1;
IoDrvIdentify := Errors.ERR_NOTIMPLEMENTED;

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

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 IoDrvWriteOutputs: UDINT
VAR_INPUT
    pConnectorMapList: pointer;
    nCount: DINT;
END_VAR
VAR
    i: DINT;
    j: DINT;
    wSize: WORD;
    pbyIecAddress: pointer;
    bySrcValue: BYTE;
    wSrcIndex: WORD;
    bySrcMask: BYTE;
    wDestIndex: WORD;
    ptr: pointer;
    ptrW: pointer;
    ptrB: 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;         

        IF (pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId &gt;= 2000 AND pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId&lt;=2002)
            OR (pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId &gt;= 9010 AND pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId&lt;=9016)
            OR (pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId &gt;= 9030 AND pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId&lt;=9031) THEN
            IF pConnectorMapList[i].pChannelMapList[j].wSize = 1 THEN
                bySrcValue := pbyIecAddress[wSrcIndex];

                IF (pbyIecAddress[wSrcIndex] AND SHL(BYTE#1, pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset MOD 8)) &lt;&gt; 0 THEN
                {IF defined (pou:SysCpuSetBit2)}
                    SysCpuSetBit2(pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific, pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset MOD 8);         
                {ELSE}    
                    SysCpuSetBit(pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific, pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset MOD 8);          
                {END_IF}    
                ELSE
                {IF defined (pou:SysCpuResetBit2)}
                    SysCpuResetBit2(pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific, pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset MOD 8);               
                {ELSE}    
                    SysCpuResetBit(pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific, pConnectorMapList[i].pChannelMapList[j].wParameterBitOffset MOD 8);                
                {END_IF}    
                END_IF          
            ELSIF pConnectorMapList[i].pChannelMapList[j].wSize = 8 THEN
                ptr:= pConnectorMapList[i].pChannelMapList[j].pParameter^.dwDriverSpecific;
                ptr^ := pbyIecAddress[wSrcIndex];
            END_IF
        ELSIF pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId = 2100 THEN
            IF pConnectorMapList[i].pChannelMapList[j].wSize = 16 THEN
                ptrW:= pbyIecAddress;   
                Monarco.wAOut1 := ptrW^;
            END_IF
        ELSIF pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId = 2101 THEN
            IF pConnectorMapList[i].pChannelMapList[j].wSize = 16 THEN
                ptrW:= pbyIecAddress;   
                Monarco.wAOut2 := ptrW^;
            END_IF
        END_IF
    END_FOR
END_FOR

IoDrvWriteOutputs := Errors.ERR_OK;

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 IoDrvUpdateConfiguration: UDINT
VAR_INPUT
    pConnectorList: pointer;
    nCount: DINT;
END_VAR
VAR
    pParameter: pointer;
    pChild: pointer;
    pstConnectorVendorName: pointer;
    pstConnectorDeviceName: pointer;
    dwTest: pointer;
    Result: RTS_IEC_RESULT;
END_VAR
IoDrvUpdateConfiguration_Count := IoDrvUpdateConfiguration_Count + 1;
IoDrvUpdateConfiguration := Errors.ERR_OK;

IF (pConnectorList = 0) THEN
    // Reset application
    // TODO: Free resources
    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}                        

    //Setup I/O area
    pParameter := IoMgrConfigGetParameter(m_pConnector, 2000);      (* outputs *)
    IF (pParameter &lt;&gt; 0) THEN
        pParameter^.dwDriverSpecific := ADR(Monarco.byDOut);                    
    END_IF


    //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 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
    Monarco.AfterReadInputs();
    Monarco.BeforeWriteOutputs();
END_IF

IoDrvStartBusCycle := 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 IoDrvReadInputs: UDINT
VAR_INPUT
    pConnectorMapList: pointer;
    nCount: DINT;
END_VAR
VAR
    i: DINT;
    j: DINT;
    wSize: WORD;
    pbyIecAddress: pointer;
    pwIecAddress: pointer;
    prIecAddress: pointer;
    prPiXtendSrc: pointer;
    bySrcValue: BYTE;
    bySrcMask: BYTE;
    wSrcIndex: WORD;
    wDestIndex: WORD;
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     
        IF pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId =1000 THEN

            pbyIecAddress := pConnectorMapList[i].pChannelMapList[j].pbyIecAddress;
            wDestIndex := pConnectorMapList[i].pChannelMapList[j].wIecAddressBitOffset / 8;

            IF (pConnectorMapList[i].pChannelMapList[j].wSize = 1) THEN
                IF (Monarco.byDIn 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 = 8 THEN
                pbyIecAddress[wDestIndex] := Monarco.byDIn;
            END_IF

        ELSIF pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId = 1110  THEN
            pwIecAddress := pConnectorMapList[i].pChannelMapList[j].pbyIecAddress;
            pwIecAddress^:= Monarco.wAIn1;
        ELSIF pConnectorMapList[i].pChannelMapList[j].pParameter^.dwParameterId = 1111  THEN
            pwIecAddress := pConnectorMapList[i].pChannelMapList[j].pbyIecAddress;
            pwIecAddress^:= Monarco.wAIn2;      
        END_IF      
    END_FOR
END_FOR

IoDrvReadInputs := Errors.ERR_OK;

FUNCTION_BLOCK MonarcoItf EXTENDS SPI
VAR_INPUT
    byDOut: BYTE;
    wAOut1: WORD;
    wAOut2: WORD;
END_VAR
VAR_OUTPUT
    byDIn: BYTE;
    wAIn1: WORD;
    wAIn2: WORD;
END_VAR
VAR
    _xInit: BOOL;
    stHwConfig: stHWConfig1;
    stAOut: stAOut;
    stDOut: stDOut;
    byLeds: BYTE;
    udiPWM1freq: UDINT;
    rDutyCycleDOut1: REAL;
    rDutyCycleDOut2: REAL;
    rDutyCycleDOut3: REAL;
    udiPWM2freq: UDINT;
    rDutyCycleDOut4: REAL;
    xOperational: BOOL;
    stDIn: stDIn;
    stAIn: stAIn;
    stCounter1Config: stCounter1Config;
    stCounter2Config: stCounter2Config;
    stPWM1: stPWM1;
    stPWM2: stPWM2;
    stHATinfo: stHATinfo;
    eComStatus: eMonarcoComStatus;
    _uiNumberOfDevices: INT;
    _stControlByte: stControlByte;
    _ControlByte8: BYTE;
    _uiWatchDog: UINT;
    _uiLastWatchDog: UINT;
    _stRS485Mode: stRS485Mode;
    _stHWConfig1: stHWConfig1;
    _stLastCounter1Cfg: stCounter1Config;
    _stLastCounter2Cfg: stCounter2Config;
    _AfterReadInputs: UINT;
    _BeforeWriteOutputs: UINT;
    _abyIntTxBuf: ARRAY [..] OF ;
    _abyIntRxBuf: ARRAY [..] OF ;
END_VAR
//********************************
//  base SPI implementation 
//********************************
SUPER^();

//********************************
//      get SPI State 
//********************************
CASE _iState OF
0:
    IF SUPER^.init() THEN
        // get only once
        THIS^.getHATinfo();
        _iState := 1;
    END_IF  

1:  _iState := 10;
END_CASE

//********************************
//  is device operational 
//********************************
IF _iState = 10 THEN
    xOperational        :=  TRUE;
ELSE
    xOperational        :=  FALSE;
END_IF

METHOD getHATinfo: 
VAR
    wValue: WORD;
    eCmd: eServiceCommand;
    eComStatus: eMonarcoComStatus;
    J: INT;
    wReqAdr: WORD;
    awSrvAnwser: ARRAY [..] OF ;
    cpu_l: DWORD;
    cpu_h: DWORD;
    lwcpu: LWORD;
END_VAR
//********************************
//Read FW, HW revision and CPU-ID 
//********************************
FOR J := 1 TO 9 DO
    CASE J OF
        1:      eCmd := eServiceCommand.SDC_FIXED_STATUSWORD; //bogus question (ABCD)
        2:      eCmd := eServiceCommand.SDC_FIXED_FWVERL;
        3:      eCmd := eServiceCommand.SDC_FIXED_FWVERH;
        4:      eCmd := eServiceCommand.SDC_FIXED_HWVERL;
        5:      eCmd := eServiceCommand.SDC_FIXED_HWVERH;
        6:      eCmd := eServiceCommand.SDC_FIXED_CPUID1;
        7:      eCmd := eServiceCommand.SDC_FIXED_CPUID2;
        8:      eCmd := eServiceCommand.SDC_FIXED_CPUID3;
        9:      eCmd := eServiceCommand.SDC_FIXED_CPUID4;
    END_CASE
    wValue              := TO_WORD(eServiceCommand.SDC_FIXED_STATUSWORD);
    eComStatus          := THIS^.ServiceRequest( wValue := wValue, eCommand := eCmd, xWrite := FALSE );
    // Get result
    eComStatus          := THIS^.dataTransfer();
    awSrvAnwser[J]      := MEM.PackBytesToWord(_abyIntRxBuf[1], _abyIntRxBuf[0]);
END_FOR

// Store anwsers in status
THIS^.stHATinfo.dwFWVersion := MEM.PackWordsToDword(awSrvAnwser[3],awSrvAnwser[2]);
THIS^.stHATinfo.dwHWversion := MEM.PackWordsToDword(awSrvAnwser[5],awSrvAnwser[4]);
cpu_l                       := MEM.PackWordsToDword(awSrvAnwser[7],awSrvAnwser[6]);
cpu_h                       := MEM.PackWordsToDword(awSrvAnwser[9],awSrvAnwser[8]);
lwcpu                       := cpu_h;
lwcpu                       := SHL(lwcpu,32);
THIS^.stHATinfo.lwCpuID     := lwcpu OR cpu_l;

METHOD getCRC: WORD
VAR_INPUT
    abyBuf: ARRAY [..] OF ;
END_VAR
VAR
    uiSizeTx: UINT;
    udiCRCLen: UDINT;
    pByteTx: pointer;
    CheckSum: WORD;
END_VAR
// startadress of bytes
pByteTx                             := ADR(abyBuf); 

// calculate CRC-16 checksum, beware that for the checksum calculation, we need to skip the checksum itself
uiSizeTx                            := SIZEOF(abyBuf);

// should always be 24 / 16#18
udiCRCLen                           := (uiSizeTx -16#02);   

CheckSum                            := MEM.CRC16_Modbus( pByteTx, TO_UINT(udiCRCLen) );

// return checksum
getCRC                              := CheckSum;

METHOD dataTransfer: eMonarcoComStatus
VAR
    tmpRxBuf: ARRAY [..] OF ;
    udiBufSize: UDINT;
    xDone: BOOL;
    CRC: WORD;
END_VAR
//init temporay buffer before filling;
//flush(tmpRxBuf);

// determine size of buffer
udiBufSize                      := SIZEOF( THIS^._abyIntTxBuf );

(* Sets CRC16 checksum, for Tx byte array [24] and [25]*)
CRC                             := THIS^.getCRC( THIS^._abyIntTxBuf );
THIS^._abyIntTxBuf[24]          := WORD_TO_BYTE(CRC);
THIS^._abyIntTxBuf[25]          := WORD_TO_BYTE(SHR(CRC,8));

// Send, Recv
xDone := THIS^.transferExt( pabyTxBuffer := ADR( THIS^._abyIntTxBuf ), 
                         pabyRxBuffer := ADR( tmpRxBuf ),       // write result into a temporary buffer for post-analysis. Beware that the received answser is from the last question, not the current question.
                         udiLen       := udiBufSize, 
                         uiDelayus    := 5,
                         udispeedHz   := 0); 

IF NOT xDone THEN 
    // Send, Recv failed
    DataTransfer := eMonarcoComStatus.ERROR;
ELSE
    // OK?
    IF THIS^.checkCRC(tmpRxBuf) THEN
        //CRC okay, return the recv buffer
        THIS^._abyIntRxBuf  := tmpRxBuf;
        DataTransfer        := eMonarcoComStatus.SUCCES;
    ELSE
        //CRC not okay, return an empty buffer
        flush(tmpRxBuf);
        THIS^._abyIntRxBuf  := tmpRxBuf;
        DataTransfer        := eMonarcoComStatus.CrcFail;
    END_IF
END_IF;

METHOD flush: BOOL
VAR_INPUT
    abyRxBuf: ARRAY [..] OF ;
END_VAR
VAR
    i: UINT;
END_VAR
//flush the temporary receive buffer before filling;
FOR i :=0 TO (ParamList.MONARCO_STRUCT_SIZE-1) DO
    abyRxBuf[i] := 16#00;
END_FOR
flush := TRUE;

METHOD checkCRC: BOOL
VAR_INPUT
    abyRxBuf: ARRAY [..] OF ;
END_VAR
VAR
    CRC: WORD;
    CRCLo: BYTE;
    CRCHi: BYTE;
END_VAR
CRC             := THIS^.GetCRC(abyRxBuf);
CRCHi           := WORD_TO_BYTE(CRC);
CRCLo           := WORD_TO_BYTE(SHR(CRC,8));

// check to self calculated CRC against the sent CRC, Equal = OK
IF (CRCHi = abyRxBuf[ParamList.MONARCO_STRUCT_SIZE - 2]) AND (CRCLo = abyRxBuf[ParamList.MONARCO_STRUCT_SIZE - 1]) THEN 
    checkCRC := TRUE;
ELSE
    checkCRC := FALSE;
END_IF;

METHOD setDutyCycleDOut4: eMonarcoComStatus
VAR_INPUT
    rDutyCycle: REAL;
END_VAR
VAR
    wDC: WORD;
END_VAR
wDC := getDutyCycle( rDutyCycle := rDutyCycle );

//   18.0   2.0  PWM2 Dutycycle ch A
THIS^._abyIntTxBuf[18]              := WORD_TO_BYTE(wDC);   // bitmask to protect prescaler
THIS^._abyIntTxBuf[19]              := WORD_TO_BYTE(SHR(wDC,8));

METHOD setDutyCycleDOut2: eMonarcoComStatus
VAR_INPUT
    rDutyCycle: REAL;
END_VAR
VAR
    wDC: WORD;
END_VAR
wDC := getDutyCycle( rDutyCycle := rDutyCycle );

//   12.0   2.0  PWM1 Dutycycle ch B
THIS^._abyIntTxBuf[12]              := WORD_TO_BYTE(wDC);   // bitmask to protect prescaler
THIS^._abyIntTxBuf[13]              := WORD_TO_BYTE(SHR(wDC,8));

METHOD setPWM1freq: eMonarcoComStatus
VAR_INPUT
    udiHz: UDINT;
END_VAR
VAR
    _uidiHz: UDINT;
    ePrescale: ePrescaler;
    uiRangeLow: UINT;
    uiRangeHigh: UINT;
    uiTOP: UINT;
    fPWM: WORD;
END_VAR
/// clip the input Hz on lower and upper bound, so;
_uidiHz := LIMIT( 1, udiHz, 100000);

/// Determine prescaler and TOP according to table
CASE _uidiHz OF
    1..9            : ePrescale     := ePrescaler.ScaleFactor512;
                      uiTOP         := TO_UINT( (32000000/ 512/ _uidiHz));// interpolate between 1 and 9Hz

    10..99          : ePrescale     := ePrescaler.ScaleFactor64;
                      uiTOP         := TO_UINT( (32000000/ 64/ _uidiHz)); // interpolate between 10 and 100Hz

    100..999        : ePrescale     := ePrescaler.ScaleFactor8;
                      uiTOP         := TO_UINT( (32000000/ 8/ _uidiHz));// interpolate between 100 and 1000Hz

    1000..100000    : ePrescale     := ePrescaler.ScaleFactor1;
                      uiTOP         := TO_UINT( (32000000/ 1/ _uidiHz));// interpolate between 1000 and 100000Hz
END_CASE

/// Now set prescaler
CASE ePrescale OF 
///  00  0 : prescaler =   1
    ePrescaler.ScaleFactor1 :   fPWM.0 := FALSE;
                                fPWM.1 := FALSE;
///  01  1 : prescaler =   8
    ePrescaler.ScaleFactor8 :   fPWM.0 := TRUE;
                                fPWM.1 := FALSE;
///  10  2 : prescaler =  64    
    ePrescaler.ScaleFactor64 :  fPWM.0 := FALSE;
                                fPWM.1 := TRUE;
///  11  3 : prescaler = 512    
    ePrescaler.ScaleFactor512 : fPWM.0 := TRUE;
                                fPWM.1 := TRUE;
END_CASE

//   8.0   2.0  PWM1 frequency
THIS^._abyIntTxBuf[8]           := WORD_TO_BYTE(fPWM) AND 16#FC;    // bitmask to protect prescaler
THIS^._abyIntTxBuf[9]           := WORD_TO_BYTE(SHR(fPWM,8));

METHOD setDutyCycleDOut3: eMonarcoComStatus
VAR_INPUT
    rDutyCycle: REAL;
END_VAR
VAR
    wDC: WORD;
END_VAR
wDC := getDutyCycle( rDutyCycle := rDutyCycle );

//   12.0   2.0  PWM1 Dutycycle ch C
THIS^._abyIntTxBuf[14]              := WORD_TO_BYTE(wDC);   // bitmask to protect prescaler
THIS^._abyIntTxBuf[15]              := WORD_TO_BYTE(SHR(wDC,8));

METHOD setDutyCycleDOut1: eMonarcoComStatus
VAR_INPUT
    rDutyCycle: REAL;
END_VAR
VAR
    wDC: WORD;
END_VAR
wDC := getDutyCycle( rDutyCycle := rDutyCycle );

//   10.0   2.0  PWM1 Dutycycle ch A
THIS^._abyIntTxBuf[10]              := WORD_TO_BYTE(wDC);   // bitmask to protect prescaler
THIS^._abyIntTxBuf[11]              := WORD_TO_BYTE(SHR(wDC,8));

METHOD setAOutWord: eMonarcoComStatus
VAR_INPUT
    wAOut1: WORD;
    wAOut2: WORD;
END_VAR
VAR
    v1: WORD;
    v2: WORD;
END_VAR
// limit the value's
v1 := LIMIT(0, wAOut1, Paramlist.MONARCO_ADC_RANGE_MAX );
v2 := LIMIT(0, wAOut2, Paramlist.MONARCO_ADC_RANGE_MAX );

THIS^._abyIntTxBuf[20]          := WORD_TO_BYTE(v1);
THIS^._abyIntTxBuf[21]          := WORD_TO_BYTE(SHR(v1,8));
THIS^._abyIntTxBuf[22]          := WORD_TO_BYTE(v2);       
THIS^._abyIntTxBuf[23]          := WORD_TO_BYTE(SHR(v2,8));

METHOD setAOut: eMonarcoComStatus
VAR_INPUT
    stAOut: stAOut;
END_VAR
VAR
    v1: WORD;
    v2: WORD;
END_VAR
// limit the value's
v1 := LIMIT(0, stAOut.wAOut1, Paramlist.MONARCO_ADC_RANGE_MAX );
v2 := LIMIT(0, stAOut.wAOut2, Paramlist.MONARCO_ADC_RANGE_MAX );

THIS^._abyIntTxBuf[20]          := WORD_TO_BYTE(v1);
THIS^._abyIntTxBuf[21]          := WORD_TO_BYTE(SHR(v1,8));
THIS^._abyIntTxBuf[22]          := WORD_TO_BYTE(v2);       
THIS^._abyIntTxBuf[23]          := WORD_TO_BYTE(SHR(v2,8));

METHOD setPWM2freq: eMonarcoComStatus
VAR_INPUT
    udiHz: UDINT;
END_VAR
VAR
    _uidiHz: UDINT;
    ePrescale: ePrescaler;
    uiRangeLow: UINT;
    uiRangeHigh: UINT;
    uiTOP: UINT;
    fPWM: WORD;
END_VAR
/// clip the input Hz on lower and upper bound;
_uidiHz := LIMIT( 1, udiHz, 100000);

//// Determine prescaler and TOP according to table
CASE _uidiHz OF
    1..9            : ePrescale     := ePrescaler.ScaleFactor512;
                      uiTOP         := TO_UINT( (32000000/ 512/ _uidiHz));// interpolate between 1 and 9Hz

    10..99          : ePrescale     := ePrescaler.ScaleFactor64;
                      uiTOP         := TO_UINT( (32000000/ 64/ _uidiHz)); // interpolate between 10 and 100Hz

    100..999        : ePrescale     := ePrescaler.ScaleFactor8;
                      uiTOP         := TO_UINT( (32000000/ 8/ _uidiHz));// interpolate between 100 and 1000Hz

    1000..100000    : ePrescale     := ePrescaler.ScaleFactor1;
                      uiTOP         := TO_UINT( (32000000/ 1/ _uidiHz));// interpolate between 1000 and 100000Hz
END_CASE

/// Now set prescaler
/// bit 15 ..  2 : TOP / 4
CASE ePrescale OF 
///  00  0 : prescaler =   1
    ePrescaler.ScaleFactor1 :   fPWM.0 := FALSE;
                                fPWM.1 := FALSE;
///  01  1 : prescaler =   8
    ePrescaler.ScaleFactor8 :   fPWM.0 := TRUE;
                                fPWM.1 := FALSE;
///  10  2 : prescaler =  64    
    ePrescaler.ScaleFactor64 :  fPWM.0 := FALSE;
                                fPWM.1 := TRUE;
///  11  3 : prescaler = 512    
    ePrescaler.ScaleFactor512 : fPWM.0 := TRUE;
                                fPWM.1 := TRUE;
END_CASE

//   8.0   2.0  PWM1 frequency
THIS^._abyIntTxBuf[16]          := WORD_TO_BYTE(fPWM);
THIS^._abyIntTxBuf[17]          := WORD_TO_BYTE(SHR(fPWM,8));

METHOD getServiceResponse: eMonarcoComStatus
VAR_OUTPUT
    Response: stServiceResponse;
END_VAR
VAR
    rsp: stServiceResponse;
END_VAR
//send/receive           
getServiceResponse          := THIS^.dataTransfer();

rsp.wServiceValue           := MEM.PackBytesToWord(_abyIntRxBuf[1],_abyIntRxBuf[0]);
rsp.wServiceAddress         := MEM.PackBytesToWord(_abyIntRxBuf[3],_abyIntRxBuf[2]);

CASE rsp.wServiceValue OF
    /// Set the error bit
    Paramlist.MONARCO_SERVICE_STATUS_OK         :   Response.xError     := FALSE;                       
    Paramlist.MONARCO_SERVICE_ERROR_UNKNOWN_REG :   Response.xError     := TRUE;
    Paramlist.MONARCO_SERVICE_ERROR_CRC         :   Response.xError     := TRUE;                                        
END_CASE

/// just give the response back
Response.wServiceValue      := MEM.PackBytesToWord(_abyIntRxBuf[1],_abyIntRxBuf[0]);
Response.wServiceAddress    := MEM.PackBytesToWord(_abyIntRxBuf[3],_abyIntRxBuf[2]);

METHOD setDOut: 
VAR_INPUT
    stDOut: stDOut;
END_VAR
THIS^._abyIntTxBuf[7].0 := stDOut.xDOut1;
THIS^._abyIntTxBuf[7].1 := stDOut.xDOut2;
THIS^._abyIntTxBuf[7].2 := stDOut.xDOut3;
THIS^._abyIntTxBuf[7].3 := stDOut.xDOut4;

METHOD setDOutBool: 
VAR_INPUT
    xDOut0: BOOL;
    xDOut1: BOOL;
    xDOut2: BOOL;
    xDOut3: BOOL;
END_VAR
THIS^._abyIntTxBuf[7].0 := xDOut0;
THIS^._abyIntTxBuf[7].1 := xDOut1;
THIS^._abyIntTxBuf[7].2 := xDOut2;
THIS^._abyIntTxBuf[7].3 := xDOut3;

METHOD setDOutByte: 
VAR_INPUT
    byDOut: BYTE;
END_VAR
THIS^._abyIntTxBuf[7].0 := byDOut.0;
THIS^._abyIntTxBuf[7].1 := byDOut.1;
THIS^._abyIntTxBuf[7].2 := byDOut.2;
THIS^._abyIntTxBuf[7].3 := byDOut.3;

METHOD getDInByte: eMonarcoComStatus
VAR_OUTPUT
    byDIn: BYTE;
END_VAR
byDIn.0                 := THIS^._abyIntRxBuf[7].0;
byDIn.1                 := THIS^._abyIntRxBuf[7].1;
byDIn.2                 := THIS^._abyIntRxBuf[7].2;
byDIn.3                 := THIS^._abyIntRxBuf[7].3;

METHOD getDInBool: eMonarcoComStatus
VAR_OUTPUT
    xDIn0: BOOL;
    xDIn1: BOOL;
    xDIn2: BOOL;
    xDIn3: BOOL;
END_VAR
xDIn0               := THIS^._abyIntRxBuf[7].0;
xDIn1               := THIS^._abyIntRxBuf[7].1;
xDIn2               := THIS^._abyIntRxBuf[7].2;
xDIn3               := THIS^._abyIntRxBuf[7].3;

METHOD getDIn: eMonarcoComStatus
VAR_OUTPUT
    stDIn: stDIn;
END_VAR
stDIn.xDIn1             := THIS^._abyIntRxBuf[7].0;
stDIn.xDIn2             := THIS^._abyIntRxBuf[7].1;
stDIn.xDIn3             := THIS^._abyIntRxBuf[7].2;
stDIn.xDIn4             := THIS^._abyIntRxBuf[7].3;

METHOD getAInWord: eMonarcoComStatus
VAR_OUTPUT
    wAIn1: WORD;
    wAIn2: WORD;
END_VAR
wAIn1               := MEM.PackBytesToWord(THIS^._abyIntRxBuf[21],THIS^._abyIntRxBuf[20]);
wAIn2               := MEM.PackBytesToWord(THIS^._abyIntRxBuf[23],THIS^._abyIntRxBuf[22]);

METHOD ServiceRequest: eMonarcoComStatus
VAR_INPUT
    wValue: WORD;
    eCommand: eServiceCommand;
    xWrite: BOOL;
END_VAR
VAR
    stRequest: stServiceRequest;
END_VAR
stRequest.wServiceValue         := wValue;
stRequest.wServiceAddress       := Paramlist.SDC_ADDRESS_MASK AND TO_WORD(eCommand);
stRequest.wServiceAddress.12    := xWrite;

// Service Data Request         
THIS^._abyIntTxBuf[0]           := WORD_TO_BYTE(stRequest.wServiceValue); // ServiceValueHi;
THIS^._abyIntTxBuf[1]           := WORD_TO_BYTE(SHR(stRequest.wServiceValue,8)); // ServiceValueLo;
THIS^._abyIntTxBuf[2]           := WORD_TO_BYTE(stRequest.wServiceAddress);//ServiceRegisterHi;
THIS^._abyIntTxBuf[3]           := WORD_TO_BYTE(SHR(stRequest.wServiceAddress,8));//ServiceRegisterLo;

//send/receive                  
ServiceRequest                  := THIS^.dataTransfer();

METHOD getAIn: eMonarcoComStatus
VAR_OUTPUT
    stAIn: stAIn;
END_VAR
stAIn.wAIn1             := MEM.PackBytesToWord(THIS^._abyIntRxBuf[21],THIS^._abyIntRxBuf[20]);
stAIn.wAIn2             := MEM.PackBytesToWord(THIS^._abyIntRxBuf[23],THIS^._abyIntRxBuf[22]);

METHOD SetUserLED: eMonarcoComStatus
VAR_INPUT
    byLeds: BYTE;
END_VAR
//   10.0   2.0  PWM1 Dutycycle ch A
THIS^._abyIntTxBuf[5]               := byLeds;  // set userled mask
THIS^._abyIntTxBuf[6]               := byLeds;  // set userled

METHOD AfterReadInputs: INT
///********************************
///  Continous intputs
///********************************
SUPER^.AfterReadInputs();

IF _iState = 10 THEN

    // SPI reads/writes outputs in a single read/write action
    THIS^.getDInByte(byDin =&gt; byDin);
    THIS^.getAInWord(wAIn1 =&gt; wAIn1, wAIn2 =&gt; wAIn2);
    THIS^.setDoutByte(byDout := byDOut);
    THIS^.setAOutWord(wAout1 := wAOut1, wAOut2 := wAOut2);

    _BeforeWriteOutputs := _BeforeWriteOutputs +1;
    eComStatus  := THIS^.dataTransfer();
END_IF

METHOD BeforeWriteOutputs: INT
///********************************
///  Continous Outputs
///******************************** 
SUPER^.BeforeWriteOutputs();

IF _iState = 10 THEN
    _BeforeWriteOutputs := _BeforeWriteOutputs +1;
END_IF

METHOD Initialize: UDINT
VAR_INPUT
    wModuleType: UINT;
    dwInstance: UDINT;
    pConnector: pointer;
END_VAR
VAR
    pParam1: pointer;
    pParam10: pointer;
    udiResult: UDINT;
END_VAR
SUPER^.Initialize(wModuleType, dwInstance, pConnector);

pParam1 := ConfigGetParameter(_pConnector, 1);
IF pParam1 &lt;&gt; 0 THEN
    THIS^.Watchdog := IoStandard.ConfigGetParameterValueByte(pParam1, ADR(udiResult));
END_IF

pParam10 := ConfigGetParameter(_pConnector, 10);
IF pParam10 &lt;&gt; 0 THEN
    THIS^.ControlByte8 := IoStandard.ConfigGetParameterValueByte(pParam10, ADR(udiResult));
END_IF

VAR_GLOBAL
MONARCO_STRUCT_SIZE: UINT;
MONARCO_SERVICE_STATUS_OK: WORD;
MONARCO_SERVICE_ERROR_UNKNOWN_REG: WORD;
MONARCO_SERVICE_ERROR_CRC: WORD;
SDC_ADDRESS_MASK: WORD;
MONARCO_ADC_RANGE_MAX: WORD;
END_VAR