📄 real-time experiment #9 interrupt service routines.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0069)http://www.rt.db.erau.edu/experiments/vx/interrupts/Experiment-9.html -->
<HTML><HEAD><TITLE>Real-Time Experiment #9: Interrupt Service Routines</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META content="MSHTML 6.00.2900.2873" name=GENERATOR></HEAD>
<BODY>
<CENTER>
<H1>Embry-Riddle Real-Time Laboratory Experiment<BR>Experiment #9<BR>Interrupt
Service Routines </H1></CENTER>
<HR SIZE=3>
<H2>Introduction</H2>Interrupt handling is important in real-time operating
systems. The system becomes aware of external events via the interrupt mechanism
and the response of a real-time systems depends on the speed of the system's
response to interrupts and the speed of processing interrupt handlers. To
achieve the best response possible the application writer must be aware of how
to take advantage of the utilities provided by VxWorks.
<P></P>
<HR SIZE=3>
<H2>Objectives</H2>The following are the primary objectives of this experiment:
<UL>
<LI>To demonstrate VxWorks' implementation of interrupt service routines.
</LI></UL>
<P></P>
<HR SIZE=3>
<H2>Description</H2>The user may write an interrupt service routine (ISR) and
attach it to a particular interrupt using the intConnect routine provided by
VxWorks. What basically happens when an interrupt to the system occurs, is at
the first non-critical code after the interrupt occured, guaranteed to be within
musec by WRS, the ISR is executed. This time span is generally knonw as
interrupt latency. Because many interrupts may occur within a short time of each
other and a higher interrupt will block lower priority interrupts, it is
necessary to keep the ISR processing to a minimum. This is the responsibility of
the application writer.
<P></P>The header files which relate to VxWorks interrupt management are,
intLib.h - this is the interrupt library header file; and ARCH/mc68k/ivMc68k.h.
The ISR code does not run in the normal task context. It has no task control
block and all ISR's share a single stack. Because of these differences there are
restrictions to the type of routines that can be used in the ISR.
<P></P>ISR's should not invoke functions which may cause <B>``blocking''</B> of
the caller. For example, semTake. malloc and free cannot be used because they
call functions which may cause blocking and thus all creation and deletion
functions are forbidden since they use malloc and free. An ISR must not perform
I/O through the VxWorks I/O system. A call to a device driver may block the
system if the caller needs to wait for the device. However, the VxWorks pipe
driver has been designed to permit writes by interrupt service code.
<P></P>The best way to print out messages from an ISR is to use the function
logMsg or other functions provided by the library logLib. ISRs should not use
floating point instructions since these registers are not saved on entry to the
ISR. If floating point instructions are to be used the registers must be saved
using the functions in fppALib. However, floating point operations are time
intensive and should be avoided in ISRs.
<P></P>Ideally, an ISR only contains a semGive system call. That is to say, the
function of a ISR is to cause the execution of a task to perform whatever
processing is necessary. To improve cooperation between VxWorks' ISRs and tasks,
the best mechanism is semaphores.
<P></P><B>1. Example: </B>
<P></P>In the example below, the <B>interruptGenerator</B> task generates a hard
interrupt, <B>sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL)</B>, which is caught
by <B>interruptCatcher</B>.
<P></P>The synatx for sysBusIntGen is: <PRE>SYNOPSIS
STATUS sysBusIntGen
(
int intLevel, /* bus interrupt level to generate */
int vector /* interrupt vector to generate (0-255) */
)
RETURNS
OK, or ERROR if intLevel is out of range or the board cannot generate
a bus interrupt.
</PRE><B>interruptCatcher</B> is able to handle this hardware interrupt by
installing an interrupt handler, <B>interruptHandler</B>.
<P></P><B>interruptCatcher</B> is "attaches" to the hardware interrupt using
<B>intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i)</B>.
The <B>INUM_TO_IVEC(INTERRUPT_LEVEL)</B> is a macro that converts a hardware
interrupt number to a vector.
<P></P>The synatx for sysBusIntGen is: <PRE>
SYNOPSIS
STATUS intConnect
(
VOIDFUNCPTR * vector, /* interrupt vector to attach to */
VOIDFUNCPTR routine, /* routine to be called */
int parameter /* parameter to be passed to routine */
)
DESCRIPTION
This routine connects a specified C routine to a specified interrupt vector. The
address of routine is stored at vector so that routine is called with parameter when the
interrupt occurs. The routine is invoked in supervisor mode at interrupt level. A proper
C environment is established, the necessary registers saved, and the stack set up.
The routine can be any normal C code, except that it must not invoke certain
operating system functions that may block or perform I/O operations.
This routine simply calls intHandlerCreate( ) and intVecSet( ). The address of the
handler returned by intHandlerCreate( ) is what actually goes in the interrupt vector.
RETURNS
OK, or ERROR if the interrupt handler cannot be built.
</PRE>The run time scenario consists of <B>interruptCatcher</B> running and
simulating normal processing until <B>interruptGenerator</B> generates a
hardware interrupt. Upon the generation of the interrupt,
<B>interruptCatcher</B> suspends its normal processing and branches to
<B>interruptHandler</B>. Once the interrupt handling code has been executed,
control is passed back to <B>interruptCatcher</B>. This activity is repeated
multiple times.
<P></P><PRE>------------------------------------------------------------------------------------
/* includes */
#include "vxWorks.h"
#include "intLib.h"
#include "taskLib.h"
#include "arch/mc68k/ivMc68k.h"
#include "logLib.h"
/* function prototypes */
void interruptHandler(int);
void interruptCatcher(void);
/* globals */
#define INTERRUPT_NUM 2
#define INTERRUPT_LEVEL 65
#define ITER1 40
#define LONG_TIME 1000000
#define PRIORITY 100
#define ONE_SECOND 100
void interruptGenerator(void) /* task to generate the SIGINT signal */
{
int i, j, taskId, priority;
STATUS taskAlive;
if((taskId = taskSpawn("interruptCatcher",PRIORITY,0x100,20000,(FUNCPTR)interruptCatcher,0,0,0,0,0,0,0,
0,0,0)) == ERROR)
logMsg("taskSpawn interruptCatcher failed\n",0,0,0,0,0,0);
for (i=0; i < ITER1; i++)
{
taskDelay(ONE_SECOND);/* suspend interruptGenerator for one second */
/* check to see if interruptCatcher task is alive! */
if ((taskAlive = taskIdVerify(taskId)) == OK)
{
logMsg("++++++++++++++++++++++++++Interrupt generated\n",0,0,0,0,0,0);
/* generate hardware interrupt 2 */
if((sysBusIntGen(INTERRUPT_NUM,INTERRUPT_LEVEL)) == ERROR)
logMsg("Interrupt not generated\n",0,0,0,0,0,0);
}
else /* interruptCatcher is dead */
break;
}
logMsg("\n***************interruptGenerator Exited***************\n\n\n\n",0,0,0,0,0,0);
}
void interruptCatcher(void) /* task to handle the interrupt */
{
int i, j;
STATUS connected;
/* connect the interrupt vector, INTERRUPT_LEVEL, to a specific interrupt
handler routine ,interruptHandler, and pass an argument, i */
if((connected = intConnect(INUM_TO_IVEC(INTERRUPT_LEVEL),(VOIDFUNCPTR)interruptHandler,i)) == ERROR)
logMsg("intConnect failed\n",0,0,0,0,0,0);
for (i=0; i < ITER1; i++)
{
for (j=0; j < LONG_TIME; j++);
logMsg("Normal processing in interruptCatcher\n",0,0,0,0,0,0);
}
logMsg("\n+++++++++++++++interruptCatcher Exited+++++++++++++++\n",0,0,0,0,0,0);
}
void interruptHandler(int arg) /* signal handler code */
{
int i;
logMsg("-------------------------------interrupt caught\n",0,0,0,0,0,0);
for (i=0; i < 5; i++)
logMsg("interrupt processing\n",0,0,0,0,0,0);
}
------------------------------------------------------------------------------------
</PRE>
<P></P>
<HR SIZE=3>
<H2>Procedures</H2>1. Copy the source code in the example and compile it.
<P></P>2. Load the object file onto the target machine.
<P></P>3. Execute the following command on the WindSh terminal: "logFdSet 1".
This will direct the <EM>logMsg()</EM> output to the virtual console.
<P></P>4. Run the examples by executing the main routine("interruptGenerator")
of the example on WindSh terminal.
<P></P>Note: Make sure you have redirected I/O, otherwise you won't see the
results of the <EM>logMsg()</EM> commands.
<HR SIZE=3>
<H2>Follow On Experiment</H2>Experiment 1. Can the <EM>printf()</EM> statement
be used instead of the <EM>logMsg()</EM>? Explain why?
<P></P>Experiment 2. Modify the example program so that it can handle hardware
interrupts 1,2, and 3. Generate these interrupts in ascending order in
<B>interruptGenerator</B>. <B>interruptCatcher</B> will use three separate
handlers for each hardware interrupt respectively.
<P></P>
<HR SIZE=3>
<H2>Additional Information</H2><B>What interrupt vectors are ok to use</B>
<P></P>The values 0 - 63 (decimal) are pre-defined by Motorola. Don't attach a
handler to these values, though they are worth looking at to familiarize oneself
with the default handlers. For 68K the file /VX_HSP_BASE/h/iv.h (which includes
/VX_HSP_BASE/h/arch/mc68k/ivMc68k.h)
<P></P>The vectors from 64 - 255 are available for use. Some are already used by
the BSP code to connect to hardware dependent interrupts. In those cases we
generally use the vectors recommended by the hardware manufacturer. The question
arises; how to determine which vectors are available for customers applications
to use. One method to determine this would be to manually check all the usages
of intConnect(). VX_HSP_BASE/src/usr/usr*.c, VX_BSP_BASE/config/all/*, and
VX_BSP_BASE/config/BSP/* are the locations to check. Admittantly, using the
above methods to determine the used vectors seems unfriendly and tedious.
<P></P>An alternative method is to use a C program to determine what vectors
have been used. This can be done because VxWorks assigns a default handler to
all "unassigned" vectors. ("Unitialized Vector...") We can scroll through the
vectors and check for that handler, if it the stub does not exist, the vector is
used. Here is some example code that will work, there is a much more elaborate
& portable version on the vxWorks users group software archive that will
perform this task (and more). There is also intVecGet().
<P></P><PRE>/* vectors.c - show which mc68k user interrupt vectors are being used */
#include "vxWorks.h"
#include "intLib.h"
#include "stdio.h"
#include "iv.h"
extern excIntStub; /* Default handler. */
void vectorShow (void)
{
int ix;
int vecBase = (int) intVecBaseGet();
for (ix = (vecBase + 64); ix < (vecBase + 256); ix++)
{
if ( *(int *) (UINT)(INUM_TO_IVEC(ix)) != (int) &excIntStub)
{
printf ("User defined interrupt vector %d has been used.\n", ix);
}
}
}
</PRE>
<P></P>Refer to VxWorks User's Manual and Reference Manual.
<P></P>
<HR SIZE=3>
<CENTER>
<H4><A
href="http://www.rt.db.erau.edu/experiments/vx/toc/TableOfContents.html">Return
to Primary Table of Contents </A></H4></CENTER>
<HR SIZE=3>
<CENTER>Last Updated: 11 April 1997<BR><EM>Created by: Dan Eyassu</EM><BR><A
href="mailto:eyassud@db.erau.edu">eyassud@db.erau.edu</A><BR></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -