NU32v2: Interrupts

From Mech
Revision as of 03:35, 31 January 2011 by Lynch (talk | contribs) (→‎Details)
Jump to navigationJump to search

*** UNDER CONSTRUCTION ***

Say your PIC is attending to some mundane task when an important event occurs. For example, the user has pressed a button. We want the PIC to respond immediately. To do so, we have this event generate an interrupt, or interrupt request (IRQ), which interrupts the program and sends the CPU to execute some other code, called the interrupt service routine (or ISR). Once this code has completed, the CPU returns to its original task.

Interrupts are a key concept in real-time control, and they can arise from many different events. This page provides a summary of PIC32 interrupt handling.

Overview

Interrupts can be generated by the processor core, peripherals, and external inputs. Example events include

  • a digital input changing its value,
  • information arriving on a communication port,
  • the completion of some task the PIC32 was executing in parallel with the CPU, and
  • the elapsing of a specified amount of time.

As an example, to guarantee performance in real-time control applications, we must read the sensors and calculate new control signals at a known fixed rate. For a robot arm, a common control loop frequency is 1 kHz. So we would configure one of the PIC32's counter/timers to use the peripheral bus clock as input, and choose prescaler and period register values so that the counter rolls over every 1 ms. This roll-over "event" generates the interrupt that calls our feedback control ISR. In this case, we would have to make sure that our control ISR is efficient code that always executes in (much) less than 1 ms.

Say the PIC is currently controlling the robot arm to hold steady in a particular position. It then receives new information from the user, who asks the arm to move to a new location. This new information also generates an interrupt, and the corresponding ISR reads in the information and stores it in global variables representing the desired state. These desired states are used in the feedback control ISR.

So what happens if we are in the middle of executing the control ISR and a communication interrupt is generated? Or if we are in the middle of the communication ISR and a control interrupt is generated? We must make a choice about which has higher priority. If a high priority interrupt occurs while a low priority ISR is executing, the CPU will jump to the high priority ISR, complete it, and then return to finish the low priority ISR. If a low priority interrupt occurs while a high priority ISR is executing, the low priority ISR will wait patiently until the high priority ISR is finished executing. When it is finished, the CPU jumps to the low priority ISR.

In our example, communication could be slow, taking several milliseconds to complete, and we might not have a guarantee as to the duration. To ensure the stability of the robot arm, we would probably choose the control interrupt to have higher priority than the communication interrupt.

Every time an interrupt is generated, the CPU must store its current context on the stack, so that when it returns from the ISR, it can read this information back in and continue where it left off. If interrupts are being nested (one ISR interrupts another which has interrupted another, etc.), then the PIC may run out of memory, causing a "stack overflow" and the program to crash. These errors can be very difficult to debug, so it is a good idea to ensure that any high priority ISRs execute in a known, fixed amount of time.

The address of the ISR in virtual memory is called the interrupt vector, and the PIC32 can support up to 64 unique interrupt vectors arising from up to 96 different interrupt sources. If all interrupts jump to the same ISR, the PIC32 is in "single vector mode." This is the default on reset. If each interrupt has its own ISR, the PIC32 is in "multi-vector mode."

Details

The CPU jumps to an ISR when three conditions are satisfied: (1) the interrupt has been enabled by setting a bit in the SFR IECx (Interrupt Enable Control) to 1; (2) an interrupt has been requested by setting the same bit in the SFR IFSx (Interrupt Flag Status) to 1; and (3) the priority of the interrupt, as represented in the SFR IPCy (Interrupt Priority Register), is greater than the current priority of the CPU. If the first two conditions are satisfied, but not the third, the interrupt remains pending until the CPU's priority drops lower.

The "x" in the IECx and IFSx SFRs above can be 0, 1, or 2, or (3 registers) x (32 bits) = 96 interrupt sources. The "y" in IPCy takes values {0..15}. Each of these registers contains 20 relevant bits: 5 bits encoding the priority for each of four different interrupt vectors. So we have (16 registers) x (4 priorities) = 64 priorities for the 64 interrupt vectors. The 5 bits defining the priority are further separated into 3 bits for the main priority taking values 1..7, with 7 being the highest priority (all bits equal to zero causes the interrupt to be disabled) and 2 bits for the sub-priority (taking values 0..3) to distinguish among ISRs at the same priority level. Thus we essentially have 7x4 = 28 priority levels. The mapping from each interrupt source and vector to x, y, and the bit locations are given in Chapter 7 of the Data Sheet. Note that since there are more interrupt sources than interrupt vectors, some sources share the same vector. For example, IRQs (interrupt request sources) 29, 30, and 31, each corresponding to events on I^2C communication channel 1, all share the same vector 25.

If two interrupts have been requested, and they have the same priority and sub-priority, their priority is determined by the "natural order priority" table given in the Data Sheet. (In this table, though, higher priority corresponds to lower numbers!)

The last thing any ISR should do is clear the interrupt flag (clear the appropriate bit of IFSx to zero), indicating that the interrupt has been serviced and the CPU is free to either return to the the program state when the ISR was called or service another interrupt that has been requested in the meantime.

When setting up an interrupt, you will set a bit IECx to 1 indicating the interrupt is enabled (all bits are set to zero upon reset) and assign values to the IPCy priority bits. As mentioned above, you will also clear the IFSx bit at the end of the ISR. You will never write code setting the IFSx bit to 1, however. Instead, when you set up the device that generates the interrupt (e.g., a counter/timer), you will indicate that it should set the interrupt flag upon the appropriate event.

Apart from the SFRs IECx, IFSx, and IPCy, three other SFRs are relevant to interrupts: INTCON (Interrupt Control), INTSTAT (Interrupt Status), and TPTMR (Temporal Proximity Timer). The SFRs are summarized below.

  • IECx (Interrupt Enable Control): A 1 enables the interrupt, a 0 disables it. Three 32-bit SFRs for 96 interrupt sources.
  • IFSx (Interrupt Flag Status): A 1 indicates an interrupt has been requested, a 0 indicates no interrupt is requested. Three 32-bit SFRs for 96 interrupt sources.
  • IPCy (Interrupt Priority Control): Sixteen registers, each with 5 bit priority values for 4 different interrupt vectors (64 vectors total).
  • INTCON (Interrupt Control): Determines whether the interrupt controller operates in single vector or multi-vector mode. Also determines whether the five external interrupt pins INT0..INT4 generate an interrupt on a rising edge or a falling edge. It also configures some other things we will ignore; see the Reference Manual for more details.
  • INTSTAT (Interrupt Status): Read-only: information on the address and priority level of the latest IRQ given to the CPU. (Not relevant for us.)
  • TPTMR (Temporal Proximity Timer): We can ignore this.

More details can be found in Chapter 7 of the Data Sheet and Section 8 of the Reference Manual.

Library Functions

Sample Code

More Information