Давно хотел рассказать про тонкости программирования обмена по протоколу Modbus RTU в случае, когда контроллер (в нашем случае S7-1214) выступает RTU Master'ом. Недавно меня попросили помочь с обменом между ПЛК и частотным преобразователем Sinamics V20, ну и почему бы не написать заодно заметку, постаравшись приблизить решение задачи к боевым условиям.
Собственно говоря, сами немцы эту тему давно осветили:
SINAMICS V: Speed Control of a V20 with S7-1200 (TIA Portal) via USS® protocol/MODBUS RTU with HMI
Смотрите этот пример, он сделан очень толково, с визуализацией, диалогами и квестами и возможностью расширить прикладную программу до опроса множества ПЧ V20 по нескольким интерфейсам (S7-1200 позволяет установить в свою корзину до 4 портов RS-485/422). Пример сделан очень хорошо и очень педантично. Вопросов коммуникаций по протоколу Modbus TCP я уже касался ранее, они есть на Хабре.
Поэтому некоторые нюансы я повторно объяснять не буду, просто сразу напишу, как стоит делать правильно и удобно с моей точки зрения конкретно в случае опроса V20. Первоначальная настройка преобразователя частоты описана в документации, в том числе и в сопутствующей документации к вышеуказанному примеру. Вынесем лишь важные для нас пункты в качестве вводных.
Адрес подчиненного устройства модбас в сети: 1
Параметры связи: 9600 8-Even-1
Регистры хранения подчиненного устройства для чтения:
40110 ZSW «Слово состояния»
40111 HIW «Текущая скорость»
Регистры хранения для записи:
40100 STW «Слово управления»
40101 HSW «Задание скорости»
Параметр частотника «Telegram off time (ms)» P2014[0] рекомендую оставить по умолчанию, равным в 2000 мс (2 секунды), хоть пример и рекомендует снизить эту величину до 130 мс. Конкретно к протоколу Modbus это замечание не относится, разумеется, просто у меня при таймауте в 130 мс, ПЧ терял связь и выдавал ошибку Fault 72.
С частотником разобрались. Теперь о моей конфигурации ПЛК. Это S7-1214 с коммуникационным модулем 1241 под RS-485/422:
Среда программирования Step 7 V15.1 Update 4, версия прошивки CPU — 4.3.
Итак, приступим. Для опроса подчиненных устройств с контроллера Simatic нам необходимо применить два функциональных блока: Modbus_Comm_Load (единовременно, только для конфигурации коммуникационного процессора) и Modbus_Master (циклически для чтения и/или записи регистров/катушек). Поэтому в программе экземпляр FB Modbus_Comm_Load у нас будет встречаться только один раз, а экземпляр Modbus_Master — несколько раз, но с разными входными параметрами, в зависимости от адреса подчиненного устройства, типа читаемых данных и их количества, а так же направления передачи данных (чтение или запись). Обращаю ваше внимание, что для одного коммуникационного процессора (а их в системе может быть очень много) у вас не может быть больше одного экземпляра каждого блока данных.
С моей точки зрения весь обмен удобнее завернуть в один внешний функциональный блок, а сам блок, с учетом необходимости разбирать данные, реализовать на текстовом языке SCL. Поэтому создаем в проекте функциональный блок с именем ModbusMasterV20 на языке SCL. Сразу после создания открываем его свойства и снимаем настройку «оптимизированный доступ», т.е. используем стандартный доступ. Личный опыт показал, что использование оптимизированного доступа рано или поздно приведет к ошибкам работы блока Modbus_Master и невозможности обмена. Это связано с порядком, в котором переменные идут в объявленной структуре данных, при стандартном доступе порядок соответствует заданному в программе, при оптимизированном — система сама «раскидывает» переменные, как сочтет нужным.
Объявляем следующие входные переменные
Init (Bool) — инициализация коммуникационного процессора, ее необходимо выполнить один раз перед началом обмена
PORT (PORT) — аппаратный идентификатор коммуникационного процессора
BAUD (UDINT) — скорость обмена по порту
STOP_BITS (USINT) — количество стоповых бит «кадра»
PARITY (USINT) — четность, где 0 — нет четности, 1 — odd, нечет, 2 — even, чет
В статической области переменных так же прописываем переменную с именем Step и типом UInt, она отвечает за «номер опроса» или «шаг работы алгоритма»
Так же в статической области объявляем экземпляры ФБ для работы по протоколу Modbus RTU
Строки программы, отвечающие за инициализацию обмена.
По флагу инициализации выставляем номер шага 1. Следующие строчки очень важны для работы
#instModbusCommLoad.MODE := 4; //для линии RS-485 должна быть 4!
#instModbusCommLoad.STOPBITS := #STOP_BITS;
Тут мы задаем значения статических переменных экземпляра ФБ Modbus_Comm_Load, которые отвечают за «физику» передачи. Не понимаю, почему немцы поместили эти важные конфигурационные параметры в статическую область, а не в область входов. Дело в том, что они (переменные) все описаны во встроенной справке. Беда лишь в том, что большинство ленивых жоп новичков до этого пункта справку не читает, а потом тратят несколько часов, а то и дней, пока не найдут ответ. А справка-то, вот она:
Переменная MODE отвечает за режим, в котором будет работать коммуникационный процессор. Как видно из справки, для RS-485 надо явно выставить 4. Значение по умолчанию 0, от этого большинство ошибок у программистов.
STOP_BITS — количество стоповых бит.
Далее следует вызов блока настройки коммуникационного интерфейса Modbus_Comm_Load. Про параметр PORT (аппаратный идентификатор) будет рассказано чуть ниже. Параметры BAUD и PARITY — скорость и четность — приходят на вход «внешнего» блока данных, куда мы и завернули весь обмен. А вот параметр MB_DB интересен. На этот вход надо подать структуру типа P2P_MB_BASE, которая находится в области статических переменных экземпляра функционального блока Modbus_Master. Этот экземпляр в нашем «большом» функциональном блоке уже объявлен, привожу скриншот:
Следующая часть: функциональный блок приступает к циклическому обмену.
Я сразу «заворачиваю» обмен в CASE, чтобы не переписывать код в дальнейшем, но пока мы ограничимся чтением слова состояния и скорости ПЧ, т.е. прочитаем два регистра хранения.
Давайте посмотрим на вызов блока Modbus Master повнимательнее:
#instModbusMaster(REQ := TRUE,
MB_ADDR := 1,
MODE := 0,
DATA_ADDR := 40110,
DATA_LEN := 2,
DATA_PTR := #ZSWHIW);
Входной параметр REQ — включить опрос. Пока на входе TRUE, он выполняется, если FALSE — не выполняется. Нет необходимости подавать положительный фронт на этот вход самостоятельно (в отличии от работы Modbus RTU в системах S7-300/S7-400), поэтому я просто даю TRUE константой
MB_ADDR — адрес подчиненного устройства Modbus RTU. В моем случае адрес частотника = 1.
MODE — направление передачи данных, 0 — чтение, 1 — запись
DATA_ADDR — адрес интересуемых нас данных. В моем случае необходимо прочитать два регистра хранения (поэтому первая цифра 4), начиная со 110го. В протоколе Modbus (что RTU, что TCP) очень часто возникает путаница в понятиях «адрес» и «номер». И очень часто производитель оборудования эту путаницу добавляет в свою систему. Вот смотрите. Мы должны прочитать 2 регистра, начиная с адреса 40110. Для чтения регистров хранения в протоколе Modbus используется функция с номером 3. Именно 3 будет передаваться в телеграмме Modbus. А в качестве адреса в телеграмме будет передаваться не 40110, а 109. Связано это с тем, что код функции уже содержит описание области данных. А в самой телеграмме мы передаем не адрес, а номер требуемого регистра или катушки. И эта нумерация идет не с единицы, а с нуля. Сейчас я работаю именно с адресами и режимом (чтении или запись), поэтому мне достаточно указать то, что я нашел в документации. Если же в вашем устройстве будет указано «входной регистр номер 0 содержит текущий статус устройства», то вам на вход DATA_ADDR необходимо будет подать 30001. Так же имейте в виду, что из-за частой путаницы с номерами и адресами, иногда эта адресация съезжает на «единицу», поэтому не бойтесь экспериментировать. Если вместо полезных данных по запросу 16ого регистра вам прилетает полная чехарда, не имеющая ничего общего с документацией, прочитайте 15ый регистр. Не помогло? Опрашивайте 17ый. Более подробно с материалом необходимо ознакомиться опять же во встроенной справке.
DATA_LEN — количество читаемых регистров, их 2
DATA_PTR — указатель на область памяти, куда необходимо «положить» результат чтения регистров. Собственно, это те данные, которые мы прочитали и необходимо подсказать функциональному блоку, куда эти данные надо записать. С моей точки зрения самый удобный способ в этом случае — это объявить в области STAT неименованную структуру должного размера. Поля структуры мы объявляем, в зависимости от самих читаемых данных, ведь это могут быть и наборы бит, и вещественные числа (расположенные в двух соседних регистрах). И нам еще повезет, если порядок байт в слове и слов в двойных словах контроллера и подчиненного устройства совпадут, иначе нам еще потребуется осуществить сдвиги байт/слов.
В данном случае я счел уместным объявить структуру из двух слов и скормить ее на вход FB:
, где
ZSW — слово состояния (так оно называется в документации на ПЧ)
HIW — скорость вращения двигателя
После вызова блока мастера, необходимо проанализировать успех или неуспех его выполнения. В принципе, на этом скриншоте в комментариях уже все написано:
В случае успешного чтения необходимо полученные сырые данные как-то разобрать или переложить в другую область, и перейти к следующему опросу (у нас пока только один опрос, так что мы остаемся на шаге №1). При ошибке чтения данных минимальный разумный ход — выставить где-нибудь флаг недостоверности и перейти к другому опросу.
Пока оставляем прием данных без обработки, компилируем и грузим программу, смотрим на результат. Кстати, обращаю еще внимание на один факт. Поскольку мы работаем, завернув системные вызовы в свой функциональный блок, то любое изменение «своего» ФБ с последующей загрузкой ПЛК, будет нарушать обмен в связи с переинициализацией экземпляра нашего ФБ. Например, будет уходить в ноль значение «шага обмена». Да и внутренние статические переменные коммуникационных вызовов тоже пострадают. Самый простой способ — стоп и старт контроллера. В боевом проекте это опасно, поэтому там на вход Init я бы подал еще одну переменную и поднимал ее самостоятельно после изменений в коммуникациях. Пока же боремся с остановом обмена простым стоп-стартом ПЛК.
Добавляем вызов нашего функционального блока в OB1 и грузим CPU:
Переменная FirstScan имеет значение «истина» при первом цикле выполнения программы OB. Она назначается операционной системой ПЛК автоматически, ее применение настраивается в свойствах CPU.
Port. Это значение смотрим в проекте Step 7, аппаратная конфигурация:
Остальные параметры касаются скорости, четности и количества стоповых бит. Загружаем контроллер и смотрим, что нам приходит в ответ на единственный циклический запрос, открыв экземпляр нашего ФБ:
В слове состояния что-то есть, скорость равна нулю. Открываем документацию и смотрим состав слова состояния ZSW:
Low enabled в примечаниях означает инверсию. К примеру, бит №15, перегрузка частотника, возникает, когда этот бит равен 0, а в нормальном состоянии приходит значение 1. Посмотрим на это слово состояния в watch table и посмотрим, какие его биты выставлены, а какие — нет, оценим общее состояние ПЧ:
Тут нам везет, порядок байт в словах совпадают. Если вкратце, то видно, что ПЧ не готов, не включен, и сейчас активен сигнал аварии (fault, бит №3).
Далее я попытался разложить слово состояния в биты состояния, заменив WORD на структуру из бит, но что-то явно пошло не так.
Если посмотреть внимательно, то в таком представлении нулевой и первый байты явно не на своих местах. В общем, вопрос порядка следования информации в зависимости от того или иного представления — он всегда важный и требует вдумчивости. Получил на этом этапе облом, я решаю вернуться к хранению внутри нашего ФБ только сырых данных, а удобочитаемый для человека формат представления информации перенести куда-нибудь во внешний глобальный блок. Добавляю в проект блок данных DataV20:
После чего задумываюсь, убираю из имен переменных окончание Inv и дописываю функциональный блок:
Теперь в глобальном блоке данных у нас находятся статусные биты преобразователя частоты без какой-либо инверсии:
Думаю, что сразу в блок данных надо вписать переменную типа Real, которая будет содержать текущие обороты двигателя. Текущие обороты приходят от ПЧ в виде определенного численного кода, и мы вольны трактовать этот код, как нам удобнее. Допустим, что хочу трактовать этот код, как Герцы, поданные на двигатель.
Пока не будем пересчитывать коды в физические величины и перейдем к следующему шагу — к записи слова управления и задания частоты. Вернемся к обработке текущей скорости чуть позже.
Обратимся к документации и посмотрим состав слова управления частотным преобразователем:
Знаете, мне, откровенно говоря, лень писать все эти переменные в глобальном интерфейсном блоке данных. В моей практике управление простыми технологическими процессами с применением преобразователей частоты ограничивалось командами включить, квитировать аварию и дать задание скорости. Поэтому, пойдем на поводу моей лени и в блоке данных V20Data пропишем всего лишь бит включить, бит квитировать и задание частоты в формате Real.
Изменю алгоритм на шаге №1, при успешном или неуспешном завершении опроса сделаю переход на шаг №2.
Добавим еще локальную структуру ФБ, которая содержит слово управления и слово задания скорости:
Дорабатываю программу обмена. Не забываем, что при изменении переменных функционального блока, после загрузки изменений в ПЛК происходит его переинициализация, посему надо выполнять стоп/старт CPU.
Параметры функционального блока модбас в данном случае отличаются от первого вызова. Разумеется, у нас тут другой адрес регистра. А так же отличается режим (MODE), он равен 1, так как в данном случае данные не читаются с частотника, а записываются в него. Разумеется, указатель на область данных так же другой.
Обратите внимание, что некоторые биты слова управления я принудительно выставляю в истину, другие — в ложь. Всего два бита управления (включить и квитировать) доступны для внешней программы. Необходимое значение бит управления я вычитал в документации примера. Разумеется, это указано и в документации на сам преобразователь частоты. Изучая исходный пример, я обратил внимание, что если частотнику отдавать «пустое» (все биты выставлены в ноль) слово управления, то это подчиненное устройство модбас возвращает ошибку Invalid data. Однако, в этом примере я пробовал слать полностью «пустое» слово управления, и V20 принимал его. Однако, некоторые биты управления, все равно, должны быть установлены. К примеру, если снять бит «Control by PLC», то запускаться ПЧ не будет. RTFM, как говорится!
Теперь пора перейти к регистру, который отвечает за задание скорости (ну и сразу же к регистру, который отображает текущую скорость). Из исходного примера я понял, что этот регистр меняет свое значение в пределах от 0 до 16384. Это же мельком нашел и в документации. Пока не будем делать никаких переводов величин, и зададим ПЧ максимальную скорость жестко прямо в программном коде.
Откроем наш блок данных DataV20 и выставим команду «пуск»:
V20 запустился и работает, судя по индикации своего экранчика, на максимальной скорости, т.е. на 50 Гц. Давайте посмотрим еще сырые данные его скорости, которые приходит по modbus.
Значит, пришло время доработать шаг №1 обмена (перевести коды скорости в герцы), ну и шаг №2 в части обратного преобразования, герцы в численное значение задания скорости. «Математика» самая простая, без проверок на достоверность и выход за диапазон, хотя все это не помешает.
После загрузки изменений откроем блок данных DataV20 и поуправляем частотником из него.
Даем задание 25 Гц, даем пуск и наблюдаем за появлением сигнала Running и текущей скоростью.
Все регистры, которые можно считать с V20, описаны в документе по ссылке.
Судя по описанию регистров, есть и другие способы управления, но нас сейчас интересует не это. В моей практике с преобразователей частоты еще частенько просили считать ток(и), напряжение(я), мощность, детализацию ошибок и т.д. Давайте последуем этой старой-доброй традиции и считаем дополнительно вот эти параметры, а так же переведем их в понятное представление:
Параметры, разумеется, могут быть любыми, но предположим, что заказчику очень хочется именно эти. Я не буду подробно описывать процесс программирования и сразу покажу результат.
В принципе, мотор маленький, крутится без нагрузки, поэтому значения похожи на достоверные. Тем не менее, задача стоит в демонстрации считывания данных, поэтому будем считать наличие хоть каких-либо «цифры» за огромный технологический прорыв. Итак, вы уже заметили, что я добавил читаемые параметры в блок данных DataV20. Дополнительно был доработан функциональный блок коммуникаций:
Читаются (mode = 0) четыре регистра хранения по адресу 40025. Результат помещается во внутренний статический массив [0..4] of WORD. Далее эти слова переводятся в формат Real и помещаются во внешний блок данных в результате несложных преобразований.
Ну, и напоследок остается проанализировать качество связи. Ведь не зря же на каждом шаге после выполнения ФБ Modbus_Master смотрю его флаги DONE или Error (кстати, эти флаги имеют значение «истина» только на протяжении одного вызова после успешного или неуспешного выполнения запросы, в остальное время — ложь). Для этого я объявил массив из булевых переменных
Массив размерностью три, по количеству запросов Modbus. Соответственно, если на шине будет 10 частотников, по три запроса к каждому, то размерность этого массива, как и количество «шагов» алгоритма, будет равно 30. Ну, и в конце каждого опроса, при анализе флагов, наконец, прописываем присвоение флагам значения.
Будем считать, что частотник стабильно обменивается информацией с ПЛК, когда все три запроса к нему выполнены успешно. Поэтому самая последняя строчка нашего функционального блока будет такой (предварительно добавим булевую переменную Connected в блоке данных DataV20):
DmitrySpb79
Спасибо что поделились. Существует ли к этим контроллерам какая-либо бесплатная среда для их программирования? Валяется один такой без дела, 21-дневный триал давно кончился, платная версия стоит судя по всему недешево.
akcount Автор
Версия Step 7 Basic для программирования контроллеров серии S7-1200 распространяется в РФ безвозмездно, т.е. даром. Свяжитесь с представительством компании Сименс в Вашем регионе.
Atxmega
От представителя физ.лицо не получит должного ответа.
Все, кто работает с Siemens, прекрасно знают о существовании ключей для разработчиков. Они не ограничивают функционал и без ограничения по времени, единственное — их нельзя оставить заказчику. А для личного использования самое то.
P.S. Sim_EKB_Install_2018_11_14
Mitya78
На самом деле достаточно счёта на покупку ПЛК, по нему лицензию высылают.
Atxmega
О, если не секрет? От какой компании получали ключ? Миг, форум, симекс, ЭТМ?
Каталог указывает на цену в 336 евро
STEP 7 Basic V16 6ES7 822-0AA06-0YA5
Mitya78
Если мне вопрос, то ключ я прошу в самом Сименс, раньше у них Разгоева этим занималась, теперь вроде кто-то другой.
Недавно с итальянцами проект запускали, так они спокойно ekb ставят.
Atxmega
Нет ничего криминального в ekb.
Сам Сименс ставит их на ПК заказчику, а по окончанию ПНР передают запечатанную коробку с ключом.
Все понимают что заказчик лицензию оплатил, и не обязательно использовать на АРМ именно купленный ключ. И автоматически уходят проблемы с восстановлением лицензии при выходе из строя АРМ.
akcount Автор
Анна Разгоева теперь стала Анной Чистяковой, а в целом ситуация не изменилась )
akcount Автор
Пишите/звоните в ближайшее местном бюро Сименс