![]() |
![]() |
CC27xxDriverLibrary
|
The CC27xx Driver Library from Texas Instruments® (also referred to as "DriverLib") is a set of low-level drivers for accessing the registers found on the CC27xx family of ARM® Cortex™-M based devices. The DriverLib functions are grouped in APIs based on either specific on-chip peripheral module (e.g. SPI, UART, etc ) or more general functionality (e.g. system control, oscillator settings, etc). Each API uses a unique pre-fix for all the DriverLib functions in that API to indicate which module or type of functionality being accessed (with a few exceptions).
A few things to note about DriverLib:
For most applications the drivers can be used as is. But in some cases the drivers must be enhanced or rewritten to meet the functionality, memory, or processing requirements of the application. If so, the existing driver can be used as a reference on how to operate the peripheral.
A brief overview of the organization of the driver library source code follows:
driverlib/
inc/
Currently, DriverLib source code supports the following toolchains:
Besides the source code for the driver library, Texas Instruments also provides pre-compiled libraries of the driver library. These libraries are found in driverlib/lib/.
Currently, pre-compiled libraries are provided for the following toolchains:
DriverLib provides support for two programming models:
Each model can be used independently or combined, based on the needs of the application or the programming environment desired by the developer.
Each programming model has advantages and disadvantages. Use of the direct register access model generally results in smaller and more efficient code than using the software driver model. However, the direct register access model requires detailed knowledge of the operation of each register and bit field, as well as their interactions and any sequencing required for proper operation of the peripheral; the software driver model insulates the developer from these details, thus generally requiring less time to develop applications.
In the direct register access model, the peripherals are programmed by the application by writing values directly into the registers in the peripheral. A set of defines, that simplify this process, is provided.
These defines are located in the inc/
directory and there is a single hw_<module>.h
header file for each peripheral type. For example, the defines for UART are located in the hw_uart.h
header file.
The defines used by the direct register access model follow a naming convention that makes it easier to know how to use a particular macro. The rules are as follows:
UART
for the UART module) and are followed by the name of the register as it appears in the data sheet._O_
(for example, the UART CTL
register in the data sheet results in UART_O_CTL
). The base address of each peripheral is defined in the memory map header file, hw_memmap.h
, located in the inc/
directory.hw_uart.h
file for the UART registers).UARTEN
bit field in the CTL
register in the UART
module is identified by UART_CTL_UARTEN
._M
represent the mask for a bit field in a register._S
represent the number of bits to shift a value in order to align it with a bit field._W
represent the width of the bit field.UART_LCRH_WLEN
bit field has a set of enumerations that specify the UART word length. E.g. the enumeration "BITL7" can be set using the define UART_LCRH_WLEN_BITL7
. This improves readability and also helps the programmer select valid values for specific bit fields.A set of macros is provided in hw_types.h
to use together with the register defines to read and write the corresponding addresses:
Given these defines and macros, the CTL
register, in the first instance of the UART peripheral (UART0), can be programmed as follows:
Alternatively, the following has the same effect (although it is not as easy to understand):
The value of the BUSY
field from the UART_O_FR
register can be extracted as follows:
In the software driver model, the APIs provided in DriverLib are used by applications to control the peripherals. Because these drivers provide complete control of the peripherals in their normal mode of operation, it is possible to write an entire application without direct access to the registers. This method provides for rapid development of the application without requiring knowledge of how to program the peripheral registers.
Corresponding to the direct register access model example, the following call also programs the CTL
register in the UART module (though the register name is hidden by the API):
The direct register access model and software driver model can be used together in a single application, thus applying the most appropriate model as needed to any particular situation within the application. For example, the software driver model can be used to configure the peripherals (because this is not performance critical) and the direct register access model can be used to operate the peripheral (which may be more performance critical). Or, the software driver model can be used for peripherals that are not performance critical (such as a UART used for data logging) and the direct register access model can be used for performance critical peripherals.
A number of functions are available in a ROM-only version. These functions are called HAPI functions (Hard API) and are generally used by various tools. Source code is not available for the HAPI functions but the functions are available for users to call. The list of HAPI functions available can be found in hapi.h
.
Invalid arguments and error conditions are handled in a non-traditional manner in DriverLib. 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 and consume less power.
In this driver library, most functions do not return error status. 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 empty, the argument checking is completely removed from the code thus avoiding the error checking overhead.
There are two definitions of the ASSERT
macro provided in debug.h
; one that is empty (used for normal/release builds) and one that evaluates the expression (used when the library is built for 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.
To enable the ASSERT
macro define the symbol DRIVERLIB_DEBUG
within your project and/or compiler setup.
DRIVERLIB_DEBUG
for the entire project might result in a significant increase in code size depending on the number of modules used in the project. Defining DRIVERLIB_DEBUG
for specific files is possible if a project-wide define results in an unacceptable code size increase.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:
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 trace) 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.
DriverLib includes a special function called SetupTrimDevice() which must always be called right after the ROM boot sequence in order to apply trim settings and certain customer configurations (from CCFG) to the device.
The CC27xx DriverLib package includes a customer configuration file (ccfg.c) which contains settings that are being applied mainly by ROM boot sequence, SetupTrimDevice() during startup, and radio SW. The configuration settings are stored in a specially allocated address range in flash referred to as "CCFG area". The user must edit the CCFG to fit the needs of the specific design and application.
Typically, a user application does not need to read the settings in CCFG; however, a few settings might be interesting for a user to read. These can be read by accessing the ccfg
variable defined in ccfg.c using dot notation.
The CC27xx DriverLib package includes a security configuration file (scfg.c) which contains default settings for the device security configuration.
Typically, a user application does not need to read the settings in SCFG.
Higher-level software for CC27xx, for example FreeRTOS, uses DriverLib as the main interface to access the hardware registers thus minimizing the need for direct register accesses from the RTOS itself. The CC27xx Product SDK package provides its own set of RTOS drivers that call DriverLib functions.
When using e.g. FreeRTOS and the included RTOS drivers it is important that a user application does not "bypass" RTOS drivers by calling DriverLib functions directly to configure any hardware that is controlled by the RTOS. Doing so can cause a conflict that may result in unexpected behavior by the RTOS.
For devices enabled with Trusted Firmware-M (TF-M), DriverLib comes in two variants
The secure and non-secure versions of DriverLib are provided as separate libraries (driverlib.a
and driverlib_ns.a
respectively).
The secure/non-secure configuration of peripherals and bus initiators is controlled by the secure TF-M image provided by Texas Instruments, using the TrustZone Control Module (TCM). The TCM configuration is reflected in hw_memmap.h:
[MODULE]_BASE
defines exist for modules that are accessible from DriverLib's security context.[MODULE]_S_BASE
defines always map to the secure aperture.[MODULE]_NS_BASE
defines always map to the non-secure aperture.[MODULE]_BASE
defines to access modules, and not [MODULE]_S_BASE
nor [MODULE]_NS_BASE
.Secure applications, and applications not using TF-M, should use the secure DriverLib. This is done by linking in driverlib.a
.
Secure DriverLib has access to all DriverLib functions and hardware registers.
In hw_memmap.h, [MODULE]_BASE
are defined for all modules and mapped to the secure aperture (bit 28 is 0).
There are specific guidelines to follow when using the non-secure DriverLib. To utilize the non-secure DriverLib, the application must
DRIVERLIB_NS
macro in your project settings, anddriverlib_ns.a
libraryThis ensures that the non-secure versions of the DriverLib functions are used.
Functions within DriverLib are documented to indicate their security context. Functions that are only available in secure mode are explicitly marked as such in the documentation. This helps developers understand the security requirements and constraints associated with each function.
When the DRIVERLIB_NS
macro is defined, only the non-secure modules will have their [MODULE]_BASE
defines available in hw_memmap.h. This provides feedback at compile-time, ensuring that the application can only interact with the non-secure hardware. For example, if UART0 is configured as non-secure in TCM, UART0_BASE
is defined in hw_memmap.h to use the non-secure aperture (bit 28 i 1). If CKMD is configured as secure in TCM, then CKMD_BASE
is not defined in hw_memmap.h