Реализация протокола Modbus
Организация связи между электронными устройствами. Коммуникационный протокол, основанный на архитектуре "клиент-сервер". Чтение флагов, дискретных входов, регистров хранения и регистров ввода. Запись регистра хранения. Обработка прерываний и запроса.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | курсовая работа |
Язык | русский |
Дата добавления | 07.07.2011 |
Размер файла | 1,4 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Федеральное агентство по образованию
Государственное образовательное учреждение высшего профессионального образования
ГОУ ВПО ЮГОРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
Институт Систем Управления и Информационных Технологий
Кафедра «Автоматизированные системы обработки информации и управления»
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА К КУРСОВОЙ РАБОТЕ
по дисциплине: «Языки программирования низкого уровня»
на тему «Реализация протокола Modbus»
Выполнил: Бирюков Н.А.
студент группы 1170
Специальность: 230102
Шифр: 117010
Проверил: преподаватель
С.Н. Горбунов
Ханты-Мансийск, 2011
Федеральное агентство по образованию
Государственное образовательное учреждение высшего профессионального образования
ЮГОРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
Институт Систем Управления и Информационных Технологий
Кафедра «Автоматизированные системы обработки информации и управления»
ЗАДАНИЕ
На курсовую работу по дисциплине «Языки программирования низкого уровня»
Тема курсовой работы: «Реализация протокола Modbus для микроконтроллера семейства AVR»
На языке Assembler создать программу, реализующую протокол Modbus в качестве Slave устройства для микроконтроллера семейства AVR.
Программа должна быть реализована для стенда СУ-МК-AVR (Контроллер At Mega 128) и выполнять следующие функции:
1. осуществлять приём/передачу сообщений по UART.
2. чтение нескольких флагов.
3. чтение нескольких дискретных регистров.
4. чтение регистров хранения.
5. чтение нескольких регистров ввода.
6. запись значения одного флага.
7. запись значения в один регистр.
Задание выдал _______________ Горбунов С.Н.
Задание принял _______________Бирюков Н.А.
Содержание
- Введение
- 1. Анализ задания на курсовую работу
- 2. Проектирование программы
- 2.1 Чтение флагов
- 2.2 Чтение дискретных входов
- 2.3 Чтение регистров хранения
- 2.4 Чтение регистров ввода
- 2.5 Запись одного флага
- 2.6 Запись одного регистра хранения
- 2.7 Исключительная ситуация
- 2.8 Контрольная сумма
- 3. Разработка программы
- 3.1 Инициализация
- 3.2 Обработка прерываний
- 3.3 Обработка запроса
- 3.4 Чтение флагов
- 3.5 Чтение регистров хранения
- 3.6 Чтение регистров ввода
- 3.7 Запись флага
- 3.8 Запись регистра
- 3.9 Обработка исключительных ситуаций
- 3.10 Контрольная сумма
- 4. Тестирование программы
- Заключение
- Список использованных источников
- Приложение А
- Приложение Б
- Введение
- Целью работы является проектирование и реализация протокола Modbus для микроконтроллера семейства AVR.
- Modbus - коммуникационный протокол, основанный на архитектуре «клиент-сервер». Широко применяется в промышленности для организации связи между электронными устройствами. Может использовать для передачи данных последовательные линии связи RS-485, RS-422, RS-232, а также сети TCP/IP (Modbus TCP).
- Существует множество решений для реализации Modbus протокола в качестве slave устройства. Представленное решение отличается простотой и наглядностью, при этом выполняются основные функции протокола.
- Всего в протоколе Modbus предусмотрено более семидесяти функций, некоторые из них выполняют функции проверки ошибок. В данной реализации упор сделан на функции чтения/записи данных.
1. Анализ задания на курсовую работу
Существует три основных реализации протокола Modbus, две для передачи данных по последовательным линиям связи, как медным EIA/TIA-232-E (RS-232), EIA-422, EIA/TIA-485-A (RS-485), так и оптическим и радио:
· Modbus RTU и
· Modbus ASCII,
и одна для передачи данных по сетям Ethernet поверх TCP/IP:
· Modbus TCP.
В данной курсовой работе представлен Modbus RTU, так как этот вариант наиболее подходит для программирования на микроконтроллере AVR, так как реализация стека TCP/IP необоснованно усложнило бы программу. Реализация ASCII не оправдана, так как формат сообщений Modbus RTU гораздо удобнее обрабатывать. Modbus ASCII использует 7 бит данных при передаче по последовательной линии, для идентификации начала и конца посылки используется дополнительные символы («:» в начале строки и символы перевода строки в конце).
Рассмотрим формат сообщений ModBus RTU. Структура ModBus состоит из запросов и ответов. Их основа -- элементарный пакет протокола, так называемый PDU (Protocol Data Unit). Структура PDU (Рисунок 1.1) не зависит от типа линии связи и включает в себя код функции (FCode) и поле данных (Data).
Рисунок 1.1 - Структура пакета PDU
Для передачи пакета по физическим линиям связи PDU помещается в другой пакет, содержащий дополнительные поля. Этот пакет носит название ADU (Application Data Unit). Общая структура ADU пакета для ModBus RTU представлена на рисунке 1.2.
Рисунок 1.2 - Общая структура ADU пакета для ModBus RTU
Пакет Modbus RTU ADU помимо PDU пакета включает в себя также Slave ID - адрес ведомого устройства и контрольную сумму CRC16 для проверки корректности пакета.
В данной реализации протокола Modbus используются следующие типы данных:
· Флаг - один бит, регистр флагов доступны как на чтение, так и на запись. Флаги хранятся в оперативной памяти микроконтроллера. Для флагов выделен 1 байт, таким образом можно обращаться к 8 флагам.
· Дискретный регистр - один бит, доступен только для чтения. Дискретный регистр является портом ввода. Дискретные регистры являются регистром статуса микроконтроллера, следовательно доступны 8 бит.
· Регистр хранения - 16-битный регистр, доступен для чтения и записи. В качестве регистров хранения выступают выделенные ячейки в оперативной памяти микроконтроллера. Для регистров хранения выделено 32 байта, таким образом, возможен доступ к 16-ти регистрам.
· Регистр ввода - 16-битный регистр, доступен только для чтения. В качестве регистров ввода используются РОН микроконтроллера. При этом старшие 8 бит регистра всегда равны «0», а младшие - содержимому запрашиваемого регистра. Для чтения доступны 32 регистра.
В данной курсовой работе реализованы следующие функции Modbus:
· 0x01 - чтение значений из нескольких регистров флагов.
· 0x02 - чтение значений из нескольких дискретных регистров.
· 0x03 - чтение из нескольких регистров хранения.
· 0x04 - чтение из нескольких регистров ввода.
Запрос состоит из адреса первого элемента таблицы, значение которого требуется прочитать, и количества считываемых элементов. Адрес и количество данных задаются 16-битными числами, старший байт каждого из них передается первым.
В ответе передаются запрошенные данные. Количество байт данных зависит от количества запрошенных элементов. Перед данными передается один байт, значение которого равно количеству байт данных.
· 0x05 - запись одного значения флага.
· 0x06 - запись значения в один регистр хранения.
Команда состоит из адреса элемента (2 байта) и устанавливаемого значения (2 байта). Для регистра хранения значение является просто 16-битным словом. Для флагов значение 0xFF00 означает включённое состояние, 0x0000 - выключенное, другие значения недопустимы. Если команда выполнена успешно, ведомое устройство возвращает копию запроса.
2. Проектирование программы
Реализация протокола modbus состоит из приёма сообщений и последующей их обработки. Для необходимых функций modbus длина пакета постоянна и равна 8. Так как UART поддерживает передачу по одному байту, необходимо ввести счётчик, который будет проверять окончание посылки.
Для того чтобы осуществлять приём и передачу, необходимо инициализировать UART. Для работы с com-портом используется UART1 микроконтроллера, который подсоединяется при помощи интерфейса USB через преобразователь COM-порта. Так как в больших скоростях передачи нет необходимости, достаточно 9600 бит/с. Формат посылки следующий:
· 8 бит данных.
· 1 стоп бит.
· без контроля чётности
Кроме инициализации UART необходимо инициализировать счётчик байтов и CRC, а также разрешить прерывания.
Вся обработка сообщений и отправка ответов производится в обработке прерываний. Поэтому после инициализации системы запускается пустой бесконечный цикл, в котором происходит ожидание прерываний.
Самая важная часть программы находится в блоке обработки прерываний. Рассмотрим его более подробно. При поступлении прерывания от UART в зависимости от номера байта в посылке будут выполняться различные действия. Формат посылки приведён на рисунке 2.1.
Рисунок 2.1 - формат посылки
Где:
· S_ID - ID устройства, 0 - для широковещательной посылки. Если посылка широковещательная, ответ не отправляется. Если ID не равен 0 и не равен ID устройства, посылка не обрабатывается.
· F_ID - ID функцию, которую необходимо выполнить. Если Функция не поддерживается, отправляется исключение.
· SR_HI - старшая часть смещения стартового регистра. В связи с использованием в данной реализации адресов, которые умещаются в 8 бит, не используется.
· SR_LO - младшая часть смещения стартового регистра.
· D_HI - старшая часть информации о данных. Для функций записи это старшая часть числа, которое нужно записать, для функций чтения - старшая часть количества запрашиваемых данных. Так как все адреса данных умещаются в 8 бит, в случае чтения этот байт не используется.
· D_LO - младшая часть информации о данных. Для функций записи это младшая часть числа, которое нужно записать, для функций чтения - младшая часть количества запрашиваемых данных.
· CRC_LO - младшая часть контрольной суммы CRC.
· CRC_HI - старшая часть контрольной суммы CRC.
При поступлении прерывания от UART каждый раз выполняется действие, которое соответствует счётчику байтов. В конце обработки каждого байта, которая заключается в сохранении в памяти переданных данных, необходимо уменьшить счётчик на 1. Если выполняется обработка первых 6-ти байт, также пересчитывается CRC.
Когда счётчик байт дойдёт до 0, будет необходимо выполнить обработку всего запроса. После обработки запроса счётчик байт нужно будет сбросить (установить значение 7) и подготовить CRC к новой посылке (установить значение 0xFFFF).
Рассмотрим более подробно блок обработки запросов. В первую очередь необходимо проверить, направлялся ли данный пакет этому устройству. Если передача была широковещательной, то необходимо установить соответствующий флаг, так как ответ посылается только при уникальном запросе. Если посылка предназначалась другому устройству, она отбрасывается.
После проверки идентификатора устройства, необходимо проверить контрольную сумму. Эта проверка состоит из двух этапов: проверка старшей и младшей части. Это обусловлено тем, что CRC является 16-битным значением, а микроконтроллер AT Mega 128 работает только с 8-ми битными данными.
Если контрольная сумма совпала, то необходимо выполнить запрашиваемую функцию. Если запрашиваемая функция не поддерживается, отправляется исключение с соответствующим кодом. Если функция поддерживается, то программа выполняет её. Далее будут рассмотрены алгоритмы обработки запросов конкретных функций.
2.1 Чтение флагов
Функция чтения флагов выполняется, если посылка была не широковещательной. При чтении флагов в ответ входят:
· S_ID
· F_ID
· Количество байт данных, которые будут переданы. В данной реализации всегда будет передаваться один байт, так как максимальное число считываемых флагов - 8
· Флаги, которые были считаны по запросу
· CRC
Если запрашивается больше данных, чем может предоставить устройство, или адрес последнего запрашиваемого флага превышает общее число флагов, отправляется исключение с соответствующим кодом.
2.2 Чтение дискретных входов
Функция чтения дискретных входов аналогична функции чтения данных. Отличие в том, что в данном случае запрашиваются биты регистра статуса. При возникновении исключительной ситуации отправляются те же коды ошибок, что и при чтении флагов.
2.3 Чтение регистров хранения
Функция чтения регистров хранения выполняется только если запрос не был широковещательным. В ответ входят:
· S_ID
· F_ID
· Количество байт. Так как регистры 16-битные, количество байт - количество запрашиваемых регистров, умноженное на 2
· Запрашиваемые регистры
· CRC
Если запрашивается больше данных, чем может предоставить устройство, или адрес последнего запрашиваемого регистра превышает общее число регистров, отправляется исключение с соответствующим кодом.
2.4 Чтение регистров ввода
Функция чтения регистров ввода аналогична функции чтения регистров хранения. Отличие в том, что в данном случае запрашиваются значения РОН микроконтроллера. При этом старшая часть регистра ввода всегда равна 0, а младшая - значению РОН.
2.5 Запись одного флага
Функция записи одного флага выполняется не зависимо от того, была ли посылка широковещательной. В случае уникальной посылки в ответе отправляется копия запроса. Для записи флагов используют следующие два значения:
· FF 00 - установить флаг
· 00 00 - сбросить флаг
В случае попытки обращения к данным вне выделенного диапазона или записи другого значения, отправляется исключение с соответствующим кодом.
2.6 Запись одного регистра хранения
Функция записи одного регистра хранения по структуре такая же, как и функция записи одного флага. Отличие в том, что устанавливается любое 16-битное значение для указанного регистра хранения.
2.7 Исключительная ситуация
Исключительная ситуация может возникнуть, если выполнилось одно из следующих условий:
· Код функции не поддерживается. Код исключения 1.
· Адреса запрашиваемых данных больше адресов предоставляемых. Код исключения 2.
· Значение устанавливаемого флага не корректно. Код исключения 3.
При возникновении исключительной ситуации в случае широковещательного запроса пакет отбрасывается, иначе отправляется пакет, содержащий следующие поля:
· S_ID
· F_ID+0x80 -код функции символизирует о возникновении исключительной ситуации
· Код ошибки
· CRC
2.8 Контрольная сумма
Контрольная сумма считается в процессе приёма или отправки сообщения. В протоколе modbus используется CRC16 с многочленом 0xA001. Перед каждым новым вычислением контрольной суммы необходимо инициализировать её значение, установив его в 0xFFFF.
Блок-схема программы приведена в приложении А.
3. Разработка программы
Для корректной работы программы понадобится ряд переменных и массивов. Все они имеют такие структуру и имена, как описано ниже:
· s_id - идентификатор устройства, является одним байтом оперативной памяти микроконтроллера.
· f_id - идентификатор функции, является одним байтом оперативной памяти.
· startreg - адрес начального регистра или флага. Является двумя байтами оперативной памяти.
· crc - два байта оперативной памяти для хранения принятой контрольной суммы.
· CRCH - старший байт высчитываемой контрольной суммы. Является РОН, специально выделенным для CRC.
· CRCL - младший байт высчитываемой контрольной суммы. Является РОН, специально выделенным для CRC.
· DATACH - старший байт для числа данных в случае чтения, или старший байт данных в случае записи. Является РОН, специально выделенным для хранения данных.
· DATACL - младший байт для числа данных в случае чтения, или младший байт данных в случае записи. Является РОН, специально выделенным для хранения данных.
· BYTENUM - счётчик принятых байтов в посылке. Является специально выделенным РОН.
· TMP - временная переменная. Является РОН.
· COUNTER - счётчик, используется в функции подсчёта CRC. Является РОН.
· MES - регистр, в котором хранится принятый по UART байт, или байт, который необходимо отправить.
· hold_regs - 32 байта оперативной памяти, которые выделены для хранения 16-ти регистров хранения
· coils - 1 байт оперативной памяти, выделенный для хранения 8 флагов.
Программа состоит из нескольких блоков. Все блоки, кроме блока инициализация выполняются в режиме обработки прерывания от UART.
3.1 Инициализация
В блоке инициализация необходимо сделать 2 вещи: подготовить систему к новой посылке и инициализировать UART. Для того, чтобы подготовить систему к новой посылке, необходимо записать в регистры CRCH и CRCL значение 0xFF. Этого требует алгоритм расчёта CRC, и установить значение 7 регистру BYTENUM. Так как посылка имеет длину 8, счётчик будет при поступлении новых байт декрементироваться до 0. Также для корректного возврата из прерываний и подпрограмм необходимо инициализировать указатель стека.
В инициализацию UART входят разрешение приёма, передачи и прерывания по приёму, настройка скорости передачи и формата посылки. Для разрешения приёма, передачи и прерывания по приёму необходимо установить в регистре UCSR1B соответствующие биты. В регистре UCSR1C устанавливаются биты, которые отвечают за формат посылки. Для настройки скорости передачи в регистр UBBR1 записывается значение делителя частоты, который высчитывается по следующей формуле:
bauddivider = XTAL/(16•baudrate)-1;
где bauddivider - значение делителя, XTAL - частота микроконтроллера, baudrate - желаемая скорость передачи. После инициализации UART необходимо глобально разрешить прерывания путём установления бита разрешения прерываний в регистре статуса командой sei. Теперь система находится в состоянии ожидания прерывания, выполняя пустой бесконечный цикл.
3.2 Обработка прерываний
В блоке обработки прерываний считывается пришедшее сообщение путём загрузки содержимого регистра UDR1 в регистр MES, и в зависимости от счётчика BYTENUM выполняется соответствующее действие.
Если BYTENUM=7, значит посылка только началась, необходимо считать S_ID. Для этого содержимое регистра MES помещается в ячейку памяти s_id. Если BYTENUM=6, значит необходимо считать F_ID, переместив содержимое регистра MES в ячейку памяти s_id.
Если BYTENUM больше или равен 2, но меньше 6, значит необходимо считать информацию о данных. В порядке уменьшения регистра BYTENUM будут считываться старшая часть startreg, младшая часть startreg, DATACH и DATACL.
После сохранения в памяти полученного значения вызывается функция getCRC, которая посчитает CRC для каждого нового байта. После подсчёта CRC декрементируется счётчик BYTENUM.
Если BYTENUM равен 1, значит нужно сохранить младшую часть CRC. После сохранения младшей части CRC функция getCRC не вызывается, но счётчик BYTENUM декрементируется.
Если BYTENUM равен 0, значит вся посылка была передана, и после сохранения в памяти старшей части CRC вызывается подпрограмма обработки запроса. После обработки запроса значения BYTENUM, CRCL и CRCH инициализируются для новой посылки.
3.3 Обработка запроса
В первую очередь необходимо проверить, предназначалась ли посылка устройству. Если s_id равен slave_id (slave_id - id данного устройства, объявлено директивой .def), то после обработки запроса отправится ответ. Если s_id равен 0, то устанавливается флаг широковещательной передачи. В другом случае посылка не обрабатывается, так как предназначалась другому устройству.
После проверки s_id сравниваются контрольные суммы, и если они не совпадают, посылка отбрасывается.
Если CRC совпадает, то происходит загрузка функции из памяти. Если необходимо выполнить функцию с кодом более 6ти, отправляется исключение. Если код функции корректный, выполняется действие, соответствующее коду. Проверка осуществляется путём цепочки условных переходов. В R17 загружается 1, в R16 - код функции. Команда cpse сравнивает эти регистры, и в случае равенства переходит по адресу обработчика функции чтения флагов. Если регистры не равны, выполняется следующая инструкция, которая осуществляет безусловный переход на следующий этап. На следующем этапе регистр R17 инкрементируется и происходит то же, что и на предыдущем этапе, только функция на каждом шаге будет своя. Была организована именно такая система переходов, так как безусловные переходы передают управление на большие расстояния. Если ни один из переходов по адресу обработки функции не был совершён, выполняется функция с кодом 7.
3.4 Чтение флагов
В первую очередь функция чтения флагов проверяет, была ли посылка широковещательной, и если была, то функция не выполняется. После этого производится проверка значений startreg и startreg+DATACL. Если одно из этих значений превосходит 8, то отправляется исключение.
Если исключения не произошло, то происходит отправка кода устройства, кода функции и количества запрашиваемых данных. Перед отправкой байта считается его CRC. Количество запрашиваемых данных всегда равно 1, так как для флагов выделен всего один байт.
Так как функции чтения флагов и чтения дискретных регистров различаются лишь тем, что флаги читаются из переменной coils, а дискретные регистры - из регистра статуса, для обработки обоих запросов используется одна и та же функция.
Если происходит запрос на чтение флагов, в регистровую пару Z загружается смещение переменной coils, и содержимое переменной загружается в регистр MES. Если читаются дискретные регистры, вызывается функция get_status_reg, которая поместит содержимое регистра статуса в регистр MES.
После загрузки флагов в регистр MES необходимо логически сдвинуть его вправо на значение startreg-1, так как нумерация адресов в протоколе modbus начинается с одного. В регистр R16 загружен startreg-1, и сдвиг происходит, пока R16 не равен нулю. Если startreg равен единице, сдвига не происходит. После сдвига регистра MES необходимо наложить на него маску, которая бы сбросила все флаги, которые не были запрошены. Число запрашиваемых флагов находится в регистре DATACL, для наложения маски вызывается функция get_mask. После наложения маски на регистр MES выполняется его отправка и расчёт контрольной суммы. После этого отправляется рассчитанное значение CRC.
Функция get_status_reg выполняет восемь проверок флагов микроконтроллера, начиная со старшего. Если бит установлен, то регистр MES сдвигается влево и инкрементируется, иначе просто сдвигается влево. Таким образом все установленные биты будут единицами в регистре MES, с сброшенные - нулями.
Функция get_mask генерирует маску «И» на первые DATACL бит. Результат помещается в регистр R25. Первые DATACL бит маски должны быть равны единицы, остальные - нулями. Для этого в регистр R25 изначально записывается 0. После этого DATACL помещается в стек, и в цикле, пока DATACL не равно нулю R25 сдвигается влево и инкрементируется. После цикла DATACL извлекается из стека.
3.5 Чтение регистров хранения
В первую очередь функция чтения регистров проверяет, была ли посылка широковещательной, и если была, то функция не выполняется. После этого производится проверка значений startreg и startreg+DATACL. Если одно из этих значений превосходит 16, то отправляется исключение.
Если исключения не произошло, то происходит отправка кода устройства, кода функции и количества запрашиваемых данных. Перед отправкой байта считается его CRC. Количество запрашиваемых данных отправляется в байтах, поэтому оно равно удвоенному числу запрашиваемых регистров.
Далее организован цикл отправки регистров. Она выполняется до тех пор, пока значение счётчика отправленных регистров не дойдёт до нуля. В цикле сначала читается и отправляется старший байт, потом указатель на ячейку памяти инкрементируется и отправляется младший байт.
После цикла отправляется посчитанная контрольная сумма.
3.6 Чтение регистров ввода
Функция чтения регистров ввода очень похожа на функцию чтения регистров хранения и отличается лишь тем, что для чтения доступно 32 регистра, а при отправке значений старшая часть всегда равна нулю, а младшая - содержимому запрашиваемого РОН. Структура функции такая же, как и при чтении регистров хранения.
3.7 Запись флага
В первую очередь функция записи флага проверяет, не превосходит ли адрес флага, который нужно записать общее число флагов. Если превосходит, то отправляется исключение с соответствующим кодом.
После проверки адреса выполняется проверка значения флага. Если записываемое значение не равно 0xFF00 или 0x0000, то также отправляется исключение.
Если адрес и значение записываемого флага корректно, то в регистровую пару Z загружается смещение переменной coils, и содержимое этой переменной загружается в регистр R17.
Далее, необходимо либо установить бит под номером startreg-1, либо его сбросить. Для обоих случаев используется маска, которая генерируется для startreg и записывается в регистр R18. В регистре R18 после работы функции bit_to_mask установлен только один бит под номером startreg-1.
Функция bit_to_mask сначала записывает в R18 значение 1. Далее она сдвигает единицу startreg-1 раз, таким образом получая в регистре R18 необходимую маску.
Если бит нужно установить, то с помощью логического «или» регистров R17 и R18 заданный бит устанавливается в единицу. Если бит необходимо сбросить, то происходит инверсия R18 с последующим логическим «и» с регистром R17.
После установки или сброса бита происходит отправка ответа, если запрос не был широковещательным. Ответ полностью копирует запрос и отправляется функцией send_echo.
Функция send_echo последовательно отправляет данные в том же порядке, в котором она их принимала. Контрольная сумма в данном случае не считается, так как она будет одинаковой для принятого и отправляемого пакета.
3.8 Запись регистра
В первую очередь функция записи флага проверяет, не превосходит ли адрес флага, который нужно записать общее число флагов. Если превосходит, то отправляется исключение с соответствующим кодом.
Значение смещения стартового регистра записывается в регистр R16. После успешной проверки адреса регистр R16 удваивается, так как необходимо работать с 16-битными регистрами. Адрес переменной hold_regs записывается в регистровую пару Z, после чего к ней добавляется смещение, записанное в регистре R16.
После вычисления адреса необходимого регистра в памяти, происходит запись старшего байта, инкремент адреса и запись младшего байта. После записи в память выполняется проверка флага широковещательной передачи и отправка ответа, если этот флаг не установлен.
3.9 Обработка исключительных ситуаций
В данной реализации предусмотрены следующие типы исключительных ситуаций:
· функция не поддерживается.
· запрашиваемый адрес не доступен.
· Значение флага не корректно.
При возникновении исключительной ситуации управление передаётся различным функциям в зависимости от причины исключения, которые установят в регистр R25 соответствующий код и передадут управление общей функции обработки исключительной ситуации.
Функция обработки исключительной ситуации проверяет флаг широковещательной передачи и отправляет данные только в том случае, если этот флаг не установлен.
После проверки флага функция инициализирует значение CRC, после чего отправляет последовательно код устройства, код функции, которая вызвала ошибку (для этого к коду функции прибавляется 0x80), код ошибки и контрольную сумму пакета. На этом обработка пакета завершается.
3.10 Контрольная сумма
Процедура подсчёта контрольной суммы вызывается каждый раз при отправке нового байта. Контрольная сумма рассчитывается в соответствии с алгоритмом CRC16 и полиномом 0xA001. Этот алгоритм и полином приняты в протоколе modbus для серийных линий.
Алгоритм заключается в следующем: в начале обработки нового байта происходит «исключающее или» с уже имеющимся значением CRC. После этого происходит 8 сдвигов значения CRC влево, при этом если выталкивается «1», то происходит исключающее или с полиномом 0xA001.
Код всей программы с комментариями приведён в приложении Б.
4. Тестирование программы
Для проверки правильной работы программы необходимо было создать такой набор тестов, который позволил бы проверить все функции с различными условиями (проверка широковещательной передачи, проверка обработки исключений). Для отправки сообщений через com-порт использовалась программа SerialNetTools. Для подсчёта контрольной суммы была написана программа.
Функция чтения дискретных входов:
Для проверки функции чтения дискретных вводов было создано 2 теста, первый запрашивает на чтение весь регистр флагов, второй - запрашивает лишние биты. В первом случае функция возвращает состояние регистра флагов, во втором - исключение. Результаты тестирование приведены на рисунке 4.1.
Рисунок 4.1 - функции чтения дискретных вводов
Функция чтения регистров ввода:
Для проверки функции чтения регистров ввода также было направлено 2 теста. Первый запрашивает старшие десять РОН микроконтроллера, второй - запрашивает недопустимые значения. В ответ на первый тест приходит содержимое старших десяти РОН, на второй - исключение. Результаты тестирования приведены на рисунке 4.2.
Рисунок 4.2 - чтение регистров ввода
Функция записи регистров хранения:
Для проверки функции записи дискретных регистров было создано четыре теста:
1. обычная запись значения регистра хранения. В ответ на этот запрос приходит его копия.
2. широковещательная запись регистра хранения. В этом случае ответ не приходит.
3. запись регистра хранения устройству с другим кодом. В этом случае ответ также не приходит.
4. запись регистра хранения по недопустимому адресу. В этом случае в ответ приходит исключение.
Для полной проверки функций записи регистров необходимо провести их чтение и сравнить записанные значения с пришедшими.
Функция чтения регистров хранения:
Для проверки функции чтения регистров хранения было создано 2 теста: первый читает первые 15 регистров, второй читает регистры по недопустимому адресу. В случае корректного чтения для регистров, чьи значения были записаны, совпадают. Стоит заметить, что значение, которое предназначалось другому устройству, записано не было. В случае чтения недопустимого адреса возвращается исключение. Результаты тестирования функций чтения и записи регистров хранения представлены на рисунке 4.3.
Рисунок 4.3 - функции чтения и записи регистров хранения
Функция записи флагов:
Для функции записи флагов было разработано следующие четыре теста:
1. установить третий флаг. Ответом на этот запрос приходит его копия.
2. установить пятый флаг всем устройствам. Ответ на этот запрос не приходит.
3. Сбросить третий флаг устройству с другим кодом. Ответ на этот запрос не приходит.
4. Записать в четвёртый флаг некорректное значение. В ответ на этот запрос приходит исключение.
Для полной проверки корректности функции записи необходимо провести чтение флагов.
Функция чтения флагов:
Для проверки функции чтения флагов было написано два теста: первый читает все флаги, второй читает флаги по недопустимому адресу. В первом случае возвращается один байт данных, в котором установлены третий и пятый бит, что говорит о том, что при попытке сбросить третий флаг для другого устройства программа отбросила пакет. Во втором случае возвращается исключение. Результаты тестирования функций чтения и записи флагов представлены на рисунке 4.4.
Рисунок 4.4 - функции чтения и записи флагов
На этапе отладки и тестирования были найдены и исправлены все ошибки, которые позволил выявить данный набор тестов.
Заключение
В результате проделанной работы была спроектирована и разработана программа, которая реализует основные функции протокола modbus для микроконтроллеров семейства AVR.
Основная задача программы - принимать сообщения и отправлять ответы в соответствии с запросами, выполнена. Так как скорость передачи по UART значительно меньше частоты контроллера, все принятые байты успевают обрабатываться до того, как будут приняты новые. Это говорит о высокой производительности системы.
К недостаткам системы можно отнести ограниченный набор функций и малый объём выделенной для хранения данных памяти. Также во время отправки ответа программа теряет много времени на ожидание готовности передатчика.
Список использованных источников
1. Ревич Ю.В. Практическое программирование микроконтроллеров Atmel AVR на языке ассемблера. - СПб.: БХВ-Петербург, 2008. - 384с.
2. Евстифеев А.В. микроконтроллеры AVR семейства Tiny и Mega фирмы «Atmel» - М.: Издательский дом «Додэка-XXI», 2004. - 560с.
3. AVR. Учебный курс. Передача данных через UART // www.easyelectronics.ru. - http://easyelectronics.ru/avr-uchebnyj-kurs-peredacha-dannyx-cherez-uart.html.
4. Modbus Specification and implementation guides // www.modbus.org. - http://modbus.org/specs.php.
Приложение А
Блок-схема инициализации программы:
Блок-схема обработки принятого байта:
Блок-схема функции обработки запроса:
Блок-схема функции чтения флагов:
Блок-схема функции чтения регистров:
Блок-схема функции записи флага:
Блок-схема функции записи регистра:
клиент протокол сервер запрос
Приложение Б
Листинг кода программы:
.include <m128def.inc>
.dseg
s_id: .byte 1 ;1 байт для хранения id устройства
f_id: .byte 1 ;1 байт для хранения функции
startreg: .byte 2 ;2 байта для хранения регистра начального адреса
hold_regs: .byte 32 ;32 байта для хранения holding registers. 16 регистров по 2 байта
coils: .byte 1 ;2 байта для хранения coils. 16 флагов по одному биту
crc: .byte 2 ;2 байта для хранения crc, которая была передана по UART
.equ XTAL = 7372800 ;частота контроллера
.equ baudrate = 9600 ;необходимая скорость
.equ bauddivider = XTAL/(16*baudrate)-1 ;значение предделителя UART
.equ slave_id = 1 ;адрес устройства
.def CRCH=R29 ;старший байт CRC, которая вычисляется для посылки
.def CRCL=R28 ;младший байт CRC, которая вычисляется для посылки
.def DataCH=R27 ;старший байт для числа данных в случае чтения, или самих данных в случае записи
.def DataCL=R26 ;младший байт для числа данных в случае чтения, или самих данных в случае записи
.def BYTENUM=R25 ;число байт в пакете, которые осталось передать
.def TMP=R24 ;временная переменная
.def COUNTER=R23; ;счётчик. временная переменная, используется в функции getCRC
.def MES=R22 ;сообщение, которое было передано по UART
.cseg
.org 0x0
rjmp Main
Main: ;инициализиация
ldi CRCH,0
sts coils,CRCH
ldi CRCH,0xFF
ldi CRCL,0xFF
ldi BYTENUM,7
ldi R16,0x0F
out SPH,R16
ldi r16,(1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1)
sts UCSR1B,r16
ldi r16,$06
sts UCSR1C,r16
ldi R16,low(bauddivider)
sts UBRR1L,r16
ldi R16,high(bauddivider)
sts UBRR1H,r16
sei
inf_loop:
;ожидание прерывания
rjmp inf_loop
.org 0x3C
rjmp InterHandler
InterHandler:
lds MES,UDR1
cpi BYTENUM,7
breq getID ;если посылка только началась, то нужно сравнить ID
cpi BYTENUM,6
breq getFID ;получить ID функции
cpi BYTENUM,2
brsh getDataInf ; байты 2-5 - инфо о данных
cpi BYTENUM,1
breq getLoCRC ; байт 1 - младшая часть CRC
getHiCRC: ; если управление здесь, значит происходит чтение 0-го байт. старшая часть CRC, выполнение операций
sts crc+1,MES
rcall perform
ldi BYTENUM,7 ;новая посылка
ldi CRCH,$FF ;подготовка CRC к новой посылке
ldi CRCL,$FF
reti
end_handle:
cpi BYTENUM,2 ; считаем CRC для битов 7-2
BRLO noCRC
call getCRC
noCRC:
dec BYTENUM
reti
getID:
sts s_id,MES
rjmp end_handle
getFID:
sts f_id,MES
rjmp end_handle
getLoCRC:
sts crc,MES
rjmp end_handle
getDataInf:
byte_5:
cpi bytenum,5
brne byte_4
sts startreg+1,mes
rjmp end_handle
byte_4:
cpi bytenum,4
brne byte_3
sts startreg,mes
rjmp end_handle
byte_3:
cpi bytenum,3
brne byte_2
mov DataCH,mes
rjmp end_handle
byte_2:
mov DataCL,mes
rjmp end_handle
perform:
;процедура обработки запроса и отправки ответа
;свободны регистры 16-25
;R25 - код ошибки
;R21 - флаг broadcast
;R22 - MES, используется для передачи
lds R16,s_id ;считываем id
ldi R21,0 ;записываем 0 в флаг броадкаст. если R21<>0, значит посылка броадкастовая
cpi R16,slave_id ;сравниваем id с id устройства
breq unicast_request ;если равны, значит посылка unicast
cpi R16,0 ;сравниваем id с нулём (broadcast)
brne perform_end ;если не равны, значит выходим из процедуры
ldi R21,0xFF ;если равны, значит посылка broadcast, выставляем флаг
unicast_request:
lds R16,crc ;проверка crc
cpse R16,CRCL ;младшая часть
rjmp perform_end
lds R16,crc+1 ;старшая часть
cpse R16,CRCH
rjmp perform_end ;если где-то не совпало, значит в пакете ошибка
lds R16,f_id ;загружаем id функции
cpi R16,7
brsh wrong_func ;поддерживаются только первые 6 функций. для других случаев отправляем исключение
;в зависимости от функции выполняем действия
;чтобы заменить условные переходы на безусловные, вводим конструкцию:
ldi R17,1
cpse R17,R16
rjmp no_1
rjmp read_coils
no_1:
inc R17
cpse R17,R16
rjmp no_2
rjmp read_coils
no_2:
inc R17
cpse R17,R16
rjmp no_3
rjmp read_hold_regs
no_3:
inc R17
cpse R17,R16
rjmp no_4
rjmp read_input_regs
no_4:
inc R17
cpse R17,R16
rjmp write_hold_reg
rjmp write_coil
write_hold_reg:
lds R16,startreg ;считываем стартовый регистр
dec R16 ;уменьшаем на 1, так как в modbus данные передаются начиная с 1
cpi R16,16 ;16 - кол-во данных
brsh out_of_range ;если считать нужно больше, чем есть, отправляем исключение
add R16,R16 ;умножаем на 2, так как регистры 16-битные
ldi ZL,low(hold_regs)
ldi ZH,high(hold_regs)
add ZL,R16 ;загружаем в Z смещение запрашиваемых данных
ldi R16,0
adc ZH,R16 ;если произошёл перенос, необходимо это учесть
st Z,DATACH ;данные хранятся в формате СтаршийБайт:Младший
adiw Z,1 ;запись в следующий байт
st Z,DATACL
cpi R21,0xFF ;если не стоит флаг броадкаст, посылаем ответ
breq perform_end
rjmp send_echo
perform_end:
ret
wrong_func:
;отправить исключение с кодом 1. код хранится в r25
ldi r25,1
rjmp exception
out_of_range:
;отправить исключение с кодом 2. код хранится в r25
ldi r25,2
rjmp exception
wrong_value:
;отправить ексепшн с кодом 3. код хранится в r25
ldi r25,3
rjmp exception
read_coils: ;функция для чтения coils или discrete input
cpi R21,0xFF ;если не стоит флаг броадкаст, посылаем ответ
breq perform_end
lds R16,startreg
dec R16
add R16,DATACL
cpi R16,9 ;8 - кол-во данных
brsh out_of_range
sub R16,DATACL
lds MES,s_id ;отправка s_id и f_id
ldi CRCL,$FF
ldi CRCH,$FF
rcall getCRC
rcall send_byte
lds MES,f_id
rcall getCRC
rcall send_byte
ldi MES,1 ;посылаем 1 байт
rcall getCRC
rcall send_byte
lds MES,f_id
cpi MES,1
breq f_coils
ldi MES,0
rcall get_status_reg
rjmp no_coils
f_coils:
ldi ZL,low(coils)
ldi ZH,high(coils)
ld MES,Z
no_coils:
cpi R16,0
breq no_lsr
lsr_loop:
lsr MES
dec R16
cpi R16,0
brne lsr_loop
no_lsr: ;теперь нужно наложить маску и отправить DATA и CRC
rcall get_mask
and MES,R25
rcall getCRC
rcall send_byte
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byte
rjmp perform_end
wrong_value_copy:
rjmp wrong_value
out_of_range_copy:
rjmp wrong_value
perform_end_copy:
rjmp perform_end
read_hold_regs:
cpi R21,0xFF ;если не стоит флаг броадкаст, посылаем ответ
breq perform_end_copy
lds R16,startreg
dec R16
cpi R16,16
brsh out_of_range_copy
add R16,DATACL
cpi R16,17
brsh out_of_range_copy
sub R16,DATACL
add R16,R16
ldi ZL,low(hold_regs)
ldi ZH,high(hold_regs)
add ZL,R16
ldi R17,0
adc ZH,R17
lds MES,s_id
ldi CRCL,$FF
ldi CRCH,$FF
rcall getCRC
rcall send_byte
lds MES,f_id
rcall getCRC
rcall send_byte
mov MES,DATACL
add MES,MES ;отправляем удвоенное число байт
rcall getCRC
rcall send_byte
mov R17,DATACL
send_hold_loop:
ld MES,Z+ ;сначала старший байт.
ld R16,Z+ ;потом младший
rcall send_byte
rcall getCRC
mov MES,R16
rcall send_byte
rcall getCRC
dec R17
cpi R17,0
brne send_hold_loop
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byte
rjmp perform_end
read_input_regs:
cpi R21,0xFF ;если не стоит флаг броадкаст, посылаем ответ
breq perform_end_copy
lds R16,startreg
dec R16
cpi R16,32
brsh out_of_range_copy
add R16,DATACL
cpi R16,33
brsh out_of_range_copy
sub R16,DATACL
mov ZL,R16
ldi ZH,0
lds MES,s_id
ldi CRCL,$FF
ldi CRCH,$FF
rcall getCRC
rcall send_byte
lds MES,f_id
rcall getCRC
rcall send_byte
mov MES,DATACL
add MES,MES ;отправляем удвоенное число байт
rcall getCRC
rcall send_byte
mov R17,DATACL
send_input_loop:
ldi MES,0 ;сначала старший байт.
ld R16,Z+ ;потом младший
rcall send_byte
rcall getCRC
mov MES,R16
rcall send_byte
rcall getCRC
dec R17
cpi R17,0
brne send_input_loop
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byte
rjmp perform_end
write_coil:
lds R16,startreg
dec R16
cpi R16,8 ;8 - кол-во данных
brsh out_of_range_copy2
cpi DATACL,0
brne wrong_value_copy2
cpse DATACH,DATACL
cpi DATACH,0xFF
brne wrong_value_copy2 ;флаг Z установится, если DATACH=DATACL=0, или если DATACH=0xFF, DATACL=0
ldi ZL,low(coils)
ldi ZH,high(coils)
ld R17,Z
rcall bit_to_mask
cpi DATACH,0xFF
breq set_bit
com R18
and R17,R18
st Z,R17
rjmp send_echo
set_bit:
or R17,R18
st Z,R17
sbrc R21,0 ;если не стоит флаг броадкаст, посылаем ответ
rjmp perform_end
rjmp send_echo
out_of_range_copy2:
rjmp out_of_range
wrong_value_copy2:
rjmp wrong_value
send_echo:
lds MES,s_id
rcall send_byte
lds MES,f_id
rcall send_byte
lds MES,startreg+1
rcall send_byte
lds MES,startreg
rcall send_byte
mov MES,DATACH
rcall send_byte
mov MES,DATACL
rcall send_byte
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byte
rjmp perform_end
send_byte:
lds TMP,UCSR1A
sbrs TMP,UDRE1
rjmp send_byte
sts UDR1,MES ;MES - буфер
ret
exception:
sbrc R21,0 ;если не стоит флаг броадкаст, посылаем ответ
rjmp perform_end
ldi CRCH,$FF
ldi CRCL,$FF
lds MES,s_id
rcall getCRC
rcall send_byte
lds MES,f_id
ldi r17,$80
add MES,r17
rcall getCRC
rcall send_byte
mov MES,r25 ;r25 - код ошибки
rcall getCRC
rcall send_byte
mov MES,CRCL
rcall send_byte
mov MES,CRCH
rcall send_byte
rjmp perform_end
getCRC:
LDI TMP,0
EOR CRCH,TMP
EOR CRCL,MES
LDI COUNTER,8
crcloop:
bst CRCL,0
brtc noxor
LSR CRCL
bst CRCH,0
bld CRCL,7
lsr CRCH
ldi tmp,1
eor CRCL,tmp
ldi tmp,$A0
eor CRCH,tmp
dec COUNTER
brne crcloop
ret
noxor:
LSR CRCL
bst CRCH,0
bld CRCL,7
lsr CRCH
dec COUNTER
brne crcloop
ret
bit_to_mask:
;R16 - регистр, который нужно преобразовать
;результат в R18
cpi R16,0
ldi R18,1
breq end_mask
cycle_mask:
lsl R18
dec R16
cpi R16,0
brne cycle_mask
end_mask:
ret
get_mask:
;нужно получить маску AND для первых DATACL бит
;результат в R25, ошибки уже не возникнет
push DATACL
ldi R25,0
cpi DATACL,0
breq mask_end
mask_loop:
lsl R25
inc R25
dec DATACL
cpi DATACL,0
brne mask_loop
mask_end:
pop DATACL
ret
get_status_reg:
brbc 7,no_inter
inc MES
no_inter:
lsl MES
brbc 6,no_temp
inc MES
no_temp:
lsl MES
brbc 5,no_half
inc MES
no_half:
lsl MES
brbc 4,no_sign
inc MES
no_sign:
lsl MES
brbc 3,no_over
inc MES
no_over:
lsl MES
brbc 2,no_neg
inc MES
no_neg:
lsl MES
brbc 1,no_zero
inc MES
no_Zero:
lsl MES
brbc 0,no_carry
inc MES
no_carry:
ret
Размещено на Allbest.ru
Подобные документы
Схема дешифратора с четырёхразрядной шиной адреса и максимальными шестнадцатью управляемыми выходами. Состояния логических элементов. Добавление функции блокировки. Запись данных в регистр. Изменение адресов регистров RA, RB, регистра дискретных входов.
курсовая работа [1,5 M], добавлен 14.01.2014Участие регистров ввода-вывода в работе периферийных устройств. Отражение состояния периферийных устройств в состоянии разрядов регистров состояния. Перечень имен и номеров регистров ввода-вывода, управления и состояния микроконтроллеров разных типов.
курсовая работа [171,2 K], добавлен 22.08.2010Характеристика регистров памяти как устройств временного хранения данных. Различия между прерываниями и исключениями команд, их обработка. Вычисление производительности ЭВМ. Программа с использованием отложенного запуска команд. Виды компьютерных сетей.
контрольная работа [24,9 K], добавлен 09.11.2010Анализ проблематики построения объектно-ориентированного канала связи. Основные понятия протокола Modbus. Возможности CodeSys для реализации объектно-ориентированного подхода. Разработка методики кроссплатформенной библиотеки для интеграции устройств.
курсовая работа [38,6 K], добавлен 15.06.2013Реализация прямого проектирования в архитектуре "файл-сервер". Процесс изменения структуры базы данных, реализация прямого проектирования в архитектуре "клиент-сервер", генерирование SQL-кода создания базы данных на основе физической модели данных.
контрольная работа [697,8 K], добавлен 16.02.2015Структурная схема микроконтроллера, процесс разработки принципиальной схемы и программного обеспечения. Конфигурирование регистров аналого-цифрового преобразователя. Код программы на языке Assembler, конфигурирование регистров внешних прерываний.
курсовая работа [267,5 K], добавлен 08.06.2019Основные функции отдела камеральных проверок налоговой инспекции. Автоматизация процесса назначения IP-адресов узлам сети с использованием протокола DHCP. Проблемы и примеры работы протокола DHCP. Модель клиент-сервер, механизм функционирования.
отчет по практике [91,2 K], добавлен 22.03.2012Реализация информационной системы "Стройгенплан" в архитектуре "клиент-сервер". Цели и задачи моделируемой информационной системы, ее функциональная и информационная модели. Описание программного обеспечения, разработанного в архитектуре "клиент-сервер".
курсовая работа [1,9 M], добавлен 30.08.2010Проектирование модуля ввода/вывода аналоговых, дискретных и цифровых сигналов, предназначенного для сбора данных со встроенных дискретных и аналоговых входов с последующей их передачей в сеть. Расчет временных задержек. Выбор резисторов на генераторе.
курсовая работа [307,1 K], добавлен 25.03.2012Определение IP-протокола, передающего пакеты между сетями без установления соединений. Структура заголовка IP-пакета. Инициализация TCP-соединения, его этапы. Реализация IP на маршрутизаторе. Протокол надежной доставки сообщений ТСР, его сегменты.
контрольная работа [86,1 K], добавлен 09.11.2014