# MCUs which can trigger an interrupt on a port mask

(Hobby and learning, not a professional question.)

I'm designing a circuit which uses a CPLD to turn a half-dozen inputs into a single output which triggers an interrupt in a microcontroller.

Are there any microcontrollers where a combination of inputs (on one port) can trigger an interrupt? (Mostly interested in Cortex M0/3/4, but will consider others.)

e.g. Have an interrupt triggered only when port A matches ?1110?01. 11110001 would cause an interrupt, but 11110000 wouldn't.

• Couldn't you just build this functionality into your CPLD (if it has enough I/O pins)? It could observe the signals that you care about and assert an interrupt when they match one of your target values. Then you can use whichever MCU you like. – Jason R Dec 11 '14 at 14:22
• Sorry, I didn't make it clear that it's easy if I use a CPLD. I'd like to remove the CPLD from the circuit, if the MCU could have an interrupt triggered on a pattern of inputs. – fadedbee Dec 12 '14 at 8:58

Check out the LPC800:

From UM10601: "Up to eight pins can be selected from all GPIO pins to contribute to a boolean expression. The boolean expression consists of specified levels and/or transitions on various combinations of these pins."

• Couldn't find the link on the page, so I manually typed it: nxp.com/documents/user_manual/UM10601.pdf – fadedbee Dec 12 '14 at 9:06
• Thanks for a great answer. On paper it looks like the LPC800 can do exactly what I'm looking for. I already had one of these chips ordered, so it should be here today. – fadedbee Dec 12 '14 at 9:10

I have never heard of one like that, no.

Normally you would have each input attached to a separate interrupt (pin change interrupts are often good for that as long as you aren't dealing with very high speeds).

All those interrupts can run the same ISR code, which first reads the port value, masks it, and compares it with the mask. If they match, then it runs its intended code, otherwise it just returns.

Pretty much any MCU with pin change interrupts (or enough interrupts otherwise) would do the job.

Either that or you would use external glue logic (or internal glue logic on those MCUs with programmable logic blocks, like the Cypress PSoC series) to combine the inputs into one single interrupt signal.

• As a marginally easier part to get at distributors: the Atmel XMEGA E series (and UC3 series) also have configurable glue logic that can be used to this effect. – user36129 Dec 11 '14 at 10:49
• Thanks, I looked at the E series, but there are a maximum of two (2 bit) LUTs which would therefore handle only three inputs, not six. – fadedbee Dec 12 '14 at 10:02

One problem with matching a pattern at an input is asking for how long the pattern needs to be present before being recognised. If Port A changes from 11110000 to 11100001, was there some time during that change when it was 11110001, so an interrupt should be raised? Unless the source and destination of these signals are synchronous - they share a clock and transfer signals reliably on every clock edge - then it is hard to define and check just when the pattern should be recognised.

• Yes the source is 3.5MHz bus, and the MCU would be running at an integer multiple. – fadedbee Dec 12 '14 at 9:03

No, but interrupts and reading ports can be very fast. Set an interrupt on pin change for all the pins of interest, then in the interrupt clear the flag, read the port, compare it, and perform the interrupt if needed, and return from the interrupt.

Of course this requires that your processor be significantly faster than the speed of the bus you are trying to detect events on. If this becomes a problem, you can employ external circuitry, or a CPLD or FPGA.

A nice solution could probably be made using Cypress PSOC devices. The digital configuration blocks are essentially programmable logic devices, and it appears you could build the necessary detector on chip. It might not detect the full width of your desired trigger, but it would significantly reduce the number of interrupts to handle, which may give the processor enough time to process all the interrupts without missing any.

• Fast is relative - I'm snooping a 3.5MHz bus. I think an M0 takes about 10 clocks to trigger an interrupt, plus a few more to do the checks. On a 20-50MHz MCU the (false) interrupts would start to pile up catastrophically. – fadedbee Dec 12 '14 at 9:02
• @chrisdew I've addressed the speed issue a little further. External digital logic isn't terribly difficult or expensive, and might enable you to use a cheaper processor to make up for the additional expense. – Adam Davis Dec 12 '14 at 12:12

There are also I2C port expanders that allow GPIO pins to be added to almost any MCU that supports I2C protocol. (The I2C protocol could be a hardware block on the MCU or bit banged IO pins). Many port expanders have an interrupt output pin that can connect to an interrupt input of the MCU. Inside the port expander part there are registers that allow selection of which I/O pins contribute to the interrupt input and polarity selection registers that select whether the contributing pins cause interrupt on a low or high pin level.

These port expanders are neat because the 2-wire I2C bus can be run over and support the expander chip near where the I/Os are needed on the board. Port expanders can be had that support 4, 8 and 16 GPIO pins. Multiple port expanders can be accommodated on a single I2C bus.

In some usage scenarios the access to read the pin states at interrupt time may take a millisecond or two to access over the I2C bus. If this is too long then consider using an MCU that has the hardware block to support the I2C protocol. Also select the MCU and expander to support the much faster I2C clock rates available these days over the old conventional 100kHz rates.

## Polling to store history of Pin

If the input is coming in serially and you have enough resources on chip to store some history, you could interrupt on a timer, poll/read the value on the pin. Then shift the new value into a byte (which will remove the oldest value at the same time). Now perform a bitwise AND with your desired interrupt sequences.

Basically, you are no longer interrupting on the values present at that pin, but polling it at some interval and storing the last 8 values. When you have collected enough history and it matches the sequence you want, you can fire your own interrupt.

• Thanks for your answer, but I specifically asked for interrupts as the MCU has other tasks to do. (Sorry, I should have made that plain.) This solution would require abusing an extra MCU as a poor CPLD, which might be a useful cost saving (50p vs £4) in a commercial project. – fadedbee Dec 12 '14 at 9:14

For bus snooping, I would suggest using a microcontroller which, while watching the bus, will be devoting all its energy to that task; if any sort of interrupt response will be needed while the bus is being monitored, use a separate processor for the bus monitoring and the other tasks. The bus-monitoring processor could probably act as an SPI slave while it is monitoring the bus, if none of the things it is asked to do while monitoring the bus would be overly complicated [e.g. if the only thing the master ever wants it to do is say whether any interesting bus cycle has happened and, if so, what data was transferred then]. Note that the firmware for the bus-monitoring processor would likely have to be written in machine code and might look something like this:

MAIN_LOOP:
bl   R12                    ; Assume R12 holds address of bus-polling rou
ldrb r0,[r11,#SPI_REGS_OFS] ; Assume R11 always holds base of SPI peripheral
beq  MAIN_LOOP
bl   R12
ldrb r4,[r12,#SPI_DATA_REG]
and  r0,r4,#15
ldr  pc,[pc,r0,asl,#2]
nop
; Insert table of 15 jump addresses here
dc.w CMD00_HANDLER, CMD01_HANDLER, CMD02_HANDLER, ... etc.

CMD00_HANDLER:
bl   R12
sub  r0,r8,r9 ; Assume R8 and R9 are queue pointers
strb r0,[r12,#SPI_DATA_REG]
b    MAIN_LOOP

CMD01_HANDLER:
bl   R12
ldrb r0,[r8],#1
bic  r8,#256 ; Assume queue is located at address of form xxxx0xxxxxxxx.
strb r0,[r12,#SPI_DATA_REG]
b    MAIN_LOOP

POLL_BUS:
ldr  r5,[r10,#PORT_OFS]  ; Assume R10 always holds port base
and  r0,r5,r7            ; Assume R7 always holds mask
cmp  r0,r6               ; Assume R0 holds compare value
b    r14

POLL_BUS2:
str  r5,[r9],#4
bic  r9,#256 ; Assume queue is located at address of form xxxx0xxxxxxxx.

Note that code never goes very long without a bl R12 which calls one of the POLL_BUS functions, and none of those take very long before returning. Keeping everything in dedicated registers allows the code to run much faster than would be possible in C. The above code would be designed to capture four-byte records any time the bus state changes to a particular pattern; once it grabs something, it will wait until something on the port (whether masked or not) changes before grabbing the next item.