Edge triggered D-type Flip Flop Function

MikeM100

Member
Despite being a a moderately experienced PIC assembler programmer I have started using PICAxes as I am impressed by their power and ease of use for complex functions.

I am however struggling with the following problem:

I wish to continuously monitor a pushbutton switch and am using the 'setint' command. The command works fine but is 'level' triggered rather than edge triggered. Thus on on return from interrupt if the switch is still pressed it calls the interrupt routine again !

(For hardware engineers (like myself) I want to implement a D-type edge triggered flip flop that toggles on negative edge.)

The manual says that I must clear the interrupt source in the interrupt routine but being a real world switch I cannot do this ?

Any ideas of how I coulddo this ?

Code Snippet below:

main:

setint %00000000, %000000010 'Enable Interrupt on PBSW input (Input 1 low)

'do main stuff here

goto main

interrupt: 'Subroutine called on PBSW closure

high LED 'Flash Test only
pause 100
low LED


setint %00000000, %000000010 'Re-enable interupt on Input1 low

return
 

westaust55

Moderator
As you say, PICAXE interrupts are levels driven. Don't think edge triggering is available so that you can retrun while the input is still low and only interrupt again on a high to low transistion.
Okay turns out they are on the X2 parts with the hintsetup command.


Commonly a loop is used to wait for the input signal to return to the alternate state. It this (see in red below) in line with what you are after:

Code:
main:

setint %00000000, %000000010 'Enable Interrupt on PBSW input (Input 1 low)

'do main stuff here

goto main

interrupt: 'Subroutine called on PBSW closure

high LED 'Flash Test only
pause 100
low LED
[COLOR="Red"]DO
LOOP UNTIL pin 1 = 1 ; wait until pin 1 is high again[/COLOR]

setint %00000000, %000000010 'Re-enable interupt on Input1 low

return
 
Last edited:

eclectic

Moderator
This might help?

Works OK in simulator

Code:
#picaxe 08M

symbol LED = 0 ; indicator 
symbol Flipflop = 2

setint %00000000, %000000010 'Enable Interrupt Input 1 low

main: 
'do main stuff here
goto main 

interrupt: 
if pin1 = 0 then interrupt ; waits until 1 is low

high LED 'Flash Test only 
pause 100
low LED


toggle Flipflop ; On or Off

setint %00000000, %000000010 'Re-enable interupt on Input1 low 

return
e
 

MikeM100

Member
Thank you both. Have spent some time trawling the forum this morning and I see that interrupts are generally 'troublesome' !

I am sure that both these proposed solutions will work but they stay in the interrupt service routine (ISR) until the switch is released. I'm busy, busy, busy and don't want the program to 'hang' until the switch is released !

I am inclined to take previous posters advice and just set a flag in the ISR and then process it the main routines but that then means I have to keep looking at the flag - which is a bit like polling the switch itself ?

As a new (ish) member I must say that I am really impressed with the helpfulness of this forum
 

westaust55

Moderator
okay then how about:

Code:
#picaxe 08M

symbol LED = 0 ; indicator 
symbol Flipflop = 2   ; output on pint 2 or what ever you want to toggle

setint %00000000, %000000010 'Enable Interrupt Input 1 low

main: 
'do main stuff here
goto main 

interrupt: 

high LED 'Flash Test only 
pause 100
low LED

If pin 1 = 0
	toggle Flipflop ; On or Off
	setint %00000010, %000000010 'Re-enable interupt on Input1 high 
ELSE
	setint %00000000, %000000010 'Re-enable interupt on Input1 low 
ENDIF
RETURN
if pin goes low the output toggles state and sets up next interrupt for input going back high

if pin goes high, do nothing more than set interrupt back to waiting for next low on the input.
 

MBrej

Member
You could just add a capacitor in series with the input, with perhaps a diode to protect the picaxe from voltages lower than 0V on the low going edge? Then just use the level based interrupt and itll only detect edges

Matt
 

hippy

Technical Support
Staff member
Be careful of using "If pinX=" within interrupts as the pin may not be the same state when read as it was when it caused the interrupt.

Symbol INT_MSK = %00000010
Symbol intFlag = b0

intFlg = 0 ' Interrupt on going low ( or intFlg = INT_MSK for going high )
SetInt intFlag, INT_MSK
Do
... things ...
Loop

Interrupt:
If intFlg = 0 Then
' Interrupt on going low
Else
' Interrupt on going high
End If
... things ...
intFlag = intFlag ^ INT_MSK
SetInt intFlg, INT_MSK
Return
 
Last edited:

MikeM100

Member
Matt,

A man after my own heart - as a hardware engineer I am 'lovin' it ! However, I am duty bound to do it in software ! Just tried westaust55s proposal and it works a treat but worried by Hippy's comment.
 

hippy

Technical Support
Staff member
There are two potential problems with the "If pinX=" usage in interrupts -

If an interrupt occurs on a negative edge, with a short pulse on the pin it could be high again before setting the subsequent SETINT condition. The interrupt will only fire again when the next negative edge is seen. That may be appropriate for some situations but in others the missing of the postive edge can be problematical.

The additional problem in this case is that for a short pulse the interrupt occurs but the pin may have already gone high by the time "If pinX=" is executed so it's not treated as if it were a negative edge - you can test that by making the PAUSE for LED flash longer; short pulses will not toggle the flip-flop LED.
 

westaust55

Moderator
There is certainly truth in what Hippy says.

Many PICAXE instructions take around 250usec (varies from ~250usec for many up comamnds to 1300usec for GOSUB). The interrupt function is akin to a GOSUB command and with a 4MHz clock, from some tests Beaniebots did a long time back, the GOSUB command takes around 1300usec.

This is because there is some time required for the BASIC interpreter to set up the return address etc before jumping to the interrupt subroutine.

The less code you have between the interrupt: label and the test the less likely you are to have problems but if the input siginal is fleeting, say less than 2 msec (thats a guess) then you are very likely to not get the correct answer in the IF...THEN test.
 

fernando_g

Senior Member
Matt,

A man after my own heart - as a hardware engineer I am 'lovin' it ! However, I am duty bound to do it in software ! Just tried westaust55s proposal and it works a treat but worried by Hippy's comment.
Mike; don't feel guilty for providing a little "hardware assistance" to the PICAXE.

The C-R trick is quite old...if you remember properly, the lowly 555 timer in monostable mode required such an arrangement for the input pin, otherwise the 555 would remain continuosly "triggred" by the input pulse if it was wide enough.
 

MartinM57

Moderator
I must be missing something here - look at the manual for hinsetup command for the X2 PICAXEs - edge triggering (+ve or -ve) is available ...

(you don't say what PICAXE is being used, so let's assume an X2 version :D)
 

MikeM100

Member
Sorry for omitting to mention that I am using 18x device that sadly does not support hardware interrupts. Have implemented Hippys software suggestion but something is still a bit 'flakey'.

Might actually try the 'hardware assistance' method tonight !
 

westaust55

Moderator
wonder if your "flakey" problem might be a result of switch contact bounce.

try adding a brief pause (eg pause 5) before return from the interrupt sub routine - just might solve the problem
 

hippy

Technical Support
Staff member
Have implemented Hippys software suggestion but something is still a bit 'flakey'.

It will help to describe in what way it's being 'flakey'. Maybe also post your source code.

While you can emulate edge triggered interrupts, on those PICAXE which don't support genuine hardware interrupts, the interrupts are actually level triggered and those levels are checked between statements ( and within PAUSE ) so if your code is in a SERIN or similar and waiting for data the interrupt won't be checked for during that time.
 

MikeM100

Member
Hmmmm - have tried adding 'Pauses' but something ain't right and I ran out of time last night.

My little LED flashes every negative edge OK but the it seems to miss out some code that sets a flag.

It is at this stage that I would like hardware debugging - 'trace' etc. like I have in a PIC assembler environment ! Do net get me wrong as I am beginning to love these little beasties.
 

Jeremy Leach

Senior Member
This might have been said, but I think you can get interrupts to do what you are after, simply by having different SETINT conditions.

Assuming your switch output is idling low when not pressed:

- Initially set the SETINT criteria to high.
- Button pressed, ISR fires. Within ISR set the SETINT criteria to low.
- Exit ISR, carry on with main program.
- When button is released the ISR will fire again. This time set the SETINT criteria to high.

So the ISR fires on the edge. You could have an Edge variable that is toggled on each interrupt.

Apologies if this has already been said !
 

westaust55

Moderator
that was what was proposed at post5 and seemingly worked, but hippy flagged a potential problem so a new concept was undertaken
 

hippy

Technical Support
Staff member
Can the example in Manual 2 page 182 (Rev 6.9) be updated in view of your advise
That should not be necessary as the example code operates correctly with respect to its intended purpose as described in the manual. Adding unnecessary code or using more complicated techniques would simply confuse the issue.
 

MikeM100

Member
As promised the relevant code:

Code:
#picaxe 18x

symbol	PbSW	= pin1	'Momentary PB switch - NO
symbol	LED	= 4
symbol 	intflag 	= b4	'Byte variable sets interrupt  


' ***** Programme Starts Here *****

init:				'Initialise on Power Up

	intflag = %00000000 	'Set Interrupt on low state

	setint  intflag, %00000010	'Set Initial Interupt level = low

' ***** Main Programme *****
				
main:			'Loop here waiting for PB Switch interrupt

	goto	main

' ***** Subroutines *****

interrupt:			'Subroutine called on Pbsw Change of State

             if intflag = 0 then	    'If interrupt from PBSW Low 
	intflag = %00000010	'Set Interrupt level High

	high	4		'LED On - Pin 4
	pause	50		'50mS - LED + Switch debounce 
	low	4		'LED Off - Pin 4

else				'If interrupt from PBSW High
	intflag = %0000000	'Set Interrupt level Low
				'Do nothing else
endif
		
	setint intflag, %00000010	'Set the Interrupt Edge
	pause	50		'Switch Debounce
	
	return			'Return from Interrupt
This code simply detects PB switch closure, flashes LED and does nothing on switch opening. My code (obviously) does a few more things than this !
 

hippy

Technical Support
Staff member
We really need to see your actual code if that's got the problem, unless there's also a problem with the code you did post :)
 

MikeM100

Member
Yes, good point - I understand. My 'real' code has a lot of possibly irrelevant stuff and therefore will post an edited version shortly. Just been 'tinkering' and when my ISR routine does nothing more than flash a LED it screws the main routines !
 

MikeM100

Member
OK my edited code below which I have stripped down to debug / demonstrate my problem.

The application is a DC motor driver using L293D Bridge Driver driving a door. Motor speed is controlled by PWM and real application has limit switches and timers etc for other functions.

Symptoms are that motor should drive backwards and forwards for 1 second with pauses of 2 seconds at end of travel which works fine and dandy until pressing the PBSW.

The ISR shown in the code does nothing more than flash a LED BUT it screws up the motor backward / forward action when pressed in that it reverses the motor and alters the motor run time.

Code:
'PICAxe-18X with L293D H-Bridge Driver

symbol	PbSW	= pin1	'Open/Close switch - Momentary PB - NO

symbol	HBDG1	= 1	'Output 1 - H-Bridge Open (Pin 7) 
symbol 	HBDG2	= 2	'Output 2 - H-Bridge Close (Pin 8)  

symbol	LED	= 4	'Debug LED

symbol 	intflag 	= b12	'Byte variable sets interrupt level  


' ***** Programme Starts Here *****

init:				'Initialise on Power Up

	intflag = %00000000 	'Interrupt on low state
	setint intflag, %00000010	'Set Initial Interupt level
	gosub	motoroff		' Motor Off


' ***** Main Programme *****

dooropen:			'Open the Door

	high 	HBDG1		'Set H-Bridge
	low 	HBDG2		'Direction 	
	pwmout 3, 250, 400	'Set Motor Speed

	pause 1000		'Drive for 1 second
	gosub	motoroff 		'Motor Off
	pause 2000		'Pause for 2 seconds

doorclose:			'Close the Door

	low 	HBDG1		'Set H-Bridge
	high 	HBDG2		'Direction	
	pwmout 3, 250, 400	'Set Motor Speed

	pause 1000		'Drive for 1 second
		
	gosub	motoroff 		'Motor Off

	pause 2000		'Pause for 2 seconds

	goto	dooropen		'Loop back


' ***** Subroutines *****

MotorOff:		'Motor Off

	low HBDG1	'Set H-Bridge
	low HBDG2	'Both Low - Motor Off
	pwmout 3,0,0	'PWM output Off
	
	return		'return from subroutine


interrupt:			'Subroutine called on Pbsw Change of State

if 	intflag = 0 then		'If interrupt from PBSW Low 
	intflag = %00000010	'Set Interrupt level High

	high	4		'LED On - Pin 4
	pause	10		'10mS - Switch debounce 
	low	4		'LED Off - Pin 4

else				'If interrupt from PBSW High
	intflag = %0000000	'Set Interrupt level Low
endif
		
	setint intflag, %00000010	'Set the Interrupt Edge
	pause	10		'Switch Debounce
	
	return			'Return from Interrupt
 

hippy

Technical Support
Staff member
The problem is likely that interrupts prematurely abort PAUSE commands. The way round that is to use smaller pauses within a FOR-NEXT loop, eg PAUSE 1000 can be any of -

For b13 = 1 To 10 : Pause 100 : Next
For b13 = 1 To 100 : Pause 10 : Next
For b13 = 1 To 1000 : Pause 1 : Next

and so on. Any spare variable can be used as the FOR-NEXT index variable
 

inglewoodpete

Senior Member
Gremlins

The problem is likely that interrupts prematurely abort PAUSE commands. The way round that is to use smaller pauses within a FOR-NEXT loop, eg PAUSE 1000 can be any of -

For b13 = 1 To 10 : Pause 100 : Next
For b13 = 1 To 100 : Pause 10 : Next
For b13 = 1 To 1000 : Pause 1 : Next

and so on. Any spare variable can be used as the FOR-NEXT index variable
Of course, spot the deliberate error! That one needs to have a word variable.
 

hippy

Technical Support
Staff member
Bonus points are awarded for spotting the deliberate ( just testing, honest ) error. Sorry about that.
 

MikeM100

Member
Quietly Sulking

Whist driving to the day job I was having a 'sulk' ! What sort of Interrupt Service Routine doesn't return to where it came from I pondered ?

But there it is on Page 181 of Manual 2 - "in the case of interrupted pause, wait, play or tune command, any remaining time delay is ignored ..... "

So, as they say, RTFM and 'learning curve' !

Thank you gentlemen for your help which I will attempt to return in kind to this forum.
 
Top