Category Archives: CoDeSys. Теория.

Опрос МВА 8 ПЛК с программной средой CoDeSys 3.5

Пример на языке ST реализует работу по интерфейсу RS-485: чтение 8 входов из прибора МВА8 с адресом 16. Для работы примера в PLC Configuration должны быть подключены библиотеки SisLibCom, OwenNet, ComService.

PROGRAM PLC_PRG

VAR

get_fl_owen:OWEN_GET_REAL; (*Чтение параметра типа Real*)

COM_SERVICE1: COM_SERVICE;

cmpl: BOOL; (* признак завершения операции *)

err: INT; (*номер ошибки*)

port_opened: BYTE := 0; (*состояние порта*)

Settings: COMSETTINGS;(* настройки последовательного порта *)

com_num: PORTS:=0 (*0 — RS-485, 1 — RS-232*);

TimeOut: TIME:=T#50ms;(*таймаут*)

Enabl: BOOL:=TRUE; (*состояние работы блока*)

wTime: WORD; (*значение времени для измери-теля*)

A: REAL; (*считанное значение*)

addres: INT:=16; (*адрес МВА8*)

B: ARRAY [0..7] OF REAL; (*массив для хранения значений входов МВА8*)

i: INT; (*номер входа МВА**)

END_VAR

(*Устанавливаем настройки COM-порта*)

IF port_opened=0 THEN

Settings.Port:=com_num; (*номер COM-порта*)

Settings.dwBaudRate:=115200; (*скорость*)

Settings.byParity:=0;

Settings.dwTimeout:=0;

Settings.byStopBits:=0;

Settings.dwBufferSize:=0;

Settings.dwScan:=0;

END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );

(*Если COM-порт открыт, то переходим к приему и передачи данных *)

IF COM_SERVICE1.ready THEN

port_opened:=2;

END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

(*чтение 8 входов из прибора МВА8 с адресом 16 *)

get_fl_owen(

Enable:=Enabl , (* разрешение работы блока *)

Addr:=addres , (*адрес прибора*)

AddrLen:=A8BIT , (*длина адреса*)

Name:=’read’ , (*имя параметра, значение которого считываем*)

Index:=65535 , (*индекс параметра*)

RealType:=FLOAT32T , (*тип параметра, значение которого считываем*)

ComHandle:= Settings.Port, (*номер COM-порта*)

TimeOut:=TimeOut , (*Таймаут T#50ms*)

Complete=>cmpl , (* скопировать признак завершения операции *)

Value=>a , (*считанное значение*)

wTime=>wTime , (*значение времени для измерителя*)

Error=> err ); (* скопировать регистр ошибок *)

(*если установлен признак завершения операции, то *)

IF Enabl = FALSE THEN

Enabl := TRUE;

END_IF

(*Если завершен обмен и нет ошибок*)

IF cmpl THEN

IF (err=0) THEN

B[i]:=a; (*присваиваем массиву значение на входе*)

ELSE

Enabl := FALSE;

END_IF;

i:=i+1; (*увеличиваем номер входа*)

addres:=16+i; (*увеличиваем адрес*)

IF (i=8) THEN (*если номер входа меньше 8*)

addres:=16;

i:=0;

END_IF

END_IF

END_IF

Опрос ТРМ 201 по сети RS-485 с использованием ПЛК со средой программирования CoDeSys 3.5

Пример на языке ST реализует работу по интерфейсу RS-485: Чтение и за-пись двух параметров с одного прибора ТРМ201. Для работы примера в PLC Con-figuration должны быть подключены библиотеки SisLibCom, OwenNet, ComService.

PROGRAM PLC_PRG

VAR

get_fl_owen:OWEN_GET_REAL; (*Чтение параметра типа Real*)

send_fl_owen:OWEN_SET_REAL; (*Запись параметра типа Real*)

COM_SERVICE1: COM_SERVICE; (*открытие COM-порта*)

cmpl: BOOL; (* признак завершения операции *)

err: INT; (*номер ошибки*)

port_opened: BYTE := 0; (*состояние порта*)

Settings: COMSETTINGS; (* настройки последова-тельного порта *)

com_num: PORTS:=0; (*0 — RS-485, 1 — RS-232*)

TimeOut: TIME:=T#50ms; (*таймаут*)

Enabl: BOOL:=TRUE; (*состояние работы блока*)

wTime: WORD; (*значение времени для измери-теля*)

master1: BYTE := 0;

A: REAL; (*считанное значение из ТРМ201*)

b: REAL:=30;

END_VAR

(*Устанавливаем настройки COM-порта*)

IF port_opened=0 THEN

Settings.Port:=com_num; (*номер COM-порта*)

Settings.dwBaudRate:=115200; (*скорость*)

Settings.byParity:=0;

Settings.dwTimeout:=0;

Settings.byStopBits:=0;

Settings.dwBufferSize:=0;

Settings.dwScan:=0;

END_IF Описание интерфейса библиотек ФБ для работы с протоколом ОВЕН 19

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );

(*Если COM-порт открыт, то переходим к приему и передачи данных *)

IF COM_SERVICE1.ready THEN

port_opened:=2;

END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

(*чтение параметра типа Real из прибора ТРМ201 с адресом 8 из параметр с именем PV и индексом 0*)

0: get_fl_owen(

Enable:=Enabl , (* разрешение работы блока *)

Addr:=8 , (*адрес прибора*)

AddrLen:=A8BIT , (*длина адреса*)

Name:=’pv’ , (*имя параметра, значение которого считываем*)

Index:=65535 , (*индекс параметра*)

RealType:=FLOAT24 , (*тип параметра, значение которого считываем*)

ComHandle:= Settings.Port, (*номер COM-порта*)

TimeOut:=TimeOut , (*Таймаут T#50ms*)

Complete=>cmpl , (* скопировать признак готовности результата *)

Value=>a , (*считанное значение*)

wTime=>wTime , (*значение времени для измерителя*)

Error=> err ); (* скопировать регистр ошибок *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

master1:=1;(*переходим к выполнению следующего блока*)

END_IF

1:(*Запись параметра типа Real в прибор ТРМ201 с адресом 8 в параметр с именем SP и индексом 0*)

send_fl_owen(

Enable:=Enabl , (* разрешение работы блока *)

Addr:= 8, (*адрес прибора*)

AddrLen:=A8BIT, (*длина адреса*)

Name:= ‘sp’, (*имя параметра, значение которого записываем в ТРМ201*)

Index:= 0, (*индекс параметра*) Описание интерфейса библиотек ФБ для работы с протоколом ОВЕН 20

RealType:= FLOAT24, (*тип параметра, значение которого записываем*)

Value:=b , (*параметр, значение значение которого записываем в TPM201*)

ComHandle:=Settings.Port, (*номер COM-порта*)

TimeOut:=TimeOut , (* время тайм-аута [мс] — макс. за-держка на обработку запроса *)

Complete=>cmpl , (* скопировать признак готовности результата *)

Error=> err); (* скопировать регистр ошибок *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

master1:=0;(*переходим к выполнению следующего блока*)

END_IF

END_CASE

IF Enabl = FALSE THEN

Enabl := TRUE;

END_IF

IF err <> 0 THEN

Enabl := FALSE;

END_IF

END_IF

Пример работы по Modbus в режиме Slave для CoDeSys 3.5

Пример на языке ST реализует работу по интерфейсу RS-485: настройку модуля ModBus-Slave для трансляции в сеть массива переменных MB_Buffer.

Объявление переменных:

PROGRAM PLC_PRG

VAR

MB_func: MB_SLAVE;

MB_Buffer: ARRAY[0..3] OF WORD; (* буфер данных для обмена по ModBus *)

MB_NewData: BOOL; (* данные обновились *)

MB_Error: MB_ERROR_TYPE; (* ошибка обработки запроса *)

port_opened: BYTE := 0;

Settings: ComSerice.SysCom.COM_Settings; (*настройки последовательного порта *)

SettingsEx: ComSerice.SysCom.COM_SettingsEx;

COM_SERVICE1: COM_SERVICE;

END_VAR

Тело программы:

IF port_opened=0 THEN

Settings.sPort:=ComSerice.SysCom.SYS_COMPORT2; (*номер COM-порта*)

Settings.ulBaudrate:=9600; (*скорость*)

Settings.byParity:=0;

Settings.ulTimeout:=0;

Settings.byStopBits:=1;

Settings.ulBufferSize:=0;

SettingsEx.byByteSize:=8;

SettingsEx.bBinary:=TRUE;

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings,sets_ex:=SettingsEx , Task:=OPEN_TSK );

(*Если COM-порт открыт, то переходим к приему и передачи данных *)

IF COM_SERVICE1.ready THEN

port_opened:=2;

END_IF

MB_Buffer[0] := 16#1234;

MB_Buffer[1] := 16#5678;

MB_Buffer[2] := 16#ABCD;

MB_Buffer[3] := 16#5555;

END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

MB_func(

ComHandle:=COM_SERVICE1.handle, (*номер COM-порта*)

DevAddr := 16,

pBuffer := ADR(MB_Buffer),

BufSize := SIZEOF(MB_Buffer),

NewData => MB_NewData,

Error => MB_Error

);

IF MB_Error <> MB_OK THEN

MB_NewData := FALSE;

END_IF

IF MB_NewData = TRUE THEN

MB_Error := MB_OK;

END_IF

END_IF

Пример записи переменных по Modbus для CoDeSys 3

Пример на языке ST реализует работу по интерфейсу RS-485: запись параметров типа Int (функция Modbus 06 — MB_WR_SNG_REG) и записи параметра типа Float (функция Modbus 16 — MB_WR_REGS). Для работы примера в PLC Configuration должны быть подключены библиотеки SisLibCom, Modbus, ComService.

PROGRAM PLC_PRG

VAR

send1_modbus:MB_WR_SNG_REG; (*функция 06 — запись параметра*)

send2_modbus: MB_WR_REGS; (*функция 16 — запись параметров*)

COM_SERVICE1: COM_SERVICE; (*ФБ открытия порта*)

Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)

cmpl: BOOL; (* признак завершения операции *)

port_opened: BYTE := 0; (*состояние порта*)

Init: BOOL; (* признак инициализации пользовательской программы *)

Settings:COMSETTINGS; (* настройки последовательного порта *)

com_num: PORTS:=1; (*0 — RS-485, 1 — RS-232*)

enabl: BOOL; (*состояние работы блока*)

err: INT; (*номер ошибки*)

TimeOut: TIME:=T#50ms; (*таймаут*)

Exception: BYTE;

DataSize: WORD;

master1: BYTE;

t: DWORD; (*переменная для организации счетчика*)

A: WORD := 0; (*счетчик*)

f1:DINT:=65; (*Записываемый параметр типа DINT*)

f2:REAL:=12.7; (*Записываемый параметр типа REAL*)

ptr_f2:POINTER TO BYTE;

END_VAR

(*Организуем счетчик, что бы передавать эти данные по сети*)

t:=t+1;

IF (t MOD 1000)=0 THEN

A := A + 1;

IF A > 9999 THEN

A := 0;

END_IF

(*Устанавливаем настройки COM-порта*)

IF port_opened=0 THEN

Settings.Port:=com_num; (*номер COM-порта*) Описание интерфейса библиотеки ФБ для работы с протоколом Modbus

Settings.dwBaudRate:=115200; (*скорость*)

Settings.byParity:=0;

Settings.dwTimeout:=0;

Settings.byStopBits:=0;

Settings.dwBufferSize:=0;

Settings.dwScan:=0;

END_IF

(*Открываем COM-порт*)

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );

(*Если COM-порт открыт, то переходим к приему и передачи данных *)

IF COM_SERVICE1.ready THEN

port_opened:=2;

END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

0: (* функция 06 — запись параметра типа Int в прибор с адресом 1 в регистр 0*)

send1_modbus(

Enable:=enabl , (* разрешение работы блока *)

Mode:=MB_ASCII , (*режим передачи*)

DevAddr:=1 , (*адрес*)

RegAddr:=0 , (*регистр*)

Value:=A, (*значение переменной, которое надо передать*)

ComHandle:= Settings.Port, (*номер сом-порта*)

TimeOut:=TimeOut , (*таймаут T#50ms*)

Complete=>cmpl , (* скопировать признак завершения операции *)

Exception=>err ); (* скопировать регистр ошибок *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

master1:=1; (*переходим к выполнению следующего блока*)

END_IF

1: (* функция 16 — запись параметров типа Int (регистр 4) и Real (регистр 6) в прибор с адресом 2 *)

(*запись в буффер параметра типа INT*)

Buffer[1] := DINT_TO_BYTE(f1);

Buffer[0] := DINT_TO_BYTE( SHR(f1,8));

Buffer[3] := DINT_TO_BYTE( SHR(f1,16));

Buffer[2] := DINT_TO_BYTE( SHR(f1,24));

(*запись в буффер параметра типа Float*)

ptr_f2:=ADR(f2);

buffer[5] := ptr_f2^;

ptr_f2:=ptr_f2+1;

21 Описание интерфейса библиотеки ФБ для работы с протоколом Modbus 22

buffer[4] := ptr_f2^;

ptr_f2:=ptr_f2+1;

buffer[7] := ptr_f2^;

ptr_f2:=ptr_f2+1;

buffer[6] := ptr_f2^;

send2_modbus(

Enable:= enabl, (* разрешение работы блока *)

Mode:=MB_ASCII , (*режим передачи*)

DevAddr:=2 , (*адрес*)

FirstAddr:= 4, (*номер регистра*)

Quantity:= 4, (*количество записываемых регистров*)

ComHandle:=Settings.Port ,(*номер сом-порта*)

TimeOut:=TimeOut , (*таймаут T#50ms*)

Buffer:=Buffer , (* буфер данных *)

Complete=>cmpl , (* скопировать признак завершения операции *)

Exception=>err , (* скопировать регистр ошибок *)

RegCnt=> DataSize); (*кол-во считанных байтов *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

master1:=0;(*переходим к выполнению следующего блока*)

END_IF

END_CASE

IF enabl = FALSE THEN

enabl := TRUE;

END_IF

IF err <> 0 THEN

enabl := FALSE;

END_IF

END_IF

Пример опроса параметров по RS-485

Пример на языке ST реализует работу по интерфейсу RS-485: чтение параметров типа Int и типа Real (Float) (функция Modbus 03 — MB_RD_HOLD_REGS), а также считывания трех параметров типа Int (функция Modbus 04 — MB_RD_INP_REGS). Для работы примера в PLC Configuration должны быть подключены библиотеки SisLibCom, Modbus, ComService.

PROGRAM PLC_PRG

VAR

get1_modbus: MB_RD_HOLD_REGS; (*функция 03 — чтение параметра типа INT*)

get2_modbus: MB_RD_INP_REGS; (*функция 04 — чтение трех параметров типа INT*)

get3_modbus: MB_RD_HOLD_REGS; (*функция 03 — чтение параметра типа Float*)

Buffer: ARRAY[0..255] OF BYTE; (* байтовый буфер данных *)

cmpl: BOOL;

port_opened: BYTE := 0;

Init: BOOL; (* признак инициализации пользовательской программы *)

Settings:COMSETTINGS;(* настройки последовательного порта *)

com_num: PORTS:=0; (*0 — RS-485, 1 — RS-232*)

enabl: BOOL; (*состояние работы блока*)

err: INT; (*номер ошибки*)

TimeOut: TIME:=T#50ms;(*таймаут*)

Exception: BYTE;

DataSize: WORD;

master1: BYTE:= 1;

t: DWORD; (*переменная для организации счетчика*)

A: WORD := 0; (*счетчик*)

x:WORD; (*считанное значение*)

x1: WORD; (*переменная для записи по сети*)

x2: WORD; (*переменная для записи по сети*)

x3: WORD; (*переменная для записи по сети*)

d: REAL; (*считанное значение*)

ptr_D:POINTER TO BYTE;

COM_SERVICE1: COM_SERVICE;

END_VAR

(*Организуем счетчик, что бы передавать эти данные по сети*)

t:=t+1;

IF (t MOD 1000)=0 THEN

A := A + 1;

IF A > 9999 THEN

A := 0;

END_IF

(*Устанавливаем настройки COM-порта*)

IF port_opened=0 THEN

Settings.Port:=com_num; (*номер COM-порта*)

Settings.dwBaudRate:=115200; (*скорость*)

Settings.byParity:=0;

Settings.dwTimeout:=0;

Settings.byStopBits:=0;

Settings.dwBufferSize:=0;

Settings.dwScan:=0;

END_IF

COM_SERVICE1(Enable:=(port_opened=0) , Settings:=Settings , Task:=OPEN_TSK );

(*Если COM-порт открыт, то переходим к приему и передачи данных *)

IF COM_SERVICE1.ready THEN

port_opened:=2;

END_IF

IF port_opened=2 THEN (*Удачно проинициализировали*)

CASE master1 OF

0: (* функция 03 инт — ФБ считывает значение параметра типа int из прибора с адресом 2 в регистр с номером 8 по протоколу Modbus-ASCII*)

get1_modbus(

Enable:=enabl , (* разрешение работы блока *)

Mode:=MB_ASCII , (*режим передачи*)

DevAddr:=2 , (*адрес*)

FirstAddr:=8 , (*номер регистра*)

Quantity:=1, (*количество регистров*)

ComHandle:=Settings.Port ,(*номер COM-порта*)

TimeOut:=TimeOut , (*Таймаут T#50ms*)

Buffer:=Buffer , (* буфер данных *)

Complete=>cmpl , (* скопировать признак завершения операции *)

Exception=>err , (* скопировать регистр ошибок *)

ByteCnt=>DataSize ); (*кол-во считанных байтов *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)

x:=BYTE_TO_WORD(BUFFER[1]) OR SHL(BYTE_TO_WORD(BUFFER[0]),8);

END_IF

master1:=1; (*переходим к выполнению следующего ФБ*)

END_IF

1: (* функция 03 флоат — ФБ считывает значение параметра типа

int из прибора с адресом 2 в регистр с номаром 10 по протоколу Modbus-ASCII *)

get3_modbus(

Enable:=enabl , (* разрешение работы блока *)

Mode:=MB_ASCII , (*режим передачи*)

DevAddr:=2 , (*адрес*)

FirstAddr:=10 , (*номер регистра*)

Quantity:=2, (*количество регистров*)

ComHandle:=Settings.Port ,(*номер COM-порта*)

TimeOut:=TimeOut , (*Таймаут T#50ms*)

Buffer:=Buffer , (* буфер данных *)

Complete=>cmpl , (* скопировать признак завершения операции *)

Exception=>err , (* скопировать регистр ошибок *)

ByteCnt=>DataSize ); (*кол-во считанных байтов *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

master1:=2;(*переходим к выполнению следующего ФБ*)

IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа FLOAT*)

ptr_D:=ADR(d);

ptr_D^:=buffer[1];

ptr_D:=ptr_D+1;

ptr_D^:=buffer[0];

ptr_D:=ptr_D+1;

ptr_D^:=buffer[3];

ptr_D:=ptr_D+1;

ptr_D^:=buffer[2];

END_IF

END_IF

2: (* функция 04 инт — ФБ считывает значения трех параметров типа Int из прибора с адресом 2 начиная с регистра с номeром 12*)

get2_modbus(

Enable:= enabl, (* разрешение работы блока *)

Mode:=MB_ASCII , (*режим передачи*)

DevAddr:=2 , (*адрес*)

FirstAddr:=12 , (*номер регистра*)

Quantity:=6 , (*количество регистров*)

ComHandle:= Settings.Port,(*номер COM-порта*)

TimeOut:=TimeOut , (*Таймаут T#50ms*)

Buffer:=Buffer , (* буфер данных *)

Complete=>cmpl , (* скопировать признак завершения операции *)

Exception=>err , (* скопировать регистр ошибок *)

ByteCnt=> DataSize); (*кол-во считанных байтов *)

(*если установлен признак завершения операции, то *)

IF cmpl THEN

IF err=0 THEN (*Если нет ошибок, то получаем данные из буфера типа INT*)

x1:=BYTE_TO_WORD(BUFFER[1]) OR SHL(BYTE_TO_WORD(BUFFER[0]),8);

x2:=BYTE_TO_WORD(BUFFER[5]) OR SHL(BYTE_TO_WORD(BUFFER[4]),8);

x3:=BYTE_TO_WORD(BUFFER[9]) OR SHL(BYTE_TO_WORD(BUFFER[8]),8);

END_IF

master1:=0;(*переходим к выполнению следующего ФБ*)

END_IF

END_CASE

IF enabl = FALSE THEN

enabl := TRUE;

END_IF

IF err <> 0 THEN

enabl := FALSE;

END_IF

END_IF

Использование CoDeSys Service Tool для диалога с ПЛК

CoDeSys Service Tool (CST) предназначен для упрощения выполнения сервисных действий с контроллером, таких как: создание или обновление загрузочного проекта, перезагрузка и запуск исполняемого кода проекта, загрузка и чтение файлов, сброс и т.д. Таким образом, с помощью CST можно проводить типовые операции этапа сопровождения прикладных проектов без применения среды программирования CoDeSys.

Помимо работы в диалоговом режиме, с CST можно работать через командную строку, что позволяет предельно упростить выполнение необходимых операций.

При запуске CST на экране монитора отображается следующее окно:

 

В верхней части данного окна расположен раздел ‘Options’, содержащий кнопки для настройки соединения. В центре окна помещен раздел ‘Online’, содержащий кнопки управления контроллером и загрузкой программ и файлов. Внизу вы видите раздел ‘Log’, в котором содержится протокол работы. Справа помещен информационный раздел, в котором показан используемый контроллер и параметры соединения.

Раздел ‘Options’ содержит кнопки для выбора контроллера и настройки канала связи с ним

Пример использования CST

Допустим, нам необходимо обновить код прикладной программы (например My_prg) в одном или нескольких ПЛК, причем сделать это должен неспециалист. Контроллер соединен с компьютером и работает.

Предположим, что мы уже имеем в определенной рабочей директории необходимые файлы нового загрузочного проекта (My_prg.prg, My_prg.chk, My_prg.sdb), программу CST.exe и файл настроек My_plc.ini. Теперь мы можем запустить CST в диалоговом режиме, считать настройки, выбрать загрузочный проект, закачать его, перезагрузить и запустить прикладную программу.

Поскольку предполагается, что обновление программы выполняет неспециалист, то желательно предельно упростить его действия. В идеальном случае, он должен щелкнуть мышкой на определенном файле своего PC (или иконке на рабочем столе) и получить понятное сообщение, подтверждающее успешный результат. Для этого мы создадим в рабочей директории bat файл (или несколько), который и будет производить все необходимые действия. Человеку, выполняющему загрузку, останется лишь запустить его.

bat файл создается в любом текстовом редакторе. Для получения сообщений на русском языке используйте кодировку DOS 866.

Ниже приведен возможный пример bat файла. В начале он выполняет проверку двух базовых условий (наличие самой сервисной программы и файла проекта) и, в случае отсутствия, дает соответствующее сообщение. Затем запускает CST с соответствующими аргументами и анализирует код возврата. Для кодов 1 и 103 предусмотрены текстовые сообщения. Вы можете использовать данный пример, адаптировав его под собственные нужды и развив диагностические сообщения в зависимости от задачи.

Пример bat файла:

@echo off

echo.

echo Обновление программы в ПЛК

echo ——————————

echo.

rem Проверяем наличие сервисной программы CST

if not exist «CST.exe» goto errorCST

rem Проверяем наличие загрузочного проекта

if not exist «My_prg.prg» goto errorPRG

echo Идет соединение с контроллером и загрузка программы. . .

CST.exe -pf %CD%/My_plc.ini -b My_prg.prg -rl -sta

echo.

if %ERRORLEVEL% LEQ 0 goto ok_result

if %ERRORLEVEL% LEQ 1 goto error1

if %ERRORLEVEL% LEQ 103 goto error103

echo Произошла ошибка N%ERRORLEVEL%

goto the_end

:errorCST

echo Не найден файл CST.exe

goto the_end

Отладка проекта CoDeSys

ПО CoDeSys располагает несколькими инструментами отладки проекта. Их краткие описания приведены ниже.

В режиме отладки ПЛК работает существенно медленнее. Из-за этого возможны прерывания связи по таймауту. Поэтому при отладке следует перенастроить таймауты в окне настройки, вызываемом командой «Проект | Опции (Project | Options)» главного меню ПО CoDeSys: на вкладке «Рабочий стол (Desktop)», значение параметра «Таймаут связи (Communication timeout)» увеличить до 15000.

Опция «Отладка»

Опция отладки ПО CoDeSys заставляет компилятор формировать дополнительный код, упрощающий поиск ошибок. Опция «Отладочный код (Debugging)» включается установкой флажка переключателя «Отладочный код (Debugging)» в окне «Опции (Options)», вызываемом командой «Проект | Опции (Project | Options)» главного меню, на вкладке «Генератор кода (Build)» (см. рисунок 1).

Рисунок 1 – Окно «Опции (Options)» вкладка «Генератор кода»

 Точки останова

Точки останова – это места, в которых выполнение программы будет приостанавливаться, что позволяет просмотреть значения переменных на определенном этапе работы программы. Точки останова можно задавать во всех редакторах. В текстовом редакторе точка останова устанавливается на номер строки, в FBD и LD – на графический элемент, в SFC – на шаг.

Система исполнения CoDeSys SP32 Bit Full автоматически деактивирует сторожевой таймер задачи, если она выходит на точку останова.

Пошаговое выполнение

Пошаговое выполнение позволяет проверить логическую правильность программы. Под «шагом» подразумевается:

  • IL: Выполнить программу до следующего оператора CAL, LD or JMP.
  • ST: Выполнить следующую инструкцию.
  • FBD, LD: Выполнить следующую цепь.
  • SFC: Продолжить действие до следующего шага.

Выполнение по циклам

Команда «Онлайн | Один цикл (Online | Single Cycle)» выполняет один рабочий цикл и останавливает контроллер после выполнения.

Эмуляция

Режим эмуляции последовательно включается и отключается выбором команды «Онлайн | Режим эмуляции (Online | Emulation)» главного мню. Включенный режим маркируется устновленным флажком в строке главного меню и записью «Эмул.» в строке состояния главного окна. Во время эмуляции созданная программа выполняется не в ПЛК, а в компьютере, на котором запущено ПО CoDeSys. В этом режиме допустимы все функции онлайн, что позволяет проверить логическую правильность программ, не используя контроллер.

В режиме эмуляции функции внешних библиотек не выполняются.

Бортжурнал (Log)

«Бортжурнал (Log)» хронологически записывает действия пользователя, внутренние сообщения системы исполнения, изменения состояния и исключения в режиме онлайн. Это позволяет анализировать условия возникновения ошибки при отладке программы. Просмотр записей «Бортжурнала (Log)» производится в режиме, вызываемом выбором строки «Бортжурнал (Log)» дерева ресурсов проекта на вкладке «Ресурсы» организатора объектов CoDeSys.

Сложные типы данных в CoDeSys 2.3

Массивы

Элементарные типы данных могут образовывать одно-, двух-, и трехмерные массивы. Массивы могут быть объявлены в разделе объявлений POU или в списке глобальных переменных.

Путем вложения массивов можно получить многомерные массивы, но не более 9-мерных («ARRAY[0..2] OF ARRAY[0..3] OF …»).

Синтаксис:

<Имя_массива>:ARRAY [<ll1>..<ul1>,<ll2>..<ul2>] OF <базовый тип>

Здесь ll1, ll2, ll3 указывают нижний предел индексов; ul1, ul2 и ul3 указывают верхние пределы. Индексы должны быть целого типа. Нельзя использовать отрицательные индексы.

Перечисления

Перечисление – это определяемый пользователем тип данных, задающий несколько строковых псевдонимов для числовых констант.

Перечисление доступно в любой части проекта, даже при локальном его объявлении внутри POU. Поэтому рационально создавать все перечисления на вкладке «Типы данных» Организатора Объектов. Объявление должно начинаться с ключевого слова TYPE и заканчиваться строкой END_TYPE.

Синтаксис:

TYPE <Имя_перечисления>:(<Элемент_0> ,< Элемент _1>, …, < Элемент_n>); END_TYPE

Переменная типа <Имя_перечисления> может принимать только перечисленные значения. При инициализации переменная получает первое значение из заданного списка. Если числовые значения элементов перечисления не указаны явно, то им присваиваются последовательно возрастающие числа, начиная с 0. Фактически элемент перечисления – это число типа INT, и работать с ними можно точно так же.

Можно напрямую присвоить число переменной типа перечисление. Элемент, уже включенный в перечисление, нельзя повторно включать в другое перечисление.

Структуры

Структуры создаются командой «Добавить объект (Add Object)» контекстного меню вкладки «Типы данных» Организатора Объектов. Новый объект отображается в дереве объектов, окно задания параметров объекта открывается в рабочей области главного окна ПО CoDeSys (см. рисунок 1).

Объявление должно начинаться с ключевых слов TYPE и STRUCT и заканчиваться строками END_STRUCT и END_TYPE.

Синтаксис:

TYPE <Имя _структуры>:

STRUCT

<Объявление переменной 1>

.

.

<Объявление переменной n>

END_STRUCT

END_TYPE

<Имя _структуры> образует новый тип данных, который может быть использован в любой части проекта наряду с базовыми типами.

Рисунок 1 – Вкладка «Типы данных»

Допускаются вложенные структуры, но запрещено размещение элементов структуры по прямым адресам (в частности, недопустимы AT объявления). Для доступа к элементам структуры используется следующий синтаксис:

<Имя_структуры>.<Имя_компонента>

Например, если структура «Week» содержит компонент «Monday», то обращение к нему будет выглядеть так: Week.Monday.

Указатели

Указатели позволяют работать с адресами переменных или функциональных блоков.

Синтаксис:

<Имя_указателя>: POINTER TO <Тип данных/Функциональный блок>;

Указатели применимы для всех базовых типов данных или функциональных блоков, включая определяемые пользователем.

Неявные переменные SFC

Есть 2 основные неявные переменные в стандартном SFC. Это признак активности шага “x” и время его работы “t”. Если заглянуть глубже, то можно заметить еще две переменные с аналогичными названиями, но начинающиеся с символа подчеркивания. Каждое МЭК действие имеет в CoDeSys аналогичный набор переменных плюс свой экземпляр управляющей структуры SFCActionControl

Рис.1 Дерево управляющих структур SFC исполнителя

В упрощенном SFC на каждый шаг неявно объявляется логическая переменная, отвечающая за активность шага. Ее имя совпадает с названием шага: <StepName>. Еще одна переменная имеет имя, начинающееся с символа подчеркивания: <_StepName> . Ее значение изменяется на один цикл вызова раньше. Например, когда шаг получает активность, переменная <_StepName> сразу приобретает значение TRUE, а <StepName> повторяет ее с запаздыванием. Аналогично при деактивации шага. Комбинация значений этих переменных образует четыре возможных состояния шага: пассивен, активация, активен, деактивация (рис. 2). Принудительно задав соответствующую комбинацию при отладке, мы можем выполнить входное, основное или выходное действие. Как вы могли заметить, устройство исполнителя упрощенного SFC существенно проще. Переменные времени шагов и структуры управления действиями для работы упрощенного SFC не нужны.

Рис.2 Диаграмма состояний шага

Но если включить механизм контроля времени выполнения шага, описанный ниже, то CoDeSys все же создает внутреннюю переменную для отсчета времени его активности. Имя переменной образуется так: _time<StepName>.

По умолчанию она не видна. Чтобы получить к ней доступ, ее нужно объявить. Для шага Run_mode она будет выглядеть так: _timeRun_mode : TIME.

Переменные с символом подчеркивания используются для работы внутренних механизмов системы исполнения. Тем не менее, доступ к ним не закрыт. Опытные программисты могут использовать их в отладочных целях.

В стандартном SFC переменные “t” содержат длительность активного состояния. Переменные “_t” хранят абсолютное время момента активации. Это необходимо для поддержки механизма отложенных действий. Как мы уже отмечали, в стандартном SFC переменные “x” можно использовать в программах для целей синхронизации перехо-

дов. В упрощенном SFC для этого служат переменные <StepName>.

Вы можете выполнять графическую трассировку неявных переменных SFC с целью динамического анализа работы диаграммы. Встроенная трассировка CoDeSys позволяет отслеживать изменения активности шагов и действий по рабочим циклам синхронно с выполнением.

В диагностических целях можно изменять неявные переменные либо средствами отладчика, либо программно. Однако не используйте неявные переменные на запись в рабочих программах. Это нарушает связь реального хода выполнения шагов с визуальным отображением на диаграмме. Таким образом, SFC диаграмма теряет смысл. Грамотно реализованный программный компонент на SFC может иметь несколько уровней вложений. Отдельные действия могут переставлять собой вложенные диаграммы. Компонент может вызывать другие POU. Но в любом случае последовательность работы компонента должна выражаться средствами языка SFC без применения трюков с неявными переменными.

Корпоративная многозадачность в CoDeSys

Допустим, нам нужно управлять тремя устройствами. Можно бы поставить три отдельных ПЛК, но устройства несложные и вполне можно попробовать обойтись одним контроллером. В таком случае, возникает логичная мысль не сваливать весь программный код в одну кучу, а разделить его на три программы.

Теперь представьте себе, что мы имеем обычный однозадачный ПЛК с классическим рабочим циклом: 1)чтение входов, 2) вызов прикладной программы, 3) запись выходов. Мы можем поступить очень просто:

написать 3 программы и добавить их вызовы один за другим в главную прикладную программу:

PRG_A;

PRG_B;

PRG_C.

Вполне работоспособное решение. Но может оказаться, что общий цикл стал слишком медленным для PRG_A, хотя PRG_C можно бы, напротив, вызывать значительно реже. Тоже не проблема. Запрограммируем в главной программе некие счетчики, так чтобы каждая программа вызывалась не в каждом цикле, а только через определенное число циклов (например, 1 раз на 10 рабочих циклов). Изменяя параметры этих счетчиков, мы можем настраивать интервалы вызова наших программ. По сути дела мы реализовали три параллельно работающих задачи. Естественно, что в один момент времени работает только одна из них, но с позиции наблюдателя они работают параллельно.

Конечно, не очень удобно возиться со счетчиками. Это лишняя работа, лучше бы поручить ее системе исполнения. Собственно это и достигается с помощью конфигуратора задач CoDeSys.

Описанный выше механизм называется корпоративной многозадачностью либо многозадачностью без вытеснения. Корпоративность обозначает тот факт, что для нормальной работы всей системы, задачи должны сотрудничать между собой. Очевидно, что если одна из задач станет выполнять очень длинные вычисления, длиннее чем заданное время рабочего цикла, то она задержит выполнение других задач. Для корректной работы каждая задача не должна содержать длительных вычислений. Их необходимо разбивать на несколько циклов вызова задачи. Например, не использовать поиск в массиве с помощью итераций по циклу FOR, а обрабатывать каждый следующий элемент массива при очередном вызове задачи. В случае ‘зацикливания’ в одной задаче, останавливается все. Каждая задача должна аккуратно отработать свой цикл, прежде чем сможет получить управление другая задача. В этом главный минус корпоративной многозадачности. В каждой задаче нужно не забывать о других задачах и аккуратно избегать длительных вычислений в одном цикле вызова.

С другой стороны, в том, что каждая задача всегда дорабатывает свой цикл и не может быть прервана в произвольный момент, состоит и большой плюс. Мы можем абсолютно спокойно использовать результаты вычислений одной задачи в другой. Почему это так, станет понятно ниже при описании вытесняющей многозадачности. Отсюда следует, что ‘корпоративная’ многозадачность не означает ‘плохая’. Часто она удобнее. В первую очередь тогда, когда задачи должны тесно взаимодействовать между собой. Без всяких ограничений в нескольких задачах можно использовать глобальные переменные любого типа. Все входы ПЛК опрашиваются перед вызовом каждой задачи, после каждой задачи происходит синхронное обновление всех выходов. Каждая задача может работать с произвольными входами/выходами, в том числе может контролировать выходы, изменяемые в другой задаче. Никакой опасности считать с них некие промежуточные, недостоверные значения нет. Практически мы пишем программы для нескольких корпоративных циклических задач точно так, как и в случае с одной задачей. Если в программе допущена серьезная ошибка, приводящая к зацикливанию, то при корпоративной многозадачности контроллер полностью теряет работоспособность. Это равносильно фатальному сбою контроллера и его приходится перезапускать вручную. Преодолеть эту проблему в CoDeSys позволяют программные сторожевые таймеры. Алгоритм их работы предельно прост: если задача не возвращает управление дольше заданного времени, то таймер ‘срабатывает’ и происходит вызов специального обработчика системного события. По умолчанию этот обработчик выполняет перезапуск контроллера. Вы можете заменить его собственной реализацией, например, включить индикацию ошибки или инициализировать зависшую программу. Как писать обработчики системных событий, мы рассмотрим позднее.

Вытесняющая многозадачность

В случае вытесняющей многозадачности более приоритетная задача получает управление в положенное время, даже если менее приоритетные задачи еще не доработали до конца своего цикла. Задача с высоким приоритетом вытесняет задачу с меньшим приоритетом.

Основной плюс состоит в том, что задачи с низким приоритетом не влияют на задачи с высоким приоритетом. Каждая задача может делать сколь угодно длинные вычисления. Она автоматически будет прервана, при необходимости выполнить более приоритетную задачу и затем снова получит управление и продолжит работу с того места, где она была прервана. Таким образом, отпадает нужда думать об обеспечении заданного времени вычислений в рамках одного цикла вызова программы.

При вытесняющей многозадачности система исполнения CoDeSys плотно взаимодействует с операционной системой ПЛК. Каждая прикладная МЭК задача является отдельным потоком ОС. Каждая задача имеет свой собственный рабочий цикл ПЛК. Опрос ‘своих’ входов, вызов ‘своих’ программ, запись ‘своих’ выходов происходит автономно, независимо от остальных задач. При компиляции проекта CoDeSys автоматически распределяет входы/выходы по задачам, в которых они задействованы и передает соответствующую конфигурацию драйверам аппаратуры.

Грубо можно считать, что при вытесняющей многозадачности мы имеем несколько ПЛК в одном корпусе. К сожалению, в CoDeSys V2.3 текст задач нельзя компилировать, перезагружать и отлаживать автономно, не нарушая работу других задач (это возможно в CoDeSys V3).

Проблемы в многозадачных проектах при вытесняющей многозадачности возможны при использовании общих аппаратных ресурсов и глобальных переменных. Представьте себе, что одна задача начала изменять некую глобальную переменную, например, структуру или строку. Пока изменение не полностью закончено, содержимое данной переменной не определено. Теперь представьте, что в этот момент другая задача получает управление и читает или, хуже того, записывает данную переменную. Результат непредсказуем. Ситуация может проявиться один раз после многих дней нормальной работы ПЛК. ‘Поймать’ ее отладчиком не реально. Остается только не допускать. Классическим приемом защитой от такой ситуации является использование семафоров. Одна задача захватывает некий ресурс для себя и взводит семафор, другие задачи ждут ‘зеленый свет’. Семафоры в CoDeSys реализованы в библиотеке SysLibSem.lib. Работа с ними описана в файле SysLibSem_RU.pdf.

Очень важным является правильный выбор приоритетов задач в конфигураторе. Нулевое значение соответствует наивысшему приоритету. Свободная задача в проекте может быть только одна. Она занимает все свободное время процессора. Если присвоить ей высокий приоритет, то никакие менее приоритетные задачи никогда не получат управления. Всегда присваивайте свободной задаче низший приоритет.

Выше мы рассмотрели способы измерения длительности выполнения своих программ. Обратите внимание, что непредвиденные задержки выполнения способны вызывать и некоторые библиотечные функции. Допустим, функция осуществляет запись в файл или запрос данных от устройства по последовательному каналу связи. Очевидно, это требует определенного времени (до нескольких секунд).

Есть два способа реализации функций. При синхронной реализации функция не возвращает управления вплоть до получения результата. Ее использование очень просто – вызываем функцию, получаем результат. Если такие функции сосредоточены в низкоприоритетной задаче, то их задержки не критичны, при вытесняющей многозадачности. Альтернативный вариант – это асинхронная реализация длительных операций. Одна функция начинает операцию и моментально возвращает управление. Некоторая отдельная функция проверяет факт окончания операции. Ее нужно вызывать периодически. Задержек в функциях нет, но программа усложняется. Трудно сказать, что удобнее. Поэтому для работы с файлами в CoDeSys есть две библиотеки: SysLibFile с синхронными функциями и SysLibFileAsync с асинхронными функциями.

Альтернативой циклическим задачам является ‘событийный’ вызов задач. Вариант ‘по событию’ в конфигураторе задач позволяет вызвать данную задачу при изменении указанной логической переменной, например, дискретного входа. Такая задача также имеет приоритет и работает с вытеснением или корпоративно, как и циклические задачи.

В некоторых моделях ПЛК существуют устройства, способные генерировать так называемые ‘внешние события’. Например, аппаратные таймеры, счетчики, компараторы и др. Вызов такой задачи может инициироваться аппаратным прерыванием.

Все отладочные средства CoDeSys доступны и в многозадачных проектах. Тонкость состоит в том, что для пошагового выполнения и контроля стека вызовов, CoDeSys нужно знать какую именно задачу мы отлаживаем. Не забудьте в конфигурации задач выбрать нужную и в контекстном меню дать команду ‘Отлаживать эту задачу’.

Мы детально рассмотрели отличия корпоративной и вытесняющей многозадачности. Вытесняющая многозадачность реализована в ПЛК с CoDeSys, выполненных на базе 32-х разрядных процессоров с многозадачной ОС. Во всех остальных случаях в системе исполнения реализуется корпоративная многозадачность.

Как точно проверить, что в имеющемся у нас ПЛК реализована вытесняющая или корпоративная многозадачность? Давайте для практики напишем тестер качества реализации многозадачности. Если вы сможете самостоятельно придумать, как написать такую программу в CoDeSys, то это означает, что вы являетесь пользователем высшей квалификации и можете смело браться за любые задачи с многозадачностью. В противном случае, следуйте за изложением ниже. Начнем с создания классической простейшей программы для ПЛК, инкрементирующей целую глобальную переменную при каждом вызове. Например, так:

g_udiQuickCounter := g_udiQuickCounter + 1;

Свяжем ее с циклической задачей названной QuickTask. Дадим ей высокий приоритет 5. Пусть она вызывается с интервалом в 2 мс.

Рис 1. Задача QuickTask

Теперь делаем вторую ‘вредную’ программу. Она будет содержать аналогичный счетчик и делать некие сложные математические вычисления в цикле. Никакого смысла кроме траты процессорного времени в этих вычислениях нет:

g_udiMainCounter := g_udiMainCounter + 1;

FOR iActIndex := 0 TO iMaxIndex DO

rArg := (2 * rPI * (iActIndex MOD 360)) / 360.0;

rRes := 40 * SIN(rArg / (2 * rPI)) + 40;

END_FOR

Ее мы поместим в другую циклическую задачу с меньшим приоритетом 10 и интервалом 10мс. Вредность этой программы заключена в том, что она будет тратить на свои бессмысленные вычисления 8мс, оставляя’окно’ 2мс. При корпоративной многозадачности в это окно будет вписываться ровно один вызов первой задачи. Мы увидим, что оба счетчика меняются одинакового, несмотря на заданные разные интервалы задач.

Соотношение счетчиков должно быть один к одному. При качественной вытесняющей многозадачности, более приоритетная задача будет вызываться четко в пять раз чаще. Соотношение счетчиков должно быть пять к одному. Отклонения от этих идеальных величин возможны, если выполнение системных сервисов (например, обслуживание сети) вызовет нарушение точности выдержки интервалов прикладных задач. Подстраивать время вычислений нашей ‘вредной’ программы мы можем, изменяя число итераций цикла iMaxIndex. Конечно, его можно просто подобрать вручную под быстродействие испытуемого ПЛК, дабы вычисления занимали 8мс. Но это не технично. Добавим в программу регулятор, который будет автоматически поддерживать заданную длительность цикла вычислений:

TickBase(SystemTime:=SystemTime);

t1 := SystemTime.ulLow;

(* тут стоит цикл вычислений FOR, см выше*)

TickBase(SystemTime:=SystemTime);

t2 := SystemTime.ulLow;

t3 := t2 — t1;

IF t3 < 7950 THEN

iMaxIndex := iMaxIndex + 100;

END_IF

IF t3 > 8050 AND iMaxIndex > 1000 THEN

iMaxIndex := iMaxIndex — 100;

END_IF

Здесь TickBase – это системный таймер CurTime из библиотеки SysLibTime. При объявлении переменная iMaxIndex получает начальное значение 20000. В данном фрагменте она регулируется так, чтобы интервал задачи составлял 8000мкс +-50.

Осталось вычислить соотношение счетчиков:

g_rRatio := UDINT_TO_REAL(g_udiQuickCounter) / UDINT_TO_REAL(g_udiMainCounter);

Полная реализация этой программы и конфигурация задачи показана на рис. 2.

Рис 2.Реализация задачи MainTask

Результаты запуска тестовой программы таковы:

CoDeSys SP RTE: соотношение 4.99. Ничего удивительного, так и должно быть в качественном контроллере с вытесняющей многозадачностью.

Овен ПЛК-150: соотношение 1.00. Проследив трассировкой значение в течении нескольких минут, мы видим стабильную единицу практически без джиттера. Можно сделать вывод, что в данном ПЛК реализована корпоративная многозадачность, причем работает она идеально.

Итак, для использованных нами ПЛК множество задач не стало проблемой.