Шрифт
Создаем простейшее usb-устройство для общения со своей программой
Play

Создаем простейшее usb-устройство для общения со своей программой

В продолжение темы о создании собственного USB-гаджета. Создание простого устройства.

Раз устройство планируется подключать к ПК, значит вероятнее всего потребуется передача данных между устройством и ПК. Начнем писать прошивку и софт, наладив связь между ними. Самым простым вариантом передачи данных является использование класса коммуникационных устройств USB (CDC). При таком подключении устройство будет видно в системе как обычный виртуальный COM-порт. Плюсом такого подключения является отсутствие необходимости писать собственные драйвера. Так же радует простота приема и передачи данных: для работы с портом в Windows достаточно открыть его как текстовый файл и производить обычные операции чтения\записи.

Железо.

Возьмем схему с минимальной обвязкой МК.

На этот раз нам нужно добавить только 4 контакта к USB и одну кнопку (кнопка нужна только для бутлоадера: куда проще нажать ее и заменить прошивку в устройстве по USB, нежели переставлять чип в программатор).

Не сильно стараясь сделать красиво, разводка может выглядеть так:

Но при желании часто экспериментировать с подключаемыми компонентами лучше сразу развести каждую ногу МК сделав аналог ардуино — Jaluino.

Прошивка

Скомпилировав данный код, записав полученный HEX файл в МК при помощи бутлоадера и запустив устройство можно будет наблюдать как в системе опрделится новое устройство: Виртуальный сom-порт.

Теперь, когда устройство уже работает, научим его общаться.

Для чтения принятого байта существует функция usb_serial_read(byte):boolean. При наличии полученного байта она заносит его в указанную переменную и возвращает true, иначе возвращает false.

Для отправки байта существует процедура usb_serial_data. Она замаскирована под переменную, потому для отправки байта достаточно присвоить ей значение отправляемого байта.

Объявим переменную размером в байт до основного цикла, в основном цикле будем проверять наличие полученных байт, и при их наличии отправлять их обратно.

Компилируем, зажимаем кнопку, передергиваем питание, запуская бутлоадер, меняем прошивку, запускаем. Устройство снова определилось в системе, теперь нам нужен софт, дабы протестировать работу устройства.

Пока у нас нет своего, используем готовый терминал: я использовал программу RealTerm. Открываем порт с нужным номером и отправляем данные.

И нам в ответ приходит то, что мы отправили. Значит, все работает как надо.

Итак, наш микроконтроллер умеет принимать байты и тут же отправлять их обратно. Теперь напишем свой софт для общения с ним (я буду использовать Delphi).

Создаем новый проект, раскидываем по форме необходимые компоненты: SpinEdit1 — для указания номера порта Button1 — для установки соединения Button2 — для разрыва соединения SpinEdit2 — для ввода байта в десятичном виде Button3 — для отправки байта Memo1 — для вывода принятой информации.

Как уже было сказано выше, с com-портом нужно работать так же, как и с обычным текстовым файлом: используя функции CreateFile, WriteFile и ReadFile.

Дабы не вдаваться в подробности, возьмем готовую библиотеку для работы с com-портом: ComPort.

Вешаем на каждую кнопку необходимую задачу и получаем конечный код:

uses Windows, Messages, SysUtils, Variants, Classes, Graphics , Controls, Forms, Dialogs, StdCtrls, Spin,ComPort;

type TForm1 = class (TForm) SpinEdit1: TSpinEdit; Button1: TButton; Button2: TButton; SpinEdit2: TSpinEdit; Button3: TButton; Memo1: TMemo; procedure OnRead(Sender: TObject; ReadBytes: array of Byte ); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button3Click(Sender: TObject); private < Private declarations > Port: TComPort; public < Public declarations > end;

var Form1: TForm1; num: integer; implementation

procedure TForm1.Button1Click(Sender: TObject); begin Port := TComPort.Create(SpinEdit1.Value, br115200); //создаем соединение Port.OnRead := OnRead; //создаем поток чтения принятых данных Button2.Enabled := true ; //активируем кнопку закрытия соединения end;

procedure TForm1.Button2Click(Sender: TObject); begin Port.Free; //закрываем соединение Button2.Enabled := false ; //отключаем кнопку end;

procedure TForm1.Button3Click(Sender: TObject); begin if Button2.Enabled then Port.Write([SpinEdit2.Value]); end;

procedure TForm1.FormDestroy(Sender: TObject); begin if Button2.Enabled then Port.Free; end;

procedure TForm1.OnRead(Sender: TObject; ReadBytes: array of Byte ); var i:integer; begin for i := Low(ReadBytes) to High(ReadBytes) do //проходим по массиву принятых байт begin Memo1.Text := Memo1.Text + '.' +InttoHex(ReadBytes[i],2); //добавляем его HEX значение в окно inc(num); //считаем колв-о принятых байт end; if num > 10 then begin Memo1.Lines.Add( '' ); //переносим строку num := 0; end; end;

Запускаем, устанавливаем соединение, отправляем байты:

Вот и готов наш самый простой терминал для работы с самым простым usb-устройством.

Как видно, чтение и запись происходит динамическими массивами байт.

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

Дополнительные возможности

Если на этом остановиться, получится обычная статья с подробным описанием примера использования библиотеки, коих на просторах сети достаточно. Потому добавлю немного более углубленной информации.

Упрощение отправки данных

Отправлять информацию по одному байту — не всегда удобно. Очень часто может пригодиться библиотека print. Она содержит процедуры по отправке данных всевозможной длины всевозможными форматами: byte,hex,dec,bin,boolean что может упростить вывод данных в программе.

Название всех команд можно посмотреть в файле библиотеки.

Ожидание подключения к ПК

Если перед стартом основного цикла микроконтроллера необходимо предварительно установить соединение с ПК, то можно дописать перед ним строчки

Привязываем к устройству номер порта

Если оставить все как есть, система при каждом новом подключении будет выделять первый свободный номер порта. А это значит что за ним придется всегда следить. Для того, что бы этого не происходило, необходимо устройству присвоить уникальное значение серийного номера до подключения библиотеки usb: Номер может быть любой длины и содержать различные символы.

Меняем имя устройства на свое

Поменять имя устройства, видимое в системе до установки драйверов можно объявив массив с именем, как и серийный номер, это необходимо сделать до подключения библиотеки USB.

Но увы, после установки драйверов устройство поменяет имя на указанное в .inf файле, потому поменяем имя и там

Организуем автоподключение устройства

Увы, никаких прямых путей выполнить данную задачу нет, потому придется исхитриться.

Прежде всего необходимо присвоить своему устройству уникальное значение производителя и продукта, дабы легко определять его среди сотен других стандартных CDC-прошивок. VID и PID выдаются за денюжку, потому пойдем по пуути китайцев: втихую возьмем себе заведомо свободные значения.

Прошивка: В прошивке необходимо объявить две переменные до подключения библиотеки USB

Вместо FF10 можно вставить любые два слова (2 байта). Конечный результат содержится в прилагаемом архиве.

Драйвера: Так как драйвера не предназначены для нашей комбинации VID и PID, допишем наши значения в .inf файл вручную:

[DeviceList] %DESCRIPTION%=DriverInstall, USB\VID_FF10&PID_FF10

[DeviceList.NTamd64] %DESCRIPTION%=DriverInstall, USB\VID_FF10&PID_FF10

Софт: Для отлова событий подключения\отключения устройства подключим библиотеку ComponentUSB. Не считаю нужным пояснять каждую строчку: все изменения можно увидеть в прилагаемом проекте.

Результат

На скриншоте сложно разглядеть, но кнопка отправки активна только в момент наличия подключенного устройства, при этом каждые 50мс программа подает запрос на получение состояния кнопки (что, впрочем, неправильно, потому как нажатие кнопки должно обрабатываться на МК).

Как видно, организовать обмен данными между МК и ПК через USB — не самое сложное занятие. Полученное соединение можно использовать не только для конечынх целей: оно так же подходит для отладки программы. Ведь отправить на компьютер результаты расчетов, текущие состояния регистров и переменных куда нагляднее, чем моргать парой светодиодов азбукой морзе.

И напоследок: советую заглянуть в исходный код лампы настроения. Там можно найти довольно-таки хороший вариант обработки принимаемых данных для организации удобного протокола обмена.

📎📎📎📎📎📎📎📎📎📎