декодирование манчестерского кода включает в себя
Манчестерский код. Синхронизация, приём и декодирование
Итак, для начала поговорим о том, что представляет собой «манчестерское» кодирование.
В «манчестерском» коде единица кодируется переходом сигнала в середине битового интервала из состояния «OFF» в состояние «ON», а ноль — наоборот, переходом сигнала в середине
битового интервала из состояния «ON» в состояние «OFF».
Что такое состояния «ON» и «OFF»?
Состояния сигнала «ON» и «OFF» — это логические состояния. В общем случае «OFF» — это неактивное состояние, такое же, как при отсутствии какого-либо обмена вообще, а «ON» — это активное состояние, то есть такое, которое как-либо отличается от неактивного. Поэтому, несмотря на то, что на картинке справа состояние сигнала «ON» показано высоким уровнем сигнала, а состояние «OFF» показано низким уровнем, это не нужно понимать буквально (просто с высоким и низким уровнем картинка привычнее и нагляднее). На самом деле состояния «ON» и «OFF» могут быть закодированы совершенно по-разному. Например, ИК-пульты кодируют эти состояния наличием или отсутствием импульсов на определённой частоте, интегральные фотоприёмники (у которых чаще всего неактивным является высокий уровень сигнала на выходе) выдают код, в котором «ON» закодировано низким уровнем, а «OFF» — высоким и т.д.
Длительность нуля и единицы в манчестерском кодировании одинаковая, то есть длина сообщения не зависит от того, сколько в сообщении нулей или единиц, а зависит только от общего количества бит.
Ключевым свойством «манчестерского» кодирования является то, что при передаче каждого бита обязательно присутствуют оба состояния сигнала: «ON» и «OFF» (ещё раз смотрим на рисунок вверху). То есть, во время передачи каждого бита сигнал должен хотя бы раз изменить своё состояние. То есть «манчестерский» код может состоять только из интервалов одинарной, если соседние биты одинаковые, и двойной, если соседние биты отличаются, длительности (это продемонстрировано на рисунке слева).
Описанное свойство позволяет дополнительно синхронизировать приёмник с передатчиком при приёме каждого бита, определять — может ли вообще принимаемый код быть «манчестерским», диагностировать конец сообщения или «потерю» сигнала передатчика.
Скажем, если предположить, что частота передатчика не может скачком измениться более чем в 1,5 раза, то отсутствие изменения состояния сигнала в течении 3-х полубит можно смело трактовать как конец сообщения или «потерю» сигнала передатчика (если мы заранее знаем длину сообщения). Или, например, при исследовании какого-то неизвестного кода, если мы видим, что в коде присутствует более двух вариантов интервалов между состояниями «ON» и «OFF», то можно однозначно сделать заключение о том, что исследуемый код не «манчестерский».
Надеюсь, с тем, что такое «манчестерский» код, всё более или менее понятно, поэтому переходим к следующему вопросу — как этот код принимать и декодировать.
Ну, очевидно, что определить начало передачи данных можно по изменению состояния сигнала, воспринимаемого приёмником, с «OFF» на «ON». Однако тут есть один нюанс. Поскольку передача единицы тоже начинается с состояния «OFF», то при первом изменении сигнала из «OFF» в «ON» мы совершенно никак не сможем диагностировать что это — середина передачи единицы или начало передачи нуля. Единственное, что тут можно сделать — это заранее условиться, какой бит должен передаваться первым (то есть ввести специальный старт-бит, значение которого будет всегда строго определено).
Всё, теперь, если мы знаем с какого бита посылка начинается, знаем длительности интервалов состояний «ON» и «OFF», наш приёмник обладает точным, стабильным генератором и мы
точно знаем сколько хотим принять бит, то можно составить первый простейший алгоритм восстановления исходной, закодированной «манчестерским» кодом посылки:
Вариант второй. Мы знаем с какого бита посылка начинается, знаем длительности интервалов «ON» и «OFF», наш приёмник обладает стабильным генератором, но мы ничего не знаем о длине сообщения. В этом случае можно воспользоваться тем свойством манчестерского кода, что сигнал не может оставаться постоянным в течении 3-х и более полубит. То есть, момент, когда сигнал в течении 3-х полубит остаётся в состоянии «OFF» можно считать концом сообщения. Алгоритм восстановления исходного кода в этом случае может выглядеть так:
Вариант третий. Мы знаем с какого бита посылка начинается, но не знаем длительности интервалов, в течении которых сигнал находится в состоянии «ON» и «OFF». Что нам делать в
этом случае? Если вы по счастливой случайности знаете значение не только первого бита, но и второго, — значит вам точно известно, через какие интервалы (через целый бит или через половину)
произойдут первые 2 переключения и вы с лёгкостью можете необходимые интервалы засечь или, научно выражаясь, — синхронизировать приёмник с передатчиком.
(Ага, вот мы и раскусили зачем у RC-5 целых 2 стартовых бита. Кстати, в сетях ethernet, где тоже используется «манчестерское» кодирование, для начальной синхронизации используется целая 56-битная преамбула).
Далее можно легко воспользоваться первым или вторым из приведённых выше алгоритмов.
Ну и, предположим, ещё один вариант. Мы знаем первые два бита посылки, но наш генератор — полное говно, хотя и работает (или, говоря научными терминами, мы можем гарантировать, что за время, равное длительности полубита, частота генератора не может измениться в 1,5 раза и более). Тут-то как быть?
Да просто надо по каждому новому фронту заново вычислять значения длительности полубита и целого бита. То есть, другими словами, нужно синхронизировать приёмник с передатчиком не один раз в самом начале, а по каждому новому фронту (под фронтом будем понимать переключения между состояниями «ON»/»OFF»), благо при манчестерском кодировании у нас новый фронт присутствует в каждом передаваемом бите.
Короче говоря, рассматривать разные комбинации можно долго, запомните главное преимущество, за которое «манчестерский» код всем так полюбился: при передаче каждого бита существует изменение состояния «ON»/»OFF», которое даёт возможность синхронизировать передатчик и приёмник.
Кроме описанного выше, существует ещё, так называемое, «разностное» или «дифференциальное» «манчестерское» кодирование. В данном случае при передаче нуля битовый интервал начинается с изменения состояния сигнала на противоположное, а при передаче единицы — состояние сигнала в начале битового интервала не изменяется. В остальном всё так же, как и в обычном «манчестерском» кодировании — в середине битового интервала состояние сигнала обязательно меняется на противоположное (смотрим рисунок слева).
Самодельные ИК-пульты и приёмники сигналов дистанционного управления
🏠 Интернет Всего 🚗
Введите число и нажмите «Encode Manchester!«:
Теперь давайте внимательно посмотрим на картинку и попробуем проанализировать и перечислить основные преимущества и недостатки преобразования данных в Манчестерский Код:
Комбинация логических уровней 11 однозначно говорит о последнем принятом 0, а комбинация 00, соответственно, говорит о 1. Таким образом после одной из них приёмник синхронизируется
Не может идти последовательно более двух одинаковых логических уровней, т.е. комбинация типа 111 или 000 невозможна
Сначала пишем тест
Теперь сами тесты. Более простой задачей является преобразование данных в Манчестерский Код (encode), поэтому с неё, пожалуй, и начнём:
Преобразовать данные в Манчестерский Код очень легко:
Предположительная реализация процесса декодирования Манчестерского кода:
Сборка и запуск тестов
Если Вы работаете в связке Windows + Visual Studio, необходимо выполнить следующее:
При использовании Linux + gcc g++:
Практика
Манчестерский код. Часть 2. Декодирование данных.
Закодировав данные манчестерским кодом следуем по наиболее логичному пути, который ведет нас к процессу декодирования ) Поставим задачу взять непосредственно те самые данные, которые были сгенерированы в предыдущей статье, подать их на декодер и сверить полученный результат с исходной последовательностью.
За основу возьмем проект из уже упомянутой статьи и добавим в него функции для декодирования манчестерского кода. Сигнал генерируется на PA3, возьмем другой пин, и на него подадим этот сигнал физически замкнув ножки. Пусть будет PA4, почему бы нет:
Выбранный вывод настраиваем на генерацию прерываний при изменении уровня сигнала, по обоим фронтам сигнала, потому что нам нужны будут и передний и задний фронты для работы:
Помимо этого в CubeMx ничего добавлять или менять не потребуется, таймер будем использовать тот же, насколько я помню — TIM2.
Декодирование манчестерского кода.
Для начала пройдемся по физической сути процесса декодирования. Я его разбил на несколько этапов, что нам добавит наглядности и прозрачности в рассмотрении протекающих явлений. Итак, список такой:
Синхронизация по фронтам.
Берем рассмотренный нами манчестерский код:
Нижний сигнал мы будем принимать на входе контроллера. И, в целом, все просто — если передний фронт (переход от низкого уровня к высокому, 0 => 1), то это должно декодироваться в бит «1». Если задний фронт (переход от высокого уровня к низкому, 1 => 0), то это закодированный бит «0».
Но из графика очевидно, что нам нужно брать не все фронты, а вполне определенные, они помечены зеленой меткой. Напротив, изменения сигнала с красными метками мы должны пропускать.
Не проблема — зная период кодирования можно отфильтровать «ненужные» перепады, поскольку время между значимыми пепепадами гарантированно соответствует этому периоду. Поскольку в реальном мире все неидеально, длительности могут плавать, поэтому зададим величину в 75% от периода кодирования. Если время между фронтами больше, то работаем, если время меньше, пропускаем фронт.
Осталось определить отправную точку. То есть для того, чтобы использовать эту логику с длительностями и периодами, необходимо знать хотя бы один значимый фронт (с зеленой меткой), чтобы относительно него уже вести отсчет. Для этой цели послужит следующий механизм…
Опять же из графика можно заметить, что два перепада разного(!) направления, разделенные периодом кодирования, гарантированно являются значимыми. И не важно, передний ли это фронт после заднего или задний после переднего. В этих случаях получаем:
В итоге процесс, названный мной синхронизацией по фронтам, заключается в комбинации этих двух механизмов. Сначала ищем соседние фронты, разделенные периодом, и с разным направлением перепада. Затем относительно этих «верных» фронтов берем следующие, также разделенные временем, равным (в реальном мире примерно равным) периоду кодирования.
Синхронизация по данным.
Переходим ко второму этапу — синхронизации по данным. Снова отсылка к статье про манчестерское кодирование, где мы специально добавили к передаваемым данным байты синхрополя. Они необходимы для того, что определить, где начинаются информационные байты.
Здесь реализация будет заключаться в следующем. Возьмем 16-битную переменную, в которую будем последовательно, бит за битом, сохранять декодированные значения. Выглядит это все так:
После каждого нового бита сохраняем его в переменную, сдвигая принятые ранее биты. В результате, в определенный момент времени значение в этой переменной будет равно значению синхрополя. Именно этот момент нам и требуется отловить, чтобы в дальнейшем относительно него считывать последующие биты.
Синхронизировавшись таким образом по данным, начинаем третий этап, собственно, сохранение и анализ информационных, то есть полезных, данных.
Декодирование данных.
Здесь механизм все тот же — смотрим на фронты, разделенные периодом кодирования. Разница только в том, что сохранять их будем не в переменную со сдвигом, а в массив. Физически суть все та же, так что переходим к практической реализации.
Декодирование манчестерского кода на STM32.
Вход для данных мы активировали, поэтому сразу к делу. Работаем в тех же файлах:
По той же схеме, начинаем с добавления констант:
Я привожу здесь только то, что добавляется относительно текущего проекта с манчестерским кодированием. Полный код будет под спойлерами в конце статьи. Там же и ссылка на полный проект для STM32F103C8.
Итак, таймер у нас тот же, значит и период его такой же — 10 мкс. Рассчитываем по аналогии MANCH_DECODE_TIMER_MAX — это период кодирования, он же — длительность передачи одного бита. Кроме того, задаем порог, который мы обсудили, равный 75% от периода кодирования:
Объявляем возможные состояния декодера и, для удобства, фронтов сигнала:
Структура для хранения данных остается неизменной:
По поводу состояний декодера:
Теперь в файле manchester_code.c объявляем переменные (я опять же привожу только отличия относительно исходного проекта):
Все, в принципе, понятно из названий:
Следующий шаг, вспомогательные функции. Определение уровня на входе PA4:
Далее по аналогии с GetDataBit(), которую мы использовали при кодировании, создаем функцию SetDataBit(), выполняющую обратную операцию, то есть установку нужного бита в массиве данных data[] в структуре:
В callback’е по переполнению таймера пока добавляем только инкрементирование счетчика:
Весь основной функционал будет в прерывании по изменению уровня сигнала на PA4. Рассмотрим полный код, затем пройдемся по нему подробно:
Для начала определяем уровень сигнала на входе, что позволяет нам судить о фронте импульса:
На выходе из функции обновляем значение для предыдущего фронта:
Остальной функционал в switch (decodeState) и подчиняется тем алгоритмам и явлениям, которые мы обсудили в начале статьи:
В исходном состоянии запускаем процесс декодирования через флаг decodeData.active, что по сути приводит к тому, что в прерывании по таймеру начинается процесс инкрементирования счетчика decodeTimerCnt.
Ищем соседние фронты с разными перепадами, разделенные периодом кодирования. Для этого сравниваем счетчик с величиной порога MANCH_DECODE_TIMER_THRESHOLD. Если находим последовательные перепады передний фронт-задний фронт, то это означает, что декодированы биты «1» и «0», которые помещаем в decodeData.bitStream:
После выполнения этих операций:
Это соответствует тому, что сначала мы декодировали «1», затем «0». Аналогично при противоположных фронтах, только с другим значением:
На этом декодер готов перейти в следующее состояние:
Начинаем синхронизацию по данным. Для этого продолжаем заполнять decodeData.bitStream битами. Если обнаружили «1», то выставляем в «1» старший бит:
В случае «0» ничего не делаем, за счет сдвига получим требуемое значение:
Вот и все! Спокойно и непринужденно анализируем декодированные биты в ожидании совпадения со значением синхрополя:
В случае совпадения закидываем байты в decodeData.data[], обнуляем счетчики битов и байт, и переводим декодер в состояние DATA_SYNC.
После чего продолжаем делать то же самое, за исключением того, что биты записываем не в decodeData.bitStream, а в массив decodeData.data[] при помощи SetDataBit(). Естественно, этому сопутствует увеличение индексов текущих битов и байт.
Логика такая же, при декодировании бита «1» — выставляем его в массиве — SetDataBit(&decodeData, 1). Если «0», то ничего не требуется, нужный «0» окажется на своем месте за счет увеличения индекса текущего бита.
Когда количество декодированных байт равно максимальному количеству (MANCH_DATA_BYTES_NUM + MANCH_SYNC_BYTES_NUM), заданному в хэдере, останавливаем процесс:
В этом тестовом проекте у меня задано 10 байт — 8 информационных + 2 байта синхрополя.
По окончании декодирования вызываем callback MANCH_DataReadyCallback(), объявленный в этом же файле чуть выше:
Работает как и с колбэками из HAL, то есть эту функцию можно переопределить в другом файле и вставить свой код. Например, я добавил в main.c:
Вызывается еще одна вспомогательная функция из manchester_code.c, которая лишь переводит декодер в исходное состояние:
Осталось учесть один немаловажный нюанс. В данной форме процесс декодирования завершится только тогда, когда принято MANCH_BYTES_NUM байт. Если исходная посылка будет короче, то произойдет коллапс, данные перепутаются. Поэтому добавим еще одно условие, по которому декодирование признается завершенным. Если в течение некоторого интервала времени, гарантированно превышающего длительность кодирования, не было обнаружено изменений уровня на входе, то останавливаем работу. Я возьму время, равное 3-м периодам кодирования. Обновляем код прерывания по таймеру:
Теперь все в норме, тестируем. В main() у нас по-прежнему осуществляется манчестерское кодирование:
Для проверки декодирования встаем брейкпинтом на callback в main.c:
В результате в полученных данных получаем полное соответствие исходным данным 👍
И на этом заканчиваем работу с кодом Манчестер-II, надеюсь, все было понятно, тем не менее вопросы на форуме или в комментариях категорически приветствуются )
P. S. Да, кстати, здесь при декодировании мы использовали информацию о периоде кодирования, но эта величина не всегда известна. Если будет спрос и интерес, опишу как провести аналогичную деятельность по декодированию при отсутствии априорной информации о длительности передачи бита.
Шифратор и дешифратор кода “Манчестер-II”
Вводные замечания
Рассмотрим процессы формирования и последующей дешифрации линейного сигнала типа “Манчестер-II”. В результате дешифрации из линейного сигнала извлекаются данные и сопровождающие их синхроимпульсы.
Сигнал “Манчестер-II” (рис.7.10) можно получить суммированием по модулю два (с помощью логического элемента Исключающее ИЛИ) сигналов NRZ и С (данных и синхросигнала). Основная особенность этого сигнала состоит в том, что он никогда не замирает. При передаче длинной цепочки единиц частота сигнала “Манчестер-II” совпадает с частотой fс синхросигнала С. То же наблюдается и при передаче
Рис.7.107. Сравнение частотных свойств сигналов NRZ и “Манчестер-II”
длинной цепочки нулей. Поэтому даже в том случае, когда частота исходного сигнала NRZ стремится к нулю (F 0), частота сигнала “Манчестер-II” остается постоянной (моментами смены фазы сигнала пренебрегаем). При передаче чередующейся последовательности нулей и единиц частота сигнала “Манчестер-II” совпадает с частотой сигнала NRZ.
Идея поддержания синхронизма между приемником и передатчиком поясняется рис.7.11. На рисунке показаны три битовых интервала Т. Предположим, что приемник уже синхронизирован с передатчиком. Тогда, обнаружив в середине первого битового интервала фронт сигнала (он обязательно должен быть: положительный либо отрицательный), приемник запускает внутренние часы.
Рис.7.108. Временная диаграмма передачи кода “Манчестер-II”. Обеспечивается коррекция хода часов приемника при передаче каждого бита
Через интервал времени, равный Т, обнаруживается положительный фронт сигнала (передается лог. 0). Приемник вновь запускает часы (“забывая” старые их показания), т.е. переносит начало отсчета времени на один битовый интервал вправо. Далее процесс повторяется. Как видим, не происходит накопления ошибок, связанных с отсчетом времени.
Поиск очередного фронта сигнала в середине следующего битового интервала ведется в некотором доверительном интервале. Из “геометрии” рисунка следует, что область поиска фронта сигнала может достигать 0,5Т. Это означает, что независимо от длины цепи передаваемых битов допустимое рассогласование хода часов передатчика и приемника может приближаться к 25% (другими дестабилизирующими факторами пренебрегаем). Для сравнения: допустимое рассогласование хода часов передатчика и приемника при использовании старт-стоповой посылки (интерфейсы RS-232, RS-422 и другие) составляет всего 4%.
Схемы шифратора и дешифратора
Перейдем к конкретным решениям. Рассмотрим упрощенную схему передачи данных в коде “Манчестер-II” (рис.7.12). В схеме отсутствует гальваническая развязка с линией, не показаны элементы электрического согласования и т.п.; тем не менее она работоспособна при условии, что линия связи достаточно короткая.
Рис.7.109. Упрощенный пример построения канала связи для передачи данных в коде “Манчестер-II” (рассмотрено одно направление передачи)
Как уже отмечалось, шифратор кода “Манчестер-II” чрезвычайно прост и может быть выполнен на двухвходовом элементе Исключающее ИЛИ (рис.7.13).
Фильтр предназначен для подавления кратковременных импульсов, которые могут возникнуть из-за неидеального совпадения отрицательного фронта сигнала С с отрицательным или положительным фронтом сигнала NRZ.
Дешифратор кода “Манчестер-II” (рис.7.14) [22] содержит формирователь F импульсов, счетный триггер СТ и D-триггер. Как
Рис.7.110. Шифратор кода “Манчестер-II”
следует из временной диаграммы, приведенной на рис.7.15, отрицательные импульсы j на выходе формирователя импульсов возникают всякий раз, когда сигнал “Манчестер-II” изменяет значение (0 1 или 1 0). Сигнал k восполняет “недостающие” импульсы j. (Временную диаграмму сигнала k можно нарисовать, используя только диаграмму сигнала j, следовательно, сигнал k можно получить из сигнала j с помощью логической схемы [22].)
Рис.7.111. Дешифратор кода “Манчестер-II”
Так как импульс k поступает на вход S установки единицы счетного триггера СТ, то в момент t0 этот триггер безусловно перейдет в единичное состояние, и в дальнейшем сигнал С* на его инверсном выходе будет в точности повторять сигнал С от источника данных (разумеется, с некоторой задержкой).
Начиная с момента t1, т.е. по прошествии одного периода тактовых импульсов от момента t0, код NRZ*, снимаемый с выхода триггера ТТ, полностью совпадает с исходным кодом NRZ (с точностью до задержки передачи). Таким образом, для того чтобы заставить приемник войти в синхронизм с передатчиком, достаточно на линии NRZ создать
Рис.7.112. Временные диаграммы работы дешифратора кода “Манчестер-II”
переход сигнала из 0 в 1. Последующая цепочка битов любой длины, передаваемая по линии NRZ, будет в точности повторена на линии NRZ* приемника. Это же относится и к синхросигналам.
Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.