08M2 interrupt issue

matherp

Senior Member
I seem to be posting a lot at the moment - apologies.

I'm using 08M2 to monitor a mechanical (not optical) rotary encoder with an integrated switch. The output is a serial stream of "L" for one step anticlockwise, "R" for one step clockwise, or "P" for a push on the hardware serial port (which on the 08M2 is also the programming output port).

The code to do this is pretty simple with just one oddity to handle. Basically it interrupts on either the switch or one of the encoder channels and exits the interrupt when all the inputs are back to the default high condition. It uses the state of the second encoder channel at the time of interrupt to decide if the rotation is left or right. The one oddity is that it must handle a button push when the encoder is in a middle state. This is programmed with a standard counter based debounce.

Code:
#picaxe 08M2
setfreq m32
symbol switched = b0
symbol reread = b1
symbol notpushed=b2
symbol waspush=b3
symbol debouncecount=b4
symbol rightpossible=b5
symbol nosecondedge=b6
symbol true=1
symbol false=0
'
'Connections
' Encoder switch pulled high and connected to C.1 (pin6)
' Encoder output1 pulled high and connected to C.3 (pin4)
' Encoder output2 pulled high and connected to C.4 (pin3)
'
'
symbol encodermask   = %00011010 ' ports c.3, c.4, c.1 pulled high
symbol interruptmask = %00010010 ' allow interrupts on c.4, c.1
symbol rotateleft    = %00000010 ' c.1 high, c.4 and c.3 low
symbol rotateright   = %00001010 ' c.4 low, c.3 high c.1 high
symbol clicked       = %00011000 ' c.1 low, c.4 and c.3 high



hsersetup B9600_32,2
dirsc=0
setint OR 0,interruptmask
disconnect
do
loop


interrupt:
	switched=pinsc & encodermask 'look at just the three inputs
	rightpossible=false 'initialise the right rotate flag
	debouncecount=0
	if switched = clicked then 'simple button push
		hserout 0,("P")
		waspush=true 'set that it can't be pushed again while in the interrupt with switched=clicked
	endif
	if switched = rotateright then
		rightpossible=true 'we have seen the front edge of the leading pulse but don't confirm until we see the edge of the second pulse
		waspush=false
	endif
	if switched = rotateleft then 'both inputs immediately so must be left pulse leading as this doesn't interrupt
		hserout 0,("L")
		waspush=false
	endif
	do  'now loop until the interrupt condition is removed
		reread=pinsc & encodermask
		nosecondedge=reread & clicked 'see if both encoder inputs are now low
		if rightpossible=true and nosecondedge=0 then 'we now have both inputs low so the right is confirmed
			hserout 0,("R")
			rightpossible=false
		endif
		if switched<>clicked then 'test for push while waiting for rotary to reset
		    if pinc.1=0 and waspush=false then 'the button has been pushed while the rotary encode is in an intermediate state
		    	  inc debouncecount 'increment the debouce counter
		    	  if debouncecount > 50 then 'valid button push
		    		hserout 0,("P")
		    		waspush=true 'set that a button push is no longer valid
		    	  	debouncecount=0 'initialise a counter to debounce the back edge of the button push
		    	  endif
		    endif
		    if pinc.1=1 and waspush=true then 'the button is no longer pushed so increment a debounce counter
		    	  inc debouncecount
		    	  if debouncecount>50 then
		    	  	waspush=false 'set that a push is now allowed again (but only if the original interrupt was not a push)
		    	  	debouncecount=0
		    	  endif
		    endif	
		endif
	loop until reread=encodermask ' all inputs back to high state
	setint OR 0,interruptmask
return
All this works perfectly. Except.....


If I abuse the encoder repeatedly going left, right, pushing, and repushing over and over again then eventually the program will stop responding and nothing will wake it up except a reset.

I've done everything possible to debug this and am coming to the conclusion that there may some sort of a bug in the 08M2 firmware. By putting a toggle c.2 command in the interrupt routine, it looks like the program is running but no output is being produced which suggests that the issue is in the hardware serial output routine (but I may of course be mis-interpreting).

The hardware uses the enhanced serial circuit and the encoder inputs are pulled high with 4K7 resistors and have a 0.1uF capacitor across the contacts, the 08m2 is properly decoupled and the power supply is a bench supply through a LM2940 with all the usual capacitors.

Before anyone suggests it, I can't use sertxd or serout as they take too long to complete and when turning the encoder fast pulses are missed. Nor can I put the writes in the main program as then they would be delayed too much.

I don't think in normal use the bug would arise but the code is going into a module I'm developing for someone else so I want it perfect:)

Any help appreciated

Peter
 
Last edited:

g6ejd

Senior Member
symbol rotateright = %00001010 ' c.3 low, c.4 high c.1 high

This line does not look correct. %00010010 would correspond to your test/comment
 

matherp

Senior Member
That's just a typo in the comment, but well spotted, the interrupt is only enabled on c.4 so c.3 will be high if it is a right twist and both will be low if it to the left.
The code does function perfectly under all normal circumstances. But sometimes after repeatedly pushing and twisting as fast as possible it stops outputting. This is why I think it is perhaps missing a hardware interrupt in the firmware.
 

g6ejd

Senior Member
Well the interpreter is polling the ports, so it is possible that an event will be missed, but captured on the next cycle, I assume the PICAXE code uses the hardware features to detect an edge trigger, I'm sure the Technical folks at RevEd will provide that answer. Have you tried clearing the interrupt flags everytime you enter the interrupt routine, that's what I would do. I doubt there is a firmware problem, these devices get too much usage for that.

Other suggestions are to seperate out your variables by say at least one byte so as to remove any possible byte/word interactions.

symbol switched = b0
symbol reread = b2
symbol notpushed = b4
symbol waspush = b6
symbol debouncecount = b8
symbol rightpossible = b10
symbol nosecondedge = b12

I've been through the code and can find no obvious logic faults, other than in the interrupt, this line gets my attention:

if switched<>clicked then 'test for push while waiting for rotary to reset


Because that's a condition that may not be triggered under some circumstances, although it won't stop the interrupt condition tests from exiting the interrupt.
If you have toggle in the Int routine, does that toggle the pin when it gets stuck? if so then good because its narrowed it down to that section and with more of these you can find out where the problem is.
 

matherp

Senior Member
G6ejd

Thanks for your detailed work on this. My starting point on believing that there may be a firmware issue is as follows:

Main loop is a do loop wth no content so can't possibly escape

Interrupt routine has only one return and must re-enable interrupts before that return

The one loop in the interrupt routine that could hang up is just looking for the input lines to return to their default pulled high condition which I can demonstrate has happened
do 'now loop until the interrupt condition is removed
reread=pinsc & encodermask
loop until reread=encodermask ' all inputs back to high state

There are definitely ways in which inputs could be missed or mis-interpreted ( a short spike on the push button may not be seen by switched<>clicked as you say in which case the routine will just fall through) but I can't find a way in which any of them can permanently halt all subsequent output

Best Regards

Peter
 

MartinM57

Moderator
Maybe you need to post the full code for peer review before looking for firmware errors? Or rather than the full code, you could strip stuff out until you have the minimum code that demonstrates the error...

What speed are you running at? What happens if you SETFREQ 'faster'?
 

hippy

Technical Support
Staff member
By putting a toggle c.2 command in the interrupt routine, it looks like the program is running but no output is being produced which suggests that the issue is in the hardware serial output routine (but I may of course be mis-interpreting).
Are you saying the C.2 keeps toggling even though no HSEROUT data is sent or something else ? Where exactly was the toggle for C.2 placed ?

The main thing is to find where the code is when it appears to have hung; is it coming out of interrupts, stuck in the interrupt DO-LOOP, in the main loop not interrupting, or somewhere else ?

The best way to do that may be to put PULSOUT's in the code ( using different pulse widths if using multiple ) to determine where it's going and look at C.2 with a scope. Alternatively, try with some other sized M2 and see if that has the same issues; if so you have more outputs to play with which will allow better and easier debugging.
 

matherp

Senior Member
Circumvented

Thanks for the various responses. In the end I've adopted the old programmers approach (I am an old programmer!) when faced with an intractable bug - recode the application until the bug goes away.

This version seems to be robust
Code:
#picaxe 08M2
setfreq m32
symbol switched = b0
symbol reread = b1
symbol waspush=b2
symbol debouncecount=b3
symbol rightpossible=b4
symbol nosecondedge=b5
symbol leftpossible=b6
symbol notclick=b7
'
'Connections
' Encoder switch pulled high and connected to C.1 (pin6)
' Encoder output1 pulled high and connected to C.3 (pin4)
' Encoder output2 pulled high and connected to C.4 (pin3)
'
'
symbol interruptmask = %00011010 ' allow interrupts on c.4,c.3, c.1
symbol rotateleft    = %00010010 ' c.3 low, c.1 high,c.4 high
symbol rotateright   = %00001010 ' c.4 low, c.1 high c.3 high
symbol clicked       = %00011000 ' c.1 low, c.4 and c.3 high
symbol notclicked    = %00000010 ' mask to test for c.1 high
'
symbol true=1
symbol false=0
symbol debounce=50
'
hsersetup B9600_32,2
dirsc=0
disconnect
setint OR 0,interruptmask
do
loop


interrupt:
	switched=pinsc & interruptmask 'look at just the three inputs
	rightpossible=false 'initialise the right rotate flag
	leftpossible=false 'initialise the right rotate flag
	debouncecount=0
	if switched = clicked then 'simple button push
		hserout 0,("P")
		waspush=true 'set that it can't be pushed again while in the interrupt with switched=clicked
	endif
	if switched = rotateright then
		rightpossible=true 'we have seen the front edge of the leading pulse but don't confirm until we see the edge of the second pulse
		waspush=false
	endif
	if switched = rotateleft then 
		leftpossible=true 'we have seen the front edge of the leading pulse but don't confirm until we see the edge of the second pulse
		waspush=false
	endif
	do  'now loop until the interrupt condition is removed
		reread=pinsc & interruptmask
		notclick=reread & notclicked / 2 'set to true if the button is not pushed
		nosecondedge=reread & clicked 'see if both encoder inputs are now low
		if rightpossible=true and nosecondedge=false then 'we now have both inputs low so the right is confirmed
			hserout 0,("R")
			rightpossible=false
		endif
		if leftpossible=true and nosecondedge=false then 'we now have both inputs low so the left is confirmed
			hserout 0,("L")
			leftpossible=false
		endif
		if switched<>clicked then 'test for push while waiting for rotary to reset if it was't a push to start with
		    if notclick=false and waspush=false then 'the button has been pushed while the rotary encode is in an intermediate state
		    	  inc debouncecount 'increment the debouce counter
		    	  if debouncecount > debounce then 'valid button push
		    		hserout 0,("P")
		    		waspush=true 'set that a button push is no longer valid
		    	  	debouncecount=0 'initialise a counter to debounce the back edge of the button push
		    	  endif
		    endif
		    if notclick=true and waspush=true then 'the button is no longer pushed so increment a debounce counter
		    	  inc debouncecount
		    	  if debouncecount>debounce then
		    	  	waspush=false 'set that a push is now allowed again (but only if the original interrupt was not a push)
		    	  	debouncecount=0
		    	  endif
		    endif	
		endif
	loop until reread=interruptmask ' all inputs back to high state
	setint OR 0,interruptmask
return
The overall approach of the application is to use a background serial read on the master processor so that these inputs from the 08M2 encoder pre-processor can be used when time allows - works well

Best regards

Peter
 

hippy

Technical Support
Staff member
recode the application until the bug goes away. This version seems to be robust
Can you give a brief synopsis of what's changed, which might indicate where the original problem lay or what was causing it.

That could also save everyone interested in having to go through the two sets of code line by line.
 

matherp

Senior Member
The main thing is that I am now interrupting on both encoder outputs rather than just one and then waiting for the second edge to come along in both cases before confirming the direction. Also I have got rid of the individual pin read for the push button and just isolated it from the port read I was already doing. Nothing that should actually make any difference and no functional change but my experience is that things like compiler bugs or unforeseen race conditions are often circumvented by inconsequential changes to code.
 
Top