Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| learn:microprocessor:tutorials:pic32-interrupts:start [2017/12/18 20:02] – created Martha | learn:microprocessor:tutorials:pic32-interrupts:start [2022/10/04 11:38] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 13: | Line 13: | ||
| ==== Inventory ==== | ==== Inventory ==== | ||
| - | * 1 [[http://store.digilentinc.com/ | + | * 1 [[https://digilent.com/shop/ |
| * [[http:// | * [[http:// | ||
| - | * 1 [[http://store.digilentinc.com/ | + | * 1 [[https://digilent.com/shop/ |
| * 1 Stepper Motor (5V – 12V, 25 Ω, unipolar or bi-polar) | * 1 Stepper Motor (5V – 12V, 25 Ω, unipolar or bi-polar) | ||
| - | * 1 Logic analyzer or oscilloscope (Digilent [[http://store.digilentinc.com/ | + | * 1 Logic analyzer or oscilloscope (Digilent [[https://digilent.com/ |
| ---- | ---- | ||
| Line 26: | Line 26: | ||
| \\ | \\ | ||
| < | < | ||
| - | <a href="https:// | + | <a href="/ |
| </ | </ | ||
| \\ | \\ | ||
| \\ | \\ | ||
| < | < | ||
| - | <a href="https:// | + | <a href="/ |
| </ | </ | ||
| \\ | \\ | ||
| \\ | \\ | ||
| + | |||
| + | ==== Mechanics of Interrupts ==== | ||
| + | Preemption by an interrupt causes the processor to stop the execution of the current code flow and begin execution of a special function called an “interrupt handler” or “interrupt service routine” (ISR). In previous projects, the order in which tasks were started and completed was strictly controlled by the order in which code was executed in the program. In this project, tasks are started and completed based on a programmed prioritization. | ||
| + | |||
| + | An [[http:// | ||
| + | |||
| + | The three main types of interrupts are software, internal hardware, and external hardware. Interrupts that are initiated by some event inside the microprocessor are also commonly referred to as exceptions or traps. Software interrupts are explicitly internally triggered by some instruction within the current program and are commonly referred to as trap instructions. Exceptions are internally generated hardware interrupts triggered by errors detected during software execution, such as illegal math operations (overflow, divide-by-zero), | ||
| + | |||
| + | The [[https:// | ||
| + | |||
| + | Interrupt latency is defined as the time between the instant when an event generates the interrupt signal and the time when the first useful instruction in the ISR is executed. This latency is made up of two major components: the time required to save the processor context and the amount of time that an interrupt is disabled or deferred by higher priority interrupts. | ||
| + | |||
| + | ==== Managing Interrupts ==== | ||
| + | Preemptive programs usually consist of two types of tasks: foreground and background. Foreground tasks are those preemptive tasks that the processor executes as soon as the need arises. Interrupts are assigned priority levels that dictate the order in which interrupts will be serviced. In the case of multiple foreground tasks both needing service at the same time, the higher the interrupt priority, the sooner the microprocessor executes the code to service that interrupt. Code executed immediately following a power-up reset runs at priority level zero and has no preemptive capability. This initial background task is responsible for setting up the resources for tasks that will eventually run as foreground tasks. | ||
| + | |||
| + | Foreground tasks have preemptive capability by having higher priorities than background tasks. The foreground task ensures adequate response times, while the background task manages deferred processing of the foreground data. Background tasks are those that the processor executes whenever it has available time and is waiting for something more important to do. Background tasks generally run at the interrupt priority level zero within a while(1) { … } loop, where event detection by polling is common. Interrupt only systems that only have foreground processes respond quickly to both periodic and sporadic events but ignore potential work that can be allocated to a background process. The use of interrupts falls under the broad category of real-time task scheduling, and a thorough investigation of this topic is beyond the scope of this project. | ||
| + | |||
| + | There are two types of preemptive operating schemes: nested and non-nested. Nested interrupt schemes allow higher priority level interrupts to preempt code that is servicing a lower priority interrupt. A non-nested interrupt scheme completes the execution of the code currently servicing an interrupt before it begins to service the code for any interrupt awaiting service regardless of interrupt priorities. | ||
| + | |||
| + | Non-nested preemptive schemes are usually easier to manage, but they can result in priority inversion, where a low priority task blocks a higher priority task from running. This scheme is easier to manage, because the developer only has to focus on one ISR, and the operation that determines which interrupts to service resembles the process of polling that we did in Project 4. For non-nested interrupt schemes, we must individually check the flags set by the interrupting event to determine which event to service. All interrupts are assigned to priority level 1 and are vectored (sent) to the same ISR where the particular interrupt flag must be cleared. For multiple simultaneous interrupts, tasks are serviced in the order that the interrupt flags are polled. A non-nested priority scheme results whenever interrupts are enabled using the instruction INTEnableSystemSingleVectoredINT(); | ||
| + | |||
| + | Nested priority schemes are generally more responsive, taking advantage of the fact that higher priority tasks can preempt (interrupt) lower priority tasks. The highest priority interrupt is guaranteed to have the lowest latency. However, nested priority schemes require more computer resources, such as time and memory, to accommodate context saving and restoring. Some low-end microcontrollers simply do not have sufficient memory and/or speed to support fully nested priority schemes. In reality, many embedded systems manage tasks using both nested and non-nested preemptive schemes as a result of either hardware and/or software limitations. Either interrupt management scheme can be used in combination with polling to detect events, as will be the case for Project 5. | ||
| + | |||
| + | The PIC32MX family of processors can use polling, nested priority, and non-nested priority schemes simultaneously. The PIC32MX processors utilize two major software level priorities called the “Group Priority” and the “Subgroup Priority.” There are seven levels that can be assigned for the group priority (1-7), with one being the lowest priority and seven being the highest. An interrupt with a higher group priority will preempt an interrupt of a lower priority. There are four subgroup priority levels that can be assigned at each of the seven levels of group priority. Interrupts assigned to different group priority levels operate as nested interrupts. | ||
| + | |||
| + | Multiple events can be assigned interrupt priorities at the same group level but at different subgroup levels. The sub-priority will not cause preemption of an interrupt in the same group priority level; rather, if two interrupts with the same group priority level are pending, the interrupt with the highest sub-priority will be handled first. The natural (hardware) priority scheme is asserted whenever multiple interrupts are generated simultaneously for events that are set for the same group and subgroup priority levels. (The notion of “simultaneous events” must be expanded to mean “if two interrupts are detected as waiting for service,” whether or not they occur at the same instant of time.) See Section 8 of the [[http:// | ||
| + | |||
| + | In any case, for the PIC32MX family of processors, the interrupt flag bit must be cleared in software prior to completing the service of the interrupt, otherwise the same interrupt will be serviced again without an event to initiate its service. | ||
| ---- | ---- | ||
| - | ===== Step 2: Building the Circuit | + | ===== PIC32 Interrupts |
| - | Having chosen our resistor and capacitor values, we can begin putting | + | ==== General Interrupt Requirements ==== |
| + | There are four essential code elements required for a program to process interrupts using C—the declaration of the functions that will be used to service the interrupts, the code to initialize the resources | ||
| - | ==== Modifying | + | Functions that have been declared as an ISR cannot be called by any other C function. There are two ways that the ISR code will be executed: either in response |
| - | - Connect the wire from pin 7 to the button' | + | |
| - | * NOTE: The right side of the button corresponds here to the “top” of the button. This means that the two button legs that are always electrically connected are oriented vertically so that they span the valley between | + | |
| - | - Connect the 5V source | + | |
| - | - Connect a 10 µF capacitor and 10 kΩ resistor to the right side of the button. | + | |
| - | - Connect the ground pin (GND) to the right side of the capacitor | + | |
| - | {{ :learn: | + | Normally, the code to initialize the resources that will generate the interrupt is executed only once for a given application. In this project, two different interrupts will be generated: Timer 1 interrupt and an I/O pin change notice interrupt. The programming requirements for each of these two interrupts are discussed below. The two statements in Listing |
| + | **Listing 1.** Global Enabling of Global Interrupts. | ||
| + | |||
| + | < | ||
| + | // Enable multi vectored interrupts | ||
| + | INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); | ||
| + | INTEnableInterrupts(); | ||
| + | </ | ||
| ---- | ---- | ||
| - | ===== Testing the Debouncing Properties of the RC Filter | + | ===== Timer Interrupts |
| - | Now that we have built the circuit, you can test the debouncing properties of the RC filter. Plug the USB cable into your chipKIT board and program it with code from Debouncing via Software: | + | In Project 4, you configured Timer 1 to set the interrupt flag once each millisecond. However, no interrupt was ever generated. In order to set up a timer to generate interrupts, you must first initialize |
| + | |||
| + | **Listing 2.** Initializing Timer 1 Interrupts. | ||
| < | < | ||
| - | const int btnPin = 7; // Number of the pushbutton pin | + | #define T1_INTR_RATE 10000 |
| - | const int ledPin | + | |
| - | int currentLedState; | + | void timer1_interrupt_initialize(void) |
| - | int previousLedState; | + | { |
| - | int currentBtnState; | + | |
| - | int previousBtnState; | + | |
| + | //configure Timer 1 using internal clock, 1:1 prescale, PR1 = T1_TICK-1 | ||
| + | OpenTimer1( (T1_ON | T1_SOURCE_INT | T1_PS_1_1), (T1_INTR_RATE - 1) ); | ||
| - | unsigned int count; | + | // set up the timer interrupt with a priority |
| - | unsigned int lastDebounceTime; | + | mT1SetIntPriority(2); // Group priority range: 1 to 7 |
| - | unsigned int debounceDelay; | + | mT1SetIntSubPriority(0); |
| + | mT1IntEnable(1); // Enable T1 interrupts | ||
| - | void setup() { | + | // Global interrupts must enabled to complete the initialization. – see |
| - | + | // Listing 1. | |
| - | pinMode(btnPin, | + | |
| - | pinMode(ledPin, | + | |
| - | + | ||
| - | currentLedState = LOW; | + | |
| - | previousLedState = LOW; | + | |
| - | currentBtnState = LOW; | + | |
| - | previousBtnState = LOW; | + | |
| - | + | ||
| - | count = 0; | + | |
| - | lastDebounceTime = 0; | + | |
| - | debounceDelay = 50; | + | |
| - | + | ||
| - | Serial.begin(9600); | + | |
| } | } | ||
| + | </ | ||
| + | |||
| + | Timer 1 interrupts can be disabled at any point in the application software by using the instruction mT1IntEnable(0); | ||
| + | |||
| + | Listing 3's code shows how to both declare a function to be an ISR and the general format of an ISR function. For this example, the parameter ipl2 sets the Timer 1 interrupt level to 2. (Note: the MPLAB X editor uses Courier font, and the lower case “L” is difficult to distinguish from the number “1” in the aforementioned font. As an example, consider setting the interrupt level to 1 using the parameter ipl1.) This method of declaring an ISR eliminates the requirement of a function prototype. Additional methods are provided in the PIC32 Interrupts section (button below) because they may be used by other sources you may find. | ||
| + | |||
| + | < | ||
| + | <a href="/ | ||
| + | </ | ||
| + | \\ | ||
| + | \\ | ||
| + | |||
| + | **Listing 3.** Timer 1 ISR at Interrupt Level 2. | ||
| + | |||
| + | < | ||
| + | void __ISR(_TIMER_1_VECTOR, | ||
| + | { | ||
| - | void loop() { | + | /* User generated code to service the interrupt is inserted here */ |
| - | currentBtnState = digitalRead(btnPin); | + | mT1ClearIntFlag(); // Macro function |
| - | + | ||
| - | if (currentBtnState != previousBtnState) { | + | |
| - | + | ||
| - | lastDebounceTime = millis(); | + | |
| - | // every time the button state changes, get the time of that change | + | |
| - | } | + | |
| - | + | ||
| - | if ((millis() - lastDebounceTime) > debounceDelay) { | + | |
| - | + | ||
| - | /* | + | |
| - | *if the difference between the last time the button changed is greater | + | |
| - | *than the delay period, it is safe to say | + | |
| - | *the button is in the final steady state, so set the LED state to | + | |
| - | *button state. | + | |
| - | */ | + | |
| - | currentLedState = currentBtnState; | + | |
| } | } | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Change Notice Interrupts ===== | ||
| + | Change notice (CN) interrupts are generated on selected enabled digital I/O pins whenever the voltage of the pin changes, causing the processor to read a logic value that is different from the previous reading of the PORT register. Pins designated as CN interrupt pins are shown in Table 1. Only BTN1 and BTN2 on the Digilent Pro MX7 processor board have the capability to generate CN interrupts. The I/O pin associated with BTN3 cannot generate a CN interrupt. | ||
| + | |||
| + | ^ CNx |^ Port ^ | ||
| + | | CN0 || RC13 | | ||
| + | | CN1 || RC14 | | ||
| + | | CN2 || RB0 | | ||
| + | | CN3 || RB1 | | ||
| + | | CN4 || RB2 | | ||
| + | | CN5 || RB3 | | ||
| + | | CN6 || RB4 | | ||
| + | | CN7 || RB5 | | ||
| + | | CN8 || RG6 — BTN1 | | ||
| + | | CN9 || RG7 — BTN2 | | ||
| + | | CN10 || RG8 | | ||
| + | | CN11 || RG9 | | ||
| + | | CN12 || RB15 | | ||
| + | | CN13 || RD4 | | ||
| + | | CN14 || RD5 | | ||
| + | | CN15 || RD6 | | ||
| + | | CN16 || RD7 | | ||
| + | | CN17 || RF4 | | ||
| + | | CN18 || RF5 | | ||
| + | | CN19 || N/A | | ||
| + | |||
| + | **Table 1. I/O pins with CN interrupt capability.** | ||
| + | |||
| + | CN interrupts can be disabled to protect segments of code using mCNIntEnable(0); | ||
| + | |||
| + | **Listing 4.** Initializing CHANGE NOTICE interrupts. | ||
| + | < | ||
| + | /* Declaration of interrupt ISR for CN at interrupt level 1 */ | ||
| + | void cn_interrupt_initialize(void) // This code is executed only once | ||
| + | { | ||
| + | // BTN1 and BTN2 pins are set as inputs by including the file | ||
| + | // chipKit_Pro_MX7.h and calling chipKit_Pro_MX7_Setup(); | ||
| + | // PORTSetPinsDigitalIn(BIT_6 | BIT7); | ||
| + | // Enable CN for BTN1 and BTN2 | ||
| + | mCNOpen(CN_ON, | ||
| - | // ********************* start functional code ************************************** | + | // Set CN interrupts priority level 1 sub priority level 0 |
| - | + | mCNSetIntPriority(1); | |
| - | + | mCNSetIntSubPriority(0); | |
| + | mCNIntEnable(1); | ||
| - | + | // Global interrupts must enabled to complete the initialization. | |
| - | + | ||
| - | // verification code, and a button press counter | + | |
| - | + | ||
| - | if ((previousLedState == LOW) && (currentLedState == HIGH)) { | + | |
| - | //count rising edges | + | |
| - | count++; | + | |
| - | Serial.println(count); | + | |
| } | } | ||
| + | </ | ||
| + | |||
| + | A single interrupt vector is used for all CN interrupts. The interrupt does not tell you if the pin went high or low; only that the condition on one of the selected pins has changed. The ISR code must return a type void and have no parameters passed to it. The CN interrupt flag must be cleared prior to exiting the ISR using the instructions mCNClearIntFlag(); | ||
| + | |||
| + | **Listing 5.** Change Notice interrupt service routine. | ||
| + | < | ||
| + | void __ISR(__CHANGE_NOTICE_VECTOR, | ||
| + | { | ||
| + | /* User ISR code inserted here */ | ||
| - | + | /* Required to clear the interrupt flag in the ISR */ | |
| - | + | mCNClearIntFlag(); // Macro function | |
| - | + | ||
| - | // ********************* end functional code ************************************** | + | |
| - | + | ||
| - | + | ||
| - | + | ||
| - | // set current states to previous states | + | |
| - | digitalWrite(ledPin, currentLedState); | + | |
| - | previousBtnState = currentBtnState; | + | |
| - | previousLedState = currentLedState; | + | |
| } | } | ||
| </ | </ | ||
| - | Once your board is programmed, the external LED should start blinking. | + | ---- |
| - | Remove the capacitor from the circuit; it's okay to do this while the circuit has power. Since nothing can flow across this gap, more current will be diverted through the resistor | + | ===== Project Tasks ===== |
| + | This project implements a system | ||
| - | To see what the circuit is actually doing, check out Fig. 2. It shows what the button | + | < |
| + | <ol type=" | ||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | </ | ||
| - | {{ : | ||
| - | {{ : | + | ---- |
| - | {{ :learn: | + | ===== Project Testing ===== |
| + | There are significant timing requirements for this project: the Timer 1 interrupt rate, the stepper motor step rate, and the button debounce delay. Table 2 is provided below to assist you in determining where to place the instructions to control the instrumentation LEDs. The red tab above shows where to connect an oscilloscope or logic analyzer to the test points for LEDA through LEDC on the PmodSTEP to instrument this project. An example | ||
| - | As you can see, the capacitor dramatically limits | + | | LED ^ Operation |
| + | | LEDA | Toggle | ||
| + | | LEDB | Toggle | ||
| + | | LEDC | SET (on) | Start of CN ISR | | ||
| + | | LEDC | Cleared (off) | End of CN ISR | | ||
| + | |||
| + | **Table 2. Instrumentation LED Operations.** | ||
| + | |||
| + | {{ : | ||
| + | Figure 1. Screen capture for the instrumentation of Project 5. Screenshot of Digilent WaveForms running on Microsoft Windows 7. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Test Your Knowledge! ===== | ||
| + | Now that you've completed this project, you should look at the following questions/ | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | </ | ||
| + | {{ : | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | < | ||
| + | <ol start=" | ||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | <font color=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| - | {{tag> | + | {{tag> |

