Spraa85e Programming
Spraa85e Programming
ABSTRACT
This application report explores hardware abstraction layer implementations to make programming of
peripherals easy using C/C++ on TMS320x28xx and TMS320x28xxx devices. The methods of using bit
field structure header files and the C2000™ Peripheral Driver Library are compared to each other and to
the traditional #define macro approach. Topics of code efficiency and special case registers are also
addressed.
Contents
1 Introduction ................................................................................................................... 2
2 Traditional #define Approach ............................................................................................... 3
3 Bit Field and Register-File Structure Approach .......................................................................... 5
4 Bit Field and Register-File Structure Advantages ...................................................................... 12
5 Code Size and Performance Using Bit Fields .......................................................................... 13
6 Read-Modify-Write Considerations When Using Bit Fields ........................................................... 17
7 Special Case Peripherals ................................................................................................. 22
8 C2000 Peripheral Driver Library Approach ............................................................................. 25
9 Code Size and Performance Using Driverlib ........................................................................... 29
10 Comparing and Combining Approaches ................................................................................ 30
11 References .................................................................................................................. 33
List of Figures
1 SCI SCICCR Register ....................................................................................................... 9
2 SCI SCICTL1 Register ...................................................................................................... 9
3 Code Composer Studio v5.1 Autocomplete Feature .................................................................. 12
4 Code Composer Studio v5.1 Expression Window ..................................................................... 13
5 Peripheral Clock Control 0 Register (PCLKCR0) ..................................................................... 13
6 CPU Timer Bit-Field (Left) and Driverlib (Right) Disassembly Comparison ........................................ 31
7 ADC Bit-Field (Left) and Driverlib (Right) Disassembly Comparison ................................................ 32
List of Tables
1 SCI-A, SCI-B Configuration and Control Registers ..................................................................... 3
2 SCI-A and SCI-B Common Register File ................................................................................. 6
3 CPU-Pipeline Activity For Read-Modify-Write Instructions in ........................................................ 15
4 Read-Modify-Write Sensitive Registers ................................................................................. 22
5 Byte Peripherals ............................................................................................................ 24
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 1
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Introduction www.ti.com
Trademarks
C2000, Piccolo, Delfino, Code Composer Studio are trademarks of Texas Instruments.
All other trademarks are the property of their respective owners.
1 Introduction
The TMS320x28xx and TMS320x28xxx are members of the C2000 family of microcontrollers (MCUs).
These devices are targeted for embedded control applications. To facilitate writing efficient and easy-to-
maintain embedded C/C++ code on these devices, Texas Instruments provides hardware abstraction layer
methods for accessing memory-mapped peripheral registers. These methods are the bit field and register-
file structure approach, and the C2000 Peripheral Driver Library approach. This application report explains
the implementation of these hardware abstraction layers and compares them to traditional #define macros.
Topics of code efficiency and special case registers are also addressed.
The bit field and register-file structure hardware abstraction layer discussed in this application report has
been implemented as a collection of C/C++ header files available for download in C2000Ware™ from
Texas Instruments:
Support for all new microcontrollers is available in the device support section of C2000Ware. At this time,
it supports and is the preferred approach for the following devices:
• Piccolo™ Series Microcontrollers
• Delfino™ Series Microcontrollers
• F28M3x Series Microcontrollers (C28x Subsystem)
Older C28x devices are not supported by C2000Ware and are instead supported in the following
downloads:
• C281x C/C++ Header Files and Peripheral Examples
• C280x, C2801x C/C++ Header Files and Peripheral Examples
• C2804x C/C++ Header Files and Peripheral Examples
The C2000 Peripheral Driver Library (often referred to as “Driverlib”) is also available for download in
C2000Ware. At this time, it supports the following devices:
• F2807x
• F28004x
• F2837xS
• F2837xD
Depending on your current needs, the software included in these downloads are learning tools or the
basis for a development platform.
• Learning Tool:
The C/C++ Header Files and Peripheral Examples include several example Code Composer Studio™
projects. These examples explain the steps required to initialize the device and utilize the on-chip
peripherals. The examples can be copied and modified to quickly experiment with peripheral
configurations.
• Development Platform:
The header files can be incorporated into a project as a hardware abstraction layer for accessing the
on-chip peripherals using C or C++ code. You can also pick and choose functions as needed and
discard the rest. This application report does not provide a tutorial on C, C++, C28x assembly, or
emulation tools. You should have a basic understanding of C code and the ability to load and run code
using Code Composer Studio. While knowledge of C28x assembly is not required to understand the
hardware abstraction layer, it is useful to understand the code optimization and read-modify-write
sections. If you have assembly instruction-related questions, see the TMS320C28x CPU and
Instruction Set Reference Guide.
Examples are based on the following software versions:
• C281x C/C++ Header Files and Peripheral Examples V1.00
• C280x, C2801x C/C++ Header Files and Peripheral Examples V1.41
2 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Traditional #define Approach
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 3
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Traditional #define Approach www.ti.com
(1) These registers are described in theTMS320x281x Serial Communications Interface (SCI) Reference Guide (SPRU051).
(2) Actual addresses may differ from device to device. See the device’s Technical Reference Manual for details.
(3) These registers are reserved on devices without the SCI-B peripheral, and some devices may have more than two instances of
the SCI peripheral. For more details, see the device-specific data manual.
A developer can implement #define macros for the SCI peripherals by adding definitions like those in
Example 1 to an application header file. These macros provide an address label, or a pointer, to each
register location. Even if a peripheral is an identical copy a macro is defined for every register. For
example, every register in SCI-A and SCI-B is specified separately.
/********************************************************************
* Traditional header file
********************************************************************/
Each macro definition can then be used as a pointer to the register's location as shown in Example 2.
/********************************************************************
* Source file using #define macros
********************************************************************/
...
*SCICTL1A = 0x0003; //write entire register
*SCICTL1B |= 0x0001; //enable RX
...
4 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Bit Field and Register-File Structure Approach
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 5
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Bit Field and Register-File Structure Approach www.ti.com
The code in Example 3 groups the SCI registers together as members of a C/C++ structure. The register
in the lowest memory location is listed first in the structure and the register in the highest memory location
is listed last. Reserved memory locations are held with variables that are not used except as space
holders, for example, rsvd1, rsvd2, rsvd3, and so forth. The register's size is indicated by its type: Uint16
for 16-bit (unsigned int) and Uint32 for 32-bit (unsigned long). The SCI peripheral registers are all 16-bits
so only Uint16 has been used.
/********************************************************************
* SCI header file
* Defines a register file structure for the SCI peripheral
********************************************************************/
struct SCI_REGS {
union SCICCR_REG SCICCR; // Communications control register
union SCICTL1_REG SCICTL1; // Control register 1
Uint16 SCIHBAUD; // Baud rate (high) register
Uint16 SCILBAUD; // Baud rate (low) register
union SCICTL2_REG SCICTL2; // Control register 2
union SCIRXST_REG SCIRXST; // Receive status register
Uint16 SCIRXEMU; // Receive emulation buffer register
union SCIRXBUF_REG SCIRXBUF; // Receive data buffer
Uint16 rsvd1; // reserved
Uint16 SCITXBUF; // Transmit data buffer
union SCIFFTX_REG SCIFFTX; // FIFO transmit register
union SCIFFRX_REG SCIFFRX; // FIFO receive register
union SCIFFCT_REG SCIFFCT; // FIFO control register
Uint16 rsvd2; // reserved
Uint16 rsvd3; // reserved
union SCIPRI_REG SCIPRI; // FIFO Priority control
};
6 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Bit Field and Register-File Structure Approach
The structure definition in Example 3 creates a new type called struct SCI_REGS. The definition alone
does not create any variables. Example 4 shows how variables of type struct SCI_REGS are created in a
way similar to built-in types such as int or unsigned int. Multiple instances of the same peripheral use the
same type definition. If there are two SCI peripherals on a device, then two variables are created:
SciaRegs and ScibRegs.
/********************************************************************
* Source file using register-file structures
* Create a variable for each of the SCI register files
********************************************************************/
The volatile keyword is very important in Example 4. A variable is declared as volatile whenever its value
can be changed by something outside the control of the code in which it appears. For example, peripheral
registers can be changed by the hardware itself or within an interrupt. If volatile is not specified, then it is
assumed the variable can only be modified by the code in which it appears and the compiler may optimize
out what is seen as an unnecessary access. The compiler will not, however, optimize out any volatile
variable access; this is true even if the compiler's optimizer is enabled.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 7
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Bit Field and Register-File Structure Approach www.ti.com
The DATA_SECTION pragma allocates space for the symbol in the section called section name. In
Example 5, the DATA_SECTION pragma is used to assign the variable SciaRegs and ScibRegs to data
sections named SciaRegsFile and ScibRegsFile. The data sections are then directly mapped to the same
memory block occupied by the respective SCI registers.
/********************************************************************
* Assign variables to data sections using the #pragma compiler statement
* C and C++ use different forms of the #pragma statement
* When compiling a C++ program, the compiler will define __cplusplus automatically
********************************************************************/
//----------------------------------------
#ifdef __cplusplus
#pragma DATA_SECTION("SciaRegsFile")
#else
#pragma DATA_SECTION(SciaRegs,"SciaRegsFile");
#endif
volatile struct SCI_REGS SciaRegs;
//----------------------------------------
#ifdef __cplusplus
#pragma DATA_SECTION("ScibRegsFile")
#else
#pragma DATA_SECTION(ScibRegs,"ScibRegsFile");
#endif
volatile struct SCI_REGS ScibRegs;
This data section assignment is repeated for each peripheral. The linker command file is then modified to
map each data section directly to the memory space where the registers are mapped. For example,
Table 1 indicates that the SCI-A registers are memory mapped starting at address 0x7050. Using the
assigned data section, the variable SciaRegs is allocated to a memory block starting at address 0x7050.
The memory allocation is defined in the linker command file (.cmd) as shown in Example 6. For more
information on using the C28x linker and linker command files, see the TMS320C28x Assembly Language
Tools User's Guide (SPRU513).
/********************************************************************
* Memory linker .cmd file
* Assign the SCI register-file structures to the corresponding memory
********************************************************************/
MEMORY
{
...
PAGE 1:
SCIA : origin = 0x007050, length = 0x000010 /* SCI-A registers */
SCIB : origin = 0x007750, length = 0x000010 /* SCI-B registers */
...
}
SECTIONS
{
...
SciaRegsFile : > SCIA, PAGE = 1
ScibRegsFile : > SCIB, PAGE = 1
...
}
8 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Bit Field and Register-File Structure Approach
By mapping the register-file structure variable directly to the memory address of the peripheral's registers,
you can access the registers directly in C/C++ code by simply modifying the required member of the
structure. Each member of a structure can be used just like a normal variable, but its name will be a bit
longer. For example, to write to the SCI-A Control Register (SCICCR), access the SCICCR member of
SciaRegs as shown in Example 7. Here the dot is an operator in C that selects a member from a
structure.
/********************************************************************
* User's source file
********************************************************************/
...
SciaRegs.SCICCR = SCICCRA_MASK;
ScibRegs.SCICCR = SCICCRB_MASK;
...
7 6 5 4 3 2 0
STOPBITS EVEN/ODD PRIORITY LOOPBACK ADDR/IDLE SCICHAR
PARITY ENABLE ENA Mode
R-0 R/W-0 R/W-0 R-0 R/W-0 R/W-0
LEGEND: R/W = Read/Write; R = Read only; -n = value after reset
7 6 5 4 3 2 1 0
Reserved RXERRINTENA SWRESET Reserved TXWAKE SLEEP TXENA RXENA
R-0 R/W-0 R/W-0 R-0 R/W-0 R/W-0 R/W-0 R/W-0
LEGEND: R/W = Read/Write; R = Read only; -n = value after reset
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 9
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Bit Field and Register-File Structure Approach www.ti.com
/********************************************************************
* SCI header file
********************************************************************/
//----------------------------------------------------------
// SCICCR communication control register bit definitions:
//
struct SCICCR_BITS { // bit description
Uint16 SCICHAR:3; // 2:0 Character length control
Uint16 ADDRIDLE_MODE:1; // 3 ADDR/IDLE Mode control
Uint16 LOOPBKENA:1; // 4 Loop Back enable
Uint16 PARITYENA:1; // 5 Parity enable
Uint16 PARITY:1; // 6 Even or Odd Parity
Uint16 STOPBITS:1; // 7 Number of Stop Bits
Uint16 rsvd1:8; // 15:8 reserved
};
//-------------------------------------------
// SCICTL1 control register 1 bit definitions:
//
struct SCICTL1_BITS { // bit description
Uint16 RXENA:1; // 0 SCI receiver enable
Uint16 TXENA:1; // 1 SCI transmitter enable
Uint16 SLEEP:1; // 2 SCI sleep
Uint16 TXWAKE:1; // 3 Transmitter wakeup method
Uint16 rsvd:1; // 4 reserved
Uint16 SWRESET:1; // 5 Software reset
Uint16 RXERRINTENA:1; // 6 Receive interrupt enable
Uint16 rsvd1:9; // 15:7 reserved
};
Example 9. Union Definition to Provide Access to Bit Fields and the Whole Register
/********************************************************************
* SCI header file
********************************************************************/
union SCICCR_REG {
Uint16 all;
struct SCICCR_BITS bit;
};
union SCICTL1_REG {
Uint16 all;
struct SCICTL1_BITS bit;
};
Once bit-field and union definitions are established for specific registers, the SCI register-file structure is
rewritten in terms of the union definitions as shown in Example 10. Note that not all registers have bit field
definitions; some registers, such as SCITXBUF, will always be accessed as a whole and a bit field
definition is not necessary.
10 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Bit Field and Register-File Structure Approach
/********************************************************************
* SCI header file
********************************************************************/
//---------------------------------------------------------------------------
// SCI Register File:
//
struct SCI_REGS {
union SCICCR_REG SCICCR; // Communications control register
union SCICTL1_REG SCICTL1; // Control register 1
Uint16 SCIHBAUD; // Baud rate (high) register
Uint16 SCILBAUD; // Baud rate (low) register
union SCICTL2_REG SCICTL2; // Control register 2
union SCIRXST_REG SCIRXST; // Receive status register
Uint16 SCIRXEMU; // Receive emulation buffer register
union SCIRXBUF_REG SCIRXBUF; // Receive data buffer
Uint16 rsvd1; // reserved
Uint16 SCITXBUF; // Transmit data buffer
union SCIFFTX_REG SCIFFTX; // FIFO transmit register
union SCIFFRX_REG SCIFFRX; // FIFO receive register
union SCIFFCT_REG SCIFFCT; // FIFO control register
Uint16 rsvd2; // reserved
Uint16 rsvd3; // reserved
union SCIPRI_REG SCIPRI; // FIFO Priority control
};
As with other structures, each member (.all or .bit) is accessed using the dot operator in C/C++ as shown
in Example 11. When the .all member is specified, the entire register is accessed. When the .bit member
is specified, then the defined bit fields can be directly accessed.
NOTE: Writing to a bit field has the appearance of writing to only the specified field. In reality,
however, the CPU performs what is called a read-modify-write operation; the entire register
is read, its contents are modified and the entire value is written back. Possible side effects of
read-modify-write instructions are discussed in Section 6.
/********************************************************************
* User's source file
********************************************************************/
// Access registers without a bit field definition (.all, .bit not used)
SciaRegs.SCIHBAUD = 0;
SciaRegs.SCILBAUD = 1;
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 11
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Bit Field and Register-File Structure Advantages www.ti.com
12 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Code Size and Performance Using Bit Fields
7 6 5 4 3 2 1 0
SPIDENCLK SPICENCLK Reserved I2CAENCLK ADCENCLK TBCLKSYNC Reserved
R/W-0 R/W-0 R-0 R/W-0 R/W-0 R/W-0 R-0
LEGEND: R/W = Read/Write; R = Read only; -n = value after reset
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 13
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Code Size and Performance Using Bit Fields www.ti.com
The code in Example 13 enables the peripheral clocks on a TMS320x2801 device. The C28x compiler
generates one assembly code instruction for each C-code register access. This is very efficient; there is a
one-to-one correlation between the C instructions and the assembly instructions. The only overhead is the
initial instruction to set the data page pointer (DP).
NOTE: EALLOW and EDIS are macros defined in the C/C++ Header Files and Peripheral
Examples. These macros expand to the EALLOW and EDIS assembly instructions.
The EALLOW protection mechanism prevents spurious CPU writes to several registers.
Executing EALLOW permits the CPU to write freely to protected registers and executing
EDIS protects them once more. For information on EALLOW protection and a list of
protected registers, see the device-specific System Control and Interrupts Reference Guide
or Technical Reference Manual (TRM).
14 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Code Size and Performance Using Bit Fields
To calculate how many cycles the code in Example 13 will take, you need to know how many wait states
are required to access the PCLKCR0 register. Wait state information for all memory blocks and peripheral
frames is listed in the device specific data manual. The PCLKCR0 register is in peripheral frame 2; this
frame requires two wait states for a read access and no wait states for a write access. This means a read
from PCLKCR0 takes three cycles total and a write takes one cycle. In addition, a new access to
PCLKCR0 cannot begin until the previous write is complete. This built-in protection mechanism removes
pipeline effects and makes sure operations proceed in the correct order; all of the peripheral registers
have this protection. In Example 13, each access to the PCLKCR0 register will take six cycles; the
pipeline phases are shown in Table 3.
AND @28,#0xFFFC 2
AND @28,#0xFFFC 3
AND @28,#0xFFFC 4
AND @28,#0xFFFC 5
AND @28,#0xFFFC 6
AND @28,#0xFFFB 7
AND @28,#0xFFFB 8
AND @28,#0xFFFB 9
AND @28,#0xFFFB 10
AND @28,#0xFFFB 11
AND @28,#0xFFFB 12
OR @28,#0x0008 13
OR @28,#0x0008 14
OR @28,#0x0008 15
OR @28,#0x0008 16
OR @28,#0x0008 17
OR @28,#0x0008 18
OR @28,#0x0010
etc...
(1)
For detailed CPU pipeline information, see theTMS320C28x CPU and Instruction Set Reference Guide (SPRU430).
When code size and cycle counts must be kept to a minimum, it is beneficial to reduce the number of
instructions required to initialize a register to as few as possible. Here are some options for reducing code
size:
• Enable the compiler's optimizer:
As mentioned in Section 3.1, register-file variables are declared as volatile. For this reason, enabling
the optimizer alone will not reduce the number of instructions. The keyword volatile alerts the compiler
that the variable's value can change outside of the currently executing code. While removing the
volatile keyword would reduce code size, it is not recommended. Removing volatile must be done with
great care and only where the developer is certain doing so will not yield incorrect results.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 15
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Code Size and Performance Using Bit Fields www.ti.com
16 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Read-Modify-Write Considerations When Using Bit Fields
With a full CPU pipeline, a C28x based device can complete one read-modify-write operation to zero wait-
state SARAM every cycle. When accessing the peripheral registers or external memory, however,
required wait states must be taken into account. In addition, the pipeline protection mechanism can further
stall instructions in the CPU pipeline. This is described in more detail in Section 5 and in the TMS320C28x
CPU and Instruction Set Reference Guide (SPRU430).
Read-modify-write instructions usually have no ill side effects. It is important, however, to realize that read-
modify-write instructions do not limit access to only specific bits in the register; these instructions write to
all of the register's bits. In some cases, the read-modify-write sequence can cause unexpected results
when bits are written to with the value originally read. Registers that are sensitive to read-modify-write
instructions fall into three categories:
• Registers with bits that hardware can change after the read, but before the write
• Registers with write 1-to-clear bits
• Registers that have bits that must be written with a value different from what the bits read back
Registers that fall into these three categories are typically found within older peripherals. To keep register
compatibility, the register files have not been redesigned to avoid this issue. Newer peripherals, such as
the ePWM, eCAP, and eQEP, however, have a register layout specifically designed to avoid these
problems.
This section describes in detail the three categories in which read-modify-write operations should be used
with care. In addition, an example of each type of register is given along with a suggested method for
safely modifying that register. At the end of the section a list of read-modify-write sensitive registers is
provided for reference.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 17
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Read-Modify-Write Considerations When Using Bit Fields www.ti.com
NOTE: This rule does not apply to the CPU's IFR register. Special instructions are provided to clear
CPU IFR bits and will not result in missing interrupts. Use the OR IFR instruction to set IFR
bits, and use the AND IFR instruction to clear pending interrupts.
/********************************************************************
* User's source file
********************************************************************/
// Pseudo ISR
// Services the interrupt & the hardware clears the PIEIFR flag
// Re-maps the interrupt to the proper ISR
interrupt void PseudoISR(void)
{
EALLOW;
PieVectTable.XINT1 = TempISR;
EDIS;
}
18 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Read-Modify-Write Considerations When Using Bit Fields
/********************************************************************
* User's source file
********************************************************************/
for(;;)
{
// Make LED Green
GpioDataRegs.GPADAT.bit.GPIO16 = 1; // (1) RED_LED_OFF;
// Read-modify-write occurs
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 19
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Read-Modify-Write Considerations When Using Bit Fields www.ti.com
/********************************************************************
* User's source file
********************************************************************/
for(;;)
{
// Make LED Green
GpioDataRegs.GPASET.bit.GPIO16 = 1; // RED_LED_OFF;
GpioDataRegs.GPACLEAR.bit.GPIO17 = 1; // GREEN_LED_ON;
delay_loop();
Example 20. Read-Modify-Write Operation Inadvertently Modifies Write 1-to-Clear Bits (TCR[TIF])
The test for TIF in Example 20 will never be true even if an interrupt has been flagged. The OR assembly
instruction to set the TSS bit performs a read-modify-write operation on the TCR register. If the TIF bit is
set when the read-modify-write operation occurs, then TIF will be read as a 1 and also written back as a 1.
The TIF bit will always be cleared as a result of this write. To avoid this, the write to TIF bit always be 0.
The TIF bit ignores writes of 0, thus, its value will be preserved. One possible implementation that
preserves TIF is shown in Example 21.
20 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Read-Modify-Write Considerations When Using Bit Fields
The content of the TCR register is copied into a shadow register. Within the shadow register the TSS bit is
set, and the TIF bit is cleared. The shadow register is then written back to TCR; the timer is stopped and
the state of TIF is preserved. The assembly instructions were generated with optimization level -o2
enabled.
/********************************************************************
* User's source file
********************************************************************/
SysCtrlRegs.WDCR = 0x0068;
See the TMS320x280x, 2801x, 2804x DSP System Control and Interrupts Reference Guide (SPRU712)
and TMS320x281x System Control and Interrupts Reference Guide (SPRU078) for more information on
the watchdog module.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 21
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Read-Modify-Write Considerations When Using Bit Fields www.ti.com
22 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com Special Case Peripherals
To force 32-bit accesses, the bit-field definitions and read-modify-write operations must not be used. The
register must be read and written using the .all member of the union definition and all 32-bits must be read
or written.
Unfortunately, not using bit fields or read-modify-write operations reduces the code readability. One
solution is to read the entire register into a shadow register, manipulate the value, and then write the new
32-bit value to the register using .all. The code in Example 24 uses a shadow register to force a 32-bit
access. If more then one register is going to be accessed, then the whole eCAN register file can be
shadowed (i.e., struct ECAN_REGS shadowECanaRegs;).
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 23
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Special Case Peripherals www.ti.com
Since the peripheral registers behave in a byte-addressable way, the addresses of the 32-bit memory-
mapped registers are placed at address offset increments of 4 (as in 4 8-bit bytes) instead of 2 as they
normally would be on a word-addressable peripheral. 16-bit words are offset at increments of 2 instead of
1. Often this can lead to issues with the compiler.
For example, Example 25 shows code that writes to the CAN_IF1CMD register on the F2837xD CAN-A
module using bit-field header files defined in the usual manner. The CAN_IF1CMD is located at address
0x048100, but the code below is accessing 0x0480D4 since the code generation tools do not comprehend
that the peripheral bridge treats addresses as byte addresses. Also note that the access to TXRQST,
which is in the upper word of the register, should be at an offset of +2 that of CAN_IF1CMD.
Fortunately, features have been added to the compiler in version 16.6.0.STS to properly handle these
alignment differences. The header files for byte peripherals in C2000Ware use a “byte_peripheral” type
attribute to generate the correct code. For more details about the attribute, see the TMS320C28x
Optimizing C/C++ Compiler User's Guide. Example 26 shows the corrected code generated with the
“byte_peripheral” type attribute.
24 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com C2000 Peripheral Driver Library Approach
/////////////////////////////////////////////////////////////////////
//
// Snippet from hw_memmap.h showing base address #defines
//
/////////////////////////////////////////////////////////////////////
...
#define SCIA_BASE 0x00007050U // SCI A Registers
#define SCIB_BASE 0x00007750U // SCI B Registers
...
/////////////////////////////////////////////////////////////////////
//
// Snippet from sci.h showing API description and base parameter
//
/////////////////////////////////////////////////////////////////////
//
//! Sets the FIFO interrupt level at which interrupts are generated.
//!
//! \param base is the base address of the SCI port.
//!
//! \param txLevel is the transmit FIFO interrupt level, specified as
//! one of the following:
//! SCI_FIFO_TX0, SCI_FIFO_TX1, SCI_FIFO_TX2, ... or SCI_FIFO_TX16.
//!
//! \param rxLevel is the receive FIFO interrupt level, specified as one
//! of the following:
//! SCI_FIFO_RX0, SCI_FIFO_RX1, SCI_FIFO_RX2, ... or SCI_FIFO_RX16.
//!
//! This function sets the FIFO level at which transmit and receive
//! interrupts are generated.
//!
//! \return None.
//
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 25
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
C2000 Peripheral Driver Library Approach www.ti.com
For the other parameters, #defines or enumerated types are often supplied to provide a readable way to specify
the desired value. Typically #defines are used when a parameter is a uint32_t or uint16_t and able to take a
bitwise OR of several #defined values. Enumerated types are used when only a single value is applicable.
These values determine what is written to the peripheral registers to configure the peripheral. The function will
determine which register or registers to write to and what value to write. The function will also perform any
necessary EALLOW or EDIS instructions. Since these details are hidden by the functions, it is not required for
the user to have complete knowledge of the hardware to program a peripheral. Example 28 shows code that
could be found in a user application that demonstrates this; given a source clock rate and a desired baud rate,
the function calculates the necessary prescalers and writes them to the appropriate registers.
/////////////////////////////////////////////////////////////////////
//
// User’s source file
//
/////////////////////////////////////////////////////////////////////
//
// Configure SCI-A with a baud rate of 9600, 8-bit data, one stop bit,
// and no parity
//
SCI_setConfig(SCIA_BASE, 25000000, 9600, (SCI_CONFIG_WLEN_8 |
SCI_CONFIG_STOP_ONE |
SCI_CONFIG_PAR_NONE));
//
// Set the FIFO interrupt level to 8 characters for both FIFOs
//
SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX8, SCI_FIFO_RX8)
//
// While the transmit FIFO is not full, write 0x00
//
while(SCI_getTxFIFOStatus(SCIA_BASE) != SCI_FIFO_TX16)
{
SCI_writeCharNonBlocking(SCIA_BASE, 0x00);
}
Since API names are primarily made up of full English words and a very limited set of acronyms, the actions of
much of the code can be easily understood, even with limited commenting. This is not always the case with code
that writes directly to registers which typically have short names made up of acronyms or abbreviations.
26 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com C2000 Peripheral Driver Library Approach
/////////////////////////////////////////////////////////////////////
//
// Example register #defines from hw_sci.h
//
/////////////////////////////////////////////////////////////////////
//*******************************************************************
//
// The following are defines for the SCI register offsets
//
//*******************************************************************
#define SCI_O_CCR 0x0U // Communications control
#define SCI_O_CTL1 0x1U // Control register 1
#define SCI_O_HBAUD 0x2U // Baud rate (high)
#define SCI_O_LBAUD 0x3U // Baud rate (low)
#define SCI_O_CTL2 0x4U // Control register 2
#define SCI_O_RXST 0x5U // Receive status
#define SCI_O_RXEMU 0x6U // Receive emulation buffer
#define SCI_O_RXBUF 0x7U // Receive data buffer
#define SCI_O_TXBUF 0x9U // Transmit data buffer
#define SCI_O_FFTX 0xAU // FIFO transmit register
#define SCI_O_FFRX 0xBU // FIFO receive register
#define SCI_O_FFCT 0xCU // FIFO control register
#define SCI_O_PRI 0xFU // SCI Priority control
//*******************************************************************
//
// The following are defines for the bit fields in the SCICCR register
//
//*******************************************************************
#define SCI_CCR_SCICHAR_S 0U
#define SCI_CCR_SCICHAR_M 0x7U // Character length control
#define SCI_CCR_ADDRIDLE_MODE 0x8U // ADDR/IDLE Mode control
#define SCI_CCR_LOOPBKENA 0x10U // Loop Back enable
#define SCI_CCR_PARITYENA 0x20U // Parity enable
#define SCI_CCR_PARITY 0x40U // Even or Odd Parity
#define SCI_CCR_STOPBITS 0x80U // Number of Stop Bits
...
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 27
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
C2000 Peripheral Driver Library Approach www.ti.com
These #defines are used in combination with a set of “HWREG(x)” macros defined in hw_types.h where x is the
address of the of the memory location to be accessed
• HWREG(x) is used for 32-bit accesses, such as reading a value from a 32-bit counter register.
• HWREGH(x) is used for 16-bit accesses. This can be used to access a 16-bit register or the upper or lower
words of a 32-bit register. This is usually the most efficient macro to use.
• HWREGB(x) is used for 8-bit accesses using the __byte() intrinsic. For more information, see the
TMS320C28x Optimizing C/C++ Compiler User's Guide. It typically should only be used when an 8-bit access
is required by the hardware. Otherwise, use HWREGH() and mask and shift out the unwanted bits.
• HWREG_BP(x) is another macro used for 32-bit accesses, but it uses the __byte_peripheral_32() compiler
intrinsic. It is meant to work with the byte peripherals described in Section 7. It tells the compiler that the 32-
bit access may not be split into two 16-bit read-modify-write operations since the upper word is not at the
expected address offset on a byte peripheral.
These macros used in combination with the register description and base address #defines make up the majority
of Driverlib code. Example 30 shows how they are used to implement the SCI_setConfig() function.
/////////////////////////////////////////////////////////////////////
//
// Example function implementation from Driverlib sci.c
//
/////////////////////////////////////////////////////////////////////
//*******************************************************************
//
// SCI_setConfig
//
//*******************************************************************
void SCI_setConfig(uint32_t base, uint32_t lspclkHz, uint32_t baud,
uint32_t config)
{
...
//
// Compute the baud rate divider.
//
divider = ((lspclkHz / (baud * 8U)) - 1U);
//
// Set the baud rate.
//
HWREGH(base + SCI_O_HBAUD) = (divider & 0xFF00U) >> 8U;
HWREGH(base + SCI_O_LBAUD) = divider & 0x00FFU;
//
// Set parity, data length, and number of stop bits.
//
HWREGH(base + SCI_O_CCR) = ((HWREGH(base + SCI_O_CCR) &
~(SCI_CCR_SCICHAR_M |
SCI_CCR_PARITYENA |
SCI_CCR_PARITY |
SCI_CCR_STOPBITS)) | config);
...
}
28 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com C2000 Peripheral Driver Library Approach
In addition to removing the overhead of the function call, inlining Driverlib functions can allow the compiler
to evaluate some of code at compile time, resulting in smaller, faster code. This is especially true when
constants are passed as parameters to the functions.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 29
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Code Size and Performance Using Driverlib www.ti.com
Example 32 shows the implementation of the ADC_setupSOC() Driverlib function. The function calculates
the address to which it needs to write based on the base and socNumber parameters. All other
parameters need to be shifted, adjusted, and combined before they can be written to the register.
Example 32 shows the assembly that is generated when the function is inlined and passed constants.
Note that all calculations have been performed at compile time and all that remains to be done is the
access protection and register write.
/////////////////////////////////////////////////////////////////////
//
// Example function implementation from Driverlib adc.h
//
/////////////////////////////////////////////////////////////////////
...
ADC_setupSOC(ADCA_BASE, EALLOW
ADC_SOC_NUMBER0, MOVB AL, #0xf
ADC_TRIGGER_EPWM1_SOCA, MOVB AH, #0x50
ADC_CH_ADCIN0, 16); MOVL XAR4, #0x007410
MOVL *+XAR4[0], ACC
EDIS
Example 34. CPU Timer Bit-Field (Left) and Driverlib (Right) Disassembly Comparison
Figure 6. CPU Timer Bit-Field (Left) and Driverlib (Right) Disassembly Comparison
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 31
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Comparing and Combining Approaches www.ti.com
In Example 34 each single line of bit-field code corresponded to a Driverlib function. This is not always the
case; Example 32 shows the ADC_setupSOC() function which configures multiple fields within the
ADCSOC0CTL register at once. Another item of note in Example 35 is that the EALLOW and EDIS
instructions are used in all of the Driverlib functions to disable and re-enable write protection on the
necessary registers. The compiler is able to optimize out the back-to-back EDIS-EALLOW pairs that this
results in when the functions are inlined.
Example 35. ADC Bit-Field (Left) and Driverlib (Right) Disassembly Comparison
EALLOW
MOVW DP, #0x1d0 EALLOW
AND @0x10, #0x7fff MOVB AL, #0xf
AND @0x11, #0xfff8 MOVB AH, #0x50
AND AL, @0x10, #0xfe00 MOVL XAR4, #0x007410
ORB AL, #0xf MOVL *+XAR4[0], ACC
MOV @0x10, AL MOV @AL, *(0:0x7407)
AND AL, @0x11, #0xfe0f AND @AL, #0xfff0
ORB AL, #0x50 MOV *(0:0x7407), @AL
MOV @0x11, AL MOV @AL, *(0:0x7407)
AND @0x7, #0xfff0 ORB AL, #0x20
OR @0x7, #0x0020 MOV *(0:0x7407), @AL
OR @0x4, #0x0001 EDIS
EDIS
If using both approaches in one application, here are some considerations on when you may choose one
over the other:
• A less detailed understanding of the hardware is required when using Driverlib which makes it a good
choice for quickly developing an application. Driverlib is the recommended approach for new
applications.
• When porting legacy code from an older C2000 device to a newer, you can continue using bit field and
register-file structures. Bit-field headers have been available for several generations of C2000 devices,
and for many peripherals, they have remained mostly compatible.
• Use the bit field and register-file structure approach particularly for performance critical code when
Driverlib does not meet requirements. In general, the bit-field approach will generate smaller, faster
code when repeated accesses are made to the same data page.
32 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ SPRAA85E – November 2005 – Revised December 2017
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
www.ti.com References
11 References
The following references include additional information on topics found in this application report:
• C281x C/C++ Header Files and Peripheral Examples
• C280x, C2801x C/C++ Header Files and Peripheral Examples
• C2804x C/C++ Header Files and Peripheral Examples
• TMS320C28x CPU and Instruction Set Reference Guide
• TMS320C28x Optimizing C/C++ Compiler User's Guide
• TMS320C28x Assembly Language Tools User's Guide
• TMS320x281x System Control and Interrupts Reference Guide
• TMS320x280x, 2801x, 2804x DSP System Control and Interrupts Reference Guide
• TMS320x281x Serial Communications Interface (SCI) Reference Guide
• C2000™ MISRA-C Policy
For peripheral guides specific to your device, see TMS320x28xx, 28xxx DSP Peripherals Reference Guide
Support for all new microcontrollers is available in the device support section of C2000Ware.
An Introduction to Texas Instruments C2000 Microcontrollers has been contributed to the TI Embedded
Processors Wiki located at: http://processors.wiki.ti.com/index.php/Category:C2000.
SPRAA85E – November 2005 – Revised December 2017 Programming TMS320x28xx and TMS320x28xxx Peripherals in C/C++ 33
Submit Documentation Feedback
Copyright © 2005–2017, Texas Instruments Incorporated
Revision History www.ti.com
Revision History
NOTE: Page numbers for previous revisions may differ from page numbers in the current version.
Texas Instruments Incorporated (‘TI”) technical, application or other design advice, services or information, including, but not limited to,
reference designs and materials relating to evaluation modules, (collectively, “TI Resources”) are intended to assist designers who are
developing applications that incorporate TI products; by downloading, accessing or using any particular TI Resource in any way, you
(individually or, if you are acting on behalf of a company, your company) agree to use it solely for this purpose and subject to the terms of
this Notice.
TI’s provision of TI Resources does not expand or otherwise alter TI’s applicable published warranties or warranty disclaimers for TI
products, and no additional obligations or liabilities arise from TI providing such TI Resources. TI reserves the right to make corrections,
enhancements, improvements and other changes to its TI Resources.
You understand and agree that you remain responsible for using your independent analysis, evaluation and judgment in designing your
applications and that you have full and exclusive responsibility to assure the safety of your applications and compliance of your applications
(and of all TI products used in or for your applications) with all applicable regulations, laws and other applicable requirements. You
represent that, with respect to your applications, you have all the necessary expertise to create and implement safeguards that (1)
anticipate dangerous consequences of failures, (2) monitor failures and their consequences, and (3) lessen the likelihood of failures that
might cause harm and take appropriate actions. You agree that prior to using or distributing any applications that include TI products, you
will thoroughly test such applications and the functionality of such TI products as used in such applications. TI has not conducted any
testing other than that specifically described in the published documentation for a particular TI Resource.
You are authorized to use, copy and modify any individual TI Resource only in connection with the development of applications that include
the TI product(s) identified in such TI Resource. NO OTHER LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE TO
ANY OTHER TI INTELLECTUAL PROPERTY RIGHT, AND NO LICENSE TO ANY TECHNOLOGY OR INTELLECTUAL PROPERTY
RIGHT OF TI OR ANY THIRD PARTY IS GRANTED HEREIN, including but not limited to any patent right, copyright, mask work right, or
other intellectual property right relating to any combination, machine, or process in which TI products or services are used. Information
regarding or referencing third-party products or services does not constitute a license to use such products or services, or a warranty or
endorsement thereof. Use of TI Resources may require a license from a third party under the patents or other intellectual property of the
third party, or a license from TI under the patents or other intellectual property of TI.
TI RESOURCES ARE PROVIDED “AS IS” AND WITH ALL FAULTS. TI DISCLAIMS ALL OTHER WARRANTIES OR
REPRESENTATIONS, EXPRESS OR IMPLIED, REGARDING TI RESOURCES OR USE THEREOF, INCLUDING BUT NOT LIMITED TO
ACCURACY OR COMPLETENESS, TITLE, ANY EPIDEMIC FAILURE WARRANTY AND ANY IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT OF ANY THIRD PARTY INTELLECTUAL
PROPERTY RIGHTS.
TI SHALL NOT BE LIABLE FOR AND SHALL NOT DEFEND OR INDEMNIFY YOU AGAINST ANY CLAIM, INCLUDING BUT NOT
LIMITED TO ANY INFRINGEMENT CLAIM THAT RELATES TO OR IS BASED ON ANY COMBINATION OF PRODUCTS EVEN IF
DESCRIBED IN TI RESOURCES OR OTHERWISE. IN NO EVENT SHALL TI BE LIABLE FOR ANY ACTUAL, DIRECT, SPECIAL,
COLLATERAL, INDIRECT, PUNITIVE, INCIDENTAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES IN CONNECTION WITH OR
ARISING OUT OF TI RESOURCES OR USE THEREOF, AND REGARDLESS OF WHETHER TI HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
You agree to fully indemnify TI and its representatives against any damages, costs, losses, and/or liabilities arising out of your non-
compliance with the terms and provisions of this Notice.
This Notice applies to TI Resources. Additional terms apply to the use and purchase of certain types of materials, TI products and services.
These include; without limitation, TI’s standard terms for semiconductor products http://www.ti.com/sc/docs/stdterms.htm), evaluation
modules, and samples (http://www.ti.com/sc/docs/sampterms.htm).
Mailing Address: Texas Instruments, Post Office Box 655303, Dallas, Texas 75265
Copyright © 2018, Texas Instruments Incorporated