Вот пришла мне такая мысль в голову.
Хотелось бы сделать наш сайт интересным даже детям. хотелось бы заинтересовать их электроникой, программированием, робототехникой!
Конечно, программирование ПЛИС - не очень простая задача, но может это им и не нужно? Есть замечательная среда программирования для детей - это Scratch. Эта среда программирования может использоваться со специальными ScratchBoard платами и к ним можно подключить датчики и моторчик.
Я уже писал раньше про Scratch. Более того, у меня даже был проект для платы Марсоход - ее можно было подключить к компьютеру как ScratchBoard. Правда был у того проекта недостаток - плата подключалась через последовательный порт. Поэтому кроме платы Марсоход нужно было еше паять дополнительную платку преобразователь уровней для RS232 - ведь последовательный порт компьютера использует уровни сигналов +12В и -12В. Кроме того, сейчас уже не у каждого компьютера есть последовательный порт. Поэтому иногда нужен еще кабель USB-Serial. В общем не очень просто...
Вот поэтому я подумал - нужно сделать новый проект для платы Марсоход.
Я сделал такую "прошивку", что теперь плата Марсоход работает как USB устройство, распознается компьютером как "последовательный порт" и посылает компьютеру данные в формате Scratch! Все стало гораздо проще! Четыре кнопочки платы Марсоход действуют как датчики сопротивление-А, сопротивление-B, сопротивление-C, сопротивление-D в программе Scratch. Значения эти датчики правда дают не аналоговые я только 0 и 100. Моторчик коллекторный, небольшой мощности (ведь у нас все питается от USB) подключается к пинам F3 и F4.
Вот посмотрите: на языке Scratch "пишем" вот такую простую программу:
Смысл этой программы очень простой - моторчик включается, когда срабатывает датчик света. К ноутбуку или компьютеру через USB подключаем нашу плату Марсоход (прошитую моим новым проектом), к плате моторчик и опто-транзистор - это датчик света. Смотрим как это работает:
Конечно это только простой пример использования. Ну а дальше я думаю ваша фантазия и фантазия ваших детей. Творите и программируйте!
Дальше я расскажу о самой "прошивке" для платы Марсоход. Проект довольно сложный, но в принципе, те кто заинтересуются могут просто, не разбираясь в тонкостях проекта ПЛИС, один раз зашить плату и использовать ее как ScratchBoard.
Итак. Что нам нужно?
Исходная отправная точка - USB проект, уже опубликованный ранее на нашем сайте. К плате Марсоход нужно припаять кварцевый генератор на 24Мгц. Этот генератор нам нужен для стабильной работы устройства.
В нашем USB устройстве все дескрипторы USB записаны во встроенной флеш памяти ПЛИС EPM240T100C5 на плате Марсоход. Именно эти дескрипторы определяют тип или класс, к которому принадлежит устройство. Когда устройство USB подключается к компьютеру, то операционная система читает дескрипторы из устройства и определяет какой драйвер загружать. Таким образом, нам нужно записать во флеш "правильные" дескрипторы, которые описывают устройство как последовательный порт. Если мы правильно зададим все дескрипторы и сделаем поведение нашего устройства "стандартным", то нам даже не придется писать драйвер к нашему устройству - операционная система использует уже имеющийся у нее набор драйверов (хотелось бы верить).
Вообще-то существует специальный документ (на английском языке) - спецификация USB для коммуникационных устройств. Я положил этот документ в раздел для загрузки на нашем сайте:

Попробую дать некоторые пояснения.
Когда устройство подключено к порту USB компьютера, то первое, что делает операционная система компьютера - читает из устройства так называемый DeviceDescriptor - описатель устройства. Структура DeviceDescriptor, если описывать на языке С выглядит вот так:
// USB Standard Device Descriptor
unsigned char usb_device_descriptor[] = {
0x12, // bLength
USB_DEVICE_DESCRIPTOR_TYPE, // bDescriptorType = 1
0x10,
0x01, // bcdUSB = 1.10
0x02, // bDeviceClass: CDC = 2
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x08, // bMaxPacketSize0
0xFB,
0x09, // idVendor = 0x09FB
0xA6,
0x60, // idProduct = 0x60A6
0x00,
0x01, // bcdDevice = 1.00
1, // Index of string descriptor describing manufacturer
2, // Index of string descriptor describing product
0, // Index of string descriptor describing the device's serial number
0x01 // bNumConfigurations
};
Мы делаем свое USB устройство и должны запулнить эту структуру. Нужно обязательно заполнить следующие поля:
- idVendor - я пишу 0x09FB, это id компании Альтера (надеюсь они меня не побьют? ведь id по правилам я должен купить у USB.ORG за большие деньги, а я использую чужой, компании Альтера)
- idProduct - берем с потолка, но помним, что у одного производителя все "продукты" пронумерованы, то есть на самом деле мы не можем взять число, уже использующееся компанией Альтера.
- bDeviceClass - ставим число 2, это будет значить устройство типа COM порта, модема и т.д, тоесть коммуникационное устройство Communication Class Device
- bMaxPacketSize0 - ставим 8, это обычная длина пакета для низко-скоростных устройств.
Вот у нас есть структура DeviceDescriptor длиной 18 байт. Мне нужно расположить ее во флеш микросхемы CPLD платы Марсоход по некоторому адресу. Но не все так просто. Дело в том, что USB транзакции сами по себе происходят за несколько довольно мудреных шагов, передача идет пакетами данных не более 8 байт и защищены контрольными суммами.
Поскольку мое USB устройство весьма примитивно, то я хочу во флеш разместить уже готовые пакеты, со всеми дополнительными байтами типа SYN, PID, CRC (зараннее посчитанные). Вот я это и сделал - содержимое флеш в чипе CPLD описывается файлом c с расширением *.mif (memory initialization file). Вот его фрагмент, который описывает DeviceDescriptor:
-- Descriptor Device
0080 : 4B8D; -- SYN&Len, PID
0081 : 0112; -- Data..
0082 : 0110;
0083 : 0002;
0084 : 0800;
0085 : CF10; -- CRC16
0086 : 0000;
0087 : 0000;
0088 : C38D; -- SYN&Len, PID
0089 : 09FB; -- Data
008A : 60A6;
008B : 0100;
008C : 0201;
008D : 5C21; -- CRC16
008E : 0000;
008F : 0000;
0090 : 4B87; -- SYN&Len, PID
0091 : 0100; -- Data
0092 : 8F3F; -- CRC16
0093 : 0000;
Так, DeviceDescriptor будет прочитан за 3 передачи, две по 8 байт данных и одна короткая 2 байта данных.
Вообще, чтобы ощутить всю "прелесть" и "замороченность" USB протокола можете скачать программу USB анализатора с сайта http://www.ellisys.com/products/usbex260/download.php Эта программа к аппаратному анализатору USB протокола - это такая коробочка, которая ставится между исследуемым устройством и компьютером и записывает весь USB трафик между ними. Конечно, у вас такой коробочки нет, но, запустив программу анализатора, вы сможете посмотреть уже записанные примеры для устройств разных классов и вы сможете посмотреть какие пакеты там ходят.
Добавлю, что у меня есть этот анализатор, что делает разработку USB устройств гораздо проще и удобнее.
Кроме DeviceDescriptor еще очень нужный описатель - это ConfigurationDescriptor. Он описывает как операционная система должна обращаться с устройством, какие у устройства есть Endpoints - каналы передачи данных, их тип и так далее. Вот эти дескрипторы особенно сложны для понимания Особенно для класса CDC (Communication Device Class) - так я по правде мало что понимаю здесь. Ну вот привожу его как есть (я посмотрел множество разных примеров реализации CDC устройств и сделал свое подобное):
UCHAR usb_configuration_descriptor[] = {
/*Configuation Descriptor*/
0x09, /* bLength: Configuation Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
VIRTUAL_COM_PORT_SIZE_CONFIG_DESC, /* wTotalLength:no of returned bytes */
0x00,
0x02, /* bNumInterfaces: 2 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*Interface Descriptor*/
0x09, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Managment Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x03, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x0f, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x82, /* bEndpointAddress: (IN2) */
0x03, /* bmAttributes: Interrupt */
VIRTUAL_COM_PORT_INT_SIZE, /* wMaxPacketSize: */
0x00,
0x20, /* bInterval: */
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
0x01, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x03, /* bEndpointAddress: (OUT3)*/
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x83, /* bEndpointAddress: (IN3)*/
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00 /* bInterval: ignore for Bulk transfer */
};
Этот описатель ОС читает после назначения устройству USB адреса. Конечно, эта структура тоже будет вписана специальным образом во флеш чипа CPLD.
Я написал программу на зыке С (Microsoft Visual Studio) которая формирует из этих структур те, что мне нужны в *.mif файле. Программа есть в архиве. Она разбивает длинные дескрипторы на пакеты USB, считает контрольные суммы пакетов и т.д. В общем остается только взять и вставить в *.mif файл.
Проект для платы Марсоход, INF файлы для установки драйверов, программу для формирования mif файла из дескрипторов можно взять здесь.
Сам проект я сделал довольно быстро. У меня уже был проект, который я взял за основу. Я только поменял модуль ls_usb_core.v (ведь теперь должен обрабатывать целых 3 Endpoints и посылать данные в формате Scratch) и поменял mif файл, где теперь хранятся новые описатели USB пакетов.
Сказать по правде больше всего времени, почти неделю, я потерял на... драйвера.
Вообще-то сколько я примеров смотрел (как правило они реализованы на микроконтроллерах AVR), обычно люди говорят, что с драйверами никаких проблем нет, так как используются родные драйвера Windows. Для ОС Windows нужно только подсунуть некий *.INF файл описывающий CDC устройство, и в нем должна быть прописаны строки типа
[Devices]
%DESCRIPTION% = DriverInstall, USB\VID_09FB&PID_60A6
Здесь VID_09FB - это наш id производителя, а PID_60A6 - это id устройства.
Так же в INF файле есть ссылка на драйвер Microsoft USBSER.SYS - именно он поддерживает Communication класс:
[DriverInstall.nt.CopyFiles]
usbser.sys,,,0x20
Вот я взял, сделал INF файл, подсунул своему Windows XP SP2 и - заработало! Уррра!
К сожалению я рано радовался. Всякие мои попытки запустить мое устройство на Windows 7 32bit оказывались безуспешними. Windows 7 мне говорил, что устройство не может быть запущено (ошибка 10). Уж чего я только не пробовал - не помогает и все. Однако, я нашел японский (!) сайт, где написано как бороться с этой проблемой. Там Osamu Tamura предлагает свой драйвер "фильтра", который и решает проблему. Вот я воспользовался этим драйвером и теперь работает и под Windows 7! Конечно, его INF файл я исправил, поставив мои idVendor и idProduct.
Вот теперь можно считать работу почти законченной. Поэтому я и выкладываю этот проект на всеобщее обозрение:
Что касается проблемы драйверов - я думаю проблема в том, что мое устройство низко-скоростное и по каким-то причинам Windows 7 считает, что оно по этому не может быть класса Communication. Эта версия нуждается в проверке. Может быть новую версию устройства я позже сделаю high-speed, посмотрим..
Подробнее...