PDA

View Full Version : Decoding PPM pulses with a pic?


OzDragonFlyer
Nov 30, 2005, 05:56 PM
Hey folks,

The way I've been determing pulse widths is:

Set the tmr to x microsecs and generate an interrupt. Use the int routine to increment a counter.

Main program: clear the counter, read the state of the input pin and wait for the transition from high-low (or vice versa)

Once transition occcurs, read the counter (which will give x number of uS)

reset the counter then do the stuff you want to do..

repeat.

I've used this code in signal conditioners but is there a newer/better way (like using some hardware functions in some new pics/avrs) ?

cheers

dave

arneansper
Nov 30, 2005, 07:00 PM
Most precise and least CPU time requiring way is to use the capture function of some PIC-s. You have a free running counter, and when a state of the input changes the value of the counter is copied into capture register where you can read it.

I used PIC that did not have this function and emulated the capture using interrupt. I had free running counter and the ISR copied the value of the counter on each input state change into buffer where it was read by the main program. Source code is attached to this message:

http://www.rcgroups.com/forums/showpost.php?p=4599867&postcount=29

Arne

PentiumPC
Dec 01, 2005, 02:46 PM
been think about this,

send ppm to 2 pins

pin 1 triggers timer on rising edge.
pin 2 capture on falling edge and triggers interrupt.

cheers,

OzDragonFlyer
Dec 01, 2005, 06:10 PM
Thx for the input..

Arne.. thx for the link to your code.. very similiar to the code i use to do this sort of thing. Some great little snippets in there though that might come in handy in future.

That's quite a good idea pentiumpc... and welcome to rcgroups.

Malc C
Dec 01, 2005, 06:31 PM
Here's the ASM code for the RC switch I feature on my web site, originally used for landing lights on a plane. It uses a 12F675 PIC, but I'm sure you can adapt it for different PICs

;12F675 Landing Lights

list p=12F675
#include "p12f675.inc"

__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _CPD_OFF

CURRENT equ 0x20
PREVIOUS equ 0x21
DIFF equ 0x22

org 0x00
nop
goto init

org 0x04
return

init
banksel 0x80 ;select upper register bank
clrf ANSEL ;make all I/O ports digital
movlw 0x0f
movwf TRISIO ;make GP4 and GP5 outputs
movlw 0xd3
movwf OPTION_REG
banksel 0x00 ;select lower register bank
clrf GPIO
clrf CURRENT
clrf PREVIOUS
clrf DIFF

test movf GPIO,w
movwf CURRENT
xorwf PREVIOUS,w
movwf DIFF
movf CURRENT,w
movwf PREVIOUS
btfss DIFF,3
goto test
btfss CURRENT,3
goto stop
start clrf TMR0
goto test
stop movf TMR0,w
sublw 59
btfss STATUS,C
goto lightson
lightsoff bcf GPIO,4
goto test
lightson bsf GPIO,4
goto test

end


Changing the value of sublw will determing the point of the transition from lights on to lights off.

OzDragonFlyer
Dec 01, 2005, 07:32 PM
thats an easier even solution :)

I'm thinking to do a user calibration at the beginning because some ppm pulses are less than 1ms and greater than 2ms. Could probably set the value of your subtraction based on the min/max values.

I'm aware of rc-cams great work but I need to write a program to trigger an autofocus camera, which means holding the focus enable low, reading a focus status pin until it's high (ready) and then needs another low to trigger the shutter. 3 pins all up. Triggering the cam without waiting for the focus status results in blurry pics unfortunately.

alexcmag
Dec 01, 2005, 10:08 PM
How about this:


//#define TICKTIME 1322 // 25s
//#define TICKTIME 2200 // 40s
#define TICKTIME 2644 // 50s

#define switch
//#define beacon
//#define select

#pragma bit input @GPIO.3

#ifdef beacon
#pragma bit trisbuz @TRISIO.2
#pragma bit outbuz @GPIO.2
#endif
#ifdef switch
#pragma bit triscam @TRISIO.2
#pragma bit outcam @GPIO.2
#ifdef select
#pragma bit trissel @TRISIO.4
#pragma bit insel @GPIO.4
#pragma bit wpusel @WPU.4
#pragma bit trisgnd @TRISIO.5
#pragma bit outgnd @GPIO.5
#endif
#endif

void delay10( char n)
{
char i;
OPTION = 7;
do {
clrwdt(); // only if watchdog enabled
i = TMR0 + 39; /* 256 microsec * 39 = 10 ms */
while ( i != TMR0)
;
} while ( --n > 0);
}

#ifdef switch
void click(void) // clicks the camera
{
triscam = 0;
outcam = 0;

delay10(60); // 600ms

outcam = 0;
triscam = 1;
}
#endif

#ifdef beacon
void sirene(void) // makes a siren sound
{
char i,j,k,ct;

trisbuz = 0;

for (k = 0; k < 2; k++)
{
for (i = 30; i > 10; i--)
{
for (j = 0; j < 100; j++)
{
outbuz = 1;
for (ct = 0; ct < i; ct++) { clrwdt(); };
outbuz = 0;
for (ct = 0; ct < i; ct++) { clrwdt(); };
}
clrwdt();
}
for (i = 10; i < 30; i++)
{
for (j = 0; j < 100; j++)
{
outbuz = 1;
for (ct = 0; ct < i; ct++) { clrwdt(); };
outbuz = 0;
for (ct = 0; ct < i; ct++) { clrwdt(); };
}
clrwdt();
}
}

trisbuz = 1;
}
#endif

void main(void)
{
// initialize clock
#asm
bsf STATUS, RP0
dw 0x23ff //__call 0x03FF
movwf OSCCAL
bcf STATUS, RP0
#endasm

bit triggerant, shot; // trigger flag, shot flag
char ct;
uns16 timer1, timera; // Timer da cāmera, Timer do localizador
char vant, dif; // Previous reading, dif to current

// initialize IOs
TRISIO= 0b11111111;
// ANSEL = 0b00000000; // Only for PIC12F675
CMCON = 0b00000111;
WPU = 0b00110011;
OPTION= 7;

GPIO = 0;
#ifdef switch
#ifdef select
trisgnd = 0;
outgnd = 0;
trissel = 1;
wpusel = 1;
#endif
outcam = 0;
#endif
#ifdef beacon
outbuz= 0;
#endif

#ifdef testeopto
for (;;)
{
triscam = 0;
shot = insel;
outcam = shot;
clrwdt();
}
#endif

timer1 = 0;
timera = 0;
triggerant = 0;

#ifdef beacon
sirene(); // Signal buzzer when power on
#endif
for (;;) /* Loop infinito */
{
clrwdt();
for (ct = 0; ct < 250; ct++) // Waits pulse
{
if (input == 1) break;
}
clrwdt();

if (input == 1) // It is a pulse, not timeout
{
for (ct = 0; ct < 250; ct++) // Measure pulse
{
if (input == 0) break;
}
clrwdt();

if ( ct < vant ) // Calculate diff from previous
{ dif = vant - ct; }
else
{ dif = ct - vant; }
vant = ct;

if ( (ct >= 90) && (ct <= 210) && (dif < 20) ) // Valid?
{
timera = 0; // Reset LMA timer
if ( ct <= 130 ) // If camera signal, make click
{
if (triggerant)
{ shot = 0; }
else
{ shot = 1; }
triggerant = 1;
}
else
{
shot = 0;
triggerant = 0;
}

#ifdef beacon
#ifdef switch
if ( ct >= 200 ) // If >2000ms, set LMA (with camera)
#else
if ( ct <= 115 ) // If < 1150ms sets the buzzer (no camera)
#endif
sirene();
#endif

#ifdef switch
timer1++;
#ifdef select
if (insel == 0)
{
timer1 = 0;
}
#endif
if (shot || (timer1 >= TICKTIME)) // if much time without taking picture, make it by itself to avoid auto turn-off
{
click();
timer1 = 0;
}
#endif
}
}

#ifdef beacon
timera++; // Incrementa o timer do localizador
if (timera > 200) // If long time without PPM, makes the LMA
{
sirene();
timera = 0;
}
#endif
}
}

OzDragonFlyer
Dec 02, 2005, 03:11 AM
I'm trying to decode this Alex.. Unfortunately my C skills are almost as bad as my brazilian language.. give me assembler any day :)

The LMA is a great idea and a useful addition too. Maybe we can turn this whole thing into a useful project for everyone.

thanhTran
Dec 13, 2005, 05:54 PM
Most precise and least CPU time requiring way is to use the capture function of some PIC-s. You have a free running counter, and when a state of the input changes the value of the counter is copied into capture register where you can read it.
...
Arne
It sounds like the most efficient way to do. Do you have a an example of what PIC that has this capture function?

thanks

thanh

cmosfet
Dec 16, 2005, 10:51 PM
Hi,

What if you wanted to capture 3 or more channels? smaller pics dont have 3 ccp's? Some great ideas on this forum, great place!