Поговорим с мышами? Или Soft USB HOST на Esp32

Esp32 весьма мощный контроллер, подходящий для эмуляции различных ретро систем, таких как Spectrum, Commodore, NES, IBM PC-XT и тд. Есть возможность сгенегировать VGA или AV — TV композитный сигнал, подключить различные компактные LCD дисплеи. Он умеет разговаривать с SD картами по SPI & SD протоколу. Вот только с USB клавиатурами, мышами и джойстиками — не умеет. Попробуем научить его говорить с ними. Есть конечно новый вариант ESP32-S3 с одним USB host контроллером, а мне нужно подключить хотя бы 3 девайса и без хаба…

Нам понадобится (ссылки только для примера) :

  1. Собственно сам ESP32 ~3$ WEMOS LOLIN32

  2. Несколько разъемов USB ~3$ за десяток

  3. Мне понадобился логический анализатор ~$3 CY7C68013A и бесплатная отличная программа анализатора протоколов

  4. Спецификации USB и USB-HID (HID — Human Interface Device).

  5. Макетные платы или любовь к паяльнику/припою/канифоли.

USB 1.1

Большинство HID умеет разговаривать с хостом этого стандарта. Имеет две скорости работы HS (High Speed) -12 Mbits/s и LS (Low speed)- 1.5 Mbits/s. К сожалению, скорости процессора ногодрыгом (Generic Pin IO) недостаточно для работы в HS, поэтому мы будем рассматривать реализацию только LS (1.5 Mbits/s). Впрочем большая часть HID devices работает именно на этой скорости. На физическом уровне USB 1.1 — имеет два сигнальных провода: D+ и D- , провод земли : GRN и провод питания: VBus. Сигналы в D+ и D- измеряются относительно провода GRN, хотя большую часть времени они работают как дифф-пара (для уменьшения синфазных помех, наводок) , то есть в противофазе. Шина (D+ и D-) может принимать 4 состояния (high — ~3.3v low — 0v) для LS:

'K' : D+ — high D- low

'J' : D+ — low D- high

SE0: D+ — low D- low

SE1: D+ — high D- high

Состояние SE1 — незаконное. В исправной системе шина в этом состоянии находиться не должна

Когда к шине хоста ничего не подключено , по стандарту, D+ и D- присоединены со стороны хоста к земле через резисторы 15 kOhm (у нас получается около 50 kOhm — это величина pull-down резисторов в ESP32 чипе, впрочем работе это не мешает). При подключении LS девайса он замыкает провод D- на 3.3v через резистор в 5 kOhm, а HS девайс — провод D+, что позволяет отличить HS девайс от LS.

Определив на шине появление LS девайса , после задержки в 200 миллисекуд (дать время девайсу закончить переходные процессы) мы начинаем с ним разговаривать. В протоколе USB инициатором всегда является хост (в отличии от протокола PS/2 например). Раз в миллисекунду хост посылает сообщение девайсу и тот отвечает. Данные передаются младшим битом вперед, 0 кодируется переходом 'K' в 'J' или 'J' в 'K'. Единица — отсутствием перехода. Если встречается больше 6 единиц подряд , то принудительно вставляется переход состояния 'K' в 'J' или 'J' в 'K' . (NRZI coding) . Передача идет пакетами, начало пакета — синхронизирующая последовательность «KJKJKJKK» ('00000001' binary или число 80Н , младший бит передается первым), конец пакета «SE0, SE0, J». первый байт пакета (после синхронизирующей последовательности) тип пакета (packet ID — PID). Остальные байты и их кол-во — зависят от типа пакета. Пакеты можно поделить на короткие (handshake) ACK, NACK, STALL … из одного байта , средние IN, OUT, SETUP из 3 байт и длинные DATA0, DATA1 из 3–11 байт. Формат короткого пакета 8 bit start, 8 bit PID, SE0, SE0, J.

Формат среднего пакета: 8 bit start, 8 bit PID, 7 bit address (назначенный адрес устройства), 4 bit EP (конечная точка), 5 bit CRC5 (контрольная сумма адреса и устройства) .

Формат длинного пакета 8 bit start, 8 bit PID (DATA0 или DATA1) 0–8 байт данных и 2 байта crc16 (только от данных).

После физического подключения устройства хост должен:

  1. запросить тип устройства (диск, камера, HID…)

  2. присвоить ему уникальный адрес

  3. выяснить количество и тип конечных точек на чтение (типа нажатые кнопки) или на запись (типа LED-ов состояния клавиатуры) и частоту их опроса (раз в сколько миллисекунд)

  4. выбрать нужную конфигурацию

  5. дальше один раз в миллисекунду:

  6. послать запрос на конечную точку и получить или данные или NACK

  7. послать пустой пакет (SE0, SE0, J) на устройство если не пришло время запрашивать данные.

Если пришли данные — проверить пакет на корректность (чек сумма и тип) и послать ACK если все верно или запросить данные повторно. К сожалению некоторые устройства не дают достаточно времени для проверки корректности пакета (не могу на CPU одновременно и принимать и посчитывать), поэтому я сначала не отвечаю на пакет , проверяю его его корректность , запрашиваю его повторно на следующей миллисекунде и только тогда отвечаю устройству ACK. Такое поведение, в целом, соответствует спецификации (если устройство соответствует спецификации USB 1.1 LS, то это должно работать).

Примерный код приемника — узкое место (должен успевать делать заметно больше двух оборотов на бит и функция _getCycleCount8d8() — младшие 8 бит абсолютного времени = цикл CPU/8 (240MHz/8 = 30 MHz)- время на 6 бит должно быть меньше 256 (в байт)):


while(act>0 && (val||nval) ) // есть активность и  не нули на шине
	{
		val  = nval;
		nval = READ_BOTH_PINS;  //прочитать порт  и наложить маску пинов D+  D-,пины в старшем байте
		received_NRZI_buffer[locRec] = _getCycleCount8d8() |  nval;  // записать время и состояние в 16 бит
		if(val!=nval) // была замечена активность
		{
			locRec++;
			act = TOUT;   // была замечена активность,отложить выход из цикла на TOUT
		}
		else act--;  // 
}

Примерный код передатчика:

SET_O;  // cконфигурировать пины на выход
// в transmit_NRZI_buffer состояния на передачу 0 - 'K'  1 - 'J  2 - se0
	for(k=0;k
Pulseview работа с тремя мышами
cd54bc7ec857f06f8ccd321e01ae8afc.jpg
Набор в работе
f474810df2ca74e34664773504aff5db.jpg
Детям после 16.
Слева направо: логический анализатор,  ESP32, STM32 обслуживающий себя самСлева направо: логический анализатор, ESP32, STM32 обслуживающий себя сам

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

sourcе на 4 HID устройства.

© Habrahabr.ru