CC26xx Driver Library
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
Error Handling

Invalid arguments and error conditions are handled in a non-traditional manner in the peripheral driver library. Typically, a function would check its arguments to make sure that they are valid (if required; some may be unconditionally valid such as a 32-bit value used as the load value for a 32-bit timer). If an invalid argument is provided, an error code would be returned. The caller then has to check the return code from each invocation of the function to make sure that it succeeded.

This method results in a significant amount of argument-checking code in each function and return-code-checking code at each call site. For a self-contained application, this extra code becomes an unneeded overhead once the application is debugged. Having a means of removing it allows the final code to be smaller and therefore run faster.

In the peripheral driver library, most functions do not return errors. Argument checking is done via a call to the ASSERT macro (provided in debug.h). This macro has the usual definition of an assert macro; it takes an expression that must be true. By making this macro be empty, the argument checking is removed from the code.

There are two definitions of the ASSERT macro provided in debug.h; one that is empty (used for normal situations) and one that evaluates the expression (used when the library is built with debugging). The debug version calls the __error__ function whenever the expression is not true, passing the file name and line number of the ASSERT macro invocation. The __error__ function is prototyped in debug.h and must be provided by the application because it is the application's responsibility to deal with error conditions.

Note
To enable the ASSERT macro make sure that the DRIVERLIB_DEBUG symbol is defined within your project and/or compiler setup.

By setting a breakpoint on the __error__ function, the debugger immediately stops whenever an error occurs anywhere in the application (something that would be very difficult to do with other error checking methods). When the debugger stops, the arguments to the __error__ function and the backtrace of the stack pinpoint the function that found an error, what it found to be a problem, and where it was called from. As an example:

void
UARTParityModeSet(uint32_t ui32Base, uint32_t ui32Parity)
{
//
// Check the arguments.
//
ASSERT(UARTBaseValid(ui32Base));
ASSERT((ui32Parity == UART_CONFIG_PAR_NONE) ||
(ui32Parity == UART_CONFIG_PAR_EVEN) ||
(ui32Parity == UART_CONFIG_PAR_ODD) ||
(ui32Parity == UART_CONFIG_PAR_ONE) ||
(ui32Parity == UART_CONFIG_PAR_ZERO));

Each argument is individually checked, so the line number of the failing ASSERT indicates the argument that is invalid. The debugger is able to display the values of the arguments (from the stack backtrace) as well as the caller of the function that had the argument error. This method allows the problem to be quickly identified at the cost of a small amount of code.