Difference between revisions of "Embedded System Tutorial Interrupts"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Assignment)
(Replaced content with "Socialledge is moving to two portals. * The Wiki will remain here for general references about the SJ-One board, and to document student reports. * The bookstack will...")
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Introduction ==
+
Socialledge is moving to two portals.   
This tutorial demonstrates how to use interrupts on a processorIn general, you will understand the concept behind interrupts on any processor, but we will use the SJ-One board as an example.
+
*  The Wiki will remain here for general references about the SJ-One board, and to document student reports.
 +
*  The bookstack will now be used for SJSU assignments
  
=== What is an interrupt? ===
+
[http://books.socialledge.com/books/embedded-drivers-real-time-operating-systems/chapter/lesson-interrupts This article has been moved here]
*  Hardware capability to break normal software flow to attend an urgent request
 
*  "An event that needs immediate attention"
 
 
 
== Science ==
 
The science behind interrupts lies in the hardware that allows the CPU to be interrupted.  Each peripheral in a microcontroller ''may be'' able to assert an interrupt to the CPU core, and then the CPU core would jump to the corresponding interrupt service routine ('''ISR''') to service the interrupt.
 
 
 
=== ISR Procedure ===
 
The following steps demonstrate what happens when an interrupt occurs :
 
*  CPU manipulates the PC (program counter) to jump to the ISR
 
*  '''IMPORTANT''': CPU will disable interrupts (or that priority level's interrupts until end of ISR)
 
*  Registers are saved before running the ISR (pushed onto the stack)
 
*  ISR is run
 
*  Registers are restored (popped from stack)
 
*  Interrupts are re-enabled (or that priority level's interrupt is re-enabled)
 
 
 
On some processors, the savings and restoring of registers is a manual step and the compiler would help you do it.  You can google "GCC interrupt attribute" to study this topic further.  On SJ-One board, which uses LPC17xx (ARM Cortex M3), this step is automatically taken care of by the CPU hardware.
 
 
 
=== The SW to HW Connection ===
 
Now that we understand how the CPU hardware services interrupts, we need to define how we inform the CPU WHERE our ISR function is located at.  There is something called an '''Interrupt Vector Table'''.  This table is nothing but addresses of functions that correspond to the microcontroller interrupts.  Specific interrupts use specific "slots" in this table, and we have to populate these spots with our software functions that service the interrupts.
 
[[File:tutorial_interrupts_vec_table.png|center|frame|HW Interrupt Vector Table]]
 
 
 
=== SJ-One (LPC17xx) Example ===
 
Through some magic of the compiler, and the linker script, the compiler is able to place the software interrupt vector table at a specific location that the CPU expects the interrupt vector table to be located at. This connects the dots about how the CPU is able to determine WHERE your interrupt service routines are located at. From there on, anytime a specific interrupt occurs, the CPU is able to fetch the address and make the JUMP.
 
[[File:tutorial_interrupts_sw_vec_table.png|center|frame|SW Interrupt Vector Table]]
 
 
 
=== GCC Magic ===
 
In the SJ-One sample project, each ISR is named, but is labeled as a "weak" function.  What this means in the land of GCC (compiler) is that unless the same function name is defined somewhere else, the weak function serves as the ISR function.  If you do define the function name somewhere else, it will override the "weak" function and the compiler will place the address of your new function into the interrupt vector table.
 
 
 
== Setup ISR on SJ-One ==
 
The first step is to be able to locate the real-name of the "weak" ISR function.  For example, you can locate a UART0 ISR function that is weak, and we will override with our new function.  The only thing to keep in mind is that due to "C++ Mangling" of function names, if your ISR is located in a *.cpp file, you will need to enclose it into '''extern "C"''' tags.  See below for examples:
 
 
 
<syntaxhighlight lang="c">
 
/***************/
 
/* my_file.cpp */
 
 
 
    extern "C"
 
    {
 
        void UART0_IRQHandler()
 
        {
 
            /* Your ISR */
 
        }
 
    }
 
 
 
/*************/
 
/* my_file.c */
 
 
 
    /* extern tag not needed for a C file */
 
    void UART0_IRQHandler()
 
    {
 
        /* Your ISR */
 
    }
 
 
 
</syntaxhighlight>
 
 
 
<BR/>
 
=== What to do inside an ISR ===
 
Do very little inside an ISR.  When you are inside an ISR, the whole system is blocked (other than higher priority interrupts).  If you spend too much time inside the ISR, then you are destroying the realtime operating system principle and everything gets clogged because of you :(
 
 
 
With that said, here is the general guideline
 
*  Spend as little time as possible. DO NOT POLL FOR ANYTHING.
 
*  If you are using FreeRTOS API, you must use '''FromISR''' functions only!
 
*  Most important: Clear the source of the interrupt
 
*:  For example, if interrupt was for rising edge of a pin, clear the "rising edge" bit such that you will not re-enter into the same interrupt function.
 
 
 
=== ISR processing inside a FreeRTOS Task ===
 
It is a popular scheme to have an ISR quickly exit, and then resume a task or thread to process the event.  For example, if we wanted to write a file upon a button press, we don't want to do that inside an ISR because it would take too long and block the system.  What we can do instead is have the ISR "give" a semaphore, and a task to block upon a semaphore.
 
 
 
What you may argue with the example below is that we do not process the ISR immediately, and therefore delay the processing.  But you can tackle this scenario by resuming a HIGHEST priority task.  Immediately, after the ISR exits, due to the ISR "yield", FreeRTOS will resume the high priority task immediately rather than servicing another task.
 
 
 
<syntaxhighlight lang="c">
 
/* Declare and create the semaphore in main() */
 
xSemaphoreHandle gButtonPressSemaphore = NULL;
 
 
 
void my_button_press_isr(void)
 
{
 
    long yield = 0;
 
    xSemaphoreGiveFromISR(gButtonPressSemaphore, &yield);
 
    portYIELD_FROM_ISR(yield);
 
}
 
 
 
void button_press_task(void *p)
 
{
 
    while(1) {
 
        if (xSemaphoreTake(gButtonPressSemaphore, portMAX_DELAY)) {
 
            /* Process the interrupt */
 
        }
 
    }
 
}
 
</syntaxhighlight>
 
 
 
<BR/>
 
== Assignment ==
 
Write the implementation of <B><CODE>eint.c</CODE></B> from scratch.  Before you get any ideas, and to make sure you are not biased, just erase this file and begin with an empty mind.
 
 
 
#  From the <B><CODE>startup.cpp</CODE></B> file, identify the name of the function for EINT3
 
#*  This interrupt function is used for Port0 and Port2 interrupts
 
#*  When either EINT3, Port0, or Port2 interrupt occurs, this function will execute
 
#  Allow the user to specify the pin number and a callback function.
 
#*  When the interrupt occurs, make the callback for the user function
 
#  Test your implementation
 
#*  Attach a couple of switches on Port2, and ensure that the callbacks are made correctly.
 
#*  If SW1 is pressed, it should go to callback1
 
#*  If SW2 is pressed, it should go to callback2
 
#  Hints:
 
#*  LPC_GPIOINT structure can configure rising or falling edges for Port0 and Port2
 

Latest revision as of 16:38, 1 March 2018

Socialledge is moving to two portals.

  • The Wiki will remain here for general references about the SJ-One board, and to document student reports.
  • The bookstack will now be used for SJSU assignments

This article has been moved here