16 KiB
profinet_io_dev
Драйвер profinet io device. Предназначен для работы в фоне и взаимодействием с Profinet контроллером с использованием ethernet.
Реализует следующий функционал:
- Читает из json файла структуру данных Profinet,
- Конфигурирует и запускает циклический обмен по Profinet,
- Принимает циклические данные от контроллера Profinet и записывает их в разделяемую память.
- Берет циклические данные из разделяемой памяти и передает их контроллеру Profinet
Использует следующие библиотеки:
- pnet
- boost (1.74)
Структура проекта:
- debug_support - содержит скрипт для запуска отладки с правами суперпользователя и обновления gdb (нужно для Ubuntu 22.04, там есть баг с gdb - он падает при переключении на другой поток в отлажеваемом ПО),
- libs - собранные библиотеки pnet и используемая ей osal,
- profinet_stack - исходники библиотеки pnet,
- profinet_test - тестовые проекты. Наиболее интересен проект в папке sample_app_echo реализует функционал тестового модуля ECHO через взаимодействие с profinet_io_dev и подключение к разделяемой памяти.
- src - исходный код приложения profinet_io_dev.
Общее описание данных Profinet
Терминалогия принятая в Profinet:
- Выходные данные - данные от контроллера к устройству,
- Входные данные - данные от устройства к контроллеру.
Профинет предоставляет абстракцию типичной системы управления на базе ПЛК, который имеет универсальные слоты в которые подключаются специализированные модули, например CAN, RS-422, аналоговых входов и т.д. В терминах Profinet все тоже самое: есть контроллер у которого есть слоты в которые он при установлении связи подключает модули устройства.
На стороне контроллера имеется один или несколько слотов(slots), каждый из которых имеет один или несколько подслотов(subslots).
На стороне устройства имеется один или несколько модулей(modules), каждый из которых имеет один или несколько подмодулей(submodules).
К каждому слоту на стороне контроллера может быть поключен модуль со стороны устройства.
К каждому подслоту на стороне контроллера может быть подключен подмодуль со стороны устройства.
Каждый подмодуль может включать:
- Выходные циклические данные (от контроллера к устройству)
- Входные циклические данные (от устройства к контроллеру)
- один или больше параметров.
Могут быть подмодули не содержащие данных вообще.
Параметры подмодуля отличаются от циклических данных тем, что записываются контроллером при установлении соединения, перед началом циклического обмена и больше не меняются. Их может изменить только пользователь, предварительно разорвав связь (по крайней мере в CodeSys так).
Параметры могут использоваться устройством для расчета входных данных для контроллера.
Описание конфигурации данных Profinet определяется специальным gsdml файлом, который на самом деле является xml файлом содержимое которого описано в спецификации Profinet. Этот файл нужен для всяких СКАДА систем при конфигурировании контроллера Profinet, загружаешь в СКАДА этот файл и в нем появляются Profinet устройство описанное в этом файле, а так-же все его модули, подмодули и параметры.
Идентификация модулей и подмодулей осуществляется с помощью 32-х битного идентификатора. Идентификация параметра осуществляется с помощью 32-х битного индекса.
Установление связи выглядит следующим образом:
- Мастер запрашивает соединение,
- Дает команду устройству: модуль с идентификатором A подключить к слоту с номером B.
- Дает команду устройству: подмодуль с идентификатором A входящий в состав модуля с идентификатором B подключить к подслоту с номером C слота с номером D.
- Записывает параметры подключенного подмодуля.
- Начинается обмен циклическими данными.
Все данные в Profinet пишутся в порядке big endian.
Циклический обмен
Циклический обмен инициируется устройством, т.е. устройство должно с каким-либо периодом отправлять данные контроллеру.
Файл конфигурации program_configure.json
Описывает общие настройки ПО.
ProfinetSettings:
- ticks_us - период таймера в мкс который вызывает pnet и реализует логику обмена.
- cyclic_ms - период обмена циклическими данными.
- eth_dev_name - наименование устройства ethernet в Linux через которое будет идти обмен по Profinet.
- profinet_device_config - путь к json файлу конфигурации устройства, который описывает структуру модулей и подмодулей.
Файл конфигурации устройства Profinet
Описывает структуру модулей и подмодулей. В качетсве примера можно рассмотреть описание тестового модуля ECHO: profinet_device_configure.json.
ProfinetDeviceSettings
Содержить часть параметров структуры pnet_cfg из pnet_api.h. Это стандартные параметры Profinet которые указаны и описаны в спецификации. Например имя станции или имя устройства.
ProfinetDeviceModulesSettings
Описание структуры данных устройства.
- modules
- id - идентификатор модуля в шеснадцатиричном или десятичном виде. Шеснадцатиричный вид обозначется как в С через 0х.
- name - имя модуля, в обмене не используется, нужно для удобства.
- submodules - описание подмодулей:
- id - идентификатор подмодуля,
- name - имя подмодуля. В обмене не используется, нужно для удобства.
- cyc_data_dir - направление циклических данных. Может принимать значения:
- PNET_DIR_NO_IO - нет циклических данных,
- PNET_DIR_IO - входные и выходные циклические данные,
- PNET_DIR_INPUT - только входные циклические данные,
- PNET_DIR_OUTPUT - только выходные циклические данные.
- cyc_inp_data_size - размер входных циклических данных в байтах,
- cyc_out_data_size - размер выходных циклических данных в байтах,
- parameters - описание параметров:
- index - индекс параметра,
- name - наименование параметра. В обмене не используется, нужно для удобства.
- data_size - размер данных параметра в байтах.
Межпроцессное взаимодействие
Разделяемая память
При запуске ПО выделяет блок разделяемой памяти с наименованием "profinet_shared_data".
Структура описана в profinet_data_map.hpp.
API для работы с памятью приведено в файле: profinet_shared_data_client.hpp
Пример подключения к памяти:
ProfinetSharedDataClient shared_data;
ProfinetData_Map * p_profinet_data = shared_data.Connect("profinet_shared_data");
/// Берем указатель на данные подмодуля в разделяемой памяти
auto echo_submod_ptr = shared_data.getSubmodule(ECHO_MODULE_ID, ECHO_SUBMOD_ID);
/// Берем указатель на данные параметра подмодуля в разделяемолй памяти
auto echo_param_gain_ptr = shared_data.getSubmoduleParameter(ECHO_MODULE_ID, ECHO_SUBMOD_ID, ECHO_PARAMETER_GAIN_IDX);
Пример ожидания наступления события
В структуре ProfinetData_Map
есть разделяемые флаги Events
типа SharedFlags
которые могут быть использованы для ожидания следующих событий:
EVENT_CONNECTION_ESTABLISHED
- соединение установленоEVENT_NEW_CYCLIC_DATA
- получены новые циклические данные. Не указывает на то, что данные изменились, просто по профинету была получена новая порция цикличесиких данных от контроллера.EVENT_NEW_PARAM_DATA
- контроллером была осуществлена запись данных параметров какого-либо подмодуля.EVENT_CONNECTION_ABORT
- соединение с контроллером разорвано.
Пример ожидания установления соединения или приема новых циклических данных:
uint32_t events = p_profinet_data->Events.wait_flags(EVENT_CONNECTION_ESTABLISHED | EVENT_NEW_CYCLIC_DATA);
wait_flags
- блокирует поток до момента установления флага.
Когда флаг установлен, его нужно сбросить:
p_profinet_data->Events.clear_flags(EVENT_NEW_CYCLIC_DATA);
Пример чтения данных параметров подмодуля
uint32_t Echo_Gain = 0;
echo_param_gain_ptr->data.Read(0, (uint8_t*)&Echo_Gain, echo_param_gain_ptr->length);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_Gain);
Пример чтения и записи циклических данных подмодуля
///Читаем данные от ПЛК
echo_submod_ptr->out_data.Read(0, Echo_outCycData.mem, echo_submod_ptr->cyc_outdata_len);
/// Конвертируем в литл эндиан
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_outCycData.data.data_f);
///Подготавливаем данные для ПЛК
Echo_inpCycData.data.data_i = Echo_Gain * Echo_outCycData.data.data_i;
Echo_inpCycData.data.data_f = Echo_Gain * Echo_outCycData.data.data_f;
///Конвертируем в биг эндиан
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_i);
endian_convert_32((uint8_t*)&Echo_inpCycData.data.data_f);
///Отправляем данные для ПЛК
echo_submod_ptr->inp_data.Write(0, Echo_inpCycData.mem, echo_submod_ptr->cyc_indata_len);
pipe каналы
Обмен происходит по двум каналам, по умолчанию это:
- profinet_io_dev_out
- profinet_io_dev_inp
Путь к аналам задается в конфигурационном файле program_configure.json
.
ПО сначала открывает канал для чтения(inp), затем ожидает подключения клиента, после чего открывает канал для записи(out).
Обмен происходит с помощью json rpc, протокол описан в файле src/interprocess/pipes/json_rpc_protocol.txt
.
Инструкция по сборке
ПО кроссплатформенное, настройка окружения сборки осущетсвляется в файле CMakePresets.json
.
Сборка под x86_64
Выбрать конфигурацию "x86_64 Debug" или "x86_64 Release"
Сборка под arm cortex-a15 (sitara)
Перед сборкой отредактировать значения переменных окружения:
-
ENV_TARGET_CROSS_COMPILE_PREFIX - префикс компилятора с полным путем к нему. Например:
/home/svad/ti/ti-processor-sdk-linux-am57xx-evm-08_02_01_00/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/arm-none-linux-gnueabihf-
-
ENV_TARGET_SYSTOOT_PATH - путь к библиотекам целефой платформы. Для платы am571x-idk - это:
/home/svad/ti/ti-processor-sdk-linux-am57xx-evm-08_02_01_00/linux-devkit/sysroots/armv7at2hf-neon-linux-gnueabi
SYSROOT можно выкачать из платы напрямую. Например для BeagleBoardAI есть скрипт
../sitara_depot/scripts/beagleboard/host/create_sysroot.sh
Зависимости
Две библиотеки:
- osal
http://server_gorbunov:3000/SmartForce4.0/profinet_io_dev/src/branch/master/profinet_stack/osal
- pnet
http://server_gorbunov:3000/SmartForce4.0/profinet_io_dev/src/branch/master/profinet_stack/p-net
Библиотеки уже собраны под две архитектуры cortex-a15 и x86_64. Сбори лежат тут:
http://server_gorbunov:3000/SmartForce4.0/profinet_io_dev/src/branch/master/libs/lib/arm_a15
http://server_gorbunov:3000/SmartForce4.0/profinet_io_dev/src/branch/master/libs/lib/x86_64
В названии файла d - значит дебажная версия библиотеки, например libosal.a - Release, а libosald.a - Debug.
Сборка библиоитек реализована аналогично.