Beispielprogramm 3 (LIN)
Download (TwinCAT 3 Beispielprogramm):
Programm
Globale Variablen zum LIN-Master Beispielprogramm
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
Datentypen zum LIN-Master-Slave Beispielprogramm
TYPE tDataFrame : ARRAY[0..8] OF BYTE; END_TYPE // Datentyp für ein LIN-Frame
Funktion zum Hinzufügen von Datenlängeninformation und Paritätsbits:
Deklarationsteil:
FUNCTION F_ADD_LIN_NODE_PARITY : BYTE
VAR_INPUT
nNodeID:BYTE; // Eingangsvariable: Knoten-Id
nReqLen:BYTE; // Eingangsvariable: Längen-Kennung: 2,4,8 Byte-Frame
END_VAR
VAR
bParity0:BYTE; // Interner Zwischenwert für Parity 0
bParity1:BYTE; // Interner Zwischenwert für Parity 1
nPrepId:BYTE; // Zwischenwert der PID
END_VAR
Ausführungsteil:
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);
Funktion zum Berechnen der Checksumme (herkömmliche Methode)
Deklarationsteil:
FUNCTION F_CALC_LIN_CHKSUM : BYTE
VAR_INPUT
pData:POINTER TO ARRAY[0..10] OF BYTE; // Zeiger auf Datenfeld
nLen:BYTE; // Anzahl Bytes zur Berechnung der Checksumme
END_VAR
VAR
i: BYTE; // interne Zählvariable
wResult:WORD; // Ausgangswert
END_VAR
Ausführungsteil:
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));
Dieser Master-Programmteil sollte von einer entsprechenden separaten Task aufgerufen werden. Hier wird alle 200 ms ein Knoten im LIN-Bus mit der ID 0x07 angefragt.
Deklarationsteil:
PROGRAM EL6001_MASTER
VAR
Timer: TON; // Timer für period. Anfragen vom Master
Send: SendData; // Funktionsblock aus TC2_SerialCom
SendBusy: BOOL; // Flag-Kopie von SendData.Busy
SendErrorID: ComError_t; // Error-ID Kopie
aDataTX:tDataFrame; // Zu sendendes Dataframe
Receive: ReceiveData; // Funktionsblock aus TC2_SerialCom
LastReceivedDataBytes: tDataFrame; // Kopie (Latch) der empfangenen Daten
DataReceived: BOOL; // Flag-Kopie von Empfangsbestätigung
ReceiveBusy: BOOL; // Flag Kopie von Empfangen noch nicht fertig
ReceiveError: BOOL; // Flag Kopie von Empfangsfehler
ReceiveErrorID: ComError_t;// Error-ID Kopie
ReceiveTimeout: BOOL; // Flag Kopie von Empangs-timeout
ReceiveCounter: UDINT := 0; //Anzahl empfangener Frames
aDataRX:tDataFrame; // Zu empfangenes Dataframe
nDataLen:BYTE := 4; // Festgelegte Datenlänge
nState:BYTE := 0; // Initialer Status (start)
bNodeId_SL1:BYTE := 16#07; // ID des Slave-Knotens
bReqLen_SL1: BYTE:= 0; // 4 Datenbytes optional Wert 2
nRxChecksum:BYTE; // Wertespeicher für empfangene Checksumme
nCalcChecksum:BYTE; // Wertespeicher für berechnete Checksumme
T_ReceiveDelay:TIME; // Wertespeicher für ber. Verzögerungszeit
END_VAR
Ausführungsteil:
(*==================================================================
Receive data
*)
CASE nState OF
0:
Timer(IN:=TRUE, PT:=T#0.5S); // Master Requests: Periodische Anfragen
IF Timer.Q THEN
// Setze ID ein:
aDataTX[0] := F_ADD_LIN_NODE_PARITY(bNodeId_SL1, bReqLen_SL1);
LastReceivedDataBytes[0] := aDataTX[0];
// Sende Anfrage an 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 // Warten bis Senden abgeschlossen ist
nState := nState + 1;
END_IF
END_IF
1:
// Verzögerungszeit aus 1/Tbaud * Anzahl Bytes * (8 Datenbits + 2 Bit:start-Stop) * 1000ms
T_ReceiveDelay := REAL_TO_TIME((1/DINT_TO_REAL(nSetBaudrate)) * 33 * 1000); // .. für nur 1 Byte
Timer(IN:=TRUE, PT:=T_ReceiveDelay);
IF Timer.Q THEN
// Warten bis ID gesendet wurde
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
// Vergleiche Checksumme
nRxChecksum := aDataRX[nDataLen];
nCalcChecksum := F_CALC_LIN_CHKSUM(pData := ADR(aDataRX), nLen := nDataLen);
IF(nRxChecksum = nCalcChecksum) THEN
//Antwort empfangen - lösche Datenpuffer:
memset(ADR(LastReceivedDataBytes[1]), 0, (SIZEOF(aDataRX)-1));
// Übernehme Daten wenn Checksumme 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: Keine Daten
IF Timer.Q THEN
nState := 0;
END_IF
END_IF
END_CASE
(*==================================================================
*)
Programm Fast-Task / RS232 Hintergrundkommunikation
Hier ist in diesem Beispiel eine dritte Task mit möglichst geringen „Cycle-Ticks“ anzulegen, die für die Hintergrundkommunikation mit der EL6001 Klemme (als Master) zuständig ist.
Deklarationsteil:
PROGRAM FAST
VAR
(* background communication with the EL6001 as Master device *)
COMportControl_MASTER: SerialLineControl;
COMportControlError_MASTER: BOOL;
COMportControlErrorID_MASTER: ComError_t;
END_VAR
Ausführungsteil:
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,
ErrorID=> COMportControlErrorID_MASTER );