section 5.4 companion and outline
This page contains companion resources and an outline for section 5.4 of the book An Introduction to Real-Time Computing for Mechanical Engineers, and it therefore lacks most of section 5.4’s contents. While some sections of the book are fully available on this site, many are not. Please consider purchasing a copy from the MIT Press.
Interrupts and their handling in real-time computing
Programming the target computer for DI interrupts
Lab 5 demonstrates the application of an external
digital interrupt. The main()
function will
simply print on the display the number of seconds that have elapsed
since the start of the program. Simultaneously, the ISR, running in a
separate thread, will respond to each asynchronous external signal by
printing the word “interrupt_
.” Properly
executed, the count will continue undisturbed, with the word “interrupt_
” interspersed randomly.
In preparation for the lab, this section describes the steps necessary to ready the target computer to respond to an interrupt from a digital input (DI). The myRIO handles the DI with its field-programmable gate array (FPGA). The FPGA must be configured to designate specific inputs as IRQ lines.
The myRIO C library header files DIIRQ.h
and IRQConfigure.h
provide constants, data types,
and utility functions for programming DI interrupts. The utility
functions are described here and the specific implementations are
described in the lab exercise.
Before the code of the ISR can respond to an interrupt, two
programming tasks must be performed. First, the digital interrupt must
be registered. That is, the specific DI must be identified and its
operational parameters configured. The second task is to create a new
thread in which the ISR will run when an interrupt occurs. In our lab
exercise, the main()
function,
executing in its own thread, will perform both of these tasks.
The two threads (main and interrupt) communicate through a globally defined thread resource structure declared in the main thread as follows:
typedef struct {
; // IRQ context reserved
NiFpga_IrqContext irqContext; // IRQ thread ready flag
NiFpga_Bool irqThreadRdyuint8_t irqNumber; // IRQ number value
} ThreadResource;
For example, main()
will use the
“IRQ thread ready flag” both to enable the ISR and to signal the
interrupt thread to terminate.
Task I.—Register the DI0 Interrupt Request
The myRIO C library provides a function to reserve the interrupt from the FPGA and configure the DI and the IRQ. Its prototype is
int32_t Irq_RegisterDiIrq(MyRio_IrqDi *irqChannel,
*irqContext,
NiFpga_IrqContext uint8_t irqNumber,
uint32_t count,
); Irq_Dio_Type type
Its return value is 0
for success, and
its five input arguments are
irqChannel
: A pointer to a structure containing the registers and settings for the IRQ I/O to modify; defined in DIIRQ.h astypedef struct{ uint32_t dioCount; // count register uint32_t dioIrqNumber; // number register uint32_t dioIrqEnable; // enable register uint32_t dioIrqRisingEdge; // rising edge-trig register uint32_t dioIrqFallingEdge; // falling edge-trig register ; // physical DIO channel number Irq_Channel dioChannel} MyRio_IrqDi;
This structure identifies the digital channel and how it is to be configured.
irqContext
: A pointer to a context variable identifying the interrupt to be reserved. It is the first component of the previously given thread resources structure.irqNumber
: The IRQ number (1–8) distinguishes this from other interrupts.count
: The number of times that an interrupt condition is met to trigger the interrupt.type
: The trigger type that is used to increment the count.
Task II.—Create the interrupt thread
The ISR will run in a pthread (subsection 5.3.1).
The function pthread_create()
,
called from main()
, creates a
new thread and configures it to service the DI interrupt. Its prototype
is
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
where the four input arguments are
thread
: A pointer to a thread identifier.attr
: A pointer to thread attributes. UseNULL
to apply the default attributes.start_routine
: The name of the function to start in the new thread.arg
: The thread resource.
The fourth argument is a pointer
to
the same thread resource structure defined above and used in the second
argument of Irq_RegisterDiIrq()
.
Just prior to creating the thread, main()
will set the
IRQ thread ready flag to enable the ISR to process interrupts.
Once the interrupt thread is created and enabled, the main thread (in
our case, running the function main()
) can carry
out its tasks while the interrupt thread runs independently.
Terminating the ISR thread and unregistering the interrupt
When the main thread has completed its tasks, it can signal the interrupt thread to terminate by resetting the interrupt ready flag:
.irqThreadRdy = NiFpga_False; irqThread0
Then the main thread should join the ISR thread to wait for its completion:
(irqThread0, NULL); pthread_join
Finally, the interrupt must be unregistered:
= Irq_UnregisterDiIrq(&irqDI0,
status .irqContext,
irqThread0.irqNumber); irqThread0
All that remains is to write the ISR.
Writing a digital input interrupt service routine
The function started in the interrupt thread created by the pthread_create()
is
generically called start_routine()
, but
it should be given a specific name for the digital interrupt ISR, such
as DI_ISR()
.
Its role is to respond to the interrupt. This thread will execute until
signaled to stop by main()
.
The ISR corresponds to the third argument of pthread_create()
, so
it must conform with that prototype. For instance, a valid ISR function
is
void* DI_ISR(void *thread_resource) {
// service the interrupt
}
The argument of the starting routine, void* thread_resource
,
should be cast to a pointer to the ThreadResource
defined in the main thread.
Remember that it is by this means that the two threads communicate.
Assuming that more than one interrupt is to be processed, the ISR
should enter a loop, waiting for each instance of the registered IRQ to
occur. That loop should terminate when signaled by the interrupt ready
flag. The Irq_Wait()
function
supplied by the myRIO C library can be invoked to pause the ISR thread
until it is activated by its IRQ. The prototype is
void Irq_Wait(NiFpga_IrqContext irqContext,
,
NiFpga_Irq irqNumberuint32_t *irqAssert,
*continueWaiting); NiFpga_Bool
The first, second, and fourth arguments correspond to the members of
the ThreadResource irqThread0
created
in the main thread. For example, it might be programmed as1
(thread_resource->irqContext,
Irq_Wait->irqNumber,
thread_resource&irqAssert,
(NiFpga_Bool*) &(thread_resource->irqThreadRdy));
The variable of the third argument, the integer irqAssert
, indicates which interrupt has
occurred. If an IRQ is asserted during the Irq_Wait()
, its bit
will be assigned to &irqAssert
. The
irqNumber
indicates the position in
irqAssert
of the bit corresponding to
the specific interrupt. This allows us to test for an assertion and
acknowledge it with a bit mask. For example,
if (irqAssert & (1 << thread_resource->irqNumber)) {
// service the interrupt here
(irqAssert); // acknowledge the IRQ(s)
Irq_Acknowledge}
The code necessary to respond to the IRQ interrupt should be placed
inside the if
-statement
here.
Once the loop has terminated, we can close the ISR pthread and return
a pointer, which can be NULL
, as
follows:
(NULL);
pthread_exitreturn NULL;
In lab 5, we will implement an ISR to process an indefinite number of DI interrupts.
Online resources for Section 5.4
No online resources.
The
->
or “arrow” operator in the expressions->m
is used to access the value of the memberm
of a structure pointers
. See Kernighan1988.↩︎