Манчестерское кодирование, реализация
- Войдите на сайт для отправки комментариев
Манчестерский код реализация
Удобный способ передачи данных. Требуется один информационный провод (+ общий). Может передаваться без постоянной составляющей ( по эфиру, трансформаторная связь, переполюсовка проводов). Синхро информация присутствует в каждом бите, в результате не критичен к стабильности скорости приема/передачи. Привожу реализацию тестовой программы ан AVR. Программа написана на МикроПаскале, учебный язык понятный всем. Передача и прием работают на одной плате ARDUINO UNO R3. Передача ведется на пином PD7, прием пином PD3 ( INT1), как бы две программы работают на одном кристалле, якобы двух задачность, так было проще отлаживать алгоритм — чем на двух платах ARDUINO. Надо ставить перемычку между пином PD7 и PD3. Скорость приема/передачи не высока = 520,3 бит/сек, легко повышается в 10 раз не переписывая ничего ( смена 3 констант ), при изменении настроек таймера еще в 5 раз ( уже до 25 000 бит/сек), а при написании обработчика таймера на ассемблере, можно повысить еще в 3 раза. Но мне надо было отладить алгоритм и на скорости 500 быт/сек, чисто технически мне было так проще. Самая сложная часть это прием, синхронизация и дешефрирование выполнено полностью на прерываниях таймера и INT, что позволяет работать и основной программе и не подвисать контроллер. Передатчик частично выполнен на прерываниях, при желании его также можно перевести в этот режим, а основная программа сможет заниматься чем то еще.
Алгоритм построен на знании с какой скоростью ведется прием/передача, не согласованность скоростей может достигать +-12,5%, что вполне устраивает при применении AVR без кварцевого резонатора, используя внутренний генератор. Можно алгоритм усложнить и приемнику рассчитывать скорость передачи бит и от этого плясать, но это в личных устройствах практически не требуеться, т. к. автор передачи и приема один и тот же товарищ.
Прием ведется по алгоритму, рассмотрим.
Каждый фронт ( любой полярности ) запускает время ожидания 0.75 периода передачи, попавший в этот промежуток времени следующий фронт пропускается, после окончания периода ожидания считывается значение на входе приемника и запоминается. Как видно на картинке зацеп синхронизации произойдет в любом случае, максимум с пропуском первых 2-3 бит. Причем прием и дешифрация могут получиться и в инверсном виде, зависит от того, как пришло от передатчика. Для разрешения данной коллизии я с начало передаю байт B11010111, где переходы из 1 в 0 и наоборот гарантируют захват синхронизации приемником, затем передаю синхро байт 0x55. При приеме этот принятый байт сравнивается с кодом 0x55 и 0xAA. Если он равен 0x55, то прием последующих байтов ведется без изменений, если он равен 0xAA, то значит прием инверсный и все последующие байты данных надо инвертировать.
Код программы
program send; { передатчик и приемник манчесстрекого кода с использованием прерываний по таймеру 2 d приемника. Прием и передача осуществляються на одном процессоре и одной плате. Передача основной процесс, прием в режиме прерываний по тацймеру 2 и внешнему прерыванию INT1 Как бы работают две не зависимые программы, одна передает манчестерский код на пин PD7, вторая программа принимает код и расшифровывает его на пине PD3-INT1- Плата исподзуеться ARDUINO UNO R3 CPU Atmega 328P 16 мГц. Надо для работы программы закоротить пин PD7 пином PD3, т.е. соеденить выход передачика со входом приемника } Var a,b,i:byte; tikpriem:byte; // Счетчик тиков приемника прерываний таймера 2 tikpered:byte; // Счетчик тиков передатчика прерываний таймера 2 bsh:byte; // Счетчик битов в байте bpriem:byte; // байт приема битиков с линии flag1440:byte; // флаг ожидания в приемнике т.к. подождали //30 тиков считали бит и готовы к след старту flagstop:byte; // Если =0 - приема НЕТ !! т.е в прерываниях // делать ничего не надо tmout:word; // Подсчет времени приема, еслти он более //2,2 сек = ошибка надо выйти это = 46 000 тиков flagsinxro:byte; // Флаг синхронизации, считывает входной байт //синхро 0xAA или 0x55 и идет процесс записи буфера bindex:byte; // идекс записи в приемный буфер ssend:array [0..31] of byte; // Буфер для передачи stran:array [0..31] of byte; // Буфер приема // Для Отладки su:string[10]; // для отладки и вывода на экран bfs:array[0..511] of byte; ch:char; // Отладка bbb,qqq,qqf:byte; // Отладка b00:word; b11:word; www,tikprw,tikperw:word; // Отладка // Преобразования байта с строку-массив БИТОВУЮ ДВОИЧНУЮ + 0 Procedure ByteTobin(b:byte; var ss:string[10]); Var pr, i, y, r: Byte; Begin ss:='00000000'; pr:=b; y:=%10000000; for i:=0 To 7 Do Begin r:= pr AND y; if r=0 Then ss[i]:='0' Else ss[i]:='1'; y:= y SHR 1; End; End; // Вывод в порт UART LN = типа - Serial.println Procedure uartLN; Begin UART1_Write(0xD); UART1_Write(0xA); End; // Ожидание нажатия клавиши, и вывода отладочной информации Procedure timeout(ss:char; bp:byte); Var hh:char; bst:string[10]; Begin ByteToBin(bp,bst); UART1_Write(ss); UART1_Write(' '); UART1_Write_Text(bst); uartLN; While (UART1_Data_Ready() <> 1) Do; hh := UART1_Read(); End; // Манчестер - передача подготовленнного буфера на пин PD7 // Передаеться начиная с младшего бита в байте к старшему Procedure Sendbuf; Var b,i,a,q:byte; Begin For q:=0 to 31 Do // Пройдемся по всему буферу Begin a:=ssend[q]; For i:=0 to 7 Do // Проход по байту Begin b:= 1 shl i; // какой бит передаем, //сначало младщий - затем старший If (a AND b)=0 Then // Передача нулевого бита //делаем '1' 960 us затем '0' и 960 us Begin PORTD:= PORTD OR %10000000; tikpered:=0; while (tikpered<20) Do; PORTD:= PORTD AND %01111111; tikpered:=0; while (tikpered<20) Do; End Else // Передача еденичного бита // делаем '0' 960 us затем '1' и 960 us Begin PORTD:= PORTD AND %01111111; tikpered:=0; while (tikpered<20) Do; PORTD:=PORTD OR %10000000; tikpered:=0; while (tikpered<20) Do; End; End; End; End; // Для приема будем использовать таймер2 и int1 - вывод PORTD.3 // Сначало обработка прерывания по int1 по любому переходу !! // Любой переход уровня на INT1 запускаеть счет 1,5 периодов // Это определяет флаг flagg1440 Procedure INT1_pr; iv IVT_ADDR_INT1 ; Label goend; Var bt:byte; Begin if (flagstop=0) then goto goend; // Если прием не включен = выйди if (flag1440>0) then goto goend; // Если была включена задержка // на 1440 мкС = выйди flag1440:=0xFF; // Запустили задержку на 1440 мкС tikpriem:=0; // Обнулили тик приемника для подсчета задержки goend: End; { Таймер 2 !! прерывания Примерно макс время обработки 8-9 мкС, что вроде допустимо, но вносит очень большую ошибку в передачу по программной задержкае 30*19= 300 мкС на0,96 мС, т.е. надо передачу также по таймерному счетчику на прерываниях сделать, Все передавать на 500 бод, т.е. период 1,920 мС. Передача из двух половинок по 0,96 мС, приемная задержка 0,75 периода = 1,440 мС. Прерывание от таймера по 48 мкС. реально не 50 мкС, а при 16 мГц = (1/125000)*6 = 48 мкС и не 1 мС, а = 960 мкс = 0,96 мС период = 1,920 мС, задержака 30 тиков 1,5 периода передачи = 1,440 мС Все константы и счетчики пересмотрены на это регламент } Procedure Timer2_pr; iv IVT_ADDR_TIMER2_OVF; Label goend; Var bt:byte; Begin // Снова зарядим таймер 6 тиков по 8 мкС = 48 мкС TCNT2:=250; tikpered:=tikpered+1; // Тик передатчика tmout:=tmout+1; // Тик тайм аута // Простые ограничителои как ошибок так и не работы if (flagstop=0) then goto goend; // Если прием не включен = выйди if (tmout>46000) then Begin tmout:=0xC000; // Чтобы сам не перешел через переполнение к нулю. goto goend; // Счетчик прерываний, если более 2,2 сек // = тайм аут = ошибка по времени End; // 2 200 000 / 48 мкС = // Если flag1440 <>0 - режим паузы 1440 мкС, ждем ее окончания, это tikpriem>30 // Нарастим счетчик, 30 тиков = 1440 мкС = 1,5 от 960 =взятие бита данных и запись его if (flag1440>0) then Begin tikpriem:=tikpriem+1; If tikpriem>30 Then Begin flag1440:=0; // Пришло время считать занчения бита bt:=PIND AND %00001000; // Выделили бит на входе порта приема PD3 bpriem:=bpriem shr 1; // занесем его в байт, предварительно сдвинув его if bt<>0 then bpriem:=bpriem OR %10000000; // Если bt=0 то уже при сдвиге в старший бит заноситься 0 // Через 8 сдвигов первый приянтый будет младшив в байте bsh:=bsh + 1; // Счетчик битов в байте if (flagsinxro=0) then // Если еще Синхронизации нет, ожидаем ее и проверяем Begin if bpriem=0xAA Then flagsinxro:=0xAA; // Есть синхронизация инверстная if bpriem=0x55 Then flagsinxro:=0x55; // Есть синхронизация прямая bsh:=0; // начинаем прием информационных битов bindex:=0; End Else // Есть синхронизация наполняем буфер Begin if (bsh=8) Then // Байт сформирован Begin if flagsinxro=0x55 Then stran[bindex]:=bpriem Else stran[bindex]:=NOT bpriem; if (bindex<29) Then bindex:=bindex+1; bsh:=0; End; End; End; End; goend: End; begin flagstop:=0; // НЕТ приема, прерывания могут быть, но обрабатыаться они не будут { Порт D Пин 7 - вывод данных Манчестерского кода 0 -> 1 передача '1' 1 -> 0 передача '0' } DDRD := DDRD OR %11100000; // пины 5,6,7 на вывод = пины 6 и 5 были мне нужны для отладжки PORTD:= PORTD AND %00011111; // Во все пины вывода запишем = 0 // Подготовка буфера к передачи, это тест для отладки, поэтому все в ручную и наглядно ssend:='===111111YUIIOPASD1234567890===='; // 0..31 ssend[0]:=%11101011; // для запуска синхронизации ssend[1]:=0x55; // синхро байт ssend[2]:=25; // После него записано 25 байт иноформации с контрольной суммой = 24 шт. + КС ssend[28]:=0; // Сюда запишем контрольную сумму For i:=3 to 27 Do ssend[28]:=ssend[28]+ssend[i]; // КС сумма // Разрешим прерывание и прерывания от INT1 по любому смену уровн и ТАЙМЕРА2 DDRD := DDRD AND %11100111; // вывод PIND.3 на ввод и PD4 (для отладки был нужен ) PORTD := PORTD OR %00011000; // и подтянем их резистором к +5V // Теперь настроим таймер 2 и организуем от него прерывания - для - 328P = UNO R3 TCCR2B := 0; // СТОП таймер. TCCR2A := 0; // Нормальный режим биты 0 и 1 TCNT2 := 250; // 250 на 16 мГц дает частоту прерываний 48 мкС // коэфф счета = 256-0,000048/(1/125000)= 250 TCCR2B := %00000101; // Предделитель на 128. Общее деление (16 000 000) / 128 = 125 000 раз в секунд TIFR2 := 1; // сброс флага прерывания -- (1<<TOV2); TIMSK2 := 1; // Прерывание от переполнения -- (1<<TOIE2); // Настройка INT1 - у порта PD2 !!! ИМЕННО для 328 // Для других кристалов - искать аналоги в даташите. EICRA := %00000100; // причина прерыванмя - любое изменение уровня на входе INT1 вызывет прерывание EIMSK := %00000010; // разрешение для int1 asm sei end; // разрешение прерывания - глобальное // Все прерывание разрешены все, установлено от INT1 по любому переходу, есть обработчик Uart1_Init(9600); // Инит канал связи с ПК // Передаем буфер // Передача с младшего бита к старшему, от младшего байта к старшему While ( 1>0 ) Do // т.е. циклись бесконечно Begin // Ждем передачи любого симбола с ПК для старта timeout('S', tikpriem); // Начальные установки передачи и приема stran:='WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW'; // Типа обнуление приемного буфера flag1440:=0; // Флаг начало запуска периода 1,5 передачи бита bindex:=0; // Индекс буфера приема = Обязательно flagsinxro:=0; // Сброс флага синхронизации, ждем кода синхр 0xAA или 0x55 tmout:=0; // Счетчик до 2 сек, чтоб не повиснуть на приеме при ошибке или отсутсвии передачи // Поехали flagstop:=0xFF; // Разрешаем работу приемнику = СТАРТ ЕМУ SendBuf; // Запустим передачу буфера ssend Delay_ms(10); // Отладочная задержка flagstop:=0; // стоп приемник; // Вывод результата и отладочной информации WordToStr(tmout,su); Uart1_Write_Text(su); // Вывод количества тиков таймера на прием всего буфера uartLN; // На новую строку // Выведем принятый буфер For bbb:=0 To 30 Do Begin Uart1_Write(stran[bbb]); End; uartLN; // На новую строку // подсчитаем КС принятого буффера и сравним с переданным b00:=0; For bbb:=1 To 25 Do b00:=b00+stran[bbb]; Uart1_Write_Text('Длина Инфо = '); ByteToStr(stran[0],su); Uart1_Write_Text(su); Uart1_Write_Text(' | КС передана = '); ByteToStr(ssend[28],su); Uart1_Write_Text(su); Uart1_Write_Text(' | КС подсчитана приема = '); ByteToStr(b00,su); Uart1_Write_Text(su); uartLN; // На новую строку timeout('I',flagsinxro); // Вывод полученного байта сихронизации End; end.
Наш человек!
Мужчина!
Щас структурасты с мисрастами набигут, начнут корованы грабить :(
Паскаль, да еще с goto, это не просто передастия, а некро-зоо-передастия! 0_0 Ин май хамбл, пАнимашь опиньён.
а если так?- https://github.com/mchr3k/arduino-libs-manchester
или так - https://gozman.space/chip/besprovodnaya-svyaz-arduino-s-ispolzovaniem-manchester.h.html
или так - https://gozman.space/chip/besprovodnaya-svyaz-arduino-s-ispolzovaniem-manchester.h.html
Спасибо, ранее не видел, посмотрел. Очень много кода и функций и таймер используеться для измерения импульса, а сам процесс приема или передачи все равно подвештвает МК, моя цель была меньше кода, всего две процедуры на передачу 10-12 команд на прием 20-25 команд и причем все работает в фоновом процессе, основная программа может заниматься еще чем пожелает, а здесь буфер пусть передаеться или принимаеться и не мешает работе основной программе.
По поводу goto. Не красиво, зато коротко, в прерывании мне надо было экономить каждую команду ( наносекунду), есть еще процессы которые надо успеть обслужить в рабочей программе, тем более буду переписывать на асссемблере, с такого вида мне легче преписывать, а на Паскале просто отработка агоритма.
Столкнулся с похожей задачей, а именно необходимостью принять и разобрать сигнал от имеющегося термо-радиодатчика, передаваемый, как выяснилось, в манчестер-коде. До этого для приема данных с термодатчика я некоторое время использовал готовое решение от Nodo RFLink Gateway - http://www.rflink.nl/blog2/easyha, представляющее из себя закрытую прошивку для Меги, т.е. для передачи полученной информации необходимо использовать дополнительный контроллер. При этом, как показала практика, прием сигнала очень не стабильный, прошивка периодически зависает. По качеству приема я сначала грешил на приемник (китайское дешевое дерьмо), а вот с зависаниями пришлось бороться программно, отправляя на Мегу ресет всякий раз, когда прошивка перестает отвечать на запрос. Когда мне все это порядком надоело я начал сам разбираться в этом зоопарке протоколов передачи данных от радиодатчиков. Безуспешно перепробовал многочисленные найденные неработающие поделки, включая целый набор клонов якобы исходного кода упомянутого проекта. Изучая код таких доморощенных поделок в попытках запустить их в работу, я пришел к выводу, что все это, как говорится, не то. Как верно заметил автор данной темы: "Очень много кода и функций и для измерения импульса используется таймер, а сам процесс приема или передачи все равно подвешивает МК". Отсюда и неустойчивый прием, и зависания, и все остальные пляски с бубном.
По этой причине для своих экспериментов я выбрал библиотеку RCSwitch - https://github.com/sui77/rc-switch, в которой измерение импульсов и прием сигнала ведется по аппаратным прерываниям, что, на мой взгляд, гораздо надежнее и, главное, оставляет контроллеру время на выполнение основной программы. Да и код написан куда более профессионально и понятно.
В исходном варианте библиотека уверенно принимает и обрабатывает данные типовых радиодатчиков, работающих по принципу ШИМ-модуляции. Кроме того, судя по дате последних изменений, библиотека по прежнему поддерживается автором. Кстати говоря, я с легкостью добавил к ней описание протокола найденного у себя в эфире соседского термо-радиодатчика, также использующего ШИМ, но с другим соотношением длительности импульсов.
Но вернемся к манчестер-кодированию, которое, как известно, использует метод наложения передаваемых данных на импульсы синхронизации. Такой подход, как уже было описано выше, вынуждает нас синхронизировать прием и считывать данные, дождавшись очередного синхроимпульса. И здесь нас опять выручают аппаратные прерывания, поскольку длительность синхроимпульсов может варьироваться в диапазоне +/- 12%. В итоге, доработка процедуры обработки прерывания указанной библиотеки для устойчивого приема сигнала в манчестер-коде вылилась у меня в пару десятков строк кода и несколько дней отладки (без осциллографа, т.е. что называется методом тыка). Расшифровка полученных данных много времени не заняла, поскольку информации на эту тему в сети достаточно, да и исходные коды опробованных ранее библиотек оказались очень даже кстати. Результат меня порадовал, теперь все работает стабильно и принимает все передачи от радиодатчиков без исключения даже с дешевым китайским приемником. А главное, что я теперь могу использовать такой код как часть основной прошивки контроллера, выполняющей также и другие задачи.
Выкладывать свои труды не вижу особого смысла, т.к. они не являются самостоятельным законченным решением, и при этом сделаны что называется "под себя". У кого возникнет желание повторить, обращайтесь, постараюсь помочь.
miks69, можете поделиться своим кодом. Хочу собрать приемник , для получения данных от беспроводного датчика китайской метеостанции. И передачи на народный мониторинг (narodmon.ru). Сигнал прочитал с помощью осциллографа.
И что за сигнал? Манчестер? Можете выложить скриншот?
Импульс длительностью около 510мкс. Стартовая пауза около 7500мкс. Короткая пауза около 2100мкс. Пауза между пачками импульсов около 4200мкс.
Данные в пакете повторяются 5 раз. В пакете 42 бита.
xx0010010001000000101001011010000010001011001
Следующий пакет данных отправляется через 50 секунд.
В пакете отсылается (ID,Ch, T, H, Bat, crc).
Это не похоже на манчестер-код, да и на обычный ШИМ тоже не похоже...
На девайсе есть хоть какие-нибудь обозначения?
аналог метеостанции DYKIE
Расшифровку брал здесь. Показания совпали. Не разобрался с контрольной суммой.
http://orobote.ru/получение-показаний-датчиков-метеос/
http://orobote.ru/дальнейшее-исследование-формата-пер/
А чем не устраивает ЭТО?
наверняка еще чтонить аппаратное найдется
аналог метеостанции DYKIE
Расшифровку брал здесь. Показания совпали. Не разобрался с контрольной суммой.
http://orobote.ru/получение-показаний-датчиков-метеос/
http://orobote.ru/дальнейшее-исследование-формата-пер/
В приведенных вами статьях действительно описан прием манчестер-кода, только выглядит он на приемной стороне по=другому, например так, как показано на рисунках в первом посте этой ветки.
Эту библиотеку пробовали - https://github.com/mchr3k/arduino-libs-manchester ?
Inspiritus, ссылка не открывается.
Этот сигнал читал на входе передатчика радиомодуля.
Форму сигнала получаемую на приемнике не читал так как нет функции записи сигнала осциллографам.
Беру свои слова обратно, это не манчестер-код. Это обычный ШИМ, который та же библиотека RCSwitch принимает на ура. Единственное что от вас требуется, это корректно указать соотношения длительностей импульсов. По вашему описанию очень похоже на протокол BL999, прием которого подробно описан здесь - https://soltau.ru/index.php/arduino/item/526-chtenie-dannykh-datchika-po.... Я сам принимал этот код с соседского датчика с помощью указанной библиотеки, в которой добавил такое описание протокола { 400, { 1, 22 }, { 1, 5 }, { 1, 10 }, false } // protocol 8 BL999 thermo/hydro sensor
Расшифровка протокола в процедуре RCSwitch::receiveProtocol
Процедура bitReverse
Получение температуры и влажности
miks69. Помоги, я не понял, что исправить в библиотеке. И куда вписывать расшифровку протокола.
У меня только базовые знания.
Создал ветку на Github - https://github.com/miksumin/rc-switch
Пример для вашего случая в папке ReceiveDemo_BL999
Качайте и пробуйте, должно работать.
Запустил ваш скетч. В сериал порт молчит. Нет сейчас осциллографа посмотреть сигнал.