BSP430  20141115
Board Support Package for MSP430 microcontrollers
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
Callback Infrastructure

Table of Contents

A key requirement for BSP430 is the ability to have independent software modules that require access to timer, port, or other peripheral interrupts without requiring that the source code for the interrupt service routine be modified to add or remove support for these modules. This is accomplished using a callback infrastructure.

The declarations associated with the callback infrastructure are included in the <bsp430/periph.h> header.

Application Callback Functions

Two types of callback function are declared:

static int
handle_rx_ni (const sBSP430halISRVoidChainNode * cb,
void * context)
{
hBSP430halSERIAL * hal = (hBSP430halSERIAL *)context;
(void)cb;
/* do something with hal->rx_byte */
return 0;
}
volatile int last_button;
static int
button_isr_ni (const struct sBSP430halISRIndexedChainNode * cb,
void * context,
int idx)
{
(void)cb;
(void)context;
last_button = idx;
return 0
}

When registered, the callback function is invoked from within the interrupt handler upon receipt of the corresponding event.

Callback Chain Nodes

A HAL structure supporting interrupts will include pointers to structures that define a chain of callbacks to be invoked in particular events. There is one structure for each callback style, those being sBSP430halISRVoidChainNode and sBSP430halISRIndexedChainNode. These structures contain only two fields: sBSP430halISRVoidChainNode.next_ni is a pointer to the next callback in the chain, while sBSP430halISRVoidChainNode.callback_ni is the pointer to the function that will service the callback.

The HAL structure tags holding the start of the callback chain, as well as the sBSP430halISRVoidChainNode.next_ni tags within chain nodes, must only be mutated while interrupts are disabled. The steps required to add or remove a valid from the chain are straightforward and can be explicitly coded if only one callback will ever be used. Nonetheless, in the general case you should assume other resources may be linked into the callback and that the node you are removing may no longer be the head of the chain. Utility macros as exemplified below support these operations:

static sBSP430halISRVoidChainNode cb_node = { .callback_ni = handle_rx_ni };
/* ... */
BSP430_HAL_ISR_CALLBACK_LINK_NI(sBSP430halISRVoidChainNode, hal->rx_cb_chain_ni, cb_node, next_ni);
/* ... */
BSP430_HAL_ISR_CALLBACK_UNLINK_NI(sBSP430halISRVoidChainNode, hal->rx_cb_chain_ni, cb_node, next_ni);
Note
In many cases, you will never have cause to unlink a callback from a chain. However, if you do, understand that the interrupt handler walks this chain, and any attempt to modify the chain from within a callback that is part of the chain may result in wild pointers.

Interrupt Callback Return Values

The callback chain infrastructure supports an arbitrary number of notifications resulting from a single event. In many cases, it would be improper to invoke chain elements past the point where the event was completely handled. In addition, it is necessary to inform the interrupt handler top half when a low power mode should be exited. This information is conveyed to the interrupt through the return value of the callback handler, using a set of flags formed by combining bit values defined in <bsp430/periph.h>. The current set of callback flags is:

Name Purpose
BSP430_HAL_ISR_CALLBACK_EXIT_LPM Interrupt handler wakes from LPM by clearing the BSP430_CORE_LPM_EXIT_MASK bits in the status register stored on interrupt entry
BSP430_HAL_ISR_CALLBACK_BREAK_CHAIN Interrupt handler does not invoke any subsequent callbacks in the chain
BSP430_HAL_ISR_CALLBACK_YIELD Interrupt handler should inform an RTOS that a context switch is required
BSP430_HAL_ISR_CALLBACK_DISABLE_INTERRUPT Interrupt handler should disable the peripheral-specific interrupt

The return value from a handler which receives an input character is likely to be:

BSP430_HAL_ISR_CALLBACK_BREAK_CHAIN indicates that the handler has consumed the input; it would be inappropriate to continue to pass it on to subsequent handlers. BSP430_HAL_ISR_CALLBACK_EXIT_LPM is added to cause the interrupt to exit any low power mode it might be in, so that normal processing can deal with the event.

The return value from a handler which transmits an output character and for which there are no additional output characters queued would add BSP430_HAL_ISR_CALLBACK_DISABLE_INTERRUPT, to inhibit the transmission interrupt from recurring. See vBSP430serialWakeupTransmit_rh.

Providing Extended Information to the Callback

In some cases it is helpful for the callback function to be provided additional information, for example state that is exchanged between the interrupt routine and control flow outside the interrupt. Rather than provide another parameter to the callback handler, such state can be stored in a location at a fixed offset from the callback chain node that is already passed.

The following code from the console utility demonstrates how to provide a buffer for incoming characters that is specific to the callback structure. The same technique can be used for other state.

typedef struct sConsoleRxBuffer {
unsigned char head;
unsigned char tail;
} sConsoleRxBuffer;
static int
console_rx_isr_ni (const struct sBSP430halISRVoidChainNode * cb,
void * context)
{
sConsoleRxBuffer * bufp = (sConsoleRxBuffer *)cb;
sBSP430halSERIAL * hal = (sBSP430halSERIAL *)context;
bufp->buffer[bufp->head] = hal->rx_byte;
bufp->head = (bufp->head + 1) & ((sizeof(bufp->buffer) / sizeof(*bufp->buffer)) - 1);
if (bufp->head == bufp->tail) {
bufp->tail = (bufp->tail + 1) & ((sizeof(bufp->buffer) / sizeof(*bufp->buffer)) - 1);
}
}
static sConsoleRxBuffer rx_buffer_ = {
.cb_node = { .callback_ni = console_rx_isr_ni },
};

Copyright 2012-2014, Peter A. Bigot