Neste artigo, mostrarei como:- Crie um projeto no STM32CubeMX e configure temporizadores para capturar sinais externos.
- Decodifique um sinal PPM a partir de um console de modelo de aeronave.
- Crie o dispositivo de interface humana no STM32 e escreva seu descritor de relatório HID.
- Voe em um simulador em um quadrocopter de corrida. :)
Prefácio
Recentemente, corridas de FPV em quadrocopters da 250ª classe (FPV - First Person View) estão ganhando cada vez mais popularidade. O número 250 significa a distância entre os eixos dos motores na diagonal, típica de pequenos helicópteros manobráveis. Tais dispositivos são construídos sobre estruturas de carbono duráveis que suportam quedas e colisões. Hélices com diâmetro de 5 a 6 polegadas com um grande passo (ângulo de inclinação das pás) são colocadas em motores potentes para o vôo mais dinâmico. A imagem da câmera de vídeo com cabeçalho analógico é transmitida a uma frequência de 5,8 GHz para o monitor ou óculos de vídeo do piloto. Como a transmissão digital via WiFi cria um longo atraso (200 a 300 ms), o vídeo é sempre transmitido em um canal analógico. Para gravar clipes espetaculares, as câmeras de ação são colocadas a bordo (GoPro, Mobius, SJcam, Xiaomi Yi, etc.).Aqui estão alguns vídeos interessantes sobre helicópteros FPV:
Antes de montar meu mini quadrocopter, eu queria voar em um simulador e ver se estaria interessado em corridas de FPV. Para treinamento, o simulador FPV FreeRider é adequado . É barato, possui uma versão demo gratuita e, de acordo com pilotos experientes, imita com muita precisão a mecânica real do voo.Você pode controlar a aeronave no simulador a partir do teclado ou do joystick. O teclado é pouco adequado para pilotagem, pois os botões podem transmitir apenas um valor discreto (o botão é pressionado / não pressionado) e é impossível transmitir suavemente os valores intermediários alterados. Os joysticks de consoles de jogos com manípulos analógicos são muito melhores, mas possuem um manípulo muito pequeno, o que não permite que você controle o dispositivo com precisão. Uma opção ideal para um simulador é um console de modelo de aeronave conectado a um computador através de um adaptador especial, graças ao qual o sistema operacional o vê como um joystick.Eu já tinha um quadrocóptero, montado para voos e fotografias de lazer, mas muito grande e pesado para corridas. Consequentemente, havia um controle remoto - Turnigy 9X (na primeira ilustração). Na parte traseira, possui um conector para conectar um adaptador ao qual é emitido um sinal PPM. Este sinal é um pulso curto com intervalos de 1 a 2 milissegundos, cuja duração corresponde à posição dos controles (mais sobre isso na seção sobre decodificação).Devo dizer que os adaptadores para conectar o controle remoto do PPM ao USB foram lançados há muito tempo e estão sendo vendidos com força e principal. Um adaptador semelhante no formato flash drive pode ser comprado por US $ 5 na China ou um pouco mais caro nas lojas russas. Também existem projetos de adaptador de código aberto nos controladores AVR.Mas um desejo agudo de voar imediatamente me ocorreu no final da noite, quando todas as lojas de modelos de aviões de Moscou já estavam fechadas. Como não queria esperar pela manhã, não havia tempo para envenenar e soldar a placa com o ATmega, por isso decidi fazer um adaptador PPM-USB na placa STM32F3Discovery, que estava inativa e estava à mão.O que é solicitado
Para fazer um adaptador, você precisará de:As placas de depuração de descoberta são muito caras. O F3 descrito custa cerca de US $ 20 e suas capacidades são redundantes para um projeto tão simples. Eu o usei porque, no momento em que escrevi, essa era a única placa com hardware USB que encontrei em casa. Para aqueles que ainda não o compraram, posso aconselhá-lo a prestar atenção às placas em miniatura com o controlador STM32F103C8T6 do AliExpress por US $ 3 e o programador ST-Link de lá. O processo não difere daquele descrito no artigo. A menos que seja necessário escolher outro controlador no início, indicar a presença de um ressonador de quartzo e usar uma pinagem ligeiramente diferente.Criando um projeto no STM32CubeMX
STM32Cube é um pacote desenvolvido pela STMicroelectronics para facilitar a vida dos desenvolvedores de dispositivos STM32. Ele consiste no utilitário de gráficos CubeMX, drivers HAL e componentes de Middleware.O CubeMX é uma ferramenta para criar projetos e inicializar periféricos. Para começar, basta selecionar o controlador, marque as caixas dos módulos necessários, selecione os modos necessários no menu e insira os valores desejados em vários campos. O CubeMX irá gerar o projeto e conectar as bibliotecas necessárias a ele. O desenvolvedor do dispositivo escreverá apenas a lógica do aplicativo.Drivers HAL(Camada de abstração de hardware) é uma API para trabalhar com módulos e periféricos do microcontrolador. O HAL permite separar a camada superior do aplicativo que o desenvolvedor cria do trabalho com registradores e tornar o código do programa o mais portátil possível entre as famílias de controladores STM32.Os middlewares ou componentes intermediários incluem o sistema operacional FreeRTOS, uma biblioteca para trabalhar com o sistema de arquivos, bibliotecas USB, TCP / IP, etc.Parece que agora é possível "programar com o mouse" em vez de escrever manualmente os bits nos registros. Mas a simplicidade e a conveniência não cancelam o fato de que você precisa estudar a documentação, especialmente nos casos em que você precisa reduzir a velocidade máxima, o consumo mínimo de energia ou usar periféricos em modos fora do padrão. O STM32Cube ainda não cobre 100% de todos os recursos do microcontrolador, mas está se aproximando disso. A STMicroelectronics atualiza o Cube de tempos em tempos, estende funções e corrige bugs. Portanto, se você já possui o Cube instalado, verifique se é a versão mais recente.Configurações iniciais
O trabalho com o projeto começa com a escolha do controlador. Inicie o STM32CubeMX, clique em Novo Projeto . Na guia Seletor do MCU , você pode selecionar o controlador desejado nos filtros. Como temos uma placa de depuração concluída, na guia Seletor de placas , encontramos STM32F3Discovery . Depois de escolher um quadro, uma imagem do controlador com pinos destacados e assinados será exibida.Existem quatro abas grandes na parte superior da janela:Pinagem - para configurar as funções de pinos e pré-configurar os módulos. Estamos nisso no momento.Configuração do relógio - ajuste do relógio, PLL, divisores.Configuração - configuração mais detalhada de periféricos e middleware.Calculadora de consumo de energia - cálculo da energia consumida pelo microcontrolador.
No menu esquerdo da guia Pinagem , você pode usar os periféricos desejados e, no circuito do controlador, selecionar uma função para qualquer uma das saídas do microcontrolador. Alguns itens à esquerda são ícones de aviso. Isso significa que os módulos (neste caso ADC, DAC, OPAMP2, RTC) agora podem ser usados incompletamente, pois algumas de suas saídas já estão ocupadas por outras funções.Os pinos configurados são destacados em verde no circuito do controlador. Como escolhemos não um controlador simples sem amarração, mas uma placa de depuração F3-Discovery pronta para o uso, algumas das saídas já estão configuradas, por exemplo, um botão azul é conectado ao PA0 e os LEDs ao PE8 ... 15. Os pinos aos quais alguns dispositivos externos estão conectados no Discovery são destacados em laranja, mas os módulos periféricos para eles ainda não foram configurados. Como você pode ver, são pinos para USB, ressonadores de quartzo, SPI e I2C para giroscópio e bússola, DP e DM para USB. Atualmente, conclusões cinzentas não são usadas, nenhuma delas podemos aplicar para nossos propósitos.Seleção de entrada
Nós vamos capturar a duração dos pulsos, então a entrada deve estar conectada a um dos canais de qualquer timer. Além disso, o nível de sinal do Turnigy 9X não é de 3,3V, como a tensão de alimentação do STM32, mas de 5V. Estamos com preguiça de soldar o divisor de tensão, então você precisa escolher uma entrada que possa suportar 5V (essas entradas são chamadas tolerantes a 5V). Os pinos adequados podem ser encontrados na folha de dados em STM32F303VCT6 na seção Pinagens e descrição dos pinos . Existem muitos temporizadores no STM32F3, eles estão espalhados por quase todos os pinos. Uma opção conveniente é o PC6. Ele suporta 5 volts e está localizado no canto inferior esquerdo da placa, ao lado de GND. Atribua o 1º canal do 3º temporizador TIM3_CH1 a este pino.Acerto do relógio
Para que o USB funcione, o microcontrolador deve ter uma freqüência muito estável, razão pela qual quase todos os dispositivos USB têm ressonadores de quartzo. A estabilidade da frequência do gerador RC embutido não é suficiente para USB. Mas na placa STM32F3 Discovery, os desenvolvedores, por algum motivo, eram gananciosos e não colocaram quartzo. No entanto, se você estudar cuidadosamente o circuito , poderá ver que o sinal MCO está conectado à entrada PF0-OSC_IN , onde o quartzo deve ser conectado . Ele vem do programador ST-Link na mesma placa em que há quartzo. O Manual do Usuário da Descoberta F3 (UM1570) na seção Relógio do OSC diz que 8 MHz estão sendo enviados para esta linha.
Assim, o microcontrolador tem clock de uma fonte externa. Esse modo é chamado de desvio. No menu de configurações periféricas na seção RCC , para registrar o relógio de alta velocidade, selecione Origem do relógio BYPASS .
Antes de prosseguir para uma configuração mais detalhada do relógio, observamos no menu periférico que o microcontrolador atuará como um dispositivo USB.
Agora você pode ir para a próxima aba grande - Configuração do relógio . Aqui veremos um diagrama enorme, que mostra quais sinais de relógio estão presentes no microcontrolador, de onde eles vêm, como se ramificam, se multiplicam e se dividem. Em amarelo, destaquei os parâmetros que devem ser observados.
Verifique se a frequência de entradaA frequência de entrada é de 8 MHz.Definimos o comutador PLL Source Mux como HSE (High Speed External) para fazer o clock de uma fonte externa e não de uma fonte interna.PLL - Loop de bloqueio de fase, ou PLL - loop de bloqueio de fase, serve para multiplicar a frequência externa várias vezes. Defina o multiplicador PLLMul como 9. Em seguida, atingiremos a frequência máxima possível para o STM32F303 - 72 MHz.O Clock do sistema Mux deve estar na posição PLLCLK para que a frequência do relógio seja multiplicada pelo PLL.Para o módulo USB, são necessários 48 MHz, portanto, coloque um divisor de 1,5 na frente do USB.Preste atenção à frequênciaO cronômetro APB1 fica no lado esquerdo do circuito. Vai para os temporizadores e é útil para nós no futuro.Se alguma frequência estiver configurada incorretamente, exceder o valor máximo possível ou os comutadores estiverem em uma posição inválida, o CubeMX destacará esse local em vermelho.Configuração do temporizador
Para medir a duração do pulso, iniciaremos o timer TIM3 no modo Captura de entrada. No Manual de Referência , na seção Temporizadores de uso geral (TIM2 / TIM3 / TIM4), há um diagrama que ilustra a operação dos temporizadores. Com cores, destaquei os sinais e registradores usados no modo Captura de entrada.
O sinal do relógio destacado em verde entra continuamente no registro do contador CNT e aumenta seu valor em 1 a cada ciclo do relógio. No divisor Prescaler PSC , a frequência do relógio pode diminuir para uma contagem mais lenta.Um sinal externo é inserido no TIMx_CH1 . Detector de bordareconhece as bordas do sinal de entrada - transições de 0 a 1 ou 1 a 0. Ao registrar a borda, ele envia dois comandos destacadas em amarelo:- um comando para escrever o valor da CNT balcão ao Capture / comparar 1 registo (CCR1) eo CC1I interrupção de chamadas .- um comando para o controlador do modo escravo , pelo qual o valor CNT é redefinido para 0 e a contagem regressiva é iniciada novamente.Aqui está uma ilustração do processo em uma linha do tempo:
Quando ocorre uma interrupção, executaremos ações com o valor capturado. Se os pulsos de entrada ocorrerem com muita frequência e as ações que ocorrerem no manipulador de interrupções forem muito longas, o valor de CCR1 poderá ser sobrescrito antes de lermos o anterior. Nesse caso, é necessário verificar o sinalizador Overcapture ou aplicar o DMA (Direct Memory Access) quando os dados do CCR1 preencherem automaticamente a matriz preparada na memória. No nosso caso, o pulso mais curto tem a duração de 1 milissegundo e o manipulador de interrupções será simples e curto, portanto, não se preocupe com a substituição. Voltepara a guia Pinagem e defina o timer TIM3 no menu Periféricos . Modo Escravo: Modo Reset
- significa que, em algum evento, o timer será redefinido para 0.Fonte de Disparo: TI1FP1 - o evento usado para redefinir e iniciar o timer é a borda do sinal capturada da entrada TI1.ClockSource: Internal Clock - o timer é gerado a partir do gerador interno do microcontrolador.Canal 1: Modo direto de captura de entrada - capture intervalos do 1º canal no registro CCR1.Na próxima grande guia Configuração , faremos configurações adicionais de timer.Prescaler é um divisor de temporizador. Se for 0, a frequência é obtida diretamente do relógio APB do barramento de clock - 72 MHz. Se o pré-calibrador for 1, a frequência é dividida por 2 e passa a 36 MHz. Defina o divisor como 71 para que a frequência seja dividida por 72. A frequência do temporizador será de 1 MHz e os intervalos serão medidos com uma resolução de 1 microssegundo.Período do contador - defina o valor máximo possível de 16 bits 0xFFFF. O período é importante para gerar intervalos de tempo, por exemplo, para PWM. Mas o período não é importante para capturar sinais; nós o tornaremos conhecido por ser grande para qualquer pulso de entrada.Seleção de polaridade: Borda de queda - os valores do temporizador serão capturados na borda de queda do sinal de entrada.Na guia NVIC Settings , coloque um dawInterrupção global TIM3 , para que eventos relacionados ao terceiro temporizador gerem uma interrupção.Configuração do dispositivo USB
Já observamos que o controlador será um dispositivo USB. Como o joystick pertence à classe de dispositivos HID, no menu Middlewares -> USB_DEVICE, selecione Classe para IP FS: Classe de dispositivo de interface humana (HID) . Em seguida, o CubeMX conectará bibliotecas para o dispositivo HID ao projeto.
Vamos para as configurações USB_DEVICE na guia Configuração na seção Middlewares :O ID do fornecedor e o ID do produto são dois identificadores de 16 bits exclusivos para cada modelo de dispositivo USB. O VID corresponde ao fabricante do dispositivo e cada fabricante atribui o PID, guiado por suas próprias considerações. Não consegui encontrar a lista oficial de VID e PID, apenas encontrei a base de identificadores suportada por entusiastas. Para obter seu próprio ID de fornecedor, você precisa ir ao USB Implementers Forum no usb.org e pagar alguns milhares de dólares. Pequenas empresas ou desenvolvedores de código aberto que não podem pagar seu VID podem solicitar um fabricante de chips USB e receber oficialmente um par VID / PID para seu projeto. Esse serviço é oferecido, por exemplo, pela FTDI ou Silicon Laboratories.Se você conectar dois dispositivos com o mesmo VID / PID, mas de um tipo diferente (por exemplo, um é um dispositivo HID e o outro é Armazenamento em Massa), o sistema operacional tentará instalar o mesmo driver para eles e pelo menos um deles não funciona. É por isso que os pares VID / PID para diferentes modelos de dispositivos devem ser exclusivos.Como não estamos fabricando um dispositivo para nós mesmos, não o venderemos e distribuiremos, deixaremos o VID 0x0483, correspondente à STMicroelectronics, e criaremos nosso próprio PID. Por padrão, o CubeMX oferece o PID 0x5710 para o dispositivo HID. Substitua-o, por exemplo, por 0x57FF.Substitua corda Produto com STM32 PPM-USB Adapter. Este nome aparecerá na lista de dispositivos no Painel de Controle do Windows. Ainda não alteraremos o número de série (S \ N).Quando o Windows detecta, ele detecta um dispositivo com uma combinação de VID, PID e S \ N que nunca havia visto antes, o sistema instala o driver apropriado para ele. Se a combinação de VID, PID e S \ N já tiver sido usada, o Windows substituirá automaticamente o driver usado anteriormente. Você pode ver isso, por exemplo, quando conecta a unidade flash ao USB. A primeira vez que conectar e instalar leva algum tempo. Nas conexões subseqüentes, a unidade começa a funcionar quase que instantaneamente. No entanto, se você conectar outra instância da unidade Flash do mesmo modelo, mas com um número de série diferente, o sistema instalará um novo driver para ele, mesmo com o mesmo VID e PID.Vou explicar por que isso é importante. Se você criou um mouse USB no seu STM32 com seu VID, PID e S \ N, conectou-o ao computador e criou um joystick USB sem alterar o VID, PID e S \ N, o Windows perceberá o novo dispositivo como um mouse que já usado no sistema e não instalará o driver do joystick. Consequentemente, o joystick não funcionará. Portanto, se você deseja alterar o tipo do seu dispositivo, deixando o VID / PID inalterado, altere o número de série.Geração de projeto para IDE
As últimas configurações que você precisa fazer são as configurações de geração do projeto. Isso é feito através de Projeto -> Configurações ... Lá, definiremos o nome, a pasta de destino e o IDE desejado, sob o qual o CubeMX criará o projeto. Eu escolhi o MDK-ARM V5 , porque uso o Keil uVision 5. Na guia Gerador de código , você pode marcar a caixa de seleção Copiar apenas os arquivos de biblioteca necessários para não confundir o projeto com arquivos desnecessários.Pressione o botão Projeto -> Gerar código . O CubeMX criará um projeto com código que pode ser aberto no Keil uVision e compilado e exibido sem configurações adicionais. No arquivo main.c na função principal (nula)funções já foram inseridas para inicializar o relógio, portas, timer e USB. Neles, os módulos do microcontrolador são configurados de acordo com os modos que definimos no CubeMX.
No código, construções desse tipo são frequentemente encontradas:
(...)
Supõe-se que o usuário incorporará seu código nessas seções. Se a opção Manter código do usuário ao gerar novamente estiver ativada nas configurações do projeto CubeMX , o código entre essas linhas não será substituído durante a geração secundária de um projeto existente. Infelizmente, apenas as seções criadas pelo CubeMX são salvas. As seções / * CÓDIGO DO USUÁRIO * / criadas pelo usuário serão perdidas. Portanto, se depois de escrever o código no IDE, você desejar retornar ao CubeMX e gerar o projeto novamente com as novas configurações, recomendo fazer uma cópia de backup do projeto.Nas configurações de firmware do uVision ( Flash -> Configurar ferramentas do Flash ), aconselho a ativar a opção Redefinir após o flashpara que o microcontrolador inicie imediatamente após piscar. Por padrão, ele está desativado e, após cada piscar, você deve pressionar o botão Redefinir na placa.
Decodificação PPM
PPM - Pulse Position Modulation - um método de codificação de sinais transmitidos, muito difundido nos modelos eletrônicos de aeronaves. É uma sequência de pulsos, cujos intervalos de tempo correspondem aos valores numéricos transmitidos.De acordo com este protocolo, o console envia informações ao módulo de rádio transmissor, que é inserido no console na parte traseira. Muitos receptores que são colocados a bordo do helicóptero podem transmitir sinais de controle para o controlador de vôo via PPM. Além disso, quase qualquer console possui conectores para conectar um segundo console no modo treinador-aluno e para conectar o console a um simulador, que também costuma usar o PPM.Escrevemos um sinal da saída do simulador do Turnigy 9X com um analisador lógico:Cada sequência codifica o estado atual dos controles no controle remoto. Geralmente, os quatro primeiros valores (também chamados de canais) correspondem à posição dos manípulos analógicos e os subsequentes à posição dos interruptores ou potenciômetros.A posição mínima do controle corresponde ao intervalo 1000 μs, o máximo - 2000 μs, a posição média - 1500 μs. Explosões de pulsos, ou quadros, são separadas por intervalos substancialmente mais longos e seguem com um período de 20 a 25 ms.Vamos dar uma olhada no sinal:Como você pode ver, três paus estão na posição neutra (1, 3, 4) e um está na posição extrema (2). Três interruptores estão desativados (5, 6, 7) e o último está ativado (8). O microcontrolador, atuando como um adaptador, deve capturar essa sequência, adicionar os valores a uma matriz e enviá-la via USB como um comando a partir do joystick. Vamos escrever um decodificador de seqüência de pulsos.Interrupção de captura
Após a inicialização no main.c, antes do loop while principal , inicie o timer TIM3 no modo de captura Input Capture a partir do canal 1, com a geração de interrupções de captura. Para fazer isso, use a função correspondente do HAL:HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
A estrutura htim3 declarada em main.c é o manipulador de timer TIM3, que contém todas as estruturas e variáveis associadas ao timer: parâmetros para inicialização, ponteiros para todos os registros de timer (valor do contador, divisor, todas as configurações, sinalizadores de interrupção), ponteiro em um manipulador de DMA que funcione com esse timer, etc. O desenvolvedor não precisa procurar por quais bits nos quais o registro é responsável por quê, e manualmente os define e redefine. É o suficiente para passar o manipulador para a função HAL. As bibliotecas HAL farão o resto elas mesmas.Os princípios da estrutura HAL são descritos em mais detalhes no documento Descrição dos drivers STAL32F3xx HAL .(UM1786). Deve-se notar que as próprias bibliotecas HAL estão bem documentadas. Para entender como o HAL funciona para o temporizador e como usá-lo, você pode ler os comentários nos stm32f3xx_hal_tim.h e arquivos stm32f3xx_hal_tim.c .Para cada interrupção que o timer TIM3 gera, o manipulador TIM3_IRQHandler é chamado . Ele está localizado no arquivo stm32f3xx_it.c , por sua vez, o manipulador HAL_TIM_IRQHandler , padrão para todos os timers, é chamado nele e um ponteiro para a estrutura htim3 é passado para ele.void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim3);
}
Se olharmos para dentro HAL_TIM_IRQHandler arquivo stm32f3xx_hal_tim.c , vemos uma enorme manipulador que verifica os sinalizadores de interrupção para causas temporizador callback-função e limpa o sinalizador após a execução. Se ocorrer um evento de captura, ele chamará a função HAL_TIM_IC_CaptureCallback . Se parece com isso:__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
}
Isto significa que pode substituir essa função em main.c . Portanto, insira esse retorno de chamada antes da função int main (void) :void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
};
Eu gostaria de ver como a interrupção é realizada. Adicione a ele um rápido início de uma das conclusões:void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_SET);
__nop();__nop();__nop();__nop();__nop();__nop();
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, GPIO_PIN_RESET);
};
O pino PE8 já foi inicializado como uma saída. Entre ligar e desligar, as instruções __nop () são inseridas , formando um atraso de 1 ciclo de relógio. Isso é feito para que meu analisador lógico chinês de US $ 8, operando a 24 MHz, não perca um pulso muito curto de um microcontrolador de 72 MHz. Agora compile o projeto Projeto -> Construir alvo e peça ao controlador Flash -> Download . Vamos conectar o PPM do controle remoto ao PC6 e ver o que acontece no PC6 e PE8 com o analisador.O retorno de chamada é realmente chamado nos momentos certos - logo após a transição do sinal de entrada de 1 para 0. Portanto, tudo foi feito corretamente.Capturar e processar dados capturados
Editaremos o retorno de chamada para que ele adicione cada valor capturado ao buffer de valor_ capturado inalterado. Se o timer capturar um valor muito grande (mais de 5000 μs), isso significa que uma pausa foi registrada, o pacote foi recebido na íntegra e pode ser processado. Os valores processados são adicionados à matriz rc_data de 5 elementos. Nos quatro primeiros, as posições do manche são reduzidas para o intervalo [0; 1000], no quinto, os bits individuais são definidos de acordo com as chaves de alternância, que serão interpretadas como pressionando botões no gamepad.uint16_t captured_value[8] = {0};
uint16_t rc_data[5] = {0};
uint8_t pointer = 0;
uint8_t data_ready = 0;
...
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
uint8_t i;
uint16_t temp;
temp = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
if ((temp > 5000) && (!data_ready))
{
pointer = 0;
for (i = 0; i < 4; i++)
{
if (captured_value[i] < 1000)
captured_value[i] = 1000;
else if (captured_value[i] > 2000)
captured_value[i] = 2000;
rc_data[i] = captured_value[i]-1000;
};
rc_data[4] = 0;
if (captured_value[4] > 1500)
rc_data[4] |= (1<<4);
if (captured_value[5] > 1500)
rc_data[4] |= (1<<5);
if (captured_value[6] > 1500)
rc_data[4] |= (1<<6);
if (captured_value[7] > 1500)
rc_data[4] |= (1<<7);
data_ready = 1;
}
else
{
captured_value[pointer] = temp;
pointer++;
};
if (pointer == 8)
pointer = 0;
}
Deixe-me explicar por que coloquei os bits correspondentes aos botões não nos 4 bits inferiores, mas nos quintos aos oitavos bits. No simulador, é suposto conectar um gamepad do Xbox, onde os botões LB, RB, Start e Back são usados, e eles têm números de 5 a 8.No loop principal, o sinalizador data_ready será rotacionado continuamente, pelo qual os dados serão enviados ao computador.while (1)
{
if (data_ready)
{
data_ready = 0;
}
}
Para verificar como isso funciona, conecte o controle remoto, compile e faça o flash novamente e inicie a depuração Debug -> Start / Stop Debug Session .Abra a janela para rastrear variáveis Ver -> Observar Janelas -> Observar 1 e adicione o valor_ capturado e rc_data lá .Iniciamos a depuração com o comando Debug -> Run e, em tempo real, sem adicionar pontos de interrupção, veremos como os números mudam após os ataques.Em seguida, você precisa enviar dados para o computador na forma de um comando do joystick.Configurar dispositivo HID e criar descritor de relatório HID
O USB HID (Dispositivo de interface humana) é uma classe de dispositivos para interação homem-computador. Isso inclui teclados, mouses, joysticks, gamepads, painéis de toque. A principal vantagem dos dispositivos HID é que eles não exigem drivers especiais em nenhum sistema operacional: Windows, OS X, Android e até iOS (via adaptador USB-Lightning). Uma descrição detalhada pode ser encontrada no documento Device Class Definition for HID . A principal coisa que precisamos saber para criar um adaptador PPM-USB é o que o HID Report e o HID Report Descriptor são .O dispositivo HID envia pacotes de bytes para o computador em um formato predefinido. Cada um desses pacotes é um relatório HID. O dispositivo informa o computador sobre o formato dos dados quando ele se conecta, enviando um Descritor de Relatório HID, uma descrição do pacote que indica quantos bytes o pacote contém e a finalidade de cada byte e bit no pacote. Por exemplo, o Relatório HID de um mouse simples consiste em quatro bytes: o primeiro byte contém informações sobre os botões pressionados, o segundo e o terceiro bytes contêm o movimento relativo do cursor ao longo de X e Y e o quarto byte contém a rotação da roda de rolagem. O Descritor de Relatórios é armazenado na memória do controlador do dispositivo como uma matriz de bytes.Antes de criar um descritor, gostaria de me debruçar sobre terminologia separadamente. Dois termos são comuns no ambiente em inglês - joystick e gamepad . A palavra joystick geralmente é chamada de manipulador, que é segurada com uma mão e inclinada em direções diferentes, e gamepad é um dispositivo com botões e paus que é segurado com as duas mãos. Usuários de língua russa costumam chamar o joystick disso e de outro. Na descrição do dispositivo HID, há uma diferença entre o joystick e o gamepad. O console do modelo de aeronave é mais semelhante em termos de funcionalidade a um gamepad; portanto, no futuro, usarei o termo "gamepad".Geramos um projeto, indicando que o dispositivo atuará como um dispositivo de interface humana. Isso significa que uma biblioteca USB HID está conectada ao projeto e um descritor de dispositivo já foi gerado. Ele está localizado no arquivo usbd_hid.c , descreve o relatório do mouse e tem a seguinte aparência:HID_Mouse_Report_Descriptor__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01,
0x09, 0x02,
0xA1, 0x01,
0x09, 0x01,
0xA1, 0x00,
0x05, 0x09,
0x19, 0x01,
0x29, 0x03,
0x15, 0x00,
0x25, 0x01,
0x95, 0x03,
0x75, 0x01,
0x81, 0x02,
0x95, 0x01,
0x75, 0x05,
0x81, 0x01,
0x05, 0x01,
0x09, 0x30,
0x09, 0x31,
0x09, 0x38,
0x15, 0x81,
0x25, 0x7F,
0x75, 0x08,
0x95, 0x03,
0x81, 0x06,
0xC0, 0x09,
0x3c, 0x05,
0xff, 0x09,
0x01, 0x15,
0x00, 0x25,
0x01, 0x75,
0x01, 0x95,
0x02, 0xb1,
0x22, 0x75,
0x06, 0x95,
0x01, 0xb1,
0x01, 0xc0
};
Criar manualmente o Descritor de Relatórios HID consome muito tempo. Para facilitar a tarefa, existe uma ferramenta chamada HID Descriptor Tool (DT). Este programa pode criar um descritor para o seu dispositivo. No arquivo, você encontra vários exemplos de descritores para diferentes dispositivos.Aqui está um artigo muito bom sobre como criar seu próprio descritor HID para mouse e teclado (em inglês). Vou dizer em russo como lidar com um gamepad.O relatório HID enviado pelo console deve conter quatro valores de 16 bits para dois eixos de manípulos analógicos e 16 valores de um bit para botões. Total de 10 bytes. Seu identificador criado no DT terá a seguinte aparência: 0x05, 0x01,
0x09, 0x05,
0xa1, 0x01,
0x09, 0x01,
0xa1, 0x00,
0x09, 0x30,
0x09, 0x31,
0x15, 0x00,
0x26, 0xe8, 0x03,
0x75, 0x10,
0x95, 0x02,
0x81, 0x02,
0xc0,
0xa1, 0x00,
0x09, 0x33,
0x09, 0x34,
0x15, 0x00,
0x26, 0xe8, 0x03,
0x75, 0x10,
0x95, 0x02,
0x81, 0x02,
0xc0,
0x05, 0x09,
0x19, 0x01,
0x29, 0x10,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x10,
0x81, 0x02,
0xc0
Parece não menos intimidador do que um descritor de mouse. Mas se você entende o que cada linha significa, tudo acaba sendo bastante compreensível e lógico.USAGE mostra como o sistema deve interpretar dados que vão além.Existem muitos tipos de uso, eles são classificados em grupos - páginas de uso. Portanto, para selecionar um Uso específico, você deve primeiro consultar o USAGE_PAGE correspondente. Sobre o que o uso pode ser encontrado no documento Hid Usage Tables . No início do descritor, indicamos que o joystick será descrito:USAGE_PAGE (área de trabalho genérica)
USAGE (Game Pad)
COLLECTION combina vários conjuntos de dados relacionados.Coleta física é usada para dados relacionados a um ponto geométrico específico, por exemplo, um stick analógico. A coleção de aplicativos é usada para combinar diferentes funções em um dispositivo. Por exemplo, um teclado com um trackpad integrado pode ter duas coleções de aplicativos. Descrevemos apenas o joystick, o que significa que a coleção será uma:COLECÇÃO (aplicação)
...
END_COLLECTION
Depois disso, você precisa especificar que os elementos que transmitem as coordenadas serão descritos. Ponteiro de uso é usado para descrever mouses, joysticks, gamepads, digitalizadores:USAGE (Ponteiro)
A seguir, são apresentadas descrições de sticks analógicos combinados em uma coleção:COLEÇÃO (Física)
USAGE (X)
USAGE (Y)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (2)
INPUT (Data,Var,Abs)
END_COLLECTION
USAGE aqui indica que os valores de desvio ao longo dos dois eixos, X e Y, sãousados . LOGICAL_MINIMUM e LOGICAL_MAXIMUM especificam até que ponto o valor transmitido pode variar.REPORT_COUNT e REPORT_SIZE definem, respectivamente, quantos números e qual tamanho vamos transferir, ou seja, dois números de 16 bits.ENTRADA (Dados, Var, Abs) significa que os dados vêm do dispositivo para o computador e esses dados podem ser alterados. Os valores em nosso caso são absolutos. Por exemplo, valores relativos vêm do mouse para mover o cursor. Às vezes, os dados são descritos como Const, não Var. Isso é necessário para transmitir bits não significativos. Por exemplo, em um relatório de mouse com três botões, 3 bits de Var para botões e 5 bits de Const são transferidos para complementar o tamanho da transferência para um byte.Como você pode ver, as descrições dos eixos X e Y são agrupadas. Eles têm o mesmo tamanho, os mesmos limites. O mesmo bastão analógico pode ser descrito a seguir, descrevendo cada eixo individualmente. Esse descritor funcionará de maneira semelhante ao anterior:COLEÇÃO (Física)
UTILIZAÇÃO (X)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (1)
ENTRADA (Dados, Var, Abs)
UTILIZAÇÃO (Y)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (1)
ENTRADA (Dados, Var, Abs)
END_COLLECTION
Após o primeiro stick, o segundo stick analógico é descrito. Seus eixos têm um uso diferente, para que você possa distingui-los do primeiro bastão - Rx e Ry:COLEÇÃO (Física)
USAGE (Rx) USAGE
(Ry)
LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1000)
REPORT_SIZE (16)
REPORT_COUNT (2)
ENTRADA (Dados, Var, Abs)
END_COLLECTION
Agora você precisa descrever alguns botões do gamepad. Isso pode ser feito da seguinte forma:USAGE_PAGE (botão) USAGE (botão
1)
USAGE (botão 2)
USAGE (botão 3)
...
USAGE (botão 16)
A gravação complicada de botões do mesmo tipo pode ser reduzida usando o intervalo de Uso:USAGE_PAGE (botão)
USAGE_MINIMUM (botão 1)
USAGE_MAXIMUM (botão 16)
Os dados transmitidos pelos botões são 16 valores de bit único, variando de 0 a 1:LOGICAL_MINIMUM (0)
LOGICAL_MAXIMUM (1)
REPORT_SIZE (1)
REPORT_COUNT (16)
INPUT (Data,Var,Abs)
A ordem das linhas no descritor não é rigorosa. Por exemplo, Logical_Minimum e Logical_Maximum podem ser gravados antes de Usage (Button), ou as linhas Report_Size e Report_Count podem ser trocadas.É importante que o comando Entrada tenha todos os parâmetros necessários para a transferência de dados (Uso, Mínimo, Máximo, Tamanho, Contagem).Quando o descritor é formado, ele pode ser verificado com o comando Parse Descriptor quanto a erros.Se tudo estiver em ordem, exporte-o com a extensão h. No arquivo usbd_hid.c, substitua o descritor por um novo e ajuste em usbd_hid.h o tamanho do descritor HID_MOUSE_REPORT_DESC_SIZE de 74 a 61. Osrelatórios são enviados usando o sinalizador data_ready . Para issomain.c incluiremos o arquivo de cabeçalho usbd_hid.h e no loop principal chamaremos a função de envio de relatório. A matriz rc_data é do tipo uint16, portanto, o ponteiro para ela deve ser convertido para um tipo de 8 bits e passar o tamanho 10 em vez de 5.#include "usbd_hid.h"
...
while (1)
{
if (data_ready)
{
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)rc_data, 10);
data_ready = 0;
};
};
Compilamos o projeto e o exibimos novamente.Conexão e uso
Reconecte o cabo USB do conector USB do ST-LINK ao conector USB do USUÁRIO. O Windows detectará o novo dispositivo e instalará automaticamente o driver. Vamos ao Painel de controle -> Dispositivos e impressoras e ver nosso dispositivo adaptador STM32 USB-PPM com um ícone de gamepad.Nos parâmetros do dispositivo, você pode ver como a cruz se move ao redor do campo e as colunas se movem depois que os paus são movidos, e os símbolos dos botões acendem na chave seletora. A calibração não é necessária porque os valores mínimo e máximo já foram definidos no descritor.Iniciando o FPV FreeRider, veremos como na tela principal do gamepad virtual desenhado os manípulos se movem de acordo com nosso controle remoto. Se os eixos não forem atribuídos corretamente por algum motivo, você poderá reconfigurá-los na seção Calibrar controlador .Os interruptores correspondentes aos botões do controle remoto são usados para alternar entre os modos de voo (acrobático / estabilizado), alternar a visualização da câmera (do quadro / do solo), iniciar um voo desde o início ou iniciar a corrida por um tempo.Voou!
No vídeo - o resultado de vários dias do meu treinamento. Enquanto estou voando com nivelamento automático, e não no modo acrobático, como fazem todos os mestres em corridas de FPV. No modo acro, se você soltar os manípulos, o helicóptero não retornará automaticamente para a posição horizontal, mas continuará voando no mesmo ângulo em que voou. Gerenciar no modo acro é muito mais difícil, mas você pode obter maior velocidade, capacidade de manobra, fazer agitações no ar e até voar de cabeça para baixo.Para Mestres CharpuAinda estou muito longe, mas continuo treinando e posso dizer com certeza que a ideia de um mini-helicóptero de corrida me interessou ainda mais. E em breve estarei definitivamente engajado em sua construção e vôos não mais no simulador, mas em dura realidade, com acidentes reais, hélices quebradas, motores quebrados e baterias queimadas. Mas este é um tópico para outros artigos :)
O projeto para o Keil uVision 5 e STM32CubeMX está no GitHub .