В любительских робототехнических проектах часто можно найти применение лентяйкам. Благодаря им можно, например, удаленно управлять роботом. В этой статье мы описали, как адаптировать пульт дистанционного управления для работы с собственными роботами. Описание будет на примере пульта Philips.
В этой статье мы постараемся использовать собранную ранее информацию на практике. Нами будет использоваться осциллограф, созданный в домашних условиях, который также может быть полезен для других приложений.
В любительских проектах часто можно встретить использование пульта ДУ. Благодаря ему мы можем запускать нашего робота с определенного расстояния (полезно во время соревнований), переключать режимы работы или просто управлять им удаленно. Практически все конструкции этого типа используют пульты стандарта RC5.
Недавно, когда нам самим понадобилось использовать пульт дистанционного управления, мы тоже выбрали RC5. Конечно, нетрудно догадаться, что данный пульт сработал не так, как мы ожидали. Но мы решили не сдаваться и расшифровать этот пульт. Результатами своей работы делимся в этой статье.
Как распознать ИК пульта ДУ?
Первым шагом было изучение существующих методов кодирования. Мы узнали, что есть два основных способа кодирования — манчестерское кодирование и импульсное кодирование.
Один кадр данных, помимо кода нажатой кнопки, также содержит код устройства и вспомогательные биты запуска, остановки, переключения и паузы. |
Кроме того, коды дистанционного управления работают на разных частотах, например:
- 36 кГц
- 38 кГц
- 40 кГц
- 56 кГц
Для считывания модулированного сигнала используются интегрированные декодеры, такие как популярный TSOP. Для считывания общих частот кодирования были разработаны различные модели этой схемы. Ниже приведена таблица моделей TSOP22xx:
Обратите внимание, что ИК датчик воспринимает определенный диапазон частот в номинальной среде.
Чем шире диапазон, тем легче датчику получать данные из другого диапазона, например, 38 кГц вместо 36 кГц. |
Обычно это негативно влияет на работу, потому что, таким образом, мы можем получать помехи от других пультов. Ниже приведена диаграмма из каталожной информации того же инфракрасного датчика TSOP22xx, показывающая силу генерируемого сигнала на отдельных частотах по отношению к номинальной:
Важно правильно отфильтровать помехи в таком датчике. Лучше всего следовать рекомендациям производителя. Ниже представлена схема, предложенная производителем ИК датчиков TSOP:
Начинаем испытания
Итак, первая задача — определить частоту, на которой работает наш пульт. Возможно, есть более простой способ сделать это, но мы выбрали довольно грубое решение. Мы решили собрать на макете простую схему с ИК датчиком, микроконтроллером и несколькими диодами:
Эта схема понадобится вам практически для всех тестов. Мы используем внутренний резонатор 4МГц во всех программах. А также мы сделали вывод UART в схеме, который будет очень полезен для отладки позже.
Написали простую программу для увеличения значения, отображаемого на светодиодах, каждый раз, когда он обнаруживает сигнал на входе датчика. Мы надеялись, что пульт будет работать на частоте 36 кГц, наши надежды оправдались. Иначе пришлось бы тестировать датчики на разных частотах.
Стоит знать, что подавляющее большинство пультов дистанционного управления работают на частоте 36 или 38 кГц. Это касается как известных брендов, так и дешевых китайских аналогов. |
У больших компаний есть много стандартов кодирования и иногда используется несколько частот, например Sony иногда использует 40 кГц. Возможно, образец, который мы тестировали, не был репрезентативным, но коды всех пультов дистанционного управления, которые у нас были, можно было прочитать с помощью ИК датчика TSOP2236.
Интерпретация фрейма данных
Как только мы поймем, что наш датчик считывает данные с пульта дистанционного управления, мы сможем перейти к основному пункту программы. Нашей задачей будет расшифровать фрейм данных с пульта ДУ и определить код отдельных кнопок.
Простой осциллограф
Лучше всего использовать осциллограф для отслеживания форм сигналов, генерируемых датчиком. Здесь есть одна серьезная проблема. Профессиональные осциллографы очень дорогие, и их вряд ли кто-то может себе позволить купить. Однако есть решение, и оно очень дешево стоит. Все, что нам нужно — это старый кабель от наушников. Кроме того, нам необходимо установить программу Soundcard Oscilloscope. Благодаря этой программе мы сможем использовать аналого-цифровой преобразователь нашей звуковой карты в качестве входа осциллографа, а цифро-аналоговый преобразователь в качестве генератора сигналов.
Программа также позволяет создавать графики XY и отслеживать формы сигналов в частотной области. Отрежьте кабель наушников подальше от джека, чтобы его можно было легко подключить к компьютеру и к тестовой плате.
Однако его нельзя использовать для проверки всех форм сигналов. Звуковая карта рассчитана на напряжение не более 2В. Перед подключением пробника к компьютеру используйте тестер, чтобы убедиться, что массы системы и звуковой карты правильные, а максимальное напряжение не превышает 2 В. Несоблюдение этих правил может привести к повреждению звуковой карты, а в конечном итоге и всего компьютера. |
Использование осциллографа
Входом по умолчанию нашего осциллографа является разъем для микрофона, а на выходе генератора — разъем для наушников. Звуковая карта адаптирована для генерации / приема сигналов на частотах в пределах слышимости человеческого уха, то есть в диапазоне примерно 20-22000 Гц. Программа очень проста в использовании.
Окно программы Soundcard Oscilloscope:
Ручки Amplitude и Time масштабируют оси, используя значение Offset, мы смещаем начальное значение формы волны по оси Y для отдельных каналов. В кадре триггера мы можем установить режимы триггера. Возможные варианты:
Авто — осциллограмма постоянно отображается на экране, даже если порог срабатывания триггера не превышен.
Нормальный — новый сигнал появляется каждый раз при превышении порогового значения.
Одиночный — при нажатии кнопки Run / Stop отображается первая осциллограмма после превышения порогового значения триггера.
Ниже вы можете выбрать канал, для которого мы устанавливаем условия запуска, нарастающий или спадающий фронт формы сигнала (edge), а порог (threshold) должен превышать наклон, зарегистрированный осциллографом.
В нижнем левом углу диаграммы появляется кнопка «Save» , которая сохраняет формы сигналов как в виде изображения, так и в виде файла csv. Благодаря этому данные с осциллографа можно анализировать с помощью специальных программ.
Некоторые могут задаться вопросом, как мы должны исследовать сигнал с частотой 36 кГц с оборудованием принимающим частоты до 22 кГц.
Частота модуляции 36 кГц используется только для передачи сигнала, тогда как данные, считываемые датчиком, имеют в несколько раз меньшую частоту. |
Вот поэтому, используя осциллограф со звуковой карты, вы также можете легко исследовать формы сигналов, модулированные с частотой 56 кГц.
Измерение
После подключения ИК датчика в соответствии с рекомендациями производителя и разводки выходов OUT и GND на пробник осциллографа, мы смогли начать тестирование форм сигналов. Мы установили осциллограф в одиночный режим и подняли порог срабатывания, чтобы он работал более точно. Последовательность нажатия кнопки «1» на пульте показана ниже:
TSOP2236 по умолчанию имеет высокий уровень и переходит на низкий уровень при получении данных. График на осциллографе показывает обратное. После быстрой проверки с помощью датчика мы пришли к выводу, что осциллограф показывает инвертированные значения.
Мы не знаем, почему это происходит, но вы должны помнить, что это всего лишь замена настоящему осциллографу. В конце концов, времена логических уровней в порядке, и смена полярности не вызывает особых проблем.
Для сравнения мы сохранили формы сигналов для кнопок 2, 3, 4 на пульте:
Расшифровка осциллограмм
Как мы видим на осциллограммах, каждый сигнал начинается с двух длинных импульсов. Затем есть несколько более коротких и еще более длинных. До этого момента каждый фрейм данных выглядел одинаково.
Они отличаются только последней серией коротких импульсов. Если мы сравним эти наблюдения с кодами, известных пульту, можно сделать вывод, что исходные длинные сигналы используются для инициализации. Следующая серия коротких импульсов — это системный код, поэтому он идентичен для каждой кнопки.
Последняя серия коротких импульсов должна содержать коды команд. Длины отдельных кадров отличаются друг от друга, поэтому мы имеем дело с кодированием по длине импульса. Теперь посмотрим на короткие серии.
Как видите, они состоят из 9 низких состояний (мы помним о дефекте осциллографа) одинаковой длины и восьми высоких состояний, длина которых принимает одно из двух значений (0 или 1). Мы можем присваивать эти значения по-своему и читать их как угодно. Мы же предпологаем, что 0 — короткий сигнал, а 1 — длинный сигнал .
Следующий шаг — узнать точное время. Для этого мы использовали Matlab, но это под силу любой программе обработки данных, даже обычной Excel. Мы отметили начальную и конечную точки для каждого типа импульса и рассчитали их длительность. Повторили операцию несколько раз, чтобы убедиться, что не ошиблись. Затем прочитали коды каждой кнопки и записали всю информацию о нашем пульте дистанционного управления:
Частота модуляции: 36 кГц кадров данных:
- стартовый бит 1: 8 мс НИЗКИЙ
- стартовый бит 2: 4 мс ВЫСОКИЙ
- восемь системных битов
- бит перерыва: 4 мс ВЫСОКИЙ
- восемь командных битов
- конец информации: ВЫСОКИЙ, пока не будет получен следующий кадр.
Системная битовая кодировка и команды:
- низкий сигнал: 422us (микросекунды)
- высокий уровень сигнала «0»: 422 us
- высокий уровень сигнала «1»: 1,6 мс
Между восьмым битом данных и разрывом имеется еще один низкий уровень сигнала 422us.
- системный код: 11000011
Список команд:
- 0 — 11110000
- 1 — 01001000
- 2 — 01101000
- 3 — 10 011 000
- 4 — 00100000
- 5 — 00110000
- 6 — 00011010
- 7 — 01110000
- 8 — 10010000
- 9 — 00111000
- power — 00011000
- mute — 01011000
- prog — 01100000
- audio — 11101000
- sys — 01010000
- lnb — 10111000
- up — 11011000
- down — 11001000
- left — 01000000
- right — 00111001
- store — 11111000
- next — 10101000
- fav — 10001000
- hv — 11010000
- tone — 01111000
- radio — 00000000
- tvsat — 10100000
- lock — 00101000
Чтобы быть уверенными, мы решили проверить осциллограммы на простой программе, написанной на языке ассемблера, используя USART для проверки правильности считывания данных. Его задача — прочитать состояние вывода в прерывании таймера, добавить его в буфер чтения, собрать восемь бит и отправить их на ПК.
Интересно, что раньше мы пытались проверить вывод в цикле, а не в прерывании, или использовать внешнее прерывание, а не таймер. Затем выяснилось, что значение при изменении в течение некоторого времени колеблется, вызывая эффект, похожий на вибрацию контактов на переключателях, но, конечно, длящийся намного короче.
К счастью, проверка прерывания таймера каждые 64 мкс полностью устранила эту проблему, и при чтении с терминала результаты такие, как и ожидались. Вот показания терминала в шестнадцатеричном коде для одного фрейма данных:
1 |
c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 ff ff ff ff ff ff ff 00 3f ff ff 00 ff ff fc 01 fc 03 f8 07 f0 07 f0 0f ff ff c0 3f ff ff 00 ff ff ff ff ff ff ff fc 03 f8 07 ff ff e0 1f c0 3f 80 7f ff ff 00 ff 00 fe 01 fc 03 |
Отдельные фрагменты осциллограммы можно легко сравнить с соответствующими цепочками нулей и единиц на терминале. Благодаря этому мы можем быть уверены, что микроконтроллер сможет читать фрейм данных без каких-либо помех. Ниже приведен программный код на языке ассемблера. Если даже вы не знаете язык, у вас все равно не должно возникнуть проблем с его пониманием благодаря комментариям и документации Atmel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
.include "m16def.inc" ;определения для atmega 16 ;имена вспомогательных регистров .def A = r16 .def licz = r17 .def buf = r18 ;USART только передача, 9600baud 8bit even parity .EQU UCSRBconf = (1<<TXEN) .EQU UCSRCconf = (1<<URSEL) | (1<<UCSZ0) | (1<<UCSZ1) | (1<<UPM1) .EQU UCRRLconf = 12 ;TIMER0 прерывание ovf, preskaler 1 .EQU TCCR0conf = (1<<CS00) .EQU TIMSKconf = (1<<TOIE0) .CSEG .ORG 0x00 jmp RESET ;перейти к поддержке сброса .ORG 0x012 jmp TIM0_OVF ;переход к обработке прерывания T0 .ORG 0x100 RESET: ;инициализация стека ldi A, high(RAMEND) out sph, A ldi A, low(RAMEND) out spl, A ;pullup для входа TSOPa, по умолчанию в качестве входа ldi A, 0x04 out PORTD, A ;настройка USART ldi A, UCSRBconf out UCSRB, A ldi A, UCSRCconf out UCSRC, A ldi A, UCRRLconf out UBRRL, A clr A out UBRRH, A ;конфигурация T0 ldi A, TCCR0conf out TCCR0, A ldi A, TIMSKconf out TIMSK, A sei ;бесконечный цикл PROGRAM: jmp PROGRAM ;прерывание от таймера TIM0_OVF: lsl buf ;смещение буфера влево sbic PIND, 2 ;если в настоящее время на входе 1 inc buf ;это увеличение buf inc licz ;увеличить счетчик бит в буфере cpi licz, 8 ;проверить, заполнен ли буфер breq SEND ;jесли да, то отправить через uart reti ;возврат от прерывания SEND: sbis UCSRA, UDRE ;цикл, пока буфер UART не rjmp SEND ;будет пустым после предыдущей передачи mov A, buf ;а затем скопировать buf в буфер UART out UDR, A clr licz ;и обнуление счетчика reti |
Программа декодирования
Пришло время написать полный код удаленному декодеру. Мы снова выбрали ассемблер по нескольким причинам. Во-первых, это будет простое приложение, использующее в основном регистровые операции, и писать их на языке ассемблера очень просто.
Во-вторых, мы сможем детально отслеживать программу в отладчике AVR Studio 4 и быстрее находить ошибки. Кроме того, пока мы пишем, мы можем скачать некоторые отрывки из заметки по применению Atmel RC5. Внизу готовый код с описанием в комментариях:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
.include "m16def.inc" ;определения для atmega 16 ;запись имен регистров .DEF tmp = r16 ;временный регистр .DEF timerL = r17 ;три таймера для поддержки прерывания T0 .DEF timerH = r18 .DEF timertemp = r19 .DEF system = r0 ;регистр, в котором хранится системный код .DEF command = r1 ;регистр, в котором хранится код команды .DEF stan = r20 ;вспомогательный регистр, хранящий текущее состояние PIN-кода.D2 ;настройка таймера T0 .EQU TIMSKconf = (1<<TOIE0) .EQU TCCR0conf = (1<<CS00) .CSEG .ORG 0x00 jmp RESET ;перейти к поддержке сброса .ORG 0x12 jmp T0_OVF ;переход к обработке прерывания T0 .ORG 0x100 ;поддержка сброса-начало программы RESET: ;инициализация стека ldi tmp, low(RAMEND) out spl, tmp ldi tmp, high(RAMEND) out sph, tmp ;pullup na PD2 ldi tmp, 0x04 out PORTD, tmp ;PORT C - отображает на линейке диодов прочитанный байт, выход, начальные значения H ser tmp out DDRC, tmp out PORTC, tmp ;настройка таймера ldi tmp, TCCR0conf out TCCR0, tmp ldi tmp, TIMSKconf out TIMSK, tmp ;активация глобальных прерываний sei ;начало функции декодирования, сброс таймеров detect: clr timerH clr timertemp detect1: clr timerL ;проверка, если он idle на 7.5 ms (тайминги для таймингов 4mhz) idle: cpi timerH, 8 ;проверка прошла ли 131ms brsh detect ;как нет, мы возвращаемся к началу sbrs stan, 0 ;проверка состояния PIN rjmp detect1 cpi timerL, 100 ;проверка, сохраняется ли состояние на 7.5 ms brlo idle ;если нет, мы возвращаемся ;обнаружение начала стартового бита clr timerH start: cpi timerH, 8 brsh detect ;сли взлета нет на 131ms, то мы возвращаемся к началу sbrc stan, 0 ;если на выводе 1, то цикл, как нет, то алгоритм идет дальше rjmp start clr timerL ;обнаружение низкого состояния продолжительностью 9.6 ms start1: cpi timerL, 150 brsh detect ;если 0 длится более 9.6 МС или менее 4 мс, то error sbrs stan, 0 ;если нет 0 на PD2 он возвращается к start1 rjmp start1 cpi timerL, 50 brlo detect clr timerL ;обнаружение высокого состояния 4ms start2: cpi timerL, 100 brsh detect ;если он длится более 6 мс или менее 2.5, то error sbrc stan, 0 rjmp start2 cpi timerL, 20 brlo detect ;начало чтения системы system_init: clr system clr tmp ;другой бит системы sys_next: clr timerL ;чтение сигнала LOW sys_low: cpi timerL, 12 brsh detect ;если больше, чем 760us sbrs stan, 0 ;если не появляется 0, то вернуться к sys_low rjmp sys_low clr timerL ;если успешно прочитано low, то мы обнуляем таймер ;высокий сигнал и чтение "0" или "1" sys_high: cpi timerL, 40 brsh detect sbrc stan, 0 ;ожидание "1" на PD2 rjmp sys_high lsl system ;операционная система < 1 бит inc tmp ;увеличение счетчика количества полученных бит cpi timerL, 15 ;если длинный high, то мы вводим 1 на последний бит системы brlo sys_end ;если нет, то будет ноль inc system sys_end: cpi tmp, 8 ; Если счетчик меньше 8, то мы возвращаемся к считыванию следующего бита brlo sys_next ;чтение последнего низкого состояния после чтения системы sys_end2: clr timerL cpi timerL, 12 brsh detect ;если больше, чем 760us sbrs stan, 0 rjmp sys_end2 clr timerL ;до этого момента, безусловно, хорошо работает ;обнаружение высокого состояния 4ms перерыва: cpi timerL, 100 brsh detect ;если он длится более 6 мс или менее 2.5, то error sbrc stan, 0 rjmp przerwa cpi timerL, 20 brlo detect ; чтение command так же, как в случае состояния только с замененными именами command_init: clr command clr tmp ;другой бит системы com_next: clr timerL ;---------------------- com_low: cpi timerL, 12 brsh detect ;если больше, чем 760us sbrs stan, 0 rjmp com_low clr timerL ;если успешно прочитано low, то мы обнуляем таймер ;высокий сигнал и чтение "0" или "1" com_high: cpi timerL, 40 brsh koniec sbrc stan, 0 rjmp com_high lsl command inc tmp cpi timerL, 15 ;если длинный high, то мы вводим 1 на последний бит системы brlo com_end ;если нет, то будет ноль inc command com_end: cpi tmp, 8 brlo com_next ;чтение последнего низкого состояния после чтения системы com_end2: clr timerL cpi timerL, 12 brsh finish ;если больше, чем 760us sbrs stan, 0 rjmp com_end2 clr timerL ;---------------------- ;после правильного чтения вернуть на линейку led значения command out PORTC, command ;вернуться к началу программы конец: jmp detect T0_OVF: clr stan ;если во время прерывания pd2 High, то состояние = 0xFF sbic PIND, 2 ;в противном случае 0x00 ser stan inc timerL ;увеличение значения таймеров inc timertemp brne t0_finish ;если timertemp= = 0 это увеличение timerH inc timerH ;timerH работает в 256 раз медленнее, чем timerL и timertemp t0_finish: reti ;возврат от прерывания |
После написания этих частей, считывающих следующие фрагменты кадра, стоит добавить линии, которые зажигают светодиоды, чтобы проверить, все ли сделано правильно.
Легче отлаживать на лету, чем искать ошибки позже. Наша программа прошла такой процесс отладки, что позволило нам справиться с ней довольно быстро .
Программа работает безотказно, распознает все коды кнопок точно так, как указано в спецификации выше. Однако у нее есть недостатки. Циклический опрос также все еще используется, несмотря на использование прерываний. Кроме того, было бы сложно использовать его как функцию в другой программе, если бы каждое чтение кнопки занимало десятки миллисекунд, в течение которых процессор не мог бы делать ничего другого.
Однако в более крупных проектах ассемблер не так удобен, и было бы неплохо иметь код C. Вот почему мы написали программу данного пульта на этом языке. Теперь мы можем успешно использовать ее как часть более крупного проекта.
Программа на C
Она выполняет все операции в прерывании. Несмотря на то, что разрыв кажется очень сложным, она состоит из множества условных операторов, и на самом деле каждый возможный путь выполняется довольно быстро.
Перерыв организован как набор состояний, в которых система может находиться в данный момент. Каждый раз, когда в ней проверяются условия перехода в другое состояние, и если они выполняются, то при следующем прерывании система перейдет в следующее состояние. Конечно, нет проблем с масштабированием программы для других значений осциллятора.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
/* Программа pilot.h содержит: -инициализации соответствующих портов и timer0 -прекращение T0OVF -переменные, необходимые для передачи */ //следующие состояния #define IDLE 0 #define DSTART 1 #define START1 2 #define START2 3 #define SYSTEMLOW 4 #define SYSTEMHIGH 5 #define SYSTEMEND 6 #define PRZERWA 7 #define COMMANDLOW 8 #define COMMANDHIGH 9 #define KONIEC 10 //коды кнопок #define BUTTON_0 0b11110000 #define BUTTON_1 0b01001000 #define BUTTON_2 0b01101000 #define BUTTON_3 0b10011000 #define BUTTON_4 0b00100000 #define BUTTON_5 0b00110000 #define BUTTON_6 0b00011010 #define BUTTON_7 0b01110000 #define BUTTON_8 0b10010000 #define BUTTON_9 0b00111000 #define BUTTON_POWER 0b00011000 #define BUTTON_MUTE 0b01011000 #define BUTTON_PROG 0b01100000 #define BUTTON_AUDIO 0b11101000 #define BUTTON_SYS 0b01010000 #define BUTTON_LNB 0b10111000 #define BUTTON_UP 0b11011000 #define BUTTON_DOWN 0b11001000 #define BUTTON_LEFT 0b01000000 #define BUTTON_RIGHT 0b00111001 #define BUTTON_STORE 0b11111000 #define BUTTON_NEXT 0b10101000 #define BUTTON_FAV 0b10001000 #define BUTTON_HV 0b11010000 #define BUTTON_TONE 0b01111000 #define BUTTON_RADIO 0b00000000 #define BUTTON_TVSAT 0b10100000 #define BUTTON_LOCK 0b00101000 #define SYSTEM_CODE 0b11000011 volatile unsigned char system=0, command=0, system_new=0, command_new=0, czy_odczytano=0; void PILOT_init(void) { TIMSK = (1<<TOIE0); TCCR0 = (1<<CS00); DDRD &= 0xFB; //PD3 input PORTD |= (1<<3); //pullup na INT2 } ISR(TIMER0_OVF_vect) { static unsigned char timerH=0, timerL=0, timertemp=0, stan=0, i=0; timerL++; timertemp++; if(!timertemp) timerH++; switch(stan) { case IDLE: { if(timerH<8) { if(PIND&0x04) {if(timerL<100) return; else {stan=DSTART; timerH=0; timerL=0; return;}} else {timerL=0; return;} } else {timertemp=0; timerH=0; timerL=0; return;} } case DSTART: { if(timerH>8) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) return; else {timerL=0; stan = START1; return;} } } case START1: { if(timerL>150) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) { if(timerL<80) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else {timerL=0; stan = START2; return;} } else return; } } case START2: { if(timerL>100) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) return; else { if(timerL<40) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else {timerL=0; stan = SYSTEMLOW; system_new=0; i=0; return;} } } } case SYSTEMLOW: { if(timerL>12) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) {timerL=0; stan=SYSTEMHIGH; return;} else return; } } case SYSTEMHIGH: { if(timerL>40) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) return; else { i++; system_new = (system_new<<1); if(timerL>15) system_new++; timerL=0; if(i<8) {stan=SYSTEMLOW; return;} else {timerL=0; stan=SYSTEMEND; return;} } } } case SYSTEMEND: { if(timerL>12) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) {timerL=0; stan=PRZERWA; return;} else return; } } case ПЕРЕРЫВ: { if(timerL>100) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) return; else { if(timerL<40) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else {timerL=0; stan = COMMANDLOW; command_new=0; i=0; return;} } } } case COMMANDLOW: { if(timerL>12) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) {timerL=0; stan=COMMANDHIGH; return;} else return; } } case COMMANDHIGH: { if(timerL>40) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) return; else { i++; command_new = (command_new<<1); if(timerL>15) command_new++; timerL=0; if(i<8) {stan=COMMANDLOW; return;} else {timerL=0; stan=KONIEC; return;} } } } case КОНЕЦ: { if(timerL>12) {timertemp=0; timerH=0; timerL=0; stan=IDLE; return;} else { if(PIND&0x04) { timerL=0; timerH=0; timertemp=0; command=command_new; system=system_new; czy_odczytano=1; stan=IDLE; return; } else return; } } } } |
Вывод
Как видите, с помощью простых инструментов и небольших познаний в области дистанционного кодирования мы смогли адаптировать сигнал от неизвестного пульта к своим собственным целям.
С любым другим кодированием можно справиться, используя аналогичные методы. Наконец, давайте проанализируем форму сигнала от другого пульта дистанционного управления телевизором, который один из сотрудников нашел у себя дома. Мы больше не будем углубляться в саму работу, но процесс декодирования будет аналогичен описанному выше.
Осциллограммы ясно показывают часть системного кода, перерыв в 20 мс и код команды.
Каждая часть начинается с двух сигналов инициализации. Кроме того, мы видим, что все проходы имеют одинаковую длину, т.е. мы имеем дело с манчестерской кодировкой.
В системном коде все биты имеют одинаковое значение, а в кодах команд легко увидеть, какие биты имеют разные значения. При расшифровке кода манчестерского типа стоит воспользоваться информацией относительно кода RC5.
Другой способ — прочитать полубиты, а затем программно заменить строки 01 и 10 соответствующими значениями. Однако оставляем это для ваших самостоятельных экспериментов.
С Уважением, МониторБанк