Отладочная плата Arduino Due построена на базе ATSAM3X, который имеет при себе собственный USB порт в периферии. Для среды разработки Arduino IDE не так давно были написаны простые инструменты для работы с портом, а в частности, работа в качестве HID устройства в системе ПК.
Уже третий год у меня пылится игровой контроллер от приставки NES. Существует множество проектов, которые используют Arduino и передают данные через FTDI. Почему бы не подключить контроллер к ПК через стандартный USB?
Пожалуй эту будет один из самых глупых способов использования 32-х бит ARM микроконтроллера. В данном случае проще всего прикинуться USB клавиатурой и эмулировать нажатия клавиш. Из примеров с сайта разработчиков можно выделить несколько необходимых функций:
- Keyboard.begin() — инициализация
- Keyboard.releaseAll() — отпустить все клавиши
- Keyboard.press(char c) — нажать клавишу
- Keyboard.release(char c) — отпустить клавишу
- Keyboard.print(char *c) — напечатать сообщение
Все коды клавиш можно найти здесь. К примеру, следующий код должен напечатать небольшое сообщение:
1 2 3 4 5 6 7 8 9 10 |
void setup() { Keyboard.begin(); Keyboard.releaseAll(); delay(1000); Keyboard.print("Hello!"); //Просто? } void loop() { } |
Загрузите программу, затем подключите плату через Native USB.
Собственно говоря, для нашего USB игрового контроллера нам необходимо опрашивать кнопки и отправлять данные о нажатиях в порт.
Дело в том, что постоянно передавать данные в порт — неправильное решение данной задачи. Необходимо отслеживать изменение состояния каждой кнопки и лишь при изменении (нажатии/отпускании) передавать данные.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#include <nesinterface.h> #define K_RIGHT(x) boolean((x >> 7) & 0x01) #define K_LEFT(x) boolean((x >> 6) & 0x01) #define K_UP(x) boolean((x >> 4) & 0x01) #define K_DOWN(x) boolean((x >> 5) & 0x01) #define K_A(x) boolean((x >> 1) & 0x01) #define K_B(x) boolean((x >> 0) & 0x01) #define K_START(x) boolean((x >> 3) & 0x01) #define K_SELECT(x) boolean((x >> 2) & 0x01) #define latch 6 //Пин для LATCH #define clock 5 //Пин для CLOCK #define data 7 //Пин для DATA NES NES; byte val, prev, delta; void setup() { NES.Init(clock, latch, data); //Настройка портов NES контроллера Keyboard.begin(); Keyboard.releaseAll(); //На всякий случай отпускаем все клавиши. prev = 0; } void emulate() { if (K_UP(delta)) if (K_UP(val)) Keyboard.press(KEY_UP_ARROW); else Keyboard.release(KEY_UP_ARROW); if (K_DOWN(delta)) if (K_DOWN(val)) Keyboard.press(KEY_DOWN_ARROW); else Keyboard.release(KEY_DOWN_ARROW); if (K_RIGHT(delta)) if (K_RIGHT(val)) Keyboard.press(KEY_RIGHT_ARROW); else Keyboard.release(KEY_RIGHT_ARROW); if (K_LEFT(delta)) if (K_LEFT(val)) Keyboard.press(KEY_LEFT_ARROW); else Keyboard.release(KEY_LEFT_ARROW); if (K_A(delta)) if (K_A(val)) Keyboard.press('z'); else Keyboard.release('z'); if (K_B(delta)) if (K_B(val)) Keyboard.press('x'); else Keyboard.release('x'); if (K_START(delta)) if (K_START(val)) Keyboard.press('s'); else Keyboard.release('s'); if (K_SELECT(delta)) if (K_SELECT(val)) Keyboard.press('a'); else Keyboard.release('a'); } void loop() { NES.Update(); //Загружаем данные от контроллера NES val = NES.Read(); delta = val ^ prev; //Выявляем изменения emulate(); //Эмулируем нажатия prev = val; } |
Переменная delta — отвечает именно за переход состояний. Для работы с игровым контроллером я использовал свою библиотеку NESInterface. В коде назначены следующие клавиши:
- D-PAD — стрелки
- Start — буква S
- Select — буква A
- A кнопка — буква Z
- B кнопка — буква X
Работает просто замечательно, проблема может приключится только с драйверами.