Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 18 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
18
Dung lượng
112,19 KB
Nội dung
Appendix C
Introduction of
C Programming for
DSP Applications
C has become the language of choice for many DSP software developments not only
because of its powerful commands and data structures but also because of its portability
to migrate between DSP platforms and devices. In this appendix, we will cover some of
the important features ofCforDSP applications.
The processes of compilation, linking/loading, and execution ofC programs differ
slightly among operating environments. To illustrate the process we use a general UNIX
system C compiler shown in Figure C.1 as an example. C compiler translates high-level
C programs into machine language that can be executed by computers or DSP proces-
sors such as the TMS320C55x. The fact that C compilers are available for a wide range
of computer platforms and DSP processors makes Cprogramming the most portable
software forDSP applications. Many Cprogramming environments include debugger
programs, which are useful for identifying errors in source programs. Debugger pro-
grams allow us to see values stored in variables at different points in a program and to
step through the program line by line.
The purpose ofDSPprogramming is to manipulate digital signals for a specific signal
processing application. To achieve this goal, DSP programs must be able to organize
the variables (different data types), describe the actions (operators), control the oper-
ations (program flow), and move data back and forth between the outside world and the
program (input/output). This appendix provides a brief overview of the elements
required for efficient programmingofDSP algorithms in C language and introduces
fundamental Cprogramming concepts using C examples, but does not attempt to
cover all the elements in detail. Cprogramming language used throughout this book
is conformed to the ANSI C standard (American National Standard Institute C
Standard).
Real-Time DigitalSignal Processing. Sen M Kuo, Bob H Lee
Copyright # 2001 John Wiley & Sons Ltd
ISBNs: 0-470-84137-0 (Hardback); 0-470-84534-1 (Electronic)
C program
(Source)
Preprocessor
Assembler
Assembly code
Object code
Data
Execution
Output
Linker
(loader)
Libraries
Compiler
Figure C.1 Program compilation, linking, and execution
C.1 A Simple C Program
In this section, we will present a simple C program and use it as an example to introduce
C language program components. As discussed in Section 3.1, an N-point unit-impulse
sequence can be written as
dn
1, n 0
0, n 1, 2, N À 1.
C:1
The following C program (impulse.c in the software package) can be used to generate
this unit-sample sequence
/****************************************************************
* IMPULSE.C À Unit impulse sequence generator *
****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define K 1024
void main()
{
float y [K];
int k;
int N 256;
/* Generate unit impulse sequence */
470 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSP APPLICATIONS
for(k 1; k < N; k)
{
y[k] 0.0; //Clear array
}
y[0] 1.0; //y(0) 1
}
A program written in C must have a few basic components. We now briefly discuss these
components used in this example C program.
C program comments may contain any message beginning with the characters
sequence /* and ending with the characters sequence */. The comments will be ignored
by the compiler. Program comments may be interspersed within the source code as well
as at the beginning of a function. In above example, the extra asterisks around the
program comments in lines one through three are there only to enhance the appearance
of the comments; they are not necessary. Most of the C compiler nowadays also accepts
the C programming language comments sequence, //. In our example, we mixed
both comment sequences for demonstration purpose. Although program comments are
optional, good programming style requires that they be used throughout a program to
document the operations and to improve its readability. Detailed comments are very
important in maintaining complicated DSP software for new readers, even for the
original programmers after time has passed.
The preprocessor is the first pass of the C compiler. It reads in C source files as input
and produces output files for the C compiler. The tasks of preprocessor are to remove
comments, expand macro definition, interpret include files, and check conditional
compilation. Preprocessor directives give instructions to the compiler that are per-
formed before the program is compiled. Each preprocessor directive begins with a
pound sign `#' followed by the preprocessor keyword. For example, the program
impulse.c contains the following directives:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define K 1024
The #include <file> directive copies the file from a directory inside the standard
C compiler, while #include `file' directly copies the file from current working
directory for compilation unless a path has been specified. Thus the first three directives
specify that the statements in the files of stdio.h, stdlib.h, and math.h should be
inserted in place of the directives before the program is compiled. Note that these files
are provided with the C compiler. The #define directs the preprocessor to replace
subsequent occurrences of K with the constant value 1024.
A C program may consist of one or more functions and one and only one function is
called main()with which the operating system will start executing. After starting the
application, the main function is executed first, and it is usually responsible for the main
control of the application program. This function can be written to return a value, or it
can be written as a void function that does not return a value. The body of the function
is enclosed in braces as follows:
A SIMPLE C PROGRAM 471
void main()
{
variable declarations; /* Statements define variables */
executable statements; /* Statements execute program */
}
As shown in the example, all C statements end with a semicolon. The function contains
two types of statements ± statements that define memory locations that will be used in the
program and statements that specify actions to be taken.
C.1.1 Variables and Assignment Operators
Before a variable can be used in a C program, it must be declared to inform the compiler
the name and type of the variable. A variable name is defined by declaring a sequence of
characters (the variable identifier or name) as a particular predefined type of data. C
constants are specific values that are included in the C statements, while variables are
memory locations that are assigned a name or identifier. The variable declaration uses
the following syntax:
data_type name;
For example, in the simple example we have
int k;
The term int indicates that the variable named k will store as integer data value. C also
allows multiple variables to be defined within one statement by separating them with the
commas. For example,
int i,j,k;
An identifier may be any sequence of characters (usually with some length restric-
tions) that starts with a letter or an underscore, and cannot be any of the C compiler
reserved keywords. Note that C is case sensitive; making the variable k different from
the variable K. C language supports several data types that represent: integers numbers,
floating-point numbers, and text data. Arrays of each variable type and pointers of each
type may also be declared and used. Once variables are defined to be a given size and
type, some sort of manipulation can be performed using the variables.
Memory locations must be defined before other statements use them. Initial values
can also be specified at the same time when memory locations are defined. For example,
int N 256;
defines the variable N as an integer, and assigns it with the value 256.
An assignment statement is used to assign a value to an identifier. The most basic
assignment operator in C is the single equal sign, =, where the value to the right of the
equal sign is assigned to the variable on the left. The general form of the assignment
statement is
identifier expression;
472 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSP APPLICATIONS
where the expression can be a constant, another variable, or the result of an operation.
C also allows multiple expressions to be placed within one statement by separating them
with the commas. Each expression is evaluated left to right, and the entire expression
assumes the value of the last expression which is evaluated. Multiple assignments are
also allowed in C, for example,
int i j k 0;
In this case, the statement is evaluated from right to left, so that 0 is assigned to k, j,
and i.
C.1.2 Numeric Data Types and Conversion
Numeric data types are used to specify the types of numbers that will be contained in
variables. There are several types of data used depending on the format in which the
numbers are stored and the accuracy of the data. In C, numeric numbers are either
integers (short, int, long) or floating-point (float, double, long double) values.
The specific ranges of values are system dependent, which means that the ranges may vary
from one computer to another. Table C.1 contains information on the precision and
range of integers represented by a 32-bit machine and a 16-bit machine. Thus the size of a
variable declared as just int depends on the compiler implementation and could make
the program behave differently on different machine. To make a program truly portable,
the program should contain only short and long declarations. In practice, explicit
defined data types are often used, such as:
#define Word16 short
#define Word32 long
main()
{
Word16 k; /* Declare as 16±bit variable */
Word32 x; /* Declare as 32±bit variable */
statements;
}
Table C.1 Example of integer type limits
Data type Value on 32-bit machine Value on 16-bit machine
Short [À32 768, 32 767] [À32 768, 32 767]
unsigned short [0, 65 535] [0, 65 535]
Int [À2 14 74 83 648, 2 14 74 83 647] [À32 768, 32 767]
unsigned int [0, 4 29 49 67 295] [0, 65 535]
Long [À2 14 74 83 648, 2 14 74 83 647] [À2 14 74 83 648, 2 14 74 83 647]
unsigned long [0, 4 29 49 67 295] [0, 4 29 49 67 295]
A SIMPLE C PROGRAM 473
Instead of using short and long data type, the example code uses Word16 for the 16-
bit integer data type and Word32 for the 32-bit data type. In addition, the three integer
types (int, short, and long) can be declared as unsigned by preceding the declaration
with unsigned. For example,
unsigned int counter;
where counter has a value range from 0 to 65 535.
Statements and expressions using the operators should normally use variables and
constants of the same type. If data types are mixed, C uses two basic rules to auto-
matically make type conversions:
1. If an operation involves two types, the value with a lower rank is converted to the
type of higher rank. This process is called promotion, and the ranking from highest
to lowest type is double, float, long, int, short, and char.
2. In an assignment statement, the result is converted to the type of the variable that is
being assigned. This may result in promotion or demotion when the value is
truncated to a lower ranking type.
Sometimes the conversion must be stated explicitly in order to demand that a con-
version be done in a certain way. A cast operator places the name of the desired type in
parentheses before the variable or expression, thus allowing us to specify a type change
in the value. For example, the data casting(int)used in the following expressions
treats the floating-point number z as an integer:
int x, y;
float z 2.8;
x (int)z; /* Truncate z to an integer x */
y (int)(z0.5); /* Rounding z to an integer y */
The casting result will truncate 2.8 to 2 and store it in x, and allows rounding of the
floating variable z to an integer 3 and stores it in y.
C.1.3 Arrays
An array groups distinct variables of the same type under a single name. A one-dimen-
sional array can be visualized as a list of values arranged in either a row or a column. We
assign an identifier to an array, and then distinguish between elements or values in the
array by using subscripts. The subscripts always start with 0 and are incremented by 1. In
C, all data types can be declared as an array by placing the number of elements to be
assigned to an array in brackets after its name. One-dimensional array is declared as
data_type array_name [N];
where the array_name is the name of an array of N elements of data type specified. For
example,
float y [5];
474 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSP APPLICATIONS
where an integer expression 5 in brackets specifies there are five float (floating-point)
elements in the array y[]. The first value in the y array is referenced by y[0], the
second value in the y array is referenced by y[1], and the last element is indexed by
y[KÀ1].
Multidimensional arrays can be defined simply by appending more brackets contain-
ing the array size in each dimension. For example,
int matrix_a [4][2];
defines a 4Â2 matrix called matrix_a. The matrix array would be referenced as
matrix_a [i][j], where i and j are row and column indices respectively.
An array can be initialized when it is defined, or the values can be assigned to it using
program statements. To initialize the array at the same time when it is defined, the
values are specified in a sequence that is separated by commas and enclosed with braces.
For example,
float y [5] {1.0, 0.0, 0.0, 0.0, 0.0 };
initializes a 5-point unit impulse response sequence in the floating-point array y[].
Arrays can also be assigned values by means of program statements. For example, the
following example generates an N-point unit impulse sequence.
for(k 1; k < N; k)
{
y[k] 0.0; /* Clear array */
}
y[0] 1.0; /* y(0) 1*/
A more detailed discussion on arrays and loops will be given later.
C.2 Arithmetic and Bitwise Operators
Once variables are defined to be a given size and type, a certain manipulation (operator)
can be performed using the variables. We have discussed assignment operators in C.1.1.
This section will introduce arithmetic and bitwise operators. Logical operators will be
introduced later.
C.2.1 Arithmetic Operators
C supplies arithmetic operators on two operands: + (add), - (subtract), * (multiply), /
(divide), and % (modulus, integer remainder after division). The first four operators are
defined for all types of variables, while the modulus operator is only defined for integer
operands. C does not have an exponential operator. However, the library function
pow(x,y)may be used to compute x
y
. Note that in C, a pow(x,y)is an expression,
while a pow(x,y); is a statement. Thus c b+(apow(x,y)); is a statement. The
result of this statement would be that the result returned by the function pow(x,y)is
assigned to a and ba is assigned to c.
ARITHMETIC AND BITWISE OPERATORS 475
The modulus operator is useful in implementing a circular pointer forsignal proces-
sing. For example,
k (k+1)%128;
makes k a circular pointer of range from 0 to 127.
C also includes increment (++) and decrement ( ) operators for incrementing and
decrementing variables. For example, i++ is equal to the statement i i+1. These
operators can be applied either in a prefix position or in a postfix position. If the
increment or decrement operator is in a prefix position, the identifier is modified first,
and then the new value is used to evaluate the rest of the expression. If the increment or
decrement operator is in a postfix position, the old value of the identifier is used to
evaluate the rest of the expression, and then the identifier is modified. These unary
operators (require only one operand) are often used for updating counters and address
pointers.
C also allows operators to be combined with the assignment operator `' so that
almost any statement of the form
variable variable operator expression;
can be replaced with
variable operator expression;
For example,
x x+y;
is equal to the statement
x y;
Some compiler implementations may generate code that is more efficient if the com-
bined operator is used. The combined operators include +, -,*,/,%, and other logical
operators.
C.2.2 Bitwise Operators
C supplies the binary bitwise operators: & (bitwise AND), | (bitwise OR), ^ (bitwise
exclusive OR), ( (arithmetic shift left), and ) (arithmetic shift right), which are
performed on integer operands. The unary bitwise operator, which inverts all the bits
in the operand, is implemented with the ~ symbol. These bitwise operators make C
programming an efficient programming language forDSP applications.
C.3 An FIR Filter Program
To introduce more features ofC programming, an example C program firfltr.c
that implements an FIR filter is included in the software package.
476 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSP APPLICATIONS
C.3.1 Command-Line Arguments
The function main can have two parameters, argc and argv [], to catch arguments
passed to main from the command line when the program begins executing. These
arguments could be file names on which the program is to act or options that
influence the logic of the program. The parameter argv []is an array of pointers to
strings, and argc is an int whose value is equal to the number of strings to which
argv []points to. The command-line arguments are passed to the main( ) function as
follows:
void main(int argc, char *argv [])
Suppose that we compile the firfltr.c such that the executable program is generated
and saved as firfltr.exe. We can run the program on a PC under MS-DOS Prompt
by typing
firfltr infile coefile outfile <enter>
The operating system passes the strings on the command line to main. More precisely,
the operating system stores the strings on the command line in memory and sets
argv [0]to the address of the first string (firfltr), the name of the file that holds
the program to be executed on the command line. argv [1]points to the address of the
second string (infile) on the command line, argv [2]to coefile, and argv [3]to
outfile. The argument argc is set to the number of strings on the command line. In
this example, argc 4.
The use of command-line arguments makes the executable program flexible, because
we can run the program with different arguments (data files, parameter values, etc.)
specified at the execution time without modifying the program and re-compiling it
again. For example, the file firfltr.exe can be used to perform FIR filtering
function for different FIR filter with their coefficients defined by coefile. This
program can also be used to filter different input signals contained in the infile.
The flexibility is especially convenient when the parameter values used in the program
need to be tuned based on given data.
C.3.2 Pointers
A pointer is a variable that holds the address of data, rather than the data itself. The use
of pointers is usually closely related to manipulating the elements in an array. Two
special pointer operators are required to effectively manipulate pointers. The indirection
operator * is used whenever the data stored at the address pointed to by a pointer
(indirect addressing) is required. The address operator & is used to set the pointer to the
desired address. For example,
int i 5;
int *ptr;
ptr &i;
*ptr 8;
AN FIR FILTER PROGRAM 477
The first statement declares i as an integer of value 5, the second statement declares
that ptr is a pointer to an integer variable, and the third statement sets the pointer ptr
to the address of the integer variable i. Finally, the last statement changes the data at
the address pointed by ptr to 8. This results in changing the value of variable i from
5to8.
An array introduced in Section C.1.3 is essentially a section of memory that is
allocated by the compiler and assigned the name given in the declaration statement.
In fact, the name given is just a fixed pointer to the beginning of the array. In C, the
array name can be used as a pointer or it can be used to reference elements of the array.
For example, in the function shift in firfltr.c, the statement
float *x;
defines x as a pointer to floating-point variables. Thus *x and x[0]are exactly
equivalent, although the meaning of x[0]is often more clear.
C.3.3 C Functions
As discussed earlier, all C programs consist of one or more functions, including the
main(). In C, functions (subroutines) are available from libraries such as the standard C
library and programmer-defined routines that are written specifically to accompany the
main function, such as:
void shift(float *, int, float);
float fir(float *, float *, int);
These functions are sets of statements that typically perform an operation, such as
shift to update data buffers for FIR filters, or as fir to compute an output of the
FIR filter.
To maintain simplicity and readability for more complicated applications, we develop
programs that use a main()function plus additional functions, instead of using one
long main function. In C, any function can call any other function, or be called by any
other function. Breaking a long program into a set of simple functions has following
advantages:
1. A function can be written and tested separately from other parts of the program.
Thus module development can be done in parallel for large projects. Several
engineers can work on the same project if it is separated into modules because the
individual modules can be developed and tested independently of each other.
2. A function is smaller than the complete program, so testing it separately is easier.
3. Once a function has been carefully tested, it can be used in other programs without
being retested. This reusability is a very important issue in the development of large
software systems because it greatly reduces development time.
4. The use of functions frequently reduces the overall length of a program, because
many solutions include steps that are repeated several places in the program.
478 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSP APPLICATIONS
[...]... all the data defined as char from 8-bit to 16-bit integer References [1] P M Embree, C Algorithms forReal-Time DSP, Englewood Cliffs, NJ: Prentice-Hall, 1995 [2] P M Embree and B Kimble, C Language Algorithms for Digital Signal Processing, Englewood Cliffs, NJ: Prentice-Hall, 1991 [3] D M Etter, Introduction to C for Engineers and Scientists, Englewood Cliffs, NJ: Prentice-Hall, 1997 [4] S P Harbison... match occurs, the statements following the corresponding case label will be executed The program execution will continue until the end of the switch statement is reached, or a break statement that redirects control to the end of the switch-case statement is reached A break statement should be included in each case not required after the last case or default statement C. 4.2 Logical Operators The condition... function calls the function shift as 480 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSPAPPLICATIONS shift(xn_buf, len_imp, xn) ; where the address (value) of the first element in xn_buf is passed to the function shift Thus in shift, we use float *x; which defines the pointer *x points to the first element of xn_buf in the main function Therefore the signal buffer array x [ i]used in the function... types of char and int , float and double , and long double Table C. 4 lists the data type supported by the C5 5x C compiler From this table, it is clear that all the integer data types (char, short and int), either signed or unsigned, have the equivalent type and are all in 16-bit size The long 486 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSPAPPLICATIONS Table C. 4 Data type supported by the TMS32 0C5 5x... statements conditionally: the if statement and the switch-case statement The if statement allows us to test a condition and then execute statements based on whether the given condition is true or false The if statement has the following general format: 482 APPENDIXC:INTRODUCTIONOFCPROGRAMMINGFORDSPAPPLICATIONS if (condition) statement1; If the condition is true, the statement1 is executed; otherwise,... can be evaluated to be true or false It is composed of expressions combined with relational and sometimes logical operators A logical operator is any operator that gives a result of true or false Table C. 2 gives the relational operators that can be used to compare two expressions in CC also supports three logical operators listed in Table C. 3 that compare conditions 484 APPENDIXC:INTRODUCTION OF. .. statement becomes inconvenient and somewhat inefficient When more than four alternatives from a single expression are chosen, the switch-case statement is very useful The basic syntax of the switch-case statement is Is Condition True? No Yes Statements Figure C. 2 Flowchart for if statement CONTROL STRUCTURES AND LOOPS Is Condition True? 483 No Yes Statements A Figure C. 3 Statements B Flowchart for if/else... provides formatted input (scanning or reading) For example, the statement fscanf(fpimp,"%f",&bn) ; reads from an arbitrary file pointed by the file pointer fpimp to a variable of address &bn, and %f indicates the number is floating-point data In addition, the formatted I/O functions also recognize %d for decimal integers, %x for hexadecimals, %c for characters, and %s for character strings The function... value in binary format using 4 bytes, whereas fprintf writes the value as ASCII text which usually need more than 4 bytes C. 4 Control Structures and Loops C language has a complete set of program control features that allow conditional execution or repetition of statements in loops based on the result of an expression C. 4.1 Control Structures The C language provides two basic methods for executing a statement... processor, the DSP run-time environment, and the host environment In this section, we will present the data types that are used by the TMS32 0C5 5x C compiler The C5 5x is designed forreal-timeDSPapplications The most common data type is the 16-bit fixed-point integer representation of data variables An important difference in data type definition used by the C5 5x and some other C compiler is the size . Appendix C
Introduction of
C Programming for
DSP Applications
C has become the language of choice for many DSP software developments not only
because of. example C program firfltr .c
that implements an FIR filter is included in the software package.
476 APPENDIX C: INTRODUCTION OF C PROGRAMMING FOR DSP APPLICATIONS
C. 3.1