
Olá queridos leitores!
Uma ideia me veio aqui, mas não montar um painel de controle para uma nave espacial. Para USB. Com suporte de driver nativo. HID personalizado. Furar e tudo funciona, sem danças e pandeiros. Como resultado, temos um tipo de "gamepad" monstruoso para simuladores espaciais. Em geral, julgue por si mesmo.
No começo, eu tinha pouca ideia do que aconteceria no final. Eu queria dois joysticks principais, como na Soyuz-MS, alguns interruptores, botões e vários monitores.
Tendo estimado a superfície de trabalho da minha mesa, escolhi as dimensões do console em largura e profundidade de 500 * 300 mm. E tendo vasculhado armazéns de construção e lojas em busca de materiais de construção, ele escolheu uma altura de 125 mm. Como resultado, adquiri uma folha de madeira compensada de 4 mm, ripas de 20 * 12 mm e uma placa de 120 * 20 mm.
No cad, um esboço do controle remoto foi rapidamente esboçado. E eu fiz isso em uma árvore por muito tempo. Três meses nos fins de semana. E não porque ele trabalhou tão imponentemente como uma serra, mas por falta de tempo. O painel foi esticado, lixado e pintado com tinta esmaltada, de cor semelhante a painéis reais de naves espaciais ou aeronaves.

Mas, por enquanto, deixe o trabalho de pintura de lado e vou falar sobre o recheio eletrônico.
Peças de rádio foram compradas em Ali. Como joysticks, eu os encontrei. Em geral, a situação com esses joysticks é uma costura completa. As soluções industriais são muito caras, mas baratas, vêm como brinquedos e, portanto, ruins. Eles são de alta qualidade, mas não sabem quanto tempo durarão.
O resto da pequena coisa não causou problemas. O controlador selecionou STM32. Como um ADC para joysticks, ADS1118 de 16 bits. Também foi comprada uma fonte de alimentação de 12 V. Na verdade, essa tensão se deve ao fato de eu ter adquirido um medidor de combustível do “xá”, que eu também queria conectar aqui.
Na foto, a fonte de alimentação, estabilizadores para 5 e 3,3 V, STM32, MCP23017, ADS1118Controlador STM32F407VET6 de 100 pinos, conectado a ele:
2 seletores para 4 posições
1 resistor variável
2 interruptores de eixo
4 eixos principais
2 eixos auxiliares
2 eixos de controle
4 interruptores de chave, 2 botões cada
20 botões com LEDs
4 interruptores principais com LEDs
2 botões de fungo com LEDs
2 botões do temporizador
3 interruptores com LEDs
13 interruptores
2 ADS1118 (ADC)
4 MAX7219 (displays LED de 8 dígitos)
2 TM1637 (horas de exibição)
1 PCF8574 (expansor de E / S, conectado à tela de síntese de caracteres)
A estrutura resultanteDecidi que será um pouco demais para centenas de pernas do MK, e adicionei aqui expansores de E / S: quatro pedaços de MCP23017, para 16 entradas ou saídas cada. Olhando para o futuro, direi que o atraso na pesquisa das entradas do expansor foi de cerca de 0,13 ms por chip, a uma velocidade de barramento I2C de 400 kHz. Ou seja, com uma margem cobre o tempo mínimo de busca USB de 1 ms.
Para não acionar o barramento I2C com solicitações inúteis, o MCP23017 possui saídas de interrupção definidas quando o status das entradas muda. Eu também os apliquei no meu projeto. No final, devido ao barulho dos contatos, essas interrupções foram inúteis.
O ADS1118 ADC não acompanha a velocidade do USB, seu desempenho declarado é de no máximo 820 amostras por segundo, ou seja, 1,2 ms, enquanto possui várias entradas que já estão conectadas ao ADC por meio do multiplexador. Eu usei 2 entradas em um chip, então o tempo de atualização dos valores é de 2,4 ms. Ruim, mas o que você pode fazer? Infelizmente, não existem outros ADCs rápidos de 16 bits no Ali.
Lá dentro, fica assim, mas depois de instalar os fios, é muito piorO programa da CPU é escrito no estilo de um programa PLC. Sem pedidos de bloqueio. O núcleo não espera pela periferia, não teve tempo e nem um pouco com ela; no próximo ciclo, ele será interrogado. Também não há RTOS no projeto, tentei, fiquei com um tempo de espera mínimo de 1 ms - ocorre lentamente se precisarmos enviar dados via USB com uma frequência de 1 ms. Como resultado, percebi que usaria o sistema operacional sem osDelay () e, então, por que RTOS? Assim como em um CLP, basta colocar as instruções do programa uma a uma dentro de um loop infinito.
Usadas, é claro, bibliotecas CubeMX e HAL. A propósito, recentemente mudei para o HAL e me perguntei sobre conveniência. Não sei por que ainda não é muito popular, o principal é descobrir isso primeiro e depois será muito simples. Parece que você está programando arduino.
O dispositivo teremos USB personalizado HID. HID é mouse, teclado, gamepad, joystick, um pouco mais. E há costume. Tudo isso não requer drivers do sistema operacional. Mais precisamente, eles já foram escritos pelo desenvolvedor. Um dispositivo personalizado é bom, pois nós mesmos combinamos os recursos de todos os dispositivos acima, a nosso critério.
Em geral, o dispositivo USB é muito complicado, possui um manual de quase mil páginas e você não pode tirá-lo rapidamente. Quem não quer ler manuais pesados, há um ótimo artigo USB em um NutShell, google. Ela também tem uma tradução. Vou tentar explicar alguns pontos "nos dedos".
Transferência de dados em pacote USB com vários níveis e abstrações. O dispositivo está conosco - ele não pode solicitar nenhum dado, o host inicia toda a transferência. O host grava e solicita dados nos chamados endpoints; fisicamente, esses são alguns buffers na memória MK. Para que o host entenda por quais terminais é possível escrever, quais terminais ler e quais dados ele pode interpretar como botões e eixos do nosso dispositivo e, em geral, que tipo de dispositivo temos aqui, no início da conexão, solicita descritores de dispositivo. Existem muitos desses descritores e é difícil compor esses descritores e você pode, como quiser, e também cometer erros em qualquer lugar. Fisicamente, eles são uma matriz de bytes.
De fato, o CubeMX irá gerar um código de inicialização HID personalizado melhor do que nós.


Por favor, preste atenção na última figura abaixo do número 3. Esse é o tamanho do descritor em bytes, que determina quais eixos e botões estão em nosso dispositivo. Este descritor é gerado na
HID Descriptor Tool . Existem vários exemplos para auto-estudo. Geralmente, aqui está o meu descritor. Ainda não existem dados para os displays, para facilitar a compreensão, mas todos os botões e eixos dos joysticks estão presentes. Ele precisa ser colocado no arquivo usbd_custom_hid_if.c. Por padrão, esse identificador torna o cubo vazio.
Descritor HID (tamanho 104 bytes)__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END = { 0x05, 0x01,
De fato, ele pode ser composto como você quiser, primeiro defina a USAGE PAGE e a USAGE necessária, por exemplo, o eixo USAGE (Throttle) e, depois da palavra INPUT (Data, Var, Abs), o sistema assumirá que temos o eixo "Gas". A dimensão do eixo variável e seu número são definidos pelos parâmetros LOGICAL_MAXIMUM, MINIMUM, REPORT_SIZE, REPORT_COUNT, que devem estar antes de INPUT.
Mais detalhes sobre esses parâmetros, bem como o que (Dados, Var, Abs) podem ser encontrados em
Definição de Classe de Dispositivo para Dispositivos de Interface Humana (HID) v1.11 .
A seguir, é apresentado um exemplo de inicialização do eixo Throttle a partir do meu descritor. Neste exemplo, o Throttle possui um intervalo de valores de 0 a 65535, que corresponde a uma variável uint16_t.
0x05, 0x02,
E sim, digamos que você não pode escrever LOGICAL_MAXIMUM, MINIMUM, REPORT_SIZE, REPORT_COUNT toda vez, o host determinará esse valor pelo parâmetro anterior. Isso é ilustrado pelos eixos que seguem um após o outro, sem especificar o tamanho e o número:
0x09, 0x32,
A estrutura a seguir corresponde a todo esse descritor, que é mais alto no spoiler. De fato, não é mais obrigatório, é apenas mais conveniente gravar com base em indicadores.
#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;
Essa estrutura pode ser enviada ao host pela função
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, (uint8_t *) &Desk, sizeof(Desk));
O primeiro parâmetro é um identificador USB, já criado em nosso cubo. Pode ser necessário incluir o arquivo necessário com o include, onde esse identificador é inicializado pela primeira vez e gravar
extern USBD_HandleTypeDef hUsbDeviceFS; para que você possa trabalhar com ele. O segundo parâmetro é um ponteiro para nossa estrutura e o terceiro é o tamanho da estrutura em bytes.
Depois de encher e piscar o controlador, você notará que algo USB está se movendo lentamente. Os dados do nosso painel não são atualizados rapidamente. Para ser rápido, nos arquivos usbd_customhid.h, você deve alterar #define CUSTOM_HID_EPIN_SIZE para o valor máximo 0x40, #define CUSTOM_HID_EPOUT_SIZE também defina 0x40. No arquivo usbd_customhid.c, localize comentários no descritor do terminal "/ * bInterval: Intervalo de pesquisa (20 ms) * /" e altere o byte do descritor para 0x01 para cada terminal, apenas duas vezes. O que corresponderá a uma troca de dados de 1 ms.
Deve ser algo assim. Dispositivo padrão sem instalar nenhum driverEm geral, a função de gerenciamento é um pouco entendida. É muito fácil de fazer e todos os botões e eixos já estão funcionando. Resta fazer as telas funcionarem. Fiz isso por cerca de seis meses e, durante meio ano, o painel acumula poeira em uma caixa comprida. Sem tempo. Por isso, decidi colocar o artigo nesta forma, caso contrário corre o risco de nem sair.
Nos monitores, tudo é igual aos eixos. Para eles, precisamos complementar o descritor HID do dispositivo, basta indicar que esses são displays e, em vez de aceitar os dados de entrada, o host enviará os dados de saída.
O identificador do dispositivo HID cresceu significativamente. Aqui, já apliquei os parâmetros de ID do relatório para não obstruir o buffer de transmissão / recebimento e os pontos finais com dados completos e distinguir que tipo de telegrama recebemos. O ID do relatório é um byte uint8_t com o valor que aparece no início do telegrama. O valor que definimos no descritor do dispositivo HID.
A saída é
processada na função
estática int8_t CUSTOM_HID_OutEvent_FS (uint8_t event_idx, uint8_t state) , que, por padrão, está localizada em 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)
Resta apenas escrever um programa em um PC que envie os relatórios necessários para orientar as telas. No entanto, para verificar o código MK,
é adequado um ótimo programa do ST:
USB HID Demonstrator . Permite enviar relatórios de um PC com qualquer conteúdo.
Teste de exibição de LEDNesta fase, eu terminei até agora. E não se sabe se vou começar de novo.
É jogado em simuladores mais interessantes do que com um teclado. Mas não tanto que houve um efeito direto de uau. O teclado também parece um painel de controle. Mas controlar os eixos do joystick é, no mínimo, incomum. Sinta-se como um astronauta. É verdade que é necessário um traje espacial para imersão completa.
Espero que você esteja interessado. Erros de digitação, imprecisões e delírios estão presentes. Quem quiser aprofundar o código pode ver
aqui .
Atenciosamente