Corridas FPV em um simulador (fazemos um joystick USB a partir de um controle remoto por rádio)

O inverno nas latitudes do norte é o momento em que o piloto do FPV tem tempo para fazer uma pausa nas corridas e avarias constantes, pegar um ferro de soldar e criar algo útil para seu hobby.

Como está frio para voar para fora, treinaremos habilidades de pilotagem em simuladores. Para fazer isso, você precisa conectar seu equipamento de rádio ao computador através de um adaptador especial, que converte o sinal PPM do controle remoto em sinais do joystick USB que o computador entende. É claro que esses adaptadores não são incomuns e custam um centavo nas lojas chinesas. No entanto, a entrega do pedido precisa esperar muito tempo e funcionará como esperávamos? Por exemplo, eu tenho este:

Por alguma razão que ainda não compreendi, ele se recusa a calibrar adequadamente no simulador FPV Freerider , embora ele funcione muito bem no Phoenix RC e no Aerofly RC 7. O FPV Freerider transmite muito bem a física do voo em um helicóptero de corrida e, além disso, tem um modo de demonstração gratuito.

Resolvido - nós mesmos fazemos o adaptador!

Um pouco de equipamento:

A maioria dos equipamentos RC mais ou menos sérios possui um conector no qual emitem sinais de controle no formato PPM (Modulação de posição de pulso). Um sinal PPM é uma sequência de pulsos curtos, cujo intervalo determina o valor de controle de cada um dos canais do equipamento de rádio.
A essência do PPM transmite perfeitamente a imagem:


Para decodificar o PPM, é necessário medir com precisão os intervalos de tempo entre pulsos consecutivos (não importa entre quais arestas: frontal ou traseira, porque a duração dos pulsos é sempre a mesma).

Implementação:

Inspirando-se em um artigo de AlexeyStn sobre a criação de um adaptador PPM para USB baseado no STM32F3Discovery, mas com apenas o Arduino Pro Micro (Leonardo) com suporte a hardware USB, vamos iniciar um caminho simples para o adaptador.

Você pode encontrar vários projetos semelhantes no github, e alguns nem exigem hardware USB no controlador. No entanto, a maioria deles precisa ser seriamente finalizada com um arquivo para que algo funcione. O projeto adequado foi rc-leonardo-joy, que começou a funcionar quase imediatamente após o preenchimento do esboço, mas imediatamente mostrou algumas falhas: todas as leituras do joystick não eram muito estáveis ​​- o marcador do cursor no painel de controle dançava o tempo todo em torno do ponto definido. Não posso dizer que isso afetou significativamente o manuseio no simulador, mas queremos treinar em bons equipamentos!

Bem, abordamos o código e vemos: o cálculo da largura de pulso do PPM é feito processando uma interrupção externa e medindo os intervalos entre as chamadas para a função micros () :

void rxInt(void) {
  uint16_t now,diff;
  static uint16_t last = 0;
  static uint8_t chan = 0;

  now = micros();
  sei();
  diff = now - last;
  last = now;
  if(diff>3000) chan = 0;
  else {
    if(900<diff && diff<2200 && chan<RC_CHANS ) {
      rcValue[chan] = adjust(diff,chan);
    }
    chan++;
  }
}

Leia sobre a função micros () na documentação do Arduino:

Retorna o número de microssegundos desde que a placa Arduino começou a executar o programa atual. Esse número excederá (volte a zero), após aproximadamente 70 minutos. Nas placas Arduino de 16 MHz (por exemplo, Duemilanove e Nano), essa função tem uma resolução de quatro microssegundos (ou seja, o valor retornado é sempre um múltiplo de quatro). Em placas Arduino de 8 MHz (por exemplo, o LilyPad), essa função possui uma resolução de oito microssegundos.

Ou seja, não apenas a função não é particularmente precisa e sempre retorna valores múltiplos de 4 μs, como também transborda após 70 minutos, fornecendo algum tipo de valor curvo do intervalo medido. Não é bom Seria melhor usar um timer e suas interrupções para capturar sinais externos.

Examinamos mais: a maioria dos dados sobre a posição do joystick é artificialmente limitada a um byte (0-255):
// Convert a value in the range 1000-2000 to 0-255
byte stickValue(int rcVal) {
  return map( constrain(rcVal - 1000, 0, 1000), 0, 1000, 0, 255);
}

Hmm, eu gostaria de ser mais preciso. Mas, para isso, você precisará reescrever o descritor HID e corrigir todos os tipos de dados relacionados.

Mal disse o que fez!
Nós bifurcamos o repositório, reescrevemos o código para usar um timer para contar os intervalos PPM:
void initTimer(void) { 
	// Input Capture setup
	// ICNC1: =0 Disable Input Capture Noise Canceler to prevent delay in reading
	// ICES1: =1 for trigger on rising edge
	// CS11: =1 set prescaler to 1/8 system clock (F_CPU)
	TCCR1A = 0;
	TCCR1B = (0<<ICNC1) | (1<<ICES1) | (1<<CS11);
	TCCR1C = 0;

	// Interrupt setup
	// ICIE1: Input capture 
	TIFR1 = (1<<ICF1); // clear pending
	TIMSK1 = (1<<ICIE1); // and enable
}
...

ISR(TIMER1_CAPT_vect) {
	union twoBytes {
		uint16_t word;
		uint8_t  byte[2];
	} timeValue;

	uint16_t now, diff;
	static uint16_t last = 0;
	static uint8_t chan = 0;
	
	timeValue.byte[0] = ICR1L;    // grab captured timer value (low byte)
	timeValue.byte[1] = ICR1H;    // grab captured timer value (high byte)

	now = timeValue.word;
	diff = now - last;
	last = now;

	//all numbers are microseconds multiplied by TIMER_COUNT_DIVIDER (as prescaler is set to 1/8 of 16 MHz)
	if(diff > (NEWFRAME_PULSE_WIDTH * TIMER_COUNT_DIVIDER)) {
		chan = 0;  // new data frame detected, start again
	}
	else {
		if(diff > (MIN_PULSE_WIDTH * TIMER_COUNT_DIVIDER - THRESHOLD) 
			&& diff < (MAX_PULSE_WIDTH * TIMER_COUNT_DIVIDER + THRESHOLD) 
			&& chan < RC_CHANNELS_COUNT) 
		{
			rcValue[chan] = adjust(diff, chan); //store detected value
		}
		chan++; //no value detected within expected range, move to next channel
	}
}

Aumente os intervalos de desvio da alavanca para 0-1000 no descritor HID:
	// Joystick
	0x05, 0x01,			// USAGE_PAGE (Generic Desktop)
	0x09, 0x04,			// USAGE (Joystick)
	0xa1, 0x01,			// COLLECTION (Application)
	0x85, JOYSTICK_REPORT_ID,	//   REPORT_ID (3)
	...
	0xA1, 0x00,		    //   COLLECTION (Physical)
	0x09, 0x30,		    //     USAGE (x)
	0x09, 0x31,		    //     USAGE (y)
	0x09, 0x33,		    //     USAGE (rx)
	0x09, 0x34,		    //     USAGE (ry)
	0x15, 0x00,		    //	   LOGICAL_MINIMUM (0)
	0x26, 0xE8, 0x03,	    //     LOGICAL_MAXIMUM (1000)
	0x75, 0x10,	  	    //	   REPORT_SIZE (16)
	0x95, 0x04,		    //     REPORT_COUNT (4)
	0x81, 0x02,		    //     INPUT (Data,Var,Abs)
	0xc0,			    //   END_COLLECTION

	0xc0				// END_COLLECTION

Ao longo do caminho, altere uint8_t para uint16_t sempre que os valores de desvio do stick forem transmitidos.
Removemos o código extra, adicionamos uma dúzia de #define e obtemos um bom esboço, afiado para funcionar como um adaptador PPM-USB.

O resultado está disponível no github: github.com/voroshkov/Leonardo-USB-RC-Adapter

Configurações de esboço:

Faz sentido remover a definição de futaba se você tiver outro hardware:
#define FUTABA

- , :
#define STICK_HALFWAY 500
#define STICK_CENTER 1500
#define THRESHOLD 100


:

, USB Arduino, .
Arduino ...\Arduino\hardware\arduino\cores\arduino\, usbapi.h hid.cpp, ArduinoLibs . , Arduino Leonardo .

:

: USB, c – ( Digital Pin 4 ) PPM . - :

No Windows 7, o dispositivo é reconhecido como um composto (teclado, mouse, joystick) com o nome Arduino Leonardo.

Menção especial merece o conector no equipamento. Em algum lugar, é uma tomada de áudio comum, e em algum lugar (como no meu Futaba 7C) é algo mais complicado:


Para a montagem de vários conectores "masculinos", utilizo cola quente há muito tempo. Para fazer isso, colocamos papel ou polietileno na “mãe”, perfuramos esse substrato com pinos, de forma que eles grudem no conector do outro lado, e depois gradualmente derramemos cola entre os pinos, modelando-os simultaneamente com os dedos molhados. Os fios, é claro, precisam ser soldados com antecedência, para não derreter o adesivo curado durante a soldagem. Acontece nem sempre esteticamente, mas muito funcional:

(Aqui, no conector, são necessários 4 pinos para um posicionamento inequívoco, existem apenas dois trabalhadores.)

Isso é tudo. Fazemos o download do simulador, conectamos o equipamento e treinamos habilidades de pilotagem enquanto tomamos chá quente em frente à lareira nas noites escuras de inverno.

PS

E se não houver Leonardo Arduino, mas houver uma placa de desenvolvimento mínimo no STM32F103C8T6?

Não se preocupe, todo o caminho. Para você, assim como para o meu próprio desenvolvimento, eu lancei o projeto já mencionado de Alexey Stankevich.
As fontes e os binários compilados para upload para este controlador podem ser encontrados aqui: github.com/voroshkov/STM32-RC-USB-Adapter .

Responderei a todas as perguntas com prazer nos comentários.

Tenha um bom vôo!

Source: https://habr.com/ru/post/pt388811/


All Articles