Потребовалось мне как то прошить на необитаемом острове контроллер. Благо был под рукою ноутбук и я подумал, что просто кнопками щелкать — это не метод настоящего джедая. Но это шутка. Девайс можно использовать и для описанных выше целей, но создавался он по другой причине. Иногда в обеденный перерыв очень хотелось что-либо поделать с исследуемыми устройствами (например LCD-дисплеем). Проблема состояла в том, что на компьютер невозможно установить какие-либо драйвера — нужно звать администратора и объяснять цель всех этих установок.
Но USB это не только поточные устройства (так называемые CDC), но еще и HID и Mass Storage. А HID-драйвер установлен уже по умолчанию. Поэтому решено было сделать HID-устройство, с возможностью вывода и ввода через порты контроллера.
Вечер потратил, что бы составить схему.
Развязка именно такая. Пара диодов D1 и D4 понижают напряжение питания примерно 3.8 — 4 вольт. Это предусмотрено для конструкций, имеющих напряжение питания ниже 5ти вольт. Стабилитрончики D2 и D3 занимаются тем же, ограничивая агрессивное напряжение пятивольтовой шины. Больше в конструкции нет ничего интересного, на что следовало бы обратить внимание.
Я делаю это не для продажи, поэтому заморачиваться с травлением плат вообще не стал. Просто купил так называемую растровую плату с дырками и собрал не ней. Снизу сделал гребенку для возможности использования конструкции на breadboard. Их получил, раскурочив панельку для установки микросхемы. Все это заняло еще один вечер. Обратную сторону я прикрыл пластиковым мягким материалом, что бы исключить случайного касания с другими элементами на breadboard.
А вот написание кода уже заняло время… Изобретать велосипед я не стал, просто взяв за основу готовый проект реализации v-usb. Но не каждый дескриптор будет работать, как задумывалось и тем более с ходу. Помучав пару программ для автоматического составления дескриптора и не получив нужного результата, пришлось изучать рекомендации форума исполнителей USB.
Через две недели я уже мигал светодиодами на доске и возил все это железо на работу.
А еще через пару дней уперся в неприятный момент: на компьютере не оказалось ни одной среды разработки. Зато имелась установленная Java (JRE). И перерыв в тот день потратил на то, чтобы найти описание как правильно писать нативную dll к Java для управления HID. И тут случайно я нашел один просто обалденный продукт. Точнее статью. Но описание меня очень заинтересовало.
Вечером я уже экспериментировал с Java-классами, пытаясь мигать лампочками. Увы, библиотека позволяла работать только с фьючерами. А просто репорты выбрасывали исключения. Связался с разработчиком. Парень пообещал помочь. Но все те, кто говорит на русском, имеют плохую память. Поэтому подождав пару дней до наступления выходных, переписал firmware микроконтроллера на работу с фьючерами и приступил к конвертации с С++ кода для моего дисплейчика. Конечно, скорость оставляет желать лучшего, но это тоже интересный вариант. Ведь мне нужно только подобрать управляющие коды к дисплею, помигать лампочками, переключить пару релюшек или (если совсем плохо на необитаемом острове) зашить случайно микроконтроллер. Тем более у меня не стоит задача управлять реактором, где за доли секунды можно проморгать цепную реакцию.
И так, что конкретно можно программно менять в контроллере:
- DS_CMD_PORTC = 0 — чтение/запись порта C;
- DS_CMD_DDRC = 1 — чтение/запись регистра управления DDRC;
- DS_CMD_PORTB = 2 — чтение/запись порта B;
- DS_CMD_DDRB = 3 — чтение/запись регистра DDRB;
- DS_CMD_UDR = 4 — чтение/запись регистра UDR;
- DS_CMD_BRRL = 5 — чтение/запись регистра BRRL;
- DS_CMD_BRRH = 6 — чтение/запись регистра BRRH;
- DS_CMD_UCSRA = 7 — чтение/запись регистра USCRA;
Есть, правда, еще одна команда, но для единичного устройства она не потребуется.
Ну и далее представлен класс, который должен выполнять работу обмена.
public class DSHIDBr extends HIDCommunication {
private final Object finalizerGuardian = new Object() {
protected void finalize() throws Throwable {
System.out.println("finalize");
if (isOpened())
CloseHIDDevice();
}
};
public static final String DATE_FORMAT_NOW = "HH:mm:ss.nnn";
public Calendar cal;
protected SimpleDateFormat sdf;
public DSHIDBr(){
cal = Calendar.getInstance();
sdf = new SimpleDateFormat("HH:mm:ss.SSS");
}
public final static byte DS_CMD_DEV_VERSION = (byte) 254;
public final static byte DS_CMD_PORTC = 0;
public final static byte DS_CMD_DDRC = 1;
public final static byte DS_CMD_PORTB = 2;
public final static byte DS_CMD_DDRB = 3;
public final static byte DS_CMD_UDR = 4;
public final static byte DS_CMD_BRRL = 5;
public final static byte DS_CMD_BRRH = 6;
public final static byte DS_CMD_UCSRA = 7;
public final static byte FOOT28C = 32;
public final static byte FOOT27C = 16;
public final static byte FOOT26C = 8;
public final static byte FOOT25C = 4;
public final static byte FOOT24C = 2;
public final static byte FOOT23C = 1;
public final static byte FOOT19B = 32;
public final static byte FOOT18B = 16;
public final static byte FOOT17B = 8;
public final static byte FOOT16B = 4;
public final static byte FOOT15B = 2;
public final static byte FOOT14B = 1;
public byte SendCommand(byte Command, byte Value) {
short /*MaxWriteLength,*/ FeatureReportLength;
/* Check if HID device opened */
if (isOpened() == false) {
return HID_DEVICE_NOT_OPENED;
}
/* Read the Feature Report Length this must be as mentioned in Hid Report descriptor + 1 (Report ID) */
FeatureReportLength = GetFeatureReportLength();
byte[] Outpacket = new byte[FeatureReportLength];
/* We don't use ReportID set it to '0' */
Outpacket[0] = 0;
/* Load the useful command number */
Outpacket[1] = Command;
Outpacket[2] = Value;
/* Write TX buffer via SetReport(Feature) requst */
byte Status = SetFeatureReport(Outpacket, FeatureReportLength);
return Status;
}
public byte readValue;
public byte ReadCommand(){
short /*MaxWriteLength,*/ FeatureReportLength;
/* Check if HID device opened */
if (isOpened() == false) {
return HID_DEVICE_NOT_OPENED;
}
/* Read the Feature Report Length this must be as mentioned in Hid Report descriptor + 1 (Report ID) */
FeatureReportLength = GetFeatureReportLength();
byte[] buffer = new byte[FeatureReportLength];
buffer[0] = 0;
byte Status = GetFeatureReport(buffer);
if (buffer.length > 0)
readValue = buffer[1];
return Status;
}
public String now() {
return sdf.format(cal.getTime());
}
}
Основных функций не много. Инизиализация, финализация, посылка байта и прием байта. Есть еще одна, вспомогательная. Возвращающая текущее время с точностью до миллисекунд. Зачем она? Для протоколирования. Самое главное в работе — это логи. А логи без точного времени могут помочь мало.
Ну и для самых терпеливых, кто умудрился добраться до конца, публикую ссылочку на фирмваре. Она, правда, на рапиде. A в добавок сами проектики на Java.
Автор: svd71