section 5.4 companion and outline
This page contains companion resources and an outline for section 5.4 of the book RTC for MEs, 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 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 {
NiFpga_IrqContext irqContext; // IRQ context reserved
NiFpga_Bool irqThreadRdy; // IRQ thread ready flag
uint8_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,
NiFpga_IrqContext *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 Irq_Channel dioChannel; // physical DIO channel number } 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. UseNULLto 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:
irqThread0.irqThreadRdy = NiFpga_False;Then the main thread should join the ISR thread to wait for its completion:
pthread_join(irqThread0, NULL);Finally, the interrupt must be unregistered:
status = Irq_UnregisterDiIrq(&irqDI0,
irqThread0.irqContext,
irqThread0.irqNumber);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 irqNumber,
uint32_t *irqAssert,
NiFpga_Bool *continueWaiting); 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
Irq_Wait(thread_resource->irqContext,
thread_resource->irqNumber,
&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
Irq_Acknowledge(irqAssert); // acknowledge the IRQ(s)
} 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:
pthread_exit(NULL);
return 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->mis used to access the value of the membermof a structure pointers. See Kernighan1988.↩︎