CPSC 359
R-PI Interrupts
PhD Student
Fall 2018
Today
What's an interrupt?
Think, for example, of how you might receive input from a keyboard.
You might have a loop constantly asking the hardware (i.e. polling the hardware) if it's seen a key press.
This feels wasteful. Instead, the keyboard may notify the CPU when a key was pressed, interrupting the current program. The CPU then reads the key press, and continues executing the original, interrupted program.
This is the key idea behind interrupts
IRQs
Many possible sources of interrupts, so there's dedicated hardware that maps input interrupt lines to device interrupt request IRQ lines.
IRQs
When an interrupt occurs, at a high level this is what happens:
- An exception occurs, and the CPU saves important program state, sets some flags, then branches to the exception handler
- The exception handler should save program state to the stack, then service the interrupt
- Exception handler restores program state, and informs the CPU that it's finished, at which point the additional previous state that the CPU saved is restored, and execution of the original program is continued
Side note: IRQ routines should be fast so that you can return to the original program without much delay (and so that you don't risk dropping other interrupts). They should also be debugged very carefully, because they run in a more privileged mode (especially important when the original code that's being interrupted was at a lower privilege level.)
Exception Vector Table
// Exception Vector Table:
//
// The start of the table must be aligned to an address
// evenly divisible by 2048 (i.e. it must end with 11 zeroes).
// Furthermore, each entry must also be aligned to an
// address evenly divisible by 128 (i.e. must end with 7 zeroes),
// and entries must follow each other consecutively in memory.
// Each vector can be as long as 32 instructions.
.align 11
_vectors:
// Synchronous
.align 7
b _synch_handler // call handler stub
// IRQ
.align 7
b _IRQ_handler // call handler stub
// FIQ
.align 7
b _FIQ_handler // call handler stub
// SError
.align 7
b _SError_handler // call handler stub
@Bottom of startV2.s in example 05_GPIO_PushButtonInterrupt, this tells the CPU where the handlers are for certain requests. You don't need to change this for assignment 3
IRQ Handler Pt.1
_IRQ_handler:
// Save state of all general purpose registers.
// We do this so that any C code that we call
// from here can use any of the general purpose
// registers.
stp x0, x1, [sp, -16]!
stp x2, x3, [sp, -16]!
stp x4, x5, [sp, -16]!
stp x6, x7, [sp, -16]!
stp x8, x9, [sp, -16]!
stp x10, x11, [sp, -16]!
stp x12, x13, [sp, -16]!
stp x14, x15, [sp, -16]!
stp x16, x17, [sp, -16]!
stp x18, x19, [sp, -16]!
stp x20, x21, [sp, -16]!
stp x22, x23, [sp, -16]!
stp x24, x25, [sp, -16]!
stp x26, x27, [sp, -16]!
stp x28, x29, [sp, -16]!
str x30, [sp, -16]!
// Call the IRQ handler written in C
bl IRQ_handler
// Restore state of all general purpose registers
ldr x30, [sp], 16
ldp x28, x29, [sp], 16
ldp x26, x27, [sp], 16
ldp x24, x25, [sp], 16
ldp x22, x23, [sp], 16
ldp x20, x21, [sp], 16
ldp x18, x19, [sp], 16
ldp x16, x17, [sp], 16
ldp x14, x15, [sp], 16
ldp x12, x13, [sp], 16
ldp x10, x11, [sp], 16
ldp x8, x9, [sp], 16
ldp x6, x7, [sp], 16
ldp x4, x5, [sp], 16
ldp x2, x3, [sp], 16
ldp x0, x1, [sp], 16
// Return from exception
eret
@Bottom of startV2.s in example 05_GPIO_PushButtonInterrupt, this saves all the registers on the stack then calls the IRQ handler in C. You don't need to change this for assignment 3
IRQ Handler Pt.2
extern unsigned int sharedValue;
void IRQ_handler()
{
unsigned int r;
// Print out exception type
uart_puts("\nInside IRQ exception handler:\n");
// Print out further information about the exception
r = getCurrentEL();
uart_puts(" CurrentEL is: 0x");
uart_puthex(r);
uart_puts("\n");
r = getDAIF();
uart_puts(" DAIF is: 0x");
uart_puthex(r);
uart_puts("\n");
r = *IRQ_PENDING_2;
uart_puts(" IRQ_PENDING_2 is: 0x");
uart_puthex(r);
uart_puts("\n");
r = *GPEDS0;
uart_puts(" GPEDS0 is: 0x");
uart_puthex(r);
uart_puts("\n");
// Handle GPIO interrupts in general
if (*IRQ_PENDING_2 == 0x00100000) {
// Handle the interrupt associated with GPIO pin 17
if (*GPEDS0 == 0x00020000) {
// Clear the interrupt by writing a 1 to the GPIO Event Detect
// Status Register at bit 17 (p. 96 of the Broadcom Manual)
*GPEDS0 = (0x1 << 17);
// Handle the interrupt:
// We do this by incrementing the shared global variable
sharedValue++;
}
}
// Return to the IRQ exception handler stub
return;
}
handlers.c in example 05_GPIO_PushButtonInterrupt. This is where you'll be making some changes in assignment 3
IRQ Handler Pt.2
// Handle GPIO interrupts in general
if (*IRQ_PENDING_2 == 0x00100000) {
// Handle the interrupt associated with GPIO pin 17
if (*GPEDS0 == 0x00020000) {
// Clear the interrupt by writing a 1 to the GPIO Event Detect
// Status Register at bit 17 (p. 96 of the Broadcom Manual)
*GPEDS0 = (0x1 << 17);
// Handle the interrupt:
// We do this by incrementing the shared global variable
sharedValue++;
}
}
This is the more important part. We check that we have an IRQ pending, then check to see if it was generated by pin 17. If it was, we clear the status register for pin 17.
Demo
Take some time to run example 05_GPIO_PushButtonInterrupt
Connect pin 17 on your breakout board to switch B (the bottom button)
Let's add another pin
// Handle GPIO interrupts in general
if (*IRQ_PENDING_2 == 0x00100000) {
// Handle the interrupt associated with GPIO pin 17
if (*GPEDS0 == 0x00020000) {
// Clear the interrupt by writing a 1 to the GPIO Event Detect
// Status Register at bit 17 (p. 96 of the Broadcom Manual)
*GPEDS0 = (0x1 << 17);
// Handle the interrupt:
// We do this by incrementing the shared global variable
sharedValue++;
}
}
Let's also service pin 18. And let's do it on a falling edge (the current example is on a rising edge.)
Let's add another pin
// Handle GPIO interrupts in general
if (*IRQ_PENDING_2 == 0x00100000) {
// Handle the interrupt associated with GPIO pin 17
if (*GPEDS0 == 0x00020000) {
// Clear the interrupt by writing a 1 to the GPIO Event Detect
// Status Register at bit 17 (p. 96 of the Broadcom Manual)
*GPEDS0 = (0x1 << 17);
// Handle the interrupt:
// We do this by incrementing the shared global variable
sharedValue++;
}
// Handle the interrupt associated with GPIO pin 18
if (*GPEDS0 == 0x00040000) {
// Clear the interrupt by writing a 1 to the GPIO Event Detect
// Status Register at bit 18 (p. 96 of the Broadcom Manual)
*GPEDS0 = (0x1 << 18);
// Handle the interrupt:
// We do this by decrementing the shared global variable
sharedValue--;
}
}
This won't do anything yet (even if you've connected pin 18 to the pulled up button on your breadboard). We need to set pin 18 to generate an interrupt.
Let's enable another pin
void init_GPIO17_to_risingEdgeInterrupt()
{
register unsigned int r;
// Get the current contents of the GPIO Function Select Register 1
r = *GPFSEL1;
// Clear bits 21 - 23. This is the field FSEL17, which maps to GPIO pin 17.
// We clear the bits by ANDing with a 000 bit pattern in the field. This
// sets the pin to be an input pin
r &= ~(0x7 << 21);
// Write the modified bit pattern back to the
// GPIO Function Select Register 1
*GPFSEL1 = r;
// Disable the pull-up/pull-down control line for GPIO pin 17. We follow the
// procedure outlined on page 101 of the BCM2837 ARM Peripherals manual. We
// will pull down the pin using an external resistor connected to ground.
// Disable internal pull-up/pull-down by setting bits 0:1
// to 00 in the GPIO Pull-Up/Down Register
*GPPUD = 0x0;
// Wait 150 cycles to provide the required set-up time
// for the control signal
r = 150;
while (r--) {
asm volatile("nop");
}
// Write to the GPIO Pull-Up/Down Clock Register 0, using a 1 on bit 17 to
// clock in the control signal for GPIO pin 17. Note that all other pins
// will retain their previous state.
*GPPUDCLK0 = (0x1 << 17);
// Wait 150 cycles to provide the required hold time
// for the control signal
r = 150;
while (r--) {
asm volatile("nop");
}
// Clear all bits in the GPIO Pull-Up/Down Clock Register 0
// in order to remove the clock
*GPPUDCLK0 = 0;
// Set pin 17 to so that it generates an interrupt on a rising edge.
// We do so by setting bit 17 in the GPIO Rising Edge Detect Enable
// Register 0 to a 1 value (p. 97 in the Broadcom manual).
*GPREN0 = (0x1 << 17);
// Enable the GPIO IRQS for ALL the GPIO pins by setting IRQ 52
// GPIO_int[3] in the Interrupt Enable Register 2 to a 1 value.
// See p. 117 in the Broadcom Peripherals Manual.
*IRQ_ENABLE_IRQS_2 = (0x1 << 20);
}
This is the init for pin 17, let's add another function for 18
Let's enable another pin
void init_GPIO18_to_fallingEdgeInterrupt()
{
register unsigned int r;
r = *GPFSEL1;
r &= ~(0x7 << 24); // For pin 18
*GPFSEL1 = r;
*GPPUD = 0x0;
r = 150;
while (r--) {
asm volatile("nop");
}
// Similar to previous function
*GPPUDCLK0 = (0x1 << 18);
r = 150;
while (r--) {
asm volatile("nop");
}
// Clear all bits in the GPIO Pull-Up/Down Clock Register 0
// in order to remove the clock
*GPPUDCLK0 = 0;
// Set pin 18 to so that it generates an interrupt on a /falling/ edge.
// We do so by setting bit 18 in the GPIO Falling Edge Detect Enable
// Register 0 to a 1 value (p. 97 in the Broadcom manual).
*GPFEN0 = (0x1 << 18);
// Enable the GPIO IRQS for ALL the GPIO pins by setting IRQ 52
// GPIO_int[3] in the Interrupt Enable Register 2 to a 1 value.
// See p. 117 in the Broadcom Peripherals Manual.
*IRQ_ENABLE_IRQS_2 = (0x1 << 20);
}
Add this to main.c
Let's enable another pin
// Initialize the sharedValue global variable and
// and set the local variable to be same value
localValue = sharedValue = 0;
// Set up GPIO pin #17 to input and so that it triggers
// an interrupt when a rising edge is detected
init_GPIO17_to_risingEdgeInterrupt();
Find this in main.c
// Initialize the sharedValue global variable and
// and set the local variable to be same value
localValue = sharedValue = 0;
// Set up GPIO pin #17 to input and so that it triggers
// an interrupt when a rising edge is detected
init_GPIO17_to_risingEdgeInterrupt();
init_GPIO18_to_fallingEdgeInterrupt();
Change it to this
Demo
Connect pin 18 on your breakout board to switch A (the top button)
CPSC 359: Pi Interrupts
By Joshua Horacsek
CPSC 359: Pi Interrupts
- 1,447