Reading 6-channel receiver signals

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all.

I have a 6-channel radio receiver and I want to read the pulse width of each channel. I'm going to use an Atmega32 and from what I gather from the datasheet there is only 3 interrupt pins.

So how should I read them? Is the only way possible to recombine all the channels together and decode them in the µC?

Thank you

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

inktomi wrote:
I'm going to use an Atmega32

You can use AVR like ATmega48 -88 -168 or ATmega164 ATmega324 ATmega644 or some attiny and so. Thay have many pins with int (PxINT) by changes.

See page. 2 datasheet.

ATtiny26 "Pin Change Interrupt on 11 Pins"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How does the receiver output the channel pulses?
Do they overlap?

And most important:
Can you make some oscilloscope pictures?

I tend to post off-topic replies when I've noticed some interesting detail.
Feel free to stop me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

How does the receiver output the channel pulses?
Do they overlap?

They'll be standard servo control pulese - 20ms frame with a 1-2ms active period. The 1-2ms active period on the various channels will not overlap because they are each encoded into the radio transmission one after the other. In fact all the R/C receiver really does is demodulate the RF and split the various channel control signals out of the continuous pulse stream. In fact one way to attack this would be to probe around inside the Rx with a scope and find the point at which the demodulated signal is available but before it's been split - then pump this into a single AVR pin and let it do the channel splitting.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't got my hands onto the radio yet but it is FUTABA 6-channel system. According to pics on the internet the signals do not overlap but if I add them I will get a continuous stream. I think I have to do a positive edge detector and combine them on one line.
Here's the pic:

[img]http://api.ning.com/files/WmjViE...

Messing with the receiver hardware is not an option since it doesn't belong to me.

Would an edge detector and a simple op amp adder suffice?

Thank you for your replies

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

but if I add them I will get a continuous stream.

Yeah but in that picture you could do exactly the analysis they've done on the top "Received PPM frame" line. Each time it rises you start measuring - the first time between rise 1 and rise 2 is the channel1 time, from that second pulse to the third i channel2 and so on...

You know when you have got back to the start when the line remains low for longer than 2ms. ("synchro blank time")

Anyway it was just an idea to save pins cos otherwise you are letting the Rx split it into 6 and then having to monitor 6 different lines into the AVR. My point was that it could be done on just 1 (possibly two if you also used the "Synchro detector output" for a slightly easier life)

Cliff

PS I guess all this is "moot" if you cannot modify the Rx

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

May be one 74hc00 or 74hc02 can help

1 4 ch -> 1 gate -> INT0
2 5    -> 2      -> INT1
3 6    -> 3      -> INT2
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

How does the receiver output the channel pulses?
Do they overlap?

The 1-2ms active period on the various channels will not overlap because they are each encoded into the radio transmission one after the other.

Only true for PPM systems. Not true for PCM, 2.4 GHz RC, and most diversity systems. Your point is where I was planning to get, though ;)

I tend to post off-topic replies when I've noticed some interesting detail.
Feel free to stop me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a3v2r wrote:
May be one 74hc00 or 74hc02 can help

1 4 ch -> 1 gate -> INT0
2 5    -> 2      -> INT1
3 6    -> 3      -> INT2

Good thinking. I will consider it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yup I'm a dyed-in-the-wool PPM user. One day I'll make the jump to the new fangled spread spectrum on 2.4G but I tend to look at everything through PPM goggles (personally I use a Hitec Eclipse 7 with an old Futaba FF6 as backup that I now mainly use for R/C sim flying on my PC with Reflex XTR 5)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I forgot the on-topic part:

You can feed all 6 signals into a multiplexer (HC151 for example). Connect the multiplexers output to your AVR's ICP. Connect any 3 other AVR outputs to the multiplexer's select inputs. Configure the ICP to trigger an interrupt on a rising edge.

Select channel 1 and wait for a rising edge. Store ICR as channel 1 start time.
Select channel 2 and wait for a rising edge. Store ICR as channel 1 end time and store ICR as channel 2 start time.
Do that for all channels, then wrap to channel 1 again.

Calculate all channel pulse widths: width = end - start.

Your timer must be running at a reasonably high frequency (2 MHz will give you 2000 steps per ms). Too fast is not possible on current AVRs.

The above is how I would do it - until now I only had non-PPM- or not-like-PPM-receivers...

I tend to post off-topic replies when I've noticed some interesting detail.
Feel free to stop me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

buffi wrote:
I forgot the on-topic part:

You can feed all 6 signals into a multiplexer (HC151 for example). Connect the multiplexers output to your AVR's ICP. Connect any 3 other AVR outputs to the multiplexer's select inputs. Configure the ICP to trigger an interrupt on a rising edge.

Thanks, that's a very good way to do it and can be easily extended to 8 channels.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
You can feed all 6 signals into a multiplexer (HC151 for example).
If the ADC is unused the mega32 already has a spare multiplexer (the analog comparator can use the ad mux and can be configured for input capture). It should be possible to use the bandgap reference for the positive comparator input so no external components are needed.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, works if I understood the datasheet correctly. That solution can as well be expanded to 8 channels. inktomi, you should really try that!

I tend to post off-topic replies when I've noticed some interesting detail.
Feel free to stop me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

buffi wrote:
inktomi, you should really try that!

Don't worry, I will! Thanks buffi.

Lajon, I have to be carefull with the no of pins I use. I'd rather use an external component than end with no pins! The end target is not well defined so I have to be careful. Thanks for the suggestion anyway. I will probably try it as well.

All that remains is to figure how to do it :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lajon wrote:
Quote:
You can feed all 6 signals into a multiplexer (HC151 for example).
If the ADC is unused the mega32 already has a spare multiplexer (the analog comparator can use the ad mux and can be configured for input capture). It should be possible to use the bandgap reference for the positive comparator input so no external components are needed.
/Lars

Can you make some source code fo it or where in datasheet m32 it is.

I cant find it. Or what registers or bits to see.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is some test code (avr-gcc). I have run this with a PPM receiver (Hitec RCD3500 with a Hitec Eclipse 7 tx) and it seems to work.
/Lars

// Multi servo input test for ATmega32 (or any AVR with a 16-bit timer and
// an analog comparator that can use the AD mux and trigger input capture).
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int usartTx(char data, FILE *stream);
void usartInit(uint16_t b);
void report(void);

static FILE mystdout = FDEV_SETUP_STREAM(usartTx, 0, _FDEV_SETUP_WRITE);

// Number of channels to store from the ad inputs
#define N_CHANNELS 6

// Computing µs from timer1 ticks (prescaler 8)
// (approx unless F_CPU is even MHz).
#define TIMER2US(t) (int)(((t)*(uint32_t)(256.0 * 8E6 / F_CPU)) / 256) 

// Counter for received frames
volatile uint16_t nFrames;

// Channel time data, stored as timer 1 ticks
// This is updated in the ISR and must be accessed with interrupts disabled
uint16_t channelTime[N_CHANNELS];

int main(void)
{
    usartInit(F_CPU / (38400 * 16L) - 1);
    stdout = &mystdout;
    printf("Starting servo input\n");

    // Start with channel 0
    ADMUX &= (1<<REFS1) | (1<<REFS0) | (1<<ADLAR);
    // ADC off
    ADCSRA &= ~(1 << ADEN);
    // Bandgap reference and input capture controlled by AC
    ACSR = (1<<ACBG) | (1<<ACIC);
    // Use AD mux as the comparator input
    SFIOR |= (1<<ACME);     
    // Start timer1 with /8 prescaler and select falling edge for 
    // input capture.
    // Note that the edge sense is reversed because the 
    // AC asserts when the input is lower than AIN0 so this is
    // to capture the first rising edge for the servo at channel 0.
    TCCR1B = (1 << CS11);  
    // Enable the input capture interrupt
    TIMSK |= (1 << TICIE1);
    // Enable interrupts
    sei();

    uint16_t currentNFrames = 0;
    uint16_t lastReport = 0;
    while(1) {
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
            currentNFrames = nFrames;
        }
        if (currentNFrames != lastReport) {
            lastReport = currentNFrames;
            // Report about once per second
            if (currentNFrames % 50 == 49) {
                report();
            }
        }
    }
    return 0;
}

ISR(TIMER1_CAPT_vect)
{
    static uint16_t start;
    uint8_t channel = ADMUX & 7;
    uint16_t t = ICR1;
    if ((TCCR1B & (1<<ICES1)) == 0) {
        // Falling edge on signal (i.e., rising on AC) from now
        TCCR1B |= (1<<ICES1);
    } else {
        channelTime[channel] = t - start;
        if (++channel == N_CHANNELS) {
            channel = 0;
            // Back to rising on signal (i.e., falling on AC) for channel 0 start
            TCCR1B &= ~(1<<ICES1);           
            nFrames++;
        }
        ADMUX = (ADMUX & ((1<<REFS1) | (1<<REFS0) | (1<<ADLAR))) | channel;
    }
    start = t;
}

void report(void)
{
    uint8_t i;
    printf("-----\n");
    for(i = 0; i < N_CHANNELS; i++) {
        uint16_t t;
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
            t = channelTime[i];
        }
        printf("%d: %4d\n", i + 1, TIMER2US(t));
     }
}

void usartInit(uint16_t baudrate)
{
    UBRRL = baudrate;
    // Enable transmitter
    UCSRB = (1<<TXEN);
}

int usartTx(char data, FILE *stream)
{
    if (data == '\n') {
		usartTx('\r', stream);
	}
    while (!(UCSRA & (1<<UDRE)))
        ;
    UDR = data;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot ! It's very interesting.