В этом примере будет происходить управление светодиодом на макетной плате AVR-USB-MEGA16 из ПО хоста, работающего на Android. В память микроконтроллера макетной платы ATMega32A прошит код устройства USB HID, которое принимает простейшие команды от хоста и в соответствии с командами зажигает или гасит светодиод, и передает его состояние.
[Firmware устройства USB HID]
Firmware для микроконтроллера ATmega32A взято готовое, из примеров библиотеки V-USB (исходный код проекта и бинарники для него можно скачать в пакете [3], см. папку avrusb-20080513\examples\hid-custom-rq). Это firmware можно скомпилировать и для любого другого микроконтроллера AVR, например для макетной платы metaboard, AVR-USB-TINY45.
USB HID, который работает в firmware микроконтроллера, поддерживает только одну конечную точку - control endpoint, её также называют конечной точкой по умолчанию. Эта конечная точка имеет номер 0, и она обязательно должна быть во всех USB-устройствах, потому что через неё происходит в основном обмен служебными данными. В нашем примере она используется также для передачи полезных данных - состояния светодиода, и команд для его переключения. Для передачи этих данных в наше устройство USB HID используются короткие управляющие передачи, что поддерживает иерархия Java классов.
Кроме консольных версий ПО хоста, работающих про Windows, Linux, FreeBSD, для этого firmware также есть и GUI-версия под Windows [4]. И вот теперь настала очередь платформы Android.
[Реализация USB host в Android]
Поддержка устройств USB (реализация хоста USB) появилась в Android начиная с версии Android 3.1.x (API Level 12 или выше). Более старые системы не поддерживают режим хоста USB, т. е. они принципиально неспособны опознавать USB-устройства и работать с ними. Кроме того, аппаратура в Android должна поддерживать стандарт OTG (в настоящее время такая поддержка есть почти во всех современных телефонах и планшетах).
Для подключения к Android, у которых нет на корпусе коннектора Type A Female (это обычное дело, если у Вас не планшет, а телефон или смартфон Android), потребуется переходник Android OTG micro usb cable [2]. Такие переходники почти задаром раздают на dealextreme, aliexpress, ebay, и его также можно купить на рынке или в любом магазине, торгующим мобильными телефонами. На фото показана макетная плата AVR-USB-MEGA16, подключенная к смартфону Samsung Galaxy Note GT-N7000 через такой переходник.
Для управления устройством USB в Android всегда используются классы UsbManager, UsbDevice, UsbInterface, UsbEndpoint, UsbDeviceConnection, которые хорошо документированы и просты в применении. Получается следующая иерархия использования классов:
UsbManager -> UsbDevice -> UsbInterface -> UsbEndpoint -> UsbDeviceConnection
Т. е. сначала запускается UsbManager, с помощью которого получают доступ к определенному нужному устройству на шине USB (устройство обычно выбирается по его описанию, например по параметрам VID и PID). Найденное нужное устройство будет представлено классом UsbDevice. Далее происходит опрос UsbDevice на наличие в нем нужного интерфейса, и запускается экземпляр класса UsbInterface. Далее UsbInterface опрашивается на наличие в нем нужных конечных точек, и в результате появляется один или несколько экземпляров класса UsbEndpoint (в нашем примере конечная точка одна-единственная, control endpoint 0). Далее на нужной конечной точке запускается соединение, и получается экземпляр класса UsbDeviceConnection. Класс UsbDeviceConnection имеет методы для реализации обмена данными с устройством USB - методы bulkTransfer и controlTransfer (в нашем примере для передачи данных в обе стороны используется только метод controlTransfer).
Примечание: использование процедуры controlTransfer удивительным образом совпадает с синтаксисом вызовов кроссплатформенной библиотеки LibUSB. Именно благодаря этому факту удалось быстро разобраться с программированием на Android устройств USB - был взят готовый код ПО хоста примера из библиотеки V-USB (см. [3], файл avrusb-20080513\examples\hid-custom-rq\commandline\set-led.c), и константы из LibUSB, и вызовы функций LibUSB были портированы в вызовы API Android. Сравните:
//Так запрашивается статус устройства USB с помощью библиотеки LibUSB
// (код работает в ПО хоста под управлением Windows, Linux, FreeBSD или MAC OS).
usb_control_msg(handle,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
CUSTOM_RQ_GET_STATUS,
0,
0,
buffer,
sizeof(buffer),
5000);
//А так запрашивается статус устройства USB с помощью API Android
// (код работает в ПО хоста под управлением Android).
connection.controlTransfer(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
CUSTOM_RQ_GET_STATUS,
0,
0,
buffer,
buffer.length,
5000);
Согласитесь, разница минимальная, что очень радует, поскольку это позволяет довольно просто портировать на Android программное обеспечение USB с других платформ.
Помимо API для обмена данными с устройствами USB в системе Android для получения доступа к устройству USB применяется специальная система разрешений, т. е. без получения явного разрешения от пользователя подключиться к устройству USB для передачи данных нельзя.
Все эти вопросы будут кратко рассмотрены далее в реализации примера программы Android.
[Пошаговая реализация примера программы Android, управляющая устройством USB HID]
Рассмотрим на конкретном простом примере создание приложения Android, которое будет обмениваться данными с простейшим устройством USB HID на макетной плате AVR-USB-MEGA16. Программы были написаны в среде Eclipse, и испытывались на смартфоне Samsung Galaxy Note GT-N7000, версия Android 4.1.2 (API Level 16). Готовый проект Eclipse можете скачать в архиве по ссылке [3] (см. также файл readme.txt архива).
1. Создайте в Eclipse проект приложения Android при помощи стандартного мастера. На этом шаге выберите минимальную версию целевого SDK API 12. Target SDK и Compile With можете выставить по максимуму, на самую последнюю поддерживаемую версию Android. Так же задайте, как обычно, имя приложения, имя проекта и имя пакета.
2. Бросьте на форму приложения чекбокс, который будет отображать состояние светодиода на макетной плате, и одновременно будет управлять им (погашен светодиод или горит). Сделайте для чекбокса обработчик клика, в обработчике мы будем переключать светодиод на макетной плате.
3. Создайте файл usb_device_filter.xml, и положите его в папку res/xml проекта. Этот файл предоставляет параметры описания устройства USB HID (фильтр по VID и PID), и он используется для получения разрешения на подключение к устройству. Благодаря использованию этого фильтра наша программа будет реагировать только на подключение нужного устройства USB, все остальные устройства будут игнорироваться. Вот содержимое файла usb_device_filter.xml:
< ?xml version="1.0" encoding="utf-8"? >
< resources >
< usb-device vendor-id="5824" product-id="1503" />
< /resources >
Атрибуты vendor-id и product-id задают параметры VID и PID, которые используются в устройстве USB HID макетной платы AVR-USB-MEGA16.
Примечание: обратите внимание, что значения VID и PID указаны здесь не в шестнадцатеричном, а в десятичном виде. Указанные здесь значения VID и PID должны совпадать со значениями, используемыми в проекте firmware нашего устройства USB HID на макетной плате AVR-USB-MEGA16 (см. файл avrusb-20080513\examples\hid-custom-rq\firmware-mega\usbconfig.h, параметры USB_CFG_VENDOR_ID и USB_CFG_DEVICE_ID. Значения VID 0x16C0 и PID 05DF переведены в десятичные значения 5824 и 1503 соответственно).
3. Добавьте в AndroidManifest.xml секцию < uses-feature android:name="android.hardware.usb.host" />, эта секция задает использование в программе возможностей хоста USB.
Добавьте также секции intent-filter и meta-data, отвечающие за определение события подключения устройства USB и получение разрешения на его использование от пользователя. В результате должен получиться примерно такой файл AndroidManifest.xml:
< ?xml version="1.0" encoding="utf-8"? >
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.setled"
android:versionCode="1"
android:versionName="1.0" >
< uses-feature android:name="android.hardware.usb.host" />
< uses-sdk
android:minSdkVersion="12"
android:targetSdkVersion="19" />
< application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
< activity
android:name="com.setled.MainActivity"
android:label="@string/app_name" >
< intent-filter >
< action android:name="android.intent.action.MAIN" />
< category android:name="android.intent.category.LAUNCHER" />
< intent-filter >
< action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
< /intent-filter >
< meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device_filter" />
< /activity >
< /application >
< /manifest >
4. Добавьте в класс MainActivity глобальные определения констант и глобальные переменные классов UsbManager, UsbDevice, UsbInterface, UsbEndpoint. Константы и экземпляры этих классов будут использоваться для доступа к устройству USB HID.
public class MainActivity extends Activity
{
private static final int USB_TYPE_VENDOR = (0x02 << 5);
private static final int USB_RECIP_DEVICE = 0x00;
private static final int USB_ENDPOINT_IN = 0x80;
private static final int USB_ENDPOINT_OUT = 0x00;
/* Этот запрос устанавливает состояние светодиода LED макетной платы
* AVR-USB-MEGA16. Используется управляющая передача вывода (Control-OUT).
* Запрашиваемый статус передается в поле "wValue" управляющей передачи
* (control transfer). Данные OUT не передются. Бит 0 в младшем байте
* wValue управляет состоянием светодиода.
*/
private static final int CUSTOM_RQ_SET_STATUS = 1;
/* Получает текущее состояние светодиода макетной платы AVR-USB-MEGA16.
* Используется управляющая передача ввода (Control-IN).
* Управляющая передача (control transfer) в 1 байте фазы данных бит 0
* передает хосту текущее состояние светодиода.
*/
private static final int CUSTOM_RQ_GET_STATUS = 2;
CheckBox cb; //чекбокс для состояния светодиода
UsbManager usbmanager;
UsbDevice usbdev;
UsbInterface usbif;
UsbEndpoint usbep;
...
Примечание: значения констант были скопированы из файла usb.h библиотеки LibUSB и из файла requests.h avrusb-20080513\examples\hid-custom-rq\firmware-mega\requests.h, см. [3]. Константы используются в вызовах controlTransfer при обмене данными с устройством USB. 5. Добавьте функцию usbOpenDevice, которая будет выполнять основную работу по подключению к нужному устройству.
private boolean usbOpenDevice ()
{
boolean opened = false;
try
{
usbmanager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap < String, UsbDevice > deviceList = usbmanager.getDeviceList();
Iterator< UsbDevice > deviceIterator = deviceList.values().iterator();
//Цикл по обнаруженным устройствам.
while(deviceIterator.hasNext())
{
usbdev = deviceIterator.next();
if (usbmanager.hasPermission(usbdev))
{
//Вывод информации об устройстве.
clog(usbdev.toString());
//Цикл по интерфейсам устройства.
for(int ifidx=0; ifidx<usbdev.getInterfaceCount(); ifidx++)
{
usbif = usbdev.getInterface(ifidx);
clog(usbif.toString());
//Цикл по конечным точкам интерфейса.
for(int epidx=0; epidx<usbif.getEndpointCount(); epidx++)
{
usbep = usbif.getEndpoint(epidx);
clog(usbep.toString());
clog("");
opened = true;
}
}
}
else
{
clog("Доступ к устройству USB запрещен.");
}
}
}
catch (Exception e)
{
clog("Failed to parse devices.xml: " + e.getMessage());
clog("Устройство USB HID не найдено.");
}
return opened;
}
Примечания к коду usbOpenDevice: функция работает с глобальными переменными usbmanager, usbdev, usbif, usbep. Если удалось открыть устройство USB, то возвращает true, и переменные находятся в рабочем состоянии, готовые к работе. Иначе будет возвращено false. Для опроса устройств, интерфейсов и конечных точек используются циклы, которые строго говоря применять необязательно, так как у нас всегда только одно устройство, и мы заранее точно знаем его параметры. Процедура clog выводит информационные сообщения в текстовую консоль, и её использование необязательно. Метод hasPermission класса UsbManager определяет, получено ли от пользователя разрешение на подключение к устройству или нет. Окно на запрос разрешения выводится примерно в таком виде:
6. Добавьте функцию getledstate, которая будет запрашивать от макетной платы AVR-USB-MEGA16 состояние светодиода.
private boolean getledstate()
{
boolean ledstate = false;
byte buffer[] = new byte[4];
if (usbOpenDevice())
{
UsbDeviceConnection connection = usbmanager.openDevice(usbdev);
connection.claimInterface(usbif, true);
connection.controlTransfer(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
CUSTOM_RQ_GET_STATUS,
0,
0,
buffer,
buffer.length,
5000);
ledstate = (buffer[0]!=0)?true:false;
}
return ledstate;
}
Перед непосредственным вызовом обмена данными запускается функция usbOpenDevice, которая устанавливает соединение с нужным устройством USB. Состояние светодиода возвращается в младшем байте буфера - если бит 0 в этом байте равен 1, то это означает горение светодиода, а если 0, то светодиод погашен.
7. Добавьте функцию setled, которая будет включать и выключать светодиод на макетной плате AVR-USB-MEGA16, и добавьте вызов этой функции в обработчик клика на чекбоксе:
private void setled(int value)
{
if (usbOpenDevice())
{
UsbDeviceConnection connection = usbmanager.openDevice(usbdev);
connection.claimInterface(usbif, true);
connection.controlTransfer(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT,
CUSTOM_RQ_SET_STATUS,
value,
0,
null,
0,
5000);
}
}
public void cbClick(View v)
{
int ledstate = cb.isChecked()?1:0;
setled(ledstate);
cb.setChecked(getledstate());
}
Ниже показан интерфейс программы Android и соответствующее состояние светодиода на макетной плате.
Подобное совместное использование Android и самодельного простейшего USB устройства открывает перед радиолюбителем широкие перспективы. Можно самому создать малогабаритное устройство с автономным питанием, которое снабжено удобным дисплеем и интерфейсом управления. К примеру, можно сделать переносной программатор, пульт управления освещением, устройство измерения или генерации сигналов, и многое другое.
[Ссылки]
1. Android как хост USB. 2. Как сделать для смартфона Android кабель microUSB OTG. 3. 140419android-USB-devices-access.zip - проекты Eclipse с примерами управления макетной платой AVR-USB-MEGA16 из Android, проект устройства USB HID с исходными кодами и готовыми прошивками. 4. Как использовать библиотеку libusb в Visual Studio (управление макетной платой AVR-USB-MEGA16). |
Комментарии
http://www.martyncurrey.com/category/app-inventor/
https://forum.lazarus.freepascal.org/index.php?topic=38412.0
https://forum.lazarus.freepascal.org/index.php/topic,38351.0.html
microsin: да, эта прошивка может управлять только ножкой порта светодиода. Так сделано специально для упрощения в целях демонстрации. Показан принцип передачи данных, чтобы можно было легко переделать код под любые цели, в том числе и для управления всеми выводами микроконтроллер а. Можно сделать даже чтение-запись регистров AVR, получив полное управление над его аппаратурой. Подобным образом устроен код прошивки Сергея Кухтецкого и соответствующая библиотека C#.
RSS лента комментариев этой записи