Is there a way to make a 18F1320 capture any faster?

Status
Not open for further replies.

Triode

Well-Known Member
I'm still trying to improve my servo signal capture project. I found a useful document **broken link removed** which explains how servo pulses are timed relative to each other. I have modified my code to check for the delays between pulses as well and that part seems to work. If I hook up channel 1 and 3 I get two readings, I was pretty excited about that, but if I hook up channel 1 and 2, 2 and 3 or 1,2 and 3 I get nothing. I think maybe it's not fast enough to catch the gap between them, or it could be some issue I haven't even thought of. Being a mechanical engineer but an electronics beginner you guys have gotten me unstuck before. Any ideas what could cause this behavior? And is there a way to make the capture any more accurate?

Code:
//Basic configureation of chip periferials
 #pragma config OSC = INTIO2, WDT = OFF, LVP = OFF
 #include <p18f1320.h>
 #include <delays.h>
 #include<timers.h>

//setup pins for PWM input
#define ReceiverPin PORTBbits.RB3
#define ReceiverTris TRISBbits.TRISB3

//PWM capture variables
unsigned int PWM1RiseTime = 0; //timer value at rising edge capture
unsigned int PWM1FallTime = 0; //timer value at falling edge capture
unsigned int PWM1Width = 0; //calculated width

unsigned int channel1_width = 0;
unsigned int channel2_width = 0;
unsigned int channel3_width = 0;
unsigned int channel4_width = 0;
unsigned int PWMGap = 0; //calculated gap between pulses

char PWM1Edge = 1; //edge currently being monitored 1 = rising, 0 = falling 
unsigned char Channel_Being_Captured = 1; //this will record which channel is being watched


//set up interrupt
void low_ISR(void);//prototype
#pragma code low_vector = 0x08 //0X08 IS LOW 0X18 IS HIGH
void low_interrupt (void){
_asm goto low_ISR _endasm
}
#pragma code
#pragma interrupt low_ISR

void main(void)
{	


	OSCCON = 0x72; //8MHz clock
	while(!OSCCONbits.IOFS); //Wait for OSC to become stable 

	//configure timer1
	//OpenTimer1(0b10010101);
	//PIE1bits.TMR1IE = 1; //periferial interupt register, 1 = timer 1 interupt enabled
	PIR1bits.TMR1IF = 0; //clears the timer 1 interupt flag
	T1CONbits.TMR1ON = 1; //turn on timer
	T1CONbits.T1CKPS1 = 0; //set prescaler
	T1CONbits.T1CKPS0 = 0; //set prescaler


	//setup timer2
	OpenTimer2(TIMER_INT_ON);
	PIR1bits.TMR2IF = 0; //clears the timer 2 interupt flag
	PR2 = 20;
	T2CON = 0b00000100; //(-)always 0 (----) postscale (-)on/off (--) prescale

	//configure CCP1
	CCP1CON = 0b0000101; //configure CCP1 for capture, rising edge
	INTCONbits.PEIE=1;		//enable peripheral interrupts
    PIE1bits.CCP1IE=1;		//enabled CCP1 interrupt
    INTCONbits.GIE=1;		//enable branching to interrupt
	ReceiverTris = 1; //set RB3 for input so the capture can work.
	TRISBbits.TRISB2 = 1; //set rb2 for in so it can be used to differentiate channels
	
	//configure ports
	ADCON1 = 0xff; //all digital
	INTCON2bits.RBPU = 0; //port b weak pullups on
	
	//configure control bits for output
	TRISAbits.TRISA0 = 0;
	TRISAbits.TRISA6 = 0;
	//controll bits for second motor
	TRISAbits.TRISA1 = 0;
	TRISAbits.TRISA4 = 0;	

	while(1)
	{
	}
}



void low_ISR(void)
{		
	

	//Timer 2 flag
	if(PIR1bits.TMR2IF == 1)
	{
		PIR1bits.TMR2IF = 0; //clears the timer 1 interupt flag
	}


	//ccp interrupt
	if(PIR1bits.CCP1IF == 1)
	{	
		PIR1bits.CCP1IF = 0; //clear the flag
		if(PWM1Edge == 1)//if detecting rising
		{
			CCP1CON = 0b0000100;//switch to detect falling edge
			PWM1RiseTime = CCPR1;//save the low timer value for the rise time
			PWM1Edge = 0;//switch to indicate falling edge is next

			PWMGap = CCPR1 - PWM1FallTime; //calculate what the gap between pulses was
			if (PWMGap>25000){Channel_Being_Captured = 1;}



		}
		else //if detecting falling
		{
			CCP1CON = 0b0000101;//switch to detect rising edge
			PWM1FallTime = CCPR1; //save the time of the pwm fall
			PWM1Edge = 1;//switch to indicate rising edge is next

			PWM1Width = CCPR1 - PWM1RiseTime; //(pwm rise time is the time that the pwm rise occured)

			//store in the apropriate channel
			if(Channel_Being_Captured == 1){channel1_width = PWM1Width;}
			if(Channel_Being_Captured == 2){channel2_width = PWM1Width;}
			if(Channel_Being_Captured == 3){channel3_width = PWM1Width;}
			if(Channel_Being_Captured == 4){channel4_width = PWM1Width;}

			Channel_Being_Captured ++; //go to next channel
		}

		
	}
}
 
Sorry to double post but I made some progress which changes the question; the problem seems to be in a different area that I previously thought. After more testing, I found that the receiver shuts down if I connect all three servo ports or any two consecutive ones. But not if I connect just 1 and 3. It still seems to have power, but it produces no signal. It did occur to me that by connecting them all to the same pin I am also connecting them to each other, but I'm not sure how this would cause the problem. any ideas?
 

They short each other out - you can't join them together.

To capture each servo signal, each one needs connecting to a different PIC input pin.
 
Hi, Paul

What about using TMR1 , starting it @ channel 1 rising edge and reading it @ each channel falling edge ???

Just an idea ... moreover , a working idea !!!

Alain
 
Acetronics, that sounds feasible, though I would be limited to the number of timers I have I have considered just doing it that way.

Nigel, I'm pretty sure you're right, so I'm just questioning to understand better. If the device is putting out a high pulse at different times how do they short out? Why would it only happen when I connect two consecutive pins but not ch 1 and 3. And to the point of solving it; is there any way I can filter it, with diodes or something, so that I don't have to figure out a way to read multiple ports? And if not, do you have any suggestions for how I can capture from several ports?
 
In a standard analog RC servo system signal pulses never occur at the same time. You could use diodes to allow a single pin connection if and ONLY if you could absolutely guarantee the servo signals don't overlap such as from a single RC receiver, but the software would get more complicated as you have to figure out where the framing pulse is without the baseband signal which actually contains the frame pulse itself.

Generically RC systems have a single frame, a pulse at the start that is longer than the legal servo signal called the framing pulse which is outputted nowhere, and then each servo is pulsed out one after another until the next frame pulse. I don't think there is a standard for a delay between each pulse or the exact length of the frame pulse itself though, it's all very manufacture dependent, though most should be really close to each other.

If I'm not mistaken given a sensible frame pulse width minimum/maximum servo pulse width and a basic delay between each pulse you can't get more than 8 channels or so in one second worth of space, anything that uses more does something different, some use frame compression to get 12 or so channels.

None of this does you any good if your signals are coming from separate devices as they could overlap. So you need to flesh out your exact setup a little bit better.
 
Last edited:
I thought I described it but it looks like I didn't, it is currently one 3 channel traxxas reciever with the +,- and signal pins from CH1 connected, and sometimes the signal pins from CH2 and 3. Nothing I've tried has shown this frame pulse, what I'm getting is that there are fairly equal, short gaps, about 3000 counts at 8mhz, and long gaps about 30000 counts. And if I start over sorting the pulses on the long ones, which I believe to be the space between the last pulse and pulse 1, I seem to get the same numbers in the same slots each time, if it weren't for this problem where it stops signaling when I connect all three I would have it working.
 
As I said Triode, you're using standard RC equipment, the receiver itself hides the frame pulse, you'll never see it except for that huge 30k count gap in your case. The reason it stopped signaling was already described, you shorted the signals together, you have no need to detect a frame pulse it's right there in your face with the 30k count lack of any signal. You're talking outputs of a receiver here, if one is set to 0 and the other 1, the 1 is going to feed into the 0 and you won't see the one, it's shorted. Use a small signal diode to only allow current to go OUT on all of the signal lines and you should see every pulse, so long as they don't overlap.
 
Ok, I've tried that idea with diodes, and I got several captures, but the pulse counts are about twice as high and a lot less stable, before with a control knob still they might vary from 2550 to 2560, now theyre like 8000 to 8400, and occasionally way outside that range. I'm confused.

Oh, they are small signal diodes, rated 100V/200mA, type 1N4148. BTW thanks for the help.
 
Last edited:
Sceadwian, I'm glad you're willing to look at it. I'm going to go over it all one more time to make sure I have it connected the way I think I do, then I'll draw it up and show you.

edit: hey mike, that's what I'm trying out now, i tried it before but I think I used too low of a resistor. I'm setting it up with a 180K ohm resistor to ground now.
 
Last edited:
I connected it with the 180K ohm resistor to ground and a diode from each receiver signal pin to the PIC input. and it seems to be filtering them correctly, that is, it doesn't shut down and the signals are definitely getting through. But they aren't reliable at all, my timing is no good. I'm going to look at what kind of signal I'm getting to determine if it is a software issue. Like you mentioned before, Sceadwian, I'm going to try to use that long gap as the reference point. But I must have it set to look for the wrong time at the moment. The odd thing is, once I hook up that third channel all the times double still. I'll draw the schematic as soon as I stop changing it, maybe with luck I'll have the solution by the time I get to that point.
 
Last edited:

If you're ORing them together how do you know which pulse is which?.

Capturing servo pulses is a VERY common PIC application, all of which use an I/O pin per connection.
 
You do have a good point. Today I'm going to rewrite and rebuild the whole thing using a stop watch type of system that has each channel on its own pin using transition events. Similar to what Acetronics suggested. It does seem like this would be a common application, but for some reason I haven't been able to find code for many examples, just examples where the code is already in hex form.
 
Here are some hints that may help you figure out a structure for decoding your servo signals.

1) Go with a 4Mhz clock so your ticks are 1 us. Easier to calculate, simulate, get results.
2) Start with conditional statements to begin/synchronize after the large blank period.
3) Just poll the inputs in consecutive loops that are 10 µs long. Use MPLAB or other simulator to verify number of instructions or stopwatch time.
4) Send out commands during the large blank period.
5) Start all over again.

So in basic syntax:

Code:
Main:
counter1 = 0
counter2 = 0
counter3 = 0
Do Until PortX.1 = 1
    Do While PortX.1 = 1
        delay Y us
        counter1 += 1
    Loop
    Do While PortX.2 = 1
        delay Y us
        counter2 += 1
    Loop
    Do While PortX.3 = 1
        delay Y us
        counter3 += 1
    Loop
Loop

;Do something with counter1,2, and 3.
goto Main
 
Well, this is embarrassing, I'm already stuck at just making the inputs work, and it has me confused because I've used inputs lots of times:

Code:
//Basic configureation of chip periferials
 #pragma config OSC = INTIO2, WDT = OFF, LVP = OFF

//includes
 #include <p18f1320.h>
 #include <delays.h>
 #include<timers.h>

//set up interrupt
void low_ISR(void);//prototype
#pragma code low_vector = 0x08 //0X08 IS LOW 0X18 IS HIGH
void low_interrupt (void){
_asm goto low_ISR _endasm
}
#pragma code
#pragma interrupt low_ISR

unsigned int B0_rise_count = 0;

void main(void)
{	
	
	TRISB = 0b11111111; //set B0 to an input
	INTCON2bits.RBPU = 0; //turn on weak pullups for inputs
	
	//setup pin B0 interrupt
	INTCONbits.INT0IE = 1;//B0 enable interrupt (1 = enabled)
	INTCON2bits.INTEDG0 = 1; //set B0 detection (1 = rising)
	INTCONbits.INT0IF = 0;//set B0 flag (0 = clear 1 = set)
	
	INTCONbits.PEIE = 1; //enable periferial interrupts
	
	
	while(1)
	{
		if(PORTBbits.RB0 == 1){
			B0_rise_count ++;
		}
	}
}

void low_ISR(void)
{
	if(INTCONbits.INT0IF == 1){
		INTCONbits.INT0IF = 0; //clear the flag
		
	}
}

the watch window shows that all except for 6 are working as inputs, but nothing I can do makes any of the bits except for 2,3 and 5 change. I'm sure I've used them before. There must be something simple im missing here. By the way B0_rise_count is just for debugging, I was going to put it in the interrupt but it doesn't even make it that far before there's a problem. There must be some simple mistake I'm making that would explain why all the port pins except 2,3 and 5 are not reacting at all (they all stay 0).

This explanation in the data sheet sounds pretty simple in theory:
 
Last edited:
This is driving me insane, I've looked at examples, sent high and low signals to the ports hundreds of times, even switched chips and completely rewrote the code, it just will not respond to anything I do on those ports.

heres what I've got now, like the above it ignores anything I do to port B0.
Code:
#pragma config OSC = INTIO2, WDT = OFF, LVP = OFF
#include <p18f1320.h> /* for the special function register declarations */
#include <portb.h> /* for the RB0/INT0 interrupt */

/*
* For high interrupts, control is transferred to address 0x8.
*/
void high_interrupt (void); /* prototype needed for 'goto' below */
#pragma code HIGH_INTERRUPT_VECTOR = 0x8
void high_ISR (void)
{
_asm
goto high_interrupt
_endasm
}
#pragma code /* allow the linker to locate the remaining code */

unsigned int B0_rise_count = 0;

#pragma interrupt high_interrupt
void high_interrupt (void)
{
INTCONbits.INT0IF = 0; /* clear flag to avoid another interrupt */

B0_rise_count ++;
}

void EnableHighInterrupts (void)
{
RCONbits.IPEN = 1; /* enable interrupt priority levels */
INTCONbits.GIEH = 1; /* enable all high priority interrupts */
}


void main (void)
{
EnableHighInterrupts ( );

OpenRB0INT (PORTB_CHANGE_INT_ON & /* enable the RB0/INT0 interrupt */
PORTB_PULLUPS_ON & /* configure the RB0 pin for input */
FALLING_EDGE_INT); /* trigger interrupt upon S3 button depression */
}
 
Nigel, did you read my post? If you're capture servo signals from a single RC receiver they will never overlap, and there will be a longer gap than normal between two pulses which will be the frame boundary, it's especially easy with a transmitter doing less than 6 channels because the frame gap is quiet long. However if you have the available I/O and are willing to code for it a single I/O line each is just as good. Personally I'd fish around on the receivers PC board for the decoder chips input and tap the baseband signal.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…