Help needed timing a pulse (2004 Robovation)

Hi everyone,
I have been reading about CCP pins and interrupts, and am at a complete loss as to what the -simplest- way of timing an input pulse is. Here are the specifics of my application:
-The signal varies from 1mS to 36.99mS
-There is a 65mS low signal between pulses
-I am currently using, essentially, the default code (added sensor capture and filtering for sonar, but the framework is the same).
-It would be best if lag during capture could be avoided (so that the proc. doesn’t wait for a signal when it could be filtering, etc.)
-I will, obviously, need access to the up-to-date value as stored in a variable.
-My PWM generator specifies down to 10uS readability; 100uS would be fine for my purposes.
-The signal is 0V or 5V, through a sensor powered by the analog pins.

Now, I know interrupts can be used like the sonar code, tracking rising and falling edges of the square wave. I also gather that CCP can do this with one pin, and appears like it will not lag the program.

How can I impliment something like this? What code do I need to add to the default code? What pins do I use?

Thank you,
Josh

Yes, you can use a single CCP module to do this, but it would still create an interrupt you’d have to service, and you’d have to constantly change the mode of the CCP module. So you may not actually gain anything. You could also use 2 CCP modules with a lighter interrupt service rountine, or no interrupt service routine and some educated guessing if your data is valid. In fact, you could also use just one interrupt in the same fashion you’d use just one CCP module. Sadly, I see no real way of doing this without some amount of interrupts. Unless you’re fine with making an (very well) educated guess about the validity of your data.

Sooo… The question would be exactly how much help you’re looking for. All the info you actually need is in the data sheet, pages 149-151. And in the Edu-bot programming reference, to put the PWM outs into USER_CCP mode. If you want, I can whip out very rough pseudo code for the scenarios about so you can see the differences.

Thanks for the reply, Kevin -
I have nothing against interrupts per-se. I simply haven’t used them, so I’m a bit apprehensive about trying. Will they accomplish what I need, without undue amounts of lag?
If you could provide a skeleton code that I can work from, that would be wonderful. I see how to set them up in the software, but their use is a bit beyond me. The overflow/preloading make sense in theory, but I am not positive on how the values for each are derived, or how I can convert the values into a usable time (mS, uS) that may be accessed like a variable.
How does Kevin Watson do this in his infrared receiver code? It looked like he might have some usable way of measuring a pulse width.

Not really a fan of CCP I am… well, assuming you know the key registers, try the following:

  1. find an available timer, 8 or 16 bit, and init it to count such that it counts to max within ur 37ms. Or around that time. Do not set timer overflow to activate an interrupt. Do not start the timer running.
  2. take one of ur interrupt pins and set it to activate on to-high and to-low transitions. in the interrupt, read back the pin to differentiate between to-high and to-low events.
  3. during to-high event, init timer to 0 and start the timer.
  4. during to-low event, stop the timer and copy the 16it value to a 16bit variable
  5. now let the pulses come in, have the output printf-ed and see if anything goes wrong…

this is fully pulse-activated interrupt based so you go about doing ur normal routines leaving this to run by its own :), reading the 16bit variable to check for the last pulse recorded.

edit: about disabling interrupts when modifying values… in this scenario u probably wont need to. But you may want to avoid reading the 16bit variable while it is in the midst of being updated… I’m not sure if 16 bit read/write are single instruction (but longer clocks) or multi instruction. Probably multi I guess… get someone to verify this.

That’s exactly what I would look at. Kevin Watson’s IR receiver code. Specifically the routines for identifying which of the two beacons is being detected. They each produced a different pulse width signal.

In a nutshell:

Set up a timer to increment at whatever resolution you need measure your pulse width at.

Start timer.

Interrupt on each falling edge (or rising, or both depending on what you want).

Service the interrupt by checking your timer variable, and reset your timer variable to 0.

Lather, Rinse, Repeat…

For interrupts, this is a pretty good first project. Just be sure to disable your interrupts temporarily when you modify variables that are modified by other interrupt service routines. In the above example, you’ll want to disable interrupts whenever you increment or reset your timer variable, then re-enable them right after.

So, like others have suggested, I’d look at Kevin’s code. But I’d specifically look at the 2005 frc_interrupts.zip file. That’s a really good interrupt handler, AND it shows you how to set up the timers. As far as actualy implementation:

Use an 1:8 prescale on a 16-bit Timer, anything else is too short. Start the timer, leave it running, it’ll save you the bother of starting and stopping it needlessly. Disable the Timer’s Interrupt, you don’t need it.

Use one of the KBI interrupts, handled by Int_3_Handler through Int_6_Handler. These interrupt on rise and fall. The value passed by the Int Handler function tells you what the state of the pin is.

On the rising edge, when the value passed is 1, clear the timer. If it’s in 16-bit Read Mode, write 0 to TMRxH THEN write 0 to TMRxL.

On the falling edge, when the value passed is 0, read TMRxL to a temp variable, read TMRxH to your actual 16-bit variable, shift 8 bits to the left, and add your temp variable. You’re done.

Stopping the timer is pointless, you have to clear it no matter what, so just clear it at the start and you’re fine.

Your program should copy the value of the variable this routine saves to to a new variable and work with the new variable, as the routine might change the value halfway through your computation otherwise. I’m pretty sure you don’t have to disable interrupts while in the interrupt handler itself in this case.

Using the CCPs is a good bit more complicated, but you’ll get a much more accurate time measure, as the CCP handles saving the Timer value the instant the of the edge trigger, without the delay of entering the interrupt routine, etc.

May I ask, is there a possibility where during the copy of the first 8 bits, the value is something like 255, and when it proceeds to copy the next 8 its, it increments by 1 (due to the overflow of the first 8 bits when the timer ticks a few more times) before it gets copied, resulting in a larger value than expected?..

That would be why you’d prefer having the timer in 16-bit read mode. In this mode, when TMRxL is read, TMRxH is instantly copied to a buffer, which can be read without fear of this problem. Writing is reversed. you write to TMRxH, which is stored to a buffer, then wrtie to TMRxL, which write that value and copies the buffer to the timer at the exact same time.

quick p.s.: once you really get to know them, you really start to love em :smiley:

by the way… if you all examine the assembly code for the add function in C++ you’ll find that you don’t need to reset the timer to zero all the time… also remember that the hardware add done by the timer works the same way… :wink:

have fun. ccp’s rock my socks. craziest thing i’ve done with em is step motor control ~15KHz on the 8722, rate and positional(with accel/decel) control and simultaneous + independent axis motion. :cool:

-Q

:smiley: okay thanks, thats much clearer now… I’d probably missed it when reading the datasheets :stuck_out_tongue:

Haha I’m an idiot at ASM… yet to learn it :confused: What do you mean by the hardware add function works the same way as… ??? :confused:

I’m still figuring how to use CCP to do a read of 8 PWM inputs simultaneously w/o causing much cpu load too :frowning:

the adds roll over to zero… :rolleyes:

let’s for instance take an unsigned integer, 0xFFFF. when we add 0x0001, and perform the “ADD” instruction, the ALU takes the value of the first digit and starts bit shifting, starting the carry to the next digit. but… we don’t have any more bits! :ahh:

so what basically happens in hardware is:

0xFFFF & 0x0000 //clear already filled digits since we’re moving up a place
0x0001 << 0x0005 //working register now 0x0000 with a 1 hanging off to the left

and in the end, we’re left with 0x0000! :smiley:

i think now you understand why you never have to stop+reset the timer.

-Q

oh… think there was a miscommunication here, I thought my idea was to start the timer on a high pulse, and every time on a high pulse it has to reset the timer. Due to timing differences between the timer and the actual pulse, it may not overflow to a nice 0x0000 at the start of a high pulse.
After kevin’s post then I realised that you need not stop/start the timer because you can do a reliable 16bit read, so you’d only do a reset-to-zero on a high pulse and a 16bit read on a low pulse :slight_smile:

Thanks guys,
I think I understand the concept now!
I wrote some code that (should) work, but doesn’t. It reads random values, even if the sensor is still. I can understand how it might read the same error each time, but if the pulse is right, it shouldn’t vary every loop, should it? So I guess, I’m trying to figure out of it’s something to do with my wiring, or something in the program itself. I don’t have access to an oscilliscope to check it, sadly.
Here’s the code:
From interrupts.c

void Initialize_Timer_1(void)  
{
	TMR1L = TMR1H = 0x00;		           	// LSB-TMR1 (read/write) = MSB-TMR1 (read/write) = 0
	T1CONbits.T1CKPS0 = T1CONbits.T1CKPS1 = 1;	// 1,1: 1:8 prescaler (clock=1.25MHz/each tick=800ns)
	T1CONbits.T1OSCEN = 0;	                        // 0  : timer 1 oscillator disabled
	T1CONbits.TMR1CS = 0;	                        // 0  : use the internal clock
	T1CONbits.RD16 = 1;                             // 1  : timer 1 register operations are done in one 16-bit access							
	IPR1bits.TMR1IP = 0;				// 0  : timer 1 overflow interrupt is low priority (leave at 0 on IFI controllers)
	PIR1bits.TMR1IF = 0;				// 0  : timer 1 overflow hasn't happened (set to 0 before enabling the interrupt)
	PIE1bits.TMR1IE = 0;				// 0  : disable timer 1 interrupt on overflow (i.e., a transition from FFFF->0)
	T1CONbits.TMR1ON = 1;				// 1  : timer 1 is enabled (running)
}
............................................................................................
void Int_6_Handler(unsigned char RB7_State)
{
if (RB7_State == 1) {//Clear the timer on the rising edge.
	TMR1L = 0x00; // LSB-TMR1
	TMR1H = 0x00; // MSB-TMR1
}
if (RB7_State == 0) {//Read the timer on the falling edge
	Timer1_Buffer = TMR1L; //Read LSB to 8 bit
	Timer1_Instant = TMR1H; //Read MSB to 16 bit
	Timer1_Instant <<=8; //Shift MSB 8 bits
	Timer1_Instant += Timer1_Buffer; //Append LSB
	//Now convert this into another value (degrees) based upon time.
	//With the 8:1 prescale, a value of 1 on the timer = 800nS.
	//1000nS = 1uS
	//1000uS = 1mS
	//10000nS = .01mS
	//800nS = .008mS = 1 "click"
	//# clicks * .008 = mS
	//(mS - 1)*10 = degrees
	//((Clicks * .008) - 1) * 10 = degrees
	//1mS to 36.99mS (0-360 degrees, 1mS offset)
	Timer1_Readable = ((Timer1_Instant * .008) - 1) * 10; //Copy to the reader variable, after processing the value (converting to degrees).
}
}

The logic seems OK, I think…

Timer1_Readable is an extern variable declared in interrupts.h. I am using a simple printf of an int from my user_routines.

Could it be that the timer is not resetting as it should? I’m going to look for a pattern now…

ehh 2 points:

  1. get a signal generator/constant pulse source to test, if possible
  2. Timer1_Readable is declared as what? Try to avoid things like multiplying by 0.008 … you may need to define Timer1_Readable as a float or single…
    As much as possible when it comes to complex calculations, I’d use a lookup table to speed things up and increase reliability :slight_smile:

Just use one of the PWM outputs to generate a pulse. A PWM value of 127 should generate a pulse width of about one millisecond.

-Kevin

Your problem is almost certainly that you’re clearing the timer in the wrong order for a 16-bit Read/write mode. In this mode TMR1H is a buffer. so you write to the buffer first, then write to TMR1L. Your code for RB7_State == 1 should be:

TMR1H = 0x00; // MSB-TMR1
TMR1L = 0x00; // LSB-TMR1

As it is, your high bits aren’t being cleared, so the value is effectively random, which is what you’re seeing.

Switching the clear order worked!
Thank you to everyone!

Good, simple information about CCP programming here http://ww1.microchip.com/downloads/en/DeviceDoc/41214a.pdf