Мой Kbyte.Ru
Рассылка Kbyte.Ru
Группы на Kbyte.Ru
Партнеры Kbyte.Ru
Реклама
Сделано руками
Сделано руками
> Статьи - Ярослав (comexe) Филипченко -

Visual Basic 5.0/6.0 - Железо и устройства

Все статьи / Железо и устройства

Работа с протоколом Modbus RTU в среде Visual Basic 6.0

Автор: Ярослав (comexe) Филипченко | добавлено: 02.07.2013, 14:27 | просмотров: 10940 (5+) | комментариев: 28 | рейтинг: *x9

Введение

 

В современном мире автоматизация технологических процессов является одной из наиважнейших сфер народного хозяйства. Этому способствуют и научно-технический прогресс, и развитие компьютерных и электронных технологий. В статье будет рассказываться о работе с одним из самых популярных протоколов современной автоматизации – Modbus RTU. Среда программирования Visual Basic 6.0 выбрана из нескольких соображений: во-первых, программы, написанные на Visual Basic, просты для восприятия начинающих инженеров; во-вторых, программы, написанные на Visual Basic могут работать на компьютерах даже со слабой производительностью; в-третьих, среда Visual Basic, на мой взгляд, является достаточно функциональной для создания систем контроля и диспетчеризации. Поскольку, в основном, программное обеспечение для работы с промышленной автоматикой (OPC-серверы, HMI-системы) являются платными, большой интерес представляет написание самостоятельного кода для коммерческого применения.

 

Кратко о Modbus

 

Modbus — открытый коммуникационный протокол, основанный на архитектуре «клиент-сервер». Широко применяется в промышленности для организации связи между электронными устройствами. Может использоваться для передачи данных через последовательные линии связи RS-485RS-422RS-232, а также сети TCP/IP (Modbus TCP).

 

Общая структура ADU следующая (в зависимости от реализации, некоторые из полей могут отсутствовать):

 

адрес ведомого (подчинённого) устройства

код функции

данные

блок обнаружения ошибок

 

где

  • адрес ведомого устройства — адрес подчинённого устройства, к которому адресован запрос. Ведомые устройства отвечают только на запросы, поступившие в их адрес. Ответ также начинается с адреса отвечающего ведомого устройства, который может изменяться от 1 до 247. Адрес 0 используется для широковещательной передачи, его распознаёт каждое устройство, адреса в диапазоне 248…255 — зарезервированы;
  • код функции — это следующее однобайтное поле кадра. Оно говорит ведомому устройству, какие данные или выполнение какого действия требует от него ведущее устройство;
  • данные — поле содержит информацию, необходимую ведомому устройству для выполнения заданной мастером функции или содержит данные, передаваемые ведомым устройством в ответ на запрос ведущего. Длина и формат поля зависит от номера функции, также в поле данных может быть детализация кода функции;
  • блок обнаружения ошибок — контрольная сумма для проверки отсутствия ошибок в кадре.

 

Максимальный размер ADU для последовательных сетей RS232/RS485 — 256 байт, для сетей TCP — 260 байт.

 

Необходимое оборудование и программное обеспечение

 

Во-первых, необходим компьютер с установленной системой Windows 98/XP и средой Visual Basic 6.0.

 

Во-вторых, потребуется конвертер USB/RS-485 или RS-232/RS-485. Я использовал преобразователь USB/RS-485 фирмы ОВЕН АС-4.

 

В-третьих, понадобится самое главное – устройство, с которого будут обрабатываться данные. Я буду буду использовать модуль ввода аналоговых сигналов ОВЕН МВ110-2А.

 

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

 

Написание кода

 

Ранее в статье уже рассказывалось о структуре ADU. Настало время на практике осуществить формирование сообщения Modbus. Пусть ведомое устройство – сервер сконфигурировано следующим образом: Адрес=16, скорость связи 9600, без контроля четности, 1 стоп-бит. Но устройство будет возвращать ошибку, пока не внедрится в сообщение так называемый контроль ошибок CRC. Протокол Modbus использует циклический избыточный код CRC16-IBM:

 

  Function CRC_16(OutputString As String) As String ' функция подсчета контрольной суммы сообщения RTU
	Dim Generator, CRC As Long
	Dim i As Integer, j As Integer, Length As Integer
	Dim Bit As Boolean
	Dim Temp As Byte
	Length = Len(OutputString)
	CRC = 65535
	Generator = 40961

	For i = 1 To Length
	  Temp = Asc(Mid(OutputString, i, 1))
          CRC = CRC Xor Temp
          For j = 1 To 8
            Bit = CRC And 1
            CRC = CRC \ 2
            If Bit = True Then
               CRC = CRC Xor Generator
            End If
        Next j
      Next i
      CRC_16 = Chr(CRC Mod 256) & Chr(CRC \ 256)
  End Function

 

Соответственно, функция проверки правильности принятого ответа имеет вид:

 

  Function Check_CRC(InputString As String) As Boolean
    ' функция проверки правильности принятого сообщения
    ' можете ее добавить в теле функции TxRx для контроля ошибок принимаемых сообщений
    Dim CRC As String, Data As String
    CRC = Mid(InputString, Len(InputString) - 2, 2)
    Data = Mid(InputString, 1, Len(InputString) - 2)
    If CRC = CRC_16(Data) Then
      Check_CRC = True
    Else
      Check_CRC = False
    End If
  End Function

 

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

 

  1. Адрес подчиненного устройства, которое будет опрашиваться. Он равен 16.
  2. Код функции равен 3, поскольку происходит считывание входного значения регистра.
  3. Адрес регистра берется из руководства по эксплуатации. В данном случае он равен 1.
  4. Количество одновременно считываемых регистров также равно 1.
  5. CRC контрольная сумма предыдущих символов

 

В итоге получается полином 16 03 01 01 crc crc в десятичной системе. Но в Modbus используется шестнадцатеричная система счисления, т.е. 10 03 01 01 0хcrc 0хcrc. Таким образом, функция будет иметь вид:

 

  Function TxRx(ByRef CommPort As Integer, ByRef Settings As String, ByRef Address As Integer, ByRef Func As Integer, ByRef Data1 As String, ByRef Data2 As String, ByRef Data3 As String, ByRef Data4 As String) As String
  'функция отправляет устройсву Modbus дейтаграмму, в ответ возвращает принятое сообщение
  'если нужно считать всё сообщение, примените цикл for, раскомментить закомментированные строки цикла
  Dim Frame As String ' формируемое сообщение на отправку
  Dim RxFrame As String ' принимаемое сообщение из порта RS485
  Frame = Chr(Address) & Chr(Func) & Chr(Data1) & Chr(Data2) & Chr(Data3) & Chr(Data4)
  Frame = Frame & CRC_16(Frame)
  MSComm1.CommPort = CommPort
  MSComm1.Settings = Settings
  MSComm1.InBufferCount = 0
  MSComm1.InputLen = 0
  MSComm1.PortOpen = True
  MSComm1.Output = Frame

  Do While (MSComm1.InBufferCount < 7) ' накапливаем буфер из 7 символов
    DoEvents
  Loop

  RxFrame = MSComm1.Input ' получаем данные из буфера
  'For i = 1 To len(RxFrame)
  'TxRx = TxRx & Asc(Mid(RxFrame, i))
    TxRx = Asc(Mid(RxFrame, 4)) * 256 + Asc(Mid(RxFrame, 5)) 'дешифруем нужные биты в 10х систему
  'Next i

  MSComm1.PortOpen = False

End Function

 

Остается лишь осуществить прикладную задачу – в тело таймера помещается код

 

  Private Sub Timer1_Timer()
    Text3.Text = TxRx(2, "9600,n,8,1", 16, 3, 0, 1, 0, 1)
    f = FreeFile
    Open Dir1.Path & "\anvalues" For Output As #f
    Print #f, , CInt(Text3.Text) / 10
    Close #f
  End Sub

 

В поле Text3.Text будет отображаться значение температуры, измеряемой датчиком.

 

Выводы

 

Описанным в статье способом работы с протоколом Modbus RTU можно написать свой OPC-сервер и HMI-систему под специфическую задачу как локального, так и распределенного управления. Экономический эффект заключается в том, что не нужно тратить средства на приобретение сторонних OPC-серверов и HMI-систем, а самим написать и внедрять свою специфическую систему и получить от этого моральное удовлетворение и дополнительную прибыль.

+ Добавить в избранное
    ? Помощь
Об авторе

Ярослав (comexe) Филипченко

Инженер по промышленной автоматизации

См. также:
Профиль автора
Ярослав (comexe) Филипченко
Последние комментарии (всего: 28)

Добавлять комментарии могут только зарегистрированные пользователи сайта.
Если у Вас уже есть учетная запись на Kbyte.Ru, пройдите процедуру авторизации OpenID.
Если Вы еще не зарегистрированы на Kbyte.Ru - зарегистрируйтесь.

День добрый-попробовал ваш пример -сразу не заработал.
Заменил в строке Open Dir1.Path & "\anvalues" For Output As #f
на Open App.Path & "\anvalues.txt" For Output As #f- и все поехало. Я в программировании не очень к сожалению.Но дальше программа через какое то время выдает ошибку (Run - timeerror 8005)Port alreadyopen, в строке
MSComm1.PortOpen = True.
Если возможно подсказать в чем проблема. Я в качестве обмена использую частотник DELTA. Спасибо
По поводу Dir1.Path - Я использовал ActiveX элемент DirListBox на форме. Совершенно верно, используйте App.Path, если используете запись в файл в папке с приложением. Можно вообще убрать этот код с записью в файл.
По поводу Port already open - проверьте, не используют ли другие программы, работающие на Вашем компьютере, этот порт, а также не запущен ли параллельно еще один экземпляр Вашей программы. Вообще, эта ошибка как раз и означает, что порт используется другой программой.
Что же касается частотников Delta, то у меня пока поэкспериментировать руки до них не дошли. Я пробовал управлять Danfoss VLT Micro и Овен ПЧВ и всё управлялось. Правда, понадобилось добавить еще битов в дейтаграмму. Получилось что-то типа:
Function TxRx(ByRef CommPort As Integer, ByRef Settings As String, ByRef Address As Integer, ByRef Func As Integer, ByRef Data1 As String, ByRef Data2 As String, ByRef Data3 As String, ByRef Data4 As String, ByRef Data5 As String, ByRef Data6 As String, ByRef Data7 As String, ByRef Data8 As String, ByRef Data9 As String) As String
Dim Frame As String ' формируемое сообщение на отправку
Dim RxFrame As String ' принимаемое сообщение из порта RS485
Frame = Chr(Address) & Chr(Func) & Chr(Data1) & Chr(Data2) & Chr(Data3) & Chr(Data4) & Chr(Data5) & Chr(Data6) & Chr(Data7) & Chr(Data8) & Chr(Data9)
Frame = Frame & CRC_16(Frame)

Ну и соответственно, привязочка к кнопкам:

Private Sub Command1_Click()
Text3.Text = TxRx(2, "9600,n,8,1", 1, 15, 0, 0, 0, 32, 4, 124, 4, 0, 64) 'стартует 100%(64hex=100)
End Sub

Private Sub Command2_Click()
Text3.Text = TxRx(2, "9600,n,8,1", 1, 15, 0, 0, 0, 32, 4, 60, 4, 0, 0) ' останов
End Sub

Private Sub Command3_Click()
Text3.Text = TxRx(2, "9600,n,8,1", 1, 15, 0, 0, 0, 32, 4, 124, 4, 0, 48) 'стартует 75%(48hex=75)
End Sub

Private Sub Command4_Click()
Text3.Text = TxRx(2, "9600,n,8,1", 1, 15, 0, 0, 0, 32, 4, 124, 4, 0, 32) 'стартует 50%(32hex=50)
End Sub

Private Sub Command5_Click()
Text3.Text = TxRx(2, "9600,n,8,1", 1, 15, 0, 0, 0, 32, 4, 124, 4, 0, 16) 'стартует 25%(16hex=25)
End Sub
И еще: внимательно изучите документацию по составу дейтаграмм Modbus к приводам Delta. Скорей всего, программу придется модернизировать, как сделал я, добавив биты. Только не забудьте в таком случае подобрать значение n в цикле Do While (MSComm1.InBufferCount < n)
Также функцию можно преобразовать в процедуру, если не требуется обратная связь.
ух ты большое спасибо за ответ. По поводу порта- решил проблему тем что открываю его при загрузки формы, а закрываю при выходе из программы и проблема ушла,а светодиоды на адаптере стали гораздо интенсивней моргать. Может это и неправильно-но пока работает и не отключается.
А запись в файл действительно пока убрал,потом поставлю и организую,сначала нужно полностью управлять частотником научится.
Такая работа с портом не очень корректна. Я по началу тоже так делал, но потом пришел к выводу, что лучше применять следующий алгоритм: открыл порт->послал команду1->получил ответ->закрыл порт->открыл порт->послал команду2->получил ответ->закрыл порт->....
Хотя, если Вас устраивает такая работа программы, то не могу возражать, главное, что работает ;)
Все комментарии (всего: 28)
Авторизация
 
OpenID
Зарегистрируйся и получи 10% скидку на добавление своего сайта в каталоги! Подробнее »
Поиск по сайту
Люди на Kbyte.Ru
Реклама
Счетчики