USB-IRPC: ИК пульт для ПК и розеток. Часть 2

USB-IRPC: ИК пульт для ПК и розеток. Часть 2

В части 1 я рассказал, как собрать USB-IRPC на основе Arduino. Зачем? Чтобы показать, как быстро собрать макет и сложности особой тут нет. Сегодня сделаем все как положено. С самого начала – с принципиальной схемы, PCB, ЛУТ. Кусочек фольгированного стеклотекстолита превратим с помощью кучки деталек в готовое устройство. Разумеется с корпусом, мы же хотим аккуратно, правда? Вот наша цель: «USB-IRPC Finished» «USB-IRPC Bare Front»

Итак, начинаем с принципиальной схемы. На схеме будет только сам USB-IRPC, так как модуль розеток с реле и ИК приемник со светодиодом – это “периферия” и мы будем использовать ее без изменений. Я использовал DipTrace для рисования схемы и печатной платы по следующим соображениям:

  • Он бесплатен до 1000 выводов, чего для моих любительских целей хватает.
  • Он неплохо разводит односторонние платы без заморочек.
  • Пользоваться им проще и удобнее, чем Eagle, который, на мой взгляд очень уж специфическим интерфейсом обладает.

У него есть существенный недостаток – не очень богатая библиотека элементов, а также не самый удобный поиск по ней. Но редактировать элементы не слишком сложно. В общем, пока что это наименее неудобная для меня программа 🙂 Вот сама схема (смотреть лучше полноразмерную версию перейдя по ссылке и открыв в оригинальном размере): «USB-IRPC Scheme» В центре у нас U1 – я использовал Atmega168PA-AU, но подойдет и Atmega88 и Atmega328P. Размер прошивки всего чуть больше 4 кб. Работу микроконтроллера (МК) на частоте 16МГц обеспечивают конденсаторы С4 и С5 с кварцем X1. С2 – просто блокировочный конденсатор, немного сглаживает пульсации в цепи питания. Блок из R3 и U2 служит для “нажатия” кнопки питания ПК. J1 – разъем для подключения к материнской плате параллельно кнопке питания. S1 – кнопка Reset. Может пригодиться при отладке. J5 и J6 – разъем ISP6 для внутрисхемного программирования (прошивки). J2 – разъем для подключения модуля розеток с реле. J3 – разъем для подключения модуля ИК приемника со светодиодом. D1 – светодиод, подключенный через токоограничивающий резистор R1, дублирует на плате выносной светодиод. Полезен при отладке, после сборки его не видно внутри коробки, поэтому впаивать его не обязательно. R4 – резистор для выносного светодиода на модуле приемника. J4 – разъем USB Mini-B. D2 и D3 – стабилитроны на 3.6В. R5, R6, R7 – резисторы для обеспечения работы V-USB. Вот, собственно и вся схема. В конце статьи ссылки на скачивание ее в формате .sch (DipTrace) Сложнее оказалось развести печатную плату. У меня это первый опыт и один из проводников в одном слое развести так и не удалось, пришлось оставить перемычку. К тому же я забыл, что вилки на плату будут устанавливаться с другой стороны и пришлось потом делать раздельную вилку для программатора. Оптопару я тоже по ошибке расположил не с той стороны, но запаять ее это не помешало. Все это я исправил в версии печатной платы, которую выложил для скачивания. Так что ваша плата будет чуть-чуть отличаться от той, что на моих фото. В лучшую сторону 🙂 В ходе запайки элементов я обнаружил, что заливка медью слишком близко к дорожкам добавляет неудобств и изменил заливку, избавившись от островков и сделав отступы от дорожек пошире. Вот что получилось:

Красная дорожка – перемычка, которую развести не удалось. Приятный бонус DipTrace – возможность посмотреть как будет выглядеть печатная плата. К сожалению, в библиотеке есть далеко не все элементы, но общий результат все равно куда более наглядный чем в плоском виде:

Видно, что на плате я добавил разъем J7 – просто еще пара штырьков, запараллеленных с J1, чтобы подключить коннектор кнопки питания с корпуса ПК. Ок, схема и образец печатной платы готовы, закупаем детали и изготавливаем.

Список деталей:

U1 – Atmega168PA-AU – в корпусе TQFP-32 – от 64 рублей (в icdarom, минимальный заказ 1500р, доставка 200), штучно можно купить по 130 рублей свободно. Подойдет Atmega88PA-AU или Atmega328P-AU

Выводные элементы: U2 – PC817, корпус PDIP4 S1 – DTS-61 Кнопка тактовая 6х6х4.3. D2, D3 – стабилитроны на 3.6В J2, J3 – PBS-4 J5, J6 – PBS-3 J1, J7 – PBS-2

SMD элементы размера 0805 R1, R4 – 390Ом R5, R6 – 68Ом R7 – 2.2 кОм D1 – светодиод (любого цвета, у меня оранжевый) J4 – USB miniB разъем С1, С2 – конденсаторы 0.1 мкФ керамические. С4, С5 – конденсаторы 22 пФ керамические X1- кварц 16МГц R3 – 100 Ом. R2 – 10 кОм

Выводные элементы покупаются на радиорынке – рублей на 50 примерно. SMD элементы я купил сразу пачку за $9.85 – там по 40 штук каждого номинала резисторов и по 30 каждого конденсатора.

Коробочку я брал в icdarom за 40р, но она есть и в других магазинах BOX-KA08 разного цвета, в районе 65 рублей. Бывает даже прозрачные:

Учить вас делать платы ЛУТом не буду – DiHALT все описал просто и понятно. Кстати, поздравьте его с днем варенья. 🙂 Остановлюсь только на моментах, специфических для DipTrace. При печати не забудьте отзеркалить и отключите печать контуров компонентов:

Можно обойтись и без DipTrace. Я экспортировал в PNG файл, берем FastStone Viewer и печатаем вот так:

Качество печати на максимум, экономию отключить, разрешение на максимум. PNG подготовлен в 600 PPI. Дальше все как обычно: переводим, травим, отмываем тонер, выпиливаем, лудим, сверлим: «USB-IRPC» У меня бумага была матовая и тонер немного скатался по краям – может не прогладил как следует, но там ничего важного нет, пострадала только эстетика и то несильно. Запаиваем компоненты и наносим маркировку: «USB-IRPC Bare Front» «USB-IRPC Bare Back» Обратите внимание, у меня PC817 не с той стороны и ряды гребенки ISP разъема в обратном порядке – это моя ошибка, на вашей плате все будет правильно. Из-за того, что маску в своем варианте сделал не очень удачно, при запайке элементов появились наплывы припоя, которые очень трудно снять совсем. Этот момент я тоже учел и исправил в окончательной версии разводки платы. Но переделывать плату не стал – работает хорошо, только выглядит не очень, к сожалению. Первый блин всегда комом. Зато залудить в этот раз удалось аккуратнее.

Сборка и оформление

Советую распечатать цветную картинку с надписями элементов в масштабе 1:1 (USB-IRPC_pcb.jpg) и использовать как шпаргалку при запайке элементов и сверловке отверстий. Надфилем аккуратно выпиливаем отверстия в коробочке. Контуры отверстий намечаем на корпусе маркером или карандашом, прорезав канцелярским ножом их контуры в бумажном шаблоне и приложив шаблон к корпусу. Сбоку пропиливаем паз для miniUSB разъема. Вставляем плату и примериваем: «USB-IRPC» Если не планируете дорабатывать прошивку, то отверстия под ISP вырезать не обязательно – пины не упираются в крышку. Затем печатаем и наклеиваем маркировку. В архиве найдете версию наклейки с ISP и без него. в зависимости от того, будете вы прорезать под них дырки или нет. (дырку под Reset я просверлил, но в наклейке прорезать не стал, ее можно проколоть зубочисткой, если понадобится, но пока не пригодилось).

Все, аппаратная часть готова. «USB-IRPC Size» ИК модуль я просто залил термоклеем и на двусторонний скотч прикрепил к ножке монитора. «USB-IRPC IR-Led module» Я не люблю яркий свет дома, а при нормальном освещении его практически незаметно – размер 1х3 см.

Программируем девайс

Прошивка написана на С в Code::Blocks IDE. Компилятор – AVR GCC из комплекта AVR Toolchain Я здесь поясню основные моменты, чтобы вы могли разобраться в коде проекта. Исходники лежат на странице проекта, ссылки в конце статьи. Я написал имитацию функций DigitalRead и DigitalWrite, т.к. некоторые библиотеки беру из Arduino и, чтобы не переписывать, просто подсовываю им свой вариант вызываемых ими функций Wiring. ir.h – это интерфейс с ИК приемником. Бессовестно адаптирован пример, который шел с китайским набором для экспериментов с ИК пультом. Практически без изменений. usb_comm.h – определяет коды команд для обмена с ПК и структуру данных, в которой они передаются.

USB pinstate bits – номера битов управления реле и светодиодом в пакете. Внутренние функции IRPC : fRelay1Switch – переключить реле 1 fRelay2Switch – переключить реле 2 fPCPwrSwitch – нажать кнопку питания ПК

irpcdata_t – это структура пакета, которым обмениваются устройство и ПК. Обмен происходит по запросу ПК. cmd – код команды (например cmdGetIRPCState) data[] – массив данных из 4 байт. Каждая команда интерпретирует его по своему.

irrc.h – соответствие кодов кнопок названиям, просто для удобства. Я записал для своих пультов. Примерно так:

В файле usbconfig.h настройки библиотеки V-USB. Я черпал информацию из этой статьи. Если вы не разбираетесь в USB протоколе, то там можно ничего не трогать. Основаная программа main.c

Определяем макрос SetPinState, который занимается установкой выходов в требуемое положение и устанавливает соответствующее состояние бит этого выхода в переменной pinStates. Также определяем пины, к которым подключена периферия и количество соответствий кнопок внутренним функциям, которые мы можем хранить (MaxBtnMapings).

Переменные (комментарии в исходнике делал для себя, поэтому в файле они в основном на корявом английском 🙂

Принимаемые команды от ПК храним в pcdata, отправляемые – в pdata.

Настраиваем пины, к которым подключены реле и светодиод как выходы. Очищаем соответствия кнопок внутренним функциям – мало ли какой мусор в памяти при включени. Загружаем байт состояний реле и светодиода – pinStates и устанавливаем их в соответствующее состояние в функции loadStateFromEEPROM(). Отключаем прерывания, отключаемся от ПК, делаем паузу и даем нас обнаружить. Затем разрешаем прерывания. Подготавливаем таймер для работы с ИК приемником. Запускаем цикл обработки данных от ИК приемника и ПК:

  • Интересуемся у ПК, не было ли чего для нас.
  • Если была принята команда, обрабатываем ее в processUSBcmd()
  • Дальше если разрешено обрабатывать коды ИК пульта, вызываем функцию прием и декодирования – это самая длинная по времени выполнения функция.
  • Обрабатываем код нажатой кнопки и записываем его для передачи ПК когда он спросит.

Когда ПК хочет передать нам пакет, вызывается функция usbFunctionWrite, когда он запрашивает пакет данных – функция usbFunctionRead.

Сначала заполняем структуру pdata – если нас спросили состояние девайса (cmdGetIRPCState), записываем pinStates и код нажатой кнопки пульта Если запросили одно из соответствий кнопок внутренним функциям – отдаем его, взяв номер функции из первого байта присланного ПК пакета (pcdata.data[0]) Если команду не узнали, просто отдаем структуру pdata – при обработке команд результаты записываются в нее. Затем побайтово записываем наш пакет по указателю, переданному в параметре data.

Тут еще проще – просто переписываем пакет с данными в pcdata, не разбираясь что к чему, обработка будет выполняться processUSBcmd(), а нам нужно вернуть управление как можно быстрее – функция вызывает прерыванием.

А вот, собственно, и она:

В зависимости от принятой команды (ее код в поле pcdata.cmd) разбираем пакет pcdata.data тут все довольно прозрачно, я думаю. Выполнив команду, сбрасываем флаг cmdReceived.

В функции process_IR() мы собираем 16 битный код кнопки:

Если код не равен 0, значит что-то нажато было, сохраним для передачи ПК и выставим флаг, что нужно зажечь светодиод – показать, что мы увидели нажатую кнопку.

дальше я обрабатываю коды кнопок своего пульта:

По кнопке “Power” нажимаю кнопку питания ПК до тех пор пока не отпустят кнопку пульта. По двум другим кнопкам переключаю состояния реле.

Затем проверяем массив назначенных соответствий кодов кнопок внутренним функциям – это для использования с другими пультами, соответствия задаются с ПК командами cmdSetIRBtnMapping – по одной команде на кнопку. Обработку этой команды мы видели выше в processUSBcmd()

В целом – главное успевать быстро обрабатывать прерывания и периодически опрашивать ИК приемник и вызывать usbPoll, иначе ПК нас потеряет – “USB device not recognized”. Версия обозначена как альфа недаром – есть несколько кусков, как вы успели заметить, где привязка к конкретному пульту пока не закомментирована, потому что настройка мэппинга кнопок и функций с ПК еще не написана, не успел. Ну и иногда бывает, что девайс перестает принимать команды от ПК, хотя в системе виден и на кнопки пульта реагирует нормально. Подозреваю, что это связано с большой длительностью опроса ИК приемника, это нужно оптимизировать. Однако, бывает это редко и не доставляет особых неудобств.

Теперь переходим к программе для ПК. Остановлюсь на ключевых моментах, чтобы можно было разобраться в исходниках. Покажу на примере плагина для MKey – в нем проще разобраться. Плагин написан на Delphi7 потому что выяснилось, что MKey не поддерживает юникод, а в Delphi 2010 с ANSI нужно дополнительно возиться. Из дополнительных библиотек используется только JVCL.

Основная работа выполняется в модуле uHID.pas В нем определяются те же константы команд, что и в девайсе, не буду их дублировать. Та же запись, описывающая пакет, с одним добавлением – reportID – мы его не поддерживаем и там всегда 0, но вообще репортов у девайса может быть не один. Просто нам хватает и одного. Дополнительные константы только id девайса – Vendor ID (Vid), Product ID (Pid) и DeviceName. По Vid и Pid мы будем искать среди подключенных HID устройств наше.

Все функции общения с девайсом собраны в класс

Поле Dev – указатель на класс устройства, мы его получаем при удачном подключении к устройству (Connect). при отключении (Disconnect) он устанавливается в nil.

Методы очень простые, поэтому покажу одну для примера, остальные примерно того же порядка сложности:

Состояние подключения (свойство Connected) определяется указателем Dev – если он не пустой, значит подключение прошло успешно.

Метод WriteIRPCcmd(cmd:TIRPCReport) отправляет пакет с командой, которую вы предварительно заполняете, девайсу. Метод ReadIRPCState(var Report:TIRPCReport):boolean запаршивает пакет у девайса. Если пакет успешно получил, вернет true, а сам пакет запишет в параметр Report.

procedure LoadIRcodes(irFile:string); – загружает файл соответствий названий кнопок пульта их кодам. Файл имеет простой формат: каждая строка содержит через запятую название кнопки и код TVFM,$FE01 В плагине этот метод не востребован – в Mkey передается код кнопки, вы сами называете ее как хотите, потому что программа не показывает что вы ей отправили. А вот в своей программе можно показывать нормальные человеческие названия кнопок. Функции BtnName возвращают название кнопки по коду, переданному в виде 16 битноо значения или строки с хексом. function BtnName(btnCode:word):string;overload; function BtnName(btnCode:string):string;overload; свойство IRButtons содержит список кнопок и их кодов property IRButtons:TStringList read fIRButtons; свойство IRname – имя загруженного методом LoadIRcodes пульта (имя файла без расширения .ir). property IRName:string read fIRName;

Как получить пакет от девайса?

С помощью метода GetFeature – передаем буфер для пакета, получаем в него пакет. Если подключения нет, метод вернет False. Передача пакета девайсу производится аналогично:

Собственно сам плагин выполнен в виде dll. Реализованы требуемые им функции: Enable, Disable, GetName, GetInfo, GetVer, GetAuthor, IsInteractive, IsHaveSettings; Соответственно, в Enable мы подключаемся к девайсу методом Connect, запускаем таймер, который будет каждые 500 мс опрашивать девайс с помощью ReadState и если получил код кнопки, отдавать его в MKey:

В процедуре Disable отключаемся от девайса:

Модуль uHID у плагина и полноценной программы управления идентичный. Основную программу выложил пока в скомпилированном виде, исходники не выкладываю, потому что над функционалом идет работа, там пока бардак 🙂 Как будет более-менее приличный вариант, в Downloads положу. Пока выглядит вот так:

  • Сворачивается в область уведомлений.
  • По клику разворачивается.
  • Период опроса настраивается в Settings
  • S – сохранение состояния в EEPROM
  • L – загрузка состояния из EEPROM
  • Power Btn – выполняет функцию fPCPwrSwitch (то бишь нажимает кнопку питания на ПК)
  • Show log покажет список событий – засыпание / просыпание ПК. (у меня кнопка питания отправляет в Sleep).
  • Остальное – тестовые кнопки.
  • Кликая по одной из трех лампочек, переключаем соответственно – светодиод, Реле1, Реле2 (всплывающие подсказки помогут разобраться)

Вроде все важное рассказал. Если остались вопросы, задавайте в комментариях, постараюсь ответить.

📎📎📎📎📎📎📎📎📎📎