Introdução ao mundo dos dispositivos USB usando o exemplo dos microcontroladores da Silicon Laboratories. Introdução ao mundo dos dispositivos USB usando o exemplo de microcontroladores da Silicon Laboratories Processando dados de entrada e gerando um pacote de saída

10.09.2021

Em desenvolvimento USB ou Bluetooth periféricos, muitas vezes é necessário implementar uma interface ESCONDIDO. Neste post faremos como um ser humano, e não como todo mundo...

Resumidamente: o que é HID e o que ele vem com ele?

Como o nome sugere, a classe de interfaces em questão foi projetada para implementar dispositivos de interação com o usuário. Mas devido à sua simplicidade e conveniência, muitas vezes funcionam com outros tipos de periféricos através desta interface. Do lado do dispositivo, a implementação do HID é muito simples: você não precisa quebrar a cabeça com protocolos de troca de dados de baixo nível, descrever relatórios e processar solicitações facilmente. E do lado do host, você pode trabalhar com dispositivos HID no espaço do usuário por meio de um driver padrão sistema operacional. É claro que o CDC ACM também permite a troca de dados com um dispositivo de maneira semelhante, mas essa classe é mais difícil de implementar em um dispositivo, é conveniente para organizar interação contínua em thread e é menos adequada para trabalhar em solicitação-resposta modo.

Leia mais: de onde crescem as pernas e como realmente funciona

HID é algo universal: a troca de dados é realizada através do envio e recebimento dos chamados relatórios (HID Reports), cada um dos quais devemos descrever de forma especial para que o driver do sistema operacional responsável por trabalhar com HID possa entender corretamente o interno estrutura dos nossos dados. Se implementarmos dispositivo padrão entrada, como teclado, mouse ou joystick, então analisando o conteúdo dos descritores de relatório do nosso dispositivo, o motorista entenderá como obter os dados que lhe interessam, sejam botões pressionados ou informações sobre o movimento do ponteiro. Mesmo que usemos o HID para alguns de nossos próprios propósitos e troquemos relatórios de nosso próprio programa, ainda seremos obrigados a descrever os relatórios adequadamente, de acordo com a especificação.

Descritores de relatório

Portanto, a formação desses mesmos descritores de relatórios é a parte mais difícil na implementação de periféricos HID. Não importa o que os desenvolvedores façam, tudo acaba dando errado. Alguns usam programas especiais(como o oficial Ferramenta descritora HID), que permitem criar descritores em interface gráfica, e a saída é um conjunto pronto de bytes. Outros usam conversores que convertem descrições de texto em código e vice-versa (por exemplo, hidrd). Tudo ficaria bem, mas o suporte adicional de tal código gerado por ferramentas de terceiros será significativamente complicado: a cada correção será necessário usar novamente o programa correspondente. Outros ainda pegam estupidamente um descritor pronto a partir de exemplos, ajustam o tamanho e a quantidade de dados para se adequar à sua tarefa e trocam pacotes em seu próprio formato que não corresponde ao conteúdo real do descritor. Mas esta abordagem também está repleta de muitos momentos ruins, que nem sempre são óbvios, como, em geral, qualquer outro desvio da letra da especificação.

Seguiremos o caminho direto: formaremos o descritor de forma clara e legível, utilizando macros do pré-processador C, de forma que seja familiar e conveniente para nós, especialistas em CSE. Esta abordagem não substitui a necessidade de se familiarizar com as especificações ESCONDIDO 1.11 para entender os princípios básicos de geração de descritores de relatórios, mas libera você da necessidade de conhecer os detalhes do formato binário. Em outras palavras, descreveremos os descritores aproximadamente como são descritos nos exemplos da especificação mencionada acima.

Princípios básicos

Aqueles que estão com preguiça de entrar nas especificações agora e se aprofundar na essência, leiam meu texto gratuito abaixo.

A descrição do relatório consiste nos chamados elementos ou pontos, cada um dos quais consiste em um cabeçalho e, opcionalmente, em um bloco de dados. Em geral os elementos podem ser curtos e longos, mas aqui descreveremos apenas os curtos, pois trabalharemos apenas com eles. O cabeçalho do elemento inclui um campo de tamanho de dados (2 bits), um campo de tipo (2 bits) e um campo de tag (4 bits) para um total de 8 bits. O campo de tamanho especifica o comprimento dos dados em bytes: 0, 1, 2 ou 4 bytes. O campo type especifica o tipo: principal, global, local. O campo tag especifica o próprio elemento. Existem muitos elementos diferentes e não iremos descrevê-los todos aqui; focaremos apenas em alguns dos principais ao longo do caminho.

Exemplo de descritor

Vamos supor que estamos desenvolvendo um dispositivo padrão como um mouse. Aqui está um exemplo de um descritor de relatório da especificação:

Página de uso (desktop genérico), uso (mouse), coleção (aplicativo), uso (ponteiro), coleção (física), página de uso (botões), uso mínimo (01), uso máximo (03), mínimo lógico (0) , Máximo lógico (1), Contagem de relatório (3), Tamanho do relatório (1), Entrada (dados, variável, absoluto), Contagem de relatório (1), Tamanho do relatório (5), Entrada (constante), Página de uso (Desktop genérico ), Uso (X), Uso (Y), Mínimo Lógico (-127), Máximo Lógico (127), Tamanho do Relatório (8), Contagem de Relatório (2), Entrada (Dados, Variável, Relativo), Coleta Final, Fim Coleção

O que vemos aqui? E vemos apenas nossos elementos, um elemento por linha. Cada elemento neste exemplo ocupa 2 ou 1 byte. O primeiro é o elemento global Usage Page, que descreve a finalidade do nosso dispositivo (Generic Desktop). Em seguida vem o elemento Usage local, que determina o tipo real de dispositivo (Mouse), dando assim uma dica ao driver do sistema operacional. Então, usando o elemento Collection principal, começa a descrição da coleção do tipo Application, e novamente há o elemento Usage Page, mas desta vez ele define a finalidade da coleção como um ponteiro (Ponteiro), assim todos os elementos subseqüentes sobem ao elemento no final da coleção se referirá ao ponteiro.

A seguir, inicia-se uma coleção do tipo Physical e dentro desta coleção é descrito o elemento de relatório Buttons. Os elementos locais Uso Mínimo e Máximo de Uso estão associados a um caso de uso específico, neste caso identificam o primeiro e o último botão do mouse. A seguir, os elementos globais Mínimo Lógico e Máximo Lógico definem os valores mínimo e máximo do estado desses botões. Os seguintes elementos globais Report Count e Report Size especificam o número de valores no relatório e o tamanho de cada valor em bits. O elemento Input principal finaliza a descrição dos botões e define os campos descritos como parte do descritor do relatório de entrada. Os seguintes sinalizadores são fornecidos entre parênteses: Dados - significa que os campos descritos devem ser tratados como dados e não como constantes, Variável - uma variável é descrita e não um array, Absoluto - os valores devem ser tratados como absolutos.

Muitas vezes é conveniente alinhar os dados por bytes ou palavras, mas nas descrições dos relatórios os tamanhos são especificados em bits, então o chamado preenchimento é adicionado para alinhamento. As próximas três linhas apenas declaram tal recuo após os bits de estado dos três botões do relatório com o número 1 e o tamanho de 5 bits, e para que o driver ignore esses bits, o flag Constant é usado no Elemento de entrada em vez de Dados.

A seguir descrevemos os campos de coordenadas do cursor de maneira semelhante. Como um mouse, diferentemente de um digitalizador, geralmente gera coordenadas relativas, Relativo é especificado no elemento Input em vez de Absoluto. E como essas coordenadas relativas podem ser positivas e negativas, os valores limites correspondentes são indicados no Mínimo Lógico e no Máximo Lógico de -127 a 127. São alocados até 8 bits para cada valor (Tamanho do Relatório) e um total de 2 valores (Contagem de Relatório). Uma dica ao driver sobre a ordem dos campos com valores de coordenadas é dada aqui usando elementos Usage.

Descrição do descritor em C

Portanto, nossa tarefa é apresentar a mesma descrição utilizando a linguagem C, com a qual o pré-processador C nos ajudará. Esta é a aparência do mesmo exemplo agora:

#incluir /* Definições de macro para nosso descritor de relatório HID DSL */ #include "hid_def.h" static const uint8_t hid_report_descriptor = ( HID_USAGE_PAGE (GENERIC_DESKTOP), HID_USAGE (MOUSE), HID_COLLECTION (APPLICATION), HID_USAGE (POINTER), HID_COLLECTION (PHYSICAL), HID_USAGE_PAGE (BOTÕES), HID_USAGE_MINIMUM (1, 1), HID_USAGE_MAXIMUM (1, 3), HID_LOGICAL_MINIMUM (1, 0), HID_LOGICAL_MAXIMUM (1, 1), HID_REPORT_COUNT (3), HID_REPORT_SIZE (1), HID_INPUT (DADOS, VARIÁVEIS, ABSOLUTOS ), Hid_report_count (1), hid_report_size (5), hid_input (constante), hid_usage_page (generic_desktop), hid_usage (x), hid_usage (y), hid_logical_minimum (1, -127), hid _Logical_maximum (1, 127), hid_report_size ( 8) , HID_REPORT_COUNT (2), HID_INPUT (DADOS, VARIÁVEIS, RELATIVOS), HID_END_COLLECTION (FÍSICO), HID_END_COLLECTION (APLICAÇÃO), );

Como você pode ver, a descrição é o mais próxima possível do exemplo da especificação, mas existem várias diferenças relacionadas à implementação de macros de pré-processador que descrevem elementos. Como o tamanho dos dados de alguns elementos não é fixo, precisávamos indicar de alguma forma esse tamanho. Portanto, o primeiro parâmetro para elementos que definem os limites mínimo/máximo é o tamanho do valor em bytes e o segundo é o próprio valor. As macros que definem os elementos principais Entrada, Saída, Recurso são projetadas de forma que possam aceitar qualquer número de sinalizadores como entrada, incluindo nenhum, portanto, os sinalizadores Dados e Absoluto não são realmente necessários, uma vez que são definidos por padrão.

Você deve ter notado que nossa macro HID_END_COLLECTION usa o tipo de coleção como argumento. Então, do ponto de vista do descritor, isso não faz muito sentido, foi feito simplesmente por conveniência, para que fique imediatamente claro qual coleção está sendo completada, mas tecnicamente você pode especificar os argumentos que quiser ou prescindir deles de forma alguma.

Juntar-se à festa

Não vou esconder, as definições de macro para nossa linguagem específica de domínio estão no arquivo oculto_def.h contém uma grande dose de magia do pré-processador que é definida em outro arquivo de cabeçalho macro.h.

Enviei este projeto para o repositório github:katyo/hid_def, então agora é a hora de começar a escrever descritores de relatórios HID de maneira humana.

Mbed OS é um código aberto sistema operacional para plataformas que usam microcontroladores Arm projetados especificamente para dispositivos de Internet das Coisas (IoT): dispositivos restritos e de baixa potência que precisam se conectar à Internet. O Mbed OS fornece uma camada de abstração para os microcontroladores em que é executado, para que os desenvolvedores possam se concentrar em escrever aplicativos C/C++ que chamam funcionalidades disponíveis em uma variedade de hardware. Os aplicativos Mbed OS podem ser reutilizados em qualquer plataforma compatível com Mbed.

Nesta página

Começando

Se você quiser mergulhar direto:

  • O código-fonte está disponível no GitHub e em nossa página de lançamento.

Código fonte e licenciamento

Lançamos o Mbed OS sob uma licença Apache 2.0, para que você possa usá-lo com segurança em projetos comerciais e pessoais. Para obter mais informações sobre licenciamento, consulte nossa documentação de licenciamento.

Diagrama de arquitetura

Esta é a arquitetura básica de uma placa Mbed:

Fundações do sistema operacional Mbed

Mbed OS usa uma camada de abstração de hardware (HAL) para suportar as partes mais comuns de um microcontrolador, como temporizadores. Essa base facilita a gravação de aplicativos em um conjunto comum de interfaces de programação de aplicativos (APIs); seu dispositivo inclui automaticamente as bibliotecas necessárias e suporte de driver para periféricos MCU padrão, como I2C, serial e SPI.

O HAL também serve como ponto de partida ao adicionar suporte para novos alvos ou recursos aos alvos existentes. Trabalhamos em estreita colaboração com nossos parceiros de silício para portar esses novos recursos para placas de desenvolvimento habilitadas para Mbed.

O Mbed OS possui um núcleo RTOS, portanto, suporta execução de software determinística, multithread e em tempo real. As primitivas RTOS estão sempre disponíveis, permitindo que drivers e aplicações dependam de threads, semáforos, mutexes e outros recursos do RTOS.

A estrutura do Mbed OS permite combinar aplicativos e sistemas de armazenamento. Em outras palavras, onde as opções de armazenamento em nível de bloco variam e dependem do aplicativo, você pode escolher o sistema de arquivos que melhor se adapta ao seu dispositivo IoT. O sistema de arquivos FAT – apoiado por um cartão SD – oferece compatibilidade com outros sistemas operacionais, como Windows, Mac OS ou Linux. Quando a alta confiabilidade e a recuperação de falhas de energia são importantes, faz sentido usar nosso sistema de arquivos incorporado, apoiado por um chip flash (Q)SPI NOR.

Por fim, o Mbed OS implementa a camada de retargeting e a integração do processo de inicialização de cada conjunto de ferramentas suportado para você, de forma que o desenvolvimento de aplicativos seja semelhante ao desenvolvimento em C ou C++ para qualquer outro sistema operacional.

Conectividade

Arm trabalha com seus parceiros para habilitar Bluetooth Low Energy, NFC, RFID, LoRa, 6LoWPAN-ND, Thread, Wi-SUN, Ethernet, Wi-Fi, IoT celular e móvel (LPWA) em dispositivos e arquiteturas de sistema executando Mbed OS. Mbed OS oferece um núcleo estável de tecnologias de conectividade existentes. Ao mesmo tempo, adiciona recursos modernos em lançamentos trimestrais de recursos, mantendo você informado sobre as tendências do setor para que você possa fazer a transição para soluções novas e inovadoras que geram valor comercial.

As pilhas de rede e conectividade são flexíveis o suficiente para atender às necessidades dos designs de dispositivos IoT mais exigentes, seja uma combinação de um microcontrolador de chip único e rádio ou vários chips conectados através de barramentos seriais. Os projetistas de sistemas podem confiar em nossas pilhas de conectividade certificadas, como nossa pilha Thread certificada, devido à sua maturidade, interoperabilidade e componentes validados.

O Mbed OS oferece suporte total à nossa plataforma Pelion IoT, para que você possa gerenciar seus dispositivos implantados e seus dados. Juntos, o Mbed OS e o Pelion formam um ecossistema coerente que atende às necessidades da maioria dos projetos prontos para produção.

Segurança

A Plataforma Pelion IoT possui segurança integrada em todos os níveis, enfatizando tanto a proteção contra violações quanto a mitigação de suas consequências. Juntamente com serviços de nuvem reforçados, pilhas de comunicação robustas e atualizações seguras de firmware, o Mbed oferece dois blocos de construção incorporados específicos de segurança: Arm Mbed TLS e um Secure Partition Manager (SPM) que atende às melhores práticas do setor como parte da arquitetura de segurança de plataforma da Arm. Mbed TLS protege canais de comunicação entre um dispositivo e gateway ou servidor, e o uso de um gerenciador de partição seguro e domínios de segurança isolados para serviços de sistema confiáveis ​​reduz a superfície de ataque. Tudo junto, isso fornece um modelo exclusivo de segurança do chip para a nuvem, contando com os recursos de baixo nível que os parceiros do ecossistema de silício da Arm fornecem para proteger os dados e a identidade dos dispositivos conectados à nuvem.

Nossa abordagem à segurança é aproveitar protocolos, cifras e conjuntos de criptografia padrão da indústria de última geração seguindo o recomendações do NIST e outras organizações relacionadas. Isto dá-nos acesso aos trabalhos mais recentes da comunidade global de investigação em segurança, em vez de um recurso interno limitado. Verificamos regularmente os resultados desses esforços com revisões de código, exercícios de penetração e outros métodos.

Atualização remota de firmware

O Mbed OS oferece uma integração perfeita com os serviços de atualização de gerenciamento de dispositivos Pelion, para que você possa atualizar o aplicativo do seu dispositivo ou a versão do Mbed OS. A ferramenta de desenvolvimento Arm Mbed CLI pode criar cargas de atualização, gerar seus manifestos e enviá-los para o seu dispositivo de desenvolvimento com dois comandos. Se quiser atualizar grupos de dispositivos, você pode gerar e fazer upload de seu manifesto e carga útil para o Portal de gerenciamento de dispositivos e executar uma campanha padrão a partir daí. O on-line O Compiler também oferece uma integração rápida para fluxos de desenvolvimento.

Hardware

Arm, seus parceiros e a comunidade de desenvolvedores Arm Mbed trabalham juntos para desenvolver o projeto Mbed OS. Este ecossistema próspero significa que o Mbed OS inclui drivers para vários hardwares diferentes, para que você possa se concentrar em um código de aplicativo limpo e portátil.

Em termos gerais, o hardware que você pode ver em nosso site é de três tipos:

  • Módulos: inclui um microcontrolador, conectividade centrada em IoT e memória integrada necessária. Eles são ideais para projetar produtos IoT, desde a prototipagem até a produção em massa. Os módulos habilitados para Mbed têm suporte total para Mbed OS com todos os drivers de conectividade disponíveis.
  • Pranchas: placas de desenvolvimento são uma maneira barata de começar a desenvolver com Mbed OS e outros componentes.
  • Componentes: o banco de dados de componentes hospeda bibliotecas reutilizáveis ​​para diferentes serviços de hardware, middleware e IoT que você pode usar com microcontroladores Arm. Esses componentes podem ser usados ​​como blocos de construção para o desenvolvimento rápido de protótipos e produtos.

Ferramentas

O conjunto de produtos Mbed inclui as ferramentas necessárias para trabalhar com o Mbed OS, independentemente do seu nível de habilidade. Se você é um desenvolvedor experiente com configuração de desktop, pode preferir trabalhar offline com Arm Mbed CLI, nossa ferramenta de linha de comando baseada em Python. Você pode usar o Mbed CLI com um dos três conjuntos de ferramentas suportados: Arm Compiler 6, GCC e IAR. Você também pode exportar projetos para outros IDEs, como Keil MDK. O Mbed OS inclui código de integração para cada conjunto de ferramentas suportado para torná-lo seguro para threads.

Os dispositivos dos Laboratórios de Silício não são muito populares nos círculos amadores; eles estão longe de carros-chefe como o Atmel. Porém, eles também possuem microcontroladores das linhas principais do pacote TQFP, bastante acessíveis a meros mortais, e kits iniciais USB ToolStick (que foram mencionados recentemente no hub). Eu mesmo comecei meu conhecimento com a tecnologia de microprocessadores trabalhando com “silabs”, e com bastante sucesso.
Neste artigo contarei como você pode organizar a comunicação entre um computador e um MK usando uma interface USB e como Silabs tentou simplificar isso para o desenvolvedor.
Como teste usaremos a placa C8051F320DK, com um microcontrolador da série F32x, respectivamente, com suporte a hardware USB, e o ambiente de desenvolvimento uVision4 da Keil.

Antes de começar a implementar a comunicação USB, você precisa decidir sobre alguns aspectos básicos do protocolo: que lugar o dispositivo ocupa na topologia (host ou escravo) e qual será a natureza da informação transmitida pela interface.

A arquitetura USB permite quatro tipos básicos de transferência de dados:

  • Mensagens de controle ( transferências de controle) – usado para configurar dispositivos durante sua conexão e para controlar dispositivos durante a operação. O protocolo fornece entrega garantida de dados.
  • Transferência de matrizes de dados ( transferências de dados em massa) são transferências sem quaisquer obrigações relativas a prazos de entrega e velocidades de transmissão. As transferências de array podem ocupar toda a largura de banda do barramento, livre de outros tipos de transferência. Essas marchas têm prioridade mais baixa e podem ser suspensas quando o ônibus estiver muito carregado. A entrega é garantida - em caso de erro acidental, é realizada uma repetição. As transferências de array são apropriadas para troca de dados com impressoras, scanners, dispositivos de armazenamento, etc.
  • Interrupções ( interromper transferências) – transmissões curtas que são de natureza espontânea e não devem ser atendidas mais lentamente do que o exigido pelo dispositivo.
    O limite de tempo de serviço é definido na faixa de 10 a 255 ms para
    baixo, 1-255 ms para velocidade máxima, sobre alta velocidade Você também pode solicitar 125 μs. Em caso de erros de troca aleatórios, é realizada uma repetição. As interrupções são utilizadas, por exemplo, ao inserir caracteres do teclado ou para transmitir uma mensagem sobre o movimento do mouse.
  • Transmissões isócronas ( transferências isócronas) – transmissões contínuas em tempo real, ocupando uma parte pré-acordada largura de banda pneus com prazo de entrega garantido. Eles permitem organizar um canal com largura de banda de 1,023 MB/s (ou dois de 0,5 MB/s cada) em velocidade máxima, ocupando 70% da largura de banda disponível (o restante pode ser preenchido com canais de menor capacidade). Em alta velocidade, o endpoint pode receber um link de até 24 MB/s (192 Mbps). Se um erro for detectado, os dados isócronos não serão repetidos - os pacotes inválidos serão ignorados. As transferências isócronas são necessárias para dispositivos de streaming: câmeras de vídeo, dispositivos de áudio digital (alto-falantes USB, microfone), dispositivos de reprodução e gravação de áudio e vídeo (CD e DVD).
Se o MK estiver conectado a um computador, o controlador obviamente será um dispositivo escravo.

Criando um dispositivo joystick compatível com USB HID

O tipo de dispositivo USB mais comum e mais fácil de implementar é o HID (Human Interface Devices). O tipo de transmissão utilizado, padrão para tais dispositivos, é a interrupção. Representantes típicos desta classe são teclados USB, mouses, joysticks, painéis de configurações de monitor, leitores de código de barras, leitores de cartão, etc.
As vantagens dos dispositivos HID são:
  • facilidade de implementação;
  • código compacto;
  • Suporte para Windows (não são necessários drivers adicionais).
Então, vamos implementar o mais simples manipulador de joystick. Por exemplo, precisaremos de um manípulo de gás com dois (ou mais) botões para o mecanismo de combate (!), que estamos montando na garagem. A placa de demonstração C8051F320DK possui um resistor variável e 2 botões - o suficiente para o mínimo.

Silabovtsy fornece um exemplo de firmware de microcontrolador, que emula um mouse USB com interface HID. Este exemplo é suficiente para implementar de forma rápida e fácil a maioria das interfaces de interação humana. Com isso, no exemplo tomado como base, é necessário retrabalhar:

  1. Configuração do descritor de dispositivo HID;
  2. procedimentos de transferência de dados;
  3. Identificador de nome de dispositivo HID.
Vamos começar com o descritor do dispositivo
Precisamos de um descritor no seguinte formato:
código const hid_report_descriptor HIDREPORTDESC =
{
0x05, 0x01, // USAGE_PAGE (Área de Trabalho Genérica)

0x09, 0x04, // USO (Joystick)
0xa1, 0x01, // COLEÇÃO (aplicativo)
0x05, 0x02, // USAGE_PAGE (controles de simulação)
0x09, 0xbb, // USO (acelerador)
0x15, 0x00, // MÍNIMO LÓGICO (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, //REPORT_SIZE (8)
0x95, 0x01, //REPORT_COUNT (1)

0x05, 0x09, // USAGE_PAGE (botão)
0x19, 0x01, //USAGE_MINIMUM (Botão 1)
0x29, 0x02, //USAGE_MAXIMUM (Botão 2)
0x15, 0x00, // MÍNIMO LÓGICO (0)
0x25, 0x01, // MÁXIMO LÓGICO (1)
0x75, 0x01, //REPORT_SIZE (1)
0x95, 0x08, //REPORT_COUNT (8)
0x55, 0x00, //UNIT_EXPONENT (0)
0x65, 0x00, // UNIDADE (Nenhum)
0x81, 0x02, //ENTRADA (Dados,Var,Abs)
0xc0 //END_COLLECTION
}
Agora vamos dar uma olhada mais de perto no que é o quê. A parte mais importante na descrição de um dispositivo futuro são os tipos de dados. A seção deve ser descrita Controles de simulação(simulação de um elemento de controle), que apenas possui Acelerador(acelerador), para isso indicamos:
  • faixa de valores em que irá operar Acelerador– LOGICAL_MINIMUM(0) e LOGICAL_MAXIMUM(255),
  • defina o tamanho deste intervalo (um byte) – REPORT_SIZE (8) e
  • número de controles deste tipo– RELATÓRIO_COUNT (1).
É uma história semelhante com botões (USAGE_PAGE ( Botão)):
  • intervalo de valores - LOGICAL_MINIMUM(0) e LOGICAL_MAXIMUM(1);
  • tamanho do intervalo (um bit) - REPORT_SIZE (1);
  • existem mais de um botão, então aqui é necessário usar um campo de comprimento de bytes, que significa REPORT_COUNT (8);
Tudo isso é necessário para o sistema operacional; agora ele saberá como lidar com os 2 bytes que receberá do controlador, utilizando o descritor como chave de descriptografia.
Sim, e também, em .h existem estas linhas, imediatamente antes da declaração hid_report_descriptor:
#define HID_REPORT_DESCRIPTOR_SIZE 0x002C
#define HID_REPORT_DESCRIPTOR_SIZE_LE 0x2C00 //PEQUENO ENDIAN

É importante aqui que o tamanho do descritor seja definido após a compilação do próprio descritor e deve ser especificado para que o controlador seja reconhecido pelo computador.

Para simplificar a tarefa de criação de um descritor, você pode usar o programa localizado em www.usb.org (HID Descriptor Tool). O programa vem com exemplos de configurações de alguns dispositivos HID, que você pode ajustar para se adequar à sua tarefa ou criar seu próprio dispositivo HID.
Isso conclui a descrição do joystick e você precisa preparar os dados para transferência para o PC.

Procedimentos de transferência de dados
Encontramos o seguinte código no exemplo:
void IN_Report(void)(

IN_PACKET = VETOR;
IN_PACKET = BOTÕES;

// aponta o ponteiro IN_BUFFER para o pacote de dados e define
// Comprimento IN_BUFFER para transmitir o tamanho correto do relatório
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Comprimento = 2;
}

Este procedimento envolve a compilação de um pacote enviado, que é então transmitido através de um ponteiro complicado (na verdade, é apenas uma estrutura de um ponteiro e seu comprimento) e transmitido pelo nosso dispositivo. O principal é compor o pacote com cuidado, como nos sugere o comentário, e então farão tudo com ele sem a nossa participação.
Agora vou contar como e onde obtemos as variáveis ​​VECTOR e BUTTONS (ambas, aliás, são do tipo unsigned char com tamanho de byte).
A variável global VECTOR recebe valores do ADC quando ocorre uma interrupção:
void ADC_Conver_ISR (void) interrupção 10
{
AD0INT = 0;

// indicação de operação do ADC
se(VETOR!= ADC0H)
LED = 1;
outro
LED = 0;

VETOR = ADC0H;
}

A variável global BUTTONS altera seu valor de forma semelhante com base no pressionamento de botão. Os botões são controlados por uma interrupção do temporizador. Defina o cronômetro de acordo com suas preferências pessoais.
void Timer2_ISR (void) interrupção 5
{
P2 &= ~Led_2;

If ((P2 & Sw1)==0) // Verifica se o botão nº 1 está pressionado
{
// pressionado
BOTÕES = BOTÕES | (1<<0);
LED2 = 1;
}
outro
{
//não pressionado
BOTÕES = BOTÕES & 0xFE;
}

If ((P2 & Sw2)==0) // Verifica se o botão nº 2 está pressionado
{
// pressionado
BOTÕES = BOTÕES | (1<<1);
LED2 = 1;
}
outro
{
//não pressionado
BOTÕES = BOTÕES & 0xFD;
}
TF2H = 0; // Limpa flag de interrupção do Timer2
}

Descritor de nome de dispositivo HID
Por fim, podemos ajustar os dados da string para que o dispositivo tenha o nome que desejamos (no meu exemplo, “JOYSTICK-HABR”).
Procurando por um descritor de string String2Desc, reescrever
#define STR2LEN sizeof ("JOYSTICK-HABR") * 2

Código const unsigned char String2Desc =
{
STR2LEN, 0x03,
"J", 0,
"O", 0,
"S", 0,
"S", 0,
"T", 0,
"Eu", 0,
"C", 0,
"K", 0,
"-", 0,
"H", 0,
"A", 0,
"B", 0,
"R", 0,
};

Identificação de dispositivo HID
Após compilar o projeto e programar o microcontrolador, você pode conectar o dispositivo à porta USB. O host determina que o dispositivo pertence à classe HID e transfere o controle do dispositivo para o driver apropriado.

Agora no Windows vamos ao Painel de Controle->Dispositivos de Jogo e vemos nosso passageiro lá. Observamos as propriedades e verificamos a funcionalidade.

A baixa velocidade de transmissão é a principal limitação da opção de design do dispositivo HID. A taxa máxima de transferência de dados possível com tal organização de troca é de 64 Kbit/s. Este valor, comparado aos 12 Mbit/s de velocidade total do barramento USB, parece uma desvantagem da tecnologia HID na escolha de uma implementação USB específica. Porém, para muitas tarefas de comunicação, a velocidade especificada é suficiente e a arquitetura HID como ferramenta especializada ocupa o seu devido lugar entre os métodos de organização da troca de dados.

De modo geral, os dispositivos HID são fáceis de implementar em quase todos os MCU habilitados para USB. Como regra, um exemplo prático dos desenvolvedores é suficiente; ajustando-o, você pode obter qualquer funcionalidade necessária.

Criando um dispositivo USB completo usando o kit de ferramentas Silabs USBXpress

Mas chega um momento em que você precisa usar seu próprio protocolo para trabalhar com o dispositivo no MK. Ao mesmo tempo, gostaria de transferir muitos dados em alta velocidade, e fazer tudo isso usando meu laptop, que tem muito USB e nenhum COM, e até seu aparelho não deve ser maior que uma caixa de fósforos, e ser montado em uma placa USB-UART em um chip FT232RL não é possível.
Foi então que os caras da Silabs decidiram facilitar a vida de todos e mostrar “o caminho para o futuro”, sem quebrar os dentes para escrever sua própria lenha e firmware.
Kit de desenvolvimento USBXpressé uma solução completa para MCU e host (PC), proporcionando trabalho simples com o protocolo USB usando uma API de alto nível para ambos os lados. Nenhum conhecimento especial do protocolo USB ou gravação de driver é necessário. Isto é o que os silabovitas escrevem em seu guia.


Falando em Guia do Programador: ocupando apenas 30 páginas, é extremamente simples e inteligível. Pessoalmente não gosto dos exemplos, muitas vezes há lugares muito tortos e é melhor nem olhar para os programas de PC, eles são extremamente ilegíveis. .
USBXpress DK está disponível para microcontroladores das linhas C8051F32x, C8051F34x e CP210x (USB-to-UART Bridge Controller). A biblioteca USBXpress inclui uma biblioteca de baixo nível, drivers USB para PCs e uma DLL para desenvolvimento de aplicativos de alto nível. E, claro, um conjunto de documentação e exemplos.
A biblioteca implementa a transferência de dados apenas no modo BULK. Ao utilizar todas as funções da biblioteca, sua implementação ocupará apenas 3 KB de memória Flash do microcontrolador.
Firmware
Vejamos um exemplo mais ou menos simples e compreensível, semelhante em funcionalidade ao exemplo HID anterior. Não entraremos no aplicativo para PC; tudo ficará claro depois de terminarmos o firmware do MK.
Então, a essência do exemplo TestPanel: fazemos leituras do microcontrolador ADC (Potenciômetro) e do termômetro embutido ( Temperatura), bem como ao pressionar botões ( Switch1Estado E Switch2Estado), e nós mesmos podemos piscar os LEDs ( Led1 E Led2).
Agora, as etapas obrigatórias e os pontos sutis que consideraremos:
  1. Escrevendo um descritor USB;
  2. Inicialização do dispositivo e USB integrado;
  3. Processamento de dados recebidos e geração de um pacote de saída;
  4. Tratamento de interrupções.
Mas primeiro, ao criar um projeto, não esqueça de incluir um arquivo de cabeçalho nele USB_API.h e a própria biblioteca USBX_F320_1.lib.
Escrevendo um descritor USB
Ao contrário do HID com sua estrutura habilmente formalizada, tudo é simples aqui
código const UINT USB_VID = 0x10C4;
código const UINT USB_PID = 0xEA61;
código const BYTE USB_MfrStr = (0x1A,0x03,"S",0,"i",0,"l",0,"a,0,"b,0,"s,0);
código const BYTE USB_ProductStr = (0x10,0x03,"U",0,"S",0,"B",0,"X",0,"_",0,"A",0,"P", 0);
código const BYTE USB_SerialStr = (0x0A,0x03,"H",0,"A",0,"B",0,"R",0);
código const BYTE USB_MaxPower = 15;
código const BYTE USB_PwAttributes = 0x80;
código const UINT USB_bcdDevice = 0x0100;

Acho que tudo fica claro com VID, PID e nomes, além disso você também pode definir a corrente máxima com o parâmetro MaxPower (max.current = _MaxPower*2), PwAttributes - o parâmetro responsável pelo despertar remoto do host, e bcdDevice – o número da versão do dispositivo.

A nuance da inicialização do dispositivo e USB integrado
Agora vamos começar com a função principal em si, na qual o MK receberá e transmitirá dados incansavelmente.
vazio principal(vazio)
{
PCA0MD &= ~0x40; //Desativa o temporizador Watchdog
USB_Clock_Start(); // Inicia o relógio USB *antes* de chamar USB_Init
USB_Init(USB_VID,USB_PID,USB_MfrStr,USB_ProductStr,USB_SerialStr,USB_MaxPower,USB_PwAttributes,USB_bcdDevice);

Inicializar();
USB_Int_Enable();
...

Aqui, como exige o comentário, primeiro é necessário inicializar o gerador de clock para USB antes de sua inicialização propriamente dita, e só então realizar as demais operações de inicialização para o MK - Initialize(); - que configura as portas, timer e ADC; então habilitamos as interrupções USB.

Processando dados de entrada e gerando um pacote de saída
Aqui chegamos ao mais importante
//... continuação do principal
enquanto (1)
{
if (Out_Packet == 1) Led1 = 1;
senão Led1 = 0;
if (Out_Packet == 1) Led2 = 1;
senão Led2 = 0;

In_Packet = Switch1State;
In_Packet = Switch2State;
In_Packet = Potenciômetro;
In_Packet = Temperatura;
}
//fim do principal
}

Out_Packet – pacote recebido do host;
In_Packet - pacote enviado ao host;
A questão é clara, o MK atualiza constantemente o pacote enviado e lê o status do pacote recebido.

Tratamento de interrupções
Agora, em poucas palavras, sobre onde obtemos os valores no pacote enviado. Como no exemplo do HID, os estados dos botões são obtidos a partir de interrupções do temporizador, e os valores do ADC e do termômetro são obtidos a partir de interrupções do ADC.
Aqui está um ponto sutil - ao inicializar o ADC, nós o configuramos para que a conversão de valores ocorra quando o temporizador estourar (o mesmo que usamos para botões), e a própria interrupção do ADC ocorra quando a conversão for concluída . E aqui, além de receber os valores do conversor ao final do procedimento, chamamos a função API
Block_Write(In_Packet, 8)
que envia os dados coletados para o computador.
O recebimento de comandos do computador ocorre no procedimento de processamento de interrupção USB:
void USB_API_TEST_ISR (void) interrupção 16
{
BYTE INTVAL = Get_Interrupt_Source();

Se (INTVAL & RX_COMPLETE)
{
Block_Read(Out_Packet, 8);
}

Se (INTVAL & DEV_SUSPEND)
{
Suspender_Dispositivo();
}

Se (INTVAL & DEV_CONFIGURED)
{
Inicializar();
}
}

Este ponto é descrito detalhadamente no Guia do Programador. O ponto é que a função API Get_Interrupt_Source() é chamada, retornando o código do motivo da interrupção da API. Em seguida, o código é analisado e a ação necessária é executada.

Programas no PC
Não vou desmontar o programa de computador. A equipe do Silab forneceu exemplos em Visual Basic e C, mas sem sequer olhar o código fonte, conectar a biblioteca em seu ambiente de desenvolvimento e ler algumas páginas sobre as funções não deve causar nenhuma complexidade.
Então usarei o programa de exemplo já compilado.

Então, compilamos o projeto para o MK, atualizamos, instalamos drivers universais para USBXpress e conectamos a placa de depuração. O sistema detectará um novo dispositivo e instalará drivers para ele.
Após a instalação, vamos ver o que está acontecendo no Gerenciador de Dispositivos do Windows:


Agora vamos executar o programa:


Vemos que ela encontrou o dispositivo corretamente.


É isso, agora você pode apertar os botões, piscar os diodos, aquecer o MK com as mãos e ver como a temperatura sobe.

Conclusão

Em geral, criar um dispositivo USB usando bibliotecas USBXpress acabou sendo um processo mais rápido e transparente do que usar a arquitetura HID. E a velocidade será definitivamente maior. O ponto mais fraco é que a biblioteca está fechada, sendo impossível saber o quão confiável é esta solução, além disso, apenas o modo de transferência de dados BULK está disponível;
Fontes utilizadas e úteis:
  1. Guk M., interfaces de hardware para PC. Enciclopédia. - São Petersburgo: Peter, 2002. - 528 p.
  2. Kurilin A.I. Microcontroladores Silicon Labs com interface USB. Revista "Componentes Eletrônicos" nº 5, 2007

Ao controle

As informações sobre um dispositivo USB são chamadas de Descritores e são armazenadas em sua ROM. A interface do descritor pode definir um dispositivo como pertencente a um número finito de classes. O dispositivo de classe HID usa o driver apropriado para recuperar e rotear todos os dados.

O descritor de dispositivo determina quais outros descritores de classe estão presentes. Por exemplo, Descritor de Relatório e Descritor Físico.

A estrutura dos descritores é a seguinte:

O descritor de dispositivo e configuração é um por dispositivo. Pode haver vários descritores de interface (por exemplo, um MFP pode ter uma interface de impressora, uma interface de scanner, etc.).

O descritor de relatório descreve cada dado que o dispositivo gera e o tamanho dos dados que realmente são alterados. Por exemplo, são definidos elementos que descrevem a posição ou os estados de um botão.

O descritor de relatório é carregado e processado pelo driver de classe HID assim que o dispositivo é detectado. Protocolos para dispositivos novos e existentes são criados misturando dados em um descritor de relatório.

Os dispositivos HID são divididos em classes (teclado, mouse, joystick, etc.). Isso permite unificar descritores de relatório. Entretanto, algumas classes podem conter subclasses, por exemplo, uma subclasse de dispositivo de inicialização.

Se o dispositivo puder ser usado no BIOS, ele será designado como inicializável usando o parâmetro

bInterfaceSubClass

0 - sem subclasse (dispositivo regular);

1 - dispositivo de inicialização;

2-255 - reservado.

O parâmetro bInterfaceProtocol só faz sentido se o dispositivo for declarado inicializável.

1 - teclado;

3-255 - reservado.

É necessário determinar se um dispositivo pertence à classe HID não pelos parâmetros bDeviceClass e bDeviceSubClass, mas pelos parâmetros bInterfaceClass e bInterfaceSubClass.

Os descritores do relatório consistem em elementos de informação (Item).

O elemento pode incluir um elemento de dados adicional. O tamanho de um bloco de dados é determinado pelo seu tipo básico (curto e longo). Resumindo, o volume pode ser 0, 1, 2 ou 4 bytes. O elemento longo tem um valor de bSize = 2.

O driver HID analisa em ordem linear todo o relatório do host, procurando elementos de acordo com o descritor do relatório e armazenando-os na tabela de elementos.

Usos

Estas são as partes do descritor do relatório que definem o que deve ser feito com os dados (por exemplo, entrada x, y, z).

Orientação HID

Por padrão, a direção do movimento é da esquerda para a direita (X), de cima para baixo (Y) e de muito mais perto (Z).

É possível transmitir valores fora dos limites permitidos, caso em que o host irá ignorá-los e os valores atuais não serão alterados. Esses são os chamados valores NULL. Recomenda-se usar 0 como NULL, ou seja, O intervalo de coordenadas aceitáveis ​​começa em 1.

Descritor HID

Define o comprimento e o tipo dos descritores escravos do dispositivo. Consiste nas seguintes partes:

Mais uma vez, um relatório é um pacote de dados do ponto de vista HID. Um descritor de relatório é um descritor da estrutura de um determinado pacote. Com base nele, o host processa relatórios do dispositivo e analisa o conteúdo de cada pacote. O descritor do relatório é condicionalmente dividido em blocos. A primeira parte do bloco contém três campos: tipo de elemento, tag de elemento e tamanho do elemento. Juntos, esses campos determinam quais informações o bloco contém.

Aqui estão três tipos de elementos: Maain, Global, Local. Por exemplo, as tags a seguir correspondem ao tipo Principal:

Entrada - refere-se aos dados do dispositivo, como posição do cursor, estado do botão, etc.

Saída - define os dados a serem enviados ao dispositivo a partir do host.

Recurso - descreve uma configuração de dispositivo que pode ser enviada ao dispositivo.

Coleção - um agrupamento semântico de elementos de entrada, saída e recurso.

Fim da Coleção - indica o fim de uma coleção de elementos.

A tag Main de cada elemento determina o tamanho dos dados retornados por um controle específico e determina se os dados são absolutos ou relativos, e assim por diante.

Os tipos Local e Global definem os valores mínimo e máximo dos dados e assim por diante.

Os elementos locais descrevem apenas os campos de dados definidos pelo próximo elemento Main. Os elementos globais tornam-se os atributos padrão para todos os campos de dados subsequentes nesse descritor.

Exemplo:

Contagem de relatórios (2)

Contagem de relatórios (2)

Report Size(3) especifica o tamanho do elemento em bits (neste caso 3);

Report Count (2) determina o número de tais elementos.

Se pudermos conter e transmitir dados sobre três teclas pressionadas simultaneamente, o relatório será o seguinte:

Tamanho do relatório (8),

Reportar moeda(3),

Se o relatório indicar o estado de 8 botões, ficará assim:

Tamanho do relatório (1),

Relatório Moeda (8),

Um descritor de relatório pode conter vários elementos principais. Cada um dos seguintes elementos de descrição de dados de controle é obrigatório (os outros são opcionais):

  • Entrada (saída ou recurso)
  • Uso
  • Página de uso
  • Mínimo Lógico
  • Máximo Lógico
  • Tamanho do relatório
  • Contagem de relatórios

Exemplo baseado em um mouse de 3 botões:

Página de uso (Desktop genérico), página de uso geral

Uso (Rato),

Coleção (Aplicativo), ;mouse tipo aberto

Uso (ponteiro),

Coleção (Física), ;coleção de índice aberto

Página de uso (botões),

Mínimo de uso (1),

Máximo de uso (3),

Mínimo Lógico (0),

Máximo Lógico (1), ;campos retornam dados de 0 a 1

Contagem de relatórios (3),

Tamanho do relatório (1), ;crie 3 campos de um bit (botões 1, 2 e 3)

Entrada (Dados, Variável, Absoluto), ;cria um campo para relatórios de entrada

Contagem de relatórios (1),

Tamanho do relatório (5), ;crie um campo constante de 5 bits

Input (Constant), ;adiciona um campo ao relatório recebido

Página de uso (desktop genérico),

Uso (X),

Uso (Y),

Mínimo Lógico (-127),

Máximo Lógico (127), ;campo retorna valores de -127 a 127

Tamanho do relatório (8),

Report Count (2), ;crie 2 campos de 8 bits (X e Y)

Entrada (Dados, Variável, Relativo), ;adiciona campos ao relatório recebido

Finalizar coleção, ;fechar coleção de ponteiros

Finalizar coleção; fechar coleção de mouse

A coleção abre um conjunto de dados:

  • Físico 0x00
  • Aplicativo 0x01
  • Lógico 0x02
  • Relatório 0x03
  • Matriz nomeada 0x04
  • Chave de uso 0x05
  • Uso 0x06
  • Reservado 0x07 - 0x7F - para uso futuro
  • Reservado 0x80 - 0xFF - para fornecedor

Todos os elementos contêm um prefixo de 1 byte que identifica o tipo subjacente do elemento. HID define 2 formatos de elementos principais:

Curtos de 1 a 5 bytes de comprimento total, usados ​​para os elementos que ocorrem com mais frequência.

Longo de 3 a 258 bytes, usado para elementos que requerem grandes quantidades de dados.

Os formatos curto e longo contêm o tamanho, o tipo e a tag do elemento no primeiro byte.

Formato curto

Formato longo

Mínimo Lógico e Máximo Lógico

LMin e LMax vinculam os valores retornados pelo dispositivo, e Mínimo Físico e Máximo Físico dão significado a esses limites, permitindo que os valores sejam escalonados. Por exemplo, um termômetro tem graus lógicos de 0 a 999, mas graus físicos de 32 a 212 graus.

Vejamos o exemplo de um mouse com sensibilidade de 400dpi:

Portanto, a fórmula para calcular a proporção deve ser

(127-(-127)) / ((3175-(-3175)) * 10^-4) = 400 dpi

Consultas padrão

A classe HID usa solicitações Get_Descriptor padrão. A solicitação Get_Descriptor(Configuration) retorna o descritor de configuração, todos os descritores de interface, descritores de endpoint e todos os descritores HID para cada interface. Não deve retornar um identificador de String, relate um identificador.

Portanto a ordem deverá ser a seguinte:

1.Get_Descriptor

Estruturas:

Tabela que define o Tipo de Descritor (byte alto de wValue na solicitação Get_Descriptor):

2. Conjunto_Descritor

Estrutura:

3. Consultas específicas de classe

Estrutura:

Valores bRequest disponíveis:

4. Obter_Relatório

mValue determina o tipo de relatório no byte alto e o ID do relatório no byte baixo. O ID do relatório será definido como 0 se não for usado. O tipo de relatório é definido da seguinte forma:

5.Set_Relatório

O significado dos campos é semelhante ao da solicitação Get_Report.

6.Get_Idle

Lê a porcentagem atual de inatividade do sistema para cada relatório de entrada.

7.Set_Idle

Esta solicitação é necessária para limitar a taxa de pesquisa para interrupções de endpoint. É também o motivo do NAK para todas as pesquisas de interrupção no terminal enquanto o valor atual permanece inalterado. Se não houver alteração, a votação continuará até atingir NAK.

8. Obter_Protocolo

A consulta lê qual protocolo está ativo no momento.

Suportado por dispositivos de classe de inicialização.

9.Set_Protocolo

Alterna entre protocolo de inicialização e protocolo de relatório. Quando inicializados, todos os dispositivos usam o protocolo de relatório por padrão.

Hoje em dia, a maioria dos computadores modernos não possui portas seriais e paralelas, que antes eram usadas para jogos. O uso de USB está se tornando cada vez mais popular entre os entusiastas. Infelizmente, o USB não é o protocolo mais fácil e muitas vezes é totalmente intimidante. Porém, existem diversas soluções no mercado que facilitam a implementação de dispositivos USB.

Este projeto analisa o uso de um microcontrolador e compilador PIC habilitado para USB para converter um joystick de porta de jogo antigo para que possa ser usado via USB. Um dos benefícios do compilador mikroC é a criação de bibliotecas USB HID que facilitam a criação de um dispositivo USB HID. Ao escrever código USB usando o compilador mikroC, o dispositivo USB produzido é um verdadeiro dispositivo HID que pode ser usado para transmitir e receber dados do microcontrolador PIC. Porém, é possível modificar o descritor USB gerado pelo mikroC para que um dispositivo USB HID específico possa ser obtido, como teclado, mouse, joystick ou tablet de entrada gráfica.

Compilador

Este projeto usa o compilador mikroC v8. Os métodos usados ​​são semelhantes aos da maioria dos outros compiladores que geram código HID.

Controle de video game

Este projeto utiliza um antigo joystick IBM 76H1571, a foto é mostrada abaixo:

76H1571 é um joystick de 4 botões com acelerador e interruptor de visualização do chapéu POV. O mais interessante é que você não pode usar a roda e o interruptor ao mesmo tempo - você pode usar um elemento ou outro. Dois botões deslizantes na frente do joystick são usados ​​para ligar e desligar o volante e o POV, para que você possa escolher o elemento que deseja usar a qualquer momento.

Como não há restrições com a interface USB, o joystick convertido poderá usar simultaneamente a roda e o botão de visualização. Então os dois interruptores não serão usados, então por que não atribuir novas funções a eles?

Concluindo, gostaria de ressaltar que o joystick convertido possui as seguintes características:

  • Joystick de 2 eixos
  • Acelerador de roda
  • Interruptor POV 4 direcional
  • 6 botões

Descritor de relatório HID

Quando o compilador gera o código USB HID, ele cria um descritor que é enviado ao host USB e indica que tipo de dispositivo USB está sendo conectado. O descritor de dispositivo HID é um pouco diferente porque possui um descritor adicional integrado que identifica o tipo de dispositivo HID e como ele é usado. Esta seção discutirá o processo de conversão de nosso dispositivo em um joystick.

Criando um identificador

O site USB IF hospeda um programa útil que facilita muito a criação de descritores de relatórios HID. É chamada de HID Descriptor Tool e pode ser baixada gratuitamente na página HID Tools. Após o download, descompacte o arquivo e execute o arquivo Dt.exe.

Usando este programa você pode criar seu próprio descritor de relatório para um joystick (ou qualquer outro dispositivo HID), definir o número de eixos e botões e especificar outras características (pedais do leme, rodas, etc.). O programa também vem com modelos de descritores que você pode usar ou modificar imediatamente para atender às suas necessidades. Eles estão localizados na mesma pasta do arquivo executável e possuem a extensão .escondido. O modelo de alça do joystick é chamado joystk.hid, foi isso que usei. Depois de carregado, a seguinte tela aparecerá:

As seções importantes são destacadas com uma moldura vermelha. Eles indicam os seguintes parâmetros:

  • 1 roda, descrita por um valor de 8 bits de -127 a 127
  • Eixos X e Y
  • 1 switch de visualização POV, com 4 posições (0-3) e representando um ângulo de 0-270, descrito por um valor de 4 bits
  • 4 botões, cada um descrito por 1 bit

Como você pode ver, o indicador RELATÓRIO_SIZE define a profundidade de bits dos dados usados ​​para representar o parâmetro e o expoente REPORT_COUNT, que especifica o número de relatórios enviados para representar o parâmetro.

Modificação do descritor

Adicionando um identificador ao seu código

Depois de criar o identificador do relatório, você deve exportá-lo para o código C. Para fazer isso, clique Arquivo->Salvar como no menu do programa HID Descriptor Tool. Na caixa de diálogo que aparece, altere o tipo de arquivo para Arquivo de cabeçalho (*.h).

Isso criará um arquivo de cabeçalho C que você poderá adicionar posteriormente ao seu projeto.

integração mikroC

Para adicionar um arquivo de cabeçalho ao descritor gerado pelo mikroC, você precisará realizar alguns passos. Se você olhar o descritor mikroC você notará que cada byte é seguido por um caractere ‘,0’ (ignorando as aspas, isso é vírgula-zero). Você precisará modificar o descritor do relatório gerado, incluindo os dados de extensão. Você deve acabar com uma alça parecida com esta:

0x05, 0, 0x01, 0, // USAGE_PAGE (Desktop genérico) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x09, 0, 0x04, 0, // USAGE (Joystick) 0xa1, 0, 0x01, 0 , // COLLECTION (aplicativo) 0x05, 0, 0x02, 0, // USAGE_PAGE (controles de simulação) 0x09, 0, 0xbb, 0, // USAGE (acelerador) 0x15, 0, 0x81, 0, // LOGICAL_MINIMUM (-127 ) 0x25, 0, 0x7f, 0, // LOGICAL_MAXIMUM (127) 0x75, 0, 0x08, 0, // REPORT_SIZE (8) 0x95, 0, 0x01, 0, // REPORT_COUNT (1) 0x81, 0, 0x02, 0 , // INPUT (Dados, Var, Abs) 0x05, 0, 0x01, 0, // USAGE_PAGE (Desktop genérico) 0x09, 0, 0x01, 0, // USAGE (Ponteiro) 0xa1, 0, 0x00, 0, // COLEÇÃO (Física) 0x09, 0, 0x30, 0, /////////////////////////////////// / // // USO (X) 0x09, 0, 0x31, 0, // USO (Y) 0x95, 0, 0x02, 0, // REPORT_COUNT (2) 0x81, 0, 0x02, 0, // ENTRADA (dados, Var,Abs) 0xc0, 0, // END_COLLECTION 0x09, 0, ////////////////////////////// // ///////// 0x39, 0, // USO (interruptor de chapéu) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x25, 0, 0x03, 0, // LOGICAL_MAXIMUM (3) 0x35, 0, 0x00, 0, // PHYSICAL_MINIMUM (0) 0x46, 0, 0x0e, 0, 0x01, 0, // PHYSICAL_MAXIMUM (270) 0x65, 0, 0x14, 0, // UNIDADE (Eng Rot:Pos Angular) 0x75, 0, 0x04, 0, // REPORT_SIZE (4) 0x95, 0, 0x01, 0, // REPORT_COUNT (1) 0x81, 0, 0x02, 0, // INPUT (Dados, Var, Abs) 0x05, 0, // /////////////////////////////// 0x09, 0, // USAGE_PAGE (Botão) 0x19, 0 , 0x01, 0 , // USAGE_MINIMUM (Botão 1) 0x29, 0, 0x04, 0, // USAGE_MAXIMUM (Botão 4) 0x15, 0, 0x00, 0, // LOGICAL_MINIMUM (0) 0x25, 0, 0x01, 0, // LOGICAL_MAXIMUM (1 ) 0x75, 0, 0x01, 0, // REPORT_SIZE (1) 0x95, 0, 0x04, 0, // REPORT_COUNT (4) 0x55, 0, 0x00, 0, // UNIT_EXPONENT (0) 0x65, 0, 0x00, 0 , // UNIDADE (Nenhum) 0x81, 0, 0x02, 0, // INPUT (Dados, Var, Abs) 0xc0, 0 // END_COLLECTION

Após preencher o descritor, o próximo passo é deletar o descritor do relatório gerado pelo mikroC e substituí-lo pelo seu. Para fazer isso, primeiro crie um identificador mikroC usando a ferramenta mikroC HID e, em seguida, abra-o em um editor.

Os dados reais do identificador são armazenados inteiramente em uma matriz DescTables. As 50 entradas inferiores na matriz são o identificador do relatório (linhas 109-160). Exclua essas linhas e cole em um novo descritor neste local. Agora você precisa fazer as seguintes modificações no arquivo USBdsc.c:

  • Altere a linha 23 para corresponder tamanho em branco descritor de relatório (ou seja, o tamanho do descritor gerado pela ferramenta HID, sem as entradas adicionais de 0' que precisam ser adicionadas para mikroC - 77 bytes no caso do descritor padrão para o joystick):
    • caractere não assinado const HID_ReportDesc_len = 77;
  • Remova os limites da matriz para DescTables na linha 36:
  • caractere não assinado const DescTables = (

Isso é tudo que havia para fazer. Agora o descritor foi modificado e pode funcionar como um joystick USB. A maneira mais fácil de testar é compilar o código do microcontrolador PIC, conectá-lo à porta USB do seu PC e certificar-se de que ele seja reconhecido corretamente pelo PC. Em seguida, você precisa ir ao Painel de Controle e abrir a caixa de diálogo Dispositivos de Jogo. Seu joystick deverá aparecer na lista de dispositivos.

Transferindo dados para o PC

Se o microcontrolador PIC for reconhecido como um joystick USB, a parte mais difícil do projeto estará concluída. A transferência de dados do joystick para o PC é feita de forma simples. Quando criamos o identificador anteriormente, projetamos um formato de dados conveniente em termos de identificador. Como resultado dos experimentos, obtivemos os seguintes parâmetros:

Os valores de roda, X e Y são valores de 8 bits. No entanto, o seletor de visualização POV e os valores dos botões são de 4 bits, portanto, são compactados em um byte. O formato dos dados é mostrado abaixo:

Graças ao formato de dados predefinido, é possível gravar facilmente o código do programa que serve de interface para alguns botões e potenciômetros, e enviar os dados para um PC para confirmar se o código do programa está funcionando corretamente. O modo de operação do joystick PIC pode ser determinado nas opções de dispositivos de jogos da caixa de diálogo do painel de controle.

Hardware

Depois de verificar o código, você precisa fazer a conversão real do joystick. Para fazer isso, primeiro você precisa desmontar o joystick e remover a placa existente e o cabo da porta de jogo:

Configurando interruptores

Em seguida, você precisa descobrir como todos os interruptores e potenciômetros estão conectados. Os potenciômetros do joystick e dos eixos das rodas são fáceis de identificar e conectar para fornecer energia, aterramento e tensão ao microcontrolador PIC. Os interruptores são um pouco mais difíceis de entender porque dependem do tipo de joystick. Ao traçar os traços na placa de circuito impresso, descobriu-se como as chaves estavam conectadas; veja a imagem abaixo:

O elemento mais interessante é a opção de visualização POV. Em vez de 4 interruptores separados, o interruptor de visualização POV é conectado como um sistema analógico onde a resistência através dos fios verde e laranja determina qual botão é pressionado. A tabela abaixo mostra a resistência de cada chave:

Portanto, para determinar a direção da chave de visualização POV, seus fios serão conectados ao circuito divisor de tensão conforme mostrado abaixo:

Ao conectar os fios a uma fonte de alimentação de 5 V, obtemos as seguintes tensões:

Diagrama elétrico

Depois de determinar as conexões da fiação do joystick, projetamos um circuito que se conectaria a todos os botões e potenciômetros e enviaria dados para o PC. Para tanto foi utilizado um microcontrolador PIC18F2550 operando na frequência de 20 MHz. Os switches são conectados às portas PORTB, os potenciômetros e o switch de visualização POV são conectados ao ADC da porta PORTA. O diagrama é mostrado abaixo:

A placa de ensaio é mostrada abaixo (há três fios de jumper, clique para ampliar a imagem):

Após criar a placa e testar, coloque a placa na base do joystick e substitua o cabo da porta de jogo por um cabo USB. Uma foto do joystick convertido é mostrada abaixo:

O dispositivo está pronto para uso! Monte o joystick e conecte-o a uma porta USB livre. Ao mesmo tempo, deve ser detectado corretamente pelo sistema operacional Windows. Você pode calibrá-lo e começar a usá-lo em jogos compatíveis com joystick.

Conclusão

Com este projeto você pode matar dois coelhos com uma cajadada só. Por um lado, o projeto mostra como criar um joystick USB HID e converter seu joystick antigo em um moderno ou criar seu próprio joystick. Por outro lado, se você sabe criar código de programa para um joystick USB, então você pode criar código para qualquer dispositivo HID, já que são todos semelhantes, a única diferença é que para cada dispositivo você precisará criar um relatório HID descritor e fornece o envio de dados para o PC.

Lista de radioelementos

Designação Tipo Denominação Quantidade ObservaçãoComprarMeu bloco de notas
U1 MK PIC 8 bits

PIC18F2550

1