DK55 работа с файлами, задачами. Шаг за шагом. CDS V2.3

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

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

Для нас, как для конечного пользователя, DK55 имеет встроенную в чип память (диск) и внешнюю в виде SD карты. Все стартовые наборы на всех чипах BECK IPC после запуска по умолчанию видят только встроенную память. Для того чтобы подключить SD карту для всех наборов требуется запустить на исполнение драйвер. Для разных наборов драйвера разные, но все они лежат в одном архиве, который можно скачать у производителя вот тут.(BECK IPC иногда меняет расположение файлов на сервере). В этом архиве есть исходные проекты на С++ всех этих драйверов, которые по желанию можно адаптировать под свои нужды. Интересующий нас драйвер extsd.exe для DK55 находится в папке extdisk_v3.01\EXTSD\image\DB54. Требуется скопировать его во внутреннюю память. Для этого мы включаем контроллер, запускаем CHIPTOOL, обнаруживаем в нем наш контроллер и щелкаем по соответствующей строчке правой клавишей мышки. В появившемся диалоге выбираем FTP и далее указываем параметры по умолчанию login “ftp” и password “ftp” (рис. 1).

img1_2.jpg
Рис 1. FTP настройки CHIPTOOL

Нажимаем Connect и, если до нас никто пароли не менял, то мы попадаем в простенький файловый менеджер, где на одной панели будут диски нашего чипа, на другой - диски нашего ПК (рис. 2).

img2_2.jpg
Рис 2. CHIPTOOL FTP-client

На диске А, а это и есть встроенный диск чипа, мы видим ряд файлов (чипы, поставляющиеся отдельно от комплектов, имеют чистое ПЗУ). Файлы BECK.GIF и CHIP.GIF – обычные иконки, появляющиеся в некоторых диалогах, их можно заменить на логотипы своей фирмы при создании своей системы исполнения. Файл CHIP.INI содержит информацию о сетевых настройках в текстовом формате, которые мы можем менять вручную.

Ниже приведено содержание моего файла:

[IP]
NETMASK=255.255.255.0
ADDRESS=192.168.0.117
DHCP=0

Файл AUTOEXEC.BAT представляет собой пакетный файл команд, выполняемых при старте контроллера. Файл MYRTS.EXE есть ничто иное как система исполнения CoDeSysSP для этого контроллера. Надо не забывать при работе с другими комплектами, что одноименный файл MYRTS.EXE для разных чипов разный. Сюда в корень диска А контроллера нам надо скопировать файл EXTSD.EXE, который сделает доступным SD флешку.

Также на диске А может присутствовать папка TEST. Эта папка создается системой исполнения CoDeSySSP (версия которая идет в комплекте с набором), и туда складываются исполняемые файлы проекта и WEB-визуализации.

После копирования EXTSD.EXE нам надо прописать в AUTOEXEC.BAT его запуск перед запуском системы исполнения, как показано на рис.3. Это можно сделать, нажав правой клавишей мышки на файле AUTOEXEC.BAT и выбрав пункт меню Edit. Файл откроется для редактирования блокнотом (рис. 3). Не обращаем внимания на заголовок окна блокнота – это временное название файла присвоенное FTP-Client-ом. При закрытии и сохранении изменить именно выбранный файл.

img3_2.jpg
Рис 3. Содержание файла AUTOEXEC.BAT

После внесенных корректировок в автозапуск закроем FTP-client и перезагрузим чип кнопкой RESET в левом нижнем углу платы. В окне CHIPTOOL строчка контроллера исчезнет и через несколько секунд появится обратно. Входим на него опять через FTP и в поле Drive со стороны панели контроллера нажимаем стрелочку вниз выпадающего меню. Тут мы видим возможность выбора диска B, которого до сих пор не было. Это и есть результат нашего труда – внешняя SD карта. Теперь мы ее можем использовать в своих целях.
Но это уже следующая часть нашего примера.
Переходим непосредственно к работе с файлами из CoDeSyS. Процесс записи и чтения файла разделяется на три части: открытия файла (для чтения или записи), запись или чтение, закрытие файла. В CoDeSys это можно сделать функциями из состава библиотеки SysLibFile.lib. Функции эти имеют несложные параметры, но при их использовании надо учесть, что реакция системы контроллера на каждую команду (вызов функции) может быть больше времени перехода от одной команды к другой, т.е. Функции - неблокирующие. Они возвращают управление раньше, чем ОС завершит выполнение соответствующей операции. В связи с этим чтобы не было неожиданных проблем с их использованием, по хорошему надо проверять завершение одной функции перед выполнением другой, но для простоты примера мы вызов этих функции разнесем во времени. Как это удобно сделать, зависит от ваших будущих проектов, тут же мы рассмотрим вариант с использованием механизма Задач (task) CoDeSys. Задача будет вызываться циклически через определенное время, и за одно исполнение будет исполнять только одну команду, связанную с работой с файлами.
Для открытия, записи и закрытия файла нам потребуются последовательный вызов функций SysFileOpen, SysFileWrite, SysFileClose. С параметрами этих функций можно познакомиться через справочную систему редактора CoDeSys. Без долгих пояснений приведу логику предлагаемого мной алгоритма записи и чтения в файл. Мы создадим программу, которая будет вызываться раз в 50 мс (время выбирается исходя из ситуации вашего проекта от объема записи и т.д., я взял примерно и с запасом.) Эта программа за один вызов будет выполнять только одну функцию. За три последовательных вызова через 50 мс программа выполнит три выше приведенные функции.
Запускаем Codesys, создаем проект, как в предыдущем примере на языке ST. Тарджет для DK55 у нас уже установлен, который автоматически подцепит нужную библиотеку. Связь с контроллером налажена. Сразу перейдем в окно объявлений глобальных переменных (вкладка Ресурсы->Глобальные переменные-> Глобальные_переменные) и запишем там вот это:

VAR_GLOBAL
StatusOfWriting,StatusOfReading: INT; (*переменные статуса чтения и записи *)
WriteBuffer1 : STRING[12]:='Hello World!';(*строка для записи в файл*)
ReadBuffer1: STRING[12]; (*Строка для чтения из файла*)
END_VAR

Эти переменные мы можем изменять из любого места нашего проекта, тем самым инициировать запись или чтение.
Переходим во вкладку POUs. Правой клавишей мышки на пустом поле добавляем объект – программу на языке ST, назовем ее WriteFile. Содержание программы такое:

VAR
f:DWORD;
pChar: POINTER TO BYTE;
sizebuf:INT;
statusf:DWORD;
END_VAR

CASE StatusOfWriting OF
1: f:=SysFileOpen('b:\myfile.txt','w'); (*Открываем файл для записи*)
pChar:=ADR(WriteBuffer1); (*получаем адрес начала строки*)
sizebuf:=SIZEOF(WriteBuffer1);(*получаем размер строки*)
StatusOfWriting:=2; (*приготовились к следующему действию в следующем цикле*)

2: statusf:=SysFileWrite(f,pChar,sizebuf);(*запись в файл*)
StatusOfWriting:=3; (*приготовились к следующему действию в следующем цикле*)

3: SysFileClose(f); (*закрываем файл*)
StatusOfWriting:=0; (*состояние - запись окончена*)

END_CASE

Программа записи в файл готова, теперь ее надо вызывать циклически через каждые 50 мс.
Для этого переходим во вкладке Ресурсы->Конфигуратор Задач, в появившемся окне слева в любом месте правой клавишей мышки добавляем задачу и называем ее taskWriteFile. Ставим галочку Циклически и в поле интервал прописываем T#50ms. Теперь щелкаем мышкой в свободном пространстве в левой части окна, наша задача будет переименована соответственно. Правой клавишей мышки на этой задаче добавляем Вызов Программы. После этого справа в поле “Вызов Программы”, воспользовавшись рядом стоящей кнопкой, указываем имя программы WriteFile.

Надо не забывать, что при активации механизма задач в CoDeSys программа PLC_PRG перестает быть обязательной и вызываться не будет, пока мы ее не вызовем искусственно. Сделаем это аналогичным WriteFile образом при помощи отдельной задачи (рис. 4).

img4_2.jpg
Рис 4. Конфигурация задач.

Обратите внимание на время вызова T#300ms программы PLC_PRG. Для чего установлен такой период, узнаем ниже. Сейчас у нас есть программа, которая может записывать в файл строку, и которая вызывается каждые 50 ms. Но (смотрим текст программы)записывать программа начнет только в случае, если StatusOfWriting получит значение 1. В этом случае программа начнет отрабатывать в каждом цикле по одному шагу конструкции CASE и в конце сбросит StatusOfWriting в 0, что будет означать конец процедуры записи. В нулевом состоянии StatusOfWriting, как и во всех остальных состояниях кроме 1,2,3, программа WriteFile будет вызываться в «холостую».

Чтение от записи отличается только названием функций. Сделаем программу для чтения файла ReadFile и проведем все те же действия с задачами с тем же временным интервалом T#50ms.

PROGRAM ReadFile
VAR
f:DWORD;
pChar: POINTER TO BYTE;
sizebuf:INT;
statusf:DWORD;

END_VAR

CASE StatusOfReading OF

1: f:=SysFileOpen('b:\myfile.txt','r');
pChar:=ADR(ReadBuffer1);
sizebuf:=SIZEOF(ReadBuffer1);
StatusOfReading:=2;
2: statusf:=SysFileRead(f,pChar,sizebuf);
StatusOfReading:=3;
3: SysFileClose(f);
StatusOfReading:=0;

END_CASE

Конечно, в нашем случае можно было заставить одну задачу вызывать обе программы и на запись, и на чтение, но в учебных целях, разведем их по разным задачам, чтобы показать некоторые проблемы с такого рода многозадачностью.
В итоге мы получаем 3 программы. Одна для записи WriteFile, вторая для чтения ReadFile и третья пока пустая PLC_PRG. Каждая программа запускается со своим периодом в отдельных задачах.

Чтобы программы записи и чтения выполнили свои функции, надо соответствующим переменным StatusOfWriting и StatusOfReading присвоить значения 1. Это можно сделать из PLC_PRG. Но представьте, если программа чтения будет вызвана со статусом на чтение файла в тот момент, когда программа WriteFile еще не закончила свое действие с этим файлом, у нас получится неожиданная ошибка. При работе в нескольких задачах с общими ресурсами надо быть очень внимательным с последовательностью использования этих общих ресурсов. Хорошо бы применить методы защитного программирования, но так как наш пример далек от этих методов, мы применим следующих ход. В одном цикле PLC_PRG мы установим параметр StatusOfWriting:=1; во втором цикле StatusOfReading:=1; Сделаем интервал вызова PLC_PRG достаточно большой, чтобы программы записи и чтения в этот интервал могли выполнить свои действия до конца. 3 этапа записи или чтения по 50мс каждый это 150 мс, добавим двукратный запас, получаем 300 мс, на что мы ссылались выше (рис. 4). Повторю, все интервалы можно рассчитать точнее, исходя из конкретной задачи. Реализуем PLC_PRG :

PROGRAM PLC_PRG
VAR
i, i1: BOOL;
x: INT;
END_VAR

x := x+1;
IF i1 THEN StatusOfReading:=1; i1:= FALSE; END_IF
IF NOT i THEN StatusOfWriting:=1; i:=TRUE; i1:=TRUE; END_IF

Используя два флага i и i1 получаем в первом цикле сработку нижней строки (произойдет запись) во втором цикле сработку строки выше (произойдет чтение). Во всех циклах производим инкремент переменной x для наблюдения работы процесса.
Все. Проект готов. Подключаемся к контроллеру, и запускаем программу (рис. 5).

img5_2.jpg
Рис. 5 Программа в действии.

Видим по растущему значению x вызовы нашей PLC_PRG. На рисунке 5 137-й цикл. В первом цикле (и только в первом) была произведена запись в файл переменной WriteBuffer1, во втором чтение в переменную ReadBuffer1. В остальных циклах идет только инкремент X. Результат можно увидеть перейдя в окно глобальных переменных. Значение WriteBuffer1 будет присвоено ReadBuffer1.
Если мы с помощью CHIPTOOL зайдем и посмотрим диск B, то обнаружим там файл с соответствующим содержанием, размером 13 байт. Фраза Hello World! имеет 12 байт. 13-й байт –терминальный 0, конец строки, который мы не видим.

Обсуждение на форуме