Управление микроконтроллером ATmega32 через интерфейс USB

 

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

    Также среда разработки LabVIEW очень удобна для радиоинженеров и научных работников, потому что имеет в своем составе множество готовых инструментов (для создания ПИД-регуляторов, обработки звука, изображений и множества других приложений), да и в целом работа в LabVIEW для людей, не посвящённых в тонкости программирования, намного проще, чем в том же Delphi или Visual Studio.

Изучая управление микроконтроллерами по шине USB на основе библиотек V-USB и LibUSB, у меня возник вопрос – как управлять разрабатываемым USB-устройством из LabVIEW, и возможно ли это вообще?

    Выяснилось, что в LabVIEW можно просмотреть все доступные функции libusb0.dll (основная DLL библиотеки LibUSB). Чтобы начать использовать функции библиотеки libusb, нужно научиться работать с типом данных handle в LabVIEW. Довольно простое решение нашлось на одном англоязычном форуме – оно заключалось в преобразовании handle в числовой тип, см. [1]. Читая статью [2] об управлении макетной платой из программы на Delphi, я подумал, что наверно можно сделать такую dll-ку в Delphi, которую потом использовать в LabVIEW для управления устройством USB. Эта DLL (назовем её V-USB-LabVIEW.dll) будет обращаться к libusb0.dll и управлять микроконтроллером ATmega32 по протоколу USB. Все манипуляции с handl-ами, PID, VID, адресами, байтами и т. д. будут происходить внутри V-USB-LabVIEW.dll. Получается следующая схема взаимодействия:

 

 

   За основу для создания V-USB-LabVIEW.dll взял почти без изменений пример программы useport от Vanoid-а [2], и готовую прошивку для контроллера от Сергея Кухтецкого [3]. Скомпилированную DLL, её исходный код и пример программы LabVIEW можно скачать по ссылке [4]. Теперь коротко о содержимом исходного кода DLL. Работу программ испытывал на макетной плате AVR-USB-MEGA16, на которой установлен микроконтроллер ATmega32A (в память микроконтроллера через USB-бутлоадер была залита прошивка Сергея Кухтецкого).

   В начале кода V-USB-LabVIEW.dll объявлены используемые константы, функции и процедуры из проекта useport: usbGetStringAscii, usbOpenDevice, SendUSBControlMessage. Они нужны, чтобы обратиться к нужному устройству USB. В процедуре SendUSBControlMessage объявлены также  PID и VID устройства USB в виде строковых констант. Я добавил функцию PortX_Write, которая будет вызываться в LabVIEW. На примере обращения к порту PB0, к которому подключен светодиод на макетной плате AVR-USB-MEGA16, эта дополнительная функция в проекте DLL на Delphi выглядит так:

 

function PortX_Write (Address:integer;on_off:integer): integer; stdcall;
var data: array [0..2] of char; //AVR отправляет обратно max 3 байта
bit: 
byte;
begin
 
//Порт PB0 со светодиодом
 
if Address = 0 then
    begin
     
//Читаем состояние ножек порта В
     SendUSBControlMessage(USB2PC,RQ_IO_READ, 0, aPORTB, 0, data);
     bit := bit0;
     
if on_off = 1 then
         data[0]:=
char(byte(data[0]) or bit)
     
else
         data[0]:=
char(byte(data[0]) and ($FF-bit));
     SendUSBControlMessage(PC2USB, RQ_IO_WRITE,
                                    
byte(data[0]), aPORTB, 0, data);
    end;
    result := 1;
end;



// Далее идет экспорт функций dll для последующего
// использования в LabVIEW
exports PortX_Write;

  Функция PortX_Write имеет два аргумента: первый Address – определяет порт, которым нужно управлять. Светодиод на макетной плате подключен к порту PB0, и для него используется адрес 0. Для всех остальных портов P1..P22 макетной платы (которые подключены к разрядам портов A, B, C, D микроконтроллера ATmega32) используются значения от 1 до 22 соответствуют портам макетки P1..P22). Второй аргумент on_off – состояние, в которое порт нужно перевести. Например, чтобы подать питание на светодиод (зажечь его), в параметре on_off  нужно передать 1, и чтобы погасить светодиод, надо передать 0. В параметре Address соответственно должен быть 0 (адрес порта светодиода).

  V-USB-LabVIEW.dll можно использовать в среде LabVIEW. Далее приведена последовательность создания программы в LabVIEW 2010 с пояснительными скриншотами. Сначала нужно создать новый виртуальный прибор (VI):

 

Далее поместите на форму компонент Numeric Control, которым будем выбирать номер порта (поле ввода адреса Port со стрелочками больше/меньше), и тумблер Vertical Toggle Switch, которым будем управлять состоянием порта (тумблер вверх – порт выдает лог. 1, тумблер вниз – порт выдает лог. 0):
 
 


    Перейдите в меню блок-диаграммы:


  Здесь уже появились наши элементы Numeric (поле ввода адреса) и Boolean (положение тумблера):

 

 

   Разместите узел для вызова функций dll. Для этого сделайте правый клик в пустом месте окна блок диаграммы => Connectivity=>Libraries & Executables => Call Library Function Node:

 

   Двойным кликом по нему вызовите окно настроек вызова функций из dll. Сначала укажите путь к dll (таким же образом можно просмотреть все доступные функции libusb0.dll), выберите нужную нам функцию PortX_Write, параметр Calling convention установите в stdcall (WINAPI):

 

 

    В этом же окне перейдите на вкладку Parameters:

 

 
    Добавьте аргументы, которые нужно будет передать нашей функции. Как помните у нас их два – адрес для порта и значение его состояния. Нажмите дважды на плюсик. Тип аргументов и тип данных для аргументов оставьте по умолчанию, как на скриншоте. Когда всё готово, нажмите ОК.
     Блок для связи с dll сразу преобразился, у него появились 2 входа и два выхода. Нам нужны только входы, первый сверху отвечает за номер порта, второй за его состояние:
    Теперь нужно сделать так, чтобы при верхнем положении тумблера у нас была возможность отправить значение 1 в dll для аргумента состояния порта, а при нижнем положении 0. Для этого воспользуемся структурой Case. Сделайте правый клик мыши в пустом месте окна блок-диаграммы, и выберите Structures => Case Structure. Соедините проводом выход тумблера со входом Case. При выбранном положении True у Case поместите в него константу 1, при False соответственно 0:
 
    Соедините выход компонента Numeric с первым сверху входом узла dll (так будет передаваться адрес порта). Второй вход dll подключите к нашим константам:
     Теперь сделаем так, чтобы управление макеткой происходило только тогда, когда тумблер был переключен. Используем структуру, которая «отлавливает» это событие. Правый клик мыши в пустом месте окна блок-диаграммы => Programming => Structures => Event Structure:
    «Оборачиваем» все наши блоки примерно таким образом:
    Правый клик там, где написано Timeout, выбираем пункт меню Edit Events Handled by This Case…:
    В открывшемся окне выберите наш тумблер Boolean, и выберите для него событие Value Change:
    Заголовок сразу сменится на выбранное событие
 

    Теперь, чтобы программа работала в зацикленном режиме, нужно ещё раз «обернуть» все наши блоки, но уже структурой While Loop:

    Примерно так:
   Всё, у нас получился виртуальный прибор (VI, по терминологии LabVIEW), который может управлять портами микроконтроллера ATmega32. Теперь, запустив программу VI и выбрав нужный порт, тумблером можно изменять состояние любого порта. Протестируйте на порте №0, светодиод на макетке будет включаться/выключаться. Макетная плата конечно при этом должена быть подключёна, иначе программа выдаст ошибку. Пример программы здесь максимально простой – для наглядности. В реальный рабочий код стоит ещё добавить обработчик ошибок (например, для ситуации, когда макетная плата AVR-USB-MEGA16 отключена), но здесь для упрощения это не рассматривается.
 
   Из виртуального прибора *.VI можно скомпилировать обычный EXE-файл, (см. [6]), готовый EXE имеется в составе общего архива по ссылке [4]. Этот исполняемый файл можно запустить на выполнение самостоятельно, не запуская систему LabVIEW, однако для его работы необходима установка LabVIEW Run-Time Engine 2010.
Кстати, это не единственный вариант USB-обмена с макеткой AVR-USB-MEGA16 в среде LabVIEW. Можно также записать в память чипа ATmega32A программу виртуального последовательного порта (класс USB CDC, virtual COM-port, см. [5]), с которым организовать обмен данными в среде LabVIEW довольно просто.

   В будущем планирую добавить работу с АЦП и ШИМ, т. е. сделать более-менее универсальную dll для LabVIEW, которая позволит использовать все возможности очень удобной прошивки С. Кухтецкого [3]. Надеюсь, пользователям LabVIEW статья поможет с организацией подключения к системе собственных USB-устройств.
Все программы разрабатывались и тестировались в WinXP, Delphi 7, LabVIEW 2010.

Эксперименты с VISA

   Разработчик LabVIEW, компания National Instruments для коммуникаций с «железом» предлагает специальную архитектуру VISA, см. [7]. VISA является стандартным программным интерфейсом приложения (API), осуществляющего ввод/вывод, и применяется для управления контрольно-измерительным оборудованием. VISA может управлять многими типами приборов (через интерфейсы VXI, GPIB, PXI, USB или COM-порты), вызывая соответствующие драйверы, которые зависят от типа используемого прибора.

   Я пробовал применить VISA для управления портами микроконтроллера ATmega32A, установленного на макетной млате AVR-USB-MEGA16, однако пока не смог сделать firmware для микроконтроллера, которое в полной мере поддерживало бы стандарт VISA. Подсистема VISA после установки своих драйверов видит контроллер и даже получает с него информацию. Рассмотрим подробнее.

   Если хотите попробовать обратиться к контроллеру через VISA, то необходимо, прежде всего, установить саму подсистему VISA. Если вы уже работали с AVR-USB-MEGA16 через libusb, то нужно в Диспетчере Устройств удалить имеющееся устройство USB. После экспериментов всё легко возвращается в исходное состояние. У меня устройство USB макетной платы AVR-USB-MEGA16 опознавалось как устройство USBasp, находящееся в ветке lib-usb-win32 devices:
 
    Теперь вызовите VISA Driver Wizard:

   Укажите интерфейс USB:

   Выбирите наш контроллер по PID и VID. Напомню, что USB-устройство на прошивке Сергея Кухтецкого имеет VID 0x16C0 и PID 0x05DC (это бесплатные VID и PID, которые предоставляет библиотека V-USB в свободное использование):
  
    В остальных окнах с настройками я нажимал Next без изменения параметров. После окончания установки, в Диспетчере Устройств операционной системы Windows появится контроллер, но уже в ветке NI VISA:
    Когда всё готово, можно запустить VISA Interactive Control:
   Выберите наш контроллер двойным кликом:

 
   Войдите в меню Input/Output и выберите вкладку USB Control, здесь можно попытаться управлять контроллером. Как видите, bmRequestType имеет верное, согласно прошивке, значение 0x80. Отправка команд по нажатию кнопки Execute показана ниже.
 
  
   Очевидно, что для обмена по стандартному протоколу VISA нужно переписать firmware микроконтроллера таким образом, чтобы оно поддерживало этот протокол. Пока мне сделать это не удалось. Если кто-нибудь сможет реализовать протокол VISA, пожалуйста, напишите об этом в комментариях на форуме.
 
[Видео на YouTube]
Ссылка: http://youtu.be/3x9E_U9ZA-0

[Словарик]

V-USB популярная библиотека (с открытым исходным кодом) для микроконтроллеров AVR, которая позволяет организовать обмен данными по шине USB для микроконтроллеров, которые не имеют встроенного контроллера USB. Эта библиотека используется в firmware Сергея Кухтецкого для микроконтроллера ATmega32, см. [3].

LibUSB другая популярная библиотека (с открытым исходным кодом, кроссплатформенная), которая упрощает написание ПО хоста на компьютере. Часто используется для управления устройствами USB.

LabVIEW (Laboratory Virtual Instrument Engineering Workbench - среда разработки лабораторных виртуальных приборов) представляет собой многоплатформенную (Windows, MacOS, Linux, Solaris, HP-UX) среду графического программирования, которая широко используется в промышленности, образовании и научно-исследовательских лабораториях в качестве стандартного инструмента для сбора данных и управления приборами.

VISA стандартный программный интерфейс приложений (API), осуществляющий ввод/вывод в системе LabVIEW. Используется для программирования контрольно-измерительного оборудования, которое используется совместно с LabVIEW. VISA может управлять многими типами приборов (через интерфейсы VXI, GPIB, PXI, USB или COM-порты), вызывая соответствующие драйверы, которые зависят от типа используемого прибора.

LabVIEW Run-Time Engine бесплатный компонент, позволяющий запускать программы, созданные в среде LabVIEW, не устанавливая саму систему LabVIEW.

VI Virtual Instrument – виртуальный прибор. В терминологии LabVIEW это означает просто программу, которая выполняет какие-то функции.

[Ссылки]

1.    LibUSB - passing device handle as integer - http://libusb.6.n5.nabble.com/Passing-device-handle-as-integer-td3218678.html
2.    Работа с USB для чайников на Delphi http://www.vanoid.ru/avr/.
3.    AVR-USB-MEGA16: быстрая разработка USB приложений на C# при помощи класса-обертки (автор Сергей Кухтецкий) http://microsin.ru/content/view/812/44/
4.    V-USB-LabVIEW.dll, её исходный код, пример программы LabVIEW и другая документация http://depositfiles.com/files/8wzqaabgf .
5.    USB консоль для управления радиолюбительскими приборами http://microsin.ru/content/view/1178/44/ .
6.    Создание исполняемого exe файла из проекта LabVIEW http://zone.ni.com/reference/en-XX/help/371361G-01/lvhowto/building_a_stand_alone_app/
7.    NI VISA http://sine.ni.com/psp/app/doc/p/id/psp-411/lang/en .

Автор Никишин В.