Sample program 3 (LIN)

Download (TwinCAT 3 example program):
Sample program 3 (LIN) 1: Program

Global variables for LIN master example program

VAR_GLOBAL
nSetBaudrate : UINT := 10417; // Für Rx-Delay-Berechnung
(* I/O variables for EL6001 terminal acting as Master*)
COMin_EL6001_MASTER AT %I* : EL6inData22B;(* linked to the EL6001 in the TwinCAT System Manager *)
COMout_EL6001_MASTER AT %Q*: EL6outData22B;(* linked to the EL6001 in the TwinCAT System Manager *)
RxBuffer_MASTER : ComBuffer;(* Receive data buffer; used with all receive function blocks *)
TxBuffer_MASTER : ComBuffer;(* Transmit data buffer; used with all receive function blocks *)
END_VAR

Data types for LIN master-slave example program

TYPE tDataFrame : ARRAY[0..8] OF BYTE; END_TYPE // Datentyp für ein LIN-Frame

Function for adding data length information and parity bits:

Declaration part:

FUNCTION F_ADD_LIN_NODE_PARITY : BYTE
VAR_INPUT
nNodeID:BYTE; // Inputvariable: node-Id
nReqLen:BYTE; // Inputvariable: length-identification: 2,4,8 Byte-Frame
END_VAR
VAR
bParity0:BYTE; // Internal intermediate value parity 0
bParity1:BYTE; // Internal intermediate value parity 1
nPrepId:BYTE; // Intermediate value for PID
END_VAR

Execution part:

nPrepId := nNodeID OR SHL(nReqLen,4);

bParity0 :=
(nNodeID AND 2#0001)
XOR (SHR((nNodeID AND 2#0010), 1))
XOR (SHR((nNodeID AND 2#0100), 2))
XOR (SHR((nNodeID AND 2#0001_0000), 4));

bParity1 := 16#01 AND (NOT(
SHR((nNodeID AND 2#0010), 1)
XOR (SHR((nNodeID AND 2#1000),3))
XOR (SHR((nNodeID AND 2#0001_0000), 4))
XOR (SHR((nNodeID AND 2#0010_0000), 5))));

F_ADD_LIN_NODE_PARITY := nPrepId OR SHL(bParity0,6) OR SHL(bParity1,7);

Function for calculating the checksum (conventional method)

Declaration part:

FUNCTION F_CALC_LIN_CHKSUM : BYTE
VAR_INPUT
pData:POINTER TO ARRAY[0..10] OF BYTE; // Pointer to source datafield
nLen:BYTE; // Number of bytes used for calculating the checksum
END_VAR
VAR
i: BYTE; // Counter variable
wResult:WORD; // Resulting output value
END_VAR

Execution part:

wResult := BYTE_TO_WORD(pData^[0]);
FOR i := 1 TO (nLen-1) DO
wResult := wResult + BYTE_TO_WORD(pData^[i]);
IF wResult > 255 THEN
wResult := wResult - 255;
END_IF
END_FOR
F_CALC_LIN_CHKSUM := WORD_TO_BYTE(NOT(wResult));

This master program part should be called from a corresponding separate task. Here, a node in the LIN bus with the ID 0x07 is queried every 200 ms.

Declaration part:

PROGRAM EL6001_MASTER
VAR
Timer: TON; // Timer for periodical requests by the master
Send: SendData; // Functionblock of TC2_SerialCom
SendBusy: BOOL; // Flag copy of SendData.Busy
SendErrorID: ComError_t; // Flag-Copy of Error-ID
aDataTX:tDataFrame; // Data frame being send
Receive: ReceiveData; // Functionblock of TC2_SerialCom
LastReceivedDataBytes: tDataFrame; // Copy (Latch) of received data
DataReceived: BOOL; // Flag copy of receive confirmation
ReceiveBusy: BOOL; // Flag copy of reception not ready
ReceiveError: BOOL; // Flag copy of receive error
ReceiveErrorID: ComError_t;// Error-ID copy
ReceiveTimeout: BOOL; // Flag copy of receive-timeout
ReceiveCounter: UDINT := 0; // Number of received frames
aDataRX:tDataFrame; // Receiving data frame buffer
nDataLen:BYTE := 4; // Fixed data length
nState:BYTE := 0; // Initial state (start)
bNodeId_SL1: BYTE := 16#07; // ID of slave node
bReqLen_SL1: BYTE := 0; // Optional entry for 4 Data bytes Value 2
nRxChecksum: BYTE; // Storage of received checksum
nCalcChecksum:BYTE; // Storage of calculated checksum
T_ReceiveDelay:TIME; // Storage of calculated delay time
END_VAR

Execution part:

(*==================================================================
Receive data
*)
CASE nState OF
0:
Timer(IN:=TRUE, PT:=T#0.5S); // Call timer for periodical master requests
IF Timer.Q THEN
// Put ID into Tranceive data:
aDataTX[0] := F_ADD_LIN_NODE_PARITY(bNodeId_SL1, bReqLen_SL1);
LastReceivedDataBytes[0] := aDataTX[0];
// Send request to Slave 1 (get Data)
Send(pSendData:= ADR(aDataTX), Length:= 1,
TXbuffer:= TxBuffer_MASTER,
Busy => SendBusy, Error => SendErrorID);
Timer(IN:=FALSE); (* reset timer *)
IF NOT SendBusy THEN // Wait until sending ends
nState := nState + 1;
END_IF
END_IF
1:
// Delaytime by 1/Tbaud * Number of Bytes * (8 Databits + 2 Bit:start-Stop) * 1000ms
T_ReceiveDelay := REAL_TO_TIME((1/DINT_TO_REAL(nSetBaudrate)) * 33 * 1000); // .. for 1 Byte
Timer(IN:=TRUE, PT:=T_ReceiveDelay);
IF Timer.Q THEN
// Wait until ID is send
nState := nState + 1;
Timer(IN:=FALSE); (* reset timer *)
END_IF
2:
Receive(
pReceiveData:= ADR(aDataRX),
SizeReceiveData:= (nDataLen + 1),
RXbuffer:= RxBuffer_MASTER,
Timeout:= T#1S,
DataReceived=> DataReceived,
busy=> ReceiveBusy,
Error=> ReceiveErrorID,
RxTimeout=> ReceiveTimeout );
IF DataReceived THEN
// DataReceived := FALSE;
ReceiveCounter := ReceiveCounter + 1;
IF NOT ReceiveBusy THEN
// Compare checksum
nRxChecksum := aDataRX[nDataLen];
nCalcChecksum := F_CALC_LIN_CHKSUM(pData := ADR(aDataRX), nLen := nDataLen);
IF(nRxChecksum = nCalcChecksum) THEN
// Response received - clear databuffer:
memset(ADR(LastReceivedDataBytes[1]), 0, (SIZEOF(aDataRX)-1));
// Take-over data when checksum OK:
memcpy(ADR(LastReceivedDataBytes[1]), ADR(aDataRX), (nDataLen +1));
END_IF
nState := 0;
END_IF
ELSE
Timer(IN:=TRUE, PT:=T#0.1S); // Receive-Timeout 100 ms: no data
IF Timer.Q THEN
nState := 0;
END_IF
END_IF
END_CASE
(*==================================================================
*)

Program fast task / RS232 background communication

In this example, third task should be created with as few “cycle ticks” as possible, which deals with the background communication with the EL6001 terminal (as master).

Declaration part:

PROGRAM FAST
VAR
(* background communication with the EL6001 as Master device *)
COMportControl_MASTER: SerialLineControl;
COMportControlError_MASTER: BOOL;
COMportControlErrorID_MASTER: ComError_t;
END_VAR

Execution part:

COMportControl_MASTER(
Mode:= SERIALLINEMODE_EL6_22B,
pComIn:= ADR(COMin_EL6001_MASTER),(* I/O data; see global variables *)
pComOut:= ADR(COMout_EL6001_MASTER),(* I/O data; see global variables *)
SizeComIn:= SIZEOF(COMin_EL6001_MASTER),(* I/O data; see global variables *)
TxBuffer:= TxBuffer_MASTER,(* transmit buffer; see global variables *)
RxBuffer:= RxBuffer_MASTER,(* receive buffer; see global variables *)
Error=> COMportControlError_MASTER,(* indicating an error *)
ErrorID=> COMportControlErrorID_MASTER );(* contains the error-ID *)