📄 ex05_isr.htm
字号:
<html><head><title>EXAMPLE 5: INTERRUPT SERVICE ROUTINES</title><link rel="stylesheet" type="text/css" href="style.css"></head><body><a href="./ex04_fifo.htm">[previous]</a><a href="./tutorial.htm#index">[index]</a><a href="./ex06_shm.htm">[next]</a><h1>Example 5: Interrupt Service Routines</h1><p>This example demonstrates how to attach a function to an interrupt, sothat it is called whenever the interrupt is triggered. These functionsare commonly known as "interrupt service routines" (ISRs), and are used tohandle interrupts from hardware such as timer chips, printers, disk drives,Ethernet controllers, etc.Refer to the <ahref="../ex05_isr/isr_task.c">commented source code</a> of the examplefor the details.<h2>Principle of Operation</h2>In this example the interrupt source is the PC parallel port.Extensive documentation on programming and using the PCparallel port for data acquisition and control can be <ahref="http://www.google.com/search?q=PC+parallel+port">found on theWeb</a>. Briefly,<ul><li>The parallel port consists of three bytes in the PC I/O addressspace, starting at address 0x378.<ul><li>The first byte at 0x378 is for 8 bits of output<li>The second byte at 0x379 is for 5 bits of input, 3 bits unused<li>The third byte at 0x37A is for 4 bits of output, 4 bits of setup</ul><li>The parallel port generates interrupts on the x86's interrupt request (IRQ)7 when pin 10 is shorted to ground (any of pins 19-25).<li>The parallelport control registers can be programmed to generate an interrupt whenthis happens, and to inhibit interrupts when one is beingserviced.<li>In this example we don't write outputs or read inputs, althoughpresumably you would in a real application. Here we just programthe parallel port's control register to generate an interrupt, and setup RT Linux to invokeour ISR when an interrupt is generated.<li>The ISR increments a cumulative count of the number of interruptsreceived, and writes this to a FIFO.<li>A Linux application reads the FIFO and prints out any new countthat arrives.</ul><h2>Setting up the Parallel Port</h2>Setting up a hardware device and writing an ISR depends almostentirely on the device. RT Linux gives the facilities to connect aparticular interrupt to a particular ISR, but that's it.<ul><li>The parallelport is perhaps the simplest of all devices to set up for interrupts:simply set bit 4 (of 0 through 7) of the control register to enableinterrupts, and clear it to disable them.<li>To set a bit at a particular I/O address, first read in the byte,then set the bit using the bitwise-or operation '|', then write the byte back out. That way the otherbits are not affected (there is no x86instruction for individual bit-setting). In our case,<pre>byte = inb(0x37A);byte = byte | 0x10; /* hex 10 = binary 00010000 */outb(byte, 0x37A);</pre>Now, shorting pin 10 to ground will generate an interrupt.<li>To clear the bit and disable interrupts, follow a similarprocedure using the bitwise-and operation '&':<pre>byte = inb(0x37A);byte = byte & 0xEF; /* hex EF = binary 11101111 */outb(byte, 0x37A);</pre>Now, shorting pin 10 to ground will have no effect.</ul><h2>Defining the ISR</h2><ul><li>An ISR is simply a function that takes no arguments and returns novalue, and mediates the interaction of a hardware device with thereal-time application (it may be the entire real-time application).<li>This simple ISR just increments the cumulative count of interruptsserviced so far, and write this integer to the fifo.<li>The interruptsource is not periodic, since they are generated by a person touchingtwo wires together, and they can be quite bursty as the wires arebrought close together.<li>We need to disable the physical source ofinterrupts while we are manipulating the fifo, otherwise the fifocode will be re-entered and cause problems. Here's the whole ISR:<pre>static void isr_code(void){ static int interrupts = 0; /* cumulative count of interrupts */ outb(inb(0x37A) & 0xEF, 0x37A); /* disable interrupt */ interrupts++; rtf_put(0, &interrupts, sizeof(interrupts)); outb(inb(0x37A) | 0x10, 0x37A); /* enable interrupt */}</pre></ul><h2>Associating the ISR to the Interrupt</h2><ul><li>Associating the ISR to the interrupt is simple, using a few RTAIfunctions. In our case, we would do this in 'init_module()':<pre>rt_free_global_irq(7); /* disconnect any other ISR */rt_request_global_irq(7, isr_code); /* connect ours */rt_startup_irq(7); /* tell RTAI to honor it */outb(inb(0x37A) | 0x10, 0x37A); /* tell the hardware to issue it */</pre><li>In 'cleanup_module()', we should disconnect the ISR:<pre>outb(inb(0x37A) & 0xEF, 0x37A); /* disable interrupt */rt_shutdown_irq(7); /* tell RTAI not to honor it */rt_free_global_irq(7); /* disconnect the ISR */</pre></ul><h2>A Note on Re-Entrancy</h2><ul><li>Suppose an interrupt happens when we are in the ISR. The ISR itselfwould be interrupted, and called again.<li>This is "re-entrancy," similarto the programming concept of "recursion," in which a function callsitself.<li>This is often useful, but care needs to be taken in order tocode re-entrant functions. The only safe area to write is the"stack," the arguments and variables that appear at the top of yourfunction, without any 'static' qualifiers that would make thempersist. A new stack is created for each invocation of an ISR.<li>In particular, avoid writing to resources that can't be writtenatomically, for instance<ul><li>global data structures<li>hardware registers<li>other non-re-entrant functions</ul><li>There are two problems that make our ISR non-re-entrant: thepersistent 'interrupts' cumulative count, and the 'rtf_put()' call. <ul><li>Incrementing 'interrupts' means reading its value, adding 1 to it,and writing it back. This sequence could be split, and we'd lose acount (perhaps not critical).<li>We can surmise that 'rtf_put()' manipulates some global datastructure associated with the FIFO. If the sequence of writing dataand updating any counts or pointers is interrupted, the FIFO will becorrupted.</ul><li>The solution is to disable interrupts as soon as the ISRruns. Hardware typically expects this to be done and will throttleback accordingly.<li>Suppose the instructions to disable interrupts is interrupted? <ul><li>In our case, these instructions are "idempotent," meaning doingthem twice or redoing them in the middle gives the same net effect asdoing them once.<li>While disabling an interrupt is typically an idempotent sequence,it may not be possible on some hardware. In this case, mutualexclusion techniques such as semaphores can be used.</ul></ul><h2>Running the Demo</h2>To run the demo, change to the 'ex05_isr' subdirectory of thetop-level tutorial directory, and run the 'run' script by typing<pre>./run</pre>Alternatively, change to the top-level tutorial directory and run the'runall' script there by typing<pre>./runall</pre>and selecting the "Interrupt Service Routine" button.<p>You'll have 10 seconds to short pin 10 to any of pins 19 through25. During this time, you'll see messages like <nobr>"cumulativeinterrupts: 123"</nobr> printing out as you short the pins. Note thatmany interrupts are generated each time you short the pins, due to thenoisy nature of the wires touching together.<p><a href="../ex05_isr/isr_task.c">See the Code</a><hr><a href="./ex06_shm.htm">Next: Example 6, Shared Memory Communication</a><p><a href="./ex04_fifo.htm">Back: Example 4, FIFOs</a></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -