Function: setint

Yex2

Well-known member
Hello,

I need some help with that function. I see that more than one gate can be used to trigger the interrupt.

The question is: can it be different interrupt or is it going to be the same with conditions inside?

For example, I want to turn on a LED if pin 0 is high and another if pin 1 is high. Normally I would do it this way. But can it be done using two distinct interrupt functions?:

Code:
setint %00000011,%00000011


main:

do

...

...

loop


interrupt:

if pin0 = 1 then : high 3

else if pin1 = 1 then : high 4

endif

setint %00000011,%00000011

return
******************************

Is something like this possible:

Code:
interrupt_0:
high 3
setint %00000011,%00000011
return


interrupt_1:
high 4
setint %00000011,%00000011
return
****************************

The obviously advantage would be the speed of execution.

Thank for your input,

Yves
 

hippy

Technical Support
Staff member
Unfortunately the PICAXE only supports one interrupt handling routine so, when interrupts come from two or more sources, one would need to determine which sources had trigged the interrupt and handle those accordingly, as in your first code.

It is possible to dynamically change the interrupt trigger so the interrupt will only occur whenever each source activates and deactivates. That can be used to improve overall responsiveness.
 

Yex2

Well-known member
Unfortunately the PICAXE only supports one interrupt handling routine so, when interrupts come from two or more sources, one would need to determine which sources had trigged the interrupt and handle those accordingly, as in your first code.
OK that is what I though. Thanks hippy!

It is possible to dynamically change the interrupt trigger so the interrupt will only occur whenever each source activates and deactivates. That can be used to improve overall responsiveness.
Can you provide an example for this?

Yex
 

hippy

Technical Support
Staff member
Here's example code of interrupting only on changes. Click pins C.1 and C.2 on and off to trigger an interrupt.

This runs in the PE6 simulator and should also run on physical hardware with the "MASK" constants changed to match.
Code:
#Picaxe 20M2
#Terminal 4800

;               76543210
Symbol MASKA = %00000010 ; C.1
Symbol MASKB = %00000100 ; C.2

Symbol MASK  = %00000110 ; All of the above

Symbol level = b2

PowerOnReset:
  SerTxd( "Started", CR, LF )
  Gosub Interrupt_Enable

MainLoop:
  Do
    Pause 100
  Loop

Interrupt:

  b0 = pinsC ^ level

  b1 = b0 And MASKA
  If b1 <> 0 Then
    b1 = b1 And level
    If b1 <> 0 Then
      SerTxd( "A (C.1) went low", CR, LF )
    Else
      SerTxd( "A (C.1) went high", CR, LF )
    End If
  End If

  b1 = b0 And MASKB
  If b1 <> 0 Then
    b1 = b1 & level
    If b1 <> 0 Then
      SerTxd( "B (C.2) went low", CR, LF )
    Else
      SerTxd( "B (C.2) went high", CR, LF )
    End If
  End If
  
  level = level ^ b0 And MASK
  SetInt Not level, MASK
  Return

Interrupt_Enable:
  level = pinsC And MASK
  SetInt Not level, MASK
  Return
 

Yex2

Well-known member
Hello hippy,

I'm having a discussion on the French forum about the time it takes for a pic to execute a line of code. There must be a way to know or calculate the time.

Can you provide insight in the matter ?

Thanks,

Yex
 

AllyCat

Senior Member
Hi,

The definitive thread is probably here but it is rather old now. The normal method is to add the instruction(s) into a FOR..NEXT loop and measure the increase in time over, say, 10 seconds. But the "direct" method that I use is described in post #12.

Cheers,, Alan.
 

hippy

Technical Support
Staff member
I'm having a discussion on the French forum about the time it takes for a pic to execute a line of code. There must be a way to know or calculate the time.
There is no easy way to determine the time it takes to execute a specific line of code accurately but it is usually possible to determine if a piece of code will complete within the time one wants it to run in.

One subtly shifts the question from how long it takes to; is it likely to work ?

For example, you appear to be wanting to determine how many pulses come from a flow meter. You could count those pulses using code like this -
Code:
#Picaxe 20M2

Symbol MASK    = %00000010 ; C.1

Symbol counter = w1 ; b3:b2
Symbol level   = b4 
 
Main:
  Gosub Interrupt_Enable
  Do
    Pause 100
  Loop

Interrupt:
  counter = level Max 1 + counter
  level = level ^ MASK
  SetInt level, MASK
  Return 

Interrupt_Enable:
  level = pinsC & MASK ^ MASK
  SetInt level, MASK
  Return
Then one can add up the number of words and symbols within the Interrupt routine; 17 'tokens' by my count.

And we have an established 'rule of thumb' that a "High <pin>" command takes 250us at 4MHz, so about 125us per token, so that interrupt routine should take about 17 x 125us, around 2ms at 4MHz.

So we can determine whether the code is likely to run fast enough or not. If you have a pulse every 10ms (100Hz) it should do fine, keep an accurate count. If it is a pulse every 1ms (1kHz) it will not be fast enough. With a pulse every 3ms (330Hz), maybe, maybe not.

But that's at 4MHz and every doubling of speed using SETFREQ halves the execution time, so, on an M2, the fastest speed of that interrupt is at 32MHz, when it takes about 250us. So that should be good for counting up to 4kHz. The slower the pulse rate is than that the more likely it is to be okay.

Looking at your posts in the other forum it seems 180Hz, a pulse every 5ms, would not be unreasonable to expect. If so then the PICAXE should be able to handle that at 4MHz and easily so at a higher operating speed.
 

hippy

Technical Support
Staff member
Code:
counter = level Max 1 + counter
I am counting rising edges there. It is equally possible to count both rising and falling edges.

If the pulses are square waves we will have doubled the resolution, increased the count accuracy and, if not, we have simply ended up with a count twice as large as it would otherwise be.

And the big advantage of counting both is it allows that code to be reduced to a simple -
Code:
counter = counter + 1
or
Code:
inc counter
Both of which are the same as far as the compiler is concerned so "inc" won't actually save more tokens than the first, but both do save tokens, increase execution speed, will allow higher pulse rates to be counted.
 

Yex2

Well-known member
Thanks AllyCat,

Hi Hippy,

I already wrote the interrruption code which is this. The chips frequency is set to 16 MHz :

Code:
interrupt:                          
if time > cycle then : today = 0 : endif     

inc impulsions                    
    if impulsions    >=etalon then     
        inc today                    
        inc total                
        impulsions =impulsions - etalon     
    endif

    if pinC.3 = 0 then                 
        setint %1000,%1000             
    else setint %0,%1000         
    endif

    enabletime                     
    time = 0                    
return
The flow-meter used generate a square wave that can either stop on LOW or HIGH, which is why the setint must be conditional to it's current state. Also it does read twice the pulse as you suggested.

The flow-meter is frequency range is from 32 to 90 Hz. Since I read both state (low and high) it's double and becomes 64 to 180 Hz. So the speed of the pulse will vary from ± 15.6 ms to 5.6 ms.

So my original question was to find out how fast is my interrupt routine is executed? Can it miss any pulse ?

My personal tests indicated that I'm not missing any pulse but I would like to know for sure.

Regards,

Yves
 
Last edited:

hippy

Technical Support
Staff member
So my original question was to find out how fast is my interrupt routine is executed? Can it miss any pulse ?
42 tokens at 125us per token gives about 5ms at 4MHz (200Hz), about 1.3ms at 16MHz (760Hz), so it should be fine.

That is also worse case, assuming all code would be executed every interrupt, and I don't believe it would be.
 

Yex2

Well-known member
Ok thank you hippy.

Other question: is my interrupt routine as efficient as the one you suggested?

I'm referring to this method or resetting the interrupt:

Code:
    if pinC.3 = 0 then                
        setint %1000,%1000            
    else setint %0,%1000        
    endif
Compare to yours:

Code:
Interrupt:
  inc counter
  level = level ^ MASK
  SetInt level, MASK
  Return

Interrupt_Enable:
  level = pinsC & MASK ^ MASK
  SetInt level, MASK
  Return
 

hippy

Technical Support
Staff member
Other question: is my interrupt routine as efficient as the one you suggested?
It's not as efficient but, as it's fast enough, it doesn't really matter.

You do notionally have a case where you could drop a pulse edge because you are setting the next interrupt condition depending on what pinC.3 is at the time you set it, where I look for the opposite of what it last was.

If you had a short pulse, got an interrupt when it went high, and the signal had returned low before you set the SETINT again, you would then interrupt on the next high, so would have missed the going low edge completely.

But, if it is a square wave, that doesn't really matter either.
 

AllyCat

Senior Member
Hi,
... is my interrupt routine as efficient as the one you suggested?
I'm referring to this method or resetting the interrupt:
There are various different ways to define "efficient". ;)
It could be the size of the program code, or the time it takes to execute, or how easy it is for a third person to understand what the program is doing !

The PE "Check Syntax" displays the size of the code generated and hippy's appears to just "win" at 14 bytes versus 15, but in practice, this difference can be "lost" in the way that the program tokens are compiled. Sometimes the size of a program will correlate with how long it takes to run, put there are exceptions, particularly if the programmer has chosen to "trade" program size for speed, or vice versa.

It's actually quite difficult to analyse the speed of those particular samples of code above. They read an input value (which might not be known, or fixed) from a pin, but we must ensure that an interrupt will NOT occur (because it would wreck the timing measurement). However, just, changing %1000 to %0000 (for example) can itself alter the timing (because the number "8" uses a larger token than zero). A further difficulty is that the IF .. THEN .. ENDIF structure takes different times to execute depending on the path, particularly if it includes an ELSE.

Thus, another possible consideration for "efficiency" is how predictable or constant is the execution time, and here hippy's "mathematical" method scores over the "conditional" (IF..THEN) structure. Perhaps surprisingly the following has a more constant (shorter) execution time than yours, because the "Jump over" of the intermediate instruction takes about the same length of time as the instruction itself:
Code:
    setint %1000,%0000
    if pinC.3 = 0 then                    
        setint %1000,%1000                 
    endif
Incidentally this rearrangement reduced the code size to 14 bytes, the same as hippy's. I also rearranged (not changed) the numbers to ensure that no interrupt was generated and then the measured times for he above and hippy's were 1.52 and 1.58 ms respectively. But differences of 60 us and 1 byte are completely insignificant in almost every application, as predicted by hippy above.

So, for me, the mathematical method has the advantage of a "guaranteed" constant execution time, whilst the "conditional" structure is probably easier to understand. ;)

Cheers, Alan.
 

hippy

Technical Support
Staff member
In terms of efficiency when applied to an interrupt routine; I would normally take that as how quickly it can do what it has to do, how quickly it can be ready to handle the next interrupt.

So an efficient interrupt routine for me would be one which does the minimum it has to, generally has the least amount of code, the smallest number of tokens, the smallest program memory size, and the fastest execution speed.

And, when it's the minimum it needs to be, other measures of efficiency such as readability and understandability will tend to fall away as a short explanation in comments can explain what is going on, give a more readable equivalence if necessary.

Most of the time the efficient version will simply be the most optimised version of the more readable less efficient version.

Of course, while it may be easy to tell when something is inefficient compared to something which is efficient, when approaching most efficient it is hard to tell what code is actually most efficient.

But most times that won't really matter; so long as the interrupt completes fast enough everything will work. Even inefficient code can be good enough, and the pursuit of efficiency is only really required in specific cases.

But that's not to say such discussion is not useful. It is intellectually and scientifically and that is knowledge can be applied to practical implementations and projects.
 

BESQUEUT

Senior Member
In terms of efficiency when applied to an interrupt routine; I would normally take that as how quickly it can do what it has to do, how quickly it can be ready to handle the next interrupt.
I agree.
But we have also to remember that soft interrupt is done between main program commands.
So if main loop contain something like a sertxd, pulses may be lost... even if the interrupt routine is very efficient...
 

kranenborg

Senior Member
I agree.
But we have also to remember that soft interrupt is done between main program commands.
So if main loop contain something like a sertxd, pulses may be lost... even if the interrupt routine is very efficient...
Part of the problem (i.e. missing an interrupt on very short pulses) may sometimes be solved by using the SR-latch facility as a memory bit: tying the interrupt source to its input and connecting its output to an interrupt-enabled input pin then allows the program to catch it in between commands in the program body, see the following post: link

/Jurjen
http://www.kranenborg.org/electronics
 
Top