Jump to content
Sign in to follow this  
JF1980

Capture and decode RX signal

Recommended Posts

Hi,

I'm in the process of teaching myself to program PIC's using C. I've been lookoing to build a small navigation light circuit as it's quite a simple place to start.

I am using a 12F675 running on the internal 4Mhz clock. The dev board is an EasyPic 4 and I'm programming in MikroC. So far I have learnt how to flash LED's using both standard delays and TMR0. I've also learnt how toggle an LED using the External interrupt pin connected to a push-button.

Now I'm confident enough to interface with an R/C receiver (I'm using Spektrum) but I'm looking for advice on how to measure the pulse? So far as I understand:

  • Servo receives a pulse from the RX which is between 1.05ms - 1.95ms with 1.5ms being dead centre.
  • I will need to use the external interrupt to detect a low/high event, start TMR0, clear the interrupt and re-enable.
  • Once I detect the high/low event I need to read the timer to get my pulse width, reset everything and go back to the start.
  • I can then use the pulse width value in my main program to decide whether my lights should be on or off.

I can't find any examples of this in C. The RC-CAM navi lights are supposedly programmed in C but only a HEX is available to download so I can't see how it's achieved (along with rejection and measurement of glitches).

Are my thoughts/assumptions correct or have I missed something? Can anyone point me toward some code samples?

Thanks.

Edited by JF1980

Share this post


Link to post
Share on other sites

I don't know 'C' but you seem to be on the right track.

I started as you did then just built up the program adding the bells and whistles.

My advice is just get stuck in ;)

Terry

Share this post


Link to post
Share on other sites
I don't know 'C' but you seem to be on the right track.

I started as you did then just built up the program adding the bells and whistles.

My advice is just get stuck in ;)

Terry

I've had a hack at it but it doesn't seem to work. I've simplified it down to:

if (INTCON.INTF){ // external trigger detected (low to high)

GPIO.F1 = 1; // turn LED on

INTCON.INTF = 0; // reset interrupt flag

}

So that if the input goes low to high at all then the LED will go on.

I have a resistor pulling GP2 to ground. If I use a push-button connecting GP2 to VCC, the LED goes on. I tried hooking up the receiver (4.7k resistor on the signal line) but the LED doesn't light up, meaning the PIC isn't detecting a low/high event. It's really quite frustrating.

A multimeter across GND and the signal line showed 2.2v; is that enough to trigger the interrupt?

Share this post


Link to post
Share on other sites

What PIC is it?

What PIN are you trying to trigger?

Did you remove the pull-down resistor before connecting to the Rx?

What R/C Rx?

Share this post


Link to post
Share on other sites
What PIC is it?

What PIN are you trying to trigger?

Did you remove the pull-down resistor before connecting to the Rx?

What R/C Rx?

It's a 12F675, triggering GP2. I have not removed the pull-down resistor (doesn't this keep the line low when there is no pulse?). I'm using s Spektrum DX7 with a 6100E RX.

I've configured the I/O as below:

// initial setup of registers, ports etc

ANSEL = 0; // set AD Pins to digital I/O

CMCON = 7; // turn off comparators

WPU = 0x00;

TRISIO = 0x04; // configure pins of gpio as output

//GPIO = 0; // clear all GPIO Pins

// set initial variable values

i = 0;

//configure initial TMR0 setup

OPTION_REG.T0CS = 0; // bit 5 TMR0 Clock Source Select bit...0 = Internal Clock (CLKO) 1 = Transition on T0CKI pin

OPTION_REG.T0SE = 0; // bit 4 TMR0 Source Edge Select bit 0 = low/high 1 = high/low

OPTION_REG.PSA = 0; // bit 3 Prescaler Assignment bit...0 = Prescaler is assigned to the WDT

OPTION_REG.PS2 = 0; // bits 2-0 PS2:PS0: Prescaler Rate Select bits

OPTION_REG.PS1 = 1;

OPTION_REG.PS0 = 0;

// configure initial INT setup

OPTION_REG.INTEDG = 1; // trigger external INT on rising edge

INTCON = 0x90; // turn on INTE on GPIO Pin 2

Hope that's helpful?

Share this post


Link to post
Share on other sites

First, a word about "pull-downs." I don't use them. Instead, I use pullups and only where necessary. Besides being a convention that was driven into me from by-gone TTL days, pull-ups will help minimize conflicts if the PIC's internal weak pull-up is enabled.

And speaking of the PIC's internal pull-ups, be sure to use them. No need to solder on a resistor. Your test switch just needs to switch to ground (logic 0).

Secondly, some R/C Rx's have series protection resistors on the servo outputs. If you use an external pullup or pull-down and the value is not chosen with care, the R/C pulse can become attenuated and lose valuable amplitude or rise above ground. The PIC's internal 40K ohm internal pullup is generally safe for this task.

Lastly, the dirty little details in the PIC data sheet will hint to you that the GP2 input is a ST type input when configured for Interrupt usage. So if your PIC is powered by 5VDC, the incoming logic pulse must be >0.8VDD (~4.0VDC). In your case, you'll never see that because the Spektrum AR6000 uses 3V logic.

A cheap work around is to power the PIC from lower supply voltage. Or, condition the R/C input signal with a TTL characterized buffer to restore the voltage level to 5V. Or, use a transistor. Or, use whatever other method that seems appropriate for the design. There's many ways to skin cats.

Share this post


Link to post
Share on other sites

Any general purpose NPN configured as an inverter will solve it.

Install a series 4.7K ohm for base current limit and use the PIC's weak pullup on the collector. Collector to GP2. Emitter to ground. Done.

I started as you did then just built up the program adding the bells and whistles.

My advice is just get stuck in

I hear you. The only way to learn any of this is to just roll up your sleeves and do it. Then ask questions when your best efforts don't work out. Frankly, just shoe-horning in someone else's code will only postpone the problems where they are often much harder to grasp and solve. Baby steps.

Share this post


Link to post
Share on other sites

Yep, I've spent (wasted) many more hours trying to understand someone elses code than I ever have writing my own.

It's much easier to deal with what you understand.

Terry

Share this post


Link to post
Share on other sites

At first glance that seems like it would do the inverse of what I actually want, but after drawing it out I think I understand:

  • Pull-up resistor on GP2 essentially brings the pin high, however it does not put the pin in a high state intitally as current can't flow to ground.
  • GP2 also connects to the collector of an NPN transistor.
  • The NPN emitter connects to ground, therefore when power is applied to the base, current can flow from +5v via the GP2 pull-up and then the NPN down to ground which means that GP2 senses a positive input.
  • A 4.7K resistor is used between the RX output and base. I'm not sure exactly why nor how the value of that resistor is calculated.

My initial thought would was that GP2 will sink a certain amount of current while acting as an input, therefore I would have used a pull-down resistor on GP2 to give a default low state and then used the transistor to apply positive voltage to the pin, e.g. collector on ground and the emitter on GP2. Can you verify that this would have been incorrect?

I'm going to experiment a little with this but I want to make sure I don't fry an expensive receiver in the process!

Share this post


Link to post
Share on other sites

Here's my quick and dirty camera R/C trigger program for a 12F675, PICC compiler:

#include <12F675.h>


// Camera has 2 lines for focus and shutter, 3.3V pulled down by 2-pos switch

#define Servo_in	PIN_A5 //PIN_A0

#define Focus_out	PIN_A1 //PIN_A4

#define	Shutter_out	PIN_A0 //PIN_A1


void main(void)

{

	ENABLE_INTERRUPTS(GLOBAL);

	ENABLE_INTERRUPTS(INT_RA5); // Pin state change interrupt on pin GP5

	port_a_pullups(0x20);


	while(1);

}


#INT_RA // Port state change interrupt

void servo_int(void)

{

	int16 Width=0;


	if(input(Servo_in)) // rising edge

		{

		SET_TIMER1(0);			

		SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_1);	//reset and start timer

		}

	else	// falling edge

		{

		SETUP_TIMER_1(T1_DISABLED);	//stop timer

		Width = GET_TIMER1();


		if(Width > 1333)

			output_low(Focus_out);

		else

			input(Focus_out);


		if(Width > 1666)

			output_low(Shutter_out);

		else

			input(Shutter_out);

		}


}

No check of any kind though, as I don't care a single bit if a glitch triggers a shot. But checking if the pulse is in the valid range is just a line away, and a couple more if you also want to filter stray pulses that are more than X ms away from the previous one etc.

I use the internal pull-up on the input, the PIC is directly connected to the receiver's power supply, ground and the wanted signal line without series resistor or anything.

My initial thought would was that GP2 will sink a certain amount of current while acting as an input

No! When a pin is an input it's in high impedance mode, so one can assume it's open for that kind of apps.

Pull-up resistor on GP2 essentially brings the pin high, it does put the pin in a high state intitally as current can't flow to ground.

If current flowed, the drop in the pull up resistor wouild bring the pin to low. With no current (NPN open) the pin is high.

The NPN emitter connects to ground, therefore when power is applied to the base, current can flow from +5v via the GP2 pull-up and then the NPN down to ground which means that GP2 senses a negative(logic low) input.

See above.

That transistor setup inverts the signal, as suggested by Mr.RC-CAM mentioning an "inverter". So you'll have to logically invert that in your software routine to compensate.

Edited by Kilrah

Share this post


Link to post
Share on other sites
That transistor setup inverts the signal, as suggested by Mr.RC-CAM mentioning an "inverter". So you'll have to logically invert that in your software routine to compensate

So I don't really understand why I would want to invert the signal? Why wouldn't I use a pull-down to ground (rather than pull-up to +5v) and swap the collector/emitter so that the collector is going to +5v and the emitter to GP2? Wouldn't that put GP2 high when the base goes high? That's what I was initally thinking of doing when a transistor was mentioned. It just makes more sense to me if high = high and low = low.

I hope nobody thinks I'm being difficult here, I would just like to understand why the solution I would have come up with is the wrong one.

Thanks.

[EDIT]

From the information I can find on Google; both will work however pull-up is prefered for TTL circuits as pins often float high, pull-up consumes less power. Pull-down is prefered for CMOS (3.3v levels) but as I'm pulling up/down on the TTL side of the circuit (the PIC side rahter than the RX side) an inverse pull-up is best.

Is this correct?

Edited by JF1980

Share this post


Link to post
Share on other sites

Still not cracked it.

I've used a BC547 (what I could find in the box of bits) with:

Base connected to the pulse channel via a 4.7k resistor

Collector going to GP2

Emitter connected to ground

I have enabled the weak pull-up on GP2.

With a multimeter across +5V and the emitter I read +5v.

  • 4.97v between ground and GP4 (which is an output switched on).
  • Between ground and the signal wire I have 0.27v and 0.06 Khz.
  • Between ground and GP2 I have 2.26v and 0.04Khz

The following code does not set GPIO.F1 high:

void main() {

// initial setup of registers, ports etc

WPU = 0x04; // enable weak-pull-up on GP2

OPTION_REG.NOT_GPPU = 1; // enable weak-pullup

ANSEL = 0; // set AD Pins to digital I/O

CMCON = 7; // turn off comparators

TRISIO = 0x04; // configure pins of gpio as output

//GPIO = 0; // clear all GPIO Pins

// set initial variable values

i = 0;

rx_out = 0;

//configure initial TMR0 setup

OPTION_REG.T0CS = 0; // bit 5 TMR0 Clock Source Select bit...0 = Internal Clock (CLKO) 1 = Transition on T0CKI pin

OPTION_REG.T0SE = 0; // bit 4 TMR0 Source Edge Select bit 0 = low/high 1 = high/low

OPTION_REG.PSA = 0; // bit 3 Prescaler Assignment bit...0 = Prescaler is assigned to the WDT

OPTION_REG.PS2 = 0; // bits 2-0 PS2:PS0: Prescaler Rate Select bits

OPTION_REG.PS1 = 1;

OPTION_REG.PS0 = 0;

// configure initial INT setup

OPTION_REG.INTEDG = 0; // trigger external INT on falling edge

INTCON = 0x90; // turn on INTE on GPIO Pin 2

-----

// main interrupt handler

void interrupt(void) {

// measure pulse on EINT pin

// start low

// is ping going low/high or high/low

// start timer and swap int state, clear int flag

// next interrupt stops timer and take reading of timer

// overflow creates rejected data

// start again

// servo uses 2ms pulse 1.5ms is central: http://www.seattlerobotics.org/guide/servos.html

// 1.05ms - 1.95ms

// if pulse is too long or too short or invalid, reject it

if (INTCON.INTF){ // external trigger detected

GPIO.F1 = 1;

INTCON.INTF = 0;

}

}

It looks like the transistor is not letting enough power through from GP2 to me. Maybe I damaged it while soldering, I will try to replace it.

Edited by JF1980

Share this post


Link to post
Share on other sites
Between ground and GP2 I have 2.26v and 0.04Khz

The voltmeter isn't going to help with this. You need a scope to see R/C pulse details that matter.

But all is not lost. Unplug the transistor's signal wire (with 4.7K) from the R/C Rx. Ground this lead. GP2 should now be at Vcc (5V). Now tie the signal lead to Vcc. GP2 should be 0V.

If not, then the transistor is incorrectly wired (probably backwards) or bad.

If that test works ok, then this wire dancing will emulate the push switch action. I recall you said the switch code worked. So, you should be able to leverage off of that.

Share this post


Link to post
Share on other sites

If I disconnect everything, so only the internal weak pull-up is connected, I still get 2.5V across GND and GP2. I've swapped the chip with the same result.

I must be missing something.

Share this post


Link to post
Share on other sites

If Vcc is 5V, then that indicates that you have not enabled the weak pullup. It's programmable, so YOU have to turn it on.

Share this post


Link to post
Share on other sites

I really won't be much help with your C code. The syntax is not the same as my compiler. When it comes to microcontrollers, there is no standard hardware interface, so every compiler will be different when it comes to manipulating the silicon.

But I am suspicious of OPTION_REG.NOT_GPPU = 1; That seems wrong to me. The GPPU (OPTION_REG.7) bit should be ZERO to enable global pullups. You are settin it to one, which would disable them. If your compiler is designed to invert the logic then you would be correct. If it does what it seems to be showing, then you are backwards.

Share this post


Link to post
Share on other sites

OPTION_REG.NOT_GPPU = 1; gives 0v across GND and GP2.

OPTION_REG.NOT_GPPU = 0; gives 2.57v across GND and GP2.

I'm going to re-study the dev board schematics to check there's nothing else happening there.

[Edit]

I've looked over them and there's nothing else connected to the pin. If I disable the internal pull-up and enable the dev board pull-up (also 10k) I get the same result.

:o

Edited by JF1980

Share this post


Link to post
Share on other sites
OPTION_REG.NOT_GPPU = 0; gives 2.57v across GND and GP2.

Then that is the syntax that enables the pullup.

The EasyPic 4 schematic is not published, so I can't help. But from the sounds of it, I would say you have an LED hanging on the pin. Probably as easy as removing a jumper somewhere to get it off the buss. So look again. Plus, confirm Vcc is really 5V and not 2.5V.

Share this post


Link to post
Share on other sites

There is an LED and resistor connected to that PIN but the switch to GND is off so that shouldn't have an effect.

I can't find anything else hooked up. Very strange, maybe I'll set up the circuit on a bread board.

Share this post


Link to post
Share on other sites
There is an LED and resistor connected to that PIN but the switch to GND is off so that shouldn't have an effect.

If it's a cheap membrane (or dirty) switch it could have some current leakage. Even 50K ohms would cause the PIC's weak pullup to have 1/2 Vcc

Pull the chip off the socket, carefully bend GP2 out, then put it back in. What voltage do you read on GP2 when it is free? If the PIC's GP2 pin has Vcc, then we can extrapolate that there is something connected to the empty socket pin.

What is the voltage across pins 1 and 8 (Vcc and Gnd)?

Share this post


Link to post
Share on other sites
So I don't really understand why I would want to invert the signal?

Because the inverting circuit is simpler. As it's not a big deal to invert again in software, why bother with more complicated hardware?

The "switch" to GND for the LEDs on mE boards is usually a jumper or DIP switch.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×