Minor problem with interrupt timing

#1
I have a circuit which generates 600µs negative pulses which I want to count with interrupts (incrementing dcl_in), because I don't want to miss any pulses while other things might be happening.

Being unfamiliar with PicAxe interrupts I followed the example in the manual and generated this:
Code:
interrupt:
SETFREQ m16
LET int_timer = int_timer + 1
IF pinC.3 = 0 THEN interrupt
SETFREQ m4
LET dcl_in = dcl_in + 1
SERTXD ("Interrupt length ",#int_timer," cycles",LF,CR)
int_timer = 0
; more stuff
SETINT %00000000 , %00001000
RETURN
This consistently returns
Interrupt length 1 cycles
which it did before I added the "SETFREQ m16" and also did when I only had a 200µs interrupt pulse, which I thought might not be long enough, but probably was.

So the question is, what's wrong with
Code:
LET int_timer = int_timer + 1
which I added to give me a rough idea of how many machine cycles the processor goes through while waiting for the interrupt condition to go away?

The variable "int_timer" is a word variable by the way, in case it ends up as a big number. It's running on an 08M2+ for the record.

Thanks.
 

hippy

Technical Support
Staff member
#2
I have a circuit which generates 600µs negative pulses which I want to count with interrupts (incrementing dcl_in), because I don't want to miss any pulses while other things might be happening.
Do you want to count pulses or measure their length as your code seems to do ?

Your problem is ...

Code:
interrupt:
SETFREQ m16
LET int_timer = int_timer + 1
IF pinC.3 = 0 THEN interrupt
That's about 10 tokens worth of PICAXE code being executed. It takes roughly, I recall, 125us per token at 4MHz so, in total, 1250us at 4MHz, 625us at 8MHz, 312us at 16MHz.

It will also take time getting into the interrupt routine so if you are lucky the count may sometimes reach two. You can take the SETFREQ M16 out of the counting loop to speed it up slightly, but I wouldn't expect any great improvement.

I am somewhat surprised it even worked at all, that you are seeing any interrupts. I guess the looping code waiting for interrupts is quite short.

Bottom line is you aren't going to measure the length of 600uS pulses with a PICAXE accurately by interrupting and you are likely going to miss a few. One would normally use HINTSETUP to not miss any.

If you want to persevere with SETINT I would recommend putting the SETFREQ M16 before looping for interrupts and timing this way. I can't vouch for how much better it will be but you never know -

Code:
Symbol IRQ_PIN = pinC.3
Symbol IRQ_MSK = %00001000

Gosub Interrupt_Enable
Do
Loop

Interrupt:
  If IRQ_PIN = 1 Then x0
  If IRQ_PIN = 1 Then x1
  If IRQ_PIN = 1 Then x2
  If IRQ_PIN = 1 Then x3
  If IRQ_PIN = 1 Then x4
  
  ; More of the same here

  Do : Loop Until IRQ_PIN = 1

  x4: w0 = w0 + 1
  x3: w0 = w0 + 1
  x2: w0 = w0 + 1
  x1: w0 = w0 + 1
  x0:

  SetFreq M4
  SerTxd( "Pulse length = ", #w0, CR, LF )

Interrupt_Enable:
  SetFreq M16
  w0 = 0
  SetInt %00000000, IRQ_MSK
  Return
The best solution, if you are generating the pulses, is not to produce pulses but toggle the output, have the counter count changes from high to low and from low to high.

The chance of spotting and counting meteorites is small on a normal night, blink and you'll miss one. Now if the sky toggled light and dark every time one arrived you would probably be able to count them all :)
 
#3
Many thanks for the quick reply Hippy.

It's only the pulses themselves I want to count, but there might be anything from one a day to five a second (possibly more), so I decided to use an interrupt in case my inefficient code is busy doing other things when a pulse arrives, then I know it will always update the dcl_in count without fail. I appreciate that COUNT for instance could probably do the job perfectly well, but I was worried that I might miss pulses in between the measurement periods while I was updating other things.
10n100k.jpg
The attached picture (horizontal scale is millisecs) is from the useful little PCB scope; sufficiently accurate to show me what's happening, so I know the hardware's generating this 600µs pulse reliably (on Ch1), but I wanted to make sure that it would be long enough to always ensure that the code got into the interrupt routine okay, hence the
Code:
LET int_timer = int_timer + 1
to just see if it was actually looping back to the start of the interrupt before the interrupt went away. If it wasn't, then I might have lengthened the pulse a bit. From what you say about 125µs per token, I shouldn't miss anything, even if I leave the clock speed at 4MHz.

Thanks for the suggested interrupt code as well, as always there are so many ways of doing things and that gives me lots of useful hints on how to improve the code.
 

hippy

Technical Support
Staff member
#4
If you just want to count pulses and not miss any there are three main options -

1) Use an internal counter driven by an external source. Easy with X2, you have to jump through some POKESFR hoops for the M2 but does work.

2) use HINT pins which will spot and remember any pulse down to microseconds and less. As long as you clear the 'has been seen' flag before the next arrives you won't miss any, and you can poll or use use interrupts to handle counting. You can also use more POKESFR magic to have seen pulses remembered even if HINT pins are not supported, though you will have to poll those with PEEKSFR to get at them.

3) Use output toggling rather than pulse generation. That's my favoured solution.
 
Top