что такое манчестерский код
🏠 Интернет Всего 🚗
Введите число и нажмите «Encode Manchester!«:
Теперь давайте внимательно посмотрим на картинку и попробуем проанализировать и перечислить основные преимущества и недостатки преобразования данных в Манчестерский Код:
Комбинация логических уровней 11 однозначно говорит о последнем принятом 0, а комбинация 00, соответственно, говорит о 1. Таким образом после одной из них приёмник синхронизируется
Не может идти последовательно более двух одинаковых логических уровней, т.е. комбинация типа 111 или 000 невозможна
Сначала пишем тест
Теперь сами тесты. Более простой задачей является преобразование данных в Манчестерский Код (encode), поэтому с неё, пожалуй, и начнём:
Преобразовать данные в Манчестерский Код очень легко:
Предположительная реализация процесса декодирования Манчестерского кода:
Сборка и запуск тестов
Если Вы работаете в связке Windows + Visual Studio, необходимо выполнить следующее:
При использовании Linux + gcc g++:
Практика
Манчестерский код. Синхронизация, приём и декодирование
Итак, для начала поговорим о том, что представляет собой «манчестерское» кодирование.
В «манчестерском» коде единица кодируется переходом сигнала в середине битового интервала из состояния «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», которое даёт возможность синхронизировать передатчик и приёмник.
Кроме описанного выше, существует ещё, так называемое, «разностное» или «дифференциальное» «манчестерское» кодирование. В данном случае при передаче нуля битовый интервал начинается с изменения состояния сигнала на противоположное, а при передаче единицы — состояние сигнала в начале битового интервала не изменяется. В остальном всё так же, как и в обычном «манчестерском» кодировании — в середине битового интервала состояние сигнала обязательно меняется на противоположное (смотрим рисунок слева).
Самодельные ИК-пульты и приёмники сигналов дистанционного управления
Манчестерский код. Часть 1. Кодирование данных.
Из названия статьи и обложки вы уже знаете, о чем сегодня пойдет речь, так что начинаем без предисловий сразу с главного. Итак, будем осуществлять генерацию манчестерского кода при помощи контроллера STM32. Разумеется, вначале разберемся, что из себя представляет данный тип кодирования в целом. Сразу могу анонсировать следующую статью, которая будет посвящена уже декодированию того сигнала, который мы сгенерируем сегодня, так что следите за обновлениями, добавляйте в закладки и подписывайтесь на всяческие наши группы )
Манчестерский код.
Манчестерское кодирование (код Манчестер-II) представляет из себя способ кодирования исходного двоичного кода двоичным же цифровым сигналом. Или в переводе — на входе единицы и нули, на выходе — единицы и нули, но порядок их следования, естественно, различается.
Существует два основных типа кодирования:
Мы будем использовать первый из этих вариантов, но это не так важно, поскольку переход на другой тип кодирования осуществить очень просто банальным инвертированием.
К особенностям манчестерского кода я бы отнес, в первую очередь:
А теперь минутка истории… Код получил свое название в честь университета Манчестера и компьютера Манчестерский Марк I, на котором он был впервые использован. Вот этот красавец:
Наступило время для дважды обещанного наглядного примера. Возьмем два произвольных байта, пусть будут и закодируем их. Напоминаю, используем манчестерский код по IEEE 802.3. И еще один момент — данные в пределах байта будут следовать от младшего бита к старшему. В общем, смотрим:
Каждому «0» на входе соответствует переход от высокого уровня к низкому на выходе (от «1» к «0»), для «1» — аналогично, но наоборот, от низкого к высокому.
На практике создать манчестерский код довольно просто. Для этого используем дополнительный виртуальный тактовый генератор. Виртуальным я его назвал по той причине, что программно этот генератор будет представлять из себя обычную переменную, значение которой будет меняться от «0» к «1» и обратно. При этом период генератора (период изменения значения переменной) должен быть в два раза меньше длительности передачи одного бита в исходной последовательности. Длительность передачи одного бита также называют периодом кодирования. Переходим к наглядности:
Для примера, первый бит: на входе — «1», на тактовом генераторе — «1», код Манчестер-II — «1» ^ «1» = «0».
Аналогично, для второго бита: на входе — «1», на тактовом генераторе — «0», код Манчестер-II — «1» ^ «0» = «1». Именно такой механизм будем использовать в программной реализации, к которой и переходим.
Генератор манчестерского кода на STM32.
По новой традиции в конце статьи я помещу не только ссылку на полный проект, но и полный код файлов под спойлерами. А пока настраиваем в STM32CubeMx все, что понадобится. В данном случае, это один из таймеров, который будет основой для всего, а также порт в режиме выхода, на который будем выводить манчестерский код. У меня выбор пал на TIM2 и PA3, само собой, можно использовать любые другие в точности также:
Не забываем включить интерфейс SWD для отладки и внешнее тактирование. Да, кстати, у меня будет STM32F103C8 с внешним резонатором на 8 МГц. Но, как и всегда, код будет универсальным, чтобы использовать его на любом другом STM32 без малейших проблем. Настройки тактирования традиционные:
То есть тактовая частота таймеров составляет 72 МГц. Настроим TIM2 на переполнение каждые 10 мкс. Для этого берем предделитель 72 (в CubeMx ставим 71), и период, равный 10:
При таком делителе получим частоту таймера равной:
То есть один «тик» таймера — 1 мкс. С периодом 10 получим желаемые 10 мкс. Использование именно такого периода для таймера не критично, эта величина устанавливается в коде и может быть любой другой. Об этом чуть ниже…
Кроме того, включаем прерывание для таймера и генерируем код. Далее CubeMx нам не понадобится. Добавляем в проект файлы для работы с кодом Манчестер-II:
Начнем с определения констант в manchester_code.h — задаем выходной порт и как раз-таки период таймера в мкс:
Также тут присутствует третье значение — MANCH_BIT_TIME_US — это длительность передачи одного бита (период кодирования), также в микросекундах. Прерывание по таймеру будет срабатывать каждые 10 мкс, длительность бита — 100 мкс, поэтому рассчитываем сразу, сколько раз таймер должен переполниться до достижения этого значения:
Далее о формате данных… Как мы уже выяснили, приемник может сам синхронизироваться с принимаемыми битами, с этим все гладко. Но приемник никак не может знать, где именно «начало» данных:
То есть, приемник самостоятельно настроился на анализ нужных фронтов (в следующей статье про декодер это и будет сделано 👍), но в этот момент он может оказаться в произвольном месте байта. А мы хотим принимать данные ровно в том порядке, в котором отправляем. Поэтому к исходным данным мы будет добавлять 16 бит со специальным кодом синхронизации. В качестве этого кода возьмем, например, значение 0xAA55:
На данный момент ограничимся тем фактом, что эти 2 байта нам нужно добавить в начало передаваемых данных, соответственно также закодировав их.
Для структуризации данных добавляем:
Пробегаем по полям:
По поводу размера массива — он задается все здесь же в manchester_code.h, пока мы вообще за пределы этого файла даже не выходили:
Пойдем поэтапно, manchester_code.c — объявление переменных:
Сейчас пойдет ряд функций для организации кодирования. Для управления выходом добавляем:
Далее по списку и по важности — функция, которая запускает процесс манчестерского кодирования:
В качестве аргументов — указатель на информационные данные и их размер (кол-во байт). Соответственно, передавать в функцию мы будем набор uint8_t значений, которые затем побитно будут кодироваться и выдаваться на PA3. На всякий случай напоминаю, что кодировать данные будем от младшего бита к старшему. То есть если на входе байт 0x22, что в двоичном виде представляет из себя 0b00100010, то на выходе будут последовательно закодированные «0», «1», «0», «0», «0», «1», «0», «0».
Итак, в функции обнуляем счетчики текущего байта и бита, а также проверяем размер данных на превышение максимально возможного количества информационных байт. В случае превышения сохраняем в bytesNum максимально допустимое значение. Далее копируем информационные данные в структуру, но начиная со 2-го байта (индекс = MANCH_SYNC_BYTES_NUM):
Делаем так по той причине, что первые два передаваемых байта будут синхрополем, которое и помещаем в массив encodeData.data[].
Следующая функция возвращает текущее значение бита на основе данных структуры MANCH_Data:
В manchData->data[] у нас данные, в manchData->byteIdx — номер байта, в manchData->bitIdx — номер бита. В результате функция возвращает значение этого бита в этом байте в этих данных.
Все, наконец, переходим к прерыванию таймера, в котором соберем воедино все, что было подробно обсуждено:
Все действия осуществляем при активном флаге:
Разберемся, что мы имеем в итоге с временными интервалами при текущей конфигурации.
В функцию HAL_TIM_PeriodElapsedCallback() мы попадаем каждые 10 мкс. При этом период кодирования у нас равен 100 мкс. Каждый раз заходя в callback мы инкрементируем счетчик encodeTimerCnt, что приведет к тому, что через 100 мкс его значение будет равно рассчитанному ранее значению MANCH_ENCODE_TIMER_MAX. Далее отсчитывать не требуется, поэтому обнуляем счетчик в этой части функции:
Кроме того, поскольку прошло 100 мкс, что есть длительность передачи одного бита, то переходим к следующему биту, инкрементируя encodeData.bitIdx. Оставшееся просто — проверяем, не вышел ли индекс бита за пределы байта, и в случае выхода, переходим на следующий байт. А если счетчик байт равен кол-ву передаваемых байт (encodeData.bytesNum), то все, финиш, процесс завершается.
Осталась маленькая, но самая важная часть, которая осуществляет генерацию манчестерского кода:
Помните, мы обсуждали, что период виртуального тактового генератора должен быть вдвое меньше длительности бита? Так вот поэтому здесь мы проверяем счетчик на равенство двум значениям, так как этот кусок кода должен выполниться дважды за время, равное длительности передачи бита. И в этом куске получаем значение текущего исходного бита и рассчитываем значение, которое необходимо выдать на выход все по той же формуле:
И в завершение меняем значение virtTact с 0 на 1, либо с 1 на 0.
Дело за малым — запускаем процесс генерации Манчестер-II кода из main() и анализируем результат. Для этого запускаем таймер HAL_TIM_Base_Start_IT(&htim2) и в while(1) будем запускать генерацию каждые 500 мс:
Также здесь фигурирует txData — обычный массив тестовых данных, объявленный здесь же, в main.c:
Последний байт инкрементируем в цикле, чтобы при декодировании убедиться, что не теряем посылки. Компилируем, прошиваем, запускаем, встаем осциллографом на PA3:
Переводим мысленно несколько байт наблюдаемого сигнала из манчестерского кода и получаем полное соответствие исходным данным (плюс синхрополе), что явно сигнализирует об успехе операции кодирования.
Засим откланиваюсь, в следующей статье этот же самый сигнал мы декодируем, причем этим же самым контроллером.
Подключение линий связи и коды передачи информации
Код RZ
Код RZ (Return to Zero – с возвратом к нулю) – этот трехуровневый код получил такое название потому, что после значащего уровня сигнала в первой половине битового интервала следует возврат к некоему «нулевому», среднему уровню (например, к нулевому потенциалу). Переход к нему происходит в середине каждого битового интервала. Логическому нулю, таким образом, соответствует положительный импульс, логической единице – отрицательный (или наоборот) в первой половине битового интервала.
Еще одно важное достоинство кода RZ – простая временная привязка приема, как к началу последовательности, так и к ее концу. Приемник просто должен анализировать, есть изменение уровня сигнала в течение битового интервала или нет. Первый битовый интервал без изменения уровня сигнала соответствует окончанию принимаемой последовательности бит (рис. 3.12). Поэтому в коде RZ можно использовать передачу последовательностями переменной длины.
Недостаток кода RZ состоит в том, что для него требуется вдвое большая полоса пропускания канала при той же скорости передачи по сравнению с NRZ (так как здесь на один битовый интервал приходится два изменения уровня сигнала). Например, для скорости передачи информации 10 Мбит/с требуется пропускная способность линии связи 10 МГц, а не 5 МГц, как при коде NRZ (рис. 3.13).
Другой важный недостаток – наличие трех уровней, что всегда усложняет аппаратуру как передатчика, так и приемника.
Код RZ применяется не только в сетях на основе электрического кабеля, но и в оптоволоконных сетях. Правда, в них не существует положительных и отрицательных уровней сигнала, поэтому используется три следующих уровня: отсутствие света, «средний» свет, «сильный» свет. Это очень удобно: даже когда нет передачи информации, свет все равно присутствует, что позволяет легко определить целостность оптоволоконной линии связи без дополнительных мер (рис. 3.14).
Манчестерский код
Как и в RZ, обязательное наличие перехода в центре бита позволяет приемнику манчестерского кода легко выделить из пришедшего сигнала синхросигнал и передать информацию сколь угодно большими последовательностями без потерь из-за рассинхронизации. Допустимое расхождение часов приемника и передатчика может достигать 25%.
Манчестерский код используется как в электрических, так и в оптоволоконных кабелях (в последнем случае один уровень соответствует отсутствию света, а другой – его наличию).
Основное достоинство манчестерского кода – постоянная составляющая в сигнале (половину времени сигнал имеет высокий уровень, другую половину – низкий). Постоянная составляющая равна среднему значению между двумя уровнями сигнала.
Если же один из уровней сигнала в манчестерском коде нулевой (как, например, в сети Ethernet), то величина постоянной составляющей в течение передачи будет равна примерно половине амплитуды сигнала. Это позволяет легко фиксировать столкновения пакетов в сети (конфликт, коллизию) по отклонению величины постоянной составляющей за установленные пределы.
Манчестерский код. Часть 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. Да, кстати, здесь при декодировании мы использовали информацию о периоде кодирования, но эта величина не всегда известна. Если будет спрос и интерес, опишу как провести аналогичную деятельность по декодированию при отсутствии априорной информации о длительности передачи бита.