How to Develop DSP Embedded Systems Using C Language

Currently, many embedded systems are built around DSP cores. However, developing DSP systems using assembly language presents challenges such as high development difficulty, long development cycles, and poor maintainability. Using C language to develop DSP systems is a pressing requirement for many embedded developers. While there is a considerable amount of reference material on C language development for microcontrollers, resources for C language development for DSP systems are quite rare. This article uses TI’s DSP device TMS320F24X series as an example to illustrate how to develop a complete DSP embedded system using C language.

Keywords: Embedded Systems; DSP Systems; C Language Development; TMS320F24X Series

Introduction

When developing embedded products, most people initially think of writing monitoring programs in the assembly language of the controller. The main reasons are: 1) Programs generated by assembly language correspond to less binary code, making execution faster than programs generated by high-level languages; 2) When the controller was first introduced, there were no corresponding high-level languages available; 3) Issues related to memory prices and addressing space limitations. The aforementioned problems have been largely resolved, so I will not elaborate on them here. The reality is that in the field of microcontroller applications, developers have begun to use C language for development. They find that developing embedded products with high-level languages is much easier, and the binary code compiled from C language programs is also very concise. Currently, the most widely used digital signal processor (DSP) is TI’s TMS320 family, with the TMS320F2XX series being the most commonly used in industrial control. TI provides both assembly language and C language options for each DSP chip for developers. I have been using C language for product development, and I have rarely seen introductions in this area, so I specifically wrote this article to recommend using C language to develop DSP embedded systems, taking TMS320F240 as an example.

1. The Uniqueness of C Language for DSP When using C language for the 51 series, you may have noticed a significant characteristic: the C language for controllers differs from the C language used on PCs in that it often requires hardware operations, resulting in numerous statements targeting the controller’s internal resources. Therefore, developers need to understand how to manipulate the controller’s internal resources using C language, specifically how to use C statements to operate registers and internal memory. For example, in 51 assembly, we write MOV A, #20H; the assembly program recognizes A as the accumulator; while in a 51 C program, we write ACC = 32;, and the compiler recognizes ACC as the accumulator rather than a general variable. Each register has a specific name for developers to use, defined in a header file reg51.h, which programmers only need to include at the beginning of their programs with the #include “reg51.h” statement. Note: These register names cannot be used as variable names. Similarly, in TMS320F240’s C language, there is a header file C240.H that defines the names of various registers. Here are a few statements for introduction. For example: #define IMR ((PORT)0x0004) #define XINTI_CR ((PORT)0x07070) IMR and XINT1_CR correspond to two registers, which are actually the addresses of the registers, referred to as pointers in high-level language terms. In the C language for DSP, we only need to prefix them with an asterisk (*), for example, *IMR = 0X1010; /* Assign hexadecimal number 1010H to IMR register */ *XINT1_CR = 0X0A0B0; /* Assign hexadecimal number A0B0H to XINT1_CR register */ Developers are advised to print out the c240.h file to understand the definitions of each register. As for syntax not involving hardware, it is the same as ANSI syntax. It should be noted that some functions in the ANSI standard are not provided in DSP compilers; readers can refer to the C language manual for DSP compilers. Once these unique aspects are understood, transitioning from assembly language to C language development becomes easy. Of course, those without a background in assembly language programming can also develop DSP application systems using C language. For C language programming related to embedded systems, you can refer to “Microcontrollers and Embedded Systems Applications” issues from January to June 2001 on “Embedded C Programming Techniques,” which will not be discussed in this article. The following will specifically address embedded C language programming using the TMS320F240 chip as a processor, hoping to guide readers in specific operations.

2. C Language Development Process for TMS320F240 Chip

In simple terms, the entire process includes the following five steps: 1) Edit the C language source program; 2) Compile the source program (pay attention to compilation parameters); 3) Link the target file (pay attention to using CMD files); 4) Online simulation; 5) Program solidification.

2.1 Source Program Editing You can write the source program using any editor, such as EDIT or NOTEPAD, and save it with a .C suffix. The source code can be written in one C file or multiple C files; some predefined variables and function prototype declarations can be concentrated in one header file. Note: Do not forget to include the register definition file at the beginning of the C program using #include “c240.h”.

2.2 Source Program Compilation Once the source program is edited, it can be compiled using the DSPCL compiler to generate OBJ files. The format of usage is: DSPCL source_filename parameters, for example: DSPCL EX1.C -V2XX -GK -MN. The meanings of commonly used parameters are: V2XX – indicates the C compiler selects the processor from the 2XX series; GK – retains the generated assembly file (.ASM file); MN – performs normal optimization. For other parameters, please refer to the DSP compiler manual. If multiple source files are compiled separately, each source file will produce one OBJ file and one ASM file after compilation.

2.3 Linking Target Files 2.3.1 TI’s COFF File Format TI’s new assembler and compiler create target files using the COFF (Common Object File Format) target file format. Using the COFF format is beneficial for modular programming, providing a more powerful and flexible way to manage code segments and target system memory. When writing assembly programs or C language programs based on the COFF format, there is no need to specify target addresses for program code and variables, greatly facilitating program writing and portability. The basic idea of the COFF format is to encourage programmers to use the concepts of code blocks and data blocks when programming in assembly language or C language. These blocks are referred to as SECTIONS, which are the smallest units in target files. All blocks are divided into two main categories: initialized blocks and uninitialized blocks. Initialized blocks contain program code and data, while uninitialized blocks are reserved blocks for uninitialized data in memory. The C compiler generates initialized and uninitialized blocks after compiling C programs; initialized blocks include .text, .const, and .cinit blocks; uninitialized blocks include .bss blocks. For example, when a programmer defines an array with the C statement float data[100]; there is no need to specify the exact location of these 100 array elements; the compiler will reserve the required space in the data area. During linking, the linker will specifically locate them. 2.3.2 Handling of Blocks by the Linker The linker has two functions in handling blocks: first, it uses the blocks in the COFF target file to establish program blocks and data blocks, combining these blocks into a COFF output module that can be executed by the DSP chip; second, it specifies storage locations for output blocks. The linker provides two commands to implement these functions: MEMORY and SECTIONS. The MEMORY command defines the memory of the target system, allowing programmers to define each memory block and specify starting addresses and lengths; the SECTIONS command is used to define the combination of input blocks and the storage locations of output blocks in memory. If MEMORY and SECTIONS commands are not used, the linker will adopt the default allocation algorithm. It is recommended to use these two commands, but be aware that they should be used in the CMD file (linker command file). Below is an analysis of a typical CMD file for the TMS320F240 chip. (Assuming the file name is EX1.CMD.) (1) Composition of CMD File and Detailed Explanation BOOT.OBJ /* Interrupt vector table for F240, see explanations below */ EX1.OBJ /* Target file corresponding to the compiled source program */ /* If the program has multiple target files, write them here */ -STACK 0X400 /* Set system stack */ -C /* ROM initialization */ -O EX1.OUT /* Output file name */ -M EX1.MAP /* Output image file name */ -L RTS2XX.LIB /* Link RTS2XX.LIB library */ MEMORY /* MEMORY command specifies the memory configuration of the system */ { PAGE0: ROM0: origin=0000h, length=003fh /* FLASH ROM */ PAGE0: ROM1: origin=0040h, length=0200h /* FLASH ROM */ PAGE0: ROM2: origin=0240h, length=3000h /* FLASH ROM */ PAGE1: RAM_B2: origin=0060h, length=0020h /* Internal RAM B2 */ PAGE1: RAM_B1: origin=0300h, length=0100h /* Internal RAM B1 */ PAGE1: RAM_B0: origin=0100h, length=0100h /* Internal RAM B0 */ PAGE1: RAM_EX: origin=0d000h, length=2800h /* External extended RAM */ } SECTIONS /* SECTIONS command specifies the specific allocation method for the blocks in the program */ { .vectors: load=ROM0 /* Specify the storage location for the vector table */ .cinit: load=rom1 /* Specify the storage location for the C initialization table */ .text: load=ROM2 /* Specify the storage location for the system program */ .bSS load=RAM_B0 /* Specify the storage location for uninitialized data */ .const load=RAM_B1 /* Specify the storage location for initialized data */ } (2) Interrupt vector table file required for linking TMS320F240 The target file for TMS320F240 requires an interrupt vector table during linking. The interrupt vector table is written in assembly language and is specific to the DSP chip. Assuming the interrupt vector table for TMS320F240 corresponds to the assembly program BOOT.ASM, the assembled file name will be BOOT.OBJ. Below is a typical vector table file. (Assuming the program name is BOOT.ASM.) .port /* Define the name of the interrupt function */ .globl_c_int0 /* Function name corresponding to interrupt 0 */ .globl_c_int1 /* Function name corresponding to interrupt 1, the following statements have the same meaning */ .globl_c_int2 /* The interrupt function name can be regarded as the entry address for the interrupt */ .globl_c_int3 /* The storage of the vector table does not require intervention from the programmer */ .globl_c_int4 .globl_c_int5 .globl_c_int6 .globl_c_int7 .globl_c_int8 •sect “.vectors” /* Use .sect command to define a block for storing the interrupt vector table */ RSVECT B _c_int0 /* After interrupt 0 occurs, the program jumps to this address */ INT1 B _c_int1 /* After interrupt 1 occurs, it jumps to c_int1() function */ INT2 B _c_int2 /* Same meaning as above, and so on */ INT3 B _c_int3 INT4 B _c_int4 INT5 B _c_int5 INT6 B _c_int6 Compile this program using the assembler with the command format: DSPABOOT.ASM -V2XX to generate the BOOT.OBJ file for the linker to use. Thus, you can write interrupt functions in the C source program as follows: void c_inx() /* x is one of 1~8 */ { /* Series of C statements for the interrupt program */ } Note: c_int0() is the system entry function, and users cannot write it. After the above introduction to the command file (CMD file) and interrupt vector table, you can then link the command file to generate the required OUT file for the DSP chip to execute or for soft simulation. The command format is: DSPLNK CMD file name, for example: DSPLNK EX1.CMD. Another case is not using CMD files and using default configurations; a brief introduction is as follows: Command format: DSPLNK OBJ file name parameters, for example: DSPLNK EX1.OBJ BOOT.OBJ -O XX1.OUT -M XX1.MAP. The above three steps can be illustrated in diagram 1.

2.4 Program Simulation

Use the EMURST emulator reset command EMU2XXW EX1.OUT to load the COFF format binary code for simulation execution. The use of the debugger is briefly mentioned. 2.5 Program Solidification After the simulation runs correctly, it needs to be solidified into Flash ROM. The TMS320F240 has 16K words of Flash ROM available for solidifying programs without needing external EPROM (if the program does not exceed 16K words). TI provides software for solidifying programs, which can write the program into the chip via the emulator through the JTAG port. A new solidification technology has been developed that allows writing to the DSP chip via the serial port, particularly suitable for on-site debugging. Below is an introduction to the solidification method via the JTAG port. First, reset the debugger using the EMURST command, then execute the following three batch files. The first step is to execute the BCO.BAT batch file to clear the Flash ROM (CLEAR), setting all to 0. The second step is to execute the BE0.BAT batch file to erase the Flash ROM (ERASE), setting all to 1. (The above two steps do not require modifications to the two BAT files included in the software package.) The third step is to execute the BP16K.BAT batch file to write your OUT file into the internal Flash ROM of the DSP. Before executing this step, modify BP16K.BAT to replace the OUT file to be written with your OUT file. Here is what this batch file looks like. Assuming the software package installation directory is C:\DSP, and there is a subdirectory SRC. prg2xx -p240 -m0x0006 -w6src\c2xx_bpx.out is the OUT file to be written. If you want to write EX1.OUT into the Flash of the DSP, execute the following command: prg2xx -p240 -m0x0006 -w6src\c2xx_bpX.out c:\dsp\EX1.out. After completing the above steps, the program solidification is finished, and the system can be put into field experiments. Note: When solidifying the program, the CPU must operate at a frequency of 20MHz. In the SRC subdirectory, there is a configuration file C240_CFG.I. Readers can set the CPU’s operating frequency to 20MHz based on the program description and their system’s external crystal frequency (during writing frequency).

Leave a Comment