
Hola queridos lectores!
Aquí se me ocurrió una idea, pero no armar un panel de control para una nave espacial. A USB. Con soporte de controladores nativos. HID personalizado. Para pegar y todo funciona, sin bailes y panderetas. Como resultado, obtuvimos una especie de monstruoso "gamepad" para simuladores espaciales. En general, juzga por ti mismo.
Al principio, tenía poca idea de lo que sucedería al final. Quería dos joysticks principales, como en Soyuz-MS, algunos interruptores, botones y varias pantallas.
Habiendo estimado la superficie de trabajo de mi mesa, elegí las dimensiones de la consola en ancho y profundidad 500 * 300 mm. Y tras hurgar en los almacenes y tiendas de construcción en busca de materiales de construcción, eligió una altura de 125 mm. Como resultado, adquirí una lámina de madera contrachapada de 4 mm, listones de 20 * 12 mm y un tablero de 120 * 20 mm.
En el cad, se dibujó rápidamente un boceto del control remoto. Y lo hice en un árbol durante mucho tiempo. Tres meses los fines de semana Y no porque trabajara tan imponentemente como una sierra, sino por falta de tiempo. El panel se colocó, lijó y pintó con pintura de esmalte, de color similar a los paneles reales de naves espaciales o aviones.

Pero por ahora, deje de lado el trabajo de pintura y hablaré sobre el relleno electrónico.
Se compraron piezas de radio en Ali. Como joysticks, encontré estos. En general, la situación con tales joysticks es una costura completa. Las soluciones industriales son demasiado caras, pero baratas, vienen como juguetes y, por lo tanto, son malas. Estos son de bastante alta calidad, pero no sabrán cuánto tiempo durarán.
El resto de la pequeña cosa no causó problemas. El controlador seleccionó STM32. Como ADC para joysticks, ADS1118 de 16 bits. También se compró una fuente de alimentación de 12 V. En realidad, este voltaje se debe al hecho de que obtuve un indicador de combustible del "shah", que también quería conectar aquí.
En la foto, la fuente de alimentación, estabilizadores para 5 y 3.3 V, STM32, MCP23017, ADS1118Controlador STM32F407VET6 de 100 pines, conectado a él:
2 selectores a 4 posiciones
1 resistencia variable
2 interruptores de eje
4 ejes principales
2 ejes auxiliares
2 ejes de control
4 interruptores de llave, 2 botones cada uno
20 botones con LEDs
4 interruptores principales con LED
2 botones de hongos con LED
2 botones de temporizador
3 interruptores con LED
13 interruptores
2 ADS1118 (ADC)
4 MAX7219 (pantallas LED de 8 dígitos)
2 TM1637 (horas de visualización)
1 PCF8574 (expansor de E / S, conectado a la pantalla de síntesis de caracteres)
La estructura resultanteDecidí que sería demasiado para cientos de patas del MK, y agregué aquí expansores de E / S: cuatro piezas de MCP23017, para 16 entradas o salidas cada una. Mirando hacia el futuro, diré que el retraso en el sondeo de las entradas del expansor resultó ser de aproximadamente 0.13 ms por chip, a una velocidad de bus I2C de 400 kHz. Es decir, con un margen cubre el tiempo mínimo de sondeo USB de 1 ms.
Para no conducir el bus I2C con solicitudes inútiles, el MCP23017 tiene salidas de interrupción que se configuran cuando cambia el estado de las entradas. También los apliqué en mi proyecto. Al final resultó que, debido al ruido de los contactos, estas interrupciones fueron inútiles.
El ADS1118 ADC no se mantiene al día con la velocidad del USB, su rendimiento declarado es de 820 muestras por segundo, que es de 1.2 ms, mientras que tiene varias entradas que ya están conectadas al ADC a través del multiplexor. Usé 2 entradas en un chip, por lo que el tiempo de actualización de los valores es de 2.4 ms. Malo, pero ¿qué puedes hacer? Desafortunadamente, no hay otros ADC rápidos de 16 bits en Ali.
Por dentro se ve así, pero después de instalar los cables es mucho peorEl programa de la CPU está escrito al estilo de un programa de PLC. No hay solicitudes de bloqueo. El núcleo no espera a la periferia, no ha tenido tiempo y al diablo con ella, en el próximo ciclo interrogará. Tampoco hay RTOS en el proyecto, lo probé, me encontré con un tiempo de espera de tarea mínimo de 1 ms: resulta lento si necesitamos enviar datos a través de USB con una frecuencia de 1 ms. Como resultado, me di cuenta de que usaría el sistema operativo sin osDelay (), y luego ¿por qué RTOS? Al igual que en un PLC, basta con colocar las instrucciones del programa una por una dentro de un bucle infinito.
Usado, por supuesto, las bibliotecas CubeMX y HAL. Por cierto, recientemente cambié a HAL y me pregunté sobre la conveniencia. No sé por qué todavía no es muy popular, lo principal es descubrirlo al principio, y luego será muy simple. Se siente como si estuvieras programando arduino.
El dispositivo tendremos USB HID personalizado. HID es mouse, teclado, gamepad, joystick, algunos más. Y hay costumbre. Todo esto no requiere controladores del sistema operativo. Más precisamente, ya están escritos por el desarrollador. Un dispositivo personalizado es bueno porque combinamos las capacidades de todos los dispositivos anteriores a nuestra discreción.
En general, lo de USB es muy complicado, tiene un manual de casi mil páginas y no se puede sacar de un instante. Quien no quiera leer manuales pesados, hay un gran artículo sobre USB en un NutShell, google. Ella también tiene una traducción. Trataré de explicar algunos puntos "en los dedos".
USB: transferencia de datos en paquetes con un montón de niveles y abstracciones. El dispositivo está con nosotros; no puede solicitar ningún dato, el host inicia la transferencia completa. El host escribe y solicita datos a los llamados puntos finales, físicamente estos son algunos buffers en la memoria MK. Para que el host entienda por qué puntos finales es posible escribir, qué puntos finales leer y qué datos puede interpretar como botones y ejes de nuestro dispositivo y, en general, qué tipo de dispositivo tenemos aquí, al comienzo de la conexión solicita descriptores de dispositivo. Hay muchos de estos descriptores y es difícil componerlos y puedes hacerlo como quieras y también cometer errores en cualquier lugar. Físicamente, son una matriz de bytes.
De hecho, CubeMX generará un código de inicialización HID personalizado mejor que nosotros.


Preste atención a la última imagen debajo del número 3. Este es el tamaño del descriptor en bytes, que determina qué ejes y botones hay en nuestro dispositivo. Este descriptor se genera en la
herramienta de descriptor HID . Hay varios ejemplos para el autoestudio. En general, aquí está mi descriptor. Todavía no hay datos para las pantallas, para facilitar la comprensión, pero todos los botones y ejes de los joysticks están presentes. Debe colocarse en el archivo usbd_custom_hid_if.c. Por defecto, este controlador hace que el cubo esté vacío.
Descriptor HID (tamaño 104 bytes)__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = { 0x05, 0x01,
De hecho, se puede componer a su gusto, primero configure los parámetros de la PÁGINA DE USO y el USO requerido, por ejemplo, el eje de USO (Acelerador), y luego después de la palabra ENTRADA (Datos, Var, Abs), el sistema asumirá que tenemos el eje "Gas". La dimensión del eje variable y su número se establecen mediante los parámetros LOGICAL_MAXIMUM, MINIMUM, REPORT_SIZE, REPORT_COUNT, que deben estar antes de INPUT.
Más detalles sobre estos parámetros, así como sobre qué (Datos, Var, Abs) se pueden encontrar en
Definición de clase de dispositivo para dispositivos de interfaz humana (HID) v1.11 .
El siguiente es un ejemplo de inicialización del eje del acelerador desde mi descriptor. En este ejemplo, Throttle tiene un rango de 0-65535, que corresponde a una variable uint16_t.
0x05, 0x02,
Y sí, todavía, digamos que no puede escribir LOGICAL_MAXIMUM, MINIMUM, REPORT_SIZE, REPORT_COUNT cada vez, el host determinará este valor por el parámetro anterior. Esto se ilustra mediante los ejes que van uno tras otro, sin especificar el tamaño y el número:
0x09, 0x32,
La siguiente estructura corresponde a todo este descriptor, que es más alto bajo el spoiler. De hecho, ya no es obligatorio, es más conveniente grabar según los punteros.
#pragma pack(push, 1) typedef struct _myReportStruct { uint16_t Throttle; uint16_t X; uint16_t Y; uint16_t Z; uint16_t Rx; uint16_t Ry; uint16_t Rz; uint16_t Slider; uint8_t Hat;
Esta estructura puede ser enviada al host por la función
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, (uint8_t *) &Desk, sizeof(Desk));
El primer parámetro es un controlador USB; ya está creado en nuestro cubo. Es posible que deba incluir el archivo necesario con la inclusión donde este identificador se inicializa por primera vez y escribir
USBD_HandleTypeDef hUsbDeviceFS externo; para que puedas trabajar con él. El segundo parámetro es un puntero a nuestra estructura y el tercero es el tamaño de la estructura en bytes.
Después de llenar y flashear el controlador, notará que algo USB se mueve lentamente. Los datos de nuestro panel no se actualizan rápidamente. Para ser rápido, en los archivos usbd_customhid.h necesita cambiar #define CUSTOM_HID_EPIN_SIZE al valor máximo 0x40, #define CUSTOM_HID_EPOUT_SIZE también establece 0x40. En el archivo usbd_customhid.c, busque comentarios en el descriptor de punto final "/ * bInterval: Polling Interval (20 ms) * /" y cambie el byte del descriptor a 0x01 para cada punto final, solo dos veces. Lo que corresponderá a 1 ms de intercambio de datos.
Debería ser algo como esto. Dispositivo estándar sin instalar ningún controladorEn general, la función de gestión se entiende poco. Es bastante fácil de hacer y todos los botones y ejes ya están funcionando. Queda por hacer que las pantallas funcionen. Lo hice, unos seis meses, y durante medio año el panel ha estado acumulando polvo en una caja larga. No hay tiempo Por lo tanto, decidí diseñar el artículo de esta forma, de lo contrario, corre el riesgo de no salir.
Con las pantallas, todo es igual que con los ejes. Para ellos, necesitamos complementar nuestro descriptor HID del dispositivo, solo indicar que se trata de pantallas y, en lugar de aceptar datos de entrada, el host enviará datos de salida.
El manejo del dispositivo HID ha crecido significativamente. Aquí ya he aplicado los parámetros de ID de informe para no obstruir el búfer de transmisión / recepción y los puntos finales con datos completos y para distinguir qué tipo de telegrama hemos recibido. La ID del informe es un byte uint8_t con el valor que viene al comienzo del telegrama. El valor que establecemos en el descriptor de dispositivo HID.
La salida se
procesa en la función
estática int8_t CUSTOM_HID_OutEvent_FS (uint8_t event_idx, uint8_t state) , que, de forma predeterminada, se encuentra en usbd_custom_hid_if.c.
static int8_t CUSTOM_HID_OutEvent_FS () static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state) { uint8_t dataReceiveArray[USBD_CUSTOMHID_OUTREPORT_BUF_SIZE]; USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData; for (uint8_t i = 0; i < USBD_CUSTOMHID_OUTREPORT_BUF_SIZE; i++) { dataReceiveArray[i] = hhid->Report_buf[i]; } if (dataReceiveArray[0] == 2)
Solo queda escribir un programa en una PC que envíe los informes necesarios para dirigir las pantallas. Sin embargo, para verificar el código MK,
es adecuado un excelente programa de ST:
USB HID Demonstrator . Le permite enviar informes desde una PC con cualquier contenido.
Prueba de pantalla LEDEn esta etapa, he terminado hasta ahora. Y no se sabe si volveré a comenzar.
Se juega en simuladores más interesantes que con un teclado. Pero no tanto como para que haya un efecto wow directo. El teclado, también se ve como un panel de control. Pero controlar los ejes del joystick es, como mínimo, inusual. Siéntete como un astronauta. Es cierto que se necesita un traje espacial para una inmersión completa.
Espero que te haya interesado. Errores tipográficos, imprecisiones y delirios están presentes. Aquellos que quieran profundizar en el código pueden ver
aquí .
Saludos