Understanding 20X2 interrupts

micrometal

New Member
I have spent the evening trying to understand interrupts on the 20X2. I am using an AXE091 Development Board with the following code :

Code:
        ' 20X2 interrupt testbed
        #terminal 9600
        #picaxe 20X2
        #no_table
        #no_data

        symbol        LED1 =    B.3
        symbol        LED2 =    B.4       
        hintsetup    %01100110
        setintflags    %00000110, %00000110
        
        sertxd("Starting ...",13,10)
        
    main:    goto main

    interrupt:
        sertxd("Interrupt received",13,10)
        if pinB.0 = 1 then        ' Test for high input on hint1
            sertxd("Pin B.0",13,10)
            high LED1
            low    LED2
        endif
        if pinB.1 = 1 then        ' Test for high input on hint2
            sertxd("Pin B.1",13,10)
            low    LED1
            high LED2
        endif
        pause 2000                ' Allow input to clear
        hint1flag    =    0        ' Reset all interrupt settings
        hint2flag    =    0
        hintsetup    %01100110
        setintflags    %00000110, %00000110

        return
The interrupts, from the push buttons, are working, but unreliably. Typically only one will work a few times and then stop. After that the other input will do the same. What is wrong with the code?

I also have some specific questions :

1. The hintsetup and setintflags statements seem to me to do exactly the same thing : request an input when either of pins B.0 or B.1 goes high. But obviously they don't mean the same thing. What do they each mean?

2. On the setintflags page of the PICAXE manual this sentence appears "[A polled interrupt] is the only type of interrupt available in the PICAXE system." On the hintsetup page it says "The hardware interrupts are triggered and processed extremely quickly." I thought that a polled interrupt was not the same as a hardware interrupt. Am I mistaken?

These questions are probably not relevant, but they make me feel less confident about what I am doing.
 

inglewoodpete

Senior Member
All good questions :) .

Something that you may have overlooked in (1.) is that hardware interrups on the X2 chips are edge triggered and the pin's change-of-state is latched. The PICAXEs interrupt polling does occur between the higher level commands (2.) but it polls the flags rather than the pins. There is no reason to have a delay (Like your Pause 2000) - remember that interrupts are disabled during the execution of the Interrupt routine.

In fact, interrupt routines are best configured to execute as briefly as possible: I would remove the pause statement and also the SerTxd statements from the interrupt routine once you know that interrupts are occurring. Read flags/inputs and set your own bit-variable flags* where approprate and get the interrupts (re)configured as quickly as possible. Reporting the change-of-state for debug purposes is best performed in the main (foreground) loop, acting on the status of *user flags.

The hardware interrupt-on-change firmware latches the event when it occurs, provided that the system (behind the scenes and out of your control) interrupts are not disabled at the time of the input event. SerTxd is one of the foreground commands that automatically disables internal interrupts so that it can accurately bit-bang the async. serial output.
 

Buzby

Senior Member
Also, I've had problems when testing interrupts if my main loop was 'main: goto main'.

In this case the PICAXE firmware is spending most of it's time outside of the BASIC code execution, so has relatively less time to look for interrupts.

Better to use 'main: pause 2000 : goto main'.
 
Last edited:

hippy

Technical Support
Staff member
The hintsetup and setintflags statements seem to me to do exactly the same thing : request an input when either of pins B.0 or B.1 goes high. But obviously they don't mean the same thing. What do they each mean?
The HINTSETUP configures the pins, defines which pins will set 'hintXflag' flags, and which type of edge, rising or falling, will set that flag.

The SETINTFLAGS configures interrupts, allows the 'hintXflag' flags from HINTSETUP to cause entry to the 'interrupt:' routine to be scheduled then occur.

So, HINTSETUP %01100110 -

* Set 'hint1flag' when the HINT1 pin (B.0) goes high.
* Set 'hint2flag' when the HINT2 pin (B.1) goes high.

And, SETINTFLAGS %00000110, %00000110 - Schedule a call to the 'interrupt:' routine whenever -

* 'hint1flag' has been set and 'hint2flag' has been set.

I think it is probably a combination of this implied 'and' condition and how your hardware is wired which is causing the interrupt routine to not always be activated. I'll do some testing and report back.
 
Last edited:

micrometal

New Member
I quickly tried Inglewood Pete's and Buzby's suggestions - an improvement but not a solution. Then I thought "What about contact bounce. Am I checking the button states too soon after the interrupt?" So I put in a "pause 100", but no change. Don't have time now to try anything else. Back later.
 

hippy

Technical Support
Staff member
I think it does come down to how your buttons are wired and the SETINTFLAGS - Are your buttons 'push to short to 0V' or 'push to short to V+' ?

The code below works for me in simulation, 'push to short to V+', rising edge interrupts on B.0 or B.1. So an interrupt occurs whenever either B.0 or B.1 pins are left-clicked and toggled to be yellow on the chip in the simulation window -

Code:
#Picaxe 20X2
#Terminal 9600
#No_Table
#No_Data

PowerOnReset:
  HIntSetup %01100110
  Gosub Interrupt_Enable

Main:
  For w1 = 1 To 60 ; Just for simulation
    Pause 1000
  Next
  End

Interrupt:
  If hint1flag = 1 Then
     hint1flag = 0
     SerTxd("HInt 1, B.0", CR, LF)
  End If
  If hint2flag = 1 Then
     hint2flag = 0
     SerTxd("HInt 2, B.1", CR, LF)
  End If
Interrupt_Enable:
  SetIntFlags OR %00000110, %00000110
  Return
The key there is "SETINTFLAGS OR" which cause an interrupt on 'hint1flag' or 'hint2flag'.

Note there is no need to call HINTSETUP at the end of the interrupt as the earlier setting will be remembered. A new SETINTFLAGS does need to be included or you will only see the first ever interrupt.

I clear the 'hint1flag' and 'hint2flag' as early as possible,. This better allows a second interrupt to be flagged on the same or other pin while in the interrupt routine but isn't essential, can be placed at the end before the SETINTFLAGS/RETURN.

Also note that I check the 'hint1flag' and 'hint2flag' to determine which interrupted. There is a possibility due to contact bounce or shortness of push that B.0 or B.1 have fallen back to their ' not pushed state' when the interrupt is entered so they may not be as expected when tested within the interrupt. This also avoids having to specify whether the interrupt on the HINT pins were rising or falling edge, high or low when activated.
 

hippy

Technical Support
Staff member
On the setintflags page of the PICAXE manual this sentence appears "[A polled interrupt] is the only type of interrupt available in the PICAXE system." On the hintsetup page it says "The hardware interrupts are triggered and processed extremely quickly." I thought that a polled interrupt was not the same as a hardware interrupt. Am I mistaken?
You are correct, to a degree.

On the PICAXE "hardware interrupts" aren't quite the same as an immediate call to the interrupt routine which exist on other microcontrollers.

Instead the hardware is configured, via HINTSETUP, to record there has been an event on a pin which necessitates the 'interrupt:' routine being called.

Using SETINTFLAGS means, at the end of every command, a note within TUNE or during PAUSE, this hardware record is checked and the 'interrupt:' routine is called if required. This is the "polling" nature described; interrupts happen then, not immediately the need for an interrupt is recorded.

Ultimately it's the same as being a traditional 'hardware interrupt' but with slightly longer latency than there may be on other microcontrollers. That is not likely to prove problematic for most uses a PICAXE will be put to.

In fact it can often be the case that native microcontroller code wants what the PICAXE provides but will struggle to achieve it. It's easy enough to flag an interrupt is required and defer calling it, but much harder to invoke the interrupt call once needed. One needs to litter the code with checks to see if a call is deferred and needed or not. Because of the interpretive nature of PICAXE Basic this is easy to implement after every command.
 

micrometal

New Member
Thanks for your help. I have modified the "main" loop as suggested by Busby and am now checking flags rather than pins as suggested by InglewoodPete. This produces virtually the code suggested by Hippy. The AXE091 board provides positive pulses from the push buttons and requires only four jumper wires: I have checked these several times.

I am now running the following code and getting pretty repeatable results ...

Code:
        hintsetup    %01100110
        setintflags    %00000110, %00000110
        sertxd("Starting ...",13,10)
        
    main:    pause 2000
            goto main

    interrupt:
        sertxd("Ok",13,10)
        if hint1flag = 1 then        ' Test for high input on hint1
            sertxd("One", CR, LF)
        endif
        if hint2flag = 1 then        ' Test for high input on hint2
            sertxd("Two", CR, LF)
        endif
        hint1flag    =    0            ' Reset interrupt settings
        hint2flag    =    0
        hintsetup    %01100110
        setintflags    %00000110, %00000110

        return

What happens is this :

1. From a fresh download the first input pressed will not trigger an interrupt, even if pressed repeatedly, but the second button pressed always will, but ....
2. When any interrupt is triggered then they are always both triggered. That is, the terminal shows a repeating list of "Ok", "One", "Two", "Ok", "One", "Two" ...
3. Once an input has triggered it usually will not trigger again, although the other input usually will, but ...
4. Single alternate presses on the input buttons will not work either - an input must be exercised at least twice before its partner will condescend to work again.

At this point I began to suspect the chip, but I found another 20X2 in an old project, reprogrammed it and it behaves exactly the same.
 

Buzby

Senior Member
I'm not familiar with the AXE091, hence this question.

Are your input pins floating at any time ?.

i.e. Do you have pull-down resistors to keep the pins 'low' when the buttons are not pressed.
 

marks

Senior Member
Hi micrometal,
that is proberly correct
The setint command causes a polled interrupt on a certain input pin conditions. This can be a combination of pins on the default input port
The default condition is a logical AND of the selected input pins.

try

setintflags OR %00000110, %00000110
 

inglewoodpete

Senior Member
I agree with @marks. There is an implied default "AND" relationship between all selected bits in the MASK byte. Your SetIntFlags statement in post #8 is saying "Cause an interrupt when hint1flag AND hint2flag have BOTH latched an input transitioning to high.". Hence, your first interrupt occurs after both input bottons have been pressed. As @marks suggests, add the "OR" parameter to all SetIntFlags commands.
 

micrometal

New Member
Yes @marks - that is it! An RTFM error on my part. I had seen those prefixes but mentally dismissed them as being "advanced usage" that didn't apply to me, and skipped over them. All is well now, and some other things learned along the way.
 

hippy

Technical Support
Staff member
Hence, your first interrupt occurs after both input bottons have been pressed.
Ah yes. I was trying to figure out how the interrupt could be occurring at all because it seemed unlikely that both were being pressed at the same time, but, of course, as you say, it's when both 'hintXflags' are set, indicating "have been pressed".

The first push sets 'hint1flag' but doesn't activate the interrupt. The other button pushed sets 'hint2flag'. Both are now set so the interrupt then occurs. And it reports both were set if the code reports on 'hintXflag' settings. When the check is on pin level, only the last pressed will likely be reported, as appears to be the case with the original code.

On return both flags are cleared so the pushes on a single button are ignored again. Interrupt only happens when the other button is pushed.

So, looks like we have an explanation for what was happening, why, and how to solve it. Success!
 

hippy

Technical Support
Staff member
I'm not familiar with the AXE091, hence this question. Are your input pins floating at any time ?.
The AXE091 buttons are "push to short to V+" and have a pull-down fitted on the board. The button connectors will always deliver a guaranteed high or low, high when pushed.

Each LED is wired to 0V via an R, so one can check a LED works by connecting to V+, and a button checked by linking it to a LED connection; will light when pushed.

Code:
V+ ---.-------------------------------
      |
      O |_
        |_|              LED
      O |
      |                  |\ |
      }------( o---o )---| >|-----.
     .|.                 |/ |    .|.
     | |                         | |
     |_|                         |_|
      |                           |
0V ---^---------------------------^---
 

micrometal

New Member
Just a footnote ...

There was one thing niggling me about this thread : "Why hadn't @hippy fixed it?". Then I carefully reread his second post and discovered that he had. I had copied and pasted his code into the PICaxe editor to test it, but I rewrote the main loop because that was "Just for simulation", and replaced the "Gosub interrupt_Enable" with a straight statement - big mistake. But it should still have worked. I can only think that I tested it by pressing only one button, and was so surprised when it didn't fix the problem that I didn't press the second and so never triggered an interrupt. Still not sure.

But anyway the answer is clear in post #6 - "The key there is "SETINTFLAGS OR" which cause an interrupt on 'hint1flag' or 'hint2flag'" - and all is right with the world.
 

hippy

Technical Support
Staff member
but I rewrote the main loop because that was "Just for simulation", and replaced the "Gosub interrupt_Enable" with a straight statement - big mistake.
The main loop should work with just "Goto Main" but, as Buzby notes in Post #3, this can sometimes be problematic because the PICAXE spends more time handling GOTO rather than between them checking if an interrupt is required. This can be more problematic while simulating without any PAUSE.

As to the "Gosub Interrupt_Enable", it should work just the same if replaced by the SETINTFLAGS in that. The main advantage of the GOSUB is there's only one SETINTFLAGS in the code, so easy to change if one wants to without the risk of having the two being different to each other.

I'm guessing there something just slightly wrong in however you changed the code and that got missed in the heat of the moment.
 
Top