Почему не работает библиотека V-USB с микроконтроллером AVR? Печать
Добавил(а) microsin   

Перевод статьи про причины проблем с использованием библиотеки V-USB [1] (ошибки наподобие "Устройство USB не обнаружено").

Прим. переводчика: V-USB это проверенная временем библиотека, позволяющая делать устройства USB (обычно классов HID и CDC) на самых обычных микроконтроллерах AVR, которые совсем для этого не предназначены. Т. е. микроконтроллеры AVR не имеют на борту аппаратного интерфейса USB, но библиотека V-USB все равно позволяет сделать из этих AVR низкоскоростные устройства USB. Протокол USB обрабатывается в библиотеке чисто программно (виртуозно написанным кодом на языке ассемблера), и физические сигналы интерфейса USB подключены к обычным портам ввода/вывода GPIO микроконтроллера AVR (используется одно аппаратное прерывание по уровню, обычно это INT0). Подробнее см. сайт авторов библиотеки V-USB [2] и Википедию [3].

Особенно обратите внимание на соответствие фьюзов реальной тактовой частоте, на которой работает микроконтроллер. AVR по умолчанию (в том состоянии, в котором Вы получаете микроконтроллер от продавца) настроен фьюзами на использование внутреннего тактового RC-генератора (такой генератор можно использовать только в отдельных случаях, когда код настроен на 12.8 или 16.5 МГц, и используется подстройка частоты по импульсам SOF).

Кроме того, по умолчанию включен внутренний делитель фьюзом CKDIV8 (не забудьте назначить значение фьюза CKDIV8 в состояние 1, что означает отключение делителя).

Прим. переводчика: чтобы не запутаться с фьюзами, старайтесь использовать их шестнадцатеричные значения, вычисленные с помощью калькулятора фьюзов [4]. Рекомендуемые шестнадцатеричные значения фьюзов часто указаны в Makefile, в комментариях к коду и в файлах readme.txt проекта. Если Вы используете макетные платы AVR-USB-MEGA16, AVR-USB-TINY45, metaboard, на которых уже установлен запрограммированный микроконтроллер, то о фьюзах заботиться не нужно, поскольку они уже установлены в корректное значение.

Чаще всего микроконтроллер AVR, в котором работает V-USB, тактируется с использованием внешнего кварцевого резонатора (протокол USB требует очень жесткого соблюдения интервалов времени сигналов D+ и D- шины USB). V-USB позволяет использовать определенный ряд тактовых частот (подробнее см. [2, 3]). Ваша задача - обеспечить точное соответствие настроек проекта (макроопределения F_CPU) реальной тактовой частоте микроконтроллера.

Прим. переводчика: для макетной платы AVR-USB-MEGA16 обычно используется кварц на 12 МГц, для metaboard на 16 МГц, а AVR-USB-TINY45 работает вообще без кварцевого резонатора (тактовая частота 16.5 МГц вырабатывается с использованием внутренней системы PLL микроконтроллера и программной подстройки частоты по импульсам SOF). Если Вы используете эти платы, то Вам остается только проверить тактовую частоту, заданную в проекте, на соответствие реально используемой. Обычно тактовая частота в проекте задается через файл Makefile и передается как макропеременная компилятору с помощью опции -D командной строки avr-gcc.

Если Вы зададите тактовую частоту через макропеременную F-CPU из разрешенного ряда частот (обычно это частоты 12 МГц, 16 МГц, хотя могут быть и частоты 15, 16.5, 20 МГц), то компилятор не предупредит Вас о некорректно установленной тактовой частоте. Однако будьте внимательны, и проследите, чтобы в Makefile (или через опции проекта AVR Studio или другой используемой Вами среды программирования) была корректно установлена макропеременная F_CPU на ту частоту, которая реально используется микроконтроллером. Пример установки F_CPU в Makefile проекта:

F_CPU   = 16000000      # тактовая частота указывается в Герцах

Вы можете определить тактовую частоту кварцевого резонатора по надписи на нем, см. также [6].

ATmega16-16MHz ATmega32A-12MHz

Электромагнитные помехи могут создать для V-USB большую проблему, если Вы об этом специально не позаботились. Проверьте, установлены ли развязывающие конденсаторы по питанию (обычно номиналом 0.1 мкФ или 0.22 мкФ) максимально близко к выводам микроконтроллера AVR. Может быть также проблема с экранированием кабеля USB. Попробуйте соединить внешнюю экранирующую оплетку кабеля USB (экран) с шиной земли GND макетной платы или Вашего устройства на микроконтроллере AVR.

AVR особенно чувствителен к пиковым выбросам помех на портах GPIO, амплитуда которых превышает напряжение питания. Длинные проводники, которые подключены к входным портам GPIO (настроенным на работу как входы) микроконтроллера, как раз могут быть источниками таких помех. Хорошие кандидаты на такие провода - сигналы данных D+ и D- шины USB. Если у Вас проблема с помехами и стабильностью в работе V-USB, то Вы можете попробовать отфильтровать высокочастотные помехи на этих сигналах путем параллельного подключения двух конденсаторов емкостью около 100 пф на шину GND (см. схему ниже). Конденсаторы вместе с сопротивлением проводов шнура USB образуют простейший ФНЧ первого порядка, гасящий высокочастотные помехи.

V-USB-EMC-block-100pF

Однако будьте осторожны: если емкость конденсаторов окажется слишком большой, и/или сопротивление проводов шнура USB будет слишком высоким, то это может исказить сигналы USB. Если емкость конденсаторов слишком мала, то они не будут давать никакого эффекта.

Дважды проверьте Вашу конструкцию, чтобы она точно соответствовала принципиальной схеме организации шины USB (рекомендуемые примеры схем подключения интерфейса USB к микроконтроллеру AVR есть в составе библиотеки V-USB, библиотеку можно скачать с сайта [2]). К примеру, еще раз проверьте полярность диодов и стабилитронов, и есть ли установленный pull-up резистор на ножке RESET микроконтроллера. Иногда проблему может создать внешний ISP-программатор, подключенный к плате, который выдает слишком низкий уровень сигнала RESET. Также убедитесь в наличии и корректном номинале pull-up резистора 1.5 кОм, подключенного к сигналу D- шины USB.

Ниже приведены несколько рекомендованных схем организации физического интерфейса USB для библиотеки V-USB на основе микроконтроллеров ATtiny45 (ATtiny85) и ATmega8 (ATmega168, ATmega328P) в корпусах DIP.

V-USB-tiny45-rc V-USB-mega8-with-series-diodes V-USB-mega8-with-vreg V-USB-mega8-with-zener

Если микроконтроллер работает от напряжения 5V, то используйте маломощные диоды Зенера (стабилитроны) на напряжение 3.6V. Вместе с pull-up резистором на 1.5 кОм вполне можно использовать стабилитроны 3.6V, рассчитанные на мощность рассеивания 0.5 Вт. Стабилитроны, рассчитанные на повышенную мощность рассеивания, требуют дополнительной проверки на работоспособность, и возможно отдельного подбора номинала pull-up резистора 1.5 кОм на сигнале D-.

Если Ваш проект не работает, то попробуйте найти ошибку, откатившись к заведомо рабочему проекту. Самый лучший способ создать новый проект устройства USB - взять в качестве основы уже готовый, проверенный проект, изучить его код и опции компилирования. Далее нужно просто исправить код под свои нужды, добавив в него нужные функции. Готовых проектов с открытым исходным кодом на основе V-USB очень много, так что выбор у Вас есть (см. [2, 5]).

Это очень важное условие, которое легко нарушить. Убедитесь в том, что Вы не используете в своем проекте AVR никакое прерывание с приоритетом выше, чем приоритет внешнего прерывания для нужд библиотеки V-USB (обычно для V-USB задействовано прерывание INT0 для отслеживания перепадов сигнала D+ шины USB). Также убедитесь в том, что Вы не запрещаете прерывания на время больше нескольких миллисекунд, и не блокируете повторение вызовов процедуры usbPoll на время больше 10 миллисекунд. Самая частая ошибка - добавление ресурсоемкого, долго выполняющегося кода в главный цикл main (из этого же главного цикла делаются вызовы usbPoll), например каких-нибудь вычислений с плавающей запятой или операторов scanf, sprintf, printf. Имейте в виду, что вычисления с плавающей запятой ОЧЕНЬ медленные, так что избегайте их использования, или вставляйте вызовы usbPoll в тело главного цикла до и после таких медленных вычислений.

Windows запоминает (кэширует) ранее использовавшиеся пары VID/PID для устройств USB, и соответствующие им драйверы операционной системы. Поэтому если Вы сначала использовали один тип устройства USB (например USB HID мышь) с определенной парой VID/PID, а потом попытались использовать ту же самую пару VID/PID для другого устройства USB, то Windows все еще может ожидать на этих VID и PID мышь, и поэтому будет некорректно работать с Вашим устройством USB.

Прим. переводчика: самый безобидный эффект такого рода - некорректное отображение устройства USB в Диспетчере Устройств. На скриншоте ниже устройство USB отображается как программатор USBasp, однако на самом деле это устройство USB HID Сергея Кухтецкого (это потому, что у них одинаковые VID 0x16С0 и PID 0x05DC). Несмотря на такое некорректное отображение устройства USB HID в Диспетчере, это никак не влияет на его работоспособность - вместе с ПО хоста все работает нормально.

V-USB-VID-PID-Windows-trouble

Есть проблема в реализации драйвера библиотеки V-USB, когда таблицы прерываний некоторых чипов не совместимы с кодом. Это касается чипов с суффиксом P, например ATmega168P, ATmega328P. Это выглядит весьма странно, поскольку код успешно компилируется, но затем просто не работает. Однако чипы серий P совместимы по двоичному коду с чипами без префикса P. Так что если Вы работаете, к примеру, с микроконтроллером ATmega168P, то компилируйте код для микроконтроллера ATmega168.

Прим. переводчика: чипы ATmega328 не бывают без префикса P, поэтому в случае использования старых версий V-USB компилировать для чипов Atmega328P нужно как для чипов ATmega168, не обращая при этом внимания на якобы возникающее переполнение памяти. Конечно же, это создает определенные неудобства.

Вы также можете попробовать задать "USB_INTR_VECTOR" как "INT0_vect" в файле usbconfig.h, или заменить "SIG_INTERRUPT0" на "INT0_vect" в файле usbdrvasm.S библиотеки V-USB (это место находится около строки 60 в версии библиотеки 20090822).

Эта проблема уже решена в более свежих версиях библиотеки V-USB, начиная с версии 20100715 (по крайней мере я пробовал компилировать для MCU = atmega328 при использовании версии vusb-20121206.zip, код нормально работал на микроконтроллере ATmega328P).

Если Вы поменяли настройки проекта (например, изменили в Makefile тактовую частоту F_CPU или модель микроконтроллера DEVICE) или его код, и хотите перекомпилировать проект, чтобы получить новую прошивку firmware, то не забывайте про команду make clean. Это довольно часто встречающаяся ошибка. Типичная последовательность команд для получения двоичного HEX-файла прошивки следующая:

make cleanmake hex

Если Вы забыли ввести make clean, и сразу выполнили make hex, то для получения прошивки возможно будут использоваться старые объектные файлы, которые были созданы ранее, и Вы получите неработоспособную прошивку. Поэтому обязательно выполняйте команду make clean!

Прим. переводчика: внимательно изучите, что на самом деле делает make clean. Иногда она вместо удаления объектных файлов библиотеки V-USB (файлы с раширением *.o) удаляет папку usbdrv целиком, а потом копирует эту папку usbdrv из другого места, где объектных файлов нет. Иногда такое поведение для новичков чревато проблемами - если команда копирования не срабатывает (неоткуда взять копию usbdrv), то новичок не понимает, почему команда make hex завершается с ошибкой. А когда сообразит, что нужно восстановить usbdrv, то новичок копирует её вместе со старыми объектными файлами.

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

//Подпрограмма инициализации портов приложения.
void SetupAllPins (void) { //делаем ножки светодиодов выходами. DDRD |= (1 << 5); // порт D DDRD |= (1 << 6); // DDRB |= (1 << 0); // порт B //делаем ножки PWM выходами DDRB |= ((1 << 2) | (1 << 1)); //порт B //настраиваем дополнительные выходы PC0..PC5 DDRC |= ((1 << OUT3_BIT) | (1 << OUT4_BIT) | (1 << OUT5_BIT) | (1 << OUT6_BIT) | (1 << OUT7_BIT) | (1 << OUT8_BIT)); }

//Подпрограмма запуска подсистемы USB.
static void hardwareInit(void) { uchar i;
/* сбросим pull-up на сигналах USB */ //Ошибка: //PORTD = (uchar)~((1 << USB_CFG_DMINUS_BIT)|(1 << USB_CFG_DPLUS_BIT)); //На самом деле правильно вот так: PORTD &= (uchar)~((1 << USB_CFG_DMINUS_BIT)|(1 << USB_CFG_DPLUS_BIT)); //Ошибка: //DDRD = (1 << USB_CFG_DMINUS_BIT)|(1 << USB_CFG_DPLUS_BIT); //На самом деле правильно вот так: DDRD |= (1 << USB_CFG_DMINUS_BIT)|(1 << USB_CFG_DPLUS_BIT);
for(i=0; i < 20; i++) { /* 300 мс дисконнекта */ wdt_reset(); _delay_ms(15); } //Ошибка: //DDRD = 0; /* удаляем USB reset condition */ //На самом деле правильно вот так: DDRD &= ~((1 << USB_CFG_DMINUS_BIT)|(1 << USB_CFG_DPLUS_BIT)); /* удаляем USB reset condition */ }

int main(void) { SetupAllPins(); wdt_enable(WDTO_1S); hardwareInit(); usbInit(); sei(); for(;;) { /* цикл событий main */ wdt_reset(); usbPoll(); ...

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

Если Вам кажется, что Вы проверили все эти причины, но устройство все равно не работает, попытайтесь ограничить возможности возникновения ошибки. Например:

• Возьмите изначально работоспособную макетную плату, про которую точно знаете, что есть прошивка, с которой интерфейс USB работает, тогда Вы сразу отсечете аппаратные ошибки (1, 2, 3, 4). Т. е. если устройство не работает, то это точно ошибка в программе, либо в конфигурации проекта.

• Возьмите за основу изначально работоспособный проект, про который точно знаете, что он работает, тогда Вы отсечете все возможные ошибки, связанные с программным кодом (5, 6, 7, 10). Т. е. если устройство не работает, то значит скорее всего проект неправильно сконфигурирован (не соответствует модель процессора, тактовая частота кварцевого резонатора), или неправильно скомпилирован (забыли make clean), или неправильно установлены фьюзы, или какая-то аппаратная ошибка.

[Ссылки]

1. V-USB Troubleshooting, Device Not Recognized Errors site:vusb.wikidot.com.
2. V-USB site:obdev.at.
3. V-USB site:ru.wikipedia.org.
4. Engbedded Atmel AVR® Fuse Calculator site:engbedded.com.
5. AVR-USB-MEGA16: примеры применения, проекты с открытым исходным кодом и принципиальной схемой.
6. Как определить тактовую частоту микроконтроллера?