PDA

View Full Version : Trying to measure time between two pulses on different pins.



Ryan7777
- 21st August 2014, 15:33
Hello all,

I need to measure the time between two different pulses leading edges.

The trigger pulse is a 60 Hz square wave with a 8.228mS width, 50% duty that is currently being generated by another PIC, but could actually be generated by the PIC I want to do the measuring from to simplify things. It needs to be constantly running.

The second pulse varies in width by three different amounts, 555uS, 1.668mS and 3.33mS, but the width is unimportant to me, I’m just trying to measure the time between the trigger pulses leading edge and the leading edge of this pulse. The second pulses leading edge always stays within the envelope of the first pulse.

I guess I’m trying to measure the phase between the two?

I really need to do this on another, third, pulse as well, it’s the same as above, I just left it out of the explanation of my problem for clarity’s sake. It would be the green pulse in the included drawing

I’ve searched around and found this post to be the most promising: http://www.picbasic.co.uk/forum/showthread.php?t=4299&highlight=pulse+measurement
But I’ve never used the CCP to measure anything or played with interrupts before.

It looks like what I may need with a few tweaks, but it would also be nice to generate the 60 Hz square wave pulse, trigger off of its leading edge and measure the time until the leading edge of the second pulse and third pulses all on one PIC

I need to spit the raw measurement out serially too. 2400 8N1 is fine.

I figure I would grab three readings of the first timing, average, and then three readings of the second timing, average it too and then spit the raw averaged numbers (t1 and t2) out via SEROUT as the measurement doesn’t vary without me making it vary, so I know when to expect a change.

My preferred hardware for this project is a 16F648A running off a 20MHz canned oscillator, but if I could pull it off on a DIP chip with less pins, I’m open to suggestions.

Any and all help is much appreciated!

7419

andywpg
- 21st August 2014, 16:16
What if you were to use DT's Instant Interrupts? Have an interrupt that times out at some frequency - every time that interrupt happens, increment a counter. When the leading edge of the first pulse happens (could also be an interrupt), zero the counter. When the leading edge of the second pulse hits, read the counter and multiply by the frequency of the interrupt to determine the time.

That was my first thought. Never actually done it - but I think it would work.

Ryan7777
- 22nd August 2014, 00:32
Wow, I am shocked speechless.

I have used Darrel's gifts to the PBP world for years now. Most of what I have done with PBP has had some kind of help or influence from Darrel, if not robbing his code line per line. I had his instant interrupts in mind when I started fleshing out this latest pic based project and was hoping he might help me yet again, even though he didn't know who I was.

I was just searching around to see if he was still active and came across the word that he has passed.

Now I don't even know what to say.

He was just an awesome person for helping so many people with the gift of knowledge. I could tell by his enthusiastic posts that it really made him happy to help out and gift his ideas to all of us.

Even when I was at my wits end with a project, just reading his posts would help me push thru my problem. It also made me want to say thank you a million times over and be as nice as I could for all of you that have helped me. Real appreciation. This stuff isn't always easy, and to have someone so willing to guide the way was amazing.

Darrel Taylor will be missed by a whole lot of people, and I think that says a lot for the guy.

R.I.P DT!

richard
- 22nd August 2014, 06:35
a pic with timer1 gate control would be a perfect fit for this , eg 16f1825

HenrikOlsson
- 22nd August 2014, 08:26
Hi,
Questions:

1) Pulse 2 and 3 both falls within the envelope of pulse 1 but is pulse 3 always trailing pulse 2 or can it equally well be leading pulse 2?

2) Once you've got your PIC doing these measurements, what else (if anything) do you need it to do?
(I ask because quite often around here you come up with a possible implementation only to then get something like: Oh, that's great! Now I need to add this and that and..... - Which may render the initial idea and work useless.

3) Will there always be two pulses or is it possible that one (or both) will be missing?

Here's a bare bones aproach, it expects the pulses to be "in order" and there's no timeout or anything. Both can be easily accommodated for though.


Time1 VAR WORD
Time2 VAR WORD
Pulse1 VAR PortB.0
Pulse2 VAR PortB.1
Pulse3 VAR PortB.2

Main:
GOSUB Measure
' Do whatever with the measurements
Goto Main


Measure:
T1CON.0 = 0 ' Stop Timer
TMR1H = 0 : TMR1L = 0 ' Clear timer

While Pulse1 : WEND ' Wait until the 60Hz Square wave signal is low before "arming"

While Not Pulse1 : WEND ' Now wait for rising edge
T1CON.0 = 1 ' Start timer

WHILE Not Pulse2 : WEND ' Wait for leading edge of second pulse
Time1.HighWord = TMR1H
Time1.LowWord = TMR1L

While Not Pulse3 : WEND ' Wait for leading edge of third pulse
Time2.HighWord = TMR1H
Time2.LowWord = TMR1L
RETURN


/Henrik.

richard
- 22nd August 2014, 08:48
henrik
is there a possibility of tmr1l rolling over after tmr1h read causing an error here ?

and as you say will pulse 3 always lag pulse 2 . I also wonder what sort of resolution is required

HenrikOlsson
- 22nd August 2014, 09:15
Hi Richard,
Yes there is. On some chips (not the 16F648 though, I believe) the timer is dubble buffered so you don't need to worry about it. On chips where it isn't you do need to cater for it by reading TMR1H "on both sides" of reading TMR1L and comparing the result to verify that it didn't roll over.

As I said, the example is a bare bones approach. I didn't want to put too much into it before getting more details.

/Henrik.

Ryan7777
- 22nd August 2014, 17:53
Ok,

Sorry I didn’t get back to my issue at hand. I was pretty bummed and just wasn’t feeling it yesterday.

Anyway…

I have two circuits that are identical. They are both clocked with a 60Hz, 50% duty, ~8.2mS pulse width +5 volt going square pulse. Low is 0V, high is 5V. They both need this signal all the time. So for now I’m just using a 12F675 to provide that signal.

With that clock signal running, the two individual circuits each output a +5V square wave of their own, which by itself on a scope, doesn’t look like much but a square wave.

But when you trigger a scope using the leading edge of the 60Hz, 50% duty pulse that the 12F675 is currently generating (and being provided to the clock inputs on the two circuits) and you have one of the other pulses on a second scope channel, you’ll see what is in my drawing, which shows just one circuits output for clarity.

You have the 60Hz pulse with the other square wave nested inside of it. And if you adjust that circuit’s control, the pulse can be moved in relation to the steady 60Hz pulse. The other circuit is identical and does the exact same thing. And yes, it can lead the other circuits pulse (not the 60Hz pulse, both other pusles are always inside of the 60Hz pulse!) or fall behind it, or be right on top of it.

My perfect solution for this would be:

1. Generate the 60Hz 50% duty signal on one pin. Always running. HPWM10 maybe?

2. Trigger a timer off of the leading edge of the 60Hz 50% duty signal (no clue, still haven’t messed with timers!!!)

3. Count until the leading edge of the pulse from the first circuit comes in on a pin, hold that number in a variable.

4. Repeat 2 and 3 a few times to build an average – hold that average in a variable.

5. Trigger a timer off of the leading edge of the 60Hz 50% duty signal

6. Count until the leading edge of the pulse from the second circuit comes in on another pin, hold that number in a variable.

7. Repeat 5 and 6 a few times to build an average – hold that average in a variable

8. Send the measurement from both average variables out via SEROUT.

The future plan is to find out exactly what numbers to expect by spitting them out via SEROUT, once I know the ranges and such, I would build a lookup table using SELECT CASE or something and if the shift in a signal falls within a certain value, send a command out serially. This would be done for both circuits

So: SEROUT serialPin, 4, [circuit1Data, circuit2Data…]

The only timing in this that is critical is the 60Hz pulse must always be there, steady, constant. And of course the measurement itself need to be fairly accurate, with my lookup table I’m planning on building in hysteresis(?) like of

circuit one's variable equals 225, my lookup would handle it as “if x = <230 AND <220 then” – or something like that just to cancel out minor errors.

The signals I am measuring only vary by user input (i.e. turning a knob) and are independent of each other and will be handled by separate lookup tables, so measuring one before the other doesn’t matter, or if they both get measured at the same time. And seeing them change in any fine detail doesn’t matter either, So waiting on a routine to finish or something isn’t a big deal. Mainly its just getting an accurate measurement of the shift between the clock pulses leading edge and each circuits outputs leading edge.

I’d like to do this all on one chip if possible. I was trying to think in terms of the 16F648A because I have them on hand and have used them quite a bit. I’m trying to stay away from having more pins than I need, so I really don’t want to have to use an 18F4550 or something. So if there is something around the same pin count as the 16F648A that can do all this in one chip, great. If not, I can live with the 12F675 and two 16F648A I guess.

As always, thanks to all of you for helping me out!

Respectfully,

Ryan

7420

Amoque
- 22nd August 2014, 18:28
I admit to not following the thread carefully; I find it difficult to follow prewritten cade (even my own) and maybe this has already been suggested, but wouldn't it work to:

Monitor two pins for input signal. First indication set TIMER to 0 and FLAG = 1.
Continue to monitor both pins
If "Other" pin indicates, capture TIMER as "elapsed count" variable, reset FLAG, and calculate for average (as the timer starts from 0 on each initial indication, there is no need to worry for rollover or complicated math).
If "Same" pin indicates, reset TIMER and FLAG = 1 (missed second signal indication - count errors if you like).

?

HenrikOlsson
- 23rd August 2014, 09:10
Ryan,
Don't know if you looked at my previous idea or not. It was an initial idea but had some issues (and syntax errors).
Unfortunately you won't be able to use HPWM to generate the 60Hz output signal - it won't go that low in frequency. There are other ways of doing it of course but for now I've been focusing on the measurments. Here's a second attempt based on the same idea as the previous code, this time with a bit more features and it does compile - I haven't tested it though.


Time1 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse2
Time2 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse3
Temp VAR BYTE ' Temporary variable, use at will

Trig VAR PortB.0
Pulse1 VAR PortB.1
Pulse2 VAR PortB.2

TMR1IF VAR PIR1.0

Main:
GOSUB Measure
' Do whatever with the measurements
Goto Main


Measure:
T1CON.0 = 0 : TMR1IF = 0 ' Stop Timer, Clear Interrupt flag
TMR1H = 0 : TMR1L = 0 ' Clear timer
Time1 = 0 : Time2 = 0 ' Clear measurments

While Trig : WEND ' Wait until the 60Hz Square wave signal is low before "arming"
While Not Trig : WEND ' Now wait for the rising edge of the 60Hz signal
T1CON.0 = 1 ' Start timer

DO
'-------------------------------------------------------------------------------
' Do we have a rising edge on Pulse1 input?
' Capture value ONLY if we haven't already done so.
IF Pulse1 AND (Time1 = 0) THEN
Time1.HighBYTE = TMR1H
Time1.LowBYTE = TMR1L

' Make sure TMR1L didn't overflow inbetween reading the high and low byte
Temp = TMR1H
If Temp <> Time1.HIGHBYTE THEN
Time1.HighBYTE = TMR1H
Time1.LowBYTE = TMR1L
ENDIF
ENDIF
'-------------------------------------------------------------------------------
' Do we have a rising edge on Pulse2 input?
' Capture value ONLY if we haven't already done so.
IF Pulse2 AND (Time2 = 0) THEN
Time2.HIGHBYTE = TMR1H
Time2.LOWBYTE = TMR1H

' Make sure TMR1L didn't overflow inbetween reading the high and low byte
Temp = TMR1H
If Temp <> Time2.HIGHBYTE THEN
Time2.HighBYTE = TMR1H
Time2.LowBYTE = TMR1L
ENDIF
ENDIF
'-------------------------------------------------------------------------------
LOOP UNTIL ((Time1 <> 0) AND (Time2 <> 0)) OR TMR1IF

RETURN

/Henrik.

HenrikOlsson
- 23rd August 2014, 11:22
Hi,
OK, this version generates the 60Hz, 50% dutycycle in the background using TMR0 and DT-Ints.
Since we are generating the output I don't see the need to feed the signal back into the PIC to trig on.
Anyway, this compiles and runs on a 16F628 here on my breadboard. It generates the 60Hz output but I haven't actually fed any pulses back into it to see if the actual measurment code works. You'll have to do that yourself. Please note that since we're using interrupts in the background you can not use SEROUT to send the data since the interrupts will mess with the timing. Fortunately the 628/648 have an USART - use it with HSEROUT as in the code.

Let us know how it works out.



DEFINE NO_CLRWDT 1 ' We need the prescaler for TMR0, can't use the WDT
DEFINE OSC 20 ' Running with a 20MHz x-tal

#CONFIG
__config _HS_OSC & _WDT_OFF & _MCLRE_OFF & _LVP_OFF & _CP_OFF
#ENDCONFIG

' Variables needed by DT-INTS (we miss you Darrel!)
wsave VAR BYTE $70 SYSTEM
wsave1 VAR BYTE $A0 SYSTEM
wsave2 VAR BYTE $120 SYSTEM

Time1 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse2
Time2 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse3
Temp VAR BYTE ' Temporary variable, use at will
Measure VAR BIT ' Semaphore to indicate when we want to start a measurment.

TrigOut VAR PortB.3 ' Pin for 60Hz output
Pulse1 VAR PortB.4 ' Input pin for first pulse
Pulse2 VAR PortB.5 ' Input pin for second pulse

TMR1IF VAR PIR1.0 ' Alias to TMR1 interrupt request flag which we use for timeout feature.

INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas"

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _ISR, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

OPTION_REG = %11000111 ' Prescaler assigned to TMR0, 1:256 ratio
TRISB = %11110111 ' Set PortB direction.

@ INT_ENABLE TMR0_INT ; Enable the 120Hz interrupt for the square wave generator.

Main:
Pause 1000
Gosub Capture

IF TMR1IF THEN
HSEROUT["Timeout before capturing both pulses.",13]
ELSE
HSEROUT["Time 1: ", DEC5 Time1, " ticks.",13]
HSEROUT["Time 2: ", DEC5 Time2, " ticks.",13]
ENDIF
Goto Main

'-------------------------------------------------------------------------------
Capture:
Measure = 1
While Measure = 1 : WEND ' Wait untill ISR have created the rising edge and started the timer
'-------------------------------------------------------------------------------
' Now look for the rising edge of the two pulses we're expecting to recieve.
' Once we see the pulse we grab the TMR1 value and store it.
' If we don't get both pulses before the TMR1 overflows (as indicated by
' the TMR1 Interrupt Flag) we abort the operation. This happens after
' 200ns * 2^16 = ~13ms.
DO

' Do we have a rising edge on Pulse1 input?
' Capture value ONLY if we haven't already done so.
IF Pulse1 AND (Time1 = 0) THEN
Time1.HighBYTE = TMR1H
Time1.LowBYTE = TMR1L

' Make sure TMR1L didn't overflow inbetween reading the high and low byte
Temp = TMR1H
If Temp <> Time1.HIGHBYTE THEN
Time1.HighBYTE = TMR1H
Time1.LowBYTE = TMR1L
ENDIF
ENDIF
'-------------------------------------------------------------------------------
' Do we have a rising edge on Pulse2 input?
' Capture value ONLY if we haven't already done so.
IF Pulse2 AND (Time2 = 0) THEN
Time2.HIGHBYTE = TMR1H
Time2.LOWBYTE = TMR1H

' Make sure TMR1L didn't overflow inbetween reading the high and low byte
Temp = TMR1H
If Temp <> Time2.HIGHBYTE THEN
Time2.HighBYTE = TMR1H
Time2.LowBYTE = TMR1L
ENDIF
ENDIF

LOOP UNTIL ((Time1 <> 0) AND (Time2 <> 0)) OR TMR1IF
'-------------------------------------------------------------------------------
RETURN

'-------------------------------------------------------------------------------
ISR:
If TrigOut = 0 THEN ' If trig output is low....
If Measure = 1 THEN ' ...and we want to start a measurment
T1CON.0 = 0 : TMR1IF = 0 ' Stop timer and clear interrupt flag
TMR1H = 0 : TMR1L = 0 ' Clear the time
Time1 = 0 : Time2 = 0 ' Clear measurements
T1CON.0 = 1 ' And start the timer
Measure = 0 ' Let main program know we've set the ball in motion.
ENDIF
TrigOut = 1 ' Set trig output high
ELSE
TrigOut = 0 ' Trig output was high, set it low.
ENDIF

TMR0 = TMR0 + 94 ' Reload TMR0 for 120Hz interrupt rate
@ INT_RETURN
'-------------------------------------------------------------------------------

/Henrik.

HenrikOlsson
- 23rd August 2014, 12:26
OK, last post, I promise.
I felt I had to test see if it worked the way it's intended and it looks like it actually does, whoohoo!

I set up my Rigol DG4162 to output a a single pulse on each of its two channels and then set it up for external trigger. I fed the 60Hz output signal from the PIC to the trigger inputs on the waveform generator. The output of the generator was fed back into the two inputs on the PIC. On the waveform generator I can set the delay between trigger event and output pulse, I set that to 1.5ms for channel 1 and 4.5ms for channel 2:
7421

The scope confirms these settings:
7422
You can see the rising edge of the 60Hz signal on channel 1 (yellow) and then the two pulses at 1.5ms (blue) and 4.5ms (purple) respectively.

I changed the HSEROUT code in the previous post to

IF TMR1IF THEN
HSEROUT["Timeout before capturing both pulses.",13]
ELSE
HSEROUT["Time 1: ", DEC4 Time1/5, " us.",13]
HSEROUT["Time 2: ", DEC4 Time2/5, " us.",13,13,13]
ENDIF
And it says

Time 1: 1525 us.
Time 2: 4523 us.
It's not perfect but pretty close. If it's critical it might be possible to tweak some of the error out but I'll leave it at this for the time being.

Ryan, let us know if you try it!

/Henrik.

Ryan7777
- 23rd August 2014, 15:32
Henrik,

That looks awesome! I will try it this weekend when I get some time.

I did get it to work on an 18F2320 by taking the first bit of code that Bruce posted in the link in my initial post and modifying it a bit. Basically I could only measure one circuits pulse in relation to the 60HZ signal though. Not both circuits.

I did that by taking out his failing edge capture and replacing it with a rising edge capture of the second pulse via CCP2

So CCP1 looks for the rising edge on the 60Hz signal, and then it switches to look for the rising edge of the second pulse on CCP2. The rest is the same as his code for the most part.

I think its pretty close to your original thinking, except using CCP to capture instead of raw pins.

I got some really nice, workable results right off the bat, so I was pretty happy I figured something out at least :D

I can wait to try your code out, as it gets me even closer.

Thanks so much to all of you for helping and lending your thoughts. I'll let you know how it goes!

-Ryan

HenrikOlsson
- 23rd August 2014, 22:13
Hi Ryan,
Yes, using a chip with two CCP modules will allow you to get the absolute best results.
Basically, what we're doing here is the same thing as the CCP module is doing when operating in capture mode except that we are polling the input pins in software in order to determine when to "capture" the timers value while that's being done all in hardware when using the CCP module.

Polling the inputs like in my example will always introduce some latency, jitter etc. You said your preferred hardware for this was the good old 628/648 (which only got one CCP module) so that's what I tried to do :-)

/Henrik.

Ryan7777
- 28th August 2014, 13:40
Ok,

Super big thanks Henrik for helping me get this working on a 16F648A!

It seems to do ok, but of course I can’t leave well enough alone.

The 16F648A code does what I need, but has some “quarks” I assume are due to the polling. Like if I run the same signal into both capture pins, the times are different by a bit, which I would think comes from the fact one pin is getting polled later than the other, which is fine, I can live with it.

But wanting to learn more about interrupts and timers, I tried taking the code for 18F chips using Darrel’s instant interrupts that Bruce did in the post here: http://www.picbasic.co.uk/forum/show...se+measurement and combining it with the code that Henrik provided.

This is what I have so far for an 18F2320, 20MHz HS, 2.60C, All configs set properly (off , input pin, etc.):


DEFINE NO_CLRWDT 1
DEFINE OSC 20
DEFINE HSER_BAUD 9600
DEFINE HSER_TXSTA 20h

TrigOut VAR PortB.3

Time1 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse2
Time2 VAR WORD ' Time between leading edge of Pulse1 and leading edge of Pulse3
Measure VAR BIT ' Semaphore to indicate when we want to start a measurment.
OverFlows VAR BYTE ' Timer1 overflow total


INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; Include if using PBP interrupts

;----[High Priority Interrupts]----------------------------------------
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _SixtyHz, PBP, yes
INT_Handler CCP1_INT, _Capture1, PBP, yes
INT_Handler CCP2_INT, _Capture2, PBP, yes
INT_Handler TMR1_INT, _Timer1, PBP, yes

endm
INT_CREATE ; Creates the High Priority interrupt processor
ENDASM

CCP1CON = %00000101 ' Capture1 mode, capture on rising edge
CCP2CON = %00000101 ' Capture2 mode, capture on rising edge
T0CON = %11000111 ' Prescaler assigned to TMR0, 1:256 ratio
T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off (200nS per count @20MHz)

@ INT_ENABLE TMR0_INT ; Enable the 120Hz interrupt for the square wave generator.

Main:
Gosub Capture
IF T1CON.0 = 0 THEN ' If done, show result
hserout ["Timer Overflows = ",DEC OverFlows]
hserout [" Remaining ticks = ",dec time1]
hserout [" Remaining ticks = ",dec time2,13,10]
ENDIF
PAUSE 2000
GOTO Main

'-------------------------------------------------------------------------------
Capture:
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP1_INT ; Start new capture
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP2_INT ; Start new capture
RETURN
'-------------------------------------------------------------------------------


'---[CCP1 - interrupt handler]------------------------------------------
Capture1:

Time1.LowByte = TMR1L ; Get remaining Timer1 counts
Time1.HighByte = TMR1H

'---[CCP2 - interrupt handler]------------------------------------------
Capture2:

Time2.LowByte = TMR1L ; Get remaining Timer1 counts
Time2.HighByte = TMR1H
@ INT_RETURN

'---[TMR1 - interrupt handler]---------------------------------------------
Timer1:
OverFlows = OverFlows + 1
@ INT_RETURN

'-------------------------------------------------------------------------------
SixtyHz:
If TrigOut = 0 THEN ' If trig output is low....
If Measure = 1 THEN ' ...and we want to start a measurment
T1CON.0 = 0 : PIR1.0 = 0 ' Stop timer and clear interrupt flag
TMR1H = 0 : TMR1L = 0 ' Clear the time
Time1 = 0 : Time2 = 0 ' Clear measurements
T1CON.0 = 1 ' And start the timer
Measure = 0 ' Let main program know we've set the ball in motion.
ENDIF
TrigOut = 1 ' Set trig output high
ELSE
TrigOut = 0 ' Trig output was high, set it low.
ENDIF

TMR0 = TMR0 + 94 ' Reload TMR0 for 120Hz interrupt rate
@ INT_RETURN
'-------------------------------------------------------------------------------


END

It compiled the first time with an error about TMR0 not being defined, so I edited the .INC file and got around that, but of course it still doesn’t work. No 60Hz square wave out and no serial output.

Anyone care to have a gander at my code and tell me what I’m doing wrong? Point and laugh?? Poke at me with a stick???

Huge thanks as always!

-Ryan

HenrikOlsson
- 28th August 2014, 14:07
Hi Ryan,

Like if I run the same signal into both capture pins, the times are different by a bit, which I would think comes from the fact one pin is getting polled later than the other, which is fine, I can live with it
Yes, that's correct.

I think you're trying to bite off a bit too much at a time with this new version.
I'd start by getting the serial output going without any interrupts or anything, a simple Hello World kind of thing.
Then I'd get a LED blinking on what will eventually be the 60Hz output, just so you know you have control over that pin.
Then I'd get the TMR0 interrupt going.
Then.... ah you get the picture ;-)

For example, PortB.3 (your 60Hz output) is an ADC input and it "comes up" in analog mode, I don't see you setting it to digital (see ADCON1 register).
You say "All configs set properly" but I don't see you setting any TRIS register in the code. All pins comes up as inputs, TRIS bit needs to be cleared for them to become outputs.

/Henrik.

Ryan7777
- 28th August 2014, 17:34
Alright, you caught me!

I wrote that, or rather pieced it together when I should have been going to bed for the evening. It does have the typical “noob” mistakes of not turning the analog inputs off and setting TRIS for my output pins and probably a few more mistakes! :p

I have a bad habit of tackling this stuff right before bed, figuring I can get things off my mind before trying to sleep with ideas bouncing around in there. Then I start racing the clock and trip myself up. I’m an electronics technician by profession, but it’s actually mostly high voltage and tubes for me. I don’t get to do as much programming unless it’s my own stuff, after work.

Another problem is that, as you can see by my forum join date, I’ve been using PicBasic Pro a few years now. But I only work on PIC based projects once every few months, sometimes longer. And as they say: if you don’t use it…

But that said I’ve done all sorts of complex projects with PicBasic Pro, from thermostats with one-wire sensors and LCD readouts to all kinds of PWM motor and lighting controllers, robots, etc. I’ve just always found ways around using scary interrupts and timers. But I guess that’s what separates the men from the boys.

I tend to use SEROUT over HSEROUT because it doesn’t require outside help to communicate directly with the serial port on my PC. And I did remember that when trying to run your code the first time and gibberish was appearing in hyperterminal . I just inverted it with a 4049 and all was well. So a “hello world” I can handle.... I hope! :confused:

I’ll start again and try to work on a bit at a time (smaller bytes :D) and maybe get it working. But I think I’m having trouble wrapping my little brain around Interrupts. I mean I understand the basics of it. Like: when pin 1 sees signal X, jump to Y routine without regard to what the program was doing, and then return to that point in the program when the ISR is satisfied. But in the context of actual code, I’m clueless.
Like:

Capture:
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP1_INT ; Start new capture
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP2_INT ; Start new capture
RETURN

Isn’t this the same as polling kind of sort of? Why would I want to do that? Am I even supposed to be doing that?! Does it really matter if CCP2 gets a read of the counter before CCP1? On and on…

Anyway, I’ll play around with it until I either figure it out or get tired and go back to hiding from the hard stuff!

Thanks Again!
Ryan

HenrikOlsson
- 29th August 2014, 07:07
Hi Ryan,

I wrote that, or rather pieced it together when I should have been going to bed for the evening. It does have the typical “noob” mistakes of not turning the analog inputs off and setting TRIS for my output pins and probably a few more mistakes!

I have a bad habit of tackling this stuff right before bed, figuring I can get things off my mind before trying to sleep with ideas bouncing around in there. Then I start racing the clock and trip myself up. <snip>
Yes, I think we're all guilty of the above now and then ;-)


Another problem is that, as you can see by my forum join date, I’ve been using PicBasic Pro a few years now. But I only work on PIC based projects once every few months, sometimes longer. And as they say: if you don’t use it…
That's the situation in my case as well. It can go months without me working on a particular project. However, what I do is visit this forum (and the MELABS forum (though it's pretty silent most of the time)) and trying to help other members with their issues, discuss implementation ideas etc. Doing that helps me remember what I've learned and learn a lot of new stuff.


But that said I’ve done all sorts of complex projects with PicBasic Pro, from thermostats with one-wire sensors and LCD readouts to all kinds of PWM motor and lighting controllers, robots, etc. I’ve just always found ways around using scary interrupts and timers. But I guess that’s what separates the men from the boys.
Not at all. Everything is hard (more or less) until you know how to do it. Interrupts are no different but Darrel has really made a BIG difference with the DT-INTS routines. It makes it really quite easy without the drawbacks of ON INTERRUPTS, though it does have it drawbacks which you'll also get familiar with over time.


I tend to use SEROUT over HSEROUT because it doesn’t require outside help to communicate directly with the serial port on my PC. And I did remember that when trying to run your code the first time and gibberish was appearing in hyperterminal .
Nothing wrong with SEROUT but you really "should" get up to speed with using HSEROUT, especially so now when you start introducing interrupts because once you do your SEROUT will basically stop working (if you don't see to it that an interrupt will NOT fire while the PIC is executing the SEROUT). I've got a couple of different development boards and they all got level translators (MAX232 etc) and/or a FTDI USB<->RS232 chip on them so I usually don't need to do anything special. When breadboarding (as I did with this code) I've got this thing (http://www.mikroe.com/add-on-boards/communication/max232/) which makes it really easy.





Capture:
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP1_INT ; Start new capture
Measure = 1
While Measure = 1 : WEND ' Wait until ISR has created the rising edge and started the timer
@ INT_ENABLE CCP2_INT ; Start new capture
RETURN
Isn’t this the same as polling kind of sort of? Why would I want to do that? Am I even supposed to be doing that?! Does it really matter if CCP2 gets a read of the counter before CCP1? On and on…
I'm not sure I understand. You're polling the Measure flag but you're not polling the actual input(s). However, the way you have it written above it will first measure the time between the trig pulse and the first pulse. Then it will sit there and wait for the next trig pulse and THEN measure the time between THAT trig pulse and the second pulse. What you probably want to do is enable the CCP interrupts from the SixtyHz interrupt routine and (again probably) get rid of the Capture subroutine alltogether.

/Henrik.

EarlyBird2
- 29th August 2014, 08:29
TrigOut VAR PortB.3

TrigOut is your output but you have not set TRIS register as output for this pin?

Ryan7777
- 3rd September 2014, 20:36
Ok,

Just focusing on getting 60Hz out of the thing. kind of left the other code in there knowing it probably won't work completely until I rewrite it, but....

I fixed ADCON1 (= 7) and TRISB to output on B.3 and now I have a pulse! The only problem is that it seems to be 38.1Hz :confused:

Not knowing what "TMR0 = TMR0 + 94" for me is doing exactly (maybe preloading timer 0 so it overflows earlier or something?) and it has a nice integer there to play with, I changed it to various things, but no difference in the 38.1Hz.

Should I be playing with the prescaler value or something? I figure as long as the oscillator is still 20MHz, it should be the same :confused: I know pretty much nothing when it comes to our timer friends.

My next plan is to maybe get the working code for the 16F648A ported over and seeing if that will work on an 18F with the 18F DTII stuff and go from there.

Thanks for taking the time to help me out!

HenrikOlsson
- 3rd September 2014, 21:07
Hi Ryan,

I fixed ADCON1 (= 7) and TRISB to output on B.3 and now I have a pulse! The only problem is that it seems to be 38.1Hz
That 38.1Hz tells me that your interrupt rate is 76.2(939)Hz, which is exactly what you get when you have 16bit timer free-running at 5MHz. (20Mhz / 4 / 2^16).

On the 16F648 TMR0 is 8 bits wide with an 8bit prescaler shared with the WDT....
On the 18F2320 TMR0 is configurable to be either 8 bit OR 16 bit wide with its own dedicated 8 bit prescaler.

Going by your description alone and not seeing the actual code my guess is you've got the timer configured in 16 bit mode. That'll work too but the code needs to be changed to use the full 16 bit timer instead of the 8bit timer AND 8bit prescaler of the 16F648.


Not knowing what "TMR0 = TMR0 + 94" for me is doing exactly (maybe preloading timer 0 so it overflows earlier or something?) and it has a nice integer there to play with, I changed it to various things, but no difference in the 38.1Hz.
Yes exactly. In the original code where we had a 8 bit timer, "restarting" the timer at value of 94 each interrupt (which isn't REALLY what happens but it's easier to understand) will cause it to overflow in 162 ticks (256-94) instead 256 ticks. With a a 16bit timer running at 5MHz, "restarting" the timer at 94 to in the ISR will only change the interrupt rate by approximately 0.11Hz which is probably why you think you don't see it change.

/Henrik.

Ryan7777
- 4th September 2014, 17:36
I have TMR0 set to 8 bit mode actually T0CON = %11000111.

Changing the prescaler to either 128 or 64 seemed to speed it up. I can't seem to get to 60Hz no matter what I do.

I tried porting over your working code from the 16F648A and using DT_INTS-18 instead of 14, but get a whole bunch of compiler errors.

I notice that the code Bruce wrote using DT_INTS-18 didn't include the wsave varibles that the DT_INTS-14 looks to require?

I know I have workable code and should just leave it alone, but I want to learn how to make this work with CPP on the 18F2320.

As always, thank you!

Haven't tried this yet, but maybe it will work?:


INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; Include if using PBP interrupts

DEFINE OSC 20 ' Running with a 20MHz x-tal

' #CONFIG - doing this manually at the moment via programmer

ADCON1 = 7

TRISB = %11110111 ' Set PortB direction.

T0CON = %11000111 ' Prescaler assigned to TMR0, 1:256 ratio

TrigOut VAR PortB.3 ' Pin for 60Hz output

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _ISR, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR0_INT ; Enable the 120Hz interrupt for the square wave generator.

Main:
Pause 2000
HSEROUT["Is This Working???",10,13]
Goto Main

ISR:
If TrigOut = 0 THEN ' If trig output is low....
TrigOut = 1 ' Set trig output high
ELSE
TrigOut = 0 ' Trig output was high, set it low.
ENDIF
TMR0 = TMR0 + 94 ' Reload TMR0 for 120Hz interrupt rate
@ INT_RETURN
'-------------------------------------------------------------------------------

mackrackit
- 4th September 2014, 20:49
For 60Hz.
Set Timer0 to 16 Bit with a 1:8 prescale
Add this to your variable section

PreLoad VAR WORD
PreLoad = 23868

and in the ISR routine

TMR0L = PreLoad.LowByte
TMR0H = PreLoad.HighByte
I think...

Ryan7777
- 5th September 2014, 01:24
Thanks Dave!

This is what is actually working for me thus far! At least some success:


INCLUDE "DT_INTS-18.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ; Include if using PBP interrupts

DEFINE OSC 20 ' Running with a 20MHz x-tal
DEFINE HSER_BAUD 9600
DEFINE HSER_TXSTA 20h

@ __CONFIG _CONFIG1H, _HS_OSC_1H & _FSCM_OFF_1H & _IESO_OFF_1H
@ __CONFIG _CONFIG2L, _PWRT_OFF_2L & _BOR_OFF_2L
@ __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_1_2H
@ __CONFIG _CONFIG4L, _STVR_OFF_4L & _LVP_OFF_4L & _DEBUG_OFF_4L
@ __CONFIG _CONFIG3H, _CCP2MX_OFF_3H & _PBAD_DIG_3H & _MCLRE_OFF_3H

ADCON1 = 7

TRISB = %11110111 ' Set PortB direction.

T0CON = %10000001 ' 16 bit Prescaler assigned to TMR0, 1:4 ratio

TrigOut VAR PortB.3 ' Pin for 60Hz output
PreLoad VAR WORD
PreLoad = 52000

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _ISR, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR0_INT ; Enable the 120Hz interrupt for the square wave generator.

Main:
Pause 2000
HSEROUT["Is This Working???",10,13] ' The lights are on... is anyone home?
GOTO Main

ISR:
IF TrigOut = 0 THEN ' If trig output is low....
TrigOut = 1 ' Set trig output high
ELSE
TrigOut = 0 ' Trig output was high, set it low.
ENDIF
TMR0L = PreLoad.LowByte ' TMR0 High and low bytes preloaded
TMR0H = PreLoad.HighByte
@ INT_RETURN
GOTO main:

Now to add to it :)

Ryan7777
- 5th September 2014, 17:31
Sorry to keep beating on this post! I just really want to figure this out and having support is nice. Also, this is what I do on my lunch break ha-ha... So I write everthing in notepad, post it here and then I can cut and paste to my IDE when I get home :D

Anyone care to look and maybe do a little bug hunting? I'm probably doing something wrong here. Something feels funny about having interrupts enable other interrupts. Maybe something will go wrong when those interrupts return? I'm not used to "Main" sitting there twirling away and then having my TMR0 doing it's own thing, which also happens to the be the starting point for all the real work.

But what i think should happen is: each time the PulseGen ISR fires off a rising edge, it will start by intializing everything and then it will start TMR1 ticking and look to see which CCP interrupt was last enabled, and if the first one wasn't, it will enable it and mask it so it doesn't get selected the next go around. When CCP1 gets a rising edge, it will stop TMR1, disable itself and TMR1 interrupts, get the count from TMR1 into a variable and return (uh-oh...)

If all goes to plan, it will start over and do the same with CCP2 and unmask CCP1 for the next round.

My worries are since all this is happening from the TMR0 PulseGen interrupt, its timing will be at the mercy of those CCP and TMR1 interrupts... Also where those interrupts are getting returned to. Is that a safe place? Will it continue to skip out of the IF THEN ELSE traps and allow PulseGen to continue on? Maybe a GOTO PulseGen: added to the return points???

Interrupts are hard on the brain! :confused:

Thanks again!!! I promise once I get this going as desired I will give a full disclosure as to what it's for - some of you might find a good use for it. Oh, and I'll shut up and go away for a while :D


INCLUDE "DT_INTS-18.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP-18.bas" ' Include if using PBP interrupts

DEFINE OSC 20 ' Running with a 20MHz x-tal
DEFINE HSER_BAUD 9600
DEFINE HSER_TXSTA 20h

@ __CONFIG _CONFIG1H, _HS_OSC_1H & _FSCM_OFF_1H & _IESO_OFF_1H
@ __CONFIG _CONFIG2L, _PWRT_OFF_2L & _BOR_OFF_2L
@ __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_1_2H
@ __CONFIG _CONFIG4L, _STVR_OFF_4L & _LVP_OFF_4L & _DEBUG_OFF_4L
@ __CONFIG _CONFIG3H, _CCP2MX_OFF_3H & _PBAD_DIG_3H & _MCLRE_OFF_3H

ADCON1 = 7 ' Turn off analog inputs
TRISB = %11110111 ' Set PortB direction.
T0CON = %10000001 ' TMR0 16 bit Prescaler 1:4 ratio
T1CON = %00000000 ' TMR1 prescale 1:1 clock=Fosc/4, TMR1=off (200nS per count @20MHz)
CCP1CON = %00000101 ' CCP1 Capture mode, capture on rising edge
CCP2CON = %00000101 ' CCP2 Capture mode, capture on rising edge

TrigOut VAR PortB.3 ' Pin for 60Hz output
OverFlows VAR BYTE ' Timer1 overflow total
Remainder1 VAR WORD ' Remaining Timer1 ticks after first pulse capture
Remainder2 VAR WORD ' Remaining Timer1 ticks after second pulse capture
FirstCapt VAR BIT : FirstCapt = 0
PreLoad VAR WORD : PreLoad = 52000 ' Preload TMR0 for 120Hz interrupt rate


;----[High Priority Interrupts]-------------------------------------------------------------------

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _PulseGen, PBP, yes
INT_Handler TMR1_INT, _Timer1, PBP, yes
INT_Handler CCP1_INT, _Capture1, PBP, yes
INT_Handler CCP2_INT, _Capture2, PBP, yes
endm
INT_CREATE ' Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR0_INT ' Enable the 120Hz interrupt for the square wave generator.

'-------------------------------------------------------------------------------------------------

'---[Main Routine]--------------------------------------------------------------------------------

Main:
IF T1CON.0 = 0 THEN ' If TMR1 done, show result
HSEROUT["Timer Overflows = ",DEC OverFlows,"Ticks 1 = ",DEC Remainder1,"Ticks 2 = ",DEC Remainder2,10,13]
ENDIF
GOTO Main:

'-------------------------------------------------------------------------------------------------

'---[TMR0 - Interrupt handler / 60Hz Squarewave Trigger Pulse Generator]--------------------------

PulseGen:
IF TrigOut = 0 THEN ' If trig output is low....
TrigOut = 1 ' Set trig output high
TMR1L = 0 ' Clear Timer1 Low Byte count
TMR1H = 0 ' Clear Timer1 High Byte counts
T1CON.0 = 1 ' Turn Timer1 on at rising edge capture
OverFlows = 0 ' Clear over flow counts
Remainder = 0 ' Clear remainder
PIR1.0 = 0 ' Clear Timer1 overflow flag before enable
@ INT_ENABLE TMR1_INT ' Enable Timer 1 Interrupts
IF FirstCapt = 0 THEN ' If first pulse hasn't been looked for yet...
FirstCapt = 1 ' Mark it as such and...
@ INT_ENABLE CCP1_INT ' Start looking for 1st pulse
ELSE
FirstCapt = 0 ' Reset first pulse search mask and...
@ INT_ENABLE CCP2_INT ' Start looking for 2st pulse
ENDIF
ELSE
TrigOut = 0 ' Trig output was high, set it low.
ENDIF

TMR0L = PreLoad.LowByte ' TMR0 High and low bytes preloaded
TMR0H = PreLoad.HighByte
@ INT_RETURN

'-------------------------------------------------------------------------------------------------

'---[CCP1 - interrupt handler]--------------------------------------------------------------------

Capture1:
T1CON.0 = 0 ' Stop Timer1
@ INT_DISABLE TMR1_INT ' Disable Timer 1 Interrupts
@ INT_DISABLE CCP1_INT ' Disable CCP1 Interrupts
Remainder1.LowByte = TMR1L ' Get remaining Timer1 counts
Remainder1.HighByte = TMR1H
@ INT_RETURN

'-------------------------------------------------------------------------------------------------

'---[CCP2 - interrupt handler]--------------------------------------------------------------------

Capture2:
T1CON.0 = 0 ' Stop Timer1
@ INT_DISABLE TMR1_INT ' Disable Timer 1 Interrupts
@ INT_DISABLE CCP2_INT ' Disable CCP2 Interrupts
Remainder2.LowByte = TMR1L ' Get remaining Timer1
Remainder2.HighByte = TMR1H
@ INT_RETURN

'-------------------------------------------------------------------------------------------------

'---[TMR1 - interrupt handler]--------------------------------------------------------------------
Timer1:
OverFlows = OverFlows + 1 ' Count the number of times TMR1 overflows
@ INT_RETURN

'-------------------------------------------------------------------------------------------------

GOTO Main:

END

mackrackit
- 6th September 2014, 12:54
A quick read through your code and I do not see a problem. So the big question...
Does it work as expected?
If so Then NoProblem...

Ryan7777
- 8th September 2014, 12:05
Nope,

It didn't work. I did catch a few mistakes as soon as I posted that. Like needing to comment my assembly stuff with ";" for example. And never flipping "FirstCapt" back after looking for the second pulse. And I had to play with the TMR0prescaler some more. The code I posted before this was running at 120Hz instead of 60Hz.

I think I would have to find a way to keep proper order and timing of when my interrupts fire. When I corrected the above mistakes and ran that code, my output varibles would lock onto a certain value and stay that way. Almost as if the CCPs were firing as soon as their interrupts were enabled. I tried all sorts of stuff and never had any luck with getting it to all work together.

I ended up using what Henrik had come up with for the 16F648A, I just got it to work on the 18F2320 and rolled in capturing with the CCP modules instead of the port B pins. It samples both CCPs 5 times, averages the results and breaks one of them out into 16 discrete "levels" before outputing via HSER.

I need to tweak some things yet. But I'll post what I came up with soon and explain what it's for.