Работа с ведомым устройством при помощи билиотеки AXLIB


• О проекте
• Обратная связь
• Полезные ссылки
• Полезные программы
• Друзья сайта


Последние комментарии

Аби: Подключение микроконтроллеров к шине RS-485
написано просто о...

Анатолий: Джойстик для денди на stm32
Автору 100500 рес...




           

Библиотека для AVR





AXLIB Генератор





Помощь сайту


				

Работа с ведомым устройством при помощи билиотеки AXLIB

	
	
	

Дата: 4 Ноября 2015. Автор: Алексей

	
	
Пример работы мастера и два слейва.
Программа виртуальный Мастер для отладки протокола MHBUS.

В данной статье я опишу принцип работы с ведущим устройством воспользовавшись библиотекой AXLIB
Ставлю задачу. Я буду от лица мастера (Терминал) формировать и передавать пакеты Слейву, а он в зависимости от запроса будет мне отвечать. Для визуализации происходящих процессов у ведущего, последний будет их выводить на ЖК дисплей 4х20. Я постараюсь продемонстрировать все возможные варианты запрос-ответов, а именно:

1) Присвоение адреса ведущему при широковещательной передаче по ID устройства.
2) Получение ответа от ведущего устройства с подтверждением присвоения ему адреса.
3) Изменение адреса ведущего устройства по текущему адресу.
4) Подтверждение ведущим изменения адреса на полученный от ведомого.
5) Передача пакета данных и ответ ведущему.
6) Имитация ошибок пакета и получение ведущим сообщения об ошибках от ведомого.

План ясен, действуем. Для тех кто не знаком с работой самого протокола MH-BUS, рекомендую почитать вот эту статью. Если Вы уже знакомы с основными принципами протокола, то начинаем программировать МК. Для теста протокола я решил использовать МК ATmega8A. К МК я подключил ЖК дисплей 4х20 следующим образом:

Ножка МК    Ножка ЖК
------------   -------------
PORTC 0      RS
PORTC 1      E
GND             RW
PORTD 4      PB4
PORTD 5      PB5
PORTD 6      PB6
PORTD 7      PB7

Для связи с терминалом необходимо соединить ПК-RX c МК-TX, а ПК-TX с МК-RX.
Все, со схематикой закончили, переходим к программе. Первым делом настраиваем, как всегда, файл main_init.h

//-------------------------------------------------------------------------
//  Протокол MH-BUS для передачи данных между устройствами
//	Для работы с шиной RS-485 необходимо выбрать порт и пин на данном порту
//	для управления передачей и скорость в БОД.
//
// Также необходимо задать уникальное ID на шине в диапазоне от 1 до 65535
//-------------------------------------------------------------------------

// Раскоментируйте данную строку, если требуется автоматическое
// управление потоком данных драйвера RS-485

//#define MH_BUS_RS485_ON				// Включить потдержку RS-485

// Раскоментируйта данною строку если необходимы функции режима Мастера

//#define MH_BUS_MASTER					// Включить в режиме Мастер

// Раскоментируйта данною строку если необходимы функции режима Слейва

#define MH_BUS_SLAVE					// Включить в режиме Слейв

// Текущие данные необходимы только в режиме Слейв
#if defined(MH_BUS_SLAVE)
	#define MH_BUS_SLAVE_ID	65530U	// Идентификационный номер устройства
#endif
	
#define MH_BUS_USART_BOD        38400		// Скорость обмена 
	
// Текущие данные необходимы только при использовании автоматическим 
// управлением потоком дпнных драйвера RS-485
#if defined(MH_BUS_RS485_ON)
	#define RS_485_DDR	DDRC	// Порт на котором будет выбран пин упраления
	#define RS_485_PORT	PORTC	// Порт на котором будет выбран пин упраления
	#define RS_485_PIN	0   	// Пин управления передачей данных
#endif

#define MH_BUS_RS485_ON Эта строчка отвечает за автоматическое управление потоком данных при подключении микросхемы драйвера RS-485. Для того чтобы мне здесь не распинаться, что это за микросхема и куда нужно подключать выводы, просто отправляю не знающих вот сюда. Я здесь подробно описал как работает шина RS-485 и какое железо необходимо для реализации данной шины у МК.
#define MH_BUS_MASTER Раскомментировав данную строку, можно перевести устройство в режим Мастер. ВАЖНО! Нельзя одновременно использовать оба режима. Либо Мастер, либо Слейв.
#define MH_BUS_SLAVE Раскомментировав данную строку, можно перевести устройство в режим Слейв. ВАЖНО! Нельзя одновременно использовать оба режима. Либо Мастер, либо Слейв. С учетом того что наш МК будет слейвом, то нужно снять комментарий с данной строки.
#define MH_BUS_SLAVE_ID 65530U В этой строке задается ID устройства. Он нужен для поиска устройства при широковещательной передаче если обо всех устройствах мастер знать не знает. То есть если у мастера есть только ID устройств, а адресов нет. Такой принцип сделан для упрощения адресации ведущих устройств. Буковка U в конце ID должна стоять обязательно!
#define MH_BUS_USART_BOD 38400 Скорость в бодах. Должна быть одинаковая как у мастера, так и у слейва.
#define RS_485_DDR DDRC
#define RS_485_PORT PORTC
#define RS_485_PIN 0
Эти строки нужны только для работы с драйвером RS-485. Задают порт и пин который будет подключен к управляющим ножкам драйвера.
После настройки данного файла, переходим к написанию программы. Так... Давайте ка сначала пробежимся по алгоритму. Что за чем у нас будет идти.

1) Проверить на наличие пакета.
2) Если пакет пришел без ошибок, обработать его.
3) Если в пакете задан адрес широковещательной передачи, то проверить не требуется ли заменить адрес ведомого и если требуется, то заменить и ответить об этом ведомому. Если нет команды на замену, то выполнить требуемые действия.
4) Если в пакете указан чужой адрес, то игнорируем пакет.
5) Если адрес в пакете совпадает с нашим, то проверяем команду. Если требуется заменить адрес ведомого, то меняем его и отвечаем об этом ведомому. Если команда требует произвести какие-либо действия, то выполняем их и отвечаем об этом мастеру.

Начнем с простого. Проверка на наличие пакета.

temp = mhbus_read(); // Проверка на наличия полученного пакета

После вызова функции mhbus_read() в переменной temp будет лежать тип состояния. А уже по типу состояния можно определить что у нас на шине.

1) FALSE - ничего не пришло.
2) MH_BUS_OK - Пакет получен. Целостность пакета не нарушена. Команда в разрешенном диапазоне.
3) MH_BUS_START_FAIL - Получен пакет, но при проверке обнаружен битый СТАРТ-БАЙТ.
4) MH_BUS_STOP_FAIL - Получен пакет, но при проверке обнаружен битый СТОП-БАЙТ.
5) MH_BUS_CON_SUM_FAIL - Получен пакет, но при проверке обнаружено не совпадение контрольной суммы.
6) MH_BUS_COM_FAIL - Получен пакет, но при проверке обнаружено значение команды вне разрешенного диапазона.

Нас интересуют лишь первые два. Если приходят последние три состояния, то мы просто об этом говорим в ответном пакете мастеру. Первый ответ говорит о том что к нам никто ничего не прислал, а вот второй ответ говорит о том что пакет получен и проблем с ним не было. Далее проверяем адрес.

// Проверка не совпал ли адрес в пакете с адресом данного устройства
if(MH_BUS_ADD == mhbus_rd_add()) 
// Проверка на передачу пакета при широковещательной передаче
if(MH_BUS_ADD == MH_BUS_ALL_ADD) 

Как видно из комментариев, можно понять, что в первом варианте проверяем на валидность адреса, а во втором на широковещательную передачу. Но есть и третий вариант. Если адрес не относится ни к широковещательной, ни к нам, значит пакет адресован кому-то еще и мы просто прекращаем его обрабатывать. Начнем с широковещательной передачи.

if(MH_BUS_COM == MH_BUS_RE_ADD) // Если необходимо изменить адрес

Этой строкой мы проверяем на получение команды по замене адреса и если данная команда пришла, то меняем адрес.

if(mhbus_id()) // Если ID устройства совпало с передаваемым в пакете

В этой строке происходит вызов функции для проверки переданного ID. Данная функция ничего не принимает в качестве аргумента, так как все данные уже есть. Наш ID мы задали в main_init.h, а передаваемый пришел в пакете от ведущего. Далее после проверки на совпадение ID функция вернет либо TRUE, либо FALSE. Соответственно либо совпал, либо нет. Если совпал, то сначала меняем адрес вызвав функцию BYTE mhbus_wr_add(BYTE add), а та в свою очередь возвращает значение либо TRUE, либо MH_BUS_ADD_OVF. Ответ MH_BUS_ADD_OVF говорит о том что адрес на который хочет заменить Мастер вышел за пределы допустимого диапазона, а именно меньше единицы или больше пятидесяти одного.

temp = mhbus_wr_add(MH_BUS_REG);

Именно в переменную temp и заносится ответ этой функции. Далее собираем пакет и отсылаем его Мастеру.

data[0] = MH_BUS_BYTE_1;
data[1] = MH_BUS_BYTE_2;

mhbus_write(MH_BUS_COM, MH_BUS_REG, data);

Честно говоря, здесь я накосячил. По идее нужно проверять значение возвращаемое функцией и в зависимости от того что в ней находится, собирать пакет. Но я уже снял видео на эту тему и мне не хочется переснимать его. Поэтому мы будем считать что адрес всегда в диапазоне разрешимого. В видео я буду запрашивать ведущего на замену адреса выше допустимого диапазона, но функция его не пропустит. То есть будет видно на ЖК что ошибку функция вернула, а в пакете передался старый адрес. А что было бы если передалась бы в пакете иная команда? А ничего, просто мы бы ввалились в тело условия обработки просто команды и сделали то, что просил нас мастер. Самое главное нужно запомнить то, что при широковещательной передаче ведущий возвращает пакет только при условии удачной замены адреса. При всех остальных запросах ведущий молчит. Это связано с тем, что бы несколько ведомых устройств не начали бы одновременно отвечать на широковещательный запрос. Понятно, да... С широковещанием покончили. Теперь давайте посмотрим что было бы если совпал адрес в пакете с нашим.

if(MH_BUS_COM == MH_BUS_RE_ADD)

Опять же проверяем на команду смены адреса. Если таковая пришла, то меняем его и возвращаем ответ Мастеру.

mhbus_wr_add(MH_BUS_REG);
mhbus_write(MH_BUS_COM, MH_BUS_REG, data);

Здесь не мешало бы тоже проверить на переполнение, но)))) Так вышло))) Ну и последнее, наш адрес совпал, но команда не по замене адреса. Да просто выполняем ее и говорим об этом Мастеру.

data[0] = 0x2D; // Пример возвращаем запрошенную температуру.

mhbus_write(MH_BUS_COM, MH_BUS_REG, data);

Возврат температуры лишь пример. Вместо data[0] = 0x2D; можно вписать реальный замер температуры и данный код можно считать готовым ведущим датчиком температуры)))

Архив с проектом

Пример работы мастера и два слева.
Программа виртуальный Мастер для отладки протокола MHBUS.

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

JW Player goes here



Олег    28.11.15 12:08

Прикольно.

Юрий    28.11.15 14:11

Спасибо! Протокол супер, все запустилось с первого раза. На скорости 9600 работает на ура. Теперь возник вопрос реализации мастера, чтобы опрашивать слейвы по очереди. Есть тема для новой статьи;-). Спасибо.

Алексей    28.11.15 20:35

Ну если это так актуально, то попробую завтра изобразить.

Олег    30.11.15 11:10

Возникла проблема с автоматическим управлением RS-485. При передаче, пакеты обрезаются. Не доходят последние два байта. Что может быть? Пробовал на 9600 и 38400. Всегда два последних байта теряются.

Алексей    30.11.15 17:01

Да, спасибо. Нашел багу и поправил. Теперь управление RS-485 работает как часы.

Олег    30.11.15 23:09

Спасибо, заработало. Присоединяюсь к Юрию и жду продолжения.

Юрий    21.12.15 01:44

Алексей! Ну где же наш мастер на аврке?

Алексей    21.12.15 09:04

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

Евгений    14.10.16 17:41

Алексей, выложи плиз схемку подключения max485 к МК со всеми необходимыми подтяжками, делаю на макетке,, так сказать для проверки

Алексей    14.10.16 17:59

А пожалуйста. Ссылка.

Алексейbird    07.09.18 15:09

Алексей, если у меня только один слейв, то я же могу вс команды передавать без указания адреса?
И второй вопрос: не уловил сути, в чём отличие ID и адреса слейва. Если мы записываем ID в саму прошивку, то накой тогда адрес и наоборот.

Алексей    07.09.18 19:28

ID это что-то вроде серийника. При его наличии можно менять адреса слейвов прям на линии. То есть меняется только сетевой адрес, а ID постоянный.

Алексейbird    23.01.19 18:59

Алексей, подскажи, отлаживаю устройство и нашёл такой глюк: если после передачи данных разорвать связь между компом и устройством, то после последующего восстановления связи устройство отвечает ошибкой 08. Не сталкивался с таким? Связь разрывается не во время обмена, а между. Помогает только сброс МК. Такой же глюк возникает если включить МК, а уже после присоединить его к компу.

Алексей    23.01.19 21:37

А на ПК терминал работает?

Алексейbird    24.01.19 20:34

Что значит работает? Я отсылаю запрос через терминал AA 00 52 00 00 00 00 00 00 52 55, это запрос на возврат четырех байтов, а получаю AA FF 52 08 01 F5 5F 25 02 D3 55. Я не использую адреса, только широковещательные пакеты, у меня один слейв в линии. А может это из-за этого?

Алексей    25.01.19 11:15

08 это ошибка битого старт байта. Нужно смотреть исходный код МК.

Алексейbird    26.01.19 14:03

Я знаю что за ошибка, ведь я ранее об этом написал. Вопрос был: "если после передачи данных разорвать связь между компом и устройством, то после последующего восстановления связи устройство отвечает ошибкой 08. Не сталкивался с таким? Связь разрывается не во время обмена, а между. Помогает только сброс МК. Такой же глюк возникает если включить МК, а уже после присоединить его к компу."

Алексейbird    26.01.19 14:34

И проблема в том, что момент соединения MHBUS воспринимает как первый принятый байт и ждёт оставшиеся. И вот когда приходят 11 правильных байт, MHBUS принимает 10(не забываем, что 1 уже принят), вычисляет их, понимает что это ошибка, выдаёт ошибку и ждёт следующие 10 байт, ведь первый байт с предыдущего приёма остался. И так по кругу. Т.е. это проблема конкретно MHBUSа. Он не умеет принимать начинать приём по правильному старт-байту, а просто принимает 11 байт и уже начинает их изучать, даже если приём этих байт был растянут во времени. Алексей, как поправить этот баг?

Алексей    26.01.19 15:58

Надо подумать.

Алексейbird    03.02.19 14:15

Алексей, самый простой вариант, это начинать отчёт по "правильному" стартовому байту. Т.е. увидел МК, что на вод пришел "АА" и начал отчитывать 11 байт.

Алексей    04.02.19 00:40

Надо переписать функционал. У меня есть законченая библиотека под STM32.

Алексейbird    09.02.19 14:00

Алексей, а в общем доступе новая библиотека будет?

Алексей    10.02.19 11:40

У меня сейчас очень мало времени и я даже не знаю когда с ней ковыряться. Я разобрался с системой создания проектов под седьмую студию и на этом застрял. Катастрофически не хватает времени.

Алексейbird    10.02.19 17:28

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

Алексей    10.02.19 20:34

Да. Создание файлов генератором для студии.

Алексейbird    10.02.19 22:35

Блин, жаль что у вас нет возможности обновить библиотеку( Хотя устройство на её основе уже готово, откладывать монтаж тогда не буду, отправлю, как говорится у нас, у айтишников, в продакшн, просто буду знать об этом баге. Ну и ждать новую версию библиотеки.

Алексей    10.02.19 23:17

Я в любом случае сяду за новую версию. Но чуть по позже. Скорее всего летом.

Алексейbird    15.07.19 10:39

Алексей, в библиотеке axlibusart.h есть вот такая функция:
BYTE usart_str_in(BYTE *str, BYTE count)
{
BYTE out = 0;
BYTE data = 0;
BYTE timeout = 0;

while(count > data)
{
data = GetData();

// Если в течении пол секунды данные не пришли
// то выйти из функции и вернуть 0
if(timeout >= 500)
{
ClearBuffer();
return out;
}

timeout++;
_delay_ms(1);
}

out = OutBufferStr(str, count);
return out;
}
меня привлекла вот эта строчка if(timeout >= 500), если я заместо 500 ставлю 50, то устройство работает нормально при условии описанным мною 26.01.19 14:03, а именно разрыв связи между мастером и слейвом. При 500 оставлял на продолжительное время(около 10 часов) работы устройства так и не возобновилась. А вот при 50 работа устройства нарушается где-то на 0.05 сек, и дальше идёт всё норм. Но мне кажется дело тут не в самой цифре, а в "размере" переменной, т.к. она обозначена как BYTE, а значение указано как 500, что несколько выходит за рамки BYTE(0-255). Что думаешь?

Алексей    15.07.19 14:59

Все верно. Изначально таймаут был 100, а потом изменил на 500, а тип переменной изменить забыл. Если сгенерить проект генератором, то там таймаут равен 250.

Алексейbird    15.07.19 16:19

и всё-таки 250 не совсем соответствуют 500, потому как при первом значении устройство норм работает, а вот при втором зависает. Хотя если тип указать как WORD, то и 500 норм работает)

Алексей    15.07.19 18:38

Ну естественно. Условие ждет 500, а там всегда максимум 255. Вот и зависание.

Aleksei_bird    15.07.19 19:52

Алексей, можешь пояснить код:
while ((!uart_flag) && timeout)
{
timeout = timeout - 1;
_delay_ms(1);
}

_delay_ms(50);

answer = usart_data();
это функция BYTE mhbus_read(BYTE test_add, BYTE timeout) библиотеки mhbus.h. Так вот зачем тут задержки, на что они влияют?

Алексей    15.07.19 22:07

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

Алексей    15.07.19 22:13

Алгоритм задумки такой:
В качестве аргумента функции передаем период ожидания пакета в миллисекундах. Функция топчется на месте ожидая, либо флага приема байта, либо окончания времени простоя. Если пришел байт, то ждем 50мс для получения остальных байт, а их 10 штук. Если время вышло, то ничего не получаем.

Алексей    15.07.19 22:15

Иногда возникают порывы все это довести до ума, но потом все тормозит STM32. Я полностью перешел на него и поэтому рубиться с AVR не хоца.

Aleksei_bird    15.07.19 22:37

странно, я убирал обе задержки, прием с ПК работает норм, а сама программа не тормозит и "летает")

Алексей    16.07.19 00:00

Значит я чего-то перемудрил)




Чтобы вставить ссылку используйте форму вида[url]http://www.адрес.ru[/url][text]текст ссылки[/text]
Чтобы вставить код используйте форму вида[code]код[/code]

Имя:   





  







Рейтинг@Mail.ru Яндекс.Метрика