Как сделать usb устройство
Перейти к содержимому

Как сделать usb устройство

  • автор:

Как я делал USB устройство

Где-то когда-то я читал, что создатели первой спецификации USB намеревались сделать этот стандарт простым для реализации в железе за счет усложнения программного обеспечения. Таким образом цель была максимально удешевить производство и сделать USB устройства очень доступными. Сейчас можно сказать, что чипмейкерам это удалось, но так ли уж прост для реализации в железе этот стандарт? Боюсь, что ответ не очевиден.

Предположим мне нужно сделать свое простое USB устройство. Какие у меня есть варианты?

1. Использование внешних специальных микросхем, например, преобразователей USB-to-Serial. Уж пользоваться последовательным портом могут все. Компания FTDI выпускает такие микросхемы. Это и FT232R USB-UART и FT245R USB-FIFO. Использование этих микросхем делает разработку устройства очень простой, но немного увеличивает его стоимость.

2. Использование микроконтроллеров с встроенным интерфейсом USB. Такие микросхемы выпускает например Atmel (например микросхема AT90USB82). Хорошее решение, требует знания системы команд AVR и вообще микропроцессора AVR.

3. Использование микроконтроллеров AVR с программной эмуляцией USB интерфейса. Это знаменитая библиотека V-USB.

Нужно заметить, что выбирая любой из этих трех путей мы сильно упрощаем себе жизнь и на самом деле остаемся в неведении: «Трудно ли реализовать USB в железе?»

Здесь, под реализацией «в железе» я подразумеваю самый низкий уровень проектирования — схемотехнику.

Мне захотелось проверить саму возможность реализвации USB в «домашних условиях». Дело оказалось сильно не простым, но получилось!

Для своей реализации USB я взял плату Марсоход с ПЛИС Altera EPM240T100C5 (240 логических элементов). Ее схема вот.
Среда разработки Altera Quartus II v9.

Были написаны на языке Verilog: модуль приемника и передатчика, ядро USB — модуль решающий когда и что посылать. Все пакеты и дескрипторы были размещены во встроенной флеш памяти чипа CPLD.

Для своего устройства я взял idVendor=0x9FB компании Альтера (надеюсь они меня простят?) и idProduct=0x60A5 (с потолка).

Драйвер для Windows я не писал — взял из комплекта Microsoft Windows Driver Developer Kit пример драйвера BULKUSB.SYS и просто откомпилировал его. А вот INF файл пришлось менять — указывать свои ID производителя и ID продукта.

Программу для работы с устройством конечно пришлось писать.

Она может читать из устройства один байт и писать в устройство один байт. На плате Марсоход есть 8 светодиодов — теперь я могу их зажигать програмно. Еще на плате есть 4 кнопочки — теперь я могу программно читать из устройства их состояние. Немного, правда? Но эта штука уже реально может заменить, например, параллельный или последовательный порт — можно подключить какую нибудь самодельную «лампу настроения» или еще что-то.

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

Итого, мое простое устройство заняло в чипе CPLD 215 логических элементов.

Конечно, я не слишком следовал стандарту USB — я не делал проверку контрольных сумм принятых пакетов или проверку четности toggle. Тем не менее испытания показали вполне стабильную работу устройства в течении длительного времени.

Более подробная информация есть на сайте платы Марсоход.

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

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

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

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

Железо.

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

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

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

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

Прошивка

Начнем с минимума:

include 18f2455 — библиотека для используемого МК

enable_digital_io () — переключение всех входов на цифровой режим

alias Button is pin_B7 — раз уж у нас подключена кнопка, объявим ее
pin_B7_direction = input — кнопка у нас работает на вход

— одна строчка — и у нас есть все необходимое для работы с USB CDC
include usb_serial — бибилотека для работы с usb

usb_serial_init () — —инициализируем USB CDC
forever loop — основной цикл, выполняется постоянно
usb_serial_flush () — обновление usb. Данная процедура выполняет все необходимые
— действия для поддержания соединения с ПК
end loop

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

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

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

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

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

include 18f2455

enable_digital_io ()

alias Button is pin_B7
pin_B7_direction = input


include usb_serial

usb_serial_init ()
var byte ch — объявляем переменную
forever loop — основной цикл
usb_serial_flush ()
if ( usb_serial_read ( ch ) ) then — если байт получен, он будет записан в ch
usb_serial_data = ch — отправляем полученный байт обратно
end if
end loop

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

Пока у нас нет своего, используем готовый терминал: я использовал программу 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-устройством.

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

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

include 18f2455

enable_digital_io ()

alias Button is pin_B7
pin_B7_direction = input


include usb_serial

usb_serial_init ()
var byte ch
var byte i — объявляем вторую переменную
forever loop — основной цикл
usb_serial_flush ()
if ( usb_serial_read ( ch ) ) then — если байт получен выполняем необходимые действия
case ch of — перебираем номер байта
0 : usb_serial_data = 0xff
1 : usb_serial_data = Button — отправка состояния кнопки
OTHERWISE block — если получено что-то иное
for 16 using i loop — отправляем 10 байт с данными
usb_serial_data = ch + i — от ch до ch+15
end loop
end block
end case
end if
end loop

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

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

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

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

> include print
.
var dword data
print_dword_hex ( usb_serial_data , data )

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

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

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

while ( usb_cdc_line_status () == 0x00 ) loop
end loop

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

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

const byte USB_STRING3 [ 24 ] =
24 , — длина массива
0x03 , — bDescriptorType
«0» , 0x00 ,
«1» , 0x00 ,
«2» , 0x00 ,
«3» , 0x00 ,
«4» , 0x00 ,
«5» , 0x00 ,
«6» , 0x00 ,
«7» , 0x00 ,
«8» , 0x00 ,
«9» , 0x00 ,
«X» , 0x00
>

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

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

const byte USB_STRING2 [ 28 ] =
28 , —
0x03 , — bDescriptorType
«D» , 0x00 ,
«e» , 0x00 ,
«m» , 0x00 ,
«o» , 0x00 ,
» « , 0x00 ,
«B» , 0x00 ,
«o» , 0x00 ,
«a» , 0x00 ,
«r» , 0x00 ,
«d» , 0x00 ,
» « , 0x00 ,
» #FF0000″>0x00 ,
«)» , 0x00
>

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

[Strings]
DESCRIPTION=«Demo CDC»

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

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

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

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

const word USB_SERIAL_PRODUCT_ID = 0xFF10
const word USB_SERIAL_VENDOR_ID = 0xFF10

Вместо 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 — не самое сложное занятие. Полученное соединение можно использовать не только для конечынх целей: оно так же подходит для отладки программы. Ведь отправить на компьютер результаты расчетов, текущие состояния регистров и переменных куда нагляднее, чем моргать парой светодиодов азбукой морзе.

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

Проекты Altera Quartus II для плат Марсоход, Марсоход2 и Марсоход3

Совсем недавно мы опубликовали статью о протоколе USB здесь .
И вот, пользуясь нашими знаниями об USB, мы попытались подключить плату Марсоход через интерфес USB к компьютеру. Дело это на самом деле оказалось совсем не простое.

Обнаружилось несколько технических проблем.

Самых главных проблем три:

  • Малое количество элементов в микрохеме CPLD, которая установлена на плате Марсоход. Микросхема EPM240T100C5 имеет всего 240 логических элемента (каждый элемент содержит триггер и некоторую программируемую логику).
  • Отсутствие подходящего тактового генератора. В чипе есть генератор, который работает на частоте примерно 5МГц, но это даже для низкоскоростного устройства не очень подходит. У Low Speed устройств частота передачи 1,5МГц, что всего в 3 раза (с хвостиком) ниже, чем имеющиеся у нас 5МГц.
  • Необходимо писать драйвера для USB устройства.

Ну что же, мы не привыкли отступать, попробуем нашу «блоху подковать» и сделать невозможное возможным.

marsohod_usb

Начну описание, пожалуй, с генератора. Как я уже сказал, у нас в наличии нет нужной нам тактовой частоты. Соотношение имеющейся частоты к нужной примерно равно 5МГц / 1,5МГц = 3,33. Это значит, что если пользоваться имеющейся опорной частотой 5МГц, то придется не все биты передаваемых данных делать одинаковой длины – некоторые будут уже (3 такта), а некоторые шире (4 такта). Я думаю, это вполне приемлимое решение, ведь приемник USB всегда синхронизируется по принимаемым данным. Именно изменение состояний линий DP/DM на противоположные обозначает бит «ноль» на приемном конце. По этому же изменению состояний и синхронизируется приемник. А что же «единица»? Если передается бит «единица», то линии DP/DM не меняют своего состояния. Если передается несколько подряд единиц, то генераторы на приемной и передающей сторонах теоретически могут разойтись и это приведет к неправильному приему. К счастью в протоколе USB применяется алгоритм «bit stuffing». По этому алгоритму если в передаваемом битовом потоке встретились шесть «единиц» подряд, то всегда принудительно вставляется один «ноль». На приемной стороне после шести «единиц» подряд следующий «ноль» принудительно удаляется. Значит, если за длительность шести бит генераторы приемника и передатчика расходятся немного, то это не страшно. Следующий «ноль» пересинхронизирует приемник и все будет ОК.

По поводу логики USB. Мы написали на VERILOG модуль передатчика ls_usb_send.v и модуль приемника ls_usb_recv.v. Первый модуль просто передает RAW пакет, а второй просто принимает RAW байты приходящих пакетов. RAW – значит пакет или байты «как есть, без обработки». Эти модули довольно простые. На мой взгляд гораздо труднее было написать третий модуль ls_usb_core.v. Этот модуль собственно реализует нашу USB функцию. Попробую объяснить в чем проблема. Любое настоящее USB устройство при подключении к компьютеру (хосту) должно пройти целую процедуру «опознания и конфигурации». Примерно вот такая последовательность:
1) Сброс шины RESET (обе DP/DM в нуле более 10 миллисекунд).
2) Хост читает описатель устройства (DeviceDescriptor). Эта процедура занимает несколько транзакций на шине USB:
a) транзакция SETUP (состоит из трех пакетов: SETUP, DATA0, ACK);
b) несколько транзакций чтения из устройства IN (каждая состоит из трех пакетов IN, DATAx, ACK);
c) транзакция записи в устройство пустого пакета OUT (состоих из трех пакетов OUT, DATA1, ACK);
Вот как может выглядеть протокол чтения DeviceDscriptor:

GetDescriptor

От того, что вернуло устройство в этом описателе многое зависит далее в процедуре инициализации. Так, например, в нем написано, есть ли у устройства серийный номер и вообще какие-нибудь строковые описатели (те, что появляются в виндовз в баллоне «найдено новое устройство. »).
3) Хост устанавливает USB адрес для нового подключенного устройства. Это тоже еще две транзакции и каждая по три пакета.

SetAddress

4) Хост читает ConfigurationDescriptor. Еще куча транзакций.
5) Хост читает описатели строк.
6) Хост устанавливает конфигурацию.

И все вышеописанное – это только самый минимум. Задача модуля ls_usb_core – правильно отвечать в нужной последовательности на все запросы хоста анализируя и разбирая запросы от хоста. Нужно зараннее приготовить все описатели, желательно сразу просчитать их контрольные суммы (потому что наш модуль из-за его простоты не считает контрольных сумм). Все эти данные нужно где-то хранить (а памяти как таковой у нас нет, есть только логика). Вся вот эта логика оказывается довольно мудреная.

Итак подведем некоторый итог. Что мы реализовали, а что не реализовали:

  1. Сделали приемник, но он не запоминает весь принятый пакет, он только передает для core принятый байт и его номер в пакете. Core должна решить, нужен ей этот байт или нет. Так экономим немного места в чипе.
  2. Приемник не проверяет контрольную сумму принятых пакетов (так же для экономии места в чипе).
  3. Приемник не следит за правильным чередованием пакетов четный-нечетный (toggle).
  4. Передатчик не считает контрольные суммы передаваемых пакетов, Core должна передать ему их вместе с остальными байтами пакета.
  5. Передатчик так же не формирует четный-нечетный пакеты. Он передает байты «как есть», те что приходят от Сore.
  6. Core не проверяет правильность PID пакетов и следит только за младшей тетрадой PID. Идентификаторы пакетов PID всегда должны быть вида 0x69, 0xe1, 0x2d – тоесть младшая тетрада это инверсия старшей. Мы не анализируем старшую тетраду.
  7. Core не проверяет USB адрес принятых пакетов! Внимание – это может служить причиной конфликтов устройств на шине USB! Не вставляйте наш девайс совместно с другими если используете USB HUB.
  8. Core имеет жестко закодированные в мультиплексор таблицы описателей (descriptors) и прочие пакеты, которые нужно посылать.
  9. Core имеет только один байтовый регистр на запись – мы будем записывать в него данные с помощью программки с компьютера.
  10. Пока мы не можем читать из нашего устройства произвольные данные (для этого нам понадобилось бы реализовать подсчет контрольной суммы передаваемых пакетов).
  11. Пока мы можем записывать в наше устройство только один байт с помощью CONTROL transfers, его будет видно на светодиодах платы.
  12. Описатели устройства мы сделали максимально похожими на существующий device – ключ защиты Rainbow. Почему именно его? Ну не знаю, попался под руку.

Ну и последний немаловажный вопрос – что делать с драйверами устройства? Сразу скажу, мы не будем писать драйвер. Ну может когда нибудь позже. Сейчас мы напишем программу, которая просто просматривает дерево подключенных USB устройств и найдет наше устройсто. Посылая этому нашему устройству специальные пакеты мы сможем зажечь светодиодики на плате Марсоход. Программа, которую мы написали выглядит вот так:

UsbControlApp

Интерфейс минимальный. Сперва нужно нажать кнопку «Scan USB» и в заголовке диалога должна появиться строка найден наш девайс или нет. Потом устанавливая галочки в CheckBox одновременно зажигаете светодиоды на плате. Программу пробовали и на Windows XP и на Windows 7.

Вы можете взять исходный код этой программы написанной на C++ (Microsoft Visual Studio 6.0) и исходный код проекта для платы Марсоход здесь:

Очень простое USB устройство сделанное из платы Марсоход ( 144333 bytes )

Посмотрите на это демонстрационное видео. Здесь показано, как при подключении платы Марсоход компьютер обнаруживает устройство и в DeviceManager появляется новая строка с желтым восклицательным знаком (нет драйверов). Потом с помощью нашей программы мы зажигаем светодиоды на плате и драйвера нам не нужны.

USB устройство своими руками

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

Прежде чем приступить к этому вы должны были в предыдущей статье ознакомится с Управлением машиной и получить знания о том, что такое микроконтроллер AVR. Если этого не было, сделайте это.

Далее нам надо для связи по USB сделать устройство, программу для микроконтроллера AVR и программу для компьютера.

USB управление для своего устройства

Берем кабель USB и с одной стороны срезаем провода, видим 4 провода, которые нам нужны — красный, чёрный, жёлтый (бывает зеленым) и белый. Красный провод — это плюс питания +5V, чёрный провод — это минус питания. Этими проводами запитываем наше устройство. А другие 2 провода — жёлтый D+ и белый D- используются для обмена данными по USB.

Схема USB устройства на микроконтроллере AVR

На рисунке показана схема простого самодельного USB устройства. Оно выполнено со всеми требованиями стандарта USB и при корректной сборке отлично работает. Встроенной частоты, на которой может работать USB в микроконтроллере AVR нет, поэтому потребуется установить внешний генератор частоты в 12 МГц, а для сглаживания сигнала от него еще два конденсатора по 20 пФ. По документации на используемый микроконтроллер AVR узнаем куда подключается внешний генератор частоты на том микроконтроллере, который мы используем. В данном случае это линии PA1 и PA0, в других же микроконтроллерах побольше обычно есть линии XTAL, тут их нет, поэтому подключаем куда написано вместо них.

Красным и чёрным проводом запитываем микроконтроллер — эти линии просто подключаются к VCC и GND. Линии с данными USB — это D+ и D- подключаются к линиям микроконтроллера, не к случайным, а к тем что заданы в программе для микроконтроллера, которую позднее будем делать, сразу скажу что это будут линии PD2 и PD4. Просто так нельзя подключать их, надо на пути поставить резистор на 68 Ом по требованию стандарта USB и еще снизить напряжение на данных линиях с 5V до 3.6V. Для резисторов посчитаем их мощность — ток USB задается в программе для микроконтроллера и мы его зададим на уровне 0.05 A, значит мощность резисторов 0.05 A * 5 V = 0.25 Вт. Такие и берем. Напряжение снижается параллельным подключением дидов Зенера 3V6 как показано на рисунке, эти диоды надо воткнуть правильной стороной, напомню что чёрная полоска на диоде с одной стороны должна быть направлена в сторону линий D+ и D-, а обратная сторона к минусу питания. И последнее, чтобы работало устройство USB, надо резистором в 1.5 кОм указать режим его работы, подключаем его к D-, это означает низкоскоростной режим, который нам нужен, поскольку мы не планируем передавать большие объемы данных, а только будем посылать простые сигналы, низкоскоростной режим нам нужен. Чтобы увидить что наше устройство работает, подключим светодиод к линии PB0, длинной плюсовой стороной к линии, а другой стороной к минусу питания.

Также воспользуемся вспомогательными инструментами для удобства — это зажим на 20 гнезд, чтобы легко извлекать микроконтроллер для перепрограммирования. И это 2 клеммника, чтобы прикрутить провода USB к макетной плате. Вообщем для сборки устройства нам потребуется сделать такой закуп:

ATtiny2313A купить

Это: Микроконтроллер
Контакты: 20
Программа: 2 Kb

Резистор 68 Ом купить

Сопротивление: 68 Ом
Мощность: 0.25 Вт

Диод Зенера 3V6 купить

Пропускает: 3.6 В
Тип: Понижающий

Резистор 1.5 кОм купить

Сопротивление: 1.5 кОм
Мощность: 0.25 Вт

Кварцевый генератор 12 купить

Частота: 12 МГц
Тип: пассивный

Конденсатор 20 pF купить

Емкость: 20 pF
Тип: Керамический
Макс. напряжение: 50 V

Светодиод купить

Цвет: Белый
Напряжение: 3 V

Зажим на 20 гнезд купить

Гнезда: 10 x 10

Клеммник купить

Контактов: 2
Зажим: прикрутка

Кабель USB купить

Интерфейс: USB 2.0
Длина: 100 см

Макетная плата купить

Размер: 63 x 2 горизонтальных линий и 4 вертикальные линии

Проводки купить

Тип: Навесные
Количество: 40

Набор проводков купить

Количество: 140

Собранное готовое к работе USB устройство выглядит так:

USB устройство своими руками

Программа на микроконтроллер AVR для USB связи

1) Скачиваем исходные коды OpenRoboFW и распакуйте архив на диск C:\, я собрал этот архив из файлов проекта V-USB, настроил их на Attiny2313A и тактовую частоту 12 МГц (эти настройки меняются в файле Makefile, если используется другой микроконтроллер), упростил код для внесения изменений. V-USB дает нам исходники программ с идентификаторами VID и PID, которые нужны для распознания USB устройства компьютером. На всякий случай вот ссылка на проект . После распаковки архива содержимое директории выглядит так:

Назначение важных файлов:
main.c — главный файл программы на языке C, она осуществляет обработку информации, пришедшей по USB, и в ней программируется реакция микроконтроллера на USB пакеты с компьютера
Makefile — параметры сборки, тип микроконтроллера (взяли — attiny2313a), частота, информация по фьюзам для других микроконтроллеров, при использовании другого микроконтроллера, в этом файле его нужно прописать в строке DEVICE (например — atmega16a)
usbconfig.h — параметры USB, порт подкючения линии D-, ток

Откройте main.c и обратите внимание на 2 области кода, которые там выделены — программа реакции на USB пакет и настройка портов. В этих областях надо вставлять свой код, остальное можно не трогать. Для примера там уже стоит код, его можно менять. В приходящем пакете USB с компьютера есть 2 параметра — p1 и p2, которые передаются из программы в виде параметров, в зависимости от их значений можно менять состояние линий микроконтроллера (портов) в программе реакции.

2) Скачиваем программу WinAVR и устанавливаем, она нужна для создания HEX-прошивки микроконтроллера AVR, сайт WinAVR

3) Запускаем командную строку Windows, которая находится тут c:\Windows\System32\cmd.exe появляется чёрное окно. В этом окне вводим 2 команды (набираем команду, нажимаем enter):

Переходим в директорию OpenRoboFW
Запускаем подготовку программы для микроконтроллера

Подготавливаем программу МК

После чего в директории C:\OpenRoboFW забираем файл main.hex и прошиваем его в микроконтроллер. Если в процессе появилась ошибка типа warning unused variable, а также can not set period, или нечто подобное, что не имеет большого значения, ничего страшного, все в порядке, используем полученный файл, он полностью нормальный и рабочий. Главное если файл появляется, значит программа была успешно сделана и может быть использована. Напоминаю Как прошивать.

Также надо прошить fuse-биты настроек микроконтроллера, чтобы изменить его тактовую частоту на совместимую с USB стандартом. Встроенной подходящей частоты нет, поэтому применяется внешний источник тактовой частоты 12 МГц. Для прошивки настроек fuse-битов устройство с программатором надо доработать, подключить к микроконтроллеру внешний кварцевый генератор 12 МГц и 2 конденсатора 20 пФ по схеме как на устройстве USB, т.е. в случае с ATTiny2313A к линиям PA1 и PA0, и также соединить это всё с минусом питания, вообщем как на схеме USB устройства, смотрите на фото ниже как подключено. Это нужно для того, чтобы устройство продолжило работу после установки для него нового источника тактового сигнала, именно этот источник 12 МГц и подключаем. Не забудьте кроме программы также прошить fuse-биты.

Меняем настройки программатором

Программатор USBasp купить

Микроконтроллеры: AVR

Кварцевый генератор 12 купить

Частота: 12 МГц
Тип: пассивный

Конденсатор 20 pF купить

Емкость: 20 pF
Тип: Керамический
Макс. напряжение: 50 V

Мини макетная плата купить

Размер: 17 x 2 горизонтальных линий

Зажим на 20 гнезд купить

Гнезда: 10 x 10

При этом установите такие биты конфигурации:

Для ATtiny2313A (наш случай):
CKSEL0..3=1111 SUT0..1=10 CKOUT=1 CKDIV=1 RSTDISBL=1 BODLEVEL2..0=101 WDTON=1 SPIEN=0 EESAVE=1 DWEN=1

Установите В справочнике эти fuse-биты и прошейте их в микроконтроллер полученной строкой через программу AVR dude, о том Как прошить тут. Также дам на всякий случай fuse-биты для других микроконтроллеров.

Для ATmega8A:
CKSEL0..3=1111 SUT0..1=10 BODEN=0 BODLEVEL=1 BOOTRST=1 BOOTSZ0..1=00 EESAVE=1 CKOPT=0 SPIEN=0 WDTON=1 RSTDISBL=1

Для ATmega16A:
CKSEL3..0=1111 SUT0..1=10 BODEN=0 BODLEVL=1 BOOTRST=1 BOOTSZ0..1=00 EESAVE=1 CKOPT=0 SPIEN=0 JTAGEN=1 OCDEN=1 LB1..2=00 BLB=0000

Теперь когда программа написана и записана в микроконтроллер, можно проверить правильность сборки устройства. Напишите программу на компьютер для USB связи, читайте далее как это сделать и при этом в процессе будет установлен фильтр для возможности опознания устройства. Подключите устройство к USB порту компьютера, установите фильтр как далее будет описано, если все правильно было собрано и установлено, то оно будет обнаружено и опознано как LEDCtlHID. Если этого не произошло, то ищите проблему и ошибки при сборке устройства или при установке программ, еще раз внимательно все сверьте и проверьте, что ничего не пропущено и что все контакты соединены или запаяны как надо. Бывает проблема тут, если пайка была совершена с неконтактами, с не правильными контактами или не всеми контактами, а также что элементы схемы были повреждены высокой температурой паяльника, и устройство поэтому не работает. Важно собирать устройство именно на макетной плате первый раз.

Программа на компьютер для USB связи

Для начала сделаем простую консольную программу, потом будем делать программу с окнами.

1) Скачиваем исходные коды консольной программы OpenRobo, которые собраны из исходников проектов V-USB и LibUSB. Распаковываем архив на диск C:\, после чего содержимое директории C:\OpenRobo должно выглядеть так:

Назначение файлов:
include и lib — это драйверы LibUSB для доступа к USB из программы
set-led.c — это исходник нашей программы

2) Скачиваем программу LibUSB, которая нужна для работы с устройством из программы. Устанавливаем. Сайт разработчика LibUSB. После установки подключите собранное устройство к USB. Запустите через меню Пуск/Программы/LibUSB-Win32/Filter Wizard. В открывшейся программе выберите для установки фильтра на порт, в следующем списке выберите только что подключенное устройство, его можно найти по vid:16c0 pid:05df. Нажмите кнопку Install. Если устройства в списке нет, значит вы либо не правильно собрали устройство и оно не опозналось, либо драйверы уже были установлены. В случае смены порта подключения устройства, надо опять заходить и устанавливать фильтр, иначе программа не сможет распознать устройство. Обязательно подключайте устройство USB к тому же порту USB, что был подключен при установке фильтра!

3) Скачиваем программу MinGW, которая нужна для сборки программы из исходника. Устанавливаем. Сайт разработчика MinGW. После установки зайдите в Панель управления, Система, Дополнительные параметры системы, Дополнительно (вкладка), Переменные среды (кнопка), Системные переменные (список), Переменная Path (найти в списке, выделить, нажать кнопку Изменить). Дописываем в поле Значение переменной в конце ;c:\MinGW\bin\ и нажимаем ОК, надеюсь вы установили MinGW в C:\MinGW, иначе надо путь другой прописать.

Настраиваем системную переменную Path для MinGW

4) Запускаем командную строку Windows, которая находится тут c:\Windows\System32\cmd.exe
В этом окне вводим 2 команды (набираем команду, нажимаем enter):

Переходим в директорию OpenRobo
Запускаем подготовку программы для компьютера

Если выходит ошибка, что команды make нет, значит вы не установили выше указанную программу WinAVR, сделайте это. Если выходит ошибка, что команды gcc нет, значит не прописан правильно путь в системную переменную Path, сверьте со скриншотами то как прописано у вас, также возможно командная строка была запущена до прописывания пути, надо перезапустить.

Подготовка программы ПК

5) Теперь смотрим в директорию C:\OpenRobo, там появился готовый файл программы set-led.exe. Не выходим из cmd, здесь же теперь мы можем набрать команды запуска программы и получить результаты:

Включает светодиод
Отключает светодиод
Показывает включен или отключен светодиод

7) Если все сделано правильно, то при подключении USB устройства и наборе команд в консоле, светодиод будет включаться и выключаться.

Изменяем программы под наши нужды

Берём под контроль все порты, прибавляем свои функции. И так, теперь в C:\OpenRobo размещены исходные коды программы для компьютера, а в C:\OpenRoboFW исходные коды программы для микроконтроллера. Их можно изменять, но после каждого изменения надо их занова пересобирать (и перепрошивать микроконтроллер), делать это теперь вы умеете — см. выше пример — пересобрать обе программы можно 4-мя командами в cmd:

Переходим в директорию OpenRoboFW
Запускаем подготовку программы для микроконтроллера
Переходим в директорию OpenRobo
Запускаем подготовку программы компьютера

Нам нужно подключить к управлению все порты микроконтроллера Attiny2313. Свободных линий портов у нас получается 13 штук, под контролем находится сейчас только 1 линия, светодиод подключен к ней. Возьмём под контроль все 13 линий.

Изменение программы для микроконтроллера

1) Открываем файл C:\OpenRoboFW\main.c, находим там область кода, обозначенную как настройка портов, надо установить все свободные порты на вывод командами DDR, меняем содержимое области на следующий код. Код для main.c:

Настроили порт B на вывод записав единицы в DDR
Порт D не весь настраиваем, а по одному биту вбиваем единицы таким образом на биты 0, 1, 3, 5 и 6

Небольшое пояснение этого кода — порт B полностью свободен, все его разряды сразу ставим на вывод, порт A полностью занят, его не трогаем, порт D частично занят, поэтому на вывод ставим только свободные разряды, которые тут и перечисляем — 0, 1, 3, 5, 6.

2) Открываем файл C:\OpenRoboFW\main.c, находим область кода, обозначенную как программа реакции на USB пакет, сделаем, чтобы по команде зажигались светодиоды на всех свободных линиях. Меняем содержимое области на. Код для main.c

Если первое число пришло 1
Записываем единицы в порт B и в части порта D
Если первое число пришло другое
Записываем нули в порт B и в части порта D

Пересобираем и перепрошиваем, убеждаемся, что светодиод работает на всех линиях.

К тому же неплохо бы научится вставлять свои функции, которые будут выполняться по вашим командам, пока есть только on, off, status, нужно добавить еще команд, например добавим команду discoteka, по которой будет вызываться какая-нибудь наша функция в микроконтроллере, например — светодиоды горят через один на порте B. Добавим в микроконтроллере обработку дополнительных кодов, пусть это будет код 2, т.к. 1 и 0 уже используются для функций on и off, из программы для компьютера будем посылать потом микроконтроллеру код 2 функцией, вызываемой по команде discoteka. Опять меняем файл C:\OpenRoboFW\main.c, область реакции. Код для main.c

Если пришла единица
Включить весь порт B
Если пришла двойка
Включить биты порта B через один
Если пришло другое
Вылючить весь порт B

Вставляем обработку кодов таким образом, старые коды 1 и 0 как видите сохранили свои функции (зажечь / потушить светодиоды) и появился еще новый код 2, по которому на порте B выставляется состояние, при котором светодиоды через один горят. Таким же образом можно сюда поставить любые другие коды и написать для них необходимые функции.

Изменение программы для компьютера

1) Открываем файл C:\OpenRobo\set-led.c

2) Надо научиться отправлять микроконтроллеру более сложные сообщения, при приеме которых он будет выполнять нужные функции. Сейчас отправляется только 2 сообщения в переменной isOn — 0 = выключить светодиод, 1 = включить светодиод. Научимся отправлять также код 2 = выставить светодиоды через один на порте B (в предыдущем примере мы подготовили микроконтроллер для обработки этого кода, теперь просто пошлём его программой с компьютера).

3) Редактируем файл set-led.c вставляем сверху от строчки

следующий код, который добавляет новый код вызова 2 для функции микроконтроллера

Код отправляет команду 2 микроконтроллеру, что вызывает там запуск дискотеки. Пересобираем программу для компьютера и нам становится доступна новая команда — discoteka. Таким образом можно вставить любые другие коды и функции для них. В C++ есть функция Sleep(1000); она задаёт задерку выполнения в милисекундах, например 1000 — это 1 секунда. Она полезна, с ней также можете сделать светодиоды мигающими. В будующем эта функция пригодится.

Обратите внимание на функцию usb_control_msg, в ней можно передать 2 изменяемых параметра (в первом у нас передаётся 2 или переменная isOn, которая равна 1 или 0), следующее число (там стоит 0) может передать еще 1 значение, например, 1 переменная может быть всё также быть изначальной командой на on или off, а второй переменной можно передать, например, номер порта, который следует включить. Получить доступ к этим параметрам в программе для микроконтроллера можно по именам p1 и p2, для этого примера p1=2, p2=0.

Как сделать программу для Windows

Чтобы создать программу для Windows, скачайте бесплатное средство разработки программ для Windows . На указанной странице найдите в середине где-то по названию продукт Microsoft Visual C++ 2010 Express и установите его. Но перед этим проверьте, что у вас не установлена программа .NET Framework версии выше 4. Если такая установлена в вашей системе — перед установкой удалите ее. В комплекте будет поставлен фреймворк нужной версии 4 автоматом. Иначе у вас не будут работать проекты, будет возникать ошибка — Microsoft Resource File To COFF Object Conversion не работает при попытке компиляции проекта. Windows update автоматом попытается поставить последнюю версию .NET Framework, нужно такую установку отменить — не перезагружать компьютер с новыми обновлениями, а зайти в Windows Update и снять галочку напротив фреймворка.

1) Заходим на сайт, выбираем русский язык, нажимаем INSTALL NOW.
2) После установки запускаем, нажимаем Создать проект. Для начала создадим простую программу с 1 кнопкой — нажимаем эту кнопку, текст в ней меняется.
3) Выбираем шаблон Приложение Windows Forms, внизу вводим название программы, нажимаем OK.
4) Перетаскиваем справа элемент Button на центральное окно с формой и оставляем его внутри формы.
5) Кликаем на форму и на кнопку, при этом справа внизу есть окно Свойства, его содержимое меняется. В этом окне задаём необходимые свойства элементов — текст кнопки и окна. Редактируем свойство Text, пишем там любой текст, например, Моя кнопка. Текст на кнопке меняется, это видно на форме в центральном окне.
6) Теперь сделаем так, чтобы при нажатии на эту кнопку, ее свойство, которое определяет текст на ней менялось на другое. Так же можно менять любые другие свойства.
7) Делаем двойной клик на кнопке Моя кнопка, которая находится в форме в центральном окне, попадаем в файл кода Form.h (его можно выбрать в любое время слева в меню). В этом файле после двойного клика была создана функций button1_Click (остальной код автоматом был создан по шаблону при создании проекта), курсор находится внутри нее, нажмите Enter, чтобы создать пустую строку, куда и будем писать код.
8) Как можно видеть в этом же файле чуть повыше есть разделы — button1 и form1, там заданы начальные установки для элементов — кнопки и формы. Воспользуемся ими, скопируем внутрь нашей функции клика по кнопке (это место отмечено красной стрелкой) строчку, где устанавливается название кнопки, а именно this->button1->Text = L»Моя кнопка»; Только текст Моя кнопка изменим на Работает!
9) Нажимаем на кнопку с зеленым треугольником, через некоторое время наша программа запустится (если вы не совершили ошибок) и при нажатии на кнопку, текст внутри меняется с Моя кнопка на Работает!
10) Забрать готовую программу (файл .exe) можно в директории текущего пользователя
Весь процесс показан на картинках.

Делаем свою программу для Windows, из которой будем управлять устройством USB

Перенос программы для USB в Windows Forms. Я перенес программу управления из консольного приложения в приложение с окнами Windows и немного улучшил её — сделал управление шаговым двигателем в дополнение к управлению светодиодом, добавил горячие клавиши запуска двигателя в определенном направлении с заданным количеством шагов, управляющие сигналы драйвера шагового двигателя подключены к портам PB7 (шаг), PB6 (направление), PB5 (питание), светодиод подключен к порту PB0. Вы можете просто скачать мой проект для Microsoft Visual C++ 2010 Express Edition (русская версия) и посмотреть как я это сделал.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *