Реализация протокола Modbus

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    654,96 kb
  • Опубликовано:
    2011-07-07
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Реализация протокола Modbus

Федеральное агентство по образованию

Государственное образовательное учреждение высшего профессионального образования

ГОУ ВПО ЮГОРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Институт Систем Управления и Информационных Технологий

Кафедра «Автоматизированные системы обработки информации и управления»





ПОЯСНИТЕЛЬНАЯ ЗАПИСКА К КУРСОВОЙ РАБОТЕ

по дисциплине: «Языки программирования низкого уровня»

на тему «Реализация протокола Modbus»

Выполнил: Бирюков Н.А.

студент группы 1170

Специальность: 230102

Шифр: 117010

Проверил: преподаватель

С.Н. Горбунов




Ханты-Мансийск, 2011

Федеральное агентство по образованию

Государственное образовательное учреждение высшего профессионального образования

ЮГОРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Институт Систем Управления и Информационных Технологий

Кафедра «Автоматизированные системы обработки информации и управления»

ЗАДАНИЕ

На курсовую работу по дисциплине «Языки программирования низкого уровня»

Тема курсовой работы: «Реализация протокола Modbus для микроконтроллера семейства AVR»

На языке Assembler создать программу, реализующую протокол Modbus в качестве Slave устройства для микроконтроллера семейства AVR.

Программа должна быть реализована для стенда СУ-МК-AVR (Контроллер At Mega 128) и выполнять следующие функции:

1.       осуществлять приём/передачу сообщений по UART.

2.      чтение нескольких флагов.

.        чтение нескольких дискретных регистров.

.        чтение регистров хранения.

.        чтение нескольких регистров ввода.

.        запись значения одного флага.

.        запись значения в один регистр.

Задание выдал _______________ Горбунов С.Н.

Задание принял _______________Бирюков Н.А.

Содержание

Введение

. Анализ задания на курсовую работу

. Проектирование программы

.1 Чтение флагов

.2 Чтение дискретных входов

.3 Чтение регистров хранения

.4 Чтение регистров ввода

.5 Запись одного флага

.6 Запись одного регистра хранения

.7 Исключительная ситуация

.8 Контрольная сумма

. Разработка программы

.1 Инициализация

.2 Обработка прерываний

.3 Обработка запроса

.4 Чтение флагов

.5 Чтение регистров хранения

.6 Чтение регистров ввода

.7 Запись флага

.8 Запись регистра

.9 Обработка исключительных ситуаций

.10 Контрольная сумма

. Тестирование программы

Заключение

Список использованных источников

Приложение А

Приложение Б

Введение

Целью работы является проектирование и реализация протокола Modbus для микроконтроллера семейства AVR.- коммуникационный протокол, основанный на архитектуре «клиент-сервер». Широко применяется в промышленности для организации связи между электронными устройствами. Может использовать для передачи данных последовательные линии связи 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 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.

·        Значение устанавливаемого флага не корректно. Код исключения 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 записывается значение делителя частоты, который высчитывается по следующей формуле:

 = 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.

Код всей программы с комментариями приведён в приложении Б.

Функция чтения дискретных входов:

Для проверки функции чтения дискретных вводов было создано 2 теста, первый запрашивает на чтение весь регистр флагов, второй - запрашивает лишние биты. В первом случае функция возвращает состояние регистра флагов, во втором - исключение. Результаты тестирование приведены на рисунке 4.1.

Рисунок 4.1 - функции чтения дискретных вводов

Функция чтения регистров ввода:

Для проверки функции чтения регистров ввода также было направлено 2 теста. Первый запрашивает старшие десять РОН микроконтроллера, второй - запрашивает недопустимые значения. В ответ на первый тест приходит содержимое старших десяти РОН, на второй - исключение. Результаты тестирования приведены на рисунке 4.2.

Рисунок 4.2 - чтение регистров ввода

Функция записи регистров хранения:

Для проверки функции записи дискретных регистров было создано четыре теста:

1.      обычная запись значения регистра хранения. В ответ на этот запрос приходит его копия.

2.      широковещательная запись регистра хранения. В этом случае ответ не приходит.

.        запись регистра хранения устройству с другим кодом. В этом случае ответ также не приходит.

.        запись регистра хранения по недопустимому адресу. В этом случае в ответ приходит исключение.

Для полной проверки функций записи регистров необходимо провести их чтение и сравнить записанные значения с пришедшими.

Функция чтения регистров хранения:

Для проверки функции чтения регистров хранения было создано 2 теста: первый читает первые 15 регистров, второй читает регистры по недопустимому адресу. В случае корректного чтения для регистров, чьи значения были записаны, совпадают. Стоит заметить, что значение, которое предназначалось другому устройству, записано не было. В случае чтения недопустимого адреса возвращается исключение. Результаты тестирования функций чтения и записи регистров хранения представлены на рисунке 4.3.

Рисунок 4.3 - функции чтения и записи регистров хранения

Функция записи флагов:

Для функции записи флагов было разработано следующие четыре теста:

1.      установить третий флаг. Ответом на этот запрос приходит его копия.

2.      установить пятый флаг всем устройствам. Ответ на этот запрос не приходит.

.        Сбросить третий флаг устройству с другим кодом. Ответ на этот запрос не приходит.

.        Записать в четвёртый флаг некорректное значение. В ответ на этот запрос приходит исключение.

Для полной проверки корректности функции записи необходимо провести чтение флагов.

Функция чтения флагов:

Для проверки функции чтения флагов было написано два теста: первый читает все флаги, второй читает флаги по недопустимому адресу. В первом случае возвращается один байт данных, в котором установлены третий и пятый бит, что говорит о том, что при попытке сбросить третий флаг для другого устройства программа отбросила пакет. Во втором случае возвращается исключение. Результаты тестирования функций чтения и записи флагов представлены на рисунке 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. - <#"529248.files/image008.gif">

Блок-схема обработки принятого байта:

Блок-схема функции обработки запроса:

Блок-схема функции чтения флагов:

Блок-схема функции чтения регистров:

Блок-схема функции записи флага:


Блок-схема функции записи регистра:

клиент протокол сервер запрос

Приложение Б


Листинг кода программы:

.include <m128def.inc>

.dseg_id: .byte 1            ;1 байт для хранения id устройства_id: .byte 1                  ;1 байт для хранения функции: .byte 2       ;2 байта для хранения регистра начального адреса

hold_regs: .byte 32       ;32 байта для хранения holding registers. 16 регистров по 2 байта: .byte 1                ;2 байта для хранения coils. 16 флагов по одному биту: .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 0x0Main

:                           ;инициализиацияCRCH,0coils,CRCHCRCH,0xFFCRCL,0xFFBYTENUM,7R16,0x0FSPH,R16r16,(1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1)UCSR1B,r16r16,$06UCSR1C,r16R16,low(bauddivider)UBRR1L,r16R16,high(bauddivider)UBRR1H,r16_loop:

;ожидание прерыванияinf_loop

.org 0x3C

:MES,UDR1

cpi BYTENUM,7getID                    ;если посылка только началась, то нужно сравнить IDBYTENUM,6getFID                      ;получить ID функцииBYTENUM,2getDataInf             ; байты 2-5 - инфо о данныхBYTENUM,1getLoCRC               ; байт 1 - младшая часть CRC:                         ; если управление здесь, значит происходит чтение 0-го байт. старшая часть CRC, выполнение операций

sts crc+1,MESperformBYTENUM,7                 ;новая посылка

ldi CRCH,$FF               ;подготовка CRC к новой посылке

ldi CRCL,$FF_handle:

cpi BYTENUM,2                   ; считаем CRC для битов 7-2

BRLO noCRCgetCRC:BYTENUM

:s_id,MESend_handle

:f_id,MESend_handle

:crc,MESend_handle

:_5:bytenum,5byte_4startreg+1,mesend_handle_4:bytenum,4byte_3startreg,mesend_handle_3:bytenum,3byte_2DataCH,mesend_handle_2:DataCL,mes end_handle

:

;процедура обработки запроса и отправки ответа

;свободны регистры 16-25

;R25 - код ошибки

;R21 - флаг broadcast

;R22 - MES, используется для передачиR16,s_id     ;считываем idR21,0               ;записываем 0 в флаг броадкаст. если R21<>0, значит посылка броадкастоваяR16,slave_id  ;сравниваем id с id устройстваunicast_request ;если равны, значит посылка unicastR16,0                            ;сравниваем id с нулём (broadcast)perform_end         ;если не равны, значит выходим из процедурыR21,0xFF          ;если равны, значит посылка broadcast, выставляем флаг

unicast_request:R16,crc                            ;проверка crcR16,CRCL                 ;младшая частьperform_endR16,crc+1           ;старшая часть R16,CRCHperform_end  ;если где-то не совпало, значит в пакете ошибкаR16,f_id                 ;загружаем id функцииR16,7wrong_func              ;поддерживаются только первые 6 функций. для других случаев отправляем исключение

;в зависимости от функции выполняем действия

;чтобы заменить условные переходы на безусловные, вводим конструкцию:

ldi R17,1R17,R16no_1read_coils_1:R17R17,R16no_2read_coils_2:R17R17,R16no_3read_hold_regs_3:R17R17,R16no_4read_input_regs_4:R17R17,R16write_hold_regwrite_coil_hold_reg:

lds R16,startreg   ;считываем стартовый регистрR16                                     ;уменьшаем на 1, так как в modbus данные передаются начиная с 1R16,16                             ;16 - кол-во данныхout_of_range   ;если считать нужно больше, чем есть, отправляем исключениеR16,R16                     ;умножаем на 2, так как регистры 16-битные

ldi ZL,low(hold_regs)ZH,high(hold_regs)

add ZL,R16                            ;загружаем в Z смещение запрашиваемых данныхR16,0ZH,R16                      ;если произошёл перенос, необходимо это учестьZ,DATACH                 ;данные хранятся в формате СтаршийБайт:МладшийZ,1                    ;запись в следующий байтZ,DATACLR21,0xFF                ;если не стоит флаг броадкаст, посылаем ответ

breq perform_endsend_echo_end:

_func:

;отправить исключение с кодом 1. код хранится в r25

ldi r25,1exception

_of_range:

;отправить исключение с кодом 2. код хранится в r25

ldi r25,2exception

_value:

;отправить ексепшн с кодом 3. код хранится в r25

ldi r25,3exception

_coils:                           ;функция для чтения coils или discrete input

cpi R21,0xFF                ;если не стоит флаг броадкаст, посылаем ответ

breq perform_end

R16,startregR16  R16,DATACLR16,9     ;8 - кол-во данныхout_of_rangeR16,DATACL

MES,s_id             ;отправка s_id и f_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,1                ;посылаем 1 байтgetCRCsend_byteMES,f_idMES,1f_coilsMES,0get_status_regno_coils_coils:ZL,low(coils)ZH,high(coils)MES,Z_coils:R16,0no_lsr_loop:MESR16

cpi R16,0lsr_loop_lsr:           ;теперь нужно наложить маску и отправить DATA и CRC

rcall get_maskMES,R25getCRCsend_byteMES,CRCLsend_byte         MES,CRCHsend_byteperform_end_value_copy:wrong_value

_of_range_copy:wrong_value

_end_copy:perform_end

read_hold_regs:R21,0xFF               ;если не стоит флаг броадкаст, посылаем ответ

breq perform_end_copy R16,startregR16R16,16out_of_range_copyR16,DATACLR16,17out_of_range_copyR16,DATACLR16,R16

ZL,low(hold_regs)ZH,high(hold_regs)ZL,R16R17,0ZH,R17

MES,s_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,DATACL

add MES,MES                       ;отправляем удвоенное число байт

rcall getCRCsend_byte

R17,DATACL_hold_loop: MES,Z+                  ;сначала старший байт.

ld R16,Z+            ;потом младшийsend_bytegetCRCMES,R16send_bytegetCRCR17R17,0send_hold_loopMES,CRCLsend_byteMES,CRCHsend_byteperform_end

_input_regs:

cpi R21,0xFF                ;если не стоит флаг броадкаст, посылаем ответ

breq perform_end_copy R16,startregR16R16,32out_of_range_copyR16,DATACLR16,33out_of_range_copyR16,DATACL

ZL,R16ZH,0

MES,s_idCRCL,$FFCRCH,$FFgetCRCsend_byteMES,f_idgetCRCsend_byteMES,DATACL

add MES,MES                       ;отправляем удвоенное число байт

rcall getCRCsend_byte

R17,DATACL

send_input_loop:MES,0                  ;сначала старший байт.

ld R16,Z+            ;потом младшийsend_bytegetCRCMES,R16send_bytegetCRCR17R17,0send_input_loopMES,CRCLsend_byteMES,CRCHsend_byteperform_end

_coil:R16,startregR16R16,8   ;8 - кол-во данныхout_of_range_copy2DATACL,0wrong_value_copy2DATACH,DATACLDATACH,0xFFwrong_value_copy2         ;флаг Z установится, если DATACH=DATACL=0, или если DATACH=0xFF, DATACL=0ZL,low(coils)ZH,high(coils)R17,Zbit_to_maskDATACH,0xFFset_bitR18R17,R18Z,R17send_echo_bit:R17,R18Z,R17

sbrc R21,0           ;если не стоит флаг броадкаст, посылаем ответ

rjmp perform_endsend_echo

_of_range_copy2:out_of_range_value_copy2:wrong_value

_echo:

         lds MES,s_id

         rcall send_byte

         lds MES,f_id

         rcall send_byte

         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_byteperform_end

_byte:TMP,UCSR1ATMP,UDRE1send_byteUDR1,MES             ;MES - буфер

ret

:R21,0                  ;если не стоит флаг броадкаст, посылаем ответ

rjmp perform_endCRCH,$FFCRCL,$FFMES,s_idgetCRCsend_byteMES,f_idr17,$80MES,r17getCRCsend_byteMES,r25                            ;r25 - код ошибкиgetCRCsend_byteMES,CRCLsend_byteMES,CRCHsend_byteperform_end

:

         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

         noxor:

         LSR CRCL

         bst CRCH,0

         bld CRCL,7

         lsr CRCH

         dec COUNTER

         brne crcloop

_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

_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

_status_reg:

         brbc 7,no_inter

         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:


Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!