También puedes hallar muy buena información acerca de radio control en www.e-radiocontrol.com.ar
Hola elgarbe, muy buena tu explicación ((:-)), nunca he usado radio controles de ese tipo
por curiosidad, porque deicidiste usar RC para tu UAV, y no algun control por RF o bluetooth?
Hola, bluetooth es la opción más simple debido a los modulos BT-232. El tema es que el alcance es muy limitado. No más de 100mts, verdad?
El tema del enlace con el UAV es digno de estudio. Normalmente hay 2 enlaces con el avion y hay una mezcla de señales. Se utilizan RC con sistemas LRS (Long range systems en 433MHz) para el comando del avion, cámara de a bordo, etc. Luego un enlace de audio y video para poder volar en primera persona. En el mismo enlace de video se monta una señal proveniente del OSD (On Screen Display) el cual superpone en la imagen de video la telemetría. Con canales adicionales del RC se suele manejar distintos menús del OSD. El tema es que si por algún problema perdes la cámara, la cual genera la señal de sincronismo de video, perdés la telemetría.
Lo ideal sería tener el enlace de video (en 1.2GHz) solo para video y un enlace adicional para el comando del UAV y la telemetría. Este enlace debería ser bidireccional y podrían ser módulos de RF. Las posibilidades son infinitas y se puede elegir la que uno mas le guste, cada una tiene pros y contras. El problema del enlace RF es que es menos universal para los que usan un UAV como hobby ya que todos usan RC para sus aviones/quadcopter/helicoptero....
También he visto que se puede hacer una especie de radio modem con el canal de sonido del transmisor de video para enviar la telemetría a tierra.
Saludos!
Tengo dudas del porque la necesidad de leer las señales del receptor. Si lo puedes conmutar con un multiplexor.
Osea bien los motores hacen caso a la señal del receptor(caso control manual) o bien al microcontrolador(caso control automático).
Por ejemplo el que utilizan en el ardupilot:
https://www.fairchildsemi.com/ds/74/74ACT157.pdf
Saludos.
#ifndef RX_H_
#define RX_H_
#include "LPC17xx.h"
#define RX_CHANNELS 4
/* defines */
#define RX_SUCCESS (0x00)
#define RX_NO_PACKET (0x01)
/* typedefs */
/* functions */
void rx_init(uint8_t irq_priority);
uint8_t rx_get_packet(uint32_t packet[RX_CHANNELS]);
#endif /* RX_H_ */
/*
* RX.c
*
* Created on: 28/01/2014
* Author: elgarbe
*/
/*****************************************************************************
Copyright 2011
All Rights Reserved
Information:
File Name : rx.c
Author(s) : Nic McDonald
Project : Quadrotor
Hardware : LPCXpresso LPC1768
Purpose : RC Receiver Interface
******************************************************************************
Modification History:
Revision Date Author Description of Revision
1.00 05/19/2011 NGM initial
******************************************************************************
Theory of Operation:
This modules implements an RC receiver interface. It expects the input
signal to have all channels combined into one signal and each pulse must be
separated.
Ex:
____
__| |___________________________ channel 1
__
_________| |______________________ channel 2
___
______________| |________________ channel 3
____ __ ___
__| |_| |_| |________________ all channels
This implementation utilizes TIMER2 to measure PWM signals.
******************************************************************************
Esta explicación es para el caso de hacer una OR con cada salida del Rx. En mi caso
extraigo la señal PPM del Rx y por lo tanto tengo que leer solamente flancos de subida,
ya que cada pulso termina donde comienza el siguiente, no hay un tiempo muerto entre
pulso y pulso
*****************************************************************************/
#include "rx.h"
/****************************************************************************/
/* Local Defines */
/****************************************************************************/
#define TICKS_PER_US (100) //A 100MHz el Tick es de 10ns x 100 = 1useg
#define TICKS_PER_MS (TICKS_PER_US*1000)
/****************************************************************************/
/* Private Variables */
/****************************************************************************/
static uint32_t pulses[RX_CHANNELS]; //Matriz para guardarel tiempo de cada pulso de cada canal
static uint8_t packet_available; //Bandera para indicar que ya recibimos RX_CHANNELS pulsos
/****************************************************************************/
/* Private Function Declarations */
/****************************************************************************/
static inline void cap2_0_interrupts_on(void);
static inline void cap2_0_interrupts_off(void);
/****************************************************************************/
/* Public Functions */
/****************************************************************************/
// This functions initializes TIMER2 as the receiver interface.
// Assumes system clock is 100 MHz.
void rx_init(uint8_t irq_priority) {
uint32_t ch;
// clear the pulses array
for (ch=0; ch<RX_CHANNELS; ch++)
pulses[ch] = 0;
// start with no packets available
packet_available = 0;
// give power to TIMER2
LPC_SC->PCONP |= (1 << 22);
// set peripheral clock selection for timer 2
LPC_SC->PCLKSEL1 &= ~(3 << 12); //Borramos bits
LPC_SC->PCLKSEL1 |= (1 << 12); // set to "01" = Core Clock (100 MHz)
// set the pin functions to CAP2.0 for P0.4
LPC_PINCON->PINSEL0 &= ~(3 << 8); //Borramos bits. Seteamos a 11 en los bits 8 y 9
LPC_PINCON->PINSEL0 |= (3 << 8); //para elejir CAP2.0 como funcion del puerto P0.4
// set the clock source for TIMER2 to PCLK
LPC_TIM2->CTCR = 0; // this is the default
// set the prescaler
LPC_TIM2->PR = 0; // TC increments on every PCLK
// enable and reset the the counters
LPC_TIM2->TCR = 0x00000003;
LPC_TIM2->TCR = 0x00000001;
// set captures to occur on rising edges
LPC_TIM2->CCR = 0x00000001; //Capture only on Rising edge. Sin interrupciones
// Habilito la interrupcion en la Input Capture
cap2_0_interrupts_on();
// Registramos la interrupcion del Timer2
NVIC_EnableIRQ(TIMER2_IRQn);
// Fijamos la prioridad de la interrupcion
NVIC_SetPriority(TIMER2_IRQn, irq_priority); // '0' es la mayor
}
// If a packet has arrived since the last call, 'packet' is filled with
// the contents of the packet and RX_SUCCESS is returned. If no packet
// is available, RX_NO_PACKET is returned.
// The size of 'packet' must be at least RX_CHANNELS.
// This is NOT thread safe.
uint8_t rx_get_packet(uint32_t packet[RX_CHANNELS]) {
uint32_t avail, ch;
// turn off timer2 capture interrupts while accessing packet_available
cap2_0_interrupts_off();
avail = packet_available; // grab current value
packet_available = 0; // set to zero
cap2_0_interrupts_on(); // Vuelvo a encender la Interrupcion
// check if a packet is available
if (avail) {
// copy packet
for (ch=0; ch<RX_CHANNELS; ch++)
packet[ch] = pulses[ch];
// set as success
return RX_SUCCESS;
}
else {
// set as failure
return RX_NO_PACKET;
}
}
/****************************************************************************/
/* Private Functions */
/****************************************************************************/
static inline void cap2_0_interrupts_on(void) {
LPC_TIM2->CCR |= 0x00000004;
}
static inline void cap2_0_interrupts_off(void) {
LPC_TIM2->CCR &= ~0x00000004;
}
/****************************************************************************/
/* Interrupt Functions */
/****************************************************************************/
void TIMER2_IRQHandler(void) {
// persistent local variables
static uint32_t tmp_pulses[RX_CHANNELS];
static uint32_t prev_ticks = 0;
static uint32_t current_pulse = 0;
// local variables
uint32_t curr_ticks, nanos;
// uint32_t rising_edge;
uint32_t c;
// // check that this interrupt is from CAP2.0
if((LPC_TIM2->IR & 0x0000003F) == 0x00000010){
// compute delta time (each tick is 10ns)
curr_ticks = LPC_TIM2->CR0;
nanos = (curr_ticks - prev_ticks) * 10;
//Si el tiempo del pulso supera los 3mseg es que estamos en el pulso de sincronismo
if (nanos > 3000000){
current_pulse = 0;
}
else {
// store this sample
tmp_pulses[current_pulse] = nanos;
// advance to next pulse
current_pulse++;
// check for end of packet
if (current_pulse == RX_CHANNELS) {
// wrap pointer
current_pulse = 0;
// copy out packet
for (c=0; c<RX_CHANNELS; c++)
pulses[c] = tmp_pulses[c];
// signal available packet
packet_available = 1;
}
}
// clear the interrupt
LPC_TIM2->IR |= 0x00000010;
// save current as previous for next event
prev_ticks = curr_ticks;
}
}
/*
===============================================================================
Name : main.c
Author : elgarbe
Description : Esta práctica es para mostrar un conteo binario con led externos (P2.0..7)
a una frecuencia establecida por el timer. Hace uso de la librería CMSIS
===============================================================================
*/
#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif
#include <cr_section_macros.h>
#include <NXP/crp.h>
#include "RX.h"
#include "uart2.h"
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
volatile uint32_t msTicks;
__INLINE static void delay_ms (uint32_t delayTicks) {
uint32_t currentTicks;
currentTicks = msTicks; // read current tick counter
while ((msTicks - currentTicks) < delayTicks);
}
void SysTick_Handler(void) {
msTicks++; /* increment counter necessary in Delay() */
}
int main(void) {
uint32_t rx_values[RX_CHANNELS];
uint8_t hay_datos = 0;
uint8_t canal=0;
//Configuro el SysTick para que interrumpa cada 1mseg
if (SysTick_Config(SystemCoreClock / 1000)) {
while (1);
}
rx_init(0); //Inicializo el Timer2 para que lea la señal PPM
UART2_Init(115200); // Inicializamos el UART a 115200
UART2_PrintString ("\r\nLeyendo Rx\r\n");
UART2_PrintString ("===== elgarbe =====\r\n");
while(1) {
hay_datos = rx_get_packet(rx_values); //Verifico si hay datos nuevo provenientes del Tx
if(hay_datos == RX_SUCCESS){ //esto es asincrónico, verifico cuando quiero saber
for (canal=0; canal<RX_CHANNELS; canal++){ //Pero la int por input capture
uart2_printUint32(rx_values[canal], 10); //esta constantemente leyendo los datos
UART2_Sendchar(','); //de la señal PPM del Rx
}
UART2_PrintString("\r\n");
}
delay_ms(250);
}
return 0 ;
}
En su momento había hecho con un 16f628 un decodificador de señal PPM (es el que figura en el sitio e-radiocontrol) y funcionaba muy bien. La prueba la realicé tomando la salida de trainer de transmisor RC. Si tu equipo posee dicha salida puedes aprovecharla para no estar trabajando sobre la plaque del recetor.
Muy bueno!!!
pregunta: la parte de RF la tenias hecha tmb?
Sds!